]> git.sesse.net Git - vlc/blob - modules/control/http.c
* string review.
[vlc] / modules / control / http.c
1 /*****************************************************************************
2  * http.c :  http mini-server ;)
3  *****************************************************************************
4  * Copyright (C) 2001-2004 VideoLAN
5  * $Id: http.c,v 1.50 2004/01/25 16:17:03 anil Exp $
6  *
7  * Authors: Gildas Bazin <gbazin@netcourrier.com>
8  *          Laurent Aimar <fenrir@via.ecp.fr>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 /*
29  * TODO:
30  *
31  * - clean up ?
32  * - doc ! (mouarf ;)
33  *
34  */
35
36 #include <stdlib.h>
37 #include <vlc/vlc.h>
38 #include <vlc/intf.h>
39
40 #include <vlc/aout.h>
41 #include <vlc/vout.h> /* for fullscreen */
42
43 #include "httpd.h"
44
45 #ifdef HAVE_SYS_STAT_H
46 #   include <sys/stat.h>
47 #endif
48 #ifdef HAVE_ERRNO_H
49 #   include <errno.h>
50 #endif
51 #ifdef HAVE_FCNTL_H
52 #   include <fcntl.h>
53 #endif
54
55 #ifdef HAVE_UNISTD_H
56 #   include <unistd.h>
57 #elif defined( WIN32 ) && !defined( UNDER_CE )
58 #   include <io.h>
59 #endif
60
61 #if (!defined( WIN32 ) || defined(__MINGW32__))
62 /* Mingw has its own version of dirent */
63 #   include <dirent.h>
64 #endif
65
66 /*****************************************************************************
67  * Module descriptor
68  *****************************************************************************/
69 static int  Activate     ( vlc_object_t * );
70 static void Close        ( vlc_object_t * );
71
72 #define HOST_TEXT N_( "Host address" )
73 #define HOST_LONGTEXT N_( \
74     "You can set the address and port the http interface will bind to." )
75 #define SRC_TEXT N_( "Source directory" )
76 #define SRC_LONGTEXT N_( "Source directory" )
77
78 vlc_module_begin();
79     set_description( _("HTTP remote control interface") );
80         add_string ( "http-host", NULL, NULL, HOST_TEXT, HOST_LONGTEXT, VLC_TRUE );
81         add_string ( "http-src",  NULL, NULL, SRC_TEXT,  SRC_LONGTEXT,  VLC_TRUE );
82     set_capability( "interface", 0 );
83     set_callbacks( Activate, Close );
84 vlc_module_end();
85
86
87 /*****************************************************************************
88  * Local prototypes
89  *****************************************************************************/
90 static void Run          ( intf_thread_t *p_intf );
91
92 static int ParseDirectory( intf_thread_t *p_intf, char *psz_root,
93                            char *psz_dir );
94
95 static int DirectoryCheck( char *psz_dir )
96 {
97     DIR           *p_dir;
98
99 #ifdef HAVE_SYS_STAT_H
100     struct stat   stat_info;
101
102     if( stat( psz_dir, &stat_info ) == -1 || !S_ISDIR( stat_info.st_mode ) )
103     {
104         return VLC_EGENERIC;
105     }
106 #endif
107
108     if( ( p_dir = opendir( psz_dir ) ) == NULL )
109     {
110         return VLC_EGENERIC;
111     }
112     closedir( p_dir );
113
114     return VLC_SUCCESS;
115 }
116
117
118 static int  http_get( httpd_file_callback_args_t *p_args,
119                       uint8_t *p_request, int i_request,
120                       uint8_t **pp_data, int *pi_data );
121
122 static char *uri_extract_value( char *psz_uri, char *psz_name,
123                                 char *psz_value, int i_value_max );
124 static void uri_decode_url_encoded( char *psz );
125
126 static char *Find_end_MRL( char *psz );
127
128 static playlist_item_t * parse_MRL( char *psz );
129
130 /*****************************************************************************
131  *
132  *****************************************************************************/
133 typedef struct mvar_s
134 {
135     char *name;
136     char *value;
137
138     int           i_field;
139     struct mvar_s **field;
140 } mvar_t;
141
142 #define STACK_MAX 100
143 typedef struct
144 {
145     char *stack[STACK_MAX];
146     int  i_stack;
147 } rpn_stack_t;
148
149 struct httpd_file_callback_args_t
150 {
151     intf_thread_t *p_intf;
152     httpd_file_t  *p_file;
153
154     char          *file;
155     char          *name;
156     char          *mime;
157
158     /* inited for each access */
159     rpn_stack_t       stack;
160     mvar_t        *vars;
161 };
162
163 struct intf_sys_t
164 {
165     httpd_t             *p_httpd;
166     httpd_host_t        *p_httpd_host;
167
168     int                         i_files;
169     httpd_file_callback_args_t  **pp_files;
170
171     playlist_t          *p_playlist;
172     input_thread_t      *p_input;
173 };
174
175
176
177 /*****************************************************************************
178  * Activate: initialize and create stuff
179  *****************************************************************************/
180 static int Activate( vlc_object_t *p_this )
181 {
182     intf_thread_t *p_intf = (intf_thread_t*)p_this;
183     intf_sys_t    *p_sys;
184     char          *psz_host;
185     char          *psz_address = "";
186     int           i_port       = 0;
187     char          *psz_src;
188
189     psz_host = config_GetPsz( p_intf, "http-host" );
190     if( psz_host )
191     {
192         char *psz_parser;
193         psz_address = psz_host;
194
195         psz_parser = strchr( psz_host, ':' );
196         if( psz_parser )
197         {
198             *psz_parser++ = '\0';
199             i_port = atoi( psz_parser );
200         }
201     }
202     if( i_port <= 0 )
203     {
204         i_port= 8080;
205     }
206
207     msg_Dbg( p_intf, "base %s:%d", psz_address, i_port );
208     p_intf->p_sys = p_sys = malloc( sizeof( intf_sys_t ) );
209     if( !p_intf->p_sys )
210     {
211         return( VLC_ENOMEM );
212     }
213     p_sys->p_playlist = NULL;
214     p_sys->p_input    = NULL;
215
216     if( ( p_sys->p_httpd = httpd_Find( VLC_OBJECT(p_intf), VLC_TRUE ) ) == NULL )
217     {
218         msg_Err( p_intf, "cannot create/find httpd" );
219         free( p_sys );
220         return VLC_EGENERIC;
221     }
222
223     if( ( p_sys->p_httpd_host =
224                 p_sys->p_httpd->pf_register_host( p_sys->p_httpd,
225                                                   psz_address, i_port ) ) == NULL )
226     {
227         msg_Err( p_intf, "cannot listen on %s:%d", psz_address, i_port );
228         httpd_Release( p_sys->p_httpd );
229         free( p_sys );
230         return VLC_EGENERIC;
231     }
232
233     if( psz_host )
234     {
235         free( psz_host );
236     }
237
238     p_sys->i_files = 0;
239     p_sys->pp_files = malloc( sizeof( httpd_file_callback_args_t *) );
240     if( !p_sys->pp_files )
241     {
242         return( VLC_ENOMEM );
243     }
244
245 #if defined(SYS_DARWIN) || defined(SYS_BEOS) || \
246         ( defined(WIN32) && !defined(UNDER_CE ) )
247     if ( ( psz_src = config_GetPsz( p_intf, "http-src" )) == NULL )
248     {
249         char * psz_vlcpath = p_intf->p_libvlc->psz_vlcpath;
250         psz_src = malloc( strlen(psz_vlcpath) + strlen("/share/http" ) + 1 );
251         if( !psz_src )
252         {
253             return( VLC_ENOMEM );
254         }
255 #if defined(WIN32)
256         sprintf( psz_src, "%s/http", psz_vlcpath);
257 #else
258         sprintf( psz_src, "%s/share/http", psz_vlcpath);
259 #endif
260     }
261 #else
262     psz_src = config_GetPsz( p_intf, "http-src" );
263
264     if( !psz_src || *psz_src == '\0' )
265     {
266         if( !DirectoryCheck( "share/http" ) )
267         {
268             psz_src = strdup( "share/http" );
269         }
270         else if( !DirectoryCheck( DATA_PATH "/http" ) )
271         {
272             psz_src = strdup( DATA_PATH "/http" );
273         }
274     }
275 #endif
276
277     if( !psz_src || *psz_src == '\0' )
278     {
279         msg_Err( p_intf, "invalid src dir" );
280         goto failed;
281     }
282
283     /* remove trainling \ or / */
284     if( psz_src[strlen( psz_src ) - 1] == '\\' ||
285         psz_src[strlen( psz_src ) - 1] == '/' )
286     {
287         psz_src[strlen( psz_src ) - 1] = '\0';
288     }
289
290     ParseDirectory( p_intf, psz_src, psz_src );
291
292
293     if( p_sys->i_files <= 0 )
294     {
295         msg_Err( p_intf, "cannot find any files (%s)", psz_src );
296         goto failed;
297     }
298     p_intf->pf_run = Run;
299     free( psz_src );
300
301     return VLC_SUCCESS;
302
303 failed:
304     if( psz_src ) free( psz_src );
305     free( p_sys->pp_files );
306     p_sys->p_httpd->pf_unregister_host( p_sys->p_httpd,
307                                         p_sys->p_httpd_host );
308     httpd_Release( p_sys->p_httpd );
309     free( p_sys );
310     return VLC_EGENERIC;
311 }
312
313 /*****************************************************************************
314  * CloseIntf: destroy interface
315  *****************************************************************************/
316 void Close ( vlc_object_t *p_this )
317 {
318     intf_thread_t *p_intf = (intf_thread_t *)p_this;
319     intf_sys_t    *p_sys = p_intf->p_sys;
320
321     int i;
322
323     for( i = 0; i < p_sys->i_files; i++ )
324     {
325        p_sys->p_httpd->pf_unregister_file( p_sys->p_httpd,
326                                            p_sys->pp_files[i]->p_file );
327        /* do not free mime */
328        free( p_sys->pp_files[i]->file );
329        free( p_sys->pp_files[i]->name );
330        free( p_sys->pp_files[i] );
331     }
332     free( p_sys->pp_files );
333     p_sys->p_httpd->pf_unregister_host( p_sys->p_httpd,
334                                         p_sys->p_httpd_host );
335     httpd_Release( p_sys->p_httpd );
336     free( p_sys );
337 }
338
339 /*****************************************************************************
340  * Run: http interface thread
341  *****************************************************************************/
342 static void Run( intf_thread_t *p_intf )
343 {
344     intf_sys_t     *p_sys = p_intf->p_sys;
345
346     while( !p_intf->b_die )
347     {
348         /* get the playlist */
349         if( p_sys->p_playlist == NULL )
350         {
351             p_sys->p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
352         }
353
354         /* Manage the input part */
355         if( p_sys->p_input == NULL )
356         {
357             if( p_sys->p_playlist )
358             {
359                 p_sys->p_input =
360                     vlc_object_find( p_sys->p_playlist,
361                                      VLC_OBJECT_INPUT,
362                                      FIND_CHILD );
363             }
364         }
365         else if( p_sys->p_input->b_dead )
366         {
367             vlc_object_release( p_sys->p_input );
368             p_sys->p_input = NULL;
369         }
370
371
372         /* Wait a bit */
373         msleep( INTF_IDLE_SLEEP );
374     }
375
376     if( p_sys->p_input )
377     {
378         vlc_object_release( p_sys->p_input );
379         p_sys->p_input = NULL;
380     }
381
382     if( p_sys->p_playlist )
383     {
384         vlc_object_release( p_sys->p_playlist );
385         p_sys->p_playlist = NULL;
386     }
387 }
388
389
390 /*****************************************************************************
391  * Local functions
392  *****************************************************************************/
393 #define MAX_DIR_SIZE 10240
394
395 /****************************************************************************
396  * FileToUrl: create a good name for an url from filename
397  ****************************************************************************/
398 static char *FileToUrl( char *name )
399 {
400     char *url, *p;
401
402     url = p = malloc( strlen( name ) + 1 );
403
404     if( !url || !p )
405     {
406         return NULL;
407     }
408
409 #ifdef WIN32
410     while( *name == '\\' || *name == '/' )
411 #else
412     while( *name == '/' )
413 #endif
414     {
415         name++;
416     }
417
418     *p++ = '/';
419     strcpy( p, name );
420
421 #ifdef WIN32
422     /* convert '\\' into '/' */
423     name = p;
424     while( *name )
425     {
426         if( *name == '\\' )
427         {
428             *p++ = '/';
429         }
430         name++;
431     }
432 #endif
433
434     /* index.* -> / */
435     if( ( p = strrchr( url, '/' ) ) != NULL )
436     {
437         if( !strncmp( p, "/index.", 7 ) )
438         {
439             p[1] = '\0';
440         }
441     }
442     return url;
443 }
444
445 /****************************************************************************
446  * FileToMime: XXX duplicated with modules/access_out/http.c
447  ****************************************************************************/
448 static struct
449 {
450     char *psz_ext;
451     char *psz_mime;
452 } http_mime[] =
453 {
454     { ".htm",   "text/html" },
455     { ".html",  "text/html" },
456
457     { ".css",   "text/css" },
458
459     /* media mime */
460     { ".avi",   "video/avi" },
461     { ".asf",   "video/x-ms-asf" },
462     { ".m1a",   "audio/mpeg" },
463     { ".m2a",   "audio/mpeg" },
464     { ".m1v",   "video/mpeg" },
465     { ".m2v",   "video/mpeg" },
466     { ".mp2",   "audio/mpeg" },
467     { ".mp3",   "audio/mpeg" },
468     { ".mpa",   "audio/mpeg" },
469     { ".mpg",   "video/mpeg" },
470     { ".mpeg",  "video/mpeg" },
471     { ".mpe",   "video/mpeg" },
472     { ".mov",   "video/quicktime" },
473     { ".moov",  "video/quicktime" },
474     { ".ogg",   "application/ogg" },
475     { ".ogm",   "application/ogg" },
476     { ".wav",   "audio/wav" },
477
478     /* end */
479     { NULL,     NULL }
480 };
481
482 static char *FileToMime( char *psz_name )
483 {
484     char *psz_ext;
485
486     psz_ext = strrchr( psz_name, '.' );
487     if( psz_ext )
488     {
489         int i;
490
491         for( i = 0; http_mime[i].psz_ext != NULL ; i++ )
492         {
493             if( !strcmp( http_mime[i].psz_ext, psz_ext ) )
494             {
495                 return( http_mime[i].psz_mime );
496             }
497         }
498     }
499     return( "application/octet-stream" );
500 }
501
502 /****************************************************************************
503  * ParseDirectory: parse recursively a directory, adding each file
504  ****************************************************************************/
505 static int ParseDirectory( intf_thread_t *p_intf, char *psz_root,
506                            char *psz_dir )
507 {
508     intf_sys_t     *p_sys = p_intf->p_sys;
509     char           dir[MAX_DIR_SIZE];
510 #ifdef HAVE_SYS_STAT_H
511     struct stat   stat_info;
512 #endif
513     DIR           *p_dir;
514     struct dirent *p_dir_content;
515     FILE          *file;
516
517     char          *user = NULL;
518     char          *password = NULL;
519
520 #ifdef HAVE_SYS_STAT_H
521     if( stat( psz_dir, &stat_info ) == -1 || !S_ISDIR( stat_info.st_mode ) )
522     {
523         return VLC_EGENERIC;
524     }
525 #endif
526
527     if( ( p_dir = opendir( psz_dir ) ) == NULL )
528     {
529         msg_Err( p_intf, "cannot open dir (%s)", psz_dir );
530         return VLC_EGENERIC;
531     }
532
533     msg_Dbg( p_intf, "dir=%s", psz_dir );
534
535     sprintf( dir, "%s/.access", psz_dir );
536     if( ( file = fopen( dir, "r" ) ) != NULL )
537     {
538         char line[1024];
539         int  i_size;
540
541         msg_Dbg( p_intf, "find .access in dir=%s", psz_dir );
542
543         i_size = fread( line, 1, 1023, file );
544         if( i_size > 0 )
545         {
546             char *p;
547             while( i_size > 0 && ( line[i_size-1] == '\n' ||
548                    line[i_size-1] == '\r' ) )
549             {
550                 i_size--;
551             }
552
553             line[i_size] = '\0';
554
555             p = strchr( line, ':' );
556             if( p )
557             {
558                 *p++ = '\0';
559                 user = strdup( line );
560                 password = strdup( p );
561             }
562         }
563         msg_Dbg( p_intf, "using user=%s password=%s (read=%d)",
564                  user, password, i_size );
565
566         fclose( file );
567     }
568
569     for( ;; )
570     {
571         /* parse psz_src dir */
572         if( ( p_dir_content = readdir( p_dir ) ) == NULL )
573         {
574             break;
575         }
576
577         if( p_dir_content->d_name[0] == '.' )
578         {
579             continue;
580         }
581         sprintf( dir, "%s/%s", psz_dir, p_dir_content->d_name );
582         if( ParseDirectory( p_intf, psz_root, dir ) )
583         {
584 #define f p_sys->pp_files[p_sys->i_files]
585             f = malloc( sizeof( httpd_file_callback_args_t ) );
586             if( !f )
587             {
588                 msg_Err( p_intf, "out of memory" );
589                 closedir( p_dir );
590                 return( VLC_ENOMEM );
591             }
592             f->p_intf  = p_intf;
593             f->file = strdup( dir );
594             f->name = FileToUrl( &dir[strlen( psz_root )] );
595             f->mime = FileToMime( &dir[strlen( psz_root )] );
596
597             if( !f->name || !f->mime )
598             {
599                 msg_Err( p_intf , "unable to parse directory" );
600                 closedir( p_dir );
601                 free( f );
602                 return( VLC_ENOMEM );
603             }
604             msg_Dbg( p_intf, "file=%s (url=%s mime=%s)",
605                      f->file, f->name, f->mime );
606
607             f->p_file =
608                 p_sys->p_httpd->pf_register_file( p_sys->p_httpd,
609                                                   f->name, f->mime,
610                                                   user, password,
611                                                   http_get, http_get,
612                                                   f );
613             if( f->p_file )
614             {
615                 p_sys->i_files++;
616                 p_sys->pp_files = realloc( p_sys->pp_files,
617                   (p_sys->i_files+1) * sizeof( httpd_file_callback_args_t ) );
618             }
619 #define fold p_sys->pp_files[p_sys->i_files-1]
620
621             /* FIXME for rep/ add rep (it would be better to do a redirection) */
622             if( p_sys->i_files && strlen(fold->name) > 1 &&
623                 fold->name[strlen(fold->name) - 1] == '/' )
624             {
625                 f = malloc( sizeof( httpd_file_callback_args_t ) );
626                 if( !f )
627                 {
628                     msg_Err( p_intf, "out of memory" );
629                     closedir( p_dir );
630                     return( VLC_ENOMEM );
631                 }
632                 f->p_intf  = p_intf;
633                 f->file = strdup( fold->file );
634                 f->name = strdup( fold->name );
635                 f->mime = fold->mime;
636
637                 f->name[strlen(f->name) - 1] = '\0';
638                 msg_Dbg( p_intf, "file=%s (url=%s mime=%s)", f->file, f->name,
639                          f->mime );
640                 f->p_file =
641                     p_sys->p_httpd->pf_register_file( p_sys->p_httpd,
642                                                       f->name, f->mime,
643                                                       user, password,
644                                                       http_get, http_get,
645                                                       f );
646                 if( f->p_file )
647                 {
648                     p_sys->i_files++;
649                     p_sys->pp_files =
650                         realloc( p_sys->pp_files, (p_sys->i_files+1) *
651                                  sizeof( httpd_file_callback_args_t ) );
652                 }
653             }
654 #undef fold
655 #undef f
656         }
657     }
658
659     if( user )
660     {
661         free( user );
662     }
663     if( password )
664     {
665         free( password );
666     }
667
668     closedir( p_dir );
669
670     return VLC_SUCCESS;
671 }
672
673 /****************************************************************************
674  * var and set handling
675  ****************************************************************************/
676
677 static mvar_t *mvar_New( char *name, char *value )
678 {
679     mvar_t *v = malloc( sizeof( mvar_t ) );
680
681     if( !v ) return NULL;
682     v->name = strdup( name );
683     v->value = strdup( value ? value : "" );
684
685     v->i_field = 0;
686     v->field = malloc( sizeof( mvar_t * ) );
687     v->field[0] = NULL;
688
689     return v;
690 }
691
692 static void mvar_Delete( mvar_t *v )
693 {
694     int i;
695
696     free( v->name );
697     free( v->value );
698
699     for( i = 0; i < v->i_field; i++ )
700     {
701         mvar_Delete( v->field[i] );
702     }
703     free( v->field );
704     free( v );
705 }
706
707 static void mvar_AppendVar( mvar_t *v, mvar_t *f )
708 {
709     v->field = realloc( v->field, sizeof( mvar_t * ) * ( v->i_field + 2 ) );
710     v->field[v->i_field] = f;
711     v->i_field++;
712 }
713
714 static mvar_t *mvar_Duplicate( mvar_t *v )
715 {
716     int i;
717     mvar_t *n;
718
719     n = mvar_New( v->name, v->value );
720     for( i = 0; i < v->i_field; i++ )
721     {
722         mvar_AppendVar( n, mvar_Duplicate( v->field[i] ) );
723     }
724
725     return n;
726 }
727
728 static void mvar_PushVar( mvar_t *v, mvar_t *f )
729 {
730     v->field = realloc( v->field, sizeof( mvar_t * ) * ( v->i_field + 2 ) );
731     if( v->i_field > 0 )
732     {
733         memmove( &v->field[1], &v->field[0], sizeof( mvar_t * ) * v->i_field );
734     }
735     v->field[0] = f;
736     v->i_field++;
737 }
738
739 static void mvar_RemoveVar( mvar_t *v, mvar_t *f )
740 {
741     int i;
742     for( i = 0; i < v->i_field; i++ )
743     {
744         if( v->field[i] == f )
745         {
746             break;
747         }
748     }
749     if( i >= v->i_field )
750     {
751         return;
752     }
753
754     if( i + 1 < v->i_field )
755     {
756         memmove( &v->field[i], &v->field[i+1], sizeof( mvar_t * ) * ( v->i_field - i - 1 ) );
757     }
758     v->i_field--;
759     /* FIXME should do a realloc */
760 }
761
762 static mvar_t *mvar_GetVar( mvar_t *s, char *name )
763 {
764     int i;
765     char base[512], *field, *p;
766     int  i_index;
767
768     /* format: name[index].field */
769
770     field = strchr( name, '.' );
771     if( field )
772     {
773         int i = field - name;
774         strncpy( base, name, i );
775         base[i] = '\0';
776         field++;
777     }
778     else
779     {
780         strcpy( base, name );
781     }
782
783     if( ( p = strchr( base, '[' ) ) )
784     {
785         *p++ = '\0';
786         sscanf( p, "%d]", &i_index );
787         if( i_index < 0 )
788         {
789             return NULL;
790         }
791     }
792     else
793     {
794         i_index = 0;
795     }
796
797     for( i = 0; i < s->i_field; i++ )
798     {
799         if( !strcmp( s->field[i]->name, base ) )
800         {
801             if( i_index > 0 )
802             {
803                 i_index--;
804             }
805             else
806             {
807                 if( field )
808                 {
809                     return mvar_GetVar( s->field[i], field );
810                 }
811                 else
812                 {
813                     return s->field[i];
814                 }
815             }
816         }
817     }
818     return NULL;
819 }
820
821
822
823 static char *mvar_GetValue( mvar_t *v, char *field )
824 {
825     if( *field == '\0' )
826     {
827         return v->value;
828     }
829     else
830     {
831         mvar_t *f = mvar_GetVar( v, field );
832         if( f )
833         {
834             return f->value;
835         }
836         else
837         {
838             return field;
839         }
840     }
841 }
842
843 static void mvar_PushNewVar( mvar_t *vars, char *name, char *value )
844 {
845     mvar_t *f = mvar_New( name, value );
846     mvar_PushVar( vars, f );
847 }
848
849 static void mvar_AppendNewVar( mvar_t *vars, char *name, char *value )
850 {
851     mvar_t *f = mvar_New( name, value );
852     mvar_AppendVar( vars, f );
853 }
854
855
856 /* arg= start[:stop[:step]],.. */
857 static mvar_t *mvar_IntegerSetNew( char *name, char *arg )
858 {
859     char *dup = strdup( arg );
860     char *str = dup;
861     mvar_t *s = mvar_New( name, "set" );
862
863     fprintf( stderr," mvar_IntegerSetNew: name=`%s' arg=`%s'\n", name, str );
864
865     while( str )
866     {
867         char *p;
868         int  i_start,i_stop,i_step;
869         int  i_match;
870
871         p = strchr( str, ',' );
872         if( p )
873         {
874             *p++ = '\0';
875         }
876
877         i_step = 0;
878         i_match = sscanf( str, "%d:%d:%d", &i_start, &i_stop, &i_step );
879         fprintf( stderr," mvar_IntegerSetNew: m=%d start=%d stop=%d step=%d\n", i_match, i_start, i_stop, i_step );
880
881         if( i_match == 1 )
882         {
883             i_stop = i_start;
884             i_step = 1;
885         }
886         else if( i_match == 2 )
887         {
888             i_step = i_start < i_stop ? 1 : -1;
889         }
890
891         if( i_match >= 1 )
892         {
893             int i;
894
895             if( ( i_start < i_stop && i_step > 0 ) ||
896                 ( i_start > i_stop && i_step < 0 ) )
897             {
898                 for( i = i_start; ; i += i_step )
899                 {
900                     char   value[512];
901
902                     if( ( i_step > 0 && i > i_stop ) ||
903                         ( i_step < 0 && i < i_stop ) )
904                     {
905                         break;
906                     }
907
908                     fprintf( stderr," mvar_IntegerSetNew: adding %d\n", i );
909                     sprintf( value, "%d", i );
910
911                     mvar_PushNewVar( s, name, value );
912                 }
913             }
914         }
915         str = p;
916     }
917
918     free( dup );
919     return s;
920 }
921
922 static mvar_t *mvar_PlaylistSetNew( char *name, playlist_t *p_pl )
923 {
924     mvar_t *s = mvar_New( name, "set" );
925     int    i;
926
927     fprintf( stderr," mvar_PlaylistSetNew: name=`%s'\n", name );
928
929     vlc_mutex_lock( &p_pl->object_lock );
930     for( i = 0; i < p_pl->i_size; i++ )
931     {
932         mvar_t *itm = mvar_New( name, "set" );
933         char   value[512];
934
935         sprintf( value, "%d", i == p_pl->i_index ? 1 : 0 );
936         mvar_AppendNewVar( itm, "current", value );
937
938         sprintf( value, "%d", i );
939         mvar_AppendNewVar( itm, "index", value );
940
941         mvar_AppendNewVar( itm, "name", p_pl->pp_items[i]->psz_name );
942
943         sprintf( value, "%d", p_pl->pp_items[i]->i_group );
944         mvar_AppendNewVar( itm, "group", value );
945
946         mvar_AppendVar( s, itm );
947     }
948     vlc_mutex_unlock( &p_pl->object_lock );
949
950     return s;
951 }
952
953 static mvar_t *mvar_InfoSetNew( char *name, input_thread_t *p_input )
954 {
955     mvar_t *s = mvar_New( name, "set" );
956
957     input_info_category_t * p_category;
958     input_info_t * p_info;
959
960     fprintf( stderr," mvar_InfoSetNew: name=`%s'\n", name );
961     if( p_input == NULL )
962     {
963         return s;
964     }
965
966     vlc_mutex_lock( &p_input->stream.stream_lock );
967     p_category = p_input->stream.p_info;
968     while ( p_category )
969     {
970         mvar_t *cat  = mvar_New( name, "set" );
971         mvar_t *iset = mvar_New( "info", "set" );
972
973         mvar_AppendNewVar( cat, "name", p_category->psz_name );
974         mvar_AppendVar( cat, iset );
975
976         p_info = p_category->p_info;
977         while ( p_info )
978         {
979             mvar_t *info = mvar_New( "info", "" );
980
981             msg_Dbg( p_input, "adding info name=%s value=%s", p_info->psz_name, p_info->psz_value );
982             mvar_AppendNewVar( info, "name",  p_info->psz_name );
983             mvar_AppendNewVar( info, "value", p_info->psz_value );
984             mvar_AppendVar( iset, info );
985             p_info = p_info->p_next;
986         }
987         mvar_AppendVar( s, cat );
988         p_category = p_category->p_next;
989     }
990     vlc_mutex_unlock( &p_input->stream.stream_lock );
991
992     return s;
993 }
994
995 static mvar_t *mvar_HttpdInfoSetNew( char *name, httpd_t *p_httpd, int i_type )
996 {
997     mvar_t       *s = mvar_New( name, "set" );
998     httpd_info_t info;
999     int          i;
1000
1001     fprintf( stderr," mvar_HttpdInfoSetNew: name=`%s'\n", name );
1002     if( !p_httpd->pf_control( p_httpd, i_type, &info, NULL ) )
1003     {
1004         for( i= 0; i < info.i_count; )
1005         {
1006             mvar_t *inf;
1007
1008             inf = mvar_New( name, "set" );
1009             do
1010             {
1011                 /* fprintf( stderr," mvar_HttpdInfoSetNew: append name=`%s' value=`%s'\n",
1012                             info.info[i].psz_name, info.info[i].psz_value ); */
1013                 mvar_AppendNewVar( inf,
1014                                    info.info[i].psz_name,
1015                                    info.info[i].psz_value );
1016                 i++;
1017             } while( i < info.i_count && strcmp( info.info[i].psz_name, "id" ) );
1018             mvar_AppendVar( s, inf );
1019         }
1020     }
1021
1022     /* free mem */
1023     for( i = 0; i < info.i_count; i++ )
1024     {
1025         free( info.info[i].psz_name );
1026         free( info.info[i].psz_value );
1027     }
1028     if( info.i_count > 0 )
1029     {
1030         free( info.info );
1031     }
1032
1033     return s;
1034 }
1035 static mvar_t *mvar_FileSetNew( char *name, char *psz_dir )
1036 {
1037     mvar_t *s = mvar_New( name, "set" );
1038     char           tmp[MAX_DIR_SIZE], *p, *src;
1039 #ifdef HAVE_SYS_STAT_H
1040     struct stat   stat_info;
1041 #endif
1042     DIR           *p_dir;
1043     struct dirent *p_dir_content;
1044     char          sep;
1045
1046     /* convert all / to native separator */
1047 #if defined( WIN32 )
1048     while( (p = strchr( psz_dir, '/' )) )
1049     {
1050         *p = '\\';
1051     }
1052     sep = '\\';
1053 #else
1054     sep = '/';
1055 #endif
1056
1057     /* remove trailling separator */
1058     while( strlen( psz_dir ) > 1 &&
1059 #if defined( WIN32 )
1060            !( strlen(psz_dir)==3 && psz_dir[1]==':' && psz_dir[2]==sep ) &&
1061 #endif
1062            psz_dir[strlen( psz_dir ) -1 ] == sep )
1063     {
1064         psz_dir[strlen( psz_dir ) -1 ]  ='\0';
1065     }
1066     /* remove double separator */
1067     for( p = src = psz_dir; *src != '\0'; src++, p++ )
1068     {
1069         if( src[0] == sep && src[1] == sep )
1070         {
1071             src++;
1072         }
1073         *p = *src;
1074     }
1075     *p = '\0';
1076
1077     if( *psz_dir == '\0' )
1078     {
1079         return s;
1080     }
1081     /* first fix all .. dir */
1082     p = src = psz_dir;
1083     while( *src )
1084     {
1085         if( src[0] == '.' && src[1] == '.' )
1086         {
1087             src += 2;
1088             if( p <= &psz_dir[1] )
1089             {
1090                 continue;
1091             }
1092
1093             p -= 2;
1094
1095             while( p > &psz_dir[1] && *p != sep )
1096             {
1097                 p--;
1098             }
1099         }
1100         else if( *src == sep )
1101         {
1102             if( p > psz_dir && p[-1] == sep )
1103             {
1104                 src++;
1105             }
1106             else
1107             {
1108                 *p++ = *src++;
1109             }
1110         }
1111         else
1112         {
1113             do
1114             {
1115                 *p++ = *src++;
1116             } while( *src && *src != sep );
1117         }
1118     }
1119     *p = '\0';
1120
1121     fprintf( stderr," mvar_FileSetNew: name=`%s' dir=`%s'\n", name, psz_dir );
1122
1123 #ifdef HAVE_SYS_STAT_H
1124     if( stat( psz_dir, &stat_info ) == -1 || !S_ISDIR( stat_info.st_mode ) )
1125     {
1126         return s;
1127     }
1128 #endif
1129
1130     if( ( p_dir = opendir( psz_dir ) ) == NULL )
1131     {
1132         fprintf( stderr, "cannot open dir (%s)", psz_dir );
1133         return s;
1134     }
1135
1136     /* remove traling / or \ */
1137     for( p = &psz_dir[strlen( psz_dir) - 1]; p >= psz_dir && ( *p =='/' || *p =='\\' ); p-- )
1138     {
1139         *p = '\0';
1140     }
1141
1142     for( ;; )
1143     {
1144         mvar_t *f;
1145
1146         /* parse psz_src dir */
1147         if( ( p_dir_content = readdir( p_dir ) ) == NULL )
1148         {
1149             break;
1150         }
1151         if( !strcmp( p_dir_content->d_name, "." ) )
1152         {
1153             continue;
1154         }
1155
1156 #if defined( WIN32 )
1157         sprintf( tmp, "%s\\%s", psz_dir, p_dir_content->d_name );
1158 #else
1159         sprintf( tmp, "%s/%s", psz_dir, p_dir_content->d_name );
1160 #endif
1161
1162 #ifdef HAVE_SYS_STAT_H
1163         if( stat( tmp, &stat_info ) == -1 )
1164         {
1165             continue;
1166         }
1167 #endif
1168         f = mvar_New( name, "set" );
1169         mvar_AppendNewVar( f, "name", tmp );
1170 #ifdef HAVE_SYS_STAT_H
1171         if( S_ISDIR( stat_info.st_mode ) )
1172         {
1173             mvar_AppendNewVar( f, "type", "directory" );
1174         }
1175         else if( S_ISREG( stat_info.st_mode ) )
1176         {
1177             mvar_AppendNewVar( f, "type", "file" );
1178         }
1179         else
1180         {
1181             mvar_AppendNewVar( f, "type", "unknown" );
1182         }
1183
1184         sprintf( tmp, "%lld", stat_info.st_size );
1185         mvar_AppendNewVar( f, "size", tmp );
1186
1187         /* FIXME memory leak FIXME */
1188 #ifdef HAVE_CTIME_R
1189         ctime_r( &stat_info.st_mtime, tmp );
1190         mvar_AppendNewVar( f, "date", tmp );
1191 #else
1192         mvar_AppendNewVar( f, "date", ctime( &stat_info.st_mtime ) );
1193 #endif
1194
1195 #else
1196         mvar_AppendNewVar( f, "type", "unknown" );
1197         mvar_AppendNewVar( f, "size", "unknown" );
1198         mvar_AppendNewVar( f, "date", "unknown" );
1199 #endif
1200         mvar_AppendVar( s, f );
1201     }
1202
1203     return s;
1204 }
1205
1206 static void SSInit( rpn_stack_t * );
1207 static void SSClean( rpn_stack_t * );
1208 static void EvaluateRPN( mvar_t  *, rpn_stack_t *, char * );
1209
1210 static void SSPush  ( rpn_stack_t *, char * );
1211 static char *SSPop  ( rpn_stack_t * );
1212
1213 static void SSPushN ( rpn_stack_t *, int );
1214 static int  SSPopN  ( rpn_stack_t *, mvar_t  * );
1215
1216
1217 /****************************************************************************
1218  * Macro handling
1219  ****************************************************************************/
1220 typedef struct
1221 {
1222     char *id;
1223     char *param1;
1224     char *param2;
1225 } macro_t;
1226
1227 static int FileLoad( FILE *f, uint8_t **pp_data, int *pi_data )
1228 {
1229     int i_read;
1230
1231     /* just load the file */
1232     *pi_data = 0;
1233     *pp_data = malloc( 1025 );  /* +1 for \0 */
1234     while( ( i_read = fread( &(*pp_data)[*pi_data], 1, 1024, f ) ) == 1024 )
1235     {
1236         *pi_data += 1024;
1237         *pp_data = realloc( *pp_data, *pi_data  + 1025 );
1238     }
1239     if( i_read > 0 )
1240     {
1241         *pi_data += i_read;
1242     }
1243     (*pp_data)[*pi_data] = '\0';
1244
1245     return VLC_SUCCESS;
1246 }
1247
1248 static int MacroParse( macro_t *m, uint8_t *psz_src )
1249 {
1250     uint8_t *dup = strdup( psz_src );
1251     uint8_t *src = dup;
1252     uint8_t *p;
1253     int     i_skip;
1254
1255 #define EXTRACT( name, l ) \
1256         src += l;    \
1257         p = strchr( src, '"' );             \
1258         if( p )                             \
1259         {                                   \
1260             *p++ = '\0';                    \
1261         }                                   \
1262         m->name = strdup( src );            \
1263         if( !p )                            \
1264         {                                   \
1265             break;                          \
1266         }                                   \
1267         src = p;
1268
1269     /* init m */
1270     m->id = NULL;
1271     m->param1 = NULL;
1272     m->param2 = NULL;
1273
1274     /* parse */
1275     src += 4;
1276
1277     while( *src )
1278     {
1279         while( *src == ' ')
1280         {
1281             src++;
1282         }
1283         if( !strncmp( src, "id=\"", 4 ) )
1284         {
1285             EXTRACT( id, 4 );
1286         }
1287         else if( !strncmp( src, "param1=\"", 8 ) )
1288         {
1289             EXTRACT( param1, 8 );
1290         }
1291         else if( !strncmp( src, "param2=\"", 8 ) )
1292         {
1293             EXTRACT( param2, 8 );
1294         }
1295         else
1296         {
1297             break;
1298         }
1299     }
1300     if( strstr( src, "/>" ) )
1301     {
1302         src = strstr( src, "/>" ) + 2;
1303     }
1304     else
1305     {
1306         src += strlen( src );
1307     }
1308
1309     if( m->id == NULL )
1310     {
1311         m->id = strdup( "" );
1312     }
1313     if( m->param1 == NULL )
1314     {
1315         m->param1 = strdup( "" );
1316     }
1317     if( m->param2 == NULL )
1318     {
1319         m->param2 = strdup( "" );
1320     }
1321     i_skip = src - dup;
1322
1323     free( dup );
1324     return i_skip;
1325 #undef EXTRACT
1326 }
1327
1328 static void MacroClean( macro_t *m )
1329 {
1330     free( m->id );
1331     free( m->param1 );
1332     free( m->param2 );
1333 }
1334
1335 enum macroType
1336 {
1337     MVLC_UNKNOWN = 0,
1338     MVLC_CONTROL,
1339         MVLC_PLAY,
1340         MVLC_STOP,
1341         MVLC_PAUSE,
1342         MVLC_NEXT,
1343         MVLC_PREVIOUS,
1344         MVLC_ADD,
1345         MVLC_DEL,
1346         MVLC_EMPTY,
1347         MVLC_SEEK,
1348         MVLC_KEEP,
1349         MVLC_SORT,
1350         MVLC_VOLUME,
1351         MVLC_FULLSCREEN,
1352
1353         MVLC_CLOSE,
1354         MVLC_SHUTDOWN,
1355     MVLC_FOREACH,
1356     MVLC_IF,
1357     MVLC_RPN,
1358     MVLC_ELSE,
1359     MVLC_END,
1360     MVLC_GET,
1361     MVLC_SET,
1362         MVLC_INT,
1363         MVLC_FLOAT,
1364         MVLC_STRING,
1365
1366     MVLC_VALUE
1367 };
1368
1369 static struct
1370 {
1371     char *psz_name;
1372     int  i_type;
1373 }
1374 StrToMacroTypeTab [] =
1375 {
1376     { "control",    MVLC_CONTROL },
1377         /* player control */
1378         { "play",           MVLC_PLAY },
1379         { "stop",           MVLC_STOP },
1380         { "pause",          MVLC_PAUSE },
1381         { "next",           MVLC_NEXT },
1382         { "previous",       MVLC_PREVIOUS },
1383         { "seek",           MVLC_SEEK },
1384         { "keep",           MVLC_KEEP },
1385         { "fullscreen",     MVLC_FULLSCREEN },
1386         { "volume",         MVLC_VOLUME },
1387
1388         /* playlist management */
1389         { "add",            MVLC_ADD },
1390         { "delete",         MVLC_DEL },
1391         { "empty",          MVLC_EMPTY },
1392         { "sort",           MVLC_SORT },
1393
1394         /* admin control */
1395         { "close",          MVLC_CLOSE },
1396         { "shutdown",       MVLC_SHUTDOWN },
1397
1398     { "rpn",        MVLC_RPN },
1399
1400     { "foreach",    MVLC_FOREACH },
1401     { "value",      MVLC_VALUE },
1402
1403     { "if",         MVLC_IF },
1404     { "else",       MVLC_ELSE },
1405     { "end",        MVLC_END },
1406     { "get",         MVLC_GET },
1407     { "set",         MVLC_SET },
1408         { "int",            MVLC_INT },
1409         { "float",          MVLC_FLOAT },
1410         { "string",         MVLC_STRING },
1411
1412     /* end */
1413     { NULL,         MVLC_UNKNOWN }
1414 };
1415
1416 static int StrToMacroType( char *name )
1417 {
1418     int i;
1419
1420     if( !name || *name == '\0')
1421     {
1422         return MVLC_UNKNOWN;
1423     }
1424     for( i = 0; StrToMacroTypeTab[i].psz_name != NULL; i++ )
1425     {
1426         if( !strcmp( name, StrToMacroTypeTab[i].psz_name ) )
1427         {
1428             return StrToMacroTypeTab[i].i_type;
1429         }
1430     }
1431     return MVLC_UNKNOWN;
1432 }
1433
1434 static void MacroDo( httpd_file_callback_args_t *p_args,
1435                      macro_t *m,
1436                      uint8_t *p_request, int i_request,
1437                      uint8_t **pp_data,  int *pi_data,
1438                      uint8_t **pp_dst )
1439 {
1440     intf_thread_t  *p_intf = p_args->p_intf;
1441     intf_sys_t     *p_sys = p_args->p_intf->p_sys;
1442     char control[512];
1443
1444 #define ALLOC( l ) \
1445     {               \
1446         int __i__ = *pp_dst - *pp_data; \
1447         *pi_data += (l);                  \
1448         *pp_data = realloc( *pp_data, *pi_data );   \
1449         *pp_dst = (*pp_data) + __i__;   \
1450     }
1451 #define PRINT( str ) \
1452     ALLOC( strlen( str ) + 1 ); \
1453     *pp_dst += sprintf( *pp_dst, str );
1454
1455 #define PRINTS( str, s ) \
1456     ALLOC( strlen( str ) + strlen( s ) + 1 ); \
1457     { \
1458         char * psz_cur = *pp_dst; \
1459         *pp_dst += sprintf( *pp_dst, str, s ); \
1460         while( psz_cur && *psz_cur ) \
1461         {  \
1462             /* Prevent script injection */ \
1463             if( *psz_cur == '<' ) *psz_cur = '*'; \
1464             if( *psz_cur == '>' ) *psz_cur = '*'; \
1465             psz_cur++ ; \
1466         } \
1467     }
1468
1469     switch( StrToMacroType( m->id ) )
1470     {
1471         case MVLC_CONTROL:
1472             if( i_request <= 0 )
1473             {
1474                 break;
1475             }
1476             uri_extract_value( p_request, "control", control, 512 );
1477             if( *m->param1 && !strstr( m->param1, control ) )
1478             {
1479                 msg_Warn( p_intf, "unauthorized control=%s", control );
1480                 break;
1481             }
1482             switch( StrToMacroType( control ) )
1483             {
1484                 case MVLC_PLAY:
1485                 {
1486                     int i_item;
1487                     char item[512];
1488
1489                     uri_extract_value( p_request, "item", item, 512 );
1490                     i_item = atoi( item );
1491                     playlist_Command( p_sys->p_playlist, PLAYLIST_GOTO, i_item );
1492                     msg_Dbg( p_intf, "requested playlist item: %i", i_item );
1493                     break;
1494                 }
1495                 case MVLC_STOP:
1496                     playlist_Command( p_sys->p_playlist, PLAYLIST_STOP, 0 );
1497                     msg_Dbg( p_intf, "requested playlist stop" );
1498                     break;
1499                 case MVLC_PAUSE:
1500                     playlist_Command( p_sys->p_playlist, PLAYLIST_PAUSE, 0 );
1501                     msg_Dbg( p_intf, "requested playlist pause" );
1502                     break;
1503                 case MVLC_NEXT:
1504                     playlist_Command( p_sys->p_playlist, PLAYLIST_GOTO,
1505                                       p_sys->p_playlist->i_index + 1 );
1506                     msg_Dbg( p_intf, "requested playlist next" );
1507                     break;
1508                 case MVLC_PREVIOUS:
1509                     playlist_Command( p_sys->p_playlist, PLAYLIST_GOTO,
1510                                       p_sys->p_playlist->i_index - 1 );
1511                     msg_Dbg( p_intf, "requested playlist next" );
1512                     break;
1513                 case MVLC_FULLSCREEN:
1514                 {
1515                     if( p_sys->p_input )
1516                     {
1517                         vout_thread_t *p_vout;
1518                         p_vout = vlc_object_find( p_sys->p_input,
1519                                                   VLC_OBJECT_VOUT, FIND_CHILD );
1520
1521                         if( p_vout )
1522                         {
1523                             p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
1524                             vlc_object_release( p_vout );
1525                             msg_Dbg( p_intf, "requested fullscreen toggle" );
1526                         }
1527                     }
1528                 }
1529                 break;
1530                 case MVLC_SEEK:
1531                 {
1532                     vlc_value_t val;
1533                     char value[30];
1534                     char * p_value;
1535                     int i_stock = 0;
1536                     uint64_t i_length;
1537                     int i_value = 0;
1538                     int i_relative = 0;
1539 #define POSITION_ABSOLUTE 12
1540 #define POSITION_REL_FOR 13
1541 #define POSITION_REL_BACK 11
1542 #define TIME_ABSOLUTE 0
1543 #define TIME_REL_FOR 1
1544 #define TIME_REL_BACK -1
1545                     if( p_sys->p_input )
1546                     {
1547                         uri_extract_value( p_request, "seek_value", value, 20 );
1548                         uri_decode_url_encoded( value );
1549                         p_value = value;
1550                         var_Get( p_sys->p_input, "length", &val);
1551                         i_length = val.i_time;
1552
1553                         while( p_value[0] != '\0' )
1554                         {
1555                             switch(p_value[0])
1556                             {
1557                                 case '+':
1558                                 {
1559                                     i_relative = TIME_REL_FOR;
1560                                     p_value++;
1561                                     break;
1562                                 }
1563                                 case '-':
1564                                 {
1565                                     i_relative = TIME_REL_BACK;
1566                                     p_value++;
1567                                     break;
1568                                 }
1569                                 case '0': case '1': case '2': case '3': case '4':
1570                                 case '5': case '6': case '7': case '8': case '9':
1571                                 {
1572                                     i_stock = strtol( p_value , &p_value , 10 );
1573                                     break;
1574                                 }
1575                                 case '%': /* for percentage ie position */
1576                                 {
1577                                     i_relative += POSITION_ABSOLUTE;
1578                                     i_value = i_stock;
1579                                     i_stock = 0;
1580                                     p_value[0] = '\0';
1581                                     break;
1582                                 }
1583                                 case ':':
1584                                 {
1585                                     i_value = 60 * (i_value + i_stock) ;
1586                                     i_stock = 0;
1587                                     p_value++;
1588                                     break;
1589                                 }
1590                                 case 'h': case 'H': /* hours */
1591                                 {
1592                                     i_value += 3600 * i_stock;
1593                                     i_stock = 0;
1594                                     /* other characters which are not numbers are not important */
1595                                     while( ((p_value[0] < '0') || (p_value[0] > '9')) && (p_value[0] != '\0') )
1596                                     {
1597                                         p_value++;
1598                                     }
1599                                     break;
1600                                 }
1601                                 case 'm': case 'M': case '\'': /* minutes */
1602                                 {
1603                                     i_value += 60 * i_stock;
1604                                     i_stock = 0;
1605                                     p_value++;
1606                                     while( ((p_value[0] < '0') || (p_value[0] > '9')) && (p_value[0] != '\0') )
1607                                     {
1608                                         p_value++;
1609                                     }
1610                                     break;
1611                                 }
1612                                 case 's': case 'S': case '"':  /* seconds */
1613                                 {
1614                                     i_value += i_stock;
1615                                     i_stock = 0;
1616                                     while( ((p_value[0] < '0') || (p_value[0] > '9')) && (p_value[0] != '\0') )
1617                                     {
1618                                         p_value++;
1619                                     }
1620                                     break;
1621                                 }
1622                                 default:
1623                                 {
1624                                     p_value++;
1625                                     break;
1626                                 }
1627                             }
1628                         }
1629
1630                         /* if there is no known symbol, I consider it as seconds. Otherwise, i_stock = 0 */
1631                         i_value += i_stock;
1632
1633                         switch(i_relative)
1634                         {
1635                             case TIME_ABSOLUTE:
1636                             {
1637                                 if( (uint64_t)( i_value ) * 1000000 <= i_length )
1638                                     val.i_time = (uint64_t)( i_value ) * 1000000;
1639                                 else
1640                                     val.i_time = i_length;
1641
1642                                 var_Set( p_sys->p_input, "time", val );
1643                                 msg_Dbg( p_intf, "requested seek position: %dsec", i_value );
1644                                 break;
1645                             }
1646                             case TIME_REL_FOR:
1647                             {
1648                                 var_Get( p_sys->p_input, "time", &val );
1649                                 if( (uint64_t)( i_value ) * 1000000 + val.i_time <= i_length )
1650                                 {
1651                                     val.i_time = ((uint64_t)( i_value ) * 1000000) + val.i_time;
1652                                 } else
1653                                 {
1654                                     val.i_time = i_length;
1655                                 }
1656                                 var_Set( p_sys->p_input, "time", val );
1657                                 msg_Dbg( p_intf, "requested seek position forward: %dsec", i_value );
1658                                 break;
1659                             }
1660                             case TIME_REL_BACK:
1661                             {
1662                                 var_Get( p_sys->p_input, "time", &val );
1663                                 if( (int64_t)( i_value ) * 1000000 > val.i_time )
1664                                 {
1665                                     val.i_time = 0;
1666                                 } else
1667                                 {
1668                                     val.i_time = val.i_time - ((uint64_t)( i_value ) * 1000000);
1669                                 }
1670                                 var_Set( p_sys->p_input, "time", val );
1671                                 msg_Dbg( p_intf, "requested seek position backward: %dsec", i_value );
1672                                 break;
1673                             }
1674                             case POSITION_ABSOLUTE:
1675                             {
1676                                 val.f_float = __MIN( __MAX( ((float) i_value ) / 100.0 , 0.0 ) , 100.0 );
1677                                 var_Set( p_sys->p_input, "position", val );
1678                                 msg_Dbg( p_intf, "requested seek percent: %d", i_value );
1679                                 break;
1680                             }
1681                             case POSITION_REL_FOR:
1682                             {
1683                                 var_Get( p_sys->p_input, "position", &val );
1684                                 val.f_float += __MIN( __MAX( ((float) i_value ) / 100.0 , 0.0 ) , 100.0 );
1685                                 var_Set( p_sys->p_input, "position", val );
1686                                 msg_Dbg( p_intf, "requested seek percent forward: %d", i_value );
1687                                 break;
1688                             }
1689                             case POSITION_REL_BACK:
1690                             {
1691                                 var_Get( p_sys->p_input, "position", &val );
1692                                 val.f_float -= __MIN( __MAX( ((float) i_value ) / 100.0 , 0.0 ) , 100.0 );
1693                                 var_Set( p_sys->p_input, "position", val );
1694                                 msg_Dbg( p_intf, "requested seek percent backward: %d", i_value );
1695                                 break;
1696                             }
1697                             default:
1698                             {
1699                                 msg_Dbg( p_intf, "requested seek: what the f*** is going on here ?" );
1700                                 break;
1701                             }
1702                         }
1703                     }
1704 #undef POSITION_ABSOLUTE
1705 #undef POSITION_REL_FOR
1706 #undef POSITION_REL_BACK
1707 #undef TIME_ABSOLUTE
1708 #undef TIME_REL_FOR
1709 #undef TIME_REL_BACK
1710                     break;
1711                 }
1712                 case MVLC_VOLUME:
1713                 {
1714                     char vol[8];
1715                     audio_volume_t i_volume;
1716                     int i_value;
1717
1718                     uri_extract_value( p_request, "value", vol, 8 );
1719                     aout_VolumeGet( p_intf, &i_volume );
1720                     uri_decode_url_encoded( vol );
1721
1722                     if( vol[0] == '+' )
1723                     {
1724                         i_value = atoi( vol + 1 );
1725                         if( (i_volume + i_value) > AOUT_VOLUME_MAX )
1726                         {
1727                             aout_VolumeSet( p_intf , AOUT_VOLUME_MAX );
1728                             msg_Dbg( p_intf, "requested volume set: max" );
1729                         } else
1730                         {
1731                             aout_VolumeSet( p_intf , (i_volume + i_value) );
1732                             msg_Dbg( p_intf, "requested volume set: +%i", (i_volume + i_value) );
1733                         }
1734                     } else
1735                     if( vol[0] == '-' )
1736                     {
1737                         i_value = atoi( vol + 1 );
1738                         if( (i_volume - i_value) < AOUT_VOLUME_MIN )
1739                         {
1740                             aout_VolumeSet( p_intf , AOUT_VOLUME_MIN );
1741                             msg_Dbg( p_intf, "requested volume set: min" );
1742                         } else
1743                         {
1744                             aout_VolumeSet( p_intf , (i_volume - i_value) );
1745                             msg_Dbg( p_intf, "requested volume set: -%i", (i_volume - i_value) );
1746                         }
1747                     } else
1748                     {
1749                         i_value = atoi( vol );
1750                         if( ( i_value <= AOUT_VOLUME_MAX ) && ( i_value >= AOUT_VOLUME_MIN ) )
1751                         {
1752                             aout_VolumeSet( p_intf , atoi( vol ) );
1753                             msg_Dbg( p_intf, "requested volume set: %i", atoi( vol ) );
1754                         }
1755                     }
1756                     break;
1757                 }
1758
1759                 /* playlist management */
1760                 case MVLC_ADD:
1761                 {
1762                     char mrl[512];
1763                     playlist_item_t * p_item;
1764
1765                     uri_extract_value( p_request, "mrl", mrl, 512 );
1766                     uri_decode_url_encoded( mrl );
1767                     p_item = parse_MRL( mrl );
1768
1769                     if( !p_item || !p_item->psz_uri || !*p_item->psz_uri )
1770                     {
1771                         msg_Dbg( p_intf, "invalid requested mrl: %s", mrl );
1772                     } else
1773                     {
1774                         playlist_AddItem( p_sys->p_playlist , p_item ,
1775                                           PLAYLIST_APPEND, PLAYLIST_END );
1776                         msg_Dbg( p_intf, "requested mrl add: %s", mrl );
1777                     }
1778
1779                     break;
1780                 }
1781                 case MVLC_DEL:
1782                 {
1783                     int i_item, *p_items = NULL, i_nb_items = 0;
1784                     char item[512], *p_parser = p_request;
1785
1786                     /* Get the list of items to delete */
1787                     while( (p_parser =
1788                             uri_extract_value( p_parser, "item", item, 512 )) )
1789                     {
1790                         if( !*item ) continue;
1791
1792                         i_item = atoi( item );
1793                         p_items = realloc( p_items, (i_nb_items + 1) *
1794                                            sizeof(int) );
1795                         p_items[i_nb_items] = i_item;
1796                         i_nb_items++;
1797                     }
1798
1799                     /* The items need to be deleted from in reversed order */
1800                     if( i_nb_items )
1801                     {
1802                         int i;
1803                         for( i = 0; i < i_nb_items; i++ )
1804                         {
1805                             int j, i_index = 0;
1806                             for( j = 0; j < i_nb_items; j++ )
1807                             {
1808                                 if( p_items[j] > p_items[i_index] )
1809                                     i_index = j;
1810                             }
1811
1812                             playlist_Delete( p_sys->p_playlist,
1813                                              p_items[i_index] );
1814                             msg_Dbg( p_intf, "requested playlist delete: %d",
1815                                      p_items[i_index] );
1816                             p_items[i_index] = -1;
1817                         }
1818                     }
1819
1820                     if( p_items ) free( p_items );
1821                     break;
1822                 }
1823                 case MVLC_KEEP:
1824                 {
1825                     int i_item, *p_items = NULL, i_nb_items = 0;
1826                     char item[512], *p_parser = p_request;
1827                     int i,j;
1828
1829                     /* Get the list of items to keep */
1830                     while( (p_parser =
1831                             uri_extract_value( p_parser, "item", item, 512 )) )
1832                     {
1833                         if( !*item ) continue;
1834
1835                         i_item = atoi( item );
1836                         p_items = realloc( p_items, (i_nb_items + 1) *
1837                                            sizeof(int) );
1838                         p_items[i_nb_items] = i_item;
1839                         i_nb_items++;
1840                     }
1841
1842                     /* The items need to be deleted from in reversed order */
1843                     for( i = p_sys->p_playlist->i_size - 1; i >= 0 ; i-- )
1844                     {
1845                         /* Check if the item is in the keep list */
1846                         for( j = 0 ; j < i_nb_items ; j++ )
1847                         {
1848                             if( p_items[j] == i ) break;
1849                         }
1850                         if( j == i_nb_items )
1851                         {
1852                             playlist_Delete( p_sys->p_playlist, i );
1853                             msg_Dbg( p_intf, "requested playlist delete: %d",
1854                                      i );
1855                         }
1856                     }
1857
1858                     if( p_items ) free( p_items );
1859                     break;
1860                 }
1861                 case MVLC_EMPTY:
1862                 {
1863                     while( p_sys->p_playlist->i_size > 0 )
1864                     {
1865                         playlist_Delete( p_sys->p_playlist, 0 );
1866                     }
1867                     msg_Dbg( p_intf, "requested playlist empty" );
1868                     break;
1869                 }
1870                 case MVLC_SORT:
1871                 {
1872                     char type[12];
1873                     char order[2];
1874                     int i_order;
1875
1876                     uri_extract_value( p_request, "type", type, 12 );
1877                     uri_extract_value( p_request, "order", order, 2 );
1878
1879                     if( order[0] == '0' ) i_order = ORDER_NORMAL;
1880                     else i_order = ORDER_REVERSE;
1881
1882                     if( !strcmp( type , "title" ) )
1883                     {
1884                         playlist_SortTitle( p_sys->p_playlist , i_order );
1885                         msg_Dbg( p_intf, "requested playlist sort by title (%d)" , i_order );
1886                     } else if( !strcmp( type , "group" ) )
1887                     {
1888                         playlist_SortGroup( p_sys->p_playlist , i_order );
1889                         msg_Dbg( p_intf, "requested playlist sort by group (%d)" , i_order );
1890                     } else if( !strcmp( type , "author" ) )
1891                     {
1892                         playlist_SortAuthor( p_sys->p_playlist , i_order );
1893                         msg_Dbg( p_intf, "requested playlist sort by author (%d)" , i_order );
1894                     }
1895
1896                     break;
1897                 }
1898
1899                 /* admin function */
1900                 case MVLC_CLOSE:
1901                 {
1902                     char id[512];
1903                     uri_extract_value( p_request, "id", id, 512 );
1904                     msg_Dbg( p_intf, "requested close id=%s", id );
1905                     if( p_sys->p_httpd->pf_control( p_sys->p_httpd, HTTPD_SET_CLOSE, id, NULL ) )
1906                     {
1907                         msg_Warn( p_intf, "close failed for id=%s", id );
1908                     }
1909                     break;
1910                 }
1911                 case MVLC_SHUTDOWN:
1912                 {
1913                     msg_Dbg( p_intf, "requested shutdown" );
1914                     p_intf->p_vlc->b_die = VLC_TRUE;
1915                     break;
1916                 }
1917                 default:
1918                     PRINTS( "<!-- control param(%s) unsuported -->", control );
1919                     break;
1920             }
1921             break;
1922
1923         case MVLC_SET:
1924         {
1925             char    value[512];
1926             int     i;
1927             float   f;
1928
1929             if( i_request <= 0 ||
1930                 *m->param1  == '\0' ||
1931                 strstr( p_request, m->param1 ) == NULL )
1932             {
1933                 break;
1934             }
1935             uri_extract_value( p_request, m->param1,  value, 512 );
1936             uri_decode_url_encoded( value );
1937
1938             switch( StrToMacroType( m->param2 ) )
1939             {
1940                 case MVLC_INT:
1941                     i = atoi( value );
1942                     config_PutInt( p_intf, m->param1, i );
1943                     break;
1944                 case MVLC_FLOAT:
1945                     f = atof( value );
1946                     config_PutFloat( p_intf, m->param1, f );
1947                     break;
1948                 case MVLC_STRING:
1949                     config_PutPsz( p_intf, m->param1, value );
1950                     break;
1951                 default:
1952                     PRINTS( "<!-- invalid type(%s) in set -->", m->param2 )
1953             }
1954             break;
1955         }
1956         case MVLC_GET:
1957         {
1958             char    value[512];
1959             int     i;
1960             float   f;
1961             char    *psz;
1962
1963             if( *m->param1  == '\0' )
1964             {
1965                 break;
1966             }
1967
1968             switch( StrToMacroType( m->param2 ) )
1969             {
1970                 case MVLC_INT:
1971                     i = config_GetInt( p_intf, m->param1 );
1972                     sprintf( value, "%i", i );
1973                     break;
1974                 case MVLC_FLOAT:
1975                     f = config_GetFloat( p_intf, m->param1 );
1976                     sprintf( value, "%f", f );
1977                     break;
1978                 case MVLC_STRING:
1979                     psz = config_GetPsz( p_intf, m->param1 );
1980                     sprintf( value, "%s", psz ? psz : "" );
1981                     if( psz ) free( psz );
1982                     break;
1983                 default:
1984                     sprintf( value, "invalid type(%s) in set", m->param2 );
1985                     break;
1986             }
1987             msg_Dbg( p_intf, "get name=%s value=%s type=%s", m->param1, value, m->param2 );
1988             PRINTS( "%s", value );
1989             break;
1990         }
1991         case MVLC_VALUE:
1992         {
1993             char *s, *v;
1994
1995             if( m->param1 )
1996             {
1997                 EvaluateRPN( p_args->vars, &p_args->stack, m->param1 );
1998                 s = SSPop( &p_args->stack );
1999                 v = mvar_GetValue( p_args->vars, s );
2000             }
2001             else
2002             {
2003                 v = s = SSPop( &p_args->stack );
2004             }
2005
2006             PRINTS( "%s", v );
2007             free( s );
2008             break;
2009         }
2010         case MVLC_RPN:
2011             EvaluateRPN( p_args->vars, &p_args->stack, m->param1 );
2012             break;
2013         case MVLC_UNKNOWN:
2014         default:
2015             PRINTS( "<!-- invalid macro id=`%s' -->", m->id );
2016             msg_Dbg( p_intf, "invalid macro id=`%s'", m->id );
2017             break;
2018     }
2019 #undef PRINTS
2020 #undef PRINT
2021 #undef ALLOC
2022 }
2023
2024 static uint8_t *MacroSearch( uint8_t *src, uint8_t *end, int i_mvlc, vlc_bool_t b_after )
2025 {
2026     int     i_id;
2027     int     i_level = 0;
2028
2029     while( src < end )
2030     {
2031         if( src + 4 < end  && !strncmp( src, "<vlc", 4 ) )
2032         {
2033             int i_skip;
2034             macro_t m;
2035
2036             i_skip = MacroParse( &m, src );
2037
2038             i_id = StrToMacroType( m.id );
2039
2040             switch( i_id )
2041             {
2042                 case MVLC_IF:
2043                 case MVLC_FOREACH:
2044                     i_level++;
2045                     break;
2046                 case MVLC_END:
2047                     i_level--;
2048                     break;
2049                 default:
2050                     break;
2051             }
2052
2053             MacroClean( &m );
2054
2055             if( ( i_mvlc == MVLC_END && i_level == -1 ) ||
2056                 ( i_mvlc != MVLC_END && i_level == 0 && i_mvlc == i_id ) )
2057             {
2058                 return src + ( b_after ? i_skip : 0 );
2059             }
2060             else if( i_level < 0 )
2061             {
2062                 return NULL;
2063             }
2064
2065             src += i_skip;
2066         }
2067         else
2068         {
2069             src++;
2070         }
2071     }
2072
2073     return NULL;
2074 }
2075
2076 static void Execute( httpd_file_callback_args_t *p_args,
2077                      uint8_t *p_request, int i_request,
2078                      uint8_t **pp_data, int *pi_data,
2079                      uint8_t **pp_dst,
2080                      uint8_t *_src, uint8_t *_end )
2081 {
2082     intf_thread_t  *p_intf = p_args->p_intf;
2083
2084     uint8_t *src, *dup, *end;
2085     uint8_t *dst = *pp_dst;
2086
2087     src = dup = malloc( _end - _src + 1 );
2088     end = src +( _end - _src );
2089
2090     memcpy( src, _src, _end - _src );
2091     *end = '\0';
2092
2093     /* we parse searching <vlc */
2094     while( src < end )
2095     {
2096         uint8_t *p;
2097         int i_copy;
2098
2099         p = strstr( src, "<vlc" );
2100         if( p < end && p == src )
2101         {
2102             macro_t m;
2103
2104             src += MacroParse( &m, src );
2105
2106             //msg_Dbg( p_intf, "macro_id=%s", m.id );
2107
2108             switch( StrToMacroType( m.id ) )
2109             {
2110                 case MVLC_IF:
2111                 {
2112                     vlc_bool_t i_test;
2113                     uint8_t    *endif;
2114
2115                     EvaluateRPN( p_args->vars, &p_args->stack, m.param1 );
2116                     if( SSPopN( &p_args->stack, p_args->vars ) )
2117                     {
2118                         i_test = 1;
2119                     }
2120                     else
2121                     {
2122                         i_test = 0;
2123                     }
2124                     endif = MacroSearch( src, end, MVLC_END, VLC_TRUE );
2125
2126                     if( i_test == 0 )
2127                     {
2128                         uint8_t *start = MacroSearch( src, endif, MVLC_ELSE, VLC_TRUE );
2129
2130                         if( start )
2131                         {
2132                             uint8_t *stop  = MacroSearch( start, endif, MVLC_END, VLC_FALSE );
2133                             if( stop )
2134                             {
2135                                 Execute( p_args, p_request, i_request, pp_data, pi_data, &dst, start, stop );
2136                             }
2137                         }
2138                     }
2139                     else if( i_test == 1 )
2140                     {
2141                         uint8_t *stop;
2142                         if( ( stop = MacroSearch( src, endif, MVLC_ELSE, VLC_FALSE ) ) == NULL )
2143                         {
2144                             stop = MacroSearch( src, endif, MVLC_END, VLC_FALSE );
2145                         }
2146                         if( stop )
2147                         {
2148                             Execute( p_args, p_request, i_request, pp_data, pi_data, &dst, src, stop );
2149                         }
2150                     }
2151
2152                     src = endif;
2153                     break;
2154                 }
2155                 case MVLC_FOREACH:
2156                 {
2157                     uint8_t *endfor = MacroSearch( src, end, MVLC_END, VLC_TRUE );
2158                     uint8_t *start = src;
2159                     uint8_t *stop = MacroSearch( src, end, MVLC_END, VLC_FALSE );
2160
2161                     if( stop )
2162                     {
2163                         mvar_t *index;
2164                         int    i_idx;
2165                         mvar_t *v;
2166                         if( !strcmp( m.param2, "integer" ) )
2167                         {
2168                             char *arg = SSPop( &p_args->stack );
2169                             index = mvar_IntegerSetNew( m.param1, arg );
2170                             free( arg );
2171                         }
2172                         else if( !strcmp( m.param2, "directory" ) )
2173                         {
2174                             char *arg = SSPop( &p_args->stack );
2175                             index = mvar_FileSetNew( m.param1, arg );
2176                             free( arg );
2177                         }
2178                         else if( !strcmp( m.param2, "playlist" ) )
2179                         {
2180                             index = mvar_PlaylistSetNew( m.param1, p_intf->p_sys->p_playlist );
2181                         }
2182                         else if( !strcmp( m.param2, "informations" ) )
2183                         {
2184                             index = mvar_InfoSetNew( m.param1, p_intf->p_sys->p_input );
2185                         }
2186                         else if( !strcmp( m.param2, "hosts" ) )
2187                         {
2188                             index = mvar_HttpdInfoSetNew( m.param1, p_intf->p_sys->p_httpd, HTTPD_GET_HOSTS );
2189                         }
2190                         else if( !strcmp( m.param2, "urls" ) )
2191                         {
2192                             index = mvar_HttpdInfoSetNew( m.param1, p_intf->p_sys->p_httpd, HTTPD_GET_URLS );
2193                         }
2194                         else if( !strcmp( m.param2, "connections" ) )
2195                         {
2196                             index = mvar_HttpdInfoSetNew(m.param1, p_intf->p_sys->p_httpd, HTTPD_GET_CONNECTIONS);
2197                         }
2198                         else if( ( v = mvar_GetVar( p_args->vars, m.param2 ) ) )
2199                         {
2200                             index = mvar_Duplicate( v );
2201                         }
2202                         else
2203                         {
2204                             msg_Dbg( p_intf, "invalid index constructor (%s)", m.param2 );
2205                             src = endfor;
2206                             break;
2207                         }
2208
2209                         for( i_idx = 0; i_idx < index->i_field; i_idx++ )
2210                         {
2211                             mvar_t *f = mvar_Duplicate( index->field[i_idx] );
2212
2213                             //msg_Dbg( p_intf, "foreach field[%d] name=%s value=%s", i_idx, f->name, f->value );
2214
2215                             free( f->name );
2216                             f->name = strdup( m.param1 );
2217
2218
2219                             mvar_PushVar( p_args->vars, f );
2220                             Execute( p_args, p_request, i_request, pp_data, pi_data, &dst, start, stop );
2221                             mvar_RemoveVar( p_args->vars, f );
2222
2223                             mvar_Delete( f );
2224                         }
2225                         mvar_Delete( index );
2226
2227                         src = endfor;
2228                     }
2229                     break;
2230                 }
2231                 default:
2232                     MacroDo( p_args, &m, p_request, i_request, pp_data, pi_data, &dst );
2233                     break;
2234             }
2235
2236             MacroClean( &m );
2237             continue;
2238         }
2239
2240         i_copy =   ( (p == NULL || p > end ) ? end : p  ) - src;
2241         if( i_copy > 0 )
2242         {
2243             int i_index = dst - *pp_data;
2244
2245             *pi_data += i_copy;
2246             *pp_data = realloc( *pp_data, *pi_data );
2247             dst = (*pp_data) + i_index;
2248
2249             memcpy( dst, src, i_copy );
2250             dst += i_copy;
2251             src += i_copy;
2252         }
2253     }
2254
2255     *pp_dst = dst;
2256     free( dup );
2257 }
2258
2259 /****************************************************************************
2260  * http_get:
2261  ****************************************************************************
2262  * a file with mime == text/html is parsed and all "macro" replaced
2263  * <vlc id="macro name" [param1="" [param2=""]] />
2264  * valid id are
2265  *
2266  ****************************************************************************/
2267 static int  http_get( httpd_file_callback_args_t *p_args,
2268                       uint8_t *p_request, int i_request,
2269                       uint8_t **pp_data, int *pi_data )
2270 {
2271     char *p;
2272     FILE *f;
2273
2274     if( ( f = fopen( p_args->file, "r" ) ) == NULL )
2275     {
2276         p = *pp_data = malloc( 10240 );
2277         if( !p )
2278         {
2279                 return VLC_EGENERIC;
2280         }
2281         p += sprintf( p, "<html>\n" );
2282         p += sprintf( p, "<head>\n" );
2283         p += sprintf( p, "<title>Error loading %s</title>\n", p_args->file );
2284         p += sprintf( p, "</head>\n" );
2285         p += sprintf( p, "<body>\n" );
2286         p += sprintf( p, "<h1><center>Error loading %s for %s</center></h1>\n", p_args->file, p_args->name );
2287         p += sprintf( p, "<hr />\n" );
2288         p += sprintf( p, "<a href=\"http://www.videolan.org/\">VideoLAN</a>\n" );
2289         p += sprintf( p, "</body>\n" );
2290         p += sprintf( p, "</html>\n" );
2291
2292         *pi_data = strlen( *pp_data );
2293
2294         return VLC_SUCCESS;
2295     }
2296
2297     if( strcmp( p_args->mime, "text/html" ) )
2298     {
2299         FileLoad( f, pp_data, pi_data );
2300     }
2301     else
2302     {
2303         int  i_buffer;
2304         uint8_t *p_buffer;
2305         uint8_t *dst;
2306         vlc_value_t val;
2307         char position[4]; /* percentage */
2308         char time[12]; /* in seconds */
2309         char length[12]; /* in seconds */
2310         audio_volume_t i_volume;
2311         char volume[5];
2312         char state[8];
2313  
2314 #define p_sys p_args->p_intf->p_sys
2315         if( p_sys->p_input )
2316         {
2317             var_Get( p_sys->p_input, "position", &val);
2318             sprintf( position, "%d" , (int)((val.f_float) * 100.0));
2319             var_Get( p_sys->p_input, "time", &val);
2320             sprintf( time, "%d" , (int)(val.i_time / 1000000) );
2321             var_Get( p_sys->p_input, "length", &val);
2322             sprintf( length, "%d" , (int)(val.i_time / 1000000) );
2323
2324             var_Get( p_sys->p_input, "state", &val );
2325             if( val.i_int == PLAYING_S )
2326             {
2327                 sprintf( state, "playing" );
2328             } else if( val.i_int == PAUSE_S )
2329             {
2330                 sprintf( state, "paused" );
2331             } else
2332             {
2333                 sprintf( state, "stop" );
2334             }
2335         } else
2336         {
2337             sprintf( position, "%d", 0 );
2338             sprintf( time, "%d", 0 );
2339             sprintf( length, "%d", 0 );
2340             sprintf( state, "stop" );
2341         }
2342 #undef p_sys
2343
2344         aout_VolumeGet( p_args->p_intf , &i_volume );
2345         sprintf( volume , "%d" , (int)i_volume );
2346
2347         p_args->vars = mvar_New( "variables", "" );
2348         mvar_AppendNewVar( p_args->vars, "url_param", i_request > 0 ? "1" : "0" );
2349         mvar_AppendNewVar( p_args->vars, "url_value", p_request );
2350         mvar_AppendNewVar( p_args->vars, "version",   VERSION_MESSAGE );
2351         mvar_AppendNewVar( p_args->vars, "copyright", COPYRIGHT_MESSAGE );
2352         mvar_AppendNewVar( p_args->vars, "stream_position", position );
2353         mvar_AppendNewVar( p_args->vars, "stream_time", time );
2354         mvar_AppendNewVar( p_args->vars, "stream_length", length );
2355         mvar_AppendNewVar( p_args->vars, "volume", volume );
2356         mvar_AppendNewVar( p_args->vars, "stream_state", state );
2357
2358         SSInit( &p_args->stack );
2359
2360         /* first we load in a temporary buffer */
2361         FileLoad( f, &p_buffer, &i_buffer );
2362
2363         /* allocate output */
2364         *pi_data = i_buffer + 1000;
2365         dst = *pp_data = malloc( *pi_data );
2366
2367         /* we parse executing all  <vlc /> macros */
2368         Execute( p_args, p_request, i_request, pp_data, pi_data, &dst, &p_buffer[0], &p_buffer[i_buffer] );
2369
2370         *dst     = '\0';
2371         *pi_data = dst - *pp_data;
2372
2373         SSClean( &p_args->stack );
2374         mvar_Delete( p_args->vars );
2375         free( p_buffer );
2376     }
2377
2378     fclose( f );
2379
2380     return VLC_SUCCESS;
2381 }
2382
2383 /****************************************************************************
2384  * uri parser
2385  ****************************************************************************/
2386 static char *uri_extract_value( char *psz_uri, char *psz_name,
2387                                 char *psz_value, int i_value_max )
2388 {
2389     char *p = psz_uri;
2390
2391     while( (p = strstr( p, psz_name )) )
2392     {
2393         /* Verify that we are dealing with a post/get argument */
2394         if( p == psz_uri || *(p - 1) == '&' || *(p - 1) == '\n' )
2395             break;
2396         p++;
2397     }
2398
2399     if( p )
2400     {
2401         int i_len;
2402
2403         p += strlen( psz_name );
2404         if( *p == '=' ) p++;
2405
2406         if( strchr( p, '&' ) )
2407         {
2408             i_len = strchr( p, '&' ) - p;
2409         }
2410         else
2411         {
2412             /* for POST method */
2413             if( strchr( p, '\n' ) )
2414             {
2415                 i_len = strchr( p, '\n' ) - p;
2416                 if( i_len && *(p+i_len-1) == '\r' ) i_len--;
2417             }
2418             else
2419             {
2420                 i_len = strlen( p );
2421             }
2422         }
2423         i_len = __MIN( i_value_max - 1, i_len );
2424         if( i_len > 0 )
2425         {
2426             strncpy( psz_value, p, i_len );
2427             psz_value[i_len] = '\0';
2428         }
2429         else
2430         {
2431             strncpy( psz_value, "", i_value_max );
2432         }
2433         p += i_len;
2434     }
2435     else
2436     {
2437         strncpy( psz_value, "", i_value_max );
2438     }
2439
2440     return p;
2441 }
2442
2443 static void uri_decode_url_encoded( char *psz )
2444 {
2445     char *dup = strdup( psz );
2446     char *p = dup;
2447
2448     while( *p )
2449     {
2450         if( *p == '%' )
2451         {
2452             char val[3];
2453             p++;
2454             if( !*p )
2455             {
2456                 break;
2457             }
2458
2459             val[0] = *p++;
2460             val[1] = *p++;
2461             val[2] = '\0';
2462
2463             *psz++ = strtol( val, NULL, 16 );
2464         }
2465         else if( *p == '+' )
2466         {
2467             *psz++ = ' ';
2468             p++;
2469         }
2470         else
2471         {
2472             *psz++ = *p++;
2473         }
2474     }
2475     *psz++  ='\0';
2476     free( dup );
2477 }
2478
2479 /****************************************************************************
2480  * Light RPN evaluator
2481  ****************************************************************************/
2482 static void SSInit( rpn_stack_t *st )
2483 {
2484     st->i_stack = 0;
2485 }
2486
2487 static void SSClean( rpn_stack_t *st )
2488 {
2489     while( st->i_stack > 0 )
2490     {
2491         free( st->stack[--st->i_stack] );
2492     }
2493 }
2494
2495 static void SSPush( rpn_stack_t *st, char *s )
2496 {
2497     if( st->i_stack < STACK_MAX )
2498     {
2499         st->stack[st->i_stack++] = strdup( s );
2500     }
2501 }
2502
2503 static char * SSPop( rpn_stack_t *st )
2504 {
2505     if( st->i_stack <= 0 )
2506     {
2507         return strdup( "" );
2508     }
2509     else
2510     {
2511         return st->stack[--st->i_stack];
2512     }
2513 }
2514
2515 static int SSPopN( rpn_stack_t *st, mvar_t  *vars )
2516 {
2517     char *name;
2518     char *value;
2519
2520     char *end;
2521     int  i;
2522
2523     name = SSPop( st );
2524     i = strtol( name, &end, 0 );
2525     if( end == name )
2526     {
2527         value = mvar_GetValue( vars, name );
2528         i = atoi( value );
2529     }
2530     free( name );
2531
2532     return( i );
2533 }
2534
2535 static void SSPushN( rpn_stack_t *st, int i )
2536 {
2537     char v[512];
2538
2539     sprintf( v, "%d", i );
2540     SSPush( st, v );
2541 }
2542
2543 static void  EvaluateRPN( mvar_t  *vars, rpn_stack_t *st, char *exp )
2544 {
2545     for( ;; )
2546     {
2547         char s[100], *p;
2548
2549         /* skip spcae */
2550         while( *exp == ' ' )
2551         {
2552             exp++;
2553         }
2554
2555         if( *exp == '\'' )
2556         {
2557             /* extract string */
2558             p = &s[0];
2559             exp++;
2560             while( *exp && *exp != '\'' )
2561             {
2562                 *p++ = *exp++;
2563             }
2564             *p = '\0';
2565             exp++;
2566             SSPush( st, s );
2567             continue;
2568         }
2569
2570         /* extract token */
2571         p = strchr( exp, ' ' );
2572         if( !p )
2573         {
2574             strcpy( s, exp );
2575
2576             exp += strlen( exp );
2577         }
2578         else
2579         {
2580             int i = p -exp;
2581             strncpy( s, exp, i );
2582             s[i] = '\0';
2583
2584             exp = p + 1;
2585         }
2586
2587         if( *s == '\0' )
2588         {
2589             break;
2590         }
2591
2592         /* 1. Integer function */
2593         if( !strcmp( s, "!" ) )
2594         {
2595             SSPushN( st, ~SSPopN( st, vars ) );
2596         }
2597         else if( !strcmp( s, "^" ) )
2598         {
2599             SSPushN( st, SSPopN( st, vars ) ^ SSPopN( st, vars ) );
2600         }
2601         else if( !strcmp( s, "&" ) )
2602         {
2603             SSPushN( st, SSPopN( st, vars ) & SSPopN( st, vars ) );
2604         }
2605         else if( !strcmp( s, "|" ) )
2606         {
2607             SSPushN( st, SSPopN( st, vars ) | SSPopN( st, vars ) );
2608         }
2609         else if( !strcmp( s, "+" ) )
2610         {
2611             SSPushN( st, SSPopN( st, vars ) + SSPopN( st, vars ) );
2612         }
2613         else if( !strcmp( s, "-" ) )
2614         {
2615             int j = SSPopN( st, vars );
2616             int i = SSPopN( st, vars );
2617             SSPushN( st, i - j );
2618         }
2619         else if( !strcmp( s, "*" ) )
2620         {
2621             SSPushN( st, SSPopN( st, vars ) * SSPopN( st, vars ) );
2622         }
2623         else if( !strcmp( s, "/" ) )
2624         {
2625             int i, j;
2626
2627             j = SSPopN( st, vars );
2628             i = SSPopN( st, vars );
2629
2630             SSPushN( st, j != 0 ? i / j : 0 );
2631         }
2632         else if( !strcmp( s, "%" ) )
2633         {
2634             int i, j;
2635
2636             j = SSPopN( st, vars );
2637             i = SSPopN( st, vars );
2638
2639             SSPushN( st, j != 0 ? i % j : 0 );
2640         }
2641         /* 2. integer tests */
2642         else if( !strcmp( s, "=" ) )
2643         {
2644             SSPushN( st, SSPopN( st, vars ) == SSPopN( st, vars ) ? -1 : 0 );
2645         }
2646         else if( !strcmp( s, "<" ) )
2647         {
2648             int j = SSPopN( st, vars );
2649             int i = SSPopN( st, vars );
2650
2651             SSPushN( st, i < j ? -1 : 0 );
2652         }
2653         else if( !strcmp( s, ">" ) )
2654         {
2655             int j = SSPopN( st, vars );
2656             int i = SSPopN( st, vars );
2657
2658             SSPushN( st, i > j ? -1 : 0 );
2659         }
2660         else if( !strcmp( s, "<=" ) )
2661         {
2662             int j = SSPopN( st, vars );
2663             int i = SSPopN( st, vars );
2664
2665             SSPushN( st, i <= j ? -1 : 0 );
2666         }
2667         else if( !strcmp( s, ">=" ) )
2668         {
2669             int j = SSPopN( st, vars );
2670             int i = SSPopN( st, vars );
2671
2672             SSPushN( st, i >= j ? -1 : 0 );
2673         }
2674         /* 3. string functions */
2675         else if( !strcmp( s, "strcat" ) )
2676         {
2677             char *s2 = SSPop( st );
2678             char *s1 = SSPop( st );
2679             char *str = malloc( strlen( s1 ) + strlen( s2 ) + 1 );
2680
2681             strcpy( str, s1 );
2682             strcat( str, s2 );
2683
2684             SSPush( st, str );
2685             free( s1 );
2686             free( s2 );
2687             free( str );
2688         }
2689         else if( !strcmp( s, "strcmp" ) )
2690         {
2691             char *s2 = SSPop( st );
2692             char *s1 = SSPop( st );
2693
2694             SSPushN( st, strcmp( s1, s2 ) );
2695             free( s1 );
2696             free( s2 );
2697         }
2698         else if( !strcmp( s, "strncmp" ) )
2699         {
2700             int n = SSPopN( st, vars );
2701             char *s2 = SSPop( st );
2702             char *s1 = SSPop( st );
2703
2704             SSPushN( st, strncmp( s1, s2 , n ) );
2705             free( s1 );
2706             free( s2 );
2707         }
2708         else if( !strcmp( s, "strlen" ) )
2709         {
2710             char *str = SSPop( st );
2711
2712             SSPushN( st, strlen( str ) );
2713             free( str );
2714         }
2715         /* 4. stack functions */
2716         else if( !strcmp( s, "dup" ) )
2717         {
2718             char *str = SSPop( st );
2719             SSPush( st, str );
2720             SSPush( st, str );
2721             free( str );
2722         }
2723         else if( !strcmp( s, "drop" ) )
2724         {
2725             char *str = SSPop( st );
2726             free( str );
2727         }
2728         else if( !strcmp( s, "swap" ) )
2729         {
2730             char *s1 = SSPop( st );
2731             char *s2 = SSPop( st );
2732
2733             SSPush( st, s1 );
2734             SSPush( st, s2 );
2735             free( s1 );
2736             free( s2 );
2737         }
2738         else if( !strcmp( s, "flush" ) )
2739         {
2740             SSClean( st );
2741             SSInit( st );
2742         }
2743         else if( !strcmp( s, "store" ) )
2744         {
2745             char *value = SSPop( st );
2746             char *name  = SSPop( st );
2747
2748             mvar_PushNewVar( vars, name, value );
2749             free( name );
2750             free( value );
2751         }
2752         else if( !strcmp( s, "value" ) )
2753         {
2754             char *name  = SSPop( st );
2755             char *value = mvar_GetValue( vars, name );
2756
2757             SSPush( st, value );
2758
2759             free( name );
2760         }
2761         else if( !strcmp( s, "url_extract" ) )
2762         {
2763             char *url = mvar_GetValue( vars, "url_value" );
2764             char *name = SSPop( st );
2765             char value[512];
2766
2767             uri_extract_value( url, name, value, 512 );
2768             uri_decode_url_encoded( value );
2769             SSPush( st, value );
2770         }
2771         else
2772         {
2773             SSPush( st, s );
2774         }
2775     }
2776 }
2777
2778 /**********************************************************************
2779  * Find_end_MRL: Find the end of the sentence :
2780  * this function parses the string psz and find the end of the item
2781  * and/or option with detecting the " and ' problems.
2782  * returns NULL if an error is detected, otherwise, returns a pointer
2783  * of the end of the sentence (after the last character)
2784  **********************************************************************/
2785 static char *Find_end_MRL( char *psz )
2786 {
2787     char *s_sent = psz;
2788
2789     switch( *s_sent )
2790     {
2791         case '\"':
2792         {
2793             s_sent++;
2794
2795             while( ( *s_sent != '\"' ) && ( *s_sent != '\0' ) )
2796             {
2797                 if( *s_sent == '\'' )
2798                 {
2799                     s_sent = Find_end_MRL( s_sent );
2800
2801                     if( s_sent == NULL )
2802                     {
2803                         return NULL;
2804                     }
2805                 } else
2806                 {
2807                     s_sent++;
2808                 }
2809             }
2810
2811             if( *s_sent == '\"' )
2812             {
2813                 s_sent++;
2814                 return s_sent;
2815             } else  /* *s_sent == '\0' , which means the number of " is incorrect */
2816             {
2817                 return NULL;
2818             }
2819             break;
2820         }
2821         case '\'':
2822         {
2823             s_sent++;
2824
2825             while( ( *s_sent != '\'' ) && ( *s_sent != '\0' ) )
2826             {
2827                 if( *s_sent == '\"' )
2828                 {
2829                     s_sent = Find_end_MRL( s_sent );
2830
2831                     if( s_sent == NULL )
2832                     {
2833                         return NULL;
2834                     }
2835                 } else
2836                 {
2837                     s_sent++;
2838                 }
2839             }
2840
2841             if( *s_sent == '\'' )
2842             {
2843                 s_sent++;
2844                 return s_sent;
2845             } else  /* *s_sent == '\0' , which means the number of ' is incorrect */
2846             {
2847                 return NULL;
2848             }
2849             break;
2850         }
2851         default: /* now we can look for spaces */
2852         {
2853             while( ( *s_sent != ' ' ) && ( *s_sent != '\0' ) )
2854             {
2855                 if( ( *s_sent == '\'' ) || ( *s_sent == '\"' ) )
2856                 {
2857                     s_sent = Find_end_MRL( s_sent );
2858                 } else
2859                 {
2860                     s_sent++;
2861                 }
2862             }
2863             return s_sent;
2864         }
2865     }
2866 }
2867
2868 /**********************************************************************
2869  * parse_MRL: parse the MRL, find the mrl string and the options,
2870  * create an item with all informations in it, and return the item.
2871  * return NULL if there is an error.
2872  **********************************************************************/
2873 playlist_item_t * parse_MRL( char *psz )
2874 {
2875     char **ppsz_options = NULL;
2876     char *mrl;
2877     char *s_mrl = psz;
2878     int i_error = 0;
2879     char *s_temp;
2880     int i = 0;
2881     int i_options = 0;
2882     playlist_item_t * p_item = NULL;
2883
2884     /* In case there is spaces before the mrl */
2885     while( ( *s_mrl == ' ' ) && ( *s_mrl != '\0' ) )
2886     {
2887         s_mrl++;
2888     }
2889
2890     /* extract the mrl */
2891     s_temp = strstr( s_mrl , " :" );
2892     if( s_temp == NULL )
2893     {
2894         s_temp = s_mrl + strlen( s_mrl );
2895     } else
2896     {
2897         while( (*s_temp == ' ') && (s_temp != s_mrl ) )
2898         {
2899             s_temp--;
2900         }
2901         s_temp++;
2902     }
2903
2904     /* if the mrl is between " or ', we must remove them */
2905     if( (*s_mrl == '\'') || (*s_mrl == '\"') )
2906     {
2907         mrl = (char *)malloc( (s_temp - s_mrl - 1) * sizeof( char ) );
2908         strncpy( mrl , (s_mrl + 1) , s_temp - s_mrl - 2 );
2909         mrl[ s_temp - s_mrl - 2 ] = '\0';
2910     } else
2911     {
2912         mrl = (char *)malloc( (s_temp - s_mrl + 1) * sizeof( char ) );
2913         strncpy( mrl , s_mrl , s_temp - s_mrl );
2914         mrl[ s_temp - s_mrl ] = '\0';
2915     }
2916
2917     s_mrl = s_temp;
2918
2919     /* now we can take care of the options */
2920     while( (*s_mrl != '\0') && (i_error == 0) )
2921     {
2922         switch( *s_mrl )
2923         {
2924             case ' ':
2925             {
2926                 s_mrl++;
2927                 break;
2928             }
2929             case ':': /* an option */
2930             {
2931                 s_temp = Find_end_MRL( s_mrl );
2932
2933                 if( s_temp == NULL )
2934                 {
2935                     i_error = 1;
2936                 }
2937                 else
2938                 {
2939                     i_options++;
2940                     ppsz_options = realloc( ppsz_options , i_options *
2941                                             sizeof(char *) );
2942                     ppsz_options[ i_options - 1 ] =
2943                         malloc( (s_temp - s_mrl + 1) * sizeof(char) );
2944
2945                     strncpy( ppsz_options[ i_options - 1 ] , s_mrl ,
2946                              s_temp - s_mrl );
2947
2948                     /* don't forget to finish the string with a '\0' */
2949                     (ppsz_options[ i_options - 1 ])[ s_temp - s_mrl ] = '\0';
2950
2951                     s_mrl = s_temp;
2952                 }
2953                 break;
2954             }
2955             default:
2956             {
2957                 i_error = 1;
2958                 break;
2959             }
2960         }
2961     }
2962
2963     if( i_error != 0 )
2964     {
2965         free( mrl );
2966     }
2967     else
2968     {
2969         /* now create an item */
2970         p_item = malloc( sizeof( playlist_item_t ) );
2971         memset( p_item, 0, sizeof( playlist_item_t ) );
2972         p_item->psz_name   = mrl;
2973         p_item->psz_uri    = strdup( mrl );
2974         p_item->i_duration = -1;
2975         p_item->b_enabled = VLC_TRUE;
2976         p_item->i_group = PLAYLIST_TYPE_MANUAL;
2977
2978         for( i = 0 ; i< i_options ; i++ )
2979         {
2980             playlist_AddItemOption( p_item, ppsz_options[i] );
2981         }
2982     }
2983
2984     for( i = 0 ; i < i_options ; i++ )
2985     {
2986         free( ppsz_options[i] );
2987     }
2988     free( ppsz_options );
2989
2990     return p_item;
2991 }