mod_musicindex  1.4.1
http.c
Go to the documentation of this file.
1 /*
2  * http.c
3  * mod_musicindex
4  *
5  * $Id: http.c 1003 2012-07-31 09:53:17Z varenet $
6  *
7  * Created by Regis BOUDIN on Sun Jun 13 2004.
8  * Copyright (c) 2003-2004 Regis BOUDIN
9  * Copyright (c) 2006-2007 Thibaut VARENE
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 2.1,
13  * as published by the Free Software Foundation.
14  *
15  */
16 
60 #include "http.h"
61 #include "sort.h"
62 #include "playlist.h"
63 #ifdef HAVE_STDLIB_H
64 #include <stdlib.h> /* atoi */
65 #endif
66 
75 void treat_get_args(request_rec *r)
76 {
77  mu_config *const conf = (mu_config *)ap_get_module_config(r->per_dir_config, &musicindex_module);
78  const char *s = r->args;
79  const char *p;
80  register unsigned short i;
81 
82  if (s == NULL)
83  return;
84 
85  conf->custom_list = r->args;
86 
87  while (s[0]) {
88  p = ap_getword(r->pool, &s, '&');
89  if (!strncmp(p, "action=", 7)) {
90  p += 7;
91 
92  if (!strcmp(p, "randomdir")) {
93  conf->options |= MI_RANDOMDIR;
94  return;
95  }
96 
97  if (conf->options & MI_ALLOWTARBALL) {
98  if (!strcmp(p, "tarball")) {
99  conf->options |= (MI_DWNLDALL | MI_QUICKPL) ;
100  conf->order[0] = SB_DIR;
101  conf->order[1] = SB_URI;
102  continue;
103  }
104  }
105 
106  if (conf->options & MI_ALLOWSTREAM) {
107  if (!strcmp(p, "playall")) {
108  conf->options |= MI_STREAMALL;
109  continue;
110  }
111  }
112 
113  if (conf->rss_items > 0) {
114  if (!strcmp(p, "RSS")) {
115  conf->options |= MI_RSS;
116  conf->order[0] = SB_MTIME;
117  conf->order[1] = SB_URI;
118  conf->options &= ~MI_RECURSIVE;
119  continue;
120  }
121 
122  else if (!strcmp(p, "podcast")) {
123  conf->options |= MI_RSS|MI_PODCAST;
124  conf->order[0] = SB_MTIME;
125  conf->order[1] = SB_URI;
126  conf->options &= ~MI_RECURSIVE;
127  continue;
128  }
129  }
130  }
131  else if (!strncmp(p, "sort=", 5)) {
132  p += 5;
133  /* Temporary workaround : only have one sort param */
134  for (i = SB_MAX; i > 0; i--)
135  conf->order[i] = conf->order[i-1];
136  conf->order[0] = (atoi(p) % SB_MAX); /* avoid out of bound values */
137  }
138  else if (!strncmp(p, "option=", 7)) {
139  p += 7;
140  if (!strcmp(p, "recursive"))
141  conf->options |= MI_RECURSIVE;
142  else if (!strcmp(p, "shuffle")) {
143  conf->order[0] = SB_RANDOM;
144  conf->order[1] = SB_DEFAULT;
145  }
146  else if (!strcmp(p, "quick"))
147  conf->options |= MI_QUICKPL;
148  }
149  else if (!strncmp(p, "limit=", 6)) {
150  if (conf->rss_items > 0)
151  conf->rss_items = atoi(p+6);
152  }
153  }
154 }
155 
170 void treat_post_args(request_rec *r)
171 {
172  mu_config *const conf = (mu_config *)ap_get_module_config(r->per_dir_config, &musicindex_module);
173  char buffer[MAX_STRING];
174  const char *s = NULL;
175  const char *p = s;
176  short i = 0;
177 
178  ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK);
179 
180  do {
181  i = ap_get_client_block(r, buffer, MAX_STRING-1);
182  buffer[i] = '\0';
183  if (s == NULL)
184  s = apr_pstrdup(r->pool, buffer);
185  else
186  s = apr_pstrcat(r->pool, s, buffer, NULL);
187  } while (i == (MAX_STRING-1));
188 
189  conf->custom_list = s;
190 
191  /* Find the search string */
192  p = strstr(s, "&search=");
193 
194  if (p != NULL)
195  p += 1;
196  else if (strncmp(s, "search=", 7) == 0)
197  p = s;
198 
199  if (p != NULL) {
200  p += 7;
201  conf->search = ap_getword(r->pool, &p, '&');
202  for (i=0; p[i]; i++) {
203  /* x-www-form-urlencoded changes spaces to '+' */
204  if (conf->search[i] == '+')
205  conf->search[i] = ' ';
206  }
207  ap_unescape_url(conf->search); /* x-www-form-urlencoded forms */
208  }
209 
210  /* Find the sort string */
211  p = strstr(s, "&sort=");
212 
213  if (p != NULL)
214  p += 1;
215  else if (strncmp(s, "sort=", 5) == 0)
216  p = s;
217 
218  if (p != NULL) {
219  p += 5;
220  /* The client sends us a complete sort sequence.
221  * The modulo works around potentially (forged?) out of bound values */
222  for (i = 0; p[i] && (p[i] != '&') && (i < SB_MAX); i++)
223  conf->order[i] = ((p[i]-'`') % SB_MAX); /* unshift from letters */
224  }
225 
226  /* Find the action string. We expect to have only one action at a time.
227  * Otherwise, supplementary actions will be ignored. */
228  p = strstr(s, "&action=");
229 
230  if (p != NULL)
231  p += 1;
232  else if (strncmp(s, "action=", 7) == 0)
233  p = s;
234 
235  if (p != NULL) {
236  p += 7;
237 
238  p = ap_getword(r->pool, &p, '&');
239 
240  if ((conf->options & MI_ALLOWSEARCH) && conf->search && conf->search[0]) {
241  if (!strcmp(p, "Search")) {
242  if (!conf->cache)
243  conf->options |= MI_QUICKPL;
244  }
245  else if (!strcmp(p, "RecursiveSearch")) {
246  conf->options |= MI_RECURSIVE;
247  conf->order[0] = SB_DIR;
248  conf->order[1] = SB_URI;
249  if (!conf->cache)
250  conf->options |= MI_QUICKPL;
251  }
252  }
253 
254  /* In any situation, no recursive directories
255  scan for Cookie operations */
256  if (conf->options & MI_COOKIEOP)
257  conf->options &= ~MI_RECURSIVE;
258 
259  if (conf->options & MI_ALLOWSTREAM) {
260  if (!strcmp(p, "PlaySelected")) {
261  conf->options |= MI_STREAMLST;
262  } else if (!strcmp(p, "PlayAll")) {
263  conf->options |= MI_STREAMALL;
264  } else if (!strcmp(p, "ShuffleAll")) {
265  conf->options |= MI_STREAMALL;
266  conf->order[0] = SB_RANDOM;
267  conf->order[1] = SB_DEFAULT;
268  }
269  }
270 
271  if (conf->options & MI_ALLOWTARBALL) {
272  if (!strcmp(p, "DownloadAll")) {
273  conf->options |= MI_DWNLDALL;
274  } else if (!strcmp(p, "DownloadSelected")) {
275  conf->options |= MI_DWNLDLST;
276  }
277  }
278 
279  if ((conf->options & (MI_ALLOWSTREAM|MI_ALLOWTARBALL))) {
280  if (!strcmp(p, "AddToPlaylist")) {
281  conf->options |= MI_COOKIEADDLST;
282  } else if (!strcmp(p, "AddAllToPlaylist")) {
283  conf->options |= MI_COOKIEADDALL;
284  }
285  }
286 
287  if (!strcmp(p, "RemoveFromPlaylist")) {
288  conf->options |= MI_COOKIEDELLST;
289  } else if (!strcmp(p, "ClearPlaylist")) {
290  conf->options |= MI_COOKIEPURGE;
291  } else if (!strcmp(p, "StreamPlaylist")) {
292  conf->options |= MI_COOKIESTREAM;
293  } else if (!strcmp(p, "DownloadPlaylist")) {
294  conf->options |= MI_COOKIEDWNLD;
295  }
296  }
297 }
298 
299 static const char *find_playlist(request_rec *r, apr_pool_t *subpool)
300 {
301  const char *cookie = NULL, *playlist_cookie = NULL;
302 
303  cookie = apr_table_get(r->headers_in, "Cookie");
304  if (cookie) {
305  playlist_cookie = strstr(cookie, "playlist=");
306  if (playlist_cookie)
307  playlist_cookie = ap_getword(subpool, &playlist_cookie, ';');
308  }
309 
310  return playlist_cookie;
311 }
312 
313 static const char *find_or_create_playlist(request_rec *r, apr_pool_t *subpool)
314 {
315  const char *playlist = NULL;
316 
317  playlist = find_playlist(r, subpool);
318  if (!playlist)
319  playlist = apr_pstrdup(subpool, "playlist=");
320 
321  return playlist;
322 }
323 
335 static const char *cookie_add(request_rec *r, apr_pool_t *subpool)
336 {
337  const mu_config *const conf = (mu_config *)ap_get_module_config(r->per_dir_config, &musicindex_module);
338  const char *new_cookie_string = NULL;
339  const char *args = conf->custom_list;
340  char *p = NULL;
341 
342  new_cookie_string = find_or_create_playlist(r, subpool);
343 
344  while (*args) {
345  p = ap_getword(subpool, &args, '&');
346  if (strncmp(p, "file=", 5))
347  continue;
348  else
349  p+=5;
350 
351  ap_unescape_url(p); /* browser will escape '=' */
352  if (strstr(new_cookie_string, p) == NULL)
353  new_cookie_string = apr_pstrcat(subpool, new_cookie_string, p, "&", NULL);
354  }
355  return new_cookie_string;
356 }
357 
369 static const char *cookie_addall(request_rec *r, apr_pool_t *subpool)
370 {
371  const mu_config *const conf = (mu_config *)ap_get_module_config(r->per_dir_config, &musicindex_module);
372  const mu_ent *custom = NULL;
373  const char *new_cookie_string = NULL;
374  char *codeduri = NULL;
375  mu_pack cookie_pack = {
376  .head = NULL,
377  .fhead = NULL,
378  .dirnb = 0,
379  .filenb = 0,
380  .fsize = 0,
381  };
382 
383  new_cookie_string = find_or_create_playlist(r, subpool);
384 
385  make_music_entry(r, subpool, &cookie_pack, NULL, MI_RECURSIVE);
386  listsort(&cookie_pack, conf->order); /* this sets cookie_pack.fhead */
387 
388  for (custom = cookie_pack.fhead; custom; custom = custom->next) {
389  codeduri = (char *)realloc(codeduri, 1 + apr_base64_encode_len(strlen(custom->uri)));
390  if (!codeduri)
391  return NULL;
392  apr_base64_encode(codeduri, custom->uri, strlen(custom->uri));
393 
394  if (strstr(new_cookie_string, codeduri) == NULL)
395  new_cookie_string = apr_pstrcat(subpool, new_cookie_string, codeduri, "&", NULL);
396  }
397 
398  free(codeduri);
399  return new_cookie_string;
400 }
401 
415 static const char *cookie_remove(request_rec *r, apr_pool_t *subpool)
416 {
417  const mu_config *const conf = (mu_config *)ap_get_module_config(r->per_dir_config, &musicindex_module);
418  const char *new_cookie_string = NULL, *p = NULL, *args;
419  char *rlist;
420 
421  args = find_playlist(r, subpool);
422 
423  if (!args)
424  return NULL;
425 
426  args = strstr(args, "playlist=");
427 
428  if (!args)
429  return NULL;
430  args += 9;
431 
432  /* at this point, args contains the series of base64& existing list
433  of files, and conf->custom_list contains the escaped base64 uris of
434  the files to remove */
435  rlist = apr_pstrdup(subpool, conf->custom_list);
436  ap_unescape_url(rlist); /* browser will escape '=' etc */
437 
438  /* we are going to duplicate all but the selected files from the original
439  cookie to a newly created one */
440  new_cookie_string = apr_pstrdup(subpool, "playlist=");
441 
442  /* If our token was found, copy from the client string
443  to our cookie_header */
444  while ((*args != '\0') && (*args != ';')) {
445  p = ap_getword(subpool, &args, '&'); /* find the base64 string */
446 
447  /* if we don't find the base64 string in the remove list, we can copy it */
448  if (strstr(rlist, p) == NULL)
449  new_cookie_string = apr_pstrcat(subpool, new_cookie_string, p, "&", NULL);
450  }
451 
452  return new_cookie_string;
453 }
454 
455 void cookie_and_stream_work(request_rec *r)
456 {
457  mu_config *const conf = (mu_config *)ap_get_module_config(r->per_dir_config, &musicindex_module);
458  const char *end_string = NULL, *args = NULL;
459  apr_pool_t *subpool = NULL;
460 
461  /* Create a subpool we will clear sometimes. This is to save memory, as
462  some operations are based on many ap_{palloc,strcat,strdup,...} for
463  playlists */
464  apr_pool_create(&subpool, r->pool);
465 
466  if (subpool == NULL)
467  subpool = r->pool;
468 
469  switch (conf->options & MI_ALLOPS) {
470  case MI_COOKIEADDALL:
471  args = cookie_addall(r, subpool);
472  break;
473  case MI_COOKIEADDLST:
474  args = cookie_add(r, subpool);
475  break;
476  case MI_COOKIEDELLST:
477  args = cookie_remove(r, subpool);
478  break;
479  case MI_COOKIEPURGE:
480  args = apr_pstrdup(subpool, "playlist=");
481  break;
482  case MI_COOKIESTREAM:
483  args = apr_pstrdup(subpool, "playlist=");
484  default:
485  conf->custom_list = NULL;
486  args = find_playlist(r, subpool);
487  break;
488  }
489 
490  if (args) {
491  end_string = apr_psprintf(subpool, "; Version=1; Max-Age=%d; Path=/",
492  (args[9] == '\0') ? 0 : conf->cookie_life);
493  conf->custom_list = apr_pstrcat(r->pool, args, end_string, NULL);
494  }
495  else
496  conf->custom_list = NULL;
497 
498  /* We do not need the subpool any more */
499  if (subpool != r->pool)
500  apr_pool_destroy(subpool);
501 }