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