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