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