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