]> git.sesse.net Git - vlc/blob - modules/control/http.c
* Fixed URLs here and there.
[vlc] / modules / control / http.c
1 /*****************************************************************************
2  * http.c :  http mini-server ;)
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  * $Id: http.c,v 1.41 2003/12/21 23:32:58 sam 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 || !p_item->psz_uri || !*p_item->psz_uri )
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;
1814                     char item[512], *p_parser = p_request;
1815                     int i,j;
1816
1817                     /* Get the list of items to keep */
1818                     while( (p_parser =
1819                             uri_extract_value( p_parser, "item", item, 512 )) )
1820                     {
1821                         if( !*item ) continue;
1822
1823                         i_item = atoi( item );
1824                         p_items = realloc( p_items, (i_nb_items + 1) *
1825                                            sizeof(int) );
1826                         p_items[i_nb_items] = i_item;
1827                         i_nb_items++;
1828                     }
1829
1830                     /* The items need to be deleted from in reversed order */
1831                     for( i = p_sys->p_playlist->i_size - 1; i >= 0 ; i-- )
1832                     {
1833                         /* Check if the item is in the keep list */
1834                         for( j = 0 ; j < i_nb_items ; j++ )
1835                         {
1836                             if( p_items[j] == i ) break;
1837                         }
1838                         if( j == i_nb_items )
1839                         {
1840                             playlist_Delete( p_sys->p_playlist, i );
1841                             msg_Dbg( p_intf, "requested playlist delete: %d",
1842                                      i );
1843                         }
1844                     }
1845
1846                     if( p_items ) free( p_items );
1847                     break;
1848                 }
1849                 case MVLC_EMPTY:
1850                 {
1851                     while( p_sys->p_playlist->i_size > 0 )
1852                     {
1853                         playlist_Delete( p_sys->p_playlist, 0 );
1854                     }
1855                     msg_Dbg( p_intf, "requested playlist empty" );
1856                     break;
1857                 }
1858                 case MVLC_SORT:
1859                 {
1860                     char type[12];
1861                     char order[2];
1862                     int i_order;
1863
1864                     uri_extract_value( p_request, "type", type, 12 );
1865                     uri_extract_value( p_request, "order", order, 2 );
1866
1867                     if( order[0] == '0' ) i_order = SORT_NORMAL;
1868                     else i_order = SORT_REVERSE;
1869
1870                     if( !strcmp( type , "title" ) )
1871                     {
1872                         playlist_SortTitle( p_sys->p_playlist , i_order );
1873                         msg_Dbg( p_intf, "requested playlist sort by title (%d)" , i_order );
1874                     } else if( !strcmp( type , "group" ) )
1875                     {
1876                         playlist_SortGroup( p_sys->p_playlist , i_order );
1877                         msg_Dbg( p_intf, "requested playlist sort by group (%d)" , i_order );
1878                     } else if( !strcmp( type , "author" ) )
1879                     {
1880                         playlist_SortAuthor( p_sys->p_playlist , i_order );
1881                         msg_Dbg( p_intf, "requested playlist sort by author (%d)" , i_order );
1882                     }
1883
1884                     break;
1885                 }
1886
1887                 /* admin function */
1888                 case MVLC_CLOSE:
1889                 {
1890                     char id[512];
1891                     uri_extract_value( p_request, "id", id, 512 );
1892                     msg_Dbg( p_intf, "requested close id=%s", id );
1893                     if( p_sys->p_httpd->pf_control( p_sys->p_httpd, HTTPD_SET_CLOSE, id, NULL ) )
1894                     {
1895                         msg_Warn( p_intf, "close failed for id=%s", id );
1896                     }
1897                     break;
1898                 }
1899                 case MVLC_SHUTDOWN:
1900                 {
1901                     msg_Dbg( p_intf, "requested shutdown" );
1902                     p_intf->p_vlc->b_die = VLC_TRUE;
1903                     break;
1904                 }
1905                 default:
1906                     PRINTS( "<!-- control param(%s) unsuported -->", control );
1907                     break;
1908             }
1909             break;
1910
1911         case MVLC_SET:
1912         {
1913             char    value[512];
1914             int     i;
1915             float   f;
1916
1917             if( i_request <= 0 ||
1918                 *m->param1  == '\0' ||
1919                 strstr( p_request, m->param1 ) == NULL )
1920             {
1921                 break;
1922             }
1923             uri_extract_value( p_request, m->param1,  value, 512 );
1924             uri_decode_url_encoded( value );
1925
1926             switch( StrToMacroType( m->param2 ) )
1927             {
1928                 case MVLC_INT:
1929                     i = atoi( value );
1930                     config_PutInt( p_intf, m->param1, i );
1931                     break;
1932                 case MVLC_FLOAT:
1933                     f = atof( value );
1934                     config_PutFloat( p_intf, m->param1, f );
1935                     break;
1936                 case MVLC_STRING:
1937                     config_PutPsz( p_intf, m->param1, value );
1938                     break;
1939                 default:
1940                     PRINTS( "<!-- invalid type(%s) in set -->", m->param2 )
1941             }
1942             break;
1943         }
1944         case MVLC_GET:
1945         {
1946             char    value[512];
1947             int     i;
1948             float   f;
1949             char    *psz;
1950
1951             if( *m->param1  == '\0' )
1952             {
1953                 break;
1954             }
1955
1956             switch( StrToMacroType( m->param2 ) )
1957             {
1958                 case MVLC_INT:
1959                     i = config_GetInt( p_intf, m->param1 );
1960                     sprintf( value, "%i", i );
1961                     break;
1962                 case MVLC_FLOAT:
1963                     f = config_GetFloat( p_intf, m->param1 );
1964                     sprintf( value, "%f", f );
1965                     break;
1966                 case MVLC_STRING:
1967                     psz = config_GetPsz( p_intf, m->param1 );
1968                     sprintf( value, "%s", psz ? psz : "" );
1969                     break;
1970                 default:
1971                     sprintf( value, "invalid type(%s) in set", m->param2 );
1972                     break;
1973             }
1974             msg_Dbg( p_intf, "get name=%s value=%s type=%s", m->param1, value, m->param2 );
1975             PRINTS( "%s", value );
1976             break;
1977         }
1978         case MVLC_VALUE:
1979         {
1980             char *s, *v;
1981
1982             if( m->param1 )
1983             {
1984                 EvaluateRPN( p_args->vars, &p_args->stack, m->param1 );
1985                 s = SSPop( &p_args->stack );
1986                 v = mvar_GetValue( p_args->vars, s );
1987             }
1988             else
1989             {
1990                 v = s = SSPop( &p_args->stack );
1991             }
1992
1993             PRINTS( "%s", v );
1994             free( s );
1995             break;
1996         }
1997         case MVLC_RPN:
1998             EvaluateRPN( p_args->vars, &p_args->stack, m->param1 );
1999             break;
2000         case MVLC_UNKNOWN:
2001         default:
2002             PRINTS( "<!-- invalid macro id=`%s' -->", m->id );
2003             msg_Dbg( p_intf, "invalid macro id=`%s'", m->id );
2004             break;
2005     }
2006 #undef PRINTS
2007 #undef PRINT
2008 #undef ALLOC
2009 }
2010
2011 static uint8_t *MacroSearch( uint8_t *src, uint8_t *end, int i_mvlc, vlc_bool_t b_after )
2012 {
2013     int     i_id;
2014     int     i_level = 0;
2015
2016     while( src < end )
2017     {
2018         if( src + 4 < end  && !strncmp( src, "<vlc", 4 ) )
2019         {
2020             int i_skip;
2021             macro_t m;
2022
2023             i_skip = MacroParse( &m, src );
2024
2025             i_id = StrToMacroType( m.id );
2026
2027             switch( i_id )
2028             {
2029                 case MVLC_IF:
2030                 case MVLC_FOREACH:
2031                     i_level++;
2032                     break;
2033                 case MVLC_END:
2034                     i_level--;
2035                     break;
2036                 default:
2037                     break;
2038             }
2039
2040             if( ( i_mvlc == MVLC_END && i_level == -1 ) ||
2041                 ( i_mvlc != MVLC_END && i_level == 0 && i_mvlc == i_id ) )
2042             {
2043                 return src + ( b_after ? i_skip : 0 );
2044             }
2045             else if( i_level < 0 )
2046             {
2047                 return NULL;
2048             }
2049
2050             src += i_skip;
2051         }
2052         else
2053         {
2054             src++;
2055         }
2056     }
2057
2058     return NULL;
2059 }
2060
2061 static void Execute( httpd_file_callback_args_t *p_args,
2062                      uint8_t *p_request, int i_request,
2063                      uint8_t **pp_data, int *pi_data,
2064                      uint8_t **pp_dst,
2065                      uint8_t *_src, uint8_t *_end )
2066 {
2067     intf_thread_t  *p_intf = p_args->p_intf;
2068
2069     uint8_t *src, *dup, *end;
2070     uint8_t *dst = *pp_dst;
2071
2072     src = dup = malloc( _end - _src + 1 );
2073     end = src +( _end - _src );
2074
2075     memcpy( src, _src, _end - _src );
2076     *end = '\0';
2077
2078     /* we parse searching <vlc */
2079     while( src < end )
2080     {
2081         uint8_t *p;
2082         int i_copy;
2083
2084         p = strstr( src, "<vlc" );
2085         if( p < end && p == src )
2086         {
2087             macro_t m;
2088
2089             src += MacroParse( &m, src );
2090
2091             //msg_Dbg( p_intf, "macro_id=%s", m.id );
2092
2093             switch( StrToMacroType( m.id ) )
2094             {
2095                 case MVLC_IF:
2096                 {
2097                     vlc_bool_t i_test;
2098                     uint8_t    *endif;
2099
2100                     EvaluateRPN( p_args->vars, &p_args->stack, m.param1 );
2101                     if( SSPopN( &p_args->stack, p_args->vars ) )
2102                     {
2103                         i_test = 1;
2104                     }
2105                     else
2106                     {
2107                         i_test = 0;
2108                     }
2109                     endif = MacroSearch( src, end, MVLC_END, VLC_TRUE );
2110
2111                     if( i_test == 0 )
2112                     {
2113                         uint8_t *start = MacroSearch( src, endif, MVLC_ELSE, VLC_TRUE );
2114
2115                         if( start )
2116                         {
2117                             uint8_t *stop  = MacroSearch( start, endif, MVLC_END, VLC_FALSE );
2118                             if( stop )
2119                             {
2120                                 Execute( p_args, p_request, i_request, pp_data, pi_data, &dst, start, stop );
2121                             }
2122                         }
2123                     }
2124                     else if( i_test == 1 )
2125                     {
2126                         uint8_t *stop;
2127                         if( ( stop = MacroSearch( src, endif, MVLC_ELSE, VLC_FALSE ) ) == NULL )
2128                         {
2129                             stop = MacroSearch( src, endif, MVLC_END, VLC_FALSE );
2130                         }
2131                         if( stop )
2132                         {
2133                             Execute( p_args, p_request, i_request, pp_data, pi_data, &dst, src, stop );
2134                         }
2135                     }
2136
2137                     src = endif;
2138                     break;
2139                 }
2140                 case MVLC_FOREACH:
2141                 {
2142                     uint8_t *endfor = MacroSearch( src, end, MVLC_END, VLC_TRUE );
2143                     uint8_t *start = src;
2144                     uint8_t *stop = MacroSearch( src, end, MVLC_END, VLC_FALSE );
2145
2146                     if( stop )
2147                     {
2148                         mvar_t *index;
2149                         int    i_idx;
2150                         mvar_t *v;
2151                         if( !strcmp( m.param2, "integer" ) )
2152                         {
2153                             char *arg = SSPop( &p_args->stack );
2154                             index = mvar_IntegerSetNew( m.param1, arg );
2155                             free( arg );
2156                         }
2157                         else if( !strcmp( m.param2, "directory" ) )
2158                         {
2159                             char *arg = SSPop( &p_args->stack );
2160                             index = mvar_FileSetNew( m.param1, arg );
2161                             free( arg );
2162                         }
2163                         else if( !strcmp( m.param2, "playlist" ) )
2164                         {
2165                             index = mvar_PlaylistSetNew( m.param1, p_intf->p_sys->p_playlist );
2166                         }
2167                         else if( !strcmp( m.param2, "informations" ) )
2168                         {
2169                             index = mvar_InfoSetNew( m.param1, p_intf->p_sys->p_input );
2170                         }
2171                         else if( !strcmp( m.param2, "hosts" ) )
2172                         {
2173                             index = mvar_HttpdInfoSetNew( m.param1, p_intf->p_sys->p_httpd, HTTPD_GET_HOSTS );
2174                         }
2175                         else if( !strcmp( m.param2, "urls" ) )
2176                         {
2177                             index = mvar_HttpdInfoSetNew( m.param1, p_intf->p_sys->p_httpd, HTTPD_GET_URLS );
2178                         }
2179                         else if( !strcmp( m.param2, "connections" ) )
2180                         {
2181                             index = mvar_HttpdInfoSetNew(m.param1, p_intf->p_sys->p_httpd, HTTPD_GET_CONNECTIONS);
2182                         }
2183                         else if( ( v = mvar_GetVar( p_args->vars, m.param2 ) ) )
2184                         {
2185                             index = mvar_Duplicate( v );
2186                         }
2187                         else
2188                         {
2189                             msg_Dbg( p_intf, "invalid index constructor (%s)", m.param2 );
2190                             src = endfor;
2191                             break;
2192                         }
2193
2194                         for( i_idx = 0; i_idx < index->i_field; i_idx++ )
2195                         {
2196                             mvar_t *f = mvar_Duplicate( index->field[i_idx] );
2197
2198                             //msg_Dbg( p_intf, "foreach field[%d] name=%s value=%s", i_idx, f->name, f->value );
2199
2200                             free( f->name );
2201                             f->name = strdup( m.param1 );
2202
2203
2204                             mvar_PushVar( p_args->vars, f );
2205                             Execute( p_args, p_request, i_request, pp_data, pi_data, &dst, start, stop );
2206                             mvar_RemoveVar( p_args->vars, f );
2207
2208                             mvar_Delete( f );
2209                         }
2210                         mvar_Delete( index );
2211
2212                         src = endfor;
2213                     }
2214                     break;
2215                 }
2216                 default:
2217                     MacroDo( p_args, &m, p_request, i_request, pp_data, pi_data, &dst );
2218                     break;
2219             }
2220
2221             MacroClean( &m );
2222             continue;
2223         }
2224
2225         i_copy =   ( (p == NULL || p > end ) ? end : p  ) - src;
2226         if( i_copy > 0 )
2227         {
2228             int i_index = dst - *pp_data;
2229
2230             *pi_data += i_copy;
2231             *pp_data = realloc( *pp_data, *pi_data );
2232             dst = (*pp_data) + i_index;
2233
2234             memcpy( dst, src, i_copy );
2235             dst += i_copy;
2236             src += i_copy;
2237         }
2238     }
2239
2240     *pp_dst = dst;
2241     free( dup );
2242 }
2243
2244 /****************************************************************************
2245  * http_get:
2246  ****************************************************************************
2247  * a file with mime == text/html is parsed and all "macro" replaced
2248  * <vlc id="macro name" [param1="" [param2=""]] />
2249  * valid id are
2250  *
2251  ****************************************************************************/
2252 static int  http_get( httpd_file_callback_args_t *p_args,
2253                       uint8_t *p_request, int i_request,
2254                       uint8_t **pp_data, int *pi_data )
2255 {
2256     char *p;
2257     FILE *f;
2258
2259     if( ( f = fopen( p_args->file, "r" ) ) == NULL )
2260     {
2261         p = *pp_data = malloc( 10240 );
2262         if( !p )
2263         {
2264                 return VLC_EGENERIC;
2265         }
2266         p += sprintf( p, "<html>\n" );
2267         p += sprintf( p, "<head>\n" );
2268         p += sprintf( p, "<title>Error loading %s</title>\n", p_args->file );
2269         p += sprintf( p, "</head>\n" );
2270         p += sprintf( p, "<body>\n" );
2271         p += sprintf( p, "<h1><center>Error loading %s for %s</center></h1>\n", p_args->file, p_args->name );
2272         p += sprintf( p, "<hr />\n" );
2273         p += sprintf( p, "<a href=\"http://www.videolan.org/\">VideoLAN</a>\n" );
2274         p += sprintf( p, "</body>\n" );
2275         p += sprintf( p, "</html>\n" );
2276
2277         *pi_data = strlen( *pp_data ) + 1;
2278
2279         return VLC_SUCCESS;
2280     }
2281
2282     if( strcmp( p_args->mime, "text/html" ) )
2283     {
2284         FileLoad( f, pp_data, pi_data );
2285     }
2286     else
2287     {
2288         int  i_buffer;
2289         uint8_t *p_buffer;
2290         uint8_t *dst;
2291         vlc_value_t val;
2292         char position[4]; /* percentage */
2293         char time[12]; /* in seconds */
2294         char length[12]; /* in seconds */
2295         audio_volume_t i_volume;
2296         char volume[5];
2297         char state[8];
2298  
2299 #define p_sys p_args->p_intf->p_sys
2300         if( p_sys->p_input )
2301         {
2302             var_Get( p_sys->p_input, "position", &val);
2303             sprintf( position, "%d" , (int)((val.f_float) * 100.0));
2304             var_Get( p_sys->p_input, "time", &val);
2305             sprintf( time, "%d" , (int)(val.i_time / 1000000) );
2306             var_Get( p_sys->p_input, "length", &val);
2307             sprintf( length, "%d" , (int)(val.i_time / 1000000) );
2308
2309             var_Get( p_sys->p_input, "state", &val );
2310             if( val.i_int == PLAYING_S )
2311             {
2312                 sprintf( state, "playing" );
2313             } else if( val.i_int == PAUSE_S )
2314             {
2315                 sprintf( state, "paused" );
2316             } else
2317             {
2318                 sprintf( state, "stop" );
2319             }
2320         } else
2321         {
2322             sprintf( position, "%d", 0 );
2323             sprintf( time, "%d", 0 );
2324             sprintf( length, "%d", 0 );
2325             sprintf( state, "stop" );
2326         }
2327 #undef p_sys
2328
2329         aout_VolumeGet( p_args->p_intf , &i_volume );
2330         sprintf( volume , "%d" , (int)i_volume );
2331
2332         p_args->vars = mvar_New( "variables", "" );
2333         mvar_AppendNewVar( p_args->vars, "url_param", i_request > 0 ? "1" : "0" );
2334         mvar_AppendNewVar( p_args->vars, "url_value", p_request );
2335         mvar_AppendNewVar( p_args->vars, "version",   VERSION_MESSAGE );
2336         mvar_AppendNewVar( p_args->vars, "copyright", COPYRIGHT_MESSAGE );
2337         mvar_AppendNewVar( p_args->vars, "stream_position", position );
2338         mvar_AppendNewVar( p_args->vars, "stream_time", time );
2339         mvar_AppendNewVar( p_args->vars, "stream_length", length );
2340         mvar_AppendNewVar( p_args->vars, "volume", volume );
2341         mvar_AppendNewVar( p_args->vars, "stream_state", state );
2342
2343         SSInit( &p_args->stack );
2344
2345         /* first we load in a temporary buffer */
2346         FileLoad( f, &p_buffer, &i_buffer );
2347
2348         /* allocate output */
2349         *pi_data = i_buffer + 1000;
2350         dst = *pp_data = malloc( *pi_data );
2351
2352         /* we parse executing all  <vlc /> macros */
2353         Execute( p_args, p_request, i_request, pp_data, pi_data, &dst, &p_buffer[0], &p_buffer[i_buffer] );
2354
2355         *dst++ = '\0';
2356         *pi_data = dst - *pp_data;
2357
2358         SSClean( &p_args->stack );
2359         mvar_Delete( p_args->vars );
2360     }
2361
2362     fclose( f );
2363
2364     return VLC_SUCCESS;
2365 }
2366
2367 /****************************************************************************
2368  * uri parser
2369  ****************************************************************************/
2370 static char *uri_extract_value( char *psz_uri, char *psz_name,
2371                                 char *psz_value, int i_value_max )
2372 {
2373     char *p;
2374
2375     p = strstr( psz_uri, psz_name );
2376     if( p )
2377     {
2378         int i_len;
2379
2380         p += strlen( psz_name );
2381         if( *p == '=' ) p++;
2382
2383         if( strchr( p, '&' ) )
2384         {
2385             i_len = strchr( p, '&' ) - p;
2386         }
2387         else
2388         {
2389             /* for POST method */
2390             if( strchr( p, '\n' ) )
2391             {
2392                 i_len = strchr( p, '\n' ) - p;
2393                 if( i_len && *(p+i_len-1) == '\r' ) i_len--;
2394             }
2395             else
2396             {
2397                 i_len = strlen( p );
2398             }
2399         }
2400         i_len = __MIN( i_value_max - 1, i_len );
2401         if( i_len > 0 )
2402         {
2403             strncpy( psz_value, p, i_len );
2404             psz_value[i_len] = '\0';
2405         }
2406         else
2407         {
2408             strncpy( psz_value, "", i_value_max );
2409         }
2410         p += i_len;
2411     }
2412     else
2413     {
2414         strncpy( psz_value, "", i_value_max );
2415     }
2416
2417     return p;
2418 }
2419
2420 static void uri_decode_url_encoded( char *psz )
2421 {
2422     char *dup = strdup( psz );
2423     char *p = dup;
2424
2425     while( *p )
2426     {
2427         if( *p == '%' )
2428         {
2429             char val[3];
2430             p++;
2431             if( !*p )
2432             {
2433                 break;
2434             }
2435
2436             val[0] = *p++;
2437             val[1] = *p++;
2438             val[2] = '\0';
2439
2440             *psz++ = strtol( val, NULL, 16 );
2441         }
2442         else if( *p == '+' )
2443         {
2444             *psz++ = ' ';
2445             p++;
2446         }
2447         else
2448         {
2449             *psz++ = *p++;
2450         }
2451     }
2452     *psz++  ='\0';
2453     free( dup );
2454 }
2455
2456 /****************************************************************************
2457  * Light RPN evaluator
2458  ****************************************************************************/
2459 static void SSInit( rpn_stack_t *st )
2460 {
2461     st->i_stack = 0;
2462 }
2463
2464 static void SSClean( rpn_stack_t *st )
2465 {
2466     while( st->i_stack > 0 )
2467     {
2468         free( st->stack[--st->i_stack] );
2469     }
2470 }
2471
2472 static void SSPush( rpn_stack_t *st, char *s )
2473 {
2474     if( st->i_stack < STACK_MAX )
2475     {
2476         st->stack[st->i_stack++] = strdup( s );
2477     }
2478 }
2479
2480 static char * SSPop( rpn_stack_t *st )
2481 {
2482     if( st->i_stack <= 0 )
2483     {
2484         return strdup( "" );
2485     }
2486     else
2487     {
2488         return st->stack[--st->i_stack];
2489     }
2490 }
2491
2492 static int SSPopN( rpn_stack_t *st, mvar_t  *vars )
2493 {
2494     char *name;
2495     char *value;
2496
2497     char *end;
2498     int  i;
2499
2500     name = SSPop( st );
2501     i = strtol( name, &end, 0 );
2502     if( end == name )
2503     {
2504         value = mvar_GetValue( vars, name );
2505         i = atoi( value );
2506     }
2507     free( name );
2508
2509     return( i );
2510 }
2511
2512 static void SSPushN( rpn_stack_t *st, int i )
2513 {
2514     char v[512];
2515
2516     sprintf( v, "%d", i );
2517     SSPush( st, v );
2518 }
2519
2520 static void  EvaluateRPN( mvar_t  *vars, rpn_stack_t *st, char *exp )
2521 {
2522     for( ;; )
2523     {
2524         char s[100], *p;
2525
2526         /* skip spcae */
2527         while( *exp == ' ' )
2528         {
2529             exp++;
2530         }
2531
2532         if( *exp == '\'' )
2533         {
2534             /* extract string */
2535             p = &s[0];
2536             exp++;
2537             while( *exp && *exp != '\'' )
2538             {
2539                 *p++ = *exp++;
2540             }
2541             *p = '\0';
2542             exp++;
2543             SSPush( st, s );
2544             continue;
2545         }
2546
2547         /* extract token */
2548         p = strchr( exp, ' ' );
2549         if( !p )
2550         {
2551             strcpy( s, exp );
2552
2553             exp += strlen( exp );
2554         }
2555         else
2556         {
2557             int i = p -exp;
2558             strncpy( s, exp, i );
2559             s[i] = '\0';
2560
2561             exp = p + 1;
2562         }
2563
2564         if( *s == '\0' )
2565         {
2566             break;
2567         }
2568
2569         /* 1. Integer function */
2570         if( !strcmp( s, "!" ) )
2571         {
2572             SSPushN( st, ~SSPopN( st, vars ) );
2573         }
2574         else if( !strcmp( s, "^" ) )
2575         {
2576             SSPushN( st, SSPopN( st, vars ) ^ SSPopN( st, vars ) );
2577         }
2578         else if( !strcmp( s, "&" ) )
2579         {
2580             SSPushN( st, SSPopN( st, vars ) & SSPopN( st, vars ) );
2581         }
2582         else if( !strcmp( s, "|" ) )
2583         {
2584             SSPushN( st, SSPopN( st, vars ) | SSPopN( st, vars ) );
2585         }
2586         else if( !strcmp( s, "+" ) )
2587         {
2588             SSPushN( st, SSPopN( st, vars ) + SSPopN( st, vars ) );
2589         }
2590         else if( !strcmp( s, "-" ) )
2591         {
2592             int j = SSPopN( st, vars );
2593             int i = SSPopN( st, vars );
2594             SSPushN( st, i - j );
2595         }
2596         else if( !strcmp( s, "*" ) )
2597         {
2598             SSPushN( st, SSPopN( st, vars ) * SSPopN( st, vars ) );
2599         }
2600         else if( !strcmp( s, "/" ) )
2601         {
2602             int i, j;
2603
2604             j = SSPopN( st, vars );
2605             i = SSPopN( st, vars );
2606
2607             SSPushN( st, j != 0 ? i / j : 0 );
2608         }
2609         else if( !strcmp( s, "%" ) )
2610         {
2611             int i, j;
2612
2613             j = SSPopN( st, vars );
2614             i = SSPopN( st, vars );
2615
2616             SSPushN( st, j != 0 ? i % j : 0 );
2617         }
2618         /* 2. integer tests */
2619         else if( !strcmp( s, "=" ) )
2620         {
2621             SSPushN( st, SSPopN( st, vars ) == SSPopN( st, vars ) ? -1 : 0 );
2622         }
2623         else if( !strcmp( s, "<" ) )
2624         {
2625             int j = SSPopN( st, vars );
2626             int i = SSPopN( st, vars );
2627
2628             SSPushN( st, i < j ? -1 : 0 );
2629         }
2630         else if( !strcmp( s, ">" ) )
2631         {
2632             int j = SSPopN( st, vars );
2633             int i = SSPopN( st, vars );
2634
2635             SSPushN( st, i > j ? -1 : 0 );
2636         }
2637         else if( !strcmp( s, "<=" ) )
2638         {
2639             int j = SSPopN( st, vars );
2640             int i = SSPopN( st, vars );
2641
2642             SSPushN( st, i <= j ? -1 : 0 );
2643         }
2644         else if( !strcmp( s, ">=" ) )
2645         {
2646             int j = SSPopN( st, vars );
2647             int i = SSPopN( st, vars );
2648
2649             SSPushN( st, i >= j ? -1 : 0 );
2650         }
2651         /* 3. string functions */
2652         else if( !strcmp( s, "strcat" ) )
2653         {
2654             char *s2 = SSPop( st );
2655             char *s1 = SSPop( st );
2656             char *str = malloc( strlen( s1 ) + strlen( s2 ) + 1 );
2657
2658             strcpy( str, s1 );
2659             strcat( str, s2 );
2660
2661             SSPush( st, str );
2662             free( s1 );
2663             free( s2 );
2664             free( str );
2665         }
2666         else if( !strcmp( s, "strcmp" ) )
2667         {
2668             char *s2 = SSPop( st );
2669             char *s1 = SSPop( st );
2670
2671             SSPushN( st, strcmp( s1, s2 ) );
2672             free( s1 );
2673             free( s2 );
2674         }
2675         else if( !strcmp( s, "strncmp" ) )
2676         {
2677             int n = SSPopN( st, vars );
2678             char *s2 = SSPop( st );
2679             char *s1 = SSPop( st );
2680
2681             SSPushN( st, strncmp( s1, s2 , n ) );
2682             free( s1 );
2683             free( s2 );
2684         }
2685         else if( !strcmp( s, "strlen" ) )
2686         {
2687             char *str = SSPop( st );
2688
2689             SSPushN( st, strlen( str ) );
2690             free( str );
2691         }
2692         /* 4. stack functions */
2693         else if( !strcmp( s, "dup" ) )
2694         {
2695             char *str = SSPop( st );
2696             SSPush( st, str );
2697             SSPush( st, str );
2698             free( str );
2699         }
2700         else if( !strcmp( s, "drop" ) )
2701         {
2702             char *str = SSPop( st );
2703             free( str );
2704         }
2705         else if( !strcmp( s, "swap" ) )
2706         {
2707             char *s1 = SSPop( st );
2708             char *s2 = SSPop( st );
2709
2710             SSPush( st, s1 );
2711             SSPush( st, s2 );
2712             free( s1 );
2713             free( s2 );
2714         }
2715         else if( !strcmp( s, "flush" ) )
2716         {
2717             SSClean( st );
2718             SSInit( st );
2719         }
2720         else if( !strcmp( s, "store" ) )
2721         {
2722             char *value = SSPop( st );
2723             char *name  = SSPop( st );
2724
2725             mvar_PushNewVar( vars, name, value );
2726             free( name );
2727             free( value );
2728         }
2729         else if( !strcmp( s, "value" ) )
2730         {
2731             char *name  = SSPop( st );
2732             char *value = mvar_GetValue( vars, name );
2733
2734             SSPush( st, value );
2735
2736             free( name );
2737         }
2738         else if( !strcmp( s, "url_extract" ) )
2739         {
2740             char *url = mvar_GetValue( vars, "url_value" );
2741             char *name = SSPop( st );
2742             char value[512];
2743
2744             uri_extract_value( url, name, value, 512 );
2745             uri_decode_url_encoded( value );
2746             SSPush( st, value );
2747         }
2748         else
2749         {
2750             SSPush( st, s );
2751         }
2752     }
2753 }
2754
2755 /**********************************************************************
2756  * Find_end_MRL: Find the end of the sentence :
2757  * this function parses the string psz and find the end of the item
2758  * and/or option with detecting the " and ' problems.
2759  * returns NULL if an error is detected, otherwise, returns a pointer
2760  * of the end of the sentence (after the last character)
2761  **********************************************************************/
2762 static char *Find_end_MRL( char *psz )
2763 {
2764     char *s_sent = psz;
2765
2766     switch( *s_sent )
2767     {
2768         case '\"':
2769         {
2770             s_sent++;
2771
2772             while( ( *s_sent != '\"' ) && ( *s_sent != '\0' ) )
2773             {
2774                 if( *s_sent == '\'' )
2775                 {
2776                     s_sent = Find_end_MRL( s_sent );
2777
2778                     if( s_sent == NULL )
2779                     {
2780                         return NULL;
2781                     }
2782                 } else
2783                 {
2784                     s_sent++;
2785                 }
2786             }
2787
2788             if( *s_sent == '\"' )
2789             {
2790                 s_sent++;
2791                 return s_sent;
2792             } else  /* *s_sent == '\0' , which means the number of " is incorrect */
2793             {
2794                 return NULL;
2795             }
2796             break;
2797         }
2798         case '\'':
2799         {
2800             s_sent++;
2801
2802             while( ( *s_sent != '\'' ) && ( *s_sent != '\0' ) )
2803             {
2804                 if( *s_sent == '\"' )
2805                 {
2806                     s_sent = Find_end_MRL( s_sent );
2807
2808                     if( s_sent == NULL )
2809                     {
2810                         return NULL;
2811                     }
2812                 } else
2813                 {
2814                     s_sent++;
2815                 }
2816             }
2817
2818             if( *s_sent == '\'' )
2819             {
2820                 s_sent++;
2821                 return s_sent;
2822             } else  /* *s_sent == '\0' , which means the number of ' is incorrect */
2823             {
2824                 return NULL;
2825             }
2826             break;
2827         }
2828         default: /* now we can look for spaces */
2829         {
2830             while( ( *s_sent != ' ' ) && ( *s_sent != '\0' ) )
2831             {
2832                 if( ( *s_sent == '\'' ) || ( *s_sent == '\"' ) )
2833                 {
2834                     s_sent = Find_end_MRL( s_sent );
2835                 } else
2836                 {
2837                     s_sent++;
2838                 }
2839             }
2840             return s_sent;
2841         }
2842     }
2843 }
2844
2845 /**********************************************************************
2846  * parse_MRL: parse the MRL, find the mrl string and the options,
2847  * create an item with all informations in it, and return the item.
2848  * return NULL if there is an error.
2849  **********************************************************************/
2850 playlist_item_t * parse_MRL( char *psz )
2851 {
2852     char **ppsz_options = NULL;
2853     char *mrl;
2854     char *s_mrl = psz;
2855     int i_error = 0;
2856     char *s_temp;
2857     int i = 0;
2858     int i_options = 0;
2859     playlist_item_t * p_item;
2860
2861     /* In case there is spaces before the mrl */
2862     while( ( *s_mrl == ' ' ) && ( *s_mrl != '\0' ) )
2863     {
2864         s_mrl++;
2865     }
2866
2867     /* extract the mrl */
2868     s_temp = strstr( s_mrl , " :" );
2869     if( s_temp == NULL )
2870     {
2871         s_temp = s_mrl + strlen( s_mrl );
2872     } else
2873     {
2874         while( (*s_temp == ' ') && (s_temp != s_mrl ) )
2875         {
2876             s_temp--;
2877         }
2878         s_temp++;
2879     }
2880
2881     /* if the mrl is between " or ', we must remove them */
2882     if( (*s_mrl == '\'') || (*s_mrl == '\"') )
2883     {
2884         mrl = (char *)malloc( (s_temp - s_mrl - 1) * sizeof( char ) );
2885         strncpy( mrl , (s_mrl + 1) , s_temp - s_mrl - 2 );
2886         mrl[ s_temp - s_mrl - 2 ] = '\0';
2887     } else
2888     {
2889         mrl = (char *)malloc( (s_temp - s_mrl + 1) * sizeof( char ) );
2890         strncpy( mrl , s_mrl , s_temp - s_mrl );
2891         mrl[ s_temp - s_mrl ] = '\0';
2892     }
2893
2894     s_mrl = s_temp;
2895
2896     /* now we can take care of the options */
2897     while( (*s_mrl != '\0') && (i_error == 0) )
2898     {
2899         switch( *s_mrl )
2900         {
2901             case ' ':
2902             {
2903                 s_mrl++;
2904                 break;
2905             }
2906             case ':': /* an option */
2907             {
2908                 s_temp = Find_end_MRL( s_mrl );
2909
2910                 if( s_temp == NULL )
2911                 {
2912                     i_error = 1;
2913                 } else
2914                 {
2915                     i_options++;
2916                     ppsz_options = (char **)realloc( ppsz_options , i_options * sizeof(char *) );
2917                     ppsz_options[ i_options - 1 ] = (char *)malloc( (s_temp - s_mrl + 1) * sizeof( char ) );
2918
2919                     strncpy( ppsz_options[ i_options - 1 ] , s_mrl , s_temp - s_mrl );
2920                     /* don't forget to finish the string with a '\0' */
2921                     (ppsz_options[ i_options - 1 ])[ s_temp - s_mrl ] = '\0';
2922
2923                     s_mrl = s_temp;
2924                 }
2925                 break;
2926             }
2927             default:
2928             {
2929                 i_error = 1;
2930                 break;
2931             }
2932         }
2933     }
2934
2935     if( i_error != 0 )
2936     {
2937         free( mrl );
2938         for( i = 0 ; i < i_options ; i++ )
2939         {
2940             free( ppsz_options[i] );
2941         }
2942         free( ppsz_options );
2943         return NULL;
2944     } else
2945     {
2946         /* now create an item */
2947         p_item = malloc( sizeof( playlist_item_t ) );
2948
2949         p_item->psz_name   = mrl;
2950         p_item->psz_uri    = strdup( mrl );
2951         p_item->psz_author = strdup( "" );
2952         p_item->i_duration = -1;
2953         p_item->i_type = 0;
2954         p_item->i_status = 0;
2955         p_item->b_autodeletion = VLC_FALSE;
2956         p_item->b_enabled = VLC_TRUE;
2957         p_item->i_group = PLAYLIST_TYPE_MANUAL;
2958
2959         p_item->ppsz_options = NULL;
2960         p_item->i_options = i_options;
2961
2962         if( i_options )
2963         {
2964             p_item->ppsz_options = ppsz_options;
2965         }
2966
2967         return p_item;
2968     }
2969 }