]> git.sesse.net Git - vlc/blob - modules/control/http.c
* vlm.*: move vlm to the core (now, vlm_New create only one instance)
[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]->psz_name );
852
853         mvar_AppendNewVar( itm, "uri", p_pl->pp_items[i]->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
869     input_info_category_t * p_category;
870     input_info_t * p_info;
871
872     fprintf( stderr," mvar_InfoSetNew: name=`%s'\n", name );
873     if( p_input == NULL )
874     {
875         return s;
876     }
877
878     vlc_mutex_lock( &p_input->stream.stream_lock );
879     p_category = p_input->stream.p_info;
880     while ( p_category )
881     {
882         mvar_t *cat  = mvar_New( name, "set" );
883         mvar_t *iset = mvar_New( "info", "set" );
884
885         mvar_AppendNewVar( cat, "name", p_category->psz_name );
886         mvar_AppendVar( cat, iset );
887
888         p_info = p_category->p_info;
889         while ( p_info )
890         {
891             mvar_t *info = mvar_New( "info", "" );
892
893             msg_Dbg( p_input, "adding info name=%s value=%s", p_info->psz_name, p_info->psz_value );
894             mvar_AppendNewVar( info, "name",  p_info->psz_name );
895             mvar_AppendNewVar( info, "value", p_info->psz_value );
896             mvar_AppendVar( iset, info );
897             p_info = p_info->p_next;
898         }
899         mvar_AppendVar( s, cat );
900         p_category = p_category->p_next;
901     }
902     vlc_mutex_unlock( &p_input->stream.stream_lock );
903
904     return s;
905 }
906 #if 0
907 static mvar_t *mvar_HttpdInfoSetNew( char *name, httpd_t *p_httpd, int i_type )
908 {
909     mvar_t       *s = mvar_New( name, "set" );
910     httpd_info_t info;
911     int          i;
912
913     fprintf( stderr," mvar_HttpdInfoSetNew: name=`%s'\n", name );
914     if( !p_httpd->pf_control( p_httpd, i_type, &info, NULL ) )
915     {
916         for( i= 0; i < info.i_count; )
917         {
918             mvar_t *inf;
919
920             inf = mvar_New( name, "set" );
921             do
922             {
923                 /* fprintf( stderr," mvar_HttpdInfoSetNew: append name=`%s' value=`%s'\n",
924                             info.info[i].psz_name, info.info[i].psz_value ); */
925                 mvar_AppendNewVar( inf,
926                                    info.info[i].psz_name,
927                                    info.info[i].psz_value );
928                 i++;
929             } while( i < info.i_count && strcmp( info.info[i].psz_name, "id" ) );
930             mvar_AppendVar( s, inf );
931         }
932     }
933
934     /* free mem */
935     for( i = 0; i < info.i_count; i++ )
936     {
937         free( info.info[i].psz_name );
938         free( info.info[i].psz_value );
939     }
940     if( info.i_count > 0 )
941     {
942         free( info.info );
943     }
944
945     return s;
946 }
947 #endif
948
949 static mvar_t *mvar_FileSetNew( char *name, char *psz_dir )
950 {
951     mvar_t *s = mvar_New( name, "set" );
952     char           tmp[MAX_DIR_SIZE], *p, *src;
953 #ifdef HAVE_SYS_STAT_H
954     struct stat   stat_info;
955 #endif
956     DIR           *p_dir;
957     struct dirent *p_dir_content;
958     char          sep;
959
960     /* convert all / to native separator */
961 #if defined( WIN32 )
962     while( (p = strchr( psz_dir, '/' )) )
963     {
964         *p = '\\';
965     }
966     sep = '\\';
967 #else
968     sep = '/';
969 #endif
970
971     /* remove trailling separator */
972     while( strlen( psz_dir ) > 1 &&
973 #if defined( WIN32 )
974            !( strlen(psz_dir)==3 && psz_dir[1]==':' && psz_dir[2]==sep ) &&
975 #endif
976            psz_dir[strlen( psz_dir ) -1 ] == sep )
977     {
978         psz_dir[strlen( psz_dir ) -1 ]  ='\0';
979     }
980     /* remove double separator */
981     for( p = src = psz_dir; *src != '\0'; src++, p++ )
982     {
983         if( src[0] == sep && src[1] == sep )
984         {
985             src++;
986         }
987         *p = *src;
988     }
989     *p = '\0';
990
991     if( *psz_dir == '\0' )
992     {
993         return s;
994     }
995     /* first fix all .. dir */
996     p = src = psz_dir;
997     while( *src )
998     {
999         if( src[0] == '.' && src[1] == '.' )
1000         {
1001             src += 2;
1002             if( p <= &psz_dir[1] )
1003             {
1004                 continue;
1005             }
1006
1007             p -= 2;
1008
1009             while( p > &psz_dir[1] && *p != sep )
1010             {
1011                 p--;
1012             }
1013         }
1014         else if( *src == sep )
1015         {
1016             if( p > psz_dir && p[-1] == sep )
1017             {
1018                 src++;
1019             }
1020             else
1021             {
1022                 *p++ = *src++;
1023             }
1024         }
1025         else
1026         {
1027             do
1028             {
1029                 *p++ = *src++;
1030             } while( *src && *src != sep );
1031         }
1032     }
1033     *p = '\0';
1034
1035     fprintf( stderr," mvar_FileSetNew: name=`%s' dir=`%s'\n", name, psz_dir );
1036
1037 #ifdef HAVE_SYS_STAT_H
1038     if( stat( psz_dir, &stat_info ) == -1 || !S_ISDIR( stat_info.st_mode ) )
1039     {
1040         return s;
1041     }
1042 #endif
1043
1044     if( ( p_dir = opendir( psz_dir ) ) == NULL )
1045     {
1046         fprintf( stderr, "cannot open dir (%s)", psz_dir );
1047         return s;
1048     }
1049
1050     /* remove traling / or \ */
1051     for( p = &psz_dir[strlen( psz_dir) - 1]; p >= psz_dir && ( *p =='/' || *p =='\\' ); p-- )
1052     {
1053         *p = '\0';
1054     }
1055
1056     for( ;; )
1057     {
1058         mvar_t *f;
1059
1060         /* parse psz_src dir */
1061         if( ( p_dir_content = readdir( p_dir ) ) == NULL )
1062         {
1063             break;
1064         }
1065         if( !strcmp( p_dir_content->d_name, "." ) )
1066         {
1067             continue;
1068         }
1069
1070 #if defined( WIN32 )
1071         sprintf( tmp, "%s\\%s", psz_dir, p_dir_content->d_name );
1072 #else
1073         sprintf( tmp, "%s/%s", psz_dir, p_dir_content->d_name );
1074 #endif
1075
1076 #ifdef HAVE_SYS_STAT_H
1077         if( stat( tmp, &stat_info ) == -1 )
1078         {
1079             continue;
1080         }
1081 #endif
1082         f = mvar_New( name, "set" );
1083         mvar_AppendNewVar( f, "name", tmp );
1084 #ifdef HAVE_SYS_STAT_H
1085         if( S_ISDIR( stat_info.st_mode ) )
1086         {
1087             mvar_AppendNewVar( f, "type", "directory" );
1088         }
1089         else if( S_ISREG( stat_info.st_mode ) )
1090         {
1091             mvar_AppendNewVar( f, "type", "file" );
1092         }
1093         else
1094         {
1095             mvar_AppendNewVar( f, "type", "unknown" );
1096         }
1097
1098         sprintf( tmp, "%lld", stat_info.st_size );
1099         mvar_AppendNewVar( f, "size", tmp );
1100
1101         /* FIXME memory leak FIXME */
1102 #ifdef HAVE_CTIME_R
1103         ctime_r( &stat_info.st_mtime, tmp );
1104         mvar_AppendNewVar( f, "date", tmp );
1105 #else
1106         mvar_AppendNewVar( f, "date", ctime( &stat_info.st_mtime ) );
1107 #endif
1108
1109 #else
1110         mvar_AppendNewVar( f, "type", "unknown" );
1111         mvar_AppendNewVar( f, "size", "unknown" );
1112         mvar_AppendNewVar( f, "date", "unknown" );
1113 #endif
1114         mvar_AppendVar( s, f );
1115     }
1116
1117     return s;
1118 }
1119
1120 static mvar_t *mvar_VlmSetNew( char *name, vlm_t *vlm )
1121 {
1122     mvar_t        *s = mvar_New( name, "set" );
1123     vlm_message_t *msg;
1124     int    i;
1125
1126     fprintf( stderr," mvar_VlmSetNew: name=`%s'\n", name );
1127     if( vlm == NULL )
1128         return s;
1129     if( vlm_ExecuteCommand( vlm, "show", &msg ) )
1130     {
1131         return s;
1132     }
1133
1134     for( i = 0; i < msg->i_child; i++ )
1135     {
1136         /* Over media, schedule */
1137         vlm_message_t *ch = msg->child[i];
1138         int j;
1139
1140         for( j = 0; j < ch->i_child; j++ )
1141         {
1142             /* Over name */
1143             vlm_message_t *el = ch->child[j];
1144             vlm_message_t *inf, *desc;
1145             mvar_t        *set;
1146             char          psz[500];
1147             int k;
1148
1149             sprintf( psz, "show %s", el->psz_name );
1150             if( vlm_ExecuteCommand( vlm, psz, &inf ) )
1151                 continue;
1152             desc = inf->child[0];
1153
1154             /* Add a node with name and info */
1155             set = mvar_New( name, "set" );
1156             mvar_AppendNewVar( set, "name", el->psz_name );
1157
1158             for( k = 0; k < desc->i_child; k++ )
1159             {
1160                 mvar_AppendNewVar( set, desc->child[k]->psz_name, desc->child[k]->psz_value );
1161             }
1162             vlm_MessageDelete( inf );
1163
1164             mvar_AppendVar( s, set );
1165         }
1166     }
1167     vlm_MessageDelete( msg );
1168
1169     return s;
1170 }
1171
1172
1173 static void SSInit( rpn_stack_t * );
1174 static void SSClean( rpn_stack_t * );
1175 static void EvaluateRPN( mvar_t  *, rpn_stack_t *, char * );
1176
1177 static void SSPush  ( rpn_stack_t *, char * );
1178 static char *SSPop  ( rpn_stack_t * );
1179
1180 static void SSPushN ( rpn_stack_t *, int );
1181 static int  SSPopN  ( rpn_stack_t *, mvar_t  * );
1182
1183
1184 /****************************************************************************
1185  * Macro handling
1186  ****************************************************************************/
1187 typedef struct
1188 {
1189     char *id;
1190     char *param1;
1191     char *param2;
1192 } macro_t;
1193
1194 static int FileLoad( FILE *f, uint8_t **pp_data, int *pi_data )
1195 {
1196     int i_read;
1197
1198     /* just load the file */
1199     *pi_data = 0;
1200     *pp_data = malloc( 1025 );  /* +1 for \0 */
1201     while( ( i_read = fread( &(*pp_data)[*pi_data], 1, 1024, f ) ) == 1024 )
1202     {
1203         *pi_data += 1024;
1204         *pp_data = realloc( *pp_data, *pi_data  + 1025 );
1205     }
1206     if( i_read > 0 )
1207     {
1208         *pi_data += i_read;
1209     }
1210     (*pp_data)[*pi_data] = '\0';
1211
1212     return VLC_SUCCESS;
1213 }
1214
1215 static int MacroParse( macro_t *m, uint8_t *psz_src )
1216 {
1217     uint8_t *dup = strdup( psz_src );
1218     uint8_t *src = dup;
1219     uint8_t *p;
1220     int     i_skip;
1221
1222 #define EXTRACT( name, l ) \
1223         src += l;    \
1224         p = strchr( src, '"' );             \
1225         if( p )                             \
1226         {                                   \
1227             *p++ = '\0';                    \
1228         }                                   \
1229         m->name = strdup( src );            \
1230         if( !p )                            \
1231         {                                   \
1232             break;                          \
1233         }                                   \
1234         src = p;
1235
1236     /* init m */
1237     m->id = NULL;
1238     m->param1 = NULL;
1239     m->param2 = NULL;
1240
1241     /* parse */
1242     src += 4;
1243
1244     while( *src )
1245     {
1246         while( *src == ' ')
1247         {
1248             src++;
1249         }
1250         if( !strncmp( src, "id=\"", 4 ) )
1251         {
1252             EXTRACT( id, 4 );
1253         }
1254         else if( !strncmp( src, "param1=\"", 8 ) )
1255         {
1256             EXTRACT( param1, 8 );
1257         }
1258         else if( !strncmp( src, "param2=\"", 8 ) )
1259         {
1260             EXTRACT( param2, 8 );
1261         }
1262         else
1263         {
1264             break;
1265         }
1266     }
1267     if( strstr( src, "/>" ) )
1268     {
1269         src = strstr( src, "/>" ) + 2;
1270     }
1271     else
1272     {
1273         src += strlen( src );
1274     }
1275
1276     if( m->id == NULL )
1277     {
1278         m->id = strdup( "" );
1279     }
1280     if( m->param1 == NULL )
1281     {
1282         m->param1 = strdup( "" );
1283     }
1284     if( m->param2 == NULL )
1285     {
1286         m->param2 = strdup( "" );
1287     }
1288     i_skip = src - dup;
1289
1290     free( dup );
1291     return i_skip;
1292 #undef EXTRACT
1293 }
1294
1295 static void MacroClean( macro_t *m )
1296 {
1297     free( m->id );
1298     free( m->param1 );
1299     free( m->param2 );
1300 }
1301
1302 enum macroType
1303 {
1304     MVLC_UNKNOWN = 0,
1305     MVLC_CONTROL,
1306         MVLC_PLAY,
1307         MVLC_STOP,
1308         MVLC_PAUSE,
1309         MVLC_NEXT,
1310         MVLC_PREVIOUS,
1311         MVLC_ADD,
1312         MVLC_DEL,
1313         MVLC_EMPTY,
1314         MVLC_SEEK,
1315         MVLC_KEEP,
1316         MVLC_SORT,
1317         MVLC_MOVE,
1318         MVLC_VOLUME,
1319         MVLC_FULLSCREEN,
1320
1321         MVLC_CLOSE,
1322         MVLC_SHUTDOWN,
1323
1324         MVLC_VLM_NEW,
1325         MVLC_VLM_SETUP,
1326         MVLC_VLM_DEL,
1327         MVLC_VLM_PLAY,
1328         MVLC_VLM_PAUSE,
1329         MVLC_VLM_STOP,
1330         MVLC_VLM_SEEK,
1331
1332     MVLC_FOREACH,
1333     MVLC_IF,
1334     MVLC_RPN,
1335     MVLC_ELSE,
1336     MVLC_END,
1337     MVLC_GET,
1338     MVLC_SET,
1339         MVLC_INT,
1340         MVLC_FLOAT,
1341         MVLC_STRING,
1342
1343     MVLC_VALUE
1344 };
1345
1346 static struct
1347 {
1348     char *psz_name;
1349     int  i_type;
1350 }
1351 StrToMacroTypeTab [] =
1352 {
1353     { "control",    MVLC_CONTROL },
1354         /* player control */
1355         { "play",           MVLC_PLAY },
1356         { "stop",           MVLC_STOP },
1357         { "pause",          MVLC_PAUSE },
1358         { "next",           MVLC_NEXT },
1359         { "previous",       MVLC_PREVIOUS },
1360         { "seek",           MVLC_SEEK },
1361         { "keep",           MVLC_KEEP },
1362         { "fullscreen",     MVLC_FULLSCREEN },
1363         { "volume",         MVLC_VOLUME },
1364
1365         /* playlist management */
1366         { "add",            MVLC_ADD },
1367         { "delete",         MVLC_DEL },
1368         { "empty",          MVLC_EMPTY },
1369         { "sort",           MVLC_SORT },
1370         { "move",           MVLC_MOVE },
1371
1372         /* admin control */
1373         { "close",          MVLC_CLOSE },
1374         { "shutdown",       MVLC_SHUTDOWN },
1375
1376         /* vlm control */
1377         { "vlm_new",        MVLC_VLM_NEW },
1378         { "vlm_setup",      MVLC_VLM_SETUP },
1379         { "vlm_del",        MVLC_VLM_DEL },
1380         { "vlm_play",       MVLC_VLM_PLAY },
1381         { "vlm_pause",      MVLC_VLM_PAUSE },
1382         { "vlm_stop",       MVLC_VLM_STOP },
1383         { "vlm_seek",       MVLC_VLM_SEEK },
1384
1385     { "rpn",        MVLC_RPN },
1386
1387     { "foreach",    MVLC_FOREACH },
1388     { "value",      MVLC_VALUE },
1389
1390     { "if",         MVLC_IF },
1391     { "else",       MVLC_ELSE },
1392     { "end",        MVLC_END },
1393     { "get",         MVLC_GET },
1394     { "set",         MVLC_SET },
1395         { "int",            MVLC_INT },
1396         { "float",          MVLC_FLOAT },
1397         { "string",         MVLC_STRING },
1398
1399     /* end */
1400     { NULL,         MVLC_UNKNOWN }
1401 };
1402
1403 static int StrToMacroType( char *name )
1404 {
1405     int i;
1406
1407     if( !name || *name == '\0')
1408     {
1409         return MVLC_UNKNOWN;
1410     }
1411     for( i = 0; StrToMacroTypeTab[i].psz_name != NULL; i++ )
1412     {
1413         if( !strcmp( name, StrToMacroTypeTab[i].psz_name ) )
1414         {
1415             return StrToMacroTypeTab[i].i_type;
1416         }
1417     }
1418     return MVLC_UNKNOWN;
1419 }
1420
1421 static void MacroDo( httpd_file_sys_t *p_args,
1422                      macro_t *m,
1423                      uint8_t *p_request, int i_request,
1424                      uint8_t **pp_data,  int *pi_data,
1425                      uint8_t **pp_dst )
1426 {
1427     intf_thread_t  *p_intf = p_args->p_intf;
1428     intf_sys_t     *p_sys = p_args->p_intf->p_sys;
1429     char control[512];
1430
1431 #define ALLOC( l ) \
1432     {               \
1433         int __i__ = *pp_dst - *pp_data; \
1434         *pi_data += (l);                  \
1435         *pp_data = realloc( *pp_data, *pi_data );   \
1436         *pp_dst = (*pp_data) + __i__;   \
1437     }
1438 #define PRINT( str ) \
1439     ALLOC( strlen( str ) + 1 ); \
1440     *pp_dst += sprintf( *pp_dst, str );
1441
1442 #define PRINTS( str, s ) \
1443     ALLOC( strlen( str ) + strlen( s ) + 1 ); \
1444     { \
1445         char * psz_cur = *pp_dst; \
1446         *pp_dst += sprintf( *pp_dst, str, s ); \
1447         while( psz_cur && *psz_cur ) \
1448         {  \
1449             /* Prevent script injection */ \
1450             if( *psz_cur == '<' ) *psz_cur = '*'; \
1451             if( *psz_cur == '>' ) *psz_cur = '*'; \
1452             psz_cur++ ; \
1453         } \
1454     }
1455
1456     switch( StrToMacroType( m->id ) )
1457     {
1458         case MVLC_CONTROL:
1459             if( i_request <= 0 )
1460             {
1461                 break;
1462             }
1463             uri_extract_value( p_request, "control", control, 512 );
1464             if( *m->param1 && !strstr( m->param1, control ) )
1465             {
1466                 msg_Warn( p_intf, "unauthorized control=%s", control );
1467                 break;
1468             }
1469             switch( StrToMacroType( control ) )
1470             {
1471                 case MVLC_PLAY:
1472                 {
1473                     int i_item;
1474                     char item[512];
1475
1476                     uri_extract_value( p_request, "item", item, 512 );
1477                     i_item = atoi( item );
1478                     playlist_Command( p_sys->p_playlist, PLAYLIST_GOTO, i_item );
1479                     msg_Dbg( p_intf, "requested playlist item: %i", i_item );
1480                     break;
1481                 }
1482                 case MVLC_STOP:
1483                     playlist_Command( p_sys->p_playlist, PLAYLIST_STOP, 0 );
1484                     msg_Dbg( p_intf, "requested playlist stop" );
1485                     break;
1486                 case MVLC_PAUSE:
1487                     playlist_Command( p_sys->p_playlist, PLAYLIST_PAUSE, 0 );
1488                     msg_Dbg( p_intf, "requested playlist pause" );
1489                     break;
1490                 case MVLC_NEXT:
1491                     playlist_Command( p_sys->p_playlist, PLAYLIST_GOTO,
1492                                       p_sys->p_playlist->i_index + 1 );
1493                     msg_Dbg( p_intf, "requested playlist next" );
1494                     break;
1495                 case MVLC_PREVIOUS:
1496                     playlist_Command( p_sys->p_playlist, PLAYLIST_GOTO,
1497                                       p_sys->p_playlist->i_index - 1 );
1498                     msg_Dbg( p_intf, "requested playlist next" );
1499                     break;
1500                 case MVLC_FULLSCREEN:
1501                 {
1502                     if( p_sys->p_input )
1503                     {
1504                         vout_thread_t *p_vout;
1505                         p_vout = vlc_object_find( p_sys->p_input,
1506                                                   VLC_OBJECT_VOUT, FIND_CHILD );
1507
1508                         if( p_vout )
1509                         {
1510                             p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
1511                             vlc_object_release( p_vout );
1512                             msg_Dbg( p_intf, "requested fullscreen toggle" );
1513                         }
1514                     }
1515                 }
1516                 break;
1517                 case MVLC_SEEK:
1518                 {
1519                     vlc_value_t val;
1520                     char value[30];
1521                     char * p_value;
1522                     int i_stock = 0;
1523                     uint64_t i_length;
1524                     int i_value = 0;
1525                     int i_relative = 0;
1526 #define POSITION_ABSOLUTE 12
1527 #define POSITION_REL_FOR 13
1528 #define POSITION_REL_BACK 11
1529 #define TIME_ABSOLUTE 0
1530 #define TIME_REL_FOR 1
1531 #define TIME_REL_BACK -1
1532                     if( p_sys->p_input )
1533                     {
1534                         uri_extract_value( p_request, "seek_value", value, 20 );
1535                         uri_decode_url_encoded( value );
1536                         p_value = value;
1537                         var_Get( p_sys->p_input, "length", &val);
1538                         i_length = val.i_time;
1539
1540                         while( p_value[0] != '\0' )
1541                         {
1542                             switch(p_value[0])
1543                             {
1544                                 case '+':
1545                                 {
1546                                     i_relative = TIME_REL_FOR;
1547                                     p_value++;
1548                                     break;
1549                                 }
1550                                 case '-':
1551                                 {
1552                                     i_relative = TIME_REL_BACK;
1553                                     p_value++;
1554                                     break;
1555                                 }
1556                                 case '0': case '1': case '2': case '3': case '4':
1557                                 case '5': case '6': case '7': case '8': case '9':
1558                                 {
1559                                     i_stock = strtol( p_value , &p_value , 10 );
1560                                     break;
1561                                 }
1562                                 case '%': /* for percentage ie position */
1563                                 {
1564                                     i_relative += POSITION_ABSOLUTE;
1565                                     i_value = i_stock;
1566                                     i_stock = 0;
1567                                     p_value[0] = '\0';
1568                                     break;
1569                                 }
1570                                 case ':':
1571                                 {
1572                                     i_value = 60 * (i_value + i_stock) ;
1573                                     i_stock = 0;
1574                                     p_value++;
1575                                     break;
1576                                 }
1577                                 case 'h': case 'H': /* hours */
1578                                 {
1579                                     i_value += 3600 * i_stock;
1580                                     i_stock = 0;
1581                                     /* other characters which are not numbers are not important */
1582                                     while( ((p_value[0] < '0') || (p_value[0] > '9')) && (p_value[0] != '\0') )
1583                                     {
1584                                         p_value++;
1585                                     }
1586                                     break;
1587                                 }
1588                                 case 'm': case 'M': case '\'': /* minutes */
1589                                 {
1590                                     i_value += 60 * i_stock;
1591                                     i_stock = 0;
1592                                     p_value++;
1593                                     while( ((p_value[0] < '0') || (p_value[0] > '9')) && (p_value[0] != '\0') )
1594                                     {
1595                                         p_value++;
1596                                     }
1597                                     break;
1598                                 }
1599                                 case 's': case 'S': case '"':  /* seconds */
1600                                 {
1601                                     i_value += i_stock;
1602                                     i_stock = 0;
1603                                     while( ((p_value[0] < '0') || (p_value[0] > '9')) && (p_value[0] != '\0') )
1604                                     {
1605                                         p_value++;
1606                                     }
1607                                     break;
1608                                 }
1609                                 default:
1610                                 {
1611                                     p_value++;
1612                                     break;
1613                                 }
1614                             }
1615                         }
1616
1617                         /* if there is no known symbol, I consider it as seconds. Otherwise, i_stock = 0 */
1618                         i_value += i_stock;
1619
1620                         switch(i_relative)
1621                         {
1622                             case TIME_ABSOLUTE:
1623                             {
1624                                 if( (uint64_t)( i_value ) * 1000000 <= i_length )
1625                                     val.i_time = (uint64_t)( i_value ) * 1000000;
1626                                 else
1627                                     val.i_time = i_length;
1628
1629                                 var_Set( p_sys->p_input, "time", val );
1630                                 msg_Dbg( p_intf, "requested seek position: %dsec", i_value );
1631                                 break;
1632                             }
1633                             case TIME_REL_FOR:
1634                             {
1635                                 var_Get( p_sys->p_input, "time", &val );
1636                                 if( (uint64_t)( i_value ) * 1000000 + val.i_time <= i_length )
1637                                 {
1638                                     val.i_time = ((uint64_t)( i_value ) * 1000000) + val.i_time;
1639                                 } else
1640                                 {
1641                                     val.i_time = i_length;
1642                                 }
1643                                 var_Set( p_sys->p_input, "time", val );
1644                                 msg_Dbg( p_intf, "requested seek position forward: %dsec", i_value );
1645                                 break;
1646                             }
1647                             case TIME_REL_BACK:
1648                             {
1649                                 var_Get( p_sys->p_input, "time", &val );
1650                                 if( (int64_t)( i_value ) * 1000000 > val.i_time )
1651                                 {
1652                                     val.i_time = 0;
1653                                 } else
1654                                 {
1655                                     val.i_time = val.i_time - ((uint64_t)( i_value ) * 1000000);
1656                                 }
1657                                 var_Set( p_sys->p_input, "time", val );
1658                                 msg_Dbg( p_intf, "requested seek position backward: %dsec", i_value );
1659                                 break;
1660                             }
1661                             case POSITION_ABSOLUTE:
1662                             {
1663                                 val.f_float = __MIN( __MAX( ((float) i_value ) / 100.0 , 0.0 ) , 100.0 );
1664                                 var_Set( p_sys->p_input, "position", val );
1665                                 msg_Dbg( p_intf, "requested seek percent: %d", i_value );
1666                                 break;
1667                             }
1668                             case POSITION_REL_FOR:
1669                             {
1670                                 var_Get( p_sys->p_input, "position", &val );
1671                                 val.f_float += __MIN( __MAX( ((float) i_value ) / 100.0 , 0.0 ) , 100.0 );
1672                                 var_Set( p_sys->p_input, "position", val );
1673                                 msg_Dbg( p_intf, "requested seek percent forward: %d", i_value );
1674                                 break;
1675                             }
1676                             case POSITION_REL_BACK:
1677                             {
1678                                 var_Get( p_sys->p_input, "position", &val );
1679                                 val.f_float -= __MIN( __MAX( ((float) i_value ) / 100.0 , 0.0 ) , 100.0 );
1680                                 var_Set( p_sys->p_input, "position", val );
1681                                 msg_Dbg( p_intf, "requested seek percent backward: %d", i_value );
1682                                 break;
1683                             }
1684                             default:
1685                             {
1686                                 msg_Dbg( p_intf, "requested seek: what the f*** is going on here ?" );
1687                                 break;
1688                             }
1689                         }
1690                     }
1691 #undef POSITION_ABSOLUTE
1692 #undef POSITION_REL_FOR
1693 #undef POSITION_REL_BACK
1694 #undef TIME_ABSOLUTE
1695 #undef TIME_REL_FOR
1696 #undef TIME_REL_BACK
1697                     break;
1698                 }
1699                 case MVLC_VOLUME:
1700                 {
1701                     char vol[8];
1702                     audio_volume_t i_volume;
1703                     int i_value;
1704
1705                     uri_extract_value( p_request, "value", vol, 8 );
1706                     aout_VolumeGet( p_intf, &i_volume );
1707                     uri_decode_url_encoded( vol );
1708
1709                     if( vol[0] == '+' )
1710                     {
1711                         i_value = atoi( vol + 1 );
1712                         if( (i_volume + i_value) > AOUT_VOLUME_MAX )
1713                         {
1714                             aout_VolumeSet( p_intf , AOUT_VOLUME_MAX );
1715                             msg_Dbg( p_intf, "requested volume set: max" );
1716                         } else
1717                         {
1718                             aout_VolumeSet( p_intf , (i_volume + i_value) );
1719                             msg_Dbg( p_intf, "requested volume set: +%i", (i_volume + i_value) );
1720                         }
1721                     } else
1722                     if( vol[0] == '-' )
1723                     {
1724                         i_value = atoi( vol + 1 );
1725                         if( (i_volume - i_value) < AOUT_VOLUME_MIN )
1726                         {
1727                             aout_VolumeSet( p_intf , AOUT_VOLUME_MIN );
1728                             msg_Dbg( p_intf, "requested volume set: min" );
1729                         } else
1730                         {
1731                             aout_VolumeSet( p_intf , (i_volume - i_value) );
1732                             msg_Dbg( p_intf, "requested volume set: -%i", (i_volume - i_value) );
1733                         }
1734                     } else
1735                     {
1736                         i_value = atoi( vol );
1737                         if( ( i_value <= AOUT_VOLUME_MAX ) && ( i_value >= AOUT_VOLUME_MIN ) )
1738                         {
1739                             aout_VolumeSet( p_intf , atoi( vol ) );
1740                             msg_Dbg( p_intf, "requested volume set: %i", atoi( vol ) );
1741                         }
1742                     }
1743                     break;
1744                 }
1745
1746                 /* playlist management */
1747                 case MVLC_ADD:
1748                 {
1749                     char mrl[512];
1750                     playlist_item_t * p_item;
1751
1752                     uri_extract_value( p_request, "mrl", mrl, 512 );
1753                     uri_decode_url_encoded( mrl );
1754                     p_item = parse_MRL( p_intf, mrl );
1755
1756                     if( !p_item || !p_item->psz_uri || !*p_item->psz_uri )
1757                     {
1758                         msg_Dbg( p_intf, "invalid requested mrl: %s", mrl );
1759                     } else
1760                     {
1761                         playlist_AddItem( p_sys->p_playlist , p_item ,
1762                                           PLAYLIST_APPEND, PLAYLIST_END );
1763                         msg_Dbg( p_intf, "requested mrl add: %s", mrl );
1764                     }
1765
1766                     break;
1767                 }
1768                 case MVLC_DEL:
1769                 {
1770                     int i_item, *p_items = NULL, i_nb_items = 0;
1771                     char item[512], *p_parser = p_request;
1772
1773                     /* Get the list of items to delete */
1774                     while( (p_parser =
1775                             uri_extract_value( p_parser, "item", item, 512 )) )
1776                     {
1777                         if( !*item ) continue;
1778
1779                         i_item = atoi( item );
1780                         p_items = realloc( p_items, (i_nb_items + 1) *
1781                                            sizeof(int) );
1782                         p_items[i_nb_items] = i_item;
1783                         i_nb_items++;
1784                     }
1785
1786                     /* The items need to be deleted from in reversed order */
1787                     if( i_nb_items )
1788                     {
1789                         int i;
1790                         for( i = 0; i < i_nb_items; i++ )
1791                         {
1792                             int j, i_index = 0;
1793                             for( j = 0; j < i_nb_items; j++ )
1794                             {
1795                                 if( p_items[j] > p_items[i_index] )
1796                                     i_index = j;
1797                             }
1798
1799                             playlist_Delete( p_sys->p_playlist,
1800                                              p_items[i_index] );
1801                             msg_Dbg( p_intf, "requested playlist delete: %d",
1802                                      p_items[i_index] );
1803                             p_items[i_index] = -1;
1804                         }
1805                     }
1806
1807                     if( p_items ) free( p_items );
1808                     break;
1809                 }
1810                 case MVLC_KEEP:
1811                 {
1812                     int i_item, *p_items = NULL, i_nb_items = 0;
1813                     char item[512], *p_parser = p_request;
1814                     int i,j;
1815
1816                     /* Get the list of items to keep */
1817                     while( (p_parser =
1818                             uri_extract_value( p_parser, "item", item, 512 )) )
1819                     {
1820                         if( !*item ) continue;
1821
1822                         i_item = atoi( item );
1823                         p_items = realloc( p_items, (i_nb_items + 1) *
1824                                            sizeof(int) );
1825                         p_items[i_nb_items] = i_item;
1826                         i_nb_items++;
1827                     }
1828
1829                     /* The items need to be deleted from in reversed order */
1830                     for( i = p_sys->p_playlist->i_size - 1; i >= 0 ; i-- )
1831                     {
1832                         /* Check if the item is in the keep list */
1833                         for( j = 0 ; j < i_nb_items ; j++ )
1834                         {
1835                             if( p_items[j] == i ) break;
1836                         }
1837                         if( j == i_nb_items )
1838                         {
1839                             playlist_Delete( p_sys->p_playlist, i );
1840                             msg_Dbg( p_intf, "requested playlist delete: %d",
1841                                      i );
1842                         }
1843                     }
1844
1845                     if( p_items ) free( p_items );
1846                     break;
1847                 }
1848                 case MVLC_EMPTY:
1849                 {
1850                     while( p_sys->p_playlist->i_size > 0 )
1851                     {
1852                         playlist_Delete( p_sys->p_playlist, 0 );
1853                     }
1854                     msg_Dbg( p_intf, "requested playlist empty" );
1855                     break;
1856                 }
1857                 case MVLC_SORT:
1858                 {
1859                     char type[12];
1860                     char order[2];
1861                     int i_order;
1862
1863                     uri_extract_value( p_request, "type", type, 12 );
1864                     uri_extract_value( p_request, "order", order, 2 );
1865
1866                     if( order[0] == '0' ) i_order = ORDER_NORMAL;
1867                     else i_order = ORDER_REVERSE;
1868
1869                     if( !strcmp( type , "title" ) )
1870                     {
1871                         playlist_SortTitle( p_sys->p_playlist , i_order );
1872                         msg_Dbg( p_intf, "requested playlist sort by title (%d)" , i_order );
1873                     } else if( !strcmp( type , "group" ) )
1874                     {
1875                         playlist_SortGroup( p_sys->p_playlist , i_order );
1876                         msg_Dbg( p_intf, "requested playlist sort by group (%d)" , i_order );
1877                     } else if( !strcmp( type , "author" ) )
1878                     {
1879                         playlist_SortAuthor( p_sys->p_playlist , i_order );
1880                         msg_Dbg( p_intf, "requested playlist sort by author (%d)" , i_order );
1881                     } else if( !strcmp( type , "shuffle" ) )
1882                     {
1883                         playlist_Sort( p_sys->p_playlist , SORT_RANDOM, ORDER_NORMAL );
1884                         msg_Dbg( p_intf, "requested playlist shuffle");
1885                     } 
1886
1887                     break;
1888                 }
1889                 case MVLC_MOVE:
1890                 {
1891                     char psz_pos[6];
1892                     char psz_newpos[6];
1893                     int i_pos;
1894                     int i_newpos;
1895                     uri_extract_value( p_request, "psz_pos", psz_pos, 6 );
1896                     uri_extract_value( p_request, "psz_newpos", psz_newpos, 6 );
1897                     i_pos = atoi( psz_pos );
1898                     i_newpos = atoi( psz_newpos );
1899                     playlist_Move( p_sys->p_playlist, i_pos, i_newpos + 1 );
1900                     msg_Dbg( p_intf, "requested move playlist item %d to %d", i_pos, i_newpos);
1901                     break;
1902                 }
1903
1904                 /* admin function */
1905                 case MVLC_CLOSE:
1906                 {
1907                     char id[512];
1908                     uri_extract_value( p_request, "id", id, 512 );
1909                     msg_Dbg( p_intf, "requested close id=%s", id );
1910 #if 0
1911                     if( p_sys->p_httpd->pf_control( p_sys->p_httpd, HTTPD_SET_CLOSE, id, NULL ) )
1912                     {
1913                         msg_Warn( p_intf, "close failed for id=%s", id );
1914                     }
1915 #endif
1916                     break;
1917                 }
1918                 case MVLC_SHUTDOWN:
1919                 {
1920                     msg_Dbg( p_intf, "requested shutdown" );
1921                     p_intf->p_vlc->b_die = VLC_TRUE;
1922                     break;
1923                 }
1924                 /* vlm */
1925                 case MVLC_VLM_NEW:
1926                 case MVLC_VLM_SETUP:
1927                 {
1928                     static const char *vlm_properties[11] =
1929                     {
1930                         "input", "output", "option", "enabled", "disabled",
1931                         "loop", "unloop", "append", "date", "period", "repeat",
1932                     };
1933                     vlm_message_t *vlm_answer;
1934                     char name[512];
1935                     char *psz = malloc( strlen( p_request ) + 1000 );
1936                     char *p = psz;
1937                     int i;
1938
1939                     if( p_intf->p_sys->p_vlm == NULL )
1940                         p_intf->p_sys->p_vlm = vlm_New( p_intf );
1941
1942                     uri_extract_value( p_request, "name", name, 512 );
1943                     if( StrToMacroType( control ) == MVLC_VLM_NEW )
1944                     {
1945                         char type[20];
1946                         uri_extract_value( p_request, "type", type, 20 );
1947                         p += sprintf( psz, "new %s %s", name, type );
1948                     }
1949                     else
1950                     {
1951                         p += sprintf( psz, "setup %s", name );
1952                     }
1953                     /* Parse the request */
1954                     for( i = 0; i < 11; i++ )
1955                     {
1956                         char val[512];
1957                         uri_extract_value( p_request, vlm_properties[i], val, 512 );
1958                         if( strlen( val ) > 0 )
1959                         {
1960                             p += sprintf( p, " %s %s", vlm_properties[i], val );
1961                         }
1962                         else if( uri_test_param( p_request, vlm_properties[i] ) )
1963                         {
1964                             p += sprintf( p, " %s", vlm_properties[i] );
1965                         }
1966                     }
1967                     vlm_ExecuteCommand( p_intf->p_sys->p_vlm, psz, &vlm_answer );
1968                     /* FIXME do a vlm_answer -> var stack conversion */
1969                     vlm_MessageDelete( vlm_answer );
1970                     free( psz );
1971                     break;
1972                 }
1973
1974                 case MVLC_VLM_DEL:
1975                 {
1976                     vlm_message_t *vlm_answer;
1977                     char name[512];
1978                     char psz[512+10];
1979                     if( p_intf->p_sys->p_vlm == NULL )
1980                         p_intf->p_sys->p_vlm = vlm_New( p_intf );
1981
1982                     uri_extract_value( p_request, "name", name, 512 );
1983                     sprintf( psz, "del %s", name );
1984
1985                     vlm_ExecuteCommand( p_intf->p_sys->p_vlm, psz, &vlm_answer );
1986                     /* FIXME do a vlm_answer -> var stack conversion */
1987                     vlm_MessageDelete( vlm_answer );
1988                     break;
1989                 }
1990
1991                 case MVLC_VLM_PLAY:
1992                 case MVLC_VLM_PAUSE:
1993                 case MVLC_VLM_STOP:
1994                 case MVLC_VLM_SEEK:
1995                 {
1996                     vlm_message_t *vlm_answer;
1997                     char name[512];
1998                     char psz[512+10];
1999                     if( p_intf->p_sys->p_vlm == NULL )
2000                         p_intf->p_sys->p_vlm = vlm_New( p_intf );
2001
2002                     uri_extract_value( p_request, "name", name, 512 );
2003                     if( StrToMacroType( control ) == MVLC_VLM_PLAY )
2004                         sprintf( psz, "control %s play", name );
2005                     else if( StrToMacroType( control ) == MVLC_VLM_PAUSE )
2006                         sprintf( psz, "control %s pause", name );
2007                     else if( StrToMacroType( control ) == MVLC_VLM_STOP )
2008                         sprintf( psz, "control %s stop", name );
2009                     else if( StrToMacroType( control ) == MVLC_VLM_SEEK )
2010                     {
2011                         char percent[20];
2012                         uri_extract_value( p_request, "percent", percent, 512 );
2013                         sprintf( psz, "control %s seek %s", name, percent );
2014                     }
2015
2016                     vlm_ExecuteCommand( p_intf->p_sys->p_vlm, psz, &vlm_answer );
2017                     /* FIXME do a vlm_answer -> var stack conversion */
2018                     vlm_MessageDelete( vlm_answer );
2019                     break;
2020                 }
2021
2022                 default:
2023                     PRINTS( "<!-- control param(%s) unsuported -->", control );
2024                     break;
2025             }
2026             break;
2027
2028         case MVLC_SET:
2029         {
2030             char    value[512];
2031             int     i;
2032             float   f;
2033
2034             if( i_request <= 0 ||
2035                 *m->param1  == '\0' ||
2036                 strstr( p_request, m->param1 ) == NULL )
2037             {
2038                 break;
2039             }
2040             uri_extract_value( p_request, m->param1,  value, 512 );
2041             uri_decode_url_encoded( value );
2042
2043             switch( StrToMacroType( m->param2 ) )
2044             {
2045                 case MVLC_INT:
2046                     i = atoi( value );
2047                     config_PutInt( p_intf, m->param1, i );
2048                     break;
2049                 case MVLC_FLOAT:
2050                     f = atof( value );
2051                     config_PutFloat( p_intf, m->param1, f );
2052                     break;
2053                 case MVLC_STRING:
2054                     config_PutPsz( p_intf, m->param1, value );
2055                     break;
2056                 default:
2057                     PRINTS( "<!-- invalid type(%s) in set -->", m->param2 )
2058             }
2059             break;
2060         }
2061         case MVLC_GET:
2062         {
2063             char    value[512];
2064             int     i;
2065             float   f;
2066             char    *psz;
2067
2068             if( *m->param1  == '\0' )
2069             {
2070                 break;
2071             }
2072
2073             switch( StrToMacroType( m->param2 ) )
2074             {
2075                 case MVLC_INT:
2076                     i = config_GetInt( p_intf, m->param1 );
2077                     sprintf( value, "%i", i );
2078                     break;
2079                 case MVLC_FLOAT:
2080                     f = config_GetFloat( p_intf, m->param1 );
2081                     sprintf( value, "%f", f );
2082                     break;
2083                 case MVLC_STRING:
2084                     psz = config_GetPsz( p_intf, m->param1 );
2085                     sprintf( value, "%s", psz ? psz : "" );
2086                     if( psz ) free( psz );
2087                     break;
2088                 default:
2089                     sprintf( value, "invalid type(%s) in set", m->param2 );
2090                     break;
2091             }
2092             msg_Dbg( p_intf, "get name=%s value=%s type=%s", m->param1, value, m->param2 );
2093             PRINTS( "%s", value );
2094             break;
2095         }
2096         case MVLC_VALUE:
2097         {
2098             char *s, *v;
2099
2100             if( m->param1 )
2101             {
2102                 EvaluateRPN( p_args->vars, &p_args->stack, m->param1 );
2103                 s = SSPop( &p_args->stack );
2104                 v = mvar_GetValue( p_args->vars, s );
2105             }
2106             else
2107             {
2108                 v = s = SSPop( &p_args->stack );
2109             }
2110
2111             PRINTS( "%s", v );
2112             free( s );
2113             break;
2114         }
2115         case MVLC_RPN:
2116             EvaluateRPN( p_args->vars, &p_args->stack, m->param1 );
2117             break;
2118
2119         case MVLC_UNKNOWN:
2120         default:
2121             PRINTS( "<!-- invalid macro id=`%s' -->", m->id );
2122             msg_Dbg( p_intf, "invalid macro id=`%s'", m->id );
2123             break;
2124     }
2125 #undef PRINTS
2126 #undef PRINT
2127 #undef ALLOC
2128 }
2129
2130 static uint8_t *MacroSearch( uint8_t *src, uint8_t *end, int i_mvlc, vlc_bool_t b_after )
2131 {
2132     int     i_id;
2133     int     i_level = 0;
2134
2135     while( src < end )
2136     {
2137         if( src + 4 < end  && !strncmp( src, "<vlc", 4 ) )
2138         {
2139             int i_skip;
2140             macro_t m;
2141
2142             i_skip = MacroParse( &m, src );
2143
2144             i_id = StrToMacroType( m.id );
2145
2146             switch( i_id )
2147             {
2148                 case MVLC_IF:
2149                 case MVLC_FOREACH:
2150                     i_level++;
2151                     break;
2152                 case MVLC_END:
2153                     i_level--;
2154                     break;
2155                 default:
2156                     break;
2157             }
2158
2159             MacroClean( &m );
2160
2161             if( ( i_mvlc == MVLC_END && i_level == -1 ) ||
2162                 ( i_mvlc != MVLC_END && i_level == 0 && i_mvlc == i_id ) )
2163             {
2164                 return src + ( b_after ? i_skip : 0 );
2165             }
2166             else if( i_level < 0 )
2167             {
2168                 return NULL;
2169             }
2170
2171             src += i_skip;
2172         }
2173         else
2174         {
2175             src++;
2176         }
2177     }
2178
2179     return NULL;
2180 }
2181
2182 static void Execute( httpd_file_sys_t *p_args,
2183                      uint8_t *p_request, int i_request,
2184                      uint8_t **pp_data, int *pi_data,
2185                      uint8_t **pp_dst,
2186                      uint8_t *_src, uint8_t *_end )
2187 {
2188     intf_thread_t  *p_intf = p_args->p_intf;
2189
2190     uint8_t *src, *dup, *end;
2191     uint8_t *dst = *pp_dst;
2192
2193     src = dup = malloc( _end - _src + 1 );
2194     end = src +( _end - _src );
2195
2196     memcpy( src, _src, _end - _src );
2197     *end = '\0';
2198
2199     /* we parse searching <vlc */
2200     while( src < end )
2201     {
2202         uint8_t *p;
2203         int i_copy;
2204
2205         p = strstr( src, "<vlc" );
2206         if( p < end && p == src )
2207         {
2208             macro_t m;
2209
2210             src += MacroParse( &m, src );
2211
2212             //msg_Dbg( p_intf, "macro_id=%s", m.id );
2213
2214             switch( StrToMacroType( m.id ) )
2215             {
2216                 case MVLC_IF:
2217                 {
2218                     vlc_bool_t i_test;
2219                     uint8_t    *endif;
2220
2221                     EvaluateRPN( p_args->vars, &p_args->stack, m.param1 );
2222                     if( SSPopN( &p_args->stack, p_args->vars ) )
2223                     {
2224                         i_test = 1;
2225                     }
2226                     else
2227                     {
2228                         i_test = 0;
2229                     }
2230                     endif = MacroSearch( src, end, MVLC_END, VLC_TRUE );
2231
2232                     if( i_test == 0 )
2233                     {
2234                         uint8_t *start = MacroSearch( src, endif, MVLC_ELSE, VLC_TRUE );
2235
2236                         if( start )
2237                         {
2238                             uint8_t *stop  = MacroSearch( start, endif, MVLC_END, VLC_FALSE );
2239                             if( stop )
2240                             {
2241                                 Execute( p_args, p_request, i_request, pp_data, pi_data, &dst, start, stop );
2242                             }
2243                         }
2244                     }
2245                     else if( i_test == 1 )
2246                     {
2247                         uint8_t *stop;
2248                         if( ( stop = MacroSearch( src, endif, MVLC_ELSE, VLC_FALSE ) ) == NULL )
2249                         {
2250                             stop = MacroSearch( src, endif, MVLC_END, VLC_FALSE );
2251                         }
2252                         if( stop )
2253                         {
2254                             Execute( p_args, p_request, i_request, pp_data, pi_data, &dst, src, stop );
2255                         }
2256                     }
2257
2258                     src = endif;
2259                     break;
2260                 }
2261                 case MVLC_FOREACH:
2262                 {
2263                     uint8_t *endfor = MacroSearch( src, end, MVLC_END, VLC_TRUE );
2264                     uint8_t *start = src;
2265                     uint8_t *stop = MacroSearch( src, end, MVLC_END, VLC_FALSE );
2266
2267                     if( stop )
2268                     {
2269                         mvar_t *index;
2270                         int    i_idx;
2271                         mvar_t *v;
2272                         if( !strcmp( m.param2, "integer" ) )
2273                         {
2274                             char *arg = SSPop( &p_args->stack );
2275                             index = mvar_IntegerSetNew( m.param1, arg );
2276                             free( arg );
2277                         }
2278                         else if( !strcmp( m.param2, "directory" ) )
2279                         {
2280                             char *arg = SSPop( &p_args->stack );
2281                             index = mvar_FileSetNew( m.param1, arg );
2282                             free( arg );
2283                         }
2284                         else if( !strcmp( m.param2, "playlist" ) )
2285                         {
2286                             index = mvar_PlaylistSetNew( m.param1, p_intf->p_sys->p_playlist );
2287                         }
2288                         else if( !strcmp( m.param2, "informations" ) )
2289                         {
2290                             index = mvar_InfoSetNew( m.param1, p_intf->p_sys->p_input );
2291                         }
2292                         else if( !strcmp( m.param2, "vlm" ) )
2293                         {
2294                             if( p_intf->p_sys->p_vlm == NULL )
2295                             {
2296                                 p_intf->p_sys->p_vlm = vlm_New( p_intf );
2297                             }
2298                             index = mvar_VlmSetNew( m.param1, p_intf->p_sys->p_vlm );
2299                         }
2300 #if 0
2301                         else if( !strcmp( m.param2, "hosts" ) )
2302                         {
2303                             index = mvar_HttpdInfoSetNew( m.param1, p_intf->p_sys->p_httpd, HTTPD_GET_HOSTS );
2304                         }
2305                         else if( !strcmp( m.param2, "urls" ) )
2306                         {
2307                             index = mvar_HttpdInfoSetNew( m.param1, p_intf->p_sys->p_httpd, HTTPD_GET_URLS );
2308                         }
2309                         else if( !strcmp( m.param2, "connections" ) )
2310                         {
2311                             index = mvar_HttpdInfoSetNew(m.param1, p_intf->p_sys->p_httpd, HTTPD_GET_CONNECTIONS);
2312                         }
2313 #endif
2314                         else if( ( v = mvar_GetVar( p_args->vars, m.param2 ) ) )
2315                         {
2316                             index = mvar_Duplicate( v );
2317                         }
2318                         else
2319                         {
2320                             msg_Dbg( p_intf, "invalid index constructor (%s)", m.param2 );
2321                             src = endfor;
2322                             break;
2323                         }
2324
2325                         for( i_idx = 0; i_idx < index->i_field; i_idx++ )
2326                         {
2327                             mvar_t *f = mvar_Duplicate( index->field[i_idx] );
2328
2329                             //msg_Dbg( p_intf, "foreach field[%d] name=%s value=%s", i_idx, f->name, f->value );
2330
2331                             free( f->name );
2332                             f->name = strdup( m.param1 );
2333
2334
2335                             mvar_PushVar( p_args->vars, f );
2336                             Execute( p_args, p_request, i_request, pp_data, pi_data, &dst, start, stop );
2337                             mvar_RemoveVar( p_args->vars, f );
2338
2339                             mvar_Delete( f );
2340                         }
2341                         mvar_Delete( index );
2342
2343                         src = endfor;
2344                     }
2345                     break;
2346                 }
2347                 default:
2348                     MacroDo( p_args, &m, p_request, i_request, pp_data, pi_data, &dst );
2349                     break;
2350             }
2351
2352             MacroClean( &m );
2353             continue;
2354         }
2355
2356         i_copy =   ( (p == NULL || p > end ) ? end : p  ) - src;
2357         if( i_copy > 0 )
2358         {
2359             int i_index = dst - *pp_data;
2360
2361             *pi_data += i_copy;
2362             *pp_data = realloc( *pp_data, *pi_data );
2363             dst = (*pp_data) + i_index;
2364
2365             memcpy( dst, src, i_copy );
2366             dst += i_copy;
2367             src += i_copy;
2368         }
2369     }
2370
2371     *pp_dst = dst;
2372     free( dup );
2373 }
2374
2375 /****************************************************************************
2376  * HttpCallback:
2377  ****************************************************************************
2378  * a file with b_html is parsed and all "macro" replaced
2379  * <vlc id="macro name" [param1="" [param2=""]] />
2380  * valid id are
2381  *
2382  ****************************************************************************/
2383 static int  HttpCallback( httpd_file_sys_t *p_args,
2384                           httpd_file_t *p_file,
2385                           uint8_t *p_request,
2386                           uint8_t **pp_data, int *pi_data )
2387 {
2388     int i_request = p_request ? strlen( p_request ) : 0;
2389     char *p;
2390     FILE *f;
2391
2392     if( ( f = fopen( p_args->file, "r" ) ) == NULL )
2393     {
2394         p = *pp_data = malloc( 10240 );
2395         if( !p )
2396         {
2397                 return VLC_EGENERIC;
2398         }
2399         p += sprintf( p, "<html>\n" );
2400         p += sprintf( p, "<head>\n" );
2401         p += sprintf( p, "<title>Error loading %s</title>\n", p_args->file );
2402         p += sprintf( p, "</head>\n" );
2403         p += sprintf( p, "<body>\n" );
2404         p += sprintf( p, "<h1><center>Error loading %s for %s</center></h1>\n", p_args->file, p_args->name );
2405         p += sprintf( p, "<hr />\n" );
2406         p += sprintf( p, "<a href=\"http://www.videolan.org/\">VideoLAN</a>\n" );
2407         p += sprintf( p, "</body>\n" );
2408         p += sprintf( p, "</html>\n" );
2409
2410         *pi_data = strlen( *pp_data );
2411
2412         return VLC_SUCCESS;
2413     }
2414
2415     if( !p_args->b_html )
2416     {
2417         FileLoad( f, pp_data, pi_data );
2418     }
2419     else
2420     {
2421         int  i_buffer;
2422         uint8_t *p_buffer;
2423         uint8_t *dst;
2424         vlc_value_t val;
2425         char position[4]; /* percentage */
2426         char time[12]; /* in seconds */
2427         char length[12]; /* in seconds */
2428         audio_volume_t i_volume;
2429         char volume[5];
2430         char state[8];
2431  
2432 #define p_sys p_args->p_intf->p_sys
2433         if( p_sys->p_input )
2434         {
2435             var_Get( p_sys->p_input, "position", &val);
2436             sprintf( position, "%d" , (int)((val.f_float) * 100.0));
2437             var_Get( p_sys->p_input, "time", &val);
2438             sprintf( time, "%d" , (int)(val.i_time / 1000000) );
2439             var_Get( p_sys->p_input, "length", &val);
2440             sprintf( length, "%d" , (int)(val.i_time / 1000000) );
2441
2442             var_Get( p_sys->p_input, "state", &val );
2443             if( val.i_int == PLAYING_S )
2444             {
2445                 sprintf( state, "playing" );
2446             } else if( val.i_int == PAUSE_S )
2447             {
2448                 sprintf( state, "paused" );
2449             } else
2450             {
2451                 sprintf( state, "stop" );
2452             }
2453         } else
2454         {
2455             sprintf( position, "%d", 0 );
2456             sprintf( time, "%d", 0 );
2457             sprintf( length, "%d", 0 );
2458             sprintf( state, "stop" );
2459         }
2460 #undef p_sys
2461
2462         aout_VolumeGet( p_args->p_intf , &i_volume );
2463         sprintf( volume , "%d" , (int)i_volume );
2464
2465         p_args->vars = mvar_New( "variables", "" );
2466         mvar_AppendNewVar( p_args->vars, "url_param", i_request > 0 ? "1" : "0" );
2467         mvar_AppendNewVar( p_args->vars, "url_value", p_request );
2468         mvar_AppendNewVar( p_args->vars, "version",   VERSION_MESSAGE );
2469         mvar_AppendNewVar( p_args->vars, "copyright", COPYRIGHT_MESSAGE );
2470         mvar_AppendNewVar( p_args->vars, "stream_position", position );
2471         mvar_AppendNewVar( p_args->vars, "stream_time", time );
2472         mvar_AppendNewVar( p_args->vars, "stream_length", length );
2473         mvar_AppendNewVar( p_args->vars, "volume", volume );
2474         mvar_AppendNewVar( p_args->vars, "stream_state", state );
2475
2476         SSInit( &p_args->stack );
2477
2478         /* first we load in a temporary buffer */
2479         FileLoad( f, &p_buffer, &i_buffer );
2480
2481         /* allocate output */
2482         *pi_data = i_buffer + 1000;
2483         dst = *pp_data = malloc( *pi_data );
2484
2485         /* we parse executing all  <vlc /> macros */
2486         Execute( p_args, p_request, i_request, pp_data, pi_data, &dst, &p_buffer[0], &p_buffer[i_buffer] );
2487
2488         *dst     = '\0';
2489         *pi_data = dst - *pp_data;
2490
2491         SSClean( &p_args->stack );
2492         mvar_Delete( p_args->vars );
2493         free( p_buffer );
2494     }
2495
2496     fclose( f );
2497
2498     return VLC_SUCCESS;
2499 }
2500
2501 /****************************************************************************
2502  * uri parser
2503  ****************************************************************************/
2504 static int uri_test_param( char *psz_uri, char *psz_name )
2505 {
2506     char *p = psz_uri;
2507
2508     while( (p = strstr( p, psz_name )) )
2509     {
2510         /* Verify that we are dealing with a post/get argument */
2511         if( p == psz_uri || *(p - 1) == '&' || *(p - 1) == '\n' )
2512         {
2513             return VLC_TRUE;
2514         }
2515         p++;
2516     }
2517
2518     return VLC_FALSE;
2519 }
2520 static char *uri_extract_value( char *psz_uri, char *psz_name,
2521                                 char *psz_value, int i_value_max )
2522 {
2523     char *p = psz_uri;
2524
2525     while( (p = strstr( p, psz_name )) )
2526     {
2527         /* Verify that we are dealing with a post/get argument */
2528         if( p == psz_uri || *(p - 1) == '&' || *(p - 1) == '\n' )
2529             break;
2530         p++;
2531     }
2532
2533     if( p )
2534     {
2535         int i_len;
2536
2537         p += strlen( psz_name );
2538         if( *p == '=' ) p++;
2539
2540         if( strchr( p, '&' ) )
2541         {
2542             i_len = strchr( p, '&' ) - p;
2543         }
2544         else
2545         {
2546             /* for POST method */
2547             if( strchr( p, '\n' ) )
2548             {
2549                 i_len = strchr( p, '\n' ) - p;
2550                 if( i_len && *(p+i_len-1) == '\r' ) i_len--;
2551             }
2552             else
2553             {
2554                 i_len = strlen( p );
2555             }
2556         }
2557         i_len = __MIN( i_value_max - 1, i_len );
2558         if( i_len > 0 )
2559         {
2560             strncpy( psz_value, p, i_len );
2561             psz_value[i_len] = '\0';
2562         }
2563         else
2564         {
2565             strncpy( psz_value, "", i_value_max );
2566         }
2567         p += i_len;
2568     }
2569     else
2570     {
2571         strncpy( psz_value, "", i_value_max );
2572     }
2573
2574     return p;
2575 }
2576
2577 static void uri_decode_url_encoded( char *psz )
2578 {
2579     char *dup = strdup( psz );
2580     char *p = dup;
2581
2582     while( *p )
2583     {
2584         if( *p == '%' )
2585         {
2586             char val[3];
2587             p++;
2588             if( !*p )
2589             {
2590                 break;
2591             }
2592
2593             val[0] = *p++;
2594             val[1] = *p++;
2595             val[2] = '\0';
2596
2597             *psz++ = strtol( val, NULL, 16 );
2598         }
2599         else if( *p == '+' )
2600         {
2601             *psz++ = ' ';
2602             p++;
2603         }
2604         else
2605         {
2606             *psz++ = *p++;
2607         }
2608     }
2609     *psz++  ='\0';
2610     free( dup );
2611 }
2612
2613 /****************************************************************************
2614  * Light RPN evaluator
2615  ****************************************************************************/
2616 static void SSInit( rpn_stack_t *st )
2617 {
2618     st->i_stack = 0;
2619 }
2620
2621 static void SSClean( rpn_stack_t *st )
2622 {
2623     while( st->i_stack > 0 )
2624     {
2625         free( st->stack[--st->i_stack] );
2626     }
2627 }
2628
2629 static void SSPush( rpn_stack_t *st, char *s )
2630 {
2631     if( st->i_stack < STACK_MAX )
2632     {
2633         st->stack[st->i_stack++] = strdup( s );
2634     }
2635 }
2636
2637 static char * SSPop( rpn_stack_t *st )
2638 {
2639     if( st->i_stack <= 0 )
2640     {
2641         return strdup( "" );
2642     }
2643     else
2644     {
2645         return st->stack[--st->i_stack];
2646     }
2647 }
2648
2649 static int SSPopN( rpn_stack_t *st, mvar_t  *vars )
2650 {
2651     char *name;
2652     char *value;
2653
2654     char *end;
2655     int  i;
2656
2657     name = SSPop( st );
2658     i = strtol( name, &end, 0 );
2659     if( end == name )
2660     {
2661         value = mvar_GetValue( vars, name );
2662         i = atoi( value );
2663     }
2664     free( name );
2665
2666     return( i );
2667 }
2668
2669 static void SSPushN( rpn_stack_t *st, int i )
2670 {
2671     char v[512];
2672
2673     sprintf( v, "%d", i );
2674     SSPush( st, v );
2675 }
2676
2677 static void  EvaluateRPN( mvar_t  *vars, rpn_stack_t *st, char *exp )
2678 {
2679     for( ;; )
2680     {
2681         char s[100], *p;
2682
2683         /* skip spcae */
2684         while( *exp == ' ' )
2685         {
2686             exp++;
2687         }
2688
2689         if( *exp == '\'' )
2690         {
2691             /* extract string */
2692             p = &s[0];
2693             exp++;
2694             while( *exp && *exp != '\'' )
2695             {
2696                 *p++ = *exp++;
2697             }
2698             *p = '\0';
2699             exp++;
2700             SSPush( st, s );
2701             continue;
2702         }
2703
2704         /* extract token */
2705         p = strchr( exp, ' ' );
2706         if( !p )
2707         {
2708             strcpy( s, exp );
2709
2710             exp += strlen( exp );
2711         }
2712         else
2713         {
2714             int i = p -exp;
2715             strncpy( s, exp, i );
2716             s[i] = '\0';
2717
2718             exp = p + 1;
2719         }
2720
2721         if( *s == '\0' )
2722         {
2723             break;
2724         }
2725
2726         /* 1. Integer function */
2727         if( !strcmp( s, "!" ) )
2728         {
2729             SSPushN( st, ~SSPopN( st, vars ) );
2730         }
2731         else if( !strcmp( s, "^" ) )
2732         {
2733             SSPushN( st, SSPopN( st, vars ) ^ SSPopN( st, vars ) );
2734         }
2735         else if( !strcmp( s, "&" ) )
2736         {
2737             SSPushN( st, SSPopN( st, vars ) & SSPopN( st, vars ) );
2738         }
2739         else if( !strcmp( s, "|" ) )
2740         {
2741             SSPushN( st, SSPopN( st, vars ) | SSPopN( st, vars ) );
2742         }
2743         else if( !strcmp( s, "+" ) )
2744         {
2745             SSPushN( st, SSPopN( st, vars ) + SSPopN( st, vars ) );
2746         }
2747         else if( !strcmp( s, "-" ) )
2748         {
2749             int j = SSPopN( st, vars );
2750             int i = SSPopN( st, vars );
2751             SSPushN( st, i - j );
2752         }
2753         else if( !strcmp( s, "*" ) )
2754         {
2755             SSPushN( st, SSPopN( st, vars ) * SSPopN( st, vars ) );
2756         }
2757         else if( !strcmp( s, "/" ) )
2758         {
2759             int i, j;
2760
2761             j = SSPopN( st, vars );
2762             i = SSPopN( st, vars );
2763
2764             SSPushN( st, j != 0 ? i / j : 0 );
2765         }
2766         else if( !strcmp( s, "%" ) )
2767         {
2768             int i, j;
2769
2770             j = SSPopN( st, vars );
2771             i = SSPopN( st, vars );
2772
2773             SSPushN( st, j != 0 ? i % j : 0 );
2774         }
2775         /* 2. integer tests */
2776         else if( !strcmp( s, "=" ) )
2777         {
2778             SSPushN( st, SSPopN( st, vars ) == SSPopN( st, vars ) ? -1 : 0 );
2779         }
2780         else if( !strcmp( s, "<" ) )
2781         {
2782             int j = SSPopN( st, vars );
2783             int i = SSPopN( st, vars );
2784
2785             SSPushN( st, i < j ? -1 : 0 );
2786         }
2787         else if( !strcmp( s, ">" ) )
2788         {
2789             int j = SSPopN( st, vars );
2790             int i = SSPopN( st, vars );
2791
2792             SSPushN( st, i > j ? -1 : 0 );
2793         }
2794         else if( !strcmp( s, "<=" ) )
2795         {
2796             int j = SSPopN( st, vars );
2797             int i = SSPopN( st, vars );
2798
2799             SSPushN( st, i <= j ? -1 : 0 );
2800         }
2801         else if( !strcmp( s, ">=" ) )
2802         {
2803             int j = SSPopN( st, vars );
2804             int i = SSPopN( st, vars );
2805
2806             SSPushN( st, i >= j ? -1 : 0 );
2807         }
2808         /* 3. string functions */
2809         else if( !strcmp( s, "strcat" ) )
2810         {
2811             char *s2 = SSPop( st );
2812             char *s1 = SSPop( st );
2813             char *str = malloc( strlen( s1 ) + strlen( s2 ) + 1 );
2814
2815             strcpy( str, s1 );
2816             strcat( str, s2 );
2817
2818             SSPush( st, str );
2819             free( s1 );
2820             free( s2 );
2821             free( str );
2822         }
2823         else if( !strcmp( s, "strcmp" ) )
2824         {
2825             char *s2 = SSPop( st );
2826             char *s1 = SSPop( st );
2827
2828             SSPushN( st, strcmp( s1, s2 ) );
2829             free( s1 );
2830             free( s2 );
2831         }
2832         else if( !strcmp( s, "strncmp" ) )
2833         {
2834             int n = SSPopN( st, vars );
2835             char *s2 = SSPop( st );
2836             char *s1 = SSPop( st );
2837
2838             SSPushN( st, strncmp( s1, s2 , n ) );
2839             free( s1 );
2840             free( s2 );
2841         }
2842         else if( !strcmp( s, "strsub" ) )
2843         {
2844             int n = SSPopN( st, vars );
2845             int m = SSPopN( st, vars );
2846             int i_len;
2847             char *s = SSPop( st );
2848             char *str;
2849
2850             if( n >= m )
2851             {
2852                 i_len = n - m + 1;
2853             }
2854             else
2855             {
2856                 i_len = 0;
2857             }
2858
2859             str = malloc( i_len + 1 );
2860
2861             memcpy( str, s + m - 1, i_len );
2862             str[ i_len ] = '\0';
2863
2864             SSPush( st, str );
2865             free( s );
2866             free( str );
2867         }
2868        else if( !strcmp( s, "strlen" ) )
2869         {
2870             char *str = SSPop( st );
2871
2872             SSPushN( st, strlen( str ) );
2873             free( str );
2874         }
2875         /* 4. stack functions */
2876         else if( !strcmp( s, "dup" ) )
2877         {
2878             char *str = SSPop( st );
2879             SSPush( st, str );
2880             SSPush( st, str );
2881             free( str );
2882         }
2883         else if( !strcmp( s, "drop" ) )
2884         {
2885             char *str = SSPop( st );
2886             free( str );
2887         }
2888         else if( !strcmp( s, "swap" ) )
2889         {
2890             char *s1 = SSPop( st );
2891             char *s2 = SSPop( st );
2892
2893             SSPush( st, s1 );
2894             SSPush( st, s2 );
2895             free( s1 );
2896             free( s2 );
2897         }
2898         else if( !strcmp( s, "flush" ) )
2899         {
2900             SSClean( st );
2901             SSInit( st );
2902         }
2903         else if( !strcmp( s, "store" ) )
2904         {
2905             char *value = SSPop( st );
2906             char *name  = SSPop( st );
2907
2908             mvar_PushNewVar( vars, name, value );
2909             free( name );
2910             free( value );
2911         }
2912         else if( !strcmp( s, "value" ) )
2913         {
2914             char *name  = SSPop( st );
2915             char *value = mvar_GetValue( vars, name );
2916
2917             SSPush( st, value );
2918
2919             free( name );
2920         }
2921         else if( !strcmp( s, "url_extract" ) )
2922         {
2923             char *url = mvar_GetValue( vars, "url_value" );
2924             char *name = SSPop( st );
2925             char value[512];
2926
2927             uri_extract_value( url, name, value, 512 );
2928             uri_decode_url_encoded( value );
2929             SSPush( st, value );
2930         }
2931         else
2932         {
2933             SSPush( st, s );
2934         }
2935     }
2936 }
2937
2938 /**********************************************************************
2939  * Find_end_MRL: Find the end of the sentence :
2940  * this function parses the string psz and find the end of the item
2941  * and/or option with detecting the " and ' problems.
2942  * returns NULL if an error is detected, otherwise, returns a pointer
2943  * of the end of the sentence (after the last character)
2944  **********************************************************************/
2945 static char *Find_end_MRL( char *psz )
2946 {
2947     char *s_sent = psz;
2948
2949     switch( *s_sent )
2950     {
2951         case '\"':
2952         {
2953             s_sent++;
2954
2955             while( ( *s_sent != '\"' ) && ( *s_sent != '\0' ) )
2956             {
2957                 if( *s_sent == '\'' )
2958                 {
2959                     s_sent = Find_end_MRL( s_sent );
2960
2961                     if( s_sent == NULL )
2962                     {
2963                         return NULL;
2964                     }
2965                 } else
2966                 {
2967                     s_sent++;
2968                 }
2969             }
2970
2971             if( *s_sent == '\"' )
2972             {
2973                 s_sent++;
2974                 return s_sent;
2975             } else  /* *s_sent == '\0' , which means the number of " is incorrect */
2976             {
2977                 return NULL;
2978             }
2979             break;
2980         }
2981         case '\'':
2982         {
2983             s_sent++;
2984
2985             while( ( *s_sent != '\'' ) && ( *s_sent != '\0' ) )
2986             {
2987                 if( *s_sent == '\"' )
2988                 {
2989                     s_sent = Find_end_MRL( s_sent );
2990
2991                     if( s_sent == NULL )
2992                     {
2993                         return NULL;
2994                     }
2995                 } else
2996                 {
2997                     s_sent++;
2998                 }
2999             }
3000
3001             if( *s_sent == '\'' )
3002             {
3003                 s_sent++;
3004                 return s_sent;
3005             } else  /* *s_sent == '\0' , which means the number of ' is incorrect */
3006             {
3007                 return NULL;
3008             }
3009             break;
3010         }
3011         default: /* now we can look for spaces */
3012         {
3013             while( ( *s_sent != ' ' ) && ( *s_sent != '\0' ) )
3014             {
3015                 if( ( *s_sent == '\'' ) || ( *s_sent == '\"' ) )
3016                 {
3017                     s_sent = Find_end_MRL( s_sent );
3018                 } else
3019                 {
3020                     s_sent++;
3021                 }
3022             }
3023             return s_sent;
3024         }
3025     }
3026 }
3027
3028 /**********************************************************************
3029  * parse_MRL: parse the MRL, find the mrl string and the options,
3030  * create an item with all informations in it, and return the item.
3031  * return NULL if there is an error.
3032  **********************************************************************/
3033 playlist_item_t * parse_MRL( intf_thread_t *p_intf, char *psz )
3034 {
3035     char **ppsz_options = NULL;
3036     char *mrl;
3037     char *s_mrl = psz;
3038     int i_error = 0;
3039     char *s_temp;
3040     int i = 0;
3041     int i_options = 0;
3042     playlist_item_t * p_item = NULL;
3043
3044     /* In case there is spaces before the mrl */
3045     while( ( *s_mrl == ' ' ) && ( *s_mrl != '\0' ) )
3046     {
3047         s_mrl++;
3048     }
3049
3050     /* extract the mrl */
3051     s_temp = strstr( s_mrl , " :" );
3052     if( s_temp == NULL )
3053     {
3054         s_temp = s_mrl + strlen( s_mrl );
3055     } else
3056     {
3057         while( (*s_temp == ' ') && (s_temp != s_mrl ) )
3058         {
3059             s_temp--;
3060         }
3061         s_temp++;
3062     }
3063
3064     /* if the mrl is between " or ', we must remove them */
3065     if( (*s_mrl == '\'') || (*s_mrl == '\"') )
3066     {
3067         mrl = (char *)malloc( (s_temp - s_mrl - 1) * sizeof( char ) );
3068         strncpy( mrl , (s_mrl + 1) , s_temp - s_mrl - 2 );
3069         mrl[ s_temp - s_mrl - 2 ] = '\0';
3070     } else
3071     {
3072         mrl = (char *)malloc( (s_temp - s_mrl + 1) * sizeof( char ) );
3073         strncpy( mrl , s_mrl , s_temp - s_mrl );
3074         mrl[ s_temp - s_mrl ] = '\0';
3075     }
3076
3077     s_mrl = s_temp;
3078
3079     /* now we can take care of the options */
3080     while( (*s_mrl != '\0') && (i_error == 0) )
3081     {
3082         switch( *s_mrl )
3083         {
3084             case ' ':
3085             {
3086                 s_mrl++;
3087                 break;
3088             }
3089             case ':': /* an option */
3090             {
3091                 s_temp = Find_end_MRL( s_mrl );
3092
3093                 if( s_temp == NULL )
3094                 {
3095                     i_error = 1;
3096                 }
3097                 else
3098                 {
3099                     i_options++;
3100                     ppsz_options = realloc( ppsz_options , i_options *
3101                                             sizeof(char *) );
3102                     ppsz_options[ i_options - 1 ] =
3103                         malloc( (s_temp - s_mrl + 1) * sizeof(char) );
3104
3105                     strncpy( ppsz_options[ i_options - 1 ] , s_mrl ,
3106                              s_temp - s_mrl );
3107
3108                     /* don't forget to finish the string with a '\0' */
3109                     (ppsz_options[ i_options - 1 ])[ s_temp - s_mrl ] = '\0';
3110
3111                     s_mrl = s_temp;
3112                 }
3113                 break;
3114             }
3115             default:
3116             {
3117                 i_error = 1;
3118                 break;
3119             }
3120         }
3121     }
3122
3123     if( i_error != 0 )
3124     {
3125         free( mrl );
3126     }
3127     else
3128     {
3129         /* now create an item */
3130         p_item = playlist_ItemNew( p_intf, mrl, mrl);
3131         for( i = 0 ; i< i_options ; i++ )
3132         {
3133             playlist_ItemAddOption( p_item, ppsz_options[i] );
3134         }
3135     }
3136
3137     for( i = 0 ; i < i_options ; i++ )
3138     {
3139         free( ppsz_options[i] );
3140     }
3141     free( ppsz_options );
3142
3143     return p_item;
3144 }