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