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