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