]> git.sesse.net Git - vlc/blob - modules/control/http.c
* modules/control/http.c: New RPN functions: vlc_play, vlc_stop, vlc_pause,
[vlc] / modules / control / http.c
1 /*****************************************************************************
2  * http.c :  http mini-server ;)
3  *****************************************************************************
4  * Copyright (C) 2001-2005 the VideoLAN team
5  * $Id$
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 #include <stdlib.h>
29 #include <ctype.h>
30 #include <vlc/vlc.h>
31 #include <vlc/intf.h>
32
33 #include <vlc/aout.h>
34 #include <vlc/vout.h> /* for fullscreen */
35
36 #include "vlc_httpd.h"
37 #include "vlc_vlm.h"
38 #include "vlc_tls.h"
39 #include "vlc_acl.h"
40 #include "charset.h"
41
42 #ifdef HAVE_SYS_STAT_H
43 #   include <sys/stat.h>
44 #endif
45 #ifdef HAVE_ERRNO_H
46 #   include <errno.h>
47 #endif
48 #ifdef HAVE_FCNTL_H
49 #   include <fcntl.h>
50 #endif
51
52 #ifdef HAVE_UNISTD_H
53 #   include <unistd.h>
54 #elif defined( WIN32 ) && !defined( UNDER_CE )
55 #   include <io.h>
56 #endif
57
58 #ifdef HAVE_DIRENT_H
59 #   include <dirent.h>
60 #endif
61
62 /* stat() support for large files on win32 */
63 #if defined( WIN32 ) && !defined( UNDER_CE )
64 #   define stat _stati64
65 #endif
66
67 /*****************************************************************************
68  * Module descriptor
69  *****************************************************************************/
70 static int  Open ( vlc_object_t * );
71 static void Close( vlc_object_t * );
72
73 #define HOST_TEXT N_( "Host address" )
74 #define HOST_LONGTEXT N_( \
75     "You can set the address and port the http interface will bind to." )
76 #define SRC_TEXT N_( "Source directory" )
77 #define SRC_LONGTEXT N_( "Source directory" )
78 #define CHARSET_TEXT N_( "Charset" )
79 #define CHARSET_LONGTEXT N_( \
80         "Charset declared in Content-Type header (default UTF-8)." )
81 #define CERT_TEXT N_( "Certificate file" )
82 #define CERT_LONGTEXT N_( "HTTP interface x509 PEM certificate file " \
83                           "(enables SSL)" )
84 #define KEY_TEXT N_( "Private key file" )
85 #define KEY_LONGTEXT N_( "HTTP interface x509 PEM private key file" )
86 #define CA_TEXT N_( "Root CA file" )
87 #define CA_LONGTEXT N_( "HTTP interface x509 PEM trusted root CA " \
88                         "certificates file" )
89 #define CRL_TEXT N_( "CRL file" )
90 #define CRL_LONGTEXT N_( "HTTP interace Certificates Revocation List file" )
91
92 vlc_module_begin();
93     set_shortname( _("HTTP"));
94     set_description( _("HTTP remote control interface") );
95     set_category( CAT_INTERFACE );
96     set_subcategory( SUBCAT_INTERFACE_GENERAL );
97         add_string ( "http-host", NULL, NULL, HOST_TEXT, HOST_LONGTEXT, VLC_TRUE );
98         add_string ( "http-src",  NULL, NULL, SRC_TEXT,  SRC_LONGTEXT,  VLC_TRUE );
99         add_string ( "http-charset", "UTF-8", NULL, CHARSET_TEXT, CHARSET_LONGTEXT, VLC_TRUE );
100         set_section( N_("HTTP SSL" ), 0 );
101         add_string ( "http-intf-cert", NULL, NULL, CERT_TEXT, CERT_LONGTEXT, VLC_TRUE );
102         add_string ( "http-intf-key",  NULL, NULL, KEY_TEXT,  KEY_LONGTEXT,  VLC_TRUE );
103         add_string ( "http-intf-ca",   NULL, NULL, CA_TEXT,   CA_LONGTEXT,   VLC_TRUE );
104         add_string ( "http-intf-crl",  NULL, NULL, CRL_TEXT,  CRL_LONGTEXT,  VLC_TRUE );
105     set_capability( "interface", 0 );
106     set_callbacks( Open, Close );
107 vlc_module_end();
108
109
110 /*****************************************************************************
111  * Local prototypes
112  *****************************************************************************/
113 static void Run          ( intf_thread_t *p_intf );
114
115 static int ParseDirectory( intf_thread_t *p_intf, char *psz_root,
116                            char *psz_dir );
117
118 #if !defined(SYS_DARWIN) && !defined(SYS_BEOS) && !defined(WIN32)
119 static int DirectoryCheck( char *psz_dir )
120 {
121     DIR           *p_dir;
122
123 #ifdef HAVE_SYS_STAT_H
124     struct stat   stat_info;
125
126     if( stat( psz_dir, &stat_info ) == -1 || !S_ISDIR( stat_info.st_mode ) )
127     {
128         return VLC_EGENERIC;
129     }
130 #endif
131
132     if( ( p_dir = opendir( psz_dir ) ) == NULL )
133     {
134         return VLC_EGENERIC;
135     }
136     closedir( p_dir );
137
138     return VLC_SUCCESS;
139 }
140 #endif
141
142 static int  HttpCallback( httpd_file_sys_t *p_args,
143                           httpd_file_t *,
144                           uint8_t *p_request,
145                           uint8_t **pp_data, int *pi_data );
146
147 static char *uri_extract_value( char *psz_uri, const char *psz_name,
148                                 char *psz_value, int i_value_max );
149 static int uri_test_param( char *psz_uri, const char *psz_name );
150
151 static void uri_decode_url_encoded( char *psz );
152
153 static char *Find_end_MRL( char *psz );
154 static playlist_item_t *parse_MRL( intf_thread_t *, char *psz, char *psz_name );
155
156 /*****************************************************************************
157  *
158  *****************************************************************************/
159 typedef struct mvar_s
160 {
161     char *name;
162     char *value;
163
164     int           i_field;
165     struct mvar_s **field;
166 } mvar_t;
167
168 #define STACK_MAX 100
169 typedef struct
170 {
171     char *stack[STACK_MAX];
172     int  i_stack;
173 } rpn_stack_t;
174
175 struct httpd_file_sys_t
176 {
177     intf_thread_t    *p_intf;
178     httpd_file_t     *p_file;
179     httpd_redirect_t *p_redir;
180     httpd_redirect_t *p_redir2;
181
182     char          *file;
183     char          *name;
184
185     vlc_bool_t    b_html;
186
187     /* inited for each access */
188     rpn_stack_t   stack;
189     mvar_t        *vars;
190 };
191
192 struct intf_sys_t
193 {
194     httpd_host_t        *p_httpd_host;
195
196     int                 i_files;
197     httpd_file_sys_t    **pp_files;
198
199     playlist_t          *p_playlist;
200     input_thread_t      *p_input;
201     vlm_t               *p_vlm;
202     char                *psz_html_type;
203     vlc_iconv_t         iconv_from_utf8, iconv_to_utf8;
204 };
205
206
207
208 /*****************************************************************************
209  * Activate: initialize and create stuff
210  *****************************************************************************/
211 static int Open( vlc_object_t *p_this )
212 {
213     intf_thread_t *p_intf = (intf_thread_t*)p_this;
214     intf_sys_t    *p_sys;
215     char          *psz_host;
216     char          *psz_address = "";
217     const char    *psz_cert = NULL, *psz_key = NULL, *psz_ca = NULL,
218                   *psz_crl = NULL;
219     int           i_port       = 0;
220     char          *psz_src;
221
222     psz_host = config_GetPsz( p_intf, "http-host" );
223     if( psz_host )
224     {
225         char *psz_parser;
226         psz_address = psz_host;
227
228         psz_parser = strchr( psz_host, ':' );
229         if( psz_parser )
230         {
231             *psz_parser++ = '\0';
232             i_port = atoi( psz_parser );
233         }
234     }
235
236     p_intf->p_sys = p_sys = malloc( sizeof( intf_sys_t ) );
237     if( !p_intf->p_sys )
238     {
239         return( VLC_ENOMEM );
240     }
241     p_sys->p_playlist = NULL;
242     p_sys->p_input    = NULL;
243     p_sys->p_vlm      = NULL;
244
245     /* determine Content-Type value for HTML pages */
246     psz_src = config_GetPsz( p_intf, "http-charset" );
247     if( psz_src == NULL || !*psz_src )
248     {
249         if( psz_src != NULL ) free( psz_src );
250         psz_src = strdup("UTF-8");
251     }
252
253     p_sys->psz_html_type = malloc( 20 + strlen( psz_src ) );
254     if( p_sys->psz_html_type == NULL )
255     {
256         free( p_sys );
257         free( psz_src );
258         return VLC_ENOMEM ;
259     }
260     sprintf( p_sys->psz_html_type, "text/html; charset=%s", psz_src );
261     msg_Dbg( p_intf, "using charset=%s", psz_src );
262
263     if( strcmp( psz_src, "UTF-8" ) )
264     {
265         p_sys->iconv_from_utf8 = vlc_iconv_open( psz_src, "UTF-8" );
266         if( p_sys->iconv_from_utf8 == (vlc_iconv_t)-1 )
267             msg_Warn( p_intf, "unable to perform charset conversion to %s",
268                       psz_src );
269         else
270         {
271             p_sys->iconv_to_utf8 = vlc_iconv_open( "UTF-8", psz_src );
272             if( p_sys->iconv_to_utf8 == (vlc_iconv_t)-1 )
273                 msg_Warn( p_intf,
274                           "unable to perform charset conversion from %s",
275                           psz_src );
276         }
277     }
278     else
279     {
280         p_sys->iconv_from_utf8 = p_sys->iconv_to_utf8 = (vlc_iconv_t)-1;
281     }
282
283     free( psz_src );
284
285     /* determine SSL configuration */
286     psz_cert = config_GetPsz( p_intf, "http-intf-cert" );
287     if ( psz_cert != NULL )
288     {
289         msg_Dbg( p_intf, "enabling TLS for HTTP interface (cert file: %s)",
290                  psz_cert );
291         psz_key = config_GetPsz( p_intf, "http-intf-key" );
292         psz_ca = config_GetPsz( p_intf, "http-intf-ca" );
293         psz_crl = config_GetPsz( p_intf, "http-intf-crl" );
294
295         if( i_port <= 0 )
296             i_port = 8443;
297     }
298     else
299     {
300         if( i_port <= 0 )
301             i_port= 8080;
302     }
303
304     msg_Dbg( p_intf, "base %s:%d", psz_address, i_port );
305
306     p_sys->p_httpd_host = httpd_TLSHostNew( VLC_OBJECT(p_intf), psz_address,
307                                             i_port, psz_cert, psz_key, psz_ca,
308                                             psz_crl );
309     if( p_sys->p_httpd_host == NULL )
310     {
311         msg_Err( p_intf, "cannot listen on %s:%d", psz_address, i_port );
312         free( p_sys->psz_html_type );
313         free( p_sys );
314         return VLC_EGENERIC;
315     }
316
317     if( psz_host )
318     {
319         free( psz_host );
320     }
321
322     p_sys->i_files  = 0;
323     p_sys->pp_files = NULL;
324
325 #if defined(SYS_DARWIN) || defined(SYS_BEOS) || defined(WIN32)
326     if ( ( psz_src = config_GetPsz( p_intf, "http-src" )) == NULL )
327     {
328         char * psz_vlcpath = p_intf->p_libvlc->psz_vlcpath;
329         psz_src = malloc( strlen(psz_vlcpath) + strlen("/share/http" ) + 1 );
330         if( !psz_src ) return VLC_ENOMEM;
331 #if defined(WIN32)
332         sprintf( psz_src, "%s/http", psz_vlcpath);
333 #else
334         sprintf( psz_src, "%s/share/http", psz_vlcpath);
335 #endif
336     }
337 #else
338     psz_src = config_GetPsz( p_intf, "http-src" );
339
340     if( !psz_src || *psz_src == '\0' )
341     {
342         if( !DirectoryCheck( "share/http" ) )
343         {
344             psz_src = strdup( "share/http" );
345         }
346         else if( !DirectoryCheck( DATA_PATH "/http" ) )
347         {
348             psz_src = strdup( DATA_PATH "/http" );
349         }
350     }
351 #endif
352
353     if( !psz_src || *psz_src == '\0' )
354     {
355         msg_Err( p_intf, "invalid src dir" );
356         goto failed;
357     }
358
359     /* remove trainling \ or / */
360     if( psz_src[strlen( psz_src ) - 1] == '\\' ||
361         psz_src[strlen( psz_src ) - 1] == '/' )
362     {
363         psz_src[strlen( psz_src ) - 1] = '\0';
364     }
365
366     ParseDirectory( p_intf, psz_src, psz_src );
367
368
369     if( p_sys->i_files <= 0 )
370     {
371         msg_Err( p_intf, "cannot find any files (%s)", psz_src );
372         goto failed;
373     }
374     p_intf->pf_run = Run;
375     free( psz_src );
376
377     return VLC_SUCCESS;
378
379 failed:
380     if( psz_src ) free( psz_src );
381     if( p_sys->pp_files )
382     {
383         free( p_sys->pp_files );
384     }
385     httpd_HostDelete( p_sys->p_httpd_host );
386     free( p_sys->psz_html_type ); 
387     if( p_sys->iconv_from_utf8 != (vlc_iconv_t)-1 )
388         vlc_iconv_close( p_sys->iconv_from_utf8 );
389     if( p_sys->iconv_to_utf8 != (vlc_iconv_t)-1 )
390         vlc_iconv_close( p_sys->iconv_to_utf8 );
391     free( p_sys );
392     return VLC_EGENERIC;
393 }
394
395 /*****************************************************************************
396  * Close: destroy interface
397  *****************************************************************************/
398 void Close ( vlc_object_t *p_this )
399 {
400     intf_thread_t *p_intf = (intf_thread_t *)p_this;
401     intf_sys_t    *p_sys = p_intf->p_sys;
402
403     int i;
404
405     if( p_sys->p_vlm )
406     {
407         vlm_Delete( p_sys->p_vlm );
408     }
409     for( i = 0; i < p_sys->i_files; i++ )
410     {
411        httpd_FileDelete( p_sys->pp_files[i]->p_file );
412        if( p_sys->pp_files[i]->p_redir )
413            httpd_RedirectDelete( p_sys->pp_files[i]->p_redir );
414        if( p_sys->pp_files[i]->p_redir2 )
415            httpd_RedirectDelete( p_sys->pp_files[i]->p_redir2 );
416
417        free( p_sys->pp_files[i]->file );
418        free( p_sys->pp_files[i]->name );
419        free( p_sys->pp_files[i] );
420     }
421     if( p_sys->pp_files )
422     {
423         free( p_sys->pp_files );
424     }
425     httpd_HostDelete( p_sys->p_httpd_host );
426     free( p_sys->psz_html_type );
427
428     if( p_sys->iconv_from_utf8 != (vlc_iconv_t)-1 )
429         vlc_iconv_close( p_sys->iconv_from_utf8 );
430     if( p_sys->iconv_to_utf8 != (vlc_iconv_t)-1 )
431         vlc_iconv_close( p_sys->iconv_to_utf8 );
432     free( p_sys );
433 }
434
435 /*****************************************************************************
436  * Run: http interface thread
437  *****************************************************************************/
438 static void Run( intf_thread_t *p_intf )
439 {
440     intf_sys_t     *p_sys = p_intf->p_sys;
441
442     while( !p_intf->b_die )
443     {
444         /* get the playlist */
445         if( p_sys->p_playlist == NULL )
446         {
447             p_sys->p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
448         }
449
450         /* Manage the input part */
451         if( p_sys->p_input == NULL )
452         {
453             if( p_sys->p_playlist )
454             {
455                 p_sys->p_input =
456                     vlc_object_find( p_sys->p_playlist,
457                                      VLC_OBJECT_INPUT,
458                                      FIND_CHILD );
459             }
460         }
461         else if( p_sys->p_input->b_dead )
462         {
463             vlc_object_release( p_sys->p_input );
464             p_sys->p_input = NULL;
465         }
466
467
468         /* Wait a bit */
469         msleep( INTF_IDLE_SLEEP );
470     }
471
472     if( p_sys->p_input )
473     {
474         vlc_object_release( p_sys->p_input );
475         p_sys->p_input = NULL;
476     }
477
478     if( p_sys->p_playlist )
479     {
480         vlc_object_release( p_sys->p_playlist );
481         p_sys->p_playlist = NULL;
482     }
483 }
484
485
486 /*****************************************************************************
487  * Local functions
488  *****************************************************************************/
489 #define MAX_DIR_SIZE 10240
490
491 /****************************************************************************
492  * FileToUrl: create a good name for an url from filename
493  ****************************************************************************/
494 static char *FileToUrl( char *name, vlc_bool_t *pb_index )
495 {
496     char *url, *p;
497
498     url = p = malloc( strlen( name ) + 1 );
499
500     *pb_index = VLC_FALSE;
501     if( !url || !p )
502     {
503         return NULL;
504     }
505
506 #ifdef WIN32
507     while( *name == '\\' || *name == '/' )
508 #else
509     while( *name == '/' )
510 #endif
511     {
512         name++;
513     }
514
515     *p++ = '/';
516     strcpy( p, name );
517
518 #ifdef WIN32
519     /* convert '\\' into '/' */
520     name = p;
521     while( *name )
522     {
523         if( *name == '\\' )
524         {
525             *p++ = '/';
526         }
527         name++;
528     }
529 #endif
530
531     /* index.* -> / */
532     if( ( p = strrchr( url, '/' ) ) != NULL )
533     {
534         if( !strncmp( p, "/index.", 7 ) )
535         {
536             p[1] = '\0';
537             *pb_index = VLC_TRUE;
538         }
539     }
540     return url;
541 }
542
543 static char *FromUTF8( intf_thread_t *p_intf, char *psz_utf8 )
544 {
545     intf_sys_t    *p_sys = p_intf->p_sys;
546
547     if ( p_sys->iconv_from_utf8 != (vlc_iconv_t)-1 )
548     {
549         char *psz_in = psz_utf8;
550         size_t i_in = strlen(psz_in);
551         size_t i_out = i_in * 2;
552         char *psz_local = malloc(i_out + 1);
553         char *psz_out = psz_local;
554
555         size_t i_ret = vlc_iconv( p_sys->iconv_from_utf8, &psz_in, &i_in,
556                                   &psz_out, &i_out );
557         if( i_ret == (size_t)-1 || i_in )
558         {
559             msg_Warn( p_intf,
560                       "failed to convert \"%s\" to desired charset (%s)",
561                       psz_utf8, strerror(errno) );
562             free( psz_local );
563             return strdup( psz_utf8 );
564         }
565
566         *psz_out = '\0';
567         return psz_local;
568     }
569     else
570         return strdup( psz_utf8 );
571 }
572
573 static char *ToUTF8( intf_thread_t *p_intf, char *psz_local )
574 {
575     intf_sys_t    *p_sys = p_intf->p_sys;
576
577     if ( p_sys->iconv_to_utf8 != (vlc_iconv_t)-1 )
578     {
579         char *psz_in = psz_local;
580         size_t i_in = strlen(psz_in);
581         size_t i_out = i_in * 6;
582         char *psz_utf8 = malloc(i_out + 1);
583         char *psz_out = psz_utf8;
584
585         size_t i_ret = vlc_iconv( p_sys->iconv_to_utf8, &psz_in, &i_in,
586                                   &psz_out, &i_out );
587         if( i_ret == (size_t)-1 || i_in )
588         {
589             msg_Warn( p_intf,
590                       "failed to convert \"%s\" to desired charset (%s)",
591                       psz_local, strerror(errno) );
592             free( psz_utf8 );
593             return strdup( psz_local );
594         }
595
596         *psz_out = '\0';
597         return psz_utf8;
598     }
599     else
600         return strdup( psz_local );
601 }
602
603 /****************************************************************************
604  * ParseDirectory: parse recursively a directory, adding each file
605  ****************************************************************************/
606 static int ParseDirectory( intf_thread_t *p_intf, char *psz_root,
607                            char *psz_dir )
608 {
609     intf_sys_t     *p_sys = p_intf->p_sys;
610     char           dir[MAX_DIR_SIZE];
611 #ifdef HAVE_SYS_STAT_H
612     struct stat   stat_info;
613 #endif
614     DIR           *p_dir;
615     struct dirent *p_dir_content;
616     vlc_acl_t     *p_acl;
617     FILE          *file;
618
619     char          *user = NULL;
620     char          *password = NULL;
621
622     int           i_dirlen;
623
624 #ifdef HAVE_SYS_STAT_H
625     if( stat( psz_dir, &stat_info ) == -1 || !S_ISDIR( stat_info.st_mode ) )
626     {
627         return VLC_EGENERIC;
628     }
629 #endif
630
631     if( ( p_dir = opendir( psz_dir ) ) == NULL )
632     {
633         msg_Err( p_intf, "cannot open dir (%s)", psz_dir );
634         return VLC_EGENERIC;
635     }
636
637     i_dirlen = strlen( psz_dir );
638     if( i_dirlen + 10 > MAX_DIR_SIZE )
639     {
640         msg_Warn( p_intf, "skipping too deep dir (%s)", psz_dir );
641         return 0;
642     }
643
644     msg_Dbg( p_intf, "dir=%s", psz_dir );
645
646     sprintf( dir, "%s/.access", psz_dir );
647     if( ( file = fopen( dir, "r" ) ) != NULL )
648     {
649         char line[1024];
650         int  i_size;
651
652         msg_Dbg( p_intf, "find .access in dir=%s", psz_dir );
653
654         i_size = fread( line, 1, 1023, file );
655         if( i_size > 0 )
656         {
657             char *p;
658             while( i_size > 0 && ( line[i_size-1] == '\n' ||
659                    line[i_size-1] == '\r' ) )
660             {
661                 i_size--;
662             }
663
664             line[i_size] = '\0';
665
666             p = strchr( line, ':' );
667             if( p )
668             {
669                 *p++ = '\0';
670                 user = strdup( line );
671                 password = strdup( p );
672             }
673         }
674         msg_Dbg( p_intf, "using user=%s password=%s (read=%d)",
675                  user, password, i_size );
676
677         fclose( file );
678     }
679
680     sprintf( dir, "%s/.hosts", psz_dir );
681     p_acl = ACL_Create( p_intf, VLC_FALSE );
682     if( ACL_LoadFile( p_acl, dir ) )
683     {
684         ACL_Destroy( p_acl );
685         p_acl = NULL;
686     }
687
688     for( ;; )
689     {
690         /* parse psz_src dir */
691         if( ( p_dir_content = readdir( p_dir ) ) == NULL )
692         {
693             break;
694         }
695
696         if( ( p_dir_content->d_name[0] == '.' )
697          || ( i_dirlen + strlen( p_dir_content->d_name ) > MAX_DIR_SIZE ) )
698             continue;
699         
700         sprintf( dir, "%s/%s", psz_dir, p_dir_content->d_name );
701         if( ParseDirectory( p_intf, psz_root, dir ) )
702         {
703             httpd_file_sys_t *f = malloc( sizeof( httpd_file_sys_t ) );
704             vlc_bool_t b_index;
705             char *psz_tmp;
706
707             f->p_intf  = p_intf;
708             f->p_file = NULL;
709             f->p_redir = NULL;
710             f->p_redir2 = NULL;
711             psz_tmp = vlc_fix_readdir_charset( p_intf, dir );
712             f->file = FromUTF8( p_intf, psz_tmp );
713             free( psz_tmp );
714             psz_tmp = vlc_fix_readdir_charset( p_intf,
715                                                &dir[strlen( psz_root )] );
716             f->name = FileToUrl( psz_tmp, &b_index );
717             free( psz_tmp );
718             f->b_html = strstr( &dir[strlen( psz_root )], ".htm" ) ? VLC_TRUE : VLC_FALSE;
719
720             if( !f->name )
721             {
722                 msg_Err( p_intf , "unable to parse directory" );
723                 closedir( p_dir );
724                 free( f );
725                 return( VLC_ENOMEM );
726             }
727             msg_Dbg( p_intf, "file=%s (url=%s)",
728                      f->file, f->name );
729
730             f->p_file = httpd_FileNew( p_sys->p_httpd_host,
731                                        f->name,
732                                        f->b_html ? p_sys->psz_html_type : NULL,
733                                        user, password, p_acl,
734                                        HttpCallback, f );
735
736             if( f->p_file )
737             {
738                 TAB_APPEND( p_sys->i_files, p_sys->pp_files, f );
739             }
740             /* for url that ends by / add
741              *  - a redirect from rep to rep/
742              *  - in case of index.* rep/index.html to rep/ */
743             if( f && f->name[strlen(f->name) - 1] == '/' )
744             {
745                 char *psz_redir = strdup( f->name );
746                 char *p;
747                 psz_redir[strlen( psz_redir ) - 1] = '\0';
748
749                 msg_Dbg( p_intf, "redir=%s -> %s", psz_redir, f->name );
750                 f->p_redir = httpd_RedirectNew( p_sys->p_httpd_host, f->name, psz_redir );
751                 free( psz_redir );
752
753                 if( b_index && ( p = strstr( f->file, "index." ) ) )
754                 {
755                     asprintf( &psz_redir, "%s%s", f->name, p );
756
757                     msg_Dbg( p_intf, "redir=%s -> %s", psz_redir, f->name );
758                     f->p_redir2 = httpd_RedirectNew( p_sys->p_httpd_host,
759                                                      f->name, psz_redir );
760
761                     free( psz_redir );
762                 }
763             }
764         }
765     }
766
767     if( user )
768     {
769         free( user );
770     }
771     if( password )
772     {
773         free( password );
774     }
775
776     ACL_Destroy( p_acl );
777     closedir( p_dir );
778
779     return VLC_SUCCESS;
780 }
781
782 /****************************************************************************
783  * var and set handling
784  ****************************************************************************/
785
786 static mvar_t *mvar_New( const char *name, const char *value )
787 {
788     mvar_t *v = malloc( sizeof( mvar_t ) );
789
790     if( !v ) return NULL;
791     v->name = strdup( name );
792     v->value = strdup( value ? value : "" );
793
794     v->i_field = 0;
795     v->field = malloc( sizeof( mvar_t * ) );
796     v->field[0] = NULL;
797
798     return v;
799 }
800
801 static void mvar_Delete( mvar_t *v )
802 {
803     int i;
804
805     free( v->name );
806     free( v->value );
807
808     for( i = 0; i < v->i_field; i++ )
809     {
810         mvar_Delete( v->field[i] );
811     }
812     free( v->field );
813     free( v );
814 }
815
816 static void mvar_AppendVar( mvar_t *v, mvar_t *f )
817 {
818     v->field = realloc( v->field, sizeof( mvar_t * ) * ( v->i_field + 2 ) );
819     v->field[v->i_field] = f;
820     v->i_field++;
821 }
822
823 static mvar_t *mvar_Duplicate( const mvar_t *v )
824 {
825     int i;
826     mvar_t *n;
827
828     n = mvar_New( v->name, v->value );
829     for( i = 0; i < v->i_field; i++ )
830     {
831         mvar_AppendVar( n, mvar_Duplicate( v->field[i] ) );
832     }
833
834     return n;
835 }
836
837 static void mvar_PushVar( mvar_t *v, mvar_t *f )
838 {
839     v->field = realloc( v->field, sizeof( mvar_t * ) * ( v->i_field + 2 ) );
840     if( v->i_field > 0 )
841     {
842         memmove( &v->field[1], &v->field[0], sizeof( mvar_t * ) * v->i_field );
843     }
844     v->field[0] = f;
845     v->i_field++;
846 }
847
848 static void mvar_RemoveVar( mvar_t *v, mvar_t *f )
849 {
850     int i;
851     for( i = 0; i < v->i_field; i++ )
852     {
853         if( v->field[i] == f )
854         {
855             break;
856         }
857     }
858     if( i >= v->i_field )
859     {
860         return;
861     }
862
863     if( i + 1 < v->i_field )
864     {
865         memmove( &v->field[i], &v->field[i+1], sizeof( mvar_t * ) * ( v->i_field - i - 1 ) );
866     }
867     v->i_field--;
868     /* FIXME should do a realloc */
869 }
870
871 static mvar_t *mvar_GetVar( mvar_t *s, const char *name )
872 {
873     int i;
874     char base[512], *field, *p;
875     int  i_index;
876
877     /* format: name[index].field */
878
879     field = strchr( name, '.' );
880     if( field )
881     {
882         int i = field - name;
883         strncpy( base, name, i );
884         base[i] = '\0';
885         field++;
886     }
887     else
888     {
889         strcpy( base, name );
890     }
891
892     if( ( p = strchr( base, '[' ) ) )
893     {
894         *p++ = '\0';
895         sscanf( p, "%d]", &i_index );
896         if( i_index < 0 )
897         {
898             return NULL;
899         }
900     }
901     else
902     {
903         i_index = 0;
904     }
905
906     for( i = 0; i < s->i_field; i++ )
907     {
908         if( !strcmp( s->field[i]->name, base ) )
909         {
910             if( i_index > 0 )
911             {
912                 i_index--;
913             }
914             else
915             {
916                 if( field )
917                 {
918                     return mvar_GetVar( s->field[i], field );
919                 }
920                 else
921                 {
922                     return s->field[i];
923                 }
924             }
925         }
926     }
927     return NULL;
928 }
929
930
931
932 static char *mvar_GetValue( mvar_t *v, char *field )
933 {
934     if( *field == '\0' )
935     {
936         return v->value;
937     }
938     else
939     {
940         mvar_t *f = mvar_GetVar( v, field );
941         if( f )
942         {
943             return f->value;
944         }
945         else
946         {
947             return field;
948         }
949     }
950 }
951
952 static void mvar_PushNewVar( mvar_t *vars, const char *name,
953                              const char *value )
954 {
955     mvar_t *f = mvar_New( name, value );
956     mvar_PushVar( vars, f );
957 }
958
959 static void mvar_AppendNewVar( mvar_t *vars, const char *name,
960                                const char *value )
961 {
962     mvar_t *f = mvar_New( name, value );
963     mvar_AppendVar( vars, f );
964 }
965
966
967 /* arg= start[:stop[:step]],.. */
968 static mvar_t *mvar_IntegerSetNew( const char *name, const char *arg )
969 {
970     char *dup = strdup( arg );
971     char *str = dup;
972     mvar_t *s = mvar_New( name, "set" );
973
974
975     while( str )
976     {
977         char *p;
978         int  i_start,i_stop,i_step;
979         int  i_match;
980
981         p = strchr( str, ',' );
982         if( p )
983         {
984             *p++ = '\0';
985         }
986
987         i_step = 0;
988         i_match = sscanf( str, "%d:%d:%d", &i_start, &i_stop, &i_step );
989
990         if( i_match == 1 )
991         {
992             i_stop = i_start;
993             i_step = 1;
994         }
995         else if( i_match == 2 )
996         {
997             i_step = i_start < i_stop ? 1 : -1;
998         }
999
1000         if( i_match >= 1 )
1001         {
1002             int i;
1003
1004             if( ( i_start <= i_stop && i_step > 0 ) ||
1005                 ( i_start >= i_stop && i_step < 0 ) )
1006             {
1007                 for( i = i_start; ; i += i_step )
1008                 {
1009                     char   value[79];
1010
1011                     if( ( i_step > 0 && i > i_stop ) ||
1012                         ( i_step < 0 && i < i_stop ) )
1013                     {
1014                         break;
1015                     }
1016
1017                     sprintf( value, "%d", i );
1018
1019                     mvar_PushNewVar( s, name, value );
1020                 }
1021             }
1022         }
1023         str = p;
1024     }
1025
1026     free( dup );
1027     return s;
1028 }
1029
1030 static void PlaylistListNode( intf_thread_t *p_intf, playlist_t *p_pl,
1031                               playlist_item_t *p_node, char *name, mvar_t *s,
1032                               int i_depth )
1033 {
1034     if( p_node != NULL )
1035     {
1036         if( p_node->i_children == -1 )
1037         {
1038             char value[512];
1039             char *psz;
1040             mvar_t *itm = mvar_New( name, "set" );
1041
1042             sprintf( value, "%d", ( p_pl->status.p_item == p_node )? 1 : 0 );
1043             mvar_AppendNewVar( itm, "current", value );
1044
1045             sprintf( value, "%d", p_node->input.i_id );
1046             mvar_AppendNewVar( itm, "index", value );
1047
1048             psz = FromUTF8( p_intf, p_node->input.psz_name );
1049             mvar_AppendNewVar( itm, "name", psz );
1050             free( psz );
1051
1052             psz = FromUTF8( p_intf, p_node->input.psz_uri );
1053             mvar_AppendNewVar( itm, "uri", psz );
1054             free( psz );
1055
1056             sprintf( value, "Item");
1057             mvar_AppendNewVar( itm, "type", value );
1058
1059             sprintf( value, "%d", i_depth );
1060             mvar_AppendNewVar( itm, "depth", value );
1061
1062             mvar_AppendVar( s, itm );
1063         }
1064         else
1065         {
1066             char value[512];
1067             char *psz;
1068             int i_child;
1069             mvar_t *itm = mvar_New( name, "set" );
1070
1071             psz = FromUTF8( p_intf, p_node->input.psz_name );
1072             mvar_AppendNewVar( itm, "name", psz );
1073             mvar_AppendNewVar( itm, "uri", psz );
1074             free( psz );
1075
1076             sprintf( value, "Node" );
1077             mvar_AppendNewVar( itm, "type", value );
1078
1079             sprintf( value, "%d", p_node->input.i_id );
1080             mvar_AppendNewVar( itm, "index", value );
1081
1082             sprintf( value, "%d", p_node->i_children);
1083             mvar_AppendNewVar( itm, "i_children", value );
1084
1085             sprintf( value, "%d", i_depth );
1086             mvar_AppendNewVar( itm, "depth", value );
1087
1088             mvar_AppendVar( s, itm );
1089
1090             for (i_child = 0 ; i_child < p_node->i_children ; i_child++)
1091                 PlaylistListNode( p_intf, p_pl, p_node->pp_children[i_child],
1092                                   name, s, i_depth + 1);
1093
1094         }
1095     }
1096 }
1097
1098 static mvar_t *mvar_PlaylistSetNew( intf_thread_t *p_intf, char *name,
1099                                     playlist_t *p_pl )
1100 {
1101     playlist_view_t *p_view;
1102     mvar_t *s = mvar_New( name, "set" );
1103
1104
1105     vlc_mutex_lock( &p_pl->object_lock );
1106
1107     p_view = playlist_ViewFind( p_pl, VIEW_CATEGORY ); /* FIXME */
1108
1109     if( p_view != NULL )
1110         PlaylistListNode( p_intf, p_pl, p_view->p_root, name, s, 0 );
1111
1112     vlc_mutex_unlock( &p_pl->object_lock );
1113
1114     return s;
1115 }
1116
1117 static mvar_t *mvar_InfoSetNew( intf_thread_t *p_intf, char *name,
1118                                 input_thread_t *p_input )
1119 {
1120     mvar_t *s = mvar_New( name, "set" );
1121     int i, j;
1122
1123     if( p_input == NULL )
1124     {
1125         return s;
1126     }
1127
1128     vlc_mutex_lock( &p_input->input.p_item->lock );
1129     for ( i = 0; i < p_input->input.p_item->i_categories; i++ )
1130     {
1131         info_category_t *p_category = p_input->input.p_item->pp_categories[i];
1132         char *psz;
1133
1134         mvar_t *cat  = mvar_New( name, "set" );
1135         mvar_t *iset = mvar_New( "info", "set" );
1136
1137         psz = FromUTF8( p_intf, p_category->psz_name );
1138         mvar_AppendNewVar( cat, "name", psz );
1139         free( psz );
1140         mvar_AppendVar( cat, iset );
1141
1142         for ( j = 0; j < p_category->i_infos; j++ )
1143         {
1144             info_t *p_info = p_category->pp_infos[j];
1145             mvar_t *info = mvar_New( "info", "" );
1146             char *psz_name = FromUTF8( p_intf, p_info->psz_name );
1147             char *psz_value = FromUTF8( p_intf, p_info->psz_value );
1148
1149             msg_Dbg( p_input, "adding info name=%s value=%s",
1150                      psz_name, psz_value );
1151             mvar_AppendNewVar( info, "name",  psz_name );
1152             mvar_AppendNewVar( info, "value", psz_value );
1153             free( psz_name );
1154             free( psz_value );
1155             mvar_AppendVar( iset, info );
1156         }
1157         mvar_AppendVar( s, cat );
1158     }
1159     vlc_mutex_unlock( &p_input->input.p_item->lock );
1160
1161     return s;
1162 }
1163 #if 0
1164 static mvar_t *mvar_HttpdInfoSetNew( char *name, httpd_t *p_httpd, int i_type )
1165 {
1166     mvar_t       *s = mvar_New( name, "set" );
1167     httpd_info_t info;
1168     int          i;
1169
1170     if( !p_httpd->pf_control( p_httpd, i_type, &info, NULL ) )
1171     {
1172         for( i= 0; i < info.i_count; )
1173         {
1174             mvar_t *inf;
1175
1176             inf = mvar_New( name, "set" );
1177             do
1178             {
1179                 /* fprintf( stderr," mvar_HttpdInfoSetNew: append name=`%s' value=`%s'\n",
1180                             info.info[i].psz_name, info.info[i].psz_value ); */
1181                 mvar_AppendNewVar( inf,
1182                                    info.info[i].psz_name,
1183                                    info.info[i].psz_value );
1184                 i++;
1185             } while( i < info.i_count && strcmp( info.info[i].psz_name, "id" ) );
1186             mvar_AppendVar( s, inf );
1187         }
1188     }
1189
1190     /* free mem */
1191     for( i = 0; i < info.i_count; i++ )
1192     {
1193         free( info.info[i].psz_name );
1194         free( info.info[i].psz_value );
1195     }
1196     if( info.i_count > 0 )
1197     {
1198         free( info.info );
1199     }
1200
1201     return s;
1202 }
1203 #endif
1204
1205 /* Utility function for scandir */
1206 static int Filter( const struct dirent *foo )
1207 {
1208     return VLC_TRUE;
1209 }
1210
1211 static mvar_t *mvar_FileSetNew( intf_thread_t *p_intf, char *name,
1212                                 char *psz_dir )
1213 {
1214     mvar_t *s = mvar_New( name, "set" );
1215     char          tmp[MAX_DIR_SIZE], dir[MAX_DIR_SIZE], *p, *src;
1216 #ifdef HAVE_SYS_STAT_H
1217     struct stat   stat_info;
1218 #endif
1219     struct dirent **pp_dir_content;
1220     int           i_dir_content, i;
1221     char          sep;
1222
1223     /* convert all / to native separator */
1224 #if defined( WIN32 )
1225     while( (p = strchr( psz_dir, '/' )) )
1226     {
1227         *p = '\\';
1228     }
1229     sep = '\\';
1230 #else
1231     sep = '/';
1232 #endif
1233
1234     /* remove trailling separator */
1235     while( strlen( psz_dir ) > 1 &&
1236 #if defined( WIN32 )
1237            !( strlen(psz_dir)==3 && psz_dir[1]==':' && psz_dir[2]==sep ) &&
1238 #endif
1239            psz_dir[strlen( psz_dir ) -1 ] == sep )
1240     {
1241         psz_dir[strlen( psz_dir ) -1 ]  ='\0';
1242     }
1243     /* remove double separator */
1244     for( p = src = psz_dir; *src != '\0'; src++, p++ )
1245     {
1246         if( src[0] == sep && src[1] == sep )
1247         {
1248             src++;
1249         }
1250         *p = *src;
1251     }
1252     *p = '\0';
1253
1254     if( *psz_dir == '\0' )
1255     {
1256         return s;
1257     }
1258
1259     if( *psz_dir == '~' )
1260     {
1261         /* This is incomplete : we should also support the ~cmassiot/ syntax. */
1262         snprintf( dir, sizeof(dir), "%s/%s", p_intf->p_vlc->psz_homedir,
1263                   psz_dir + 1 );
1264         psz_dir = dir;
1265     }
1266
1267     /* first fix all .. dir */
1268     p = src = psz_dir;
1269     while( *src )
1270     {
1271         if( src[0] == '.' && src[1] == '.' )
1272         {
1273             src += 2;
1274             if( p <= &psz_dir[1] )
1275             {
1276                 continue;
1277             }
1278
1279             p -= 2;
1280
1281             while( p > &psz_dir[1] && *p != sep )
1282             {
1283                 p--;
1284             }
1285         }
1286         else if( *src == sep )
1287         {
1288             if( p > psz_dir && p[-1] == sep )
1289             {
1290                 src++;
1291             }
1292             else
1293             {
1294                 *p++ = *src++;
1295             }
1296         }
1297         else
1298         {
1299             do
1300             {
1301                 *p++ = *src++;
1302             } while( *src && *src != sep );
1303         }
1304     }
1305     *p = '\0';
1306
1307
1308 #ifdef HAVE_SYS_STAT_H
1309     if( stat( psz_dir, &stat_info ) == -1 || !S_ISDIR( stat_info.st_mode ) )
1310     {
1311         return s;
1312     }
1313 #endif
1314
1315     /* parse psz_src dir */
1316     if( ( i_dir_content = scandir( psz_dir, &pp_dir_content, Filter,
1317                                    alphasort ) ) == -1 )
1318     {
1319         msg_Warn( p_intf, "scandir error on %s (%s)", psz_dir,
1320                   strerror(errno) );
1321         return s;
1322     }
1323
1324     /* remove trailing / or \ */
1325     for( p = &psz_dir[strlen( psz_dir) - 1];
1326          p >= psz_dir && ( *p =='/' || *p =='\\' ); p-- )
1327     {
1328         *p = '\0';
1329     }
1330
1331     for( i = 0; i < i_dir_content; i++ )
1332     {
1333         struct dirent *p_dir_content = pp_dir_content[i];
1334         mvar_t *f;
1335         const char *psz_ext;
1336         char *psz_name, *psz_tmp;
1337
1338         if( !strcmp( p_dir_content->d_name, "." ) )
1339         {
1340             continue;
1341         }
1342
1343         snprintf( tmp, sizeof(tmp), "%s/%s", psz_dir, p_dir_content->d_name );
1344
1345 #ifdef HAVE_SYS_STAT_H
1346         if( stat( tmp, &stat_info ) == -1 )
1347         {
1348             continue;
1349         }
1350 #endif
1351         f = mvar_New( name, "set" );
1352
1353         psz_tmp = vlc_fix_readdir_charset( p_intf, p_dir_content->d_name );
1354         psz_name = FromUTF8( p_intf, psz_tmp );
1355         free( psz_tmp );
1356         snprintf( tmp, sizeof(tmp), "%s/%s", psz_dir, psz_name );
1357         mvar_AppendNewVar( f, "name", tmp );
1358         mvar_AppendNewVar( f, "basename", psz_name );
1359
1360         /* put file extension in 'ext' */
1361         psz_ext = strrchr( psz_name, '.' );
1362         mvar_AppendNewVar( f, "ext", psz_ext != NULL ? psz_ext + 1 : "" );
1363
1364         free( psz_name );
1365
1366 #ifdef HAVE_SYS_STAT_H
1367         if( S_ISDIR( stat_info.st_mode ) )
1368         {
1369             mvar_AppendNewVar( f, "type", "directory" );
1370         }
1371         else if( S_ISREG( stat_info.st_mode ) )
1372         {
1373             mvar_AppendNewVar( f, "type", "file" );
1374         }
1375         else
1376         {
1377             mvar_AppendNewVar( f, "type", "unknown" );
1378         }
1379
1380         sprintf( tmp, I64Fd, (int64_t)stat_info.st_size );
1381         mvar_AppendNewVar( f, "size", tmp );
1382
1383         /* FIXME memory leak FIXME */
1384 #ifdef HAVE_CTIME_R
1385         ctime_r( &stat_info.st_mtime, tmp );
1386         mvar_AppendNewVar( f, "date", tmp );
1387 #else
1388         mvar_AppendNewVar( f, "date", ctime( &stat_info.st_mtime ) );
1389 #endif
1390
1391 #else
1392         mvar_AppendNewVar( f, "type", "unknown" );
1393         mvar_AppendNewVar( f, "size", "unknown" );
1394         mvar_AppendNewVar( f, "date", "unknown" );
1395 #endif
1396         mvar_AppendVar( s, f );
1397     }
1398
1399     return s;
1400 }
1401
1402 static mvar_t *mvar_VlmSetNew( char *name, vlm_t *vlm )
1403 {
1404     mvar_t        *s = mvar_New( name, "set" );
1405     vlm_message_t *msg;
1406     int    i;
1407
1408     if( vlm == NULL ) return s;
1409
1410     if( vlm_ExecuteCommand( vlm, "show", &msg ) )
1411     {
1412         return s;
1413     }
1414
1415     for( i = 0; i < msg->i_child; i++ )
1416     {
1417         /* Over media, schedule */
1418         vlm_message_t *ch = msg->child[i];
1419         int j;
1420
1421         for( j = 0; j < ch->i_child; j++ )
1422         {
1423             /* Over name */
1424             vlm_message_t *el = ch->child[j];
1425             vlm_message_t *inf, *desc;
1426             mvar_t        *set;
1427             char          psz[500];
1428             int k;
1429
1430             sprintf( psz, "show %s", el->psz_name );
1431             if( vlm_ExecuteCommand( vlm, psz, &inf ) )
1432                 continue;
1433             desc = inf->child[0];
1434
1435             /* Add a node with name and info */
1436             set = mvar_New( name, "set" );
1437             mvar_AppendNewVar( set, "name", el->psz_name );
1438
1439             for( k = 0; k < desc->i_child; k++ )
1440             {
1441                 vlm_message_t *ch = desc->child[k];
1442                 if( ch->i_child > 0 )
1443                 {
1444                     int c;
1445                     mvar_t *n = mvar_New( ch->psz_name, "set" );
1446
1447                     for( c = 0; c < ch->i_child; c++ )
1448                     {
1449                         if( ch->child[c]->psz_value )
1450                         {
1451                             mvar_AppendNewVar( n, ch->child[c]->psz_name, ch->child[c]->psz_value );
1452                         }
1453                         else
1454                         {
1455                             mvar_t *in = mvar_New( ch->psz_name, ch->child[c]->psz_name );
1456                             mvar_AppendVar( n, in );
1457                         }
1458                     }
1459                     mvar_AppendVar( set, n );
1460                 }
1461                 else
1462                 {
1463                     mvar_AppendNewVar( set, ch->psz_name, ch->psz_value );
1464                 }
1465             }
1466             vlm_MessageDelete( inf );
1467
1468             mvar_AppendVar( s, set );
1469         }
1470     }
1471     vlm_MessageDelete( msg );
1472
1473     return s;
1474 }
1475
1476
1477 static void SSInit( rpn_stack_t * );
1478 static void SSClean( rpn_stack_t * );
1479 static void EvaluateRPN( intf_thread_t *p_intf, mvar_t  *, rpn_stack_t *,
1480                          char * );
1481
1482 static void SSPush  ( rpn_stack_t *, char * );
1483 static char *SSPop  ( rpn_stack_t * );
1484
1485 static void SSPushN ( rpn_stack_t *, int );
1486 static int  SSPopN  ( rpn_stack_t *, mvar_t  * );
1487
1488
1489 /****************************************************************************
1490  * Macro handling
1491  ****************************************************************************/
1492 typedef struct
1493 {
1494     char *id;
1495     char *param1;
1496     char *param2;
1497 } macro_t;
1498
1499 static int FileLoad( FILE *f, char **pp_data, int *pi_data )
1500 {
1501     int i_read;
1502
1503     /* just load the file */
1504     *pi_data = 0;
1505     *pp_data = malloc( 1025 );  /* +1 for \0 */
1506     while( ( i_read = fread( &(*pp_data)[*pi_data], 1, 1024, f ) ) == 1024 )
1507     {
1508         *pi_data += 1024;
1509         *pp_data = realloc( *pp_data, *pi_data  + 1025 );
1510     }
1511     if( i_read > 0 )
1512     {
1513         *pi_data += i_read;
1514     }
1515     (*pp_data)[*pi_data] = '\0';
1516
1517     return VLC_SUCCESS;
1518 }
1519
1520 static int MacroParse( macro_t *m, char *psz_src )
1521 {
1522     char *dup = strdup( (char *)psz_src );
1523     char *src = dup;
1524     char *p;
1525     int     i_skip;
1526
1527 #define EXTRACT( name, l ) \
1528         src += l;    \
1529         p = strchr( src, '"' );             \
1530         if( p )                             \
1531         {                                   \
1532             *p++ = '\0';                    \
1533         }                                   \
1534         m->name = strdup( src );            \
1535         if( !p )                            \
1536         {                                   \
1537             break;                          \
1538         }                                   \
1539         src = p;
1540
1541     /* init m */
1542     m->id = NULL;
1543     m->param1 = NULL;
1544     m->param2 = NULL;
1545
1546     /* parse */
1547     src += 4;
1548
1549     while( *src )
1550     {
1551         while( *src == ' ')
1552         {
1553             src++;
1554         }
1555         if( !strncmp( src, "id=\"", 4 ) )
1556         {
1557             EXTRACT( id, 4 );
1558         }
1559         else if( !strncmp( src, "param1=\"", 8 ) )
1560         {
1561             EXTRACT( param1, 8 );
1562         }
1563         else if( !strncmp( src, "param2=\"", 8 ) )
1564         {
1565             EXTRACT( param2, 8 );
1566         }
1567         else
1568         {
1569             break;
1570         }
1571     }
1572     if( strstr( src, "/>" ) )
1573     {
1574         src = strstr( src, "/>" ) + 2;
1575     }
1576     else
1577     {
1578         src += strlen( src );
1579     }
1580
1581     if( m->id == NULL )
1582     {
1583         m->id = strdup( "" );
1584     }
1585     if( m->param1 == NULL )
1586     {
1587         m->param1 = strdup( "" );
1588     }
1589     if( m->param2 == NULL )
1590     {
1591         m->param2 = strdup( "" );
1592     }
1593     i_skip = src - dup;
1594
1595     free( dup );
1596     return i_skip;
1597 #undef EXTRACT
1598 }
1599
1600 static void MacroClean( macro_t *m )
1601 {
1602     free( m->id );
1603     free( m->param1 );
1604     free( m->param2 );
1605 }
1606
1607 enum macroType
1608 {
1609     MVLC_UNKNOWN = 0,
1610     MVLC_CONTROL,
1611         MVLC_PLAY,
1612         MVLC_STOP,
1613         MVLC_PAUSE,
1614         MVLC_NEXT,
1615         MVLC_PREVIOUS,
1616         MVLC_ADD,
1617         MVLC_DEL,
1618         MVLC_EMPTY,
1619         MVLC_SEEK,
1620         MVLC_KEEP,
1621         MVLC_SORT,
1622         MVLC_MOVE,
1623         MVLC_VOLUME,
1624         MVLC_FULLSCREEN,
1625
1626         MVLC_CLOSE,
1627         MVLC_SHUTDOWN,
1628
1629         MVLC_VLM_NEW,
1630         MVLC_VLM_SETUP,
1631         MVLC_VLM_DEL,
1632         MVLC_VLM_PLAY,
1633         MVLC_VLM_PAUSE,
1634         MVLC_VLM_STOP,
1635         MVLC_VLM_SEEK,
1636         MVLC_VLM_LOAD,
1637         MVLC_VLM_SAVE,
1638
1639     MVLC_FOREACH,
1640     MVLC_IF,
1641     MVLC_RPN,
1642     MVLC_STACK,
1643     MVLC_ELSE,
1644     MVLC_END,
1645     MVLC_GET,
1646     MVLC_SET,
1647         MVLC_INT,
1648         MVLC_FLOAT,
1649         MVLC_STRING,
1650
1651     MVLC_VALUE
1652 };
1653
1654 static struct
1655 {
1656     char *psz_name;
1657     int  i_type;
1658 }
1659 StrToMacroTypeTab [] =
1660 {
1661     { "control",    MVLC_CONTROL },
1662         /* player control */
1663         { "play",           MVLC_PLAY },
1664         { "stop",           MVLC_STOP },
1665         { "pause",          MVLC_PAUSE },
1666         { "next",           MVLC_NEXT },
1667         { "previous",       MVLC_PREVIOUS },
1668         { "seek",           MVLC_SEEK },
1669         { "keep",           MVLC_KEEP },
1670         { "fullscreen",     MVLC_FULLSCREEN },
1671         { "volume",         MVLC_VOLUME },
1672
1673         /* playlist management */
1674         { "add",            MVLC_ADD },
1675         { "delete",         MVLC_DEL },
1676         { "empty",          MVLC_EMPTY },
1677         { "sort",           MVLC_SORT },
1678         { "move",           MVLC_MOVE },
1679
1680         /* admin control */
1681         { "close",          MVLC_CLOSE },
1682         { "shutdown",       MVLC_SHUTDOWN },
1683
1684         /* vlm control */
1685         { "vlm_new",        MVLC_VLM_NEW },
1686         { "vlm_setup",      MVLC_VLM_SETUP },
1687         { "vlm_del",        MVLC_VLM_DEL },
1688         { "vlm_play",       MVLC_VLM_PLAY },
1689         { "vlm_pause",      MVLC_VLM_PAUSE },
1690         { "vlm_stop",       MVLC_VLM_STOP },
1691         { "vlm_seek",       MVLC_VLM_SEEK },
1692         { "vlm_load",       MVLC_VLM_LOAD },
1693         { "vlm_save",       MVLC_VLM_SAVE },
1694
1695     { "rpn",        MVLC_RPN },
1696     { "stack",      MVLC_STACK },
1697
1698     { "foreach",    MVLC_FOREACH },
1699     { "value",      MVLC_VALUE },
1700
1701     { "if",         MVLC_IF },
1702     { "else",       MVLC_ELSE },
1703     { "end",        MVLC_END },
1704     { "get",        MVLC_GET },
1705     { "set",        MVLC_SET },
1706         { "int",            MVLC_INT },
1707         { "float",          MVLC_FLOAT },
1708         { "string",         MVLC_STRING },
1709
1710     /* end */
1711     { NULL,         MVLC_UNKNOWN }
1712 };
1713
1714 static int StrToMacroType( char *name )
1715 {
1716     int i;
1717
1718     if( !name || *name == '\0')
1719     {
1720         return MVLC_UNKNOWN;
1721     }
1722     for( i = 0; StrToMacroTypeTab[i].psz_name != NULL; i++ )
1723     {
1724         if( !strcmp( name, StrToMacroTypeTab[i].psz_name ) )
1725         {
1726             return StrToMacroTypeTab[i].i_type;
1727         }
1728     }
1729     return MVLC_UNKNOWN;
1730 }
1731
1732 static void MacroDo( httpd_file_sys_t *p_args,
1733                      macro_t *m,
1734                      char *p_request, int i_request,
1735                      char **pp_data,  int *pi_data,
1736                      char **pp_dst )
1737 {
1738     intf_thread_t  *p_intf = p_args->p_intf;
1739     intf_sys_t     *p_sys = p_args->p_intf->p_sys;
1740     char control[512];
1741
1742 #define ALLOC( l ) \
1743     {               \
1744         int __i__ = *pp_dst - *pp_data; \
1745         *pi_data += (l);                  \
1746         *pp_data = realloc( *pp_data, *pi_data );   \
1747         *pp_dst = (*pp_data) + __i__;   \
1748     }
1749 #define PRINT( str ) \
1750     ALLOC( strlen( str ) + 1 ); \
1751     *pp_dst += sprintf( *pp_dst, str );
1752
1753 #define PRINTS( str, s ) \
1754     ALLOC( strlen( str ) + strlen( s ) + 1 ); \
1755     { \
1756         char * psz_cur = *pp_dst; \
1757         *pp_dst += sprintf( *pp_dst, str, s ); \
1758         while( psz_cur && *psz_cur ) \
1759         {  \
1760             /* Prevent script injection */ \
1761             if( *psz_cur == '<' ) *psz_cur = '*'; \
1762             if( *psz_cur == '>' ) *psz_cur = '*'; \
1763             psz_cur++ ; \
1764         } \
1765     }
1766
1767     switch( StrToMacroType( m->id ) )
1768     {
1769         case MVLC_CONTROL:
1770             if( i_request <= 0 )
1771             {
1772                 break;
1773             }
1774             uri_extract_value( p_request, "control", control, 512 );
1775             if( *m->param1 && !strstr( m->param1, control ) )
1776             {
1777                 msg_Warn( p_intf, "unauthorized control=%s", control );
1778                 break;
1779             }
1780             switch( StrToMacroType( control ) )
1781             {
1782                 case MVLC_PLAY:
1783                 {
1784                     int i_item;
1785                     char item[512];
1786
1787                     uri_extract_value( p_request, "item", item, 512 );
1788                     i_item = atoi( item );
1789                     playlist_Control( p_sys->p_playlist, PLAYLIST_ITEMPLAY,
1790                                       playlist_ItemGetById( p_sys->p_playlist,
1791                                       i_item ) );
1792                     msg_Dbg( p_intf, "requested playlist item: %i", i_item );
1793                     break;
1794                 }
1795                 case MVLC_STOP:
1796                     playlist_Control( p_sys->p_playlist, PLAYLIST_STOP );
1797                     msg_Dbg( p_intf, "requested playlist stop" );
1798                     break;
1799                 case MVLC_PAUSE:
1800                     playlist_Control( p_sys->p_playlist, PLAYLIST_PAUSE );
1801                     msg_Dbg( p_intf, "requested playlist pause" );
1802                     break;
1803                 case MVLC_NEXT:
1804                     playlist_Control( p_sys->p_playlist, PLAYLIST_SKIP, 1 );
1805                     msg_Dbg( p_intf, "requested playlist next" );
1806                     break;
1807                 case MVLC_PREVIOUS:
1808                     playlist_Control( p_sys->p_playlist, PLAYLIST_SKIP, -1 );
1809                     msg_Dbg( p_intf, "requested playlist previous" );
1810                     break;
1811                 case MVLC_FULLSCREEN:
1812                 {
1813                     if( p_sys->p_input )
1814                     {
1815                         vout_thread_t *p_vout;
1816                         p_vout = vlc_object_find( p_sys->p_input,
1817                                                   VLC_OBJECT_VOUT, FIND_CHILD );
1818
1819                         if( p_vout )
1820                         {
1821                             p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
1822                             vlc_object_release( p_vout );
1823                             msg_Dbg( p_intf, "requested fullscreen toggle" );
1824                         }
1825                     }
1826                 }
1827                 break;
1828                 case MVLC_SEEK:
1829                 {
1830                     vlc_value_t val;
1831                     char value[30];
1832                     char * p_value;
1833                     int i_stock = 0;
1834                     uint64_t i_length;
1835                     int i_value = 0;
1836                     int i_relative = 0;
1837 #define POSITION_ABSOLUTE 12
1838 #define POSITION_REL_FOR 13
1839 #define POSITION_REL_BACK 11
1840 #define VL_TIME_ABSOLUTE 0
1841 #define VL_TIME_REL_FOR 1
1842 #define VL_TIME_REL_BACK -1
1843                     if( p_sys->p_input )
1844                     {
1845                         uri_extract_value( p_request, "seek_value", value, 20 );
1846                         uri_decode_url_encoded( value );
1847                         p_value = value;
1848                         var_Get( p_sys->p_input, "length", &val);
1849                         i_length = val.i_time;
1850
1851                         while( p_value[0] != '\0' )
1852                         {
1853                             switch(p_value[0])
1854                             {
1855                                 case '+':
1856                                 {
1857                                     i_relative = VL_TIME_REL_FOR;
1858                                     p_value++;
1859                                     break;
1860                                 }
1861                                 case '-':
1862                                 {
1863                                     i_relative = VL_TIME_REL_BACK;
1864                                     p_value++;
1865                                     break;
1866                                 }
1867                                 case '0': case '1': case '2': case '3': case '4':
1868                                 case '5': case '6': case '7': case '8': case '9':
1869                                 {
1870                                     i_stock = strtol( p_value , &p_value , 10 );
1871                                     break;
1872                                 }
1873                                 case '%': /* for percentage ie position */
1874                                 {
1875                                     i_relative += POSITION_ABSOLUTE;
1876                                     i_value = i_stock;
1877                                     i_stock = 0;
1878                                     p_value[0] = '\0';
1879                                     break;
1880                                 }
1881                                 case ':':
1882                                 {
1883                                     i_value = 60 * (i_value + i_stock) ;
1884                                     i_stock = 0;
1885                                     p_value++;
1886                                     break;
1887                                 }
1888                                 case 'h': case 'H': /* hours */
1889                                 {
1890                                     i_value += 3600 * i_stock;
1891                                     i_stock = 0;
1892                                     /* other characters which are not numbers are not important */
1893                                     while( ((p_value[0] < '0') || (p_value[0] > '9')) && (p_value[0] != '\0') )
1894                                     {
1895                                         p_value++;
1896                                     }
1897                                     break;
1898                                 }
1899                                 case 'm': case 'M': case '\'': /* minutes */
1900                                 {
1901                                     i_value += 60 * i_stock;
1902                                     i_stock = 0;
1903                                     p_value++;
1904                                     while( ((p_value[0] < '0') || (p_value[0] > '9')) && (p_value[0] != '\0') )
1905                                     {
1906                                         p_value++;
1907                                     }
1908                                     break;
1909                                 }
1910                                 case 's': case 'S': case '"':  /* seconds */
1911                                 {
1912                                     i_value += i_stock;
1913                                     i_stock = 0;
1914                                     while( ((p_value[0] < '0') || (p_value[0] > '9')) && (p_value[0] != '\0') )
1915                                     {
1916                                         p_value++;
1917                                     }
1918                                     break;
1919                                 }
1920                                 default:
1921                                 {
1922                                     p_value++;
1923                                     break;
1924                                 }
1925                             }
1926                         }
1927
1928                         /* if there is no known symbol, I consider it as seconds. Otherwise, i_stock = 0 */
1929                         i_value += i_stock;
1930
1931                         switch(i_relative)
1932                         {
1933                             case VL_TIME_ABSOLUTE:
1934                             {
1935                                 if( (uint64_t)( i_value ) * 1000000 <= i_length )
1936                                     val.i_time = (uint64_t)( i_value ) * 1000000;
1937                                 else
1938                                     val.i_time = i_length;
1939
1940                                 var_Set( p_sys->p_input, "time", val );
1941                                 msg_Dbg( p_intf, "requested seek position: %dsec", i_value );
1942                                 break;
1943                             }
1944                             case VL_TIME_REL_FOR:
1945                             {
1946                                 var_Get( p_sys->p_input, "time", &val );
1947                                 if( (uint64_t)( i_value ) * 1000000 + val.i_time <= i_length )
1948                                 {
1949                                     val.i_time = ((uint64_t)( i_value ) * 1000000) + val.i_time;
1950                                 } else
1951                                 {
1952                                     val.i_time = i_length;
1953                                 }
1954                                 var_Set( p_sys->p_input, "time", val );
1955                                 msg_Dbg( p_intf, "requested seek position forward: %dsec", i_value );
1956                                 break;
1957                             }
1958                             case VL_TIME_REL_BACK:
1959                             {
1960                                 var_Get( p_sys->p_input, "time", &val );
1961                                 if( (int64_t)( i_value ) * 1000000 > val.i_time )
1962                                 {
1963                                     val.i_time = 0;
1964                                 } else
1965                                 {
1966                                     val.i_time = val.i_time - ((uint64_t)( i_value ) * 1000000);
1967                                 }
1968                                 var_Set( p_sys->p_input, "time", val );
1969                                 msg_Dbg( p_intf, "requested seek position backward: %dsec", i_value );
1970                                 break;
1971                             }
1972                             case POSITION_ABSOLUTE:
1973                             {
1974                                 val.f_float = __MIN( __MAX( ((float) i_value ) / 100.0 , 0.0 ) , 100.0 );
1975                                 var_Set( p_sys->p_input, "position", val );
1976                                 msg_Dbg( p_intf, "requested seek percent: %d", i_value );
1977                                 break;
1978                             }
1979                             case POSITION_REL_FOR:
1980                             {
1981                                 var_Get( p_sys->p_input, "position", &val );
1982                                 val.f_float += __MIN( __MAX( ((float) i_value ) / 100.0 , 0.0 ) , 100.0 );
1983                                 var_Set( p_sys->p_input, "position", val );
1984                                 msg_Dbg( p_intf, "requested seek percent forward: %d", i_value );
1985                                 break;
1986                             }
1987                             case POSITION_REL_BACK:
1988                             {
1989                                 var_Get( p_sys->p_input, "position", &val );
1990                                 val.f_float -= __MIN( __MAX( ((float) i_value ) / 100.0 , 0.0 ) , 100.0 );
1991                                 var_Set( p_sys->p_input, "position", val );
1992                                 msg_Dbg( p_intf, "requested seek percent backward: %d", i_value );
1993                                 break;
1994                             }
1995                             default:
1996                             {
1997                                 msg_Dbg( p_intf, "requested seek: what the f*** is going on here ?" );
1998                                 break;
1999                             }
2000                         }
2001                     }
2002 #undef POSITION_ABSOLUTE
2003 #undef POSITION_REL_FOR
2004 #undef POSITION_REL_BACK
2005 #undef VL_TIME_ABSOLUTE
2006 #undef VL_TIME_REL_FOR
2007 #undef VL_TIME_REL_BACK
2008                     break;
2009                 }
2010                 case MVLC_VOLUME:
2011                 {
2012                     char vol[8];
2013                     audio_volume_t i_volume;
2014                     int i_value;
2015
2016                     uri_extract_value( p_request, "value", vol, 8 );
2017                     aout_VolumeGet( p_intf, &i_volume );
2018                     uri_decode_url_encoded( vol );
2019
2020                     if( vol[0] == '+' )
2021                     {
2022                         i_value = atoi( vol + 1 );
2023                         if( (i_volume + i_value) > AOUT_VOLUME_MAX )
2024                         {
2025                             aout_VolumeSet( p_intf , AOUT_VOLUME_MAX );
2026                             msg_Dbg( p_intf, "requested volume set: max" );
2027                         }
2028                         else
2029                         {
2030                             aout_VolumeSet( p_intf , (i_volume + i_value) );
2031                             msg_Dbg( p_intf, "requested volume set: +%i", (i_volume + i_value) );
2032                         }
2033                     }
2034                     else if( vol[0] == '-' )
2035                     {
2036                         i_value = atoi( vol + 1 );
2037                         if( (i_volume - i_value) < AOUT_VOLUME_MIN )
2038                         {
2039                             aout_VolumeSet( p_intf , AOUT_VOLUME_MIN );
2040                             msg_Dbg( p_intf, "requested volume set: min" );
2041                         }
2042                         else
2043                         {
2044                             aout_VolumeSet( p_intf , (i_volume - i_value) );
2045                             msg_Dbg( p_intf, "requested volume set: -%i", (i_volume - i_value) );
2046                         }
2047                     }
2048                     else if( strstr(vol, "%") != NULL )
2049                     {
2050                         i_value = atoi( vol );
2051                         if( (i_value <= 400) && (i_value>=0) ){
2052                             aout_VolumeSet( p_intf, (i_value * (AOUT_VOLUME_MAX - AOUT_VOLUME_MIN))/400+AOUT_VOLUME_MIN);
2053                             msg_Dbg( p_intf, "requested volume set: %i%%", atoi( vol ));
2054                         }
2055                     }
2056                     else
2057                     {
2058                         i_value = atoi( vol );
2059                         if( ( i_value <= AOUT_VOLUME_MAX ) && ( i_value >= AOUT_VOLUME_MIN ) )
2060                         {
2061                             aout_VolumeSet( p_intf , atoi( vol ) );
2062                             msg_Dbg( p_intf, "requested volume set: %i", atoi( vol ) );
2063                         }
2064                     }
2065                     break;
2066                 }
2067
2068                 /* playlist management */
2069                 case MVLC_ADD:
2070                 {
2071                     char mrl[1024], psz_name[1024];
2072                     playlist_item_t *p_item;
2073
2074                     uri_extract_value( p_request, "mrl", mrl, 1024 );
2075                     uri_decode_url_encoded( mrl );
2076                     uri_extract_value( p_request, "name", psz_name, 1024 );
2077                     uri_decode_url_encoded( psz_name );
2078                     if( !*psz_name )
2079                     {
2080                         memcpy( psz_name, mrl, 1024 );
2081                     }
2082                     p_item = parse_MRL( p_intf, mrl, psz_name );
2083
2084                     if( !p_item || !p_item->input.psz_uri ||
2085                         !*p_item->input.psz_uri )
2086                     {
2087                         msg_Dbg( p_intf, "invalid requested mrl: %s", mrl );
2088                     }
2089                     else
2090                     {
2091                         playlist_AddItem( p_sys->p_playlist, p_item,
2092                                           PLAYLIST_APPEND, PLAYLIST_END );
2093                         msg_Dbg( p_intf, "requested mrl add: %s", mrl );
2094                     }
2095
2096                     break;
2097                 }
2098                 case MVLC_DEL:
2099                 {
2100                     int i_item, *p_items = NULL, i_nb_items = 0;
2101                     char item[512], *p_parser = p_request;
2102
2103                     /* Get the list of items to delete */
2104                     while( (p_parser =
2105                             uri_extract_value( p_parser, "item", item, 512 )) )
2106                     {
2107                         if( !*item ) continue;
2108
2109                         i_item = atoi( item );
2110                         p_items = realloc( p_items, (i_nb_items + 1) *
2111                                            sizeof(int) );
2112                         p_items[i_nb_items] = i_item;
2113                         i_nb_items++;
2114                     }
2115
2116                     if( i_nb_items )
2117                     {
2118                         int i;
2119                         for( i = 0; i < i_nb_items; i++ )
2120                         {
2121                             playlist_LockDelete( p_sys->p_playlist, p_items[i] );
2122                             msg_Dbg( p_intf, "requested playlist delete: %d",
2123                                      p_items[i] );
2124                             p_items[i] = -1;
2125                         }
2126                     }
2127
2128                     if( p_items ) free( p_items );
2129                     break;
2130                 }
2131                 case MVLC_KEEP:
2132                 {
2133                     int i_item, *p_items = NULL, i_nb_items = 0;
2134                     char item[512], *p_parser = p_request;
2135                     int i,j;
2136
2137                     /* Get the list of items to keep */
2138                     while( (p_parser =
2139                             uri_extract_value( p_parser, "item", item, 512 )) )
2140                     {
2141                         if( !*item ) continue;
2142
2143                         i_item = atoi( item );
2144                         p_items = realloc( p_items, (i_nb_items + 1) *
2145                                            sizeof(int) );
2146                         p_items[i_nb_items] = i_item;
2147                         i_nb_items++;
2148                     }
2149
2150                     for( i = p_sys->p_playlist->i_size - 1 ; i >= 0; i-- )
2151                     {
2152                         /* Check if the item is in the keep list */
2153                         for( j = 0 ; j < i_nb_items ; j++ )
2154                         {
2155                             if( p_items[j] ==
2156                                 p_sys->p_playlist->pp_items[i]->input.i_id ) break;
2157                         }
2158                         if( j == i_nb_items )
2159                         {
2160                             playlist_LockDelete( p_sys->p_playlist, p_sys->p_playlist->pp_items[i]->input.i_id );
2161                             msg_Dbg( p_intf, "requested playlist delete: %d",
2162                                      i );
2163                         }
2164                     }
2165
2166                     if( p_items ) free( p_items );
2167                     break;
2168                 }
2169                 case MVLC_EMPTY:
2170                 {
2171                     playlist_LockClear( p_sys->p_playlist );
2172                     msg_Dbg( p_intf, "requested playlist empty" );
2173                     break;
2174                 }
2175                 case MVLC_SORT:
2176                 {
2177                     char type[12];
2178                     char order[2];
2179                     char item[512];
2180                     int i_order;
2181                     int i_item;
2182
2183                     uri_extract_value( p_request, "type", type, 12 );
2184                     uri_extract_value( p_request, "order", order, 2 );
2185                     uri_extract_value( p_request, "item", item, 512 );
2186                     i_item = atoi( item );
2187
2188                     if( order[0] == '0' ) i_order = ORDER_NORMAL;
2189                     else i_order = ORDER_REVERSE;
2190
2191                     if( !strcmp( type , "title" ) )
2192                     {
2193                         playlist_RecursiveNodeSort( p_sys->p_playlist, /*playlist_ItemGetById( p_sys->p_playlist, i_item ),*/
2194                                                     p_sys->p_playlist->pp_views[0]->p_root,
2195                                                     SORT_TITLE_NODES_FIRST,
2196                                                     ( i_order == 0 ) ? ORDER_NORMAL : ORDER_REVERSE );
2197                         msg_Dbg( p_intf, "requested playlist sort by title (%d)" , i_order );
2198                     }
2199                     else if( !strcmp( type , "author" ) )
2200                     {
2201                         playlist_RecursiveNodeSort( p_sys->p_playlist, /*playlist_ItemGetById( p_sys->p_playlist, i_item ),*/
2202                                                     p_sys->p_playlist->pp_views[0]->p_root,
2203                                                     SORT_AUTHOR,
2204                                                     ( i_order == 0 ) ? ORDER_NORMAL : ORDER_REVERSE );
2205                         msg_Dbg( p_intf, "requested playlist sort by author (%d)" , i_order );
2206                     }
2207                     else if( !strcmp( type , "shuffle" ) )
2208                     {
2209                         playlist_RecursiveNodeSort( p_sys->p_playlist, /*playlist_ItemGetById( p_sys->p_playlist, i_item ),*/
2210                                                     p_sys->p_playlist->pp_views[0]->p_root,
2211                                                     SORT_RANDOM,
2212                                                     ( i_order == 0 ) ? ORDER_NORMAL : ORDER_REVERSE );
2213                         msg_Dbg( p_intf, "requested playlist shuffle");
2214                     }
2215
2216                     break;
2217                 }
2218                 case MVLC_MOVE:
2219                 {
2220                     char psz_pos[6];
2221                     char psz_newpos[6];
2222                     int i_pos;
2223                     int i_newpos;
2224                     uri_extract_value( p_request, "psz_pos", psz_pos, 6 );
2225                     uri_extract_value( p_request, "psz_newpos", psz_newpos, 6 );
2226                     i_pos = atoi( psz_pos );
2227                     i_newpos = atoi( psz_newpos );
2228                     if ( i_pos < i_newpos )
2229                     {
2230                         playlist_Move( p_sys->p_playlist, i_pos, i_newpos + 1 );
2231                     }
2232                     else
2233                     {
2234                         playlist_Move( p_sys->p_playlist, i_pos, i_newpos );
2235                     }
2236                     msg_Dbg( p_intf, "requested move playlist item %d to %d", i_pos, i_newpos);
2237                     break;
2238                 }
2239
2240                 /* admin function */
2241                 case MVLC_CLOSE:
2242                 {
2243                     char id[512];
2244                     uri_extract_value( p_request, "id", id, 512 );
2245                     msg_Dbg( p_intf, "requested close id=%s", id );
2246 #if 0
2247                     if( p_sys->p_httpd->pf_control( p_sys->p_httpd, HTTPD_SET_CLOSE, id, NULL ) )
2248                     {
2249                         msg_Warn( p_intf, "close failed for id=%s", id );
2250                     }
2251 #endif
2252                     break;
2253                 }
2254                 case MVLC_SHUTDOWN:
2255                 {
2256                     msg_Dbg( p_intf, "requested shutdown" );
2257                     p_intf->p_vlc->b_die = VLC_TRUE;
2258                     break;
2259                 }
2260                 /* vlm */
2261                 case MVLC_VLM_NEW:
2262                 case MVLC_VLM_SETUP:
2263                 {
2264                     static const char *vlm_properties[11] =
2265                     {
2266                         /* no args */
2267                         "enabled", "disabled", "loop", "unloop",
2268                         /* args required */
2269                         "input", "output", "option", "date", "period", "repeat", "append",
2270                     };
2271                     vlm_message_t *vlm_answer;
2272                     char name[512];
2273                     char *psz = malloc( strlen( p_request ) + 1000 );
2274                     char *p = psz;
2275                     char *vlm_error;
2276                     int i;
2277
2278                     if( p_intf->p_sys->p_vlm == NULL )
2279                         p_intf->p_sys->p_vlm = vlm_New( p_intf );
2280
2281                     if( p_intf->p_sys->p_vlm == NULL ) break;
2282
2283                     uri_extract_value( p_request, "name", name, 512 );
2284                     if( StrToMacroType( control ) == MVLC_VLM_NEW )
2285                     {
2286                         char type[20];
2287                         uri_extract_value( p_request, "type", type, 20 );
2288                         p += sprintf( psz, "new %s %s", name, type );
2289                     }
2290                     else
2291                     {
2292                         p += sprintf( psz, "setup %s", name );
2293                     }
2294                     /* Parse the request */
2295                     for( i = 0; i < 11; i++ )
2296                     {
2297                         char val[512];
2298                         uri_extract_value( p_request, vlm_properties[i], val, 512 );
2299                         uri_decode_url_encoded( val );
2300                         if( strlen( val ) > 0 && i >= 4 )
2301                         {
2302                             p += sprintf( p, " %s %s", vlm_properties[i], val );
2303                         }
2304                         else if( uri_test_param( p_request, vlm_properties[i] ) && i < 4 )
2305                         {
2306                             p += sprintf( p, " %s", vlm_properties[i] );
2307                         }
2308                     }
2309                     vlm_ExecuteCommand( p_intf->p_sys->p_vlm, psz, &vlm_answer );
2310                     if( vlm_answer->psz_value == NULL ) /* there is no error */
2311                     {
2312                         vlm_error = strdup( "" );
2313                     }
2314                     else
2315                     {
2316                         vlm_error = malloc( strlen(vlm_answer->psz_name) +
2317                                             strlen(vlm_answer->psz_value) +
2318                                             strlen( " : ") + 1 );
2319                         sprintf( vlm_error , "%s : %s" , vlm_answer->psz_name,
2320                                                          vlm_answer->psz_value );
2321                     }
2322
2323                     mvar_AppendNewVar( p_args->vars, "vlm_error", vlm_error );
2324
2325                     vlm_MessageDelete( vlm_answer );
2326                     free( vlm_error );
2327                     free( psz );
2328                     break;
2329                 }
2330
2331                 case MVLC_VLM_DEL:
2332                 {
2333                     vlm_message_t *vlm_answer;
2334                     char name[512];
2335                     char psz[512+10];
2336                     if( p_intf->p_sys->p_vlm == NULL )
2337                         p_intf->p_sys->p_vlm = vlm_New( p_intf );
2338
2339                     if( p_intf->p_sys->p_vlm == NULL ) break;
2340
2341                     uri_extract_value( p_request, "name", name, 512 );
2342                     sprintf( psz, "del %s", name );
2343
2344                     vlm_ExecuteCommand( p_intf->p_sys->p_vlm, psz, &vlm_answer );
2345                     /* FIXME do a vlm_answer -> var stack conversion */
2346                     vlm_MessageDelete( vlm_answer );
2347                     break;
2348                 }
2349
2350                 case MVLC_VLM_PLAY:
2351                 case MVLC_VLM_PAUSE:
2352                 case MVLC_VLM_STOP:
2353                 case MVLC_VLM_SEEK:
2354                 {
2355                     vlm_message_t *vlm_answer;
2356                     char name[512];
2357                     char psz[512+10];
2358                     if( p_intf->p_sys->p_vlm == NULL )
2359                         p_intf->p_sys->p_vlm = vlm_New( p_intf );
2360
2361                     if( p_intf->p_sys->p_vlm == NULL ) break;
2362
2363                     uri_extract_value( p_request, "name", name, 512 );
2364                     if( StrToMacroType( control ) == MVLC_VLM_PLAY )
2365                         sprintf( psz, "control %s play", name );
2366                     else if( StrToMacroType( control ) == MVLC_VLM_PAUSE )
2367                         sprintf( psz, "control %s pause", name );
2368                     else if( StrToMacroType( control ) == MVLC_VLM_STOP )
2369                         sprintf( psz, "control %s stop", name );
2370                     else if( StrToMacroType( control ) == MVLC_VLM_SEEK )
2371                     {
2372                         char percent[20];
2373                         uri_extract_value( p_request, "percent", percent, 512 );
2374                         sprintf( psz, "control %s seek %s", name, percent );
2375                     }
2376
2377                     vlm_ExecuteCommand( p_intf->p_sys->p_vlm, psz, &vlm_answer );
2378                     /* FIXME do a vlm_answer -> var stack conversion */
2379                     vlm_MessageDelete( vlm_answer );
2380                     break;
2381                 }
2382                 case MVLC_VLM_LOAD:
2383                 case MVLC_VLM_SAVE:
2384                 {
2385                     vlm_message_t *vlm_answer;
2386                     char file[512];
2387                     char psz[512];
2388
2389                     if( p_intf->p_sys->p_vlm == NULL )
2390                         p_intf->p_sys->p_vlm = vlm_New( p_intf );
2391
2392                     if( p_intf->p_sys->p_vlm == NULL ) break;
2393
2394                     uri_extract_value( p_request, "file", file, 512 );
2395                     uri_decode_url_encoded( file );
2396
2397                     if( StrToMacroType( control ) == MVLC_VLM_LOAD )
2398                         sprintf( psz, "load %s", file );
2399                     else
2400                         sprintf( psz, "save %s", file );
2401
2402                     vlm_ExecuteCommand( p_intf->p_sys->p_vlm, psz, &vlm_answer );
2403                     /* FIXME do a vlm_answer -> var stack conversion */
2404                     vlm_MessageDelete( vlm_answer );
2405                     break;
2406                 }
2407
2408                 default:
2409                     if( *control )
2410                     {
2411                         PRINTS( "<!-- control param(%s) unsupported -->", control );
2412                     }
2413                     break;
2414             }
2415             break;
2416
2417         case MVLC_SET:
2418         {
2419             char    value[512];
2420             int     i;
2421             float   f;
2422
2423             if( i_request <= 0 ||
2424                 *m->param1  == '\0' ||
2425                 strstr( p_request, m->param1 ) == NULL )
2426             {
2427                 break;
2428             }
2429             uri_extract_value( p_request, m->param1,  value, 512 );
2430             uri_decode_url_encoded( value );
2431
2432             switch( StrToMacroType( m->param2 ) )
2433             {
2434                 case MVLC_INT:
2435                     i = atoi( value );
2436                     config_PutInt( p_intf, m->param1, i );
2437                     break;
2438                 case MVLC_FLOAT:
2439                     f = atof( value );
2440                     config_PutFloat( p_intf, m->param1, f );
2441                     break;
2442                 case MVLC_STRING:
2443                     config_PutPsz( p_intf, m->param1, value );
2444                     break;
2445                 default:
2446                     PRINTS( "<!-- invalid type(%s) in set -->", m->param2 )
2447             }
2448             break;
2449         }
2450         case MVLC_GET:
2451         {
2452             char    value[512];
2453             int     i;
2454             float   f;
2455             char    *psz;
2456
2457             if( *m->param1  == '\0' )
2458             {
2459                 break;
2460             }
2461
2462             switch( StrToMacroType( m->param2 ) )
2463             {
2464                 case MVLC_INT:
2465                     i = config_GetInt( p_intf, m->param1 );
2466                     sprintf( value, "%d", i );
2467                     break;
2468                 case MVLC_FLOAT:
2469                     f = config_GetFloat( p_intf, m->param1 );
2470                     sprintf( value, "%f", f );
2471                     break;
2472                 case MVLC_STRING:
2473                     psz = config_GetPsz( p_intf, m->param1 );
2474                     if( psz != NULL )
2475                     {
2476                         strncpy( value, psz,sizeof( value ) );
2477                         free( psz );
2478                         value[sizeof( value ) - 1] = '\0';
2479                     }
2480                     else
2481                         *value = '\0';
2482                     msg_Dbg( p_intf, "%d: value = \"%s\"", __LINE__, value );
2483                     break;
2484                 default:
2485                     snprintf( value, sizeof( value ),
2486                               "invalid type(%s) in set", m->param2 );
2487                     value[sizeof( value ) - 1] = '\0';
2488                     break;
2489             }
2490             PRINTS( "%s", value );
2491             break;
2492         }
2493         case MVLC_VALUE:
2494         {
2495             char *s, *v;
2496
2497             if( m->param1 )
2498             {
2499                 EvaluateRPN( p_intf, p_args->vars, &p_args->stack, m->param1 );
2500                 s = SSPop( &p_args->stack );
2501                 v = mvar_GetValue( p_args->vars, s );
2502             }
2503             else
2504             {
2505                 v = s = SSPop( &p_args->stack );
2506             }
2507
2508             PRINTS( "%s", v );
2509             free( s );
2510             break;
2511         }
2512         case MVLC_RPN:
2513             EvaluateRPN( p_intf, p_args->vars, &p_args->stack, m->param1 );
2514             break;
2515
2516 /* Usefull for learning stack management */
2517         case MVLC_STACK:
2518         {
2519             int i;
2520             msg_Dbg( p_intf, "stack" );
2521             for (i=0;i<(&p_args->stack)->i_stack;i++)
2522                 msg_Dbg( p_intf, "%d -> %s", i, (&p_args->stack)->stack[i] );
2523             break;
2524         }
2525
2526         case MVLC_UNKNOWN:
2527         default:
2528             PRINTS( "<!-- invalid macro id=`%s' -->", m->id );
2529             msg_Dbg( p_intf, "invalid macro id=`%s'", m->id );
2530             break;
2531     }
2532 #undef PRINTS
2533 #undef PRINT
2534 #undef ALLOC
2535 }
2536
2537 static char *MacroSearch( char *src, char *end, int i_mvlc, vlc_bool_t b_after )
2538 {
2539     int     i_id;
2540     int     i_level = 0;
2541
2542     while( src < end )
2543     {
2544         if( src + 4 < end  && !strncmp( (char *)src, "<vlc", 4 ) )
2545         {
2546             int i_skip;
2547             macro_t m;
2548
2549             i_skip = MacroParse( &m, src );
2550
2551             i_id = StrToMacroType( m.id );
2552
2553             switch( i_id )
2554             {
2555                 case MVLC_IF:
2556                 case MVLC_FOREACH:
2557                     i_level++;
2558                     break;
2559                 case MVLC_END:
2560                     i_level--;
2561                     break;
2562                 default:
2563                     break;
2564             }
2565
2566             MacroClean( &m );
2567
2568             if( ( i_mvlc == MVLC_END && i_level == -1 ) ||
2569                 ( i_mvlc != MVLC_END && i_level == 0 && i_mvlc == i_id ) )
2570             {
2571                 return src + ( b_after ? i_skip : 0 );
2572             }
2573             else if( i_level < 0 )
2574             {
2575                 return NULL;
2576             }
2577
2578             src += i_skip;
2579         }
2580         else
2581         {
2582             src++;
2583         }
2584     }
2585
2586     return NULL;
2587 }
2588
2589 static void Execute( httpd_file_sys_t *p_args,
2590                      char *p_request, int i_request,
2591                      char **pp_data, int *pi_data,
2592                      char **pp_dst,
2593                      char *_src, char *_end )
2594 {
2595     intf_thread_t  *p_intf = p_args->p_intf;
2596
2597     char *src, *dup, *end;
2598     char *dst = *pp_dst;
2599
2600     src = dup = malloc( _end - _src + 1 );
2601     end = src +( _end - _src );
2602
2603     memcpy( src, _src, _end - _src );
2604     *end = '\0';
2605
2606     /* we parse searching <vlc */
2607     while( src < end )
2608     {
2609         char *p;
2610         int i_copy;
2611
2612         p = (char *)strstr( (char *)src, "<vlc" );
2613         if( p < end && p == src )
2614         {
2615             macro_t m;
2616
2617             src += MacroParse( &m, src );
2618
2619             //msg_Dbg( p_intf, "macro_id=%s", m.id );
2620
2621             switch( StrToMacroType( m.id ) )
2622             {
2623                 case MVLC_IF:
2624                 {
2625                     vlc_bool_t i_test;
2626                     char    *endif;
2627
2628                     EvaluateRPN( p_intf, p_args->vars, &p_args->stack, m.param1 );
2629                     if( SSPopN( &p_args->stack, p_args->vars ) )
2630                     {
2631                         i_test = 1;
2632                     }
2633                     else
2634                     {
2635                         i_test = 0;
2636                     }
2637                     endif = MacroSearch( src, end, MVLC_END, VLC_TRUE );
2638
2639                     if( i_test == 0 )
2640                     {
2641                         char *start = MacroSearch( src, endif, MVLC_ELSE, VLC_TRUE );
2642
2643                         if( start )
2644                         {
2645                             char *stop  = MacroSearch( start, endif, MVLC_END, VLC_FALSE );
2646                             if( stop )
2647                             {
2648                                 Execute( p_args, p_request, i_request,
2649                                          pp_data, pi_data, &dst, start, stop );
2650                             }
2651                         }
2652                     }
2653                     else if( i_test == 1 )
2654                     {
2655                         char *stop;
2656                         if( ( stop = MacroSearch( src, endif, MVLC_ELSE, VLC_FALSE ) ) == NULL )
2657                         {
2658                             stop = MacroSearch( src, endif, MVLC_END, VLC_FALSE );
2659                         }
2660                         if( stop )
2661                         {
2662                             Execute( p_args, p_request, i_request,
2663                                      pp_data, pi_data, &dst, src, stop );
2664                         }
2665                     }
2666
2667                     src = endif;
2668                     break;
2669                 }
2670                 case MVLC_FOREACH:
2671                 {
2672                     char *endfor = MacroSearch( src, end, MVLC_END, VLC_TRUE );
2673                     char *start = src;
2674                     char *stop = MacroSearch( src, end, MVLC_END, VLC_FALSE );
2675
2676                     if( stop )
2677                     {
2678                         mvar_t *index;
2679                         int    i_idx;
2680                         mvar_t *v;
2681                         if( !strcmp( m.param2, "integer" ) )
2682                         {
2683                             char *arg = SSPop( &p_args->stack );
2684                             index = mvar_IntegerSetNew( m.param1, arg );
2685                             free( arg );
2686                         }
2687                         else if( !strcmp( m.param2, "directory" ) )
2688                         {
2689                             char *arg = SSPop( &p_args->stack );
2690                             index = mvar_FileSetNew( p_intf, m.param1, arg );
2691                             free( arg );
2692                         }
2693                         else if( !strcmp( m.param2, "playlist" ) )
2694                         {
2695                             index = mvar_PlaylistSetNew( p_intf, m.param1,
2696                                                     p_intf->p_sys->p_playlist );
2697                         }
2698                         else if( !strcmp( m.param2, "information" ) )
2699                         {
2700                             index = mvar_InfoSetNew( p_intf, m.param1,
2701                                                      p_intf->p_sys->p_input );
2702                         }
2703                         else if( !strcmp( m.param2, "vlm" ) )
2704                         {
2705                             if( p_intf->p_sys->p_vlm == NULL )
2706                                 p_intf->p_sys->p_vlm = vlm_New( p_intf );
2707                             index = mvar_VlmSetNew( m.param1, p_intf->p_sys->p_vlm );
2708                         }
2709 #if 0
2710                         else if( !strcmp( m.param2, "hosts" ) )
2711                         {
2712                             index = mvar_HttpdInfoSetNew( m.param1, p_intf->p_sys->p_httpd, HTTPD_GET_HOSTS );
2713                         }
2714                         else if( !strcmp( m.param2, "urls" ) )
2715                         {
2716                             index = mvar_HttpdInfoSetNew( m.param1, p_intf->p_sys->p_httpd, HTTPD_GET_URLS );
2717                         }
2718                         else if( !strcmp( m.param2, "connections" ) )
2719                         {
2720                             index = mvar_HttpdInfoSetNew(m.param1, p_intf->p_sys->p_httpd, HTTPD_GET_CONNECTIONS);
2721                         }
2722 #endif
2723                         else if( ( v = mvar_GetVar( p_args->vars, m.param2 ) ) )
2724                         {
2725                             index = mvar_Duplicate( v );
2726                         }
2727                         else
2728                         {
2729                             msg_Dbg( p_intf, "invalid index constructor (%s)", m.param2 );
2730                             src = endfor;
2731                             break;
2732                         }
2733
2734                         for( i_idx = 0; i_idx < index->i_field; i_idx++ )
2735                         {
2736                             mvar_t *f = mvar_Duplicate( index->field[i_idx] );
2737
2738                             //msg_Dbg( p_intf, "foreach field[%d] name=%s value=%s", i_idx, f->name, f->value );
2739
2740                             free( f->name );
2741                             f->name = strdup( m.param1 );
2742
2743
2744                             mvar_PushVar( p_args->vars, f );
2745                             Execute( p_args, p_request, i_request,
2746                                      pp_data, pi_data, &dst, start, stop );
2747                             mvar_RemoveVar( p_args->vars, f );
2748
2749                             mvar_Delete( f );
2750                         }
2751                         mvar_Delete( index );
2752
2753                         src = endfor;
2754                     }
2755                     break;
2756                 }
2757                 default:
2758                     MacroDo( p_args, &m, p_request, i_request,
2759                              pp_data, pi_data, &dst );
2760                     break;
2761             }
2762
2763             MacroClean( &m );
2764             continue;
2765         }
2766
2767         i_copy =   ( (p == NULL || p > end ) ? end : p  ) - src;
2768         if( i_copy > 0 )
2769         {
2770             int i_index = dst - *pp_data;
2771
2772             *pi_data += i_copy;
2773             *pp_data = realloc( *pp_data, *pi_data );
2774             dst = (*pp_data) + i_index;
2775
2776             memcpy( dst, src, i_copy );
2777             dst += i_copy;
2778             src += i_copy;
2779         }
2780     }
2781
2782     *pp_dst = dst;
2783     free( dup );
2784 }
2785
2786 /****************************************************************************
2787  * HttpCallback:
2788  ****************************************************************************
2789  * a file with b_html is parsed and all "macro" replaced
2790  * <vlc id="macro name" [param1="" [param2=""]] />
2791  * valid id are
2792  *
2793  ****************************************************************************/
2794 static int  HttpCallback( httpd_file_sys_t *p_args,
2795                           httpd_file_t *p_file,
2796                           uint8_t *_p_request,
2797                           uint8_t **_pp_data, int *pi_data )
2798 {
2799     char *p_request = (char *)_p_request;
2800     char **pp_data = (char **)_pp_data;
2801     int i_request = p_request ? strlen( p_request ) : 0;
2802     char *p;
2803     FILE *f;
2804
2805     if( ( f = fopen( p_args->file, "r" ) ) == NULL )
2806     {
2807         p = *pp_data = malloc( 10240 );
2808         if( !p )
2809         {
2810                 return VLC_EGENERIC;
2811         }
2812         p += sprintf( p, "<html>\n" );
2813         p += sprintf( p, "<head>\n" );
2814         p += sprintf( p, "<title>Error loading %s</title>\n", p_args->file );
2815         p += sprintf( p, "</head>\n" );
2816         p += sprintf( p, "<body>\n" );
2817         p += sprintf( p, "<h1><center>Error loading %s for %s</center></h1>\n", p_args->file, p_args->name );
2818         p += sprintf( p, "<hr />\n" );
2819         p += sprintf( p, "<a href=\"http://www.videolan.org/\">VideoLAN</a>\n" );
2820         p += sprintf( p, "</body>\n" );
2821         p += sprintf( p, "</html>\n" );
2822
2823         *pi_data = strlen( *pp_data );
2824
2825         return VLC_SUCCESS;
2826     }
2827
2828     if( !p_args->b_html )
2829     {
2830         FileLoad( f, pp_data, pi_data );
2831     }
2832     else
2833     {
2834         int  i_buffer;
2835         char *p_buffer;
2836         char *dst;
2837         vlc_value_t val;
2838         char position[4]; /* percentage */
2839         char time[12]; /* in seconds */
2840         char length[12]; /* in seconds */
2841         audio_volume_t i_volume;
2842         char volume[5];
2843         char state[8];
2844  
2845 #define p_sys p_args->p_intf->p_sys
2846         if( p_sys->p_input )
2847         {
2848             var_Get( p_sys->p_input, "position", &val);
2849             sprintf( position, "%d" , (int)((val.f_float) * 100.0));
2850             var_Get( p_sys->p_input, "time", &val);
2851             sprintf( time, "%d" , (int)(val.i_time / 1000000) );
2852             var_Get( p_sys->p_input, "length", &val);
2853             sprintf( length, "%d" , (int)(val.i_time / 1000000) );
2854
2855             var_Get( p_sys->p_input, "state", &val );
2856             if( val.i_int == PLAYING_S )
2857             {
2858                 sprintf( state, "playing" );
2859             } else if( val.i_int == PAUSE_S )
2860             {
2861                 sprintf( state, "paused" );
2862             } else
2863             {
2864                 sprintf( state, "stop" );
2865             }
2866         } else
2867         {
2868             sprintf( position, "%d", 0 );
2869             sprintf( time, "%d", 0 );
2870             sprintf( length, "%d", 0 );
2871             sprintf( state, "stop" );
2872         }
2873 #undef p_sys
2874
2875         aout_VolumeGet( p_args->p_intf , &i_volume );
2876         sprintf( volume , "%d" , (int)i_volume );
2877
2878         p_args->vars = mvar_New( "variables", "" );
2879         mvar_AppendNewVar( p_args->vars, "url_param", i_request > 0 ? "1" : "0" );
2880         mvar_AppendNewVar( p_args->vars, "url_value", p_request );
2881         mvar_AppendNewVar( p_args->vars, "version",   VERSION_MESSAGE );
2882         mvar_AppendNewVar( p_args->vars, "copyright", COPYRIGHT_MESSAGE );
2883         mvar_AppendNewVar( p_args->vars, "stream_position", position );
2884         mvar_AppendNewVar( p_args->vars, "stream_time", time );
2885         mvar_AppendNewVar( p_args->vars, "stream_length", length );
2886         mvar_AppendNewVar( p_args->vars, "volume", volume );
2887         mvar_AppendNewVar( p_args->vars, "stream_state", state );
2888
2889         SSInit( &p_args->stack );
2890
2891         /* first we load in a temporary buffer */
2892         FileLoad( f, &p_buffer, &i_buffer );
2893
2894         /* allocate output */
2895         *pi_data = i_buffer + 1000;
2896         dst = *pp_data = malloc( *pi_data );
2897
2898         /* we parse executing all  <vlc /> macros */
2899         Execute( p_args, p_request, i_request, pp_data, pi_data, &dst,
2900                  &p_buffer[0], &p_buffer[i_buffer] );
2901
2902         *dst     = '\0';
2903         *pi_data = dst - *pp_data;
2904
2905         SSClean( &p_args->stack );
2906         mvar_Delete( p_args->vars );
2907         free( p_buffer );
2908     }
2909
2910     fclose( f );
2911
2912     return VLC_SUCCESS;
2913 }
2914
2915 /****************************************************************************
2916  * uri parser
2917  ****************************************************************************/
2918 static int uri_test_param( char *psz_uri, const char *psz_name )
2919 {
2920     char *p = psz_uri;
2921
2922     while( (p = strstr( p, psz_name )) )
2923     {
2924         /* Verify that we are dealing with a post/get argument */
2925         if( p == psz_uri || *(p - 1) == '&' || *(p - 1) == '\n' )
2926         {
2927             return VLC_TRUE;
2928         }
2929         p++;
2930     }
2931
2932     return VLC_FALSE;
2933 }
2934 static char *uri_extract_value( char *psz_uri, const char *psz_name,
2935                                 char *psz_value, int i_value_max )
2936 {
2937     char *p = psz_uri;
2938
2939     while( (p = strstr( p, psz_name )) )
2940     {
2941         /* Verify that we are dealing with a post/get argument */
2942         if( p == psz_uri || *(p - 1) == '&' || *(p - 1) == '\n' )
2943             break;
2944         p++;
2945     }
2946
2947     if( p )
2948     {
2949         int i_len;
2950
2951         p += strlen( psz_name );
2952         if( *p == '=' ) p++;
2953
2954         if( strchr( p, '&' ) )
2955         {
2956             i_len = strchr( p, '&' ) - p;
2957         }
2958         else
2959         {
2960             /* for POST method */
2961             if( strchr( p, '\n' ) )
2962             {
2963                 i_len = strchr( p, '\n' ) - p;
2964                 if( i_len && *(p+i_len-1) == '\r' ) i_len--;
2965             }
2966             else
2967             {
2968                 i_len = strlen( p );
2969             }
2970         }
2971         i_len = __MIN( i_value_max - 1, i_len );
2972         if( i_len > 0 )
2973         {
2974             strncpy( psz_value, p, i_len );
2975             psz_value[i_len] = '\0';
2976         }
2977         else
2978         {
2979             strncpy( psz_value, "", i_value_max );
2980         }
2981         p += i_len;
2982     }
2983     else
2984     {
2985         strncpy( psz_value, "", i_value_max );
2986     }
2987
2988     return p;
2989 }
2990
2991 static void uri_decode_url_encoded( char *psz )
2992 {
2993     char *dup = strdup( psz );
2994     char *p = dup;
2995
2996     while( *p )
2997     {
2998         if( *p == '%' )
2999         {
3000             char val[3];
3001             p++;
3002             if( !*p )
3003             {
3004                 break;
3005             }
3006
3007             val[0] = *p++;
3008             val[1] = *p++;
3009             val[2] = '\0';
3010
3011             *psz++ = strtol( val, NULL, 16 );
3012         }
3013         else if( *p == '+' )
3014         {
3015             *psz++ = ' ';
3016             p++;
3017         }
3018         else
3019         {
3020             *psz++ = *p++;
3021         }
3022     }
3023     *psz++ = '\0';
3024     free( dup );
3025 }
3026
3027 /****************************************************************************
3028  * Light RPN evaluator
3029  ****************************************************************************/
3030 static void SSInit( rpn_stack_t *st )
3031 {
3032     st->i_stack = 0;
3033 }
3034
3035 static void SSClean( rpn_stack_t *st )
3036 {
3037     while( st->i_stack > 0 )
3038     {
3039         free( st->stack[--st->i_stack] );
3040     }
3041 }
3042
3043 static void SSPush( rpn_stack_t *st, char *s )
3044 {
3045     if( st->i_stack < STACK_MAX )
3046     {
3047         st->stack[st->i_stack++] = strdup( s );
3048     }
3049 }
3050
3051 static char * SSPop( rpn_stack_t *st )
3052 {
3053     if( st->i_stack <= 0 )
3054     {
3055         return strdup( "" );
3056     }
3057     else
3058     {
3059         return st->stack[--st->i_stack];
3060     }
3061 }
3062
3063 static int SSPopN( rpn_stack_t *st, mvar_t  *vars )
3064 {
3065     char *name;
3066     char *value;
3067
3068     char *end;
3069     int  i;
3070
3071     name = SSPop( st );
3072     i = strtol( name, &end, 0 );
3073     if( end == name )
3074     {
3075         value = mvar_GetValue( vars, name );
3076         i = atoi( value );
3077     }
3078     free( name );
3079
3080     return( i );
3081 }
3082
3083 static void SSPushN( rpn_stack_t *st, int i )
3084 {
3085     char v[512];
3086
3087     sprintf( v, "%d", i );
3088     SSPush( st, v );
3089 }
3090
3091 static void  EvaluateRPN( intf_thread_t *p_intf, mvar_t  *vars,
3092                           rpn_stack_t *st, char *exp )
3093 {
3094     intf_sys_t    *p_sys = p_intf->p_sys;
3095
3096     for( ;; )
3097     {
3098         char s[100], *p;
3099
3100         /* skip space */
3101         while( *exp == ' ' )
3102         {
3103             exp++;
3104         }
3105
3106         if( *exp == '\'' )
3107         {
3108             /* extract string */
3109             p = &s[0];
3110             exp++;
3111             while( *exp && *exp != '\'' )
3112             {
3113                 /* strip a level of backslashes */
3114                 if( *exp == '\\' && exp[1] != '\0' )
3115                     exp++;
3116                 *p++ = *exp++;
3117             }
3118             *p = '\0';
3119             exp++;
3120             SSPush( st, s );
3121             continue;
3122         }
3123
3124         /* extract token */
3125         p = strchr( exp, ' ' );
3126         if( !p )
3127         {
3128             strcpy( s, exp );
3129
3130             exp += strlen( exp );
3131         }
3132         else
3133         {
3134             int i = p -exp;
3135             strncpy( s, exp, i );
3136             s[i] = '\0';
3137
3138             exp = p + 1;
3139         }
3140
3141         if( *s == '\0' )
3142         {
3143             break;
3144         }
3145
3146         /* 1. Integer function */
3147         if( !strcmp( s, "!" ) )
3148         {
3149             SSPushN( st, ~SSPopN( st, vars ) );
3150         }
3151         else if( !strcmp( s, "^" ) )
3152         {
3153             SSPushN( st, SSPopN( st, vars ) ^ SSPopN( st, vars ) );
3154         }
3155         else if( !strcmp( s, "&" ) )
3156         {
3157             SSPushN( st, SSPopN( st, vars ) & SSPopN( st, vars ) );
3158         }
3159         else if( !strcmp( s, "|" ) )
3160         {
3161             SSPushN( st, SSPopN( st, vars ) | SSPopN( st, vars ) );
3162         }
3163         else if( !strcmp( s, "+" ) )
3164         {
3165             SSPushN( st, SSPopN( st, vars ) + SSPopN( st, vars ) );
3166         }
3167         else if( !strcmp( s, "-" ) )
3168         {
3169             int j = SSPopN( st, vars );
3170             int i = SSPopN( st, vars );
3171             SSPushN( st, i - j );
3172         }
3173         else if( !strcmp( s, "*" ) )
3174         {
3175             SSPushN( st, SSPopN( st, vars ) * SSPopN( st, vars ) );
3176         }
3177         else if( !strcmp( s, "/" ) )
3178         {
3179             int i, j;
3180
3181             j = SSPopN( st, vars );
3182             i = SSPopN( st, vars );
3183
3184             SSPushN( st, j != 0 ? i / j : 0 );
3185         }
3186         else if( !strcmp( s, "%" ) )
3187         {
3188             int i, j;
3189
3190             j = SSPopN( st, vars );
3191             i = SSPopN( st, vars );
3192
3193             SSPushN( st, j != 0 ? i % j : 0 );
3194         }
3195         /* 2. integer tests */
3196         else if( !strcmp( s, "=" ) )
3197         {
3198             SSPushN( st, SSPopN( st, vars ) == SSPopN( st, vars ) ? -1 : 0 );
3199         }
3200         else if( !strcmp( s, "!=" ) )
3201         {
3202             SSPushN( st, SSPopN( st, vars ) != SSPopN( st, vars ) ? -1 : 0 );
3203         }
3204         else if( !strcmp( s, "<" ) )
3205         {
3206             int j = SSPopN( st, vars );
3207             int i = SSPopN( st, vars );
3208
3209             SSPushN( st, i < j ? -1 : 0 );
3210         }
3211         else if( !strcmp( s, ">" ) )
3212         {
3213             int j = SSPopN( st, vars );
3214             int i = SSPopN( st, vars );
3215
3216             SSPushN( st, i > j ? -1 : 0 );
3217         }
3218         else if( !strcmp( s, "<=" ) )
3219         {
3220             int j = SSPopN( st, vars );
3221             int i = SSPopN( st, vars );
3222
3223             SSPushN( st, i <= j ? -1 : 0 );
3224         }
3225         else if( !strcmp( s, ">=" ) )
3226         {
3227             int j = SSPopN( st, vars );
3228             int i = SSPopN( st, vars );
3229
3230             SSPushN( st, i >= j ? -1 : 0 );
3231         }
3232         /* 3. string functions */
3233         else if( !strcmp( s, "strcat" ) )
3234         {
3235             char *s2 = SSPop( st );
3236             char *s1 = SSPop( st );
3237             char *str = malloc( strlen( s1 ) + strlen( s2 ) + 1 );
3238
3239             strcpy( str, s1 );
3240             strcat( str, s2 );
3241
3242             SSPush( st, str );
3243             free( s1 );
3244             free( s2 );
3245             free( str );
3246         }
3247         else if( !strcmp( s, "strcmp" ) )
3248         {
3249             char *s2 = SSPop( st );
3250             char *s1 = SSPop( st );
3251
3252             SSPushN( st, strcmp( s1, s2 ) );
3253             free( s1 );
3254             free( s2 );
3255         }
3256         else if( !strcmp( s, "strncmp" ) )
3257         {
3258             int n = SSPopN( st, vars );
3259             char *s2 = SSPop( st );
3260             char *s1 = SSPop( st );
3261
3262             SSPushN( st, strncmp( s1, s2 , n ) );
3263             free( s1 );
3264             free( s2 );
3265         }
3266         else if( !strcmp( s, "strsub" ) )
3267         {
3268             int n = SSPopN( st, vars );
3269             int m = SSPopN( st, vars );
3270             int i_len;
3271             char *s = SSPop( st );
3272             char *str;
3273
3274             if( n >= m )
3275             {
3276                 i_len = n - m + 1;
3277             }
3278             else
3279             {
3280                 i_len = 0;
3281             }
3282
3283             str = malloc( i_len + 1 );
3284
3285             memcpy( str, s + m - 1, i_len );
3286             str[ i_len ] = '\0';
3287
3288             SSPush( st, str );
3289             free( s );
3290             free( str );
3291         }
3292        else if( !strcmp( s, "strlen" ) )
3293         {
3294             char *str = SSPop( st );
3295
3296             SSPushN( st, strlen( str ) );
3297             free( str );
3298         }
3299         /* 4. stack functions */
3300         else if( !strcmp( s, "dup" ) )
3301         {
3302             char *str = SSPop( st );
3303             SSPush( st, str );
3304             SSPush( st, str );
3305             free( str );
3306         }
3307         else if( !strcmp( s, "drop" ) )
3308         {
3309             char *str = SSPop( st );
3310             free( str );
3311         }
3312         else if( !strcmp( s, "swap" ) )
3313         {
3314             char *s1 = SSPop( st );
3315             char *s2 = SSPop( st );
3316
3317             SSPush( st, s1 );
3318             SSPush( st, s2 );
3319             free( s1 );
3320             free( s2 );
3321         }
3322         else if( !strcmp( s, "flush" ) )
3323         {
3324             SSClean( st );
3325             SSInit( st );
3326         }
3327         else if( !strcmp( s, "store" ) )
3328         {
3329             char *value = SSPop( st );
3330             char *name  = SSPop( st );
3331
3332             mvar_PushNewVar( vars, name, value );
3333             free( name );
3334             free( value );
3335         }
3336         else if( !strcmp( s, "value" ) )
3337         {
3338             char *name  = SSPop( st );
3339             char *value = mvar_GetValue( vars, name );
3340
3341             SSPush( st, value );
3342
3343             free( name );
3344         }
3345         else if( !strcmp( s, "url_extract" ) )
3346         {
3347             char *url = mvar_GetValue( vars, "url_value" );
3348             char *name = SSPop( st );
3349             char value[512];
3350             char *tmp;
3351
3352             uri_extract_value( url, name, value, 512 );
3353             uri_decode_url_encoded( value );
3354             tmp = FromUTF8( p_intf, value );
3355             SSPush( st, tmp );
3356             free( tmp );
3357         }
3358         else if( !strcmp( s, "url_encode" ) )
3359         {
3360             char *url = SSPop( st );
3361             char *value;
3362
3363             url = ToUTF8( p_intf, url );
3364             value = vlc_UrlEncode( url );
3365             free( url );
3366             SSPush( st, value );
3367             free( value );
3368         }
3369         /* 5. player control */
3370         else if( !strcmp( s, "vlc_play" ) )
3371         {
3372             int i_id = SSPopN( st, vars );
3373             int i_ret;
3374
3375             i_ret = playlist_Control( p_sys->p_playlist, PLAYLIST_ITEMPLAY,
3376                                       playlist_ItemGetById( p_sys->p_playlist,
3377                                       i_id ) );
3378             msg_Dbg( p_intf, "requested playlist item: %i", i_id );
3379             SSPushN( st, i_ret );
3380         }
3381         else if( !strcmp( s, "vlc_stop" ) )
3382         {
3383             playlist_Control( p_sys->p_playlist, PLAYLIST_STOP );
3384             msg_Dbg( p_intf, "requested playlist stop" );
3385         }
3386         else if( !strcmp( s, "vlc_pause" ) )
3387         {
3388             playlist_Control( p_sys->p_playlist, PLAYLIST_PAUSE );
3389             msg_Dbg( p_intf, "requested playlist pause" );
3390         }
3391         else if( !strcmp( s, "vlc_next" ) )
3392         {
3393             playlist_Control( p_sys->p_playlist, PLAYLIST_SKIP, 1 );
3394             msg_Dbg( p_intf, "requested playlist next" );
3395         }
3396         else if( !strcmp( s, "vlc_previous" ) )
3397         {
3398             playlist_Control( p_sys->p_playlist, PLAYLIST_SKIP, -1 );
3399             msg_Dbg( p_intf, "requested playlist previous" );
3400         }
3401         /* 6. playlist functions */
3402         else if( !strcmp( s, "playlist_add" ) )
3403         {
3404             char *psz_name = SSPop( st );
3405             char *mrl = SSPop( st );
3406             playlist_item_t *p_item;
3407             int i_id;
3408
3409             psz_name = ToUTF8( p_intf, psz_name );
3410             mrl = ToUTF8( p_intf, mrl );
3411
3412             if( !*psz_name )
3413             {
3414                 p_item = parse_MRL( p_intf, mrl, mrl );
3415             }
3416             else
3417             {
3418                 p_item = parse_MRL( p_intf, mrl, psz_name );
3419             }
3420
3421             if( p_item == NULL || p_item->input.psz_uri == NULL ||
3422                 !*p_item->input.psz_uri )
3423             {
3424                 i_id = VLC_EGENERIC;
3425                 msg_Dbg( p_intf, "invalid requested mrl: %s", mrl );
3426             }
3427             else
3428             {
3429                 i_id = playlist_AddItem( p_sys->p_playlist, p_item,
3430                                          PLAYLIST_APPEND, PLAYLIST_END );
3431                 msg_Dbg( p_intf, "requested mrl add: %s", mrl );
3432             }
3433             SSPushN( st, i_id );
3434
3435             free( mrl );
3436             free( psz_name );
3437         }
3438         else if( !strcmp( s, "playlist_empty" ) )
3439         {
3440             playlist_LockClear( p_sys->p_playlist );
3441             msg_Dbg( p_intf, "requested playlist empty" );
3442         }
3443         else
3444         {
3445             SSPush( st, s );
3446         }
3447     }
3448 }
3449
3450 /**********************************************************************
3451  * Find_end_MRL: Find the end of the sentence :
3452  * this function parses the string psz and find the end of the item
3453  * and/or option with detecting the " and ' problems.
3454  * returns NULL if an error is detected, otherwise, returns a pointer
3455  * of the end of the sentence (after the last character)
3456  **********************************************************************/
3457 static char *Find_end_MRL( char *psz )
3458 {
3459     char *s_sent = psz;
3460
3461     switch( *s_sent )
3462     {
3463         case '\"':
3464         {
3465             s_sent++;
3466
3467             while( ( *s_sent != '\"' ) && ( *s_sent != '\0' ) )
3468             {
3469                 if( *s_sent == '\'' )
3470                 {
3471                     s_sent = Find_end_MRL( s_sent );
3472
3473                     if( s_sent == NULL )
3474                     {
3475                         return NULL;
3476                     }
3477                 } else
3478                 {
3479                     s_sent++;
3480                 }
3481             }
3482
3483             if( *s_sent == '\"' )
3484             {
3485                 s_sent++;
3486                 return s_sent;
3487             } else  /* *s_sent == '\0' , which means the number of " is incorrect */
3488             {
3489                 return NULL;
3490             }
3491             break;
3492         }
3493         case '\'':
3494         {
3495             s_sent++;
3496
3497             while( ( *s_sent != '\'' ) && ( *s_sent != '\0' ) )
3498             {
3499                 if( *s_sent == '\"' )
3500                 {
3501                     s_sent = Find_end_MRL( s_sent );
3502
3503                     if( s_sent == NULL )
3504                     {
3505                         return NULL;
3506                     }
3507                 } else
3508                 {
3509                     s_sent++;
3510                 }
3511             }
3512
3513             if( *s_sent == '\'' )
3514             {
3515                 s_sent++;
3516                 return s_sent;
3517             } else  /* *s_sent == '\0' , which means the number of ' is incorrect */
3518             {
3519                 return NULL;
3520             }
3521             break;
3522         }
3523         default: /* now we can look for spaces */
3524         {
3525             while( ( *s_sent != ' ' ) && ( *s_sent != '\0' ) )
3526             {
3527                 if( ( *s_sent == '\'' ) || ( *s_sent == '\"' ) )
3528                 {
3529                     s_sent = Find_end_MRL( s_sent );
3530                 } else
3531                 {
3532                     s_sent++;
3533                 }
3534             }
3535             return s_sent;
3536         }
3537     }
3538 }
3539
3540 /**********************************************************************
3541  * parse_MRL: parse the MRL, find the mrl string and the options,
3542  * create an item with all information in it, and return the item.
3543  * return NULL if there is an error.
3544  **********************************************************************/
3545 static playlist_item_t *parse_MRL( intf_thread_t *p_intf, char *psz,
3546                                    char *psz_name )
3547 {
3548     char **ppsz_options = NULL;
3549     char *mrl;
3550     char *s_mrl = psz;
3551     int i_error = 0;
3552     char *s_temp;
3553     int i = 0;
3554     int i_options = 0;
3555     playlist_item_t * p_item = NULL;
3556
3557     /* In case there is spaces before the mrl */
3558     while( ( *s_mrl == ' ' ) && ( *s_mrl != '\0' ) )
3559     {
3560         s_mrl++;
3561     }
3562
3563     /* extract the mrl */
3564     s_temp = strstr( s_mrl , " :" );
3565     if( s_temp == NULL )
3566     {
3567         s_temp = s_mrl + strlen( s_mrl );
3568     } else
3569     {
3570         while( (*s_temp == ' ') && (s_temp != s_mrl ) )
3571         {
3572             s_temp--;
3573         }
3574         s_temp++;
3575     }
3576
3577     /* if the mrl is between " or ', we must remove them */
3578     if( (*s_mrl == '\'') || (*s_mrl == '\"') )
3579     {
3580         mrl = (char *)malloc( (s_temp - s_mrl - 1) * sizeof( char ) );
3581         strncpy( mrl , (s_mrl + 1) , s_temp - s_mrl - 2 );
3582         mrl[ s_temp - s_mrl - 2 ] = '\0';
3583     } else
3584     {
3585         mrl = (char *)malloc( (s_temp - s_mrl + 1) * sizeof( char ) );
3586         strncpy( mrl , s_mrl , s_temp - s_mrl );
3587         mrl[ s_temp - s_mrl ] = '\0';
3588     }
3589
3590     s_mrl = s_temp;
3591
3592     /* now we can take care of the options */
3593     while( (*s_mrl != '\0') && (i_error == 0) )
3594     {
3595         switch( *s_mrl )
3596         {
3597             case ' ':
3598             {
3599                 s_mrl++;
3600                 break;
3601             }
3602             case ':': /* an option */
3603             {
3604                 s_temp = Find_end_MRL( s_mrl );
3605
3606                 if( s_temp == NULL )
3607                 {
3608                     i_error = 1;
3609                 }
3610                 else
3611                 {
3612                     i_options++;
3613                     ppsz_options = realloc( ppsz_options , i_options *
3614                                             sizeof(char *) );
3615                     ppsz_options[ i_options - 1 ] =
3616                         malloc( (s_temp - s_mrl + 1) * sizeof(char) );
3617
3618                     strncpy( ppsz_options[ i_options - 1 ] , s_mrl ,
3619                              s_temp - s_mrl );
3620
3621                     /* don't forget to finish the string with a '\0' */
3622                     (ppsz_options[ i_options - 1 ])[ s_temp - s_mrl ] = '\0';
3623
3624                     s_mrl = s_temp;
3625                 }
3626                 break;
3627             }
3628             default:
3629             {
3630                 i_error = 1;
3631                 break;
3632             }
3633         }
3634     }
3635
3636     if( i_error != 0 )
3637     {
3638         free( mrl );
3639     }
3640     else
3641     {
3642         /* now create an item */
3643         p_item = playlist_ItemNew( p_intf, mrl, psz_name );
3644         for( i = 0 ; i< i_options ; i++ )
3645         {
3646             playlist_ItemAddOption( p_item, ppsz_options[i] );
3647         }
3648     }
3649
3650     for( i = 0; i < i_options; i++ ) free( ppsz_options[i] );
3651     if( i_options ) free( ppsz_options );
3652
3653     return p_item;
3654 }