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