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