]> git.sesse.net Git - vlc/blob - modules/control/http.c
freetype.c : use the default BeOS font on BeOS
[vlc] / modules / control / http.c
1 /*****************************************************************************
2  * http.c :  http mini-server ;)
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  * $Id: http.c,v 1.17 2003/07/23 01:13:47 gbazin Exp $
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 "httpd.h"
41
42 #ifdef HAVE_SYS_STAT_H
43 #   include <sys/stat.h>
44 #endif
45 #ifdef HAVE_ERRNO_H
46 #   include <errno.h>
47 #endif
48 #ifdef HAVE_FCNTL_H
49 #   include <fcntl.h>
50 #endif
51
52 #ifdef HAVE_UNISTD_H
53 #   include <unistd.h>
54 #elif defined( WIN32 ) && !defined( UNDER_CE )
55 #   include <io.h>
56 #endif
57
58 #if (!defined( WIN32 ) || defined(__MINGW32__))
59 /* Mingw has its own version of dirent */
60 #   include <dirent.h>
61 #endif
62
63
64 /*****************************************************************************
65  * Local prototypes
66  *****************************************************************************/
67 static int  Activate     ( vlc_object_t * );
68 static void Close        ( vlc_object_t * );
69 static void Run          ( intf_thread_t *p_intf );
70
71 static int ParseDirectory( intf_thread_t *p_intf, char *psz_root,
72                            char *psz_dir );
73
74 static int  http_get( httpd_file_callback_args_t *p_args,
75                       uint8_t *p_request, int i_request,
76                       uint8_t **pp_data, int *pi_data );
77
78 static void uri_extract_value( char *psz_uri, char *psz_name,
79                                char *psz_value, int i_value_max );
80 static void uri_decode_url_encoded( char *psz );
81
82 /*****************************************************************************
83  *
84  *****************************************************************************/
85 typedef struct mvar_s
86 {
87     char *name;
88     char *value;
89
90     int           i_field;
91     struct mvar_s **field;
92 } mvar_t;
93
94 #define STACK_MAX 100
95 typedef struct
96 {
97     char *stack[STACK_MAX];
98     int  i_stack;
99 } rpn_stack_t;
100
101 struct httpd_file_callback_args_t
102 {
103     intf_thread_t *p_intf;
104     httpd_file_t  *p_file;
105
106     char          *file;
107     char          *name;
108     char          *mime;
109
110     /* inited for each access */
111     rpn_stack_t       stack;
112     mvar_t        *vars;
113 };
114
115 struct intf_sys_t
116 {
117     httpd_t             *p_httpd;
118     httpd_host_t        *p_httpd_host;
119
120     int                         i_files;
121     httpd_file_callback_args_t  **pp_files;
122
123     playlist_t          *p_playlist;
124     input_thread_t      *p_input;
125 };
126
127
128 /*****************************************************************************
129  * Module descriptor
130  *****************************************************************************/
131 #define HOST_TEXT N_( "Host address" )
132 #define HOST_LONGTEXT N_( \
133     "You can set the address and port on which the http interface will bind" )
134 #define SRC_TEXT N_( "Source directory" )
135 #define SRC_LONGTEXT N_( "Source directory" )
136
137 vlc_module_begin();
138     set_description( _("HTTP remote control interface") );
139     add_category_hint( N_("HTTP remote control"), NULL, VLC_TRUE );
140         add_string ( "http-host", NULL, NULL, HOST_TEXT, HOST_LONGTEXT, VLC_TRUE );
141 #if defined(SYS_DARWIN) || defined(SYS_BEOS) \
142              || (defined(WIN32) && !defined(UNDER_CE))
143         add_string ( "http-src",  NULL, NULL, SRC_TEXT,  SRC_LONGTEXT,  VLC_TRUE );
144 #else
145         add_string ( "http-src",  "share/http", NULL, SRC_TEXT,  SRC_LONGTEXT,  VLC_TRUE );
146 #endif
147     set_capability( "interface", 0 );
148     set_callbacks( Activate, Close );
149 vlc_module_end();
150
151 /*****************************************************************************
152  * Activate: initialize and create stuff
153  *****************************************************************************/
154 static int Activate( vlc_object_t *p_this )
155 {
156     intf_thread_t *p_intf = (intf_thread_t*)p_this;
157     intf_sys_t    *p_sys;
158     char          *psz_host;
159     char          *psz_address = "";
160     int           i_port       = 0;
161     char          *psz_src;
162
163     psz_host = config_GetPsz( p_intf, "http-host" );
164     if( psz_host )
165     {
166         char *psz_parser;
167         psz_address = psz_host;
168
169         psz_parser = strchr( psz_host, ':' );
170         if( psz_parser )
171         {
172             *psz_parser++ = '\0';
173             i_port = atoi( psz_parser );
174         }
175     }
176     if( i_port <= 0 )
177     {
178         i_port= 8080;
179     }
180
181     msg_Dbg( p_intf, "base %s:%d", psz_address, i_port );
182     p_intf->p_sys = p_sys = malloc( sizeof( intf_sys_t ) );
183     p_sys->p_playlist = NULL;
184     p_sys->p_input    = NULL;
185
186     if( ( p_sys->p_httpd = httpd_Find( VLC_OBJECT(p_intf), VLC_TRUE ) ) == NULL )
187     {
188         msg_Err( p_intf, "cannot create/find httpd" );
189         free( p_sys );
190         return VLC_EGENERIC;
191     }
192
193     if( ( p_sys->p_httpd_host =
194                 p_sys->p_httpd->pf_register_host( p_sys->p_httpd,
195                                                   psz_address, i_port ) ) == NULL )
196     {
197         msg_Err( p_intf, "cannot listen on %s:%d", psz_address, i_port );
198         httpd_Release( p_sys->p_httpd );
199         free( p_sys );
200         return VLC_EGENERIC;
201     }
202
203     if( psz_host )
204     {
205         free( psz_host );
206     }
207
208     p_sys->i_files = 0;
209     p_sys->pp_files = malloc( sizeof( httpd_file_callback_args_t *) );
210
211 #if defined(SYS_DARWIN) || defined(SYS_BEOS) || \
212         ( defined(WIN32) && !defined(UNDER_CE ) )
213     if ( ( psz_src = config_GetPsz( p_intf, "http-src" )) == NULL )
214     {
215         char * psz_vlcpath = p_intf->p_libvlc->psz_vlcpath;
216         psz_src = malloc( strlen(psz_vlcpath) + strlen("/share/http" ) + 1 );
217         sprintf( psz_src, "%s/share/http", psz_vlcpath);
218     }
219 #else
220     psz_src = config_GetPsz( p_intf, "http-src" );
221 #endif
222     if( !psz_src || *psz_src == '\0' )
223     {
224         msg_Err( p_intf, "invalid src dir" );
225         goto failed;
226     }
227
228     /* remove trainling \ or / */
229     if( psz_src[strlen( psz_src ) - 1] == '\\' ||
230         psz_src[strlen( psz_src ) - 1] == '/' )
231     {
232         psz_src[strlen( psz_src ) - 1] = '\0';
233     }
234
235     ParseDirectory( p_intf, psz_src, psz_src );
236
237
238     if( p_sys->i_files <= 0 )
239     {
240         msg_Err( p_intf, "cannot find any files" );
241         goto failed;
242     }
243     p_intf->pf_run = Run;
244
245     return VLC_SUCCESS;
246
247 failed:
248     free( p_sys->pp_files );
249     p_sys->p_httpd->pf_unregister_host( p_sys->p_httpd,
250                                         p_sys->p_httpd_host );
251     httpd_Release( p_sys->p_httpd );
252     free( p_sys );
253     return VLC_EGENERIC;
254 }
255
256 /*****************************************************************************
257  * CloseIntf: destroy interface
258  *****************************************************************************/
259 void Close ( vlc_object_t *p_this )
260 {
261     intf_thread_t *p_intf = (intf_thread_t *)p_this;
262     intf_sys_t    *p_sys = p_intf->p_sys;
263
264     int i;
265
266     for( i = 0; i < p_sys->i_files; i++ )
267     {
268        p_sys->p_httpd->pf_unregister_file( p_sys->p_httpd,
269                                            p_sys->pp_files[i]->p_file );
270        /* do not free mime */
271        free( p_sys->pp_files[i]->file );
272        free( p_sys->pp_files[i]->name );
273        free( p_sys->pp_files[i] );
274     }
275     free( p_sys->pp_files );
276     p_sys->p_httpd->pf_unregister_host( p_sys->p_httpd,
277                                         p_sys->p_httpd_host );
278     httpd_Release( p_sys->p_httpd );
279     free( p_sys );
280 }
281
282 /*****************************************************************************
283  * Run: http interface thread
284  *****************************************************************************/
285 static void Run( intf_thread_t *p_intf )
286 {
287     intf_sys_t     *p_sys = p_intf->p_sys;
288
289     while( !p_intf->b_die )
290     {
291         /* get the playlist */
292         if( p_sys->p_playlist == NULL )
293         {
294             p_sys->p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
295         }
296
297         /* Manage the input part */
298         if( p_sys->p_input == NULL )
299         {
300             if( p_sys->p_playlist )
301             {
302                 p_sys->p_input =
303                     vlc_object_find( p_sys->p_playlist,
304                                      VLC_OBJECT_INPUT,
305                                      FIND_CHILD );
306             }
307         }
308         else if( p_sys->p_input->b_dead )
309         {
310             vlc_object_release( p_sys->p_input );
311             p_sys->p_input = NULL;
312         }
313
314
315         /* Wait a bit */
316         msleep( INTF_IDLE_SLEEP );
317     }
318
319     if( p_sys->p_input )
320     {
321         vlc_object_release( p_sys->p_input );
322         p_sys->p_input = NULL;
323     }
324
325     if( p_sys->p_playlist )
326     {
327         vlc_object_release( p_sys->p_playlist );
328         p_sys->p_playlist = NULL;
329     }
330 }
331
332
333 /*****************************************************************************
334  * Local functions
335  *****************************************************************************/
336 #define MAX_DIR_SIZE 10240
337
338 /****************************************************************************
339  * FileToUrl: create a good name for an url from filename
340  ****************************************************************************/
341 static char *FileToUrl( char *name )
342 {
343     char *url, *p;
344
345     url = p = malloc( strlen( name ) + 1 );
346
347 #ifdef WIN32
348     while( *name == '\\' || *name == '/' )
349 #else
350     while( *name == '/' )
351 #endif
352     {
353         name++;
354     }
355
356     *p++ = '/';
357     strcpy( p, name );
358
359 #ifdef WIN32
360     /* convert '\\' into '/' */
361     name = p;
362     while( *name )
363     {
364         if( *name == '\\' )
365         {
366             *p++ = '/';
367         }
368         name++;
369     }
370 #endif
371
372     /* index.* -> / */
373     if( ( p = strrchr( url, '/' ) ) != NULL )
374     {
375         if( !strncmp( p, "/index.", 7 ) )
376         {
377             p[1] = '\0';
378         }
379     }
380     return url;
381 }
382
383 /****************************************************************************
384  * FileToMime: XXX duplicated with modules/access_out/http.c
385  ****************************************************************************/
386 static struct
387 {
388     char *psz_ext;
389     char *psz_mime;
390 } http_mime[] =
391 {
392     { ".htm",   "text/html" },
393     { ".html",  "text/html" },
394
395     /* media mime */
396     { ".avi",   "video/avi" },
397     { ".asf",   "video/x-ms-asf" },
398     { ".m1a",   "audio/mpeg" },
399     { ".m2a",   "audio/mpeg" },
400     { ".m1v",   "video/mpeg" },
401     { ".m2v",   "video/mpeg" },
402     { ".mp2",   "audio/mpeg" },
403     { ".mp3",   "audio/mpeg" },
404     { ".mpa",   "audio/mpeg" },
405     { ".mpg",   "video/mpeg" },
406     { ".mpeg",  "video/mpeg" },
407     { ".mpe",   "video/mpeg" },
408     { ".mov",   "video/quicktime" },
409     { ".moov",  "video/quicktime" },
410     { ".ogg",   "application/ogg" },
411     { ".ogm",   "application/ogg" },
412     { ".wav",   "audio/wav" },
413
414     /* end */
415     { NULL,     NULL }
416 };
417
418 static char *FileToMime( char *psz_name )
419 {
420     char *psz_ext;
421
422     psz_ext = strrchr( psz_name, '.' );
423     if( psz_ext )
424     {
425         int i;
426
427         for( i = 0; http_mime[i].psz_ext != NULL ; i++ )
428         {
429             if( !strcmp( http_mime[i].psz_ext, psz_ext ) )
430             {
431                 return( http_mime[i].psz_mime );
432             }
433         }
434     }
435     return( "application/octet-stream" );
436 }
437
438 /****************************************************************************
439  * ParseDirectory: parse recursively a directory, adding each file
440  ****************************************************************************/
441 static int ParseDirectory( intf_thread_t *p_intf, char *psz_root,
442                            char *psz_dir )
443 {
444     intf_sys_t     *p_sys = p_intf->p_sys;
445     char           dir[MAX_DIR_SIZE];
446 #ifdef HAVE_SYS_STAT_H
447     struct stat   stat_info;
448 #endif
449     DIR           *p_dir;
450     struct dirent *p_dir_content;
451     FILE          *file;
452
453     char          *user = NULL;
454     char          *password = NULL;
455
456 #ifdef HAVE_SYS_STAT_H
457     if( stat( psz_dir, &stat_info ) == -1 || !S_ISDIR( stat_info.st_mode ) )
458     {
459         return VLC_EGENERIC;
460     }
461 #endif
462
463     if( ( p_dir = opendir( psz_dir ) ) == NULL )
464     {
465         msg_Err( p_intf, "cannot open dir (%s)", psz_dir );
466         return VLC_EGENERIC;
467     }
468
469     msg_Dbg( p_intf, "dir=%s", psz_dir );
470
471     sprintf( dir, "%s/.access", psz_dir );
472     if( ( file = fopen( dir, "r" ) ) != NULL )
473     {
474         char line[1024];
475         int  i_size;
476
477         msg_Dbg( p_intf, "find .access in dir=%s", psz_dir );
478
479         i_size = fread( line, 1, 1023, file );
480         if( i_size > 0 )
481         {
482             char *p;
483             while( i_size > 0 && ( line[i_size-1] == '\n' ||
484                    line[i_size-1] == '\r' ) )
485             {
486                 i_size--;
487             }
488
489             line[i_size] = '\0';
490
491             p = strchr( line, ':' );
492             if( p )
493             {
494                 *p++ = '\0';
495                 user = strdup( line );
496                 password = strdup( p );
497             }
498         }
499         msg_Dbg( p_intf, "using user=%s password=%s (read=%d)",
500                  user, password, i_size );
501
502         fclose( file );
503     }
504
505     for( ;; )
506     {
507         /* parse psz_src dir */
508         if( ( p_dir_content = readdir( p_dir ) ) == NULL )
509         {
510             break;
511         }
512
513         if( p_dir_content->d_name[0] == '.' )
514         {
515             continue;
516         }
517         sprintf( dir, "%s/%s", psz_dir, p_dir_content->d_name );
518         if( ParseDirectory( p_intf, psz_root, dir ) )
519         {
520 #define f p_sys->pp_files[p_sys->i_files]
521             f = malloc( sizeof( httpd_file_callback_args_t ) );
522             f->p_intf  = p_intf;
523             f->file = strdup( dir );
524             f->name = FileToUrl( &dir[strlen( psz_root )] );
525             f->mime = FileToMime( &dir[strlen( psz_root )] );
526
527             msg_Dbg( p_intf, "file=%s (url=%s mime=%s)",
528                      f->file, f->name, f->mime );
529
530             f->p_file =
531                 p_sys->p_httpd->pf_register_file( p_sys->p_httpd,
532                                                   f->name, f->mime,
533                                                   user, password,
534                                                   http_get, http_get,
535                                                   f );
536             if( f->p_file )
537             {
538                 p_sys->i_files++;
539                 p_sys->pp_files = realloc( p_sys->pp_files,
540                   (p_sys->i_files+1) * sizeof( httpd_file_callback_args_t ) );
541             }
542 #define fold p_sys->pp_files[p_sys->i_files-1]
543
544             /* FIXME for rep/ add rep (it would be better to do a redirection) */
545             if( strlen(fold->name) > 1 &&
546                 fold->name[strlen(fold->name) - 1] == '/' )
547             {
548                 f = malloc( sizeof( httpd_file_callback_args_t ) );
549                 f->p_intf  = p_intf;
550                 f->file = strdup( fold->file );
551                 f->name = strdup( fold->name );
552                 f->mime = fold->mime;
553
554                 f->name[strlen(f->name) - 1] = '\0';
555                 msg_Dbg( p_intf, "file=%s (url=%s mime=%s)", f->file, f->name,
556                          f->mime );
557                 f->p_file =
558                     p_sys->p_httpd->pf_register_file( p_sys->p_httpd,
559                                                       f->name, f->mime,
560                                                       user, password,
561                                                       http_get, http_get,
562                                                       f );
563                 if( f->p_file )
564                 {
565                     p_sys->i_files++;
566                     p_sys->pp_files =
567                         realloc( p_sys->pp_files, (p_sys->i_files+1) *
568                                  sizeof( httpd_file_callback_args_t ) );
569                 }
570             }
571 #undef fold
572 #undef f
573         }
574     }
575
576     if( user )
577     {
578         free( user );
579     }
580     if( password )
581     {
582         free( password );
583     }
584     return VLC_SUCCESS;
585 }
586
587 /****************************************************************************
588  * var and set handling
589  ****************************************************************************/
590
591 static mvar_t *mvar_New( char *name, char *value )
592 {
593     mvar_t *v = malloc( sizeof( mvar_t ) );
594
595     v->name = strdup( name );
596     v->value = strdup( value ? value : "" );
597
598     v->i_field = 0;
599     v->field = malloc( sizeof( mvar_t * ) );
600     v->field[0] = NULL;
601
602     return v;
603 }
604
605 static void mvar_Delete( mvar_t *v )
606 {
607     int i;
608
609     free( v->name );
610     free( v->value );
611
612     for( i = 0; i < v->i_field; i++ )
613     {
614         mvar_Delete( v->field[i] );
615     }
616     free( v->field );
617     free( v );
618 }
619
620 static void mvar_AppendVar( mvar_t *v, mvar_t *f )
621 {
622     v->field = realloc( v->field, sizeof( mvar_t * ) * ( v->i_field + 2 ) );
623     v->field[v->i_field] = f;
624     v->i_field++;
625 }
626
627 static mvar_t *mvar_Duplicate( mvar_t *v )
628 {
629     int i;
630     mvar_t *n;
631
632     n = mvar_New( v->name, v->value );
633     for( i = 0; i < v->i_field; i++ )
634     {
635         mvar_AppendVar( n, mvar_Duplicate( v->field[i] ) );
636     }
637
638     return n;
639 }
640
641 static void mvar_PushVar( mvar_t *v, mvar_t *f )
642 {
643     v->field = realloc( v->field, sizeof( mvar_t * ) * ( v->i_field + 2 ) );
644     if( v->i_field > 0 )
645     {
646         memmove( &v->field[1], &v->field[0], sizeof( mvar_t * ) * v->i_field );
647     }
648     v->field[0] = f;
649     v->i_field++;
650 }
651
652 static void mvar_RemoveVar( mvar_t *v, mvar_t *f )
653 {
654     int i;
655     for( i = 0; i < v->i_field; i++ )
656     {
657         if( v->field[i] == f )
658         {
659             break;
660         }
661     }
662     if( i >= v->i_field )
663     {
664         return;
665     }
666
667     if( i + 1 < v->i_field )
668     {
669         memmove( &v->field[i], &v->field[i+1], sizeof( mvar_t * ) * ( v->i_field - i - 1 ) );
670     }
671     v->i_field--;
672     /* FIXME should do a realloc */
673 }
674
675 static mvar_t *mvar_GetVar( mvar_t *s, char *name )
676 {
677     int i;
678     char base[512], *field, *p;
679     int  i_index;
680
681     /* format: name[index].field */
682
683     field = strchr( name, '.' );
684     if( field )
685     {
686         int i = field - name;
687         strncpy( base, name, i );
688         base[i] = '\0';
689         field++;
690     }
691     else
692     {
693         strcpy( base, name );
694     }
695
696     if( ( p = strchr( base, '[' ) ) )
697     {
698         *p++ = '\0';
699         sscanf( p, "%d]", &i_index );
700         if( i_index < 0 )
701         {
702             return NULL;
703         }
704     }
705     else
706     {
707         i_index = 0;
708     }
709
710     for( i = 0; i < s->i_field; i++ )
711     {
712         if( !strcmp( s->field[i]->name, base ) )
713         {
714             if( i_index > 0 )
715             {
716                 i_index--;
717             }
718             else
719             {
720                 if( field )
721                 {
722                     return mvar_GetVar( s->field[i], field );
723                 }
724                 else
725                 {
726                     return s->field[i];
727                 }
728             }
729         }
730     }
731     return NULL;
732 }
733
734
735
736 static char *mvar_GetValue( mvar_t *v, char *field )
737 {
738     if( *field == '\0' )
739     {
740         return v->value;
741     }
742     else
743     {
744         mvar_t *f = mvar_GetVar( v, field );
745         if( f )
746         {
747             return f->value;
748         }
749         else
750         {
751             return field;
752         }
753     }
754 }
755
756 static void mvar_PushNewVar( mvar_t *vars, char *name, char *value )
757 {
758     mvar_t *f = mvar_New( name, value );
759     mvar_PushVar( vars, f );
760 }
761
762 static void mvar_AppendNewVar( mvar_t *vars, char *name, char *value )
763 {
764     mvar_t *f = mvar_New( name, value );
765     mvar_AppendVar( vars, f );
766 }
767
768
769 /* arg= start[:stop[:step]],.. */
770 static mvar_t *mvar_IntegerSetNew( char *name, char *arg )
771 {
772     char *dup = strdup( arg );
773     char *str = dup;
774     mvar_t *s = mvar_New( name, "set" );
775
776     fprintf( stderr," mvar_IntegerSetNew: name=`%s' arg=`%s'\n", name, str );
777
778     while( str )
779     {
780         char *p;
781         int  i_start,i_stop,i_step;
782         int  i_match;
783
784         p = strchr( str, ',' );
785         if( p )
786         {
787             *p++ = '\0';
788         }
789
790         i_step = 0;
791         i_match = sscanf( str, "%d:%d:%d", &i_start, &i_stop, &i_step );
792         fprintf( stderr," mvar_IntegerSetNew: m=%d start=%d stop=%d step=%d\n", i_match, i_start, i_stop, i_step );
793
794         if( i_match == 1 )
795         {
796             i_stop = i_start;
797             i_step = 1;
798         }
799         else if( i_match == 2 )
800         {
801             i_step = i_start < i_stop ? 1 : -1;
802         }
803
804         if( i_match >= 1 )
805         {
806             int i;
807
808             if( ( i_start < i_stop && i_step > 0 ) ||
809                 ( i_start > i_stop && i_step < 0 ) )
810             {
811                 for( i = i_start; ; i += i_step )
812                 {
813                     char   value[512];
814
815                     if( ( i_step > 0 && i > i_stop ) ||
816                         ( i_step < 0 && i < i_stop ) )
817                     {
818                         break;
819                     }
820
821                     fprintf( stderr," mvar_IntegerSetNew: adding %d\n", i );
822                     sprintf( value, "%d", i );
823
824                     mvar_PushNewVar( s, name, value );
825                 }
826             }
827         }
828         str = p;
829     }
830
831     free( dup );
832     return s;
833 }
834
835 static mvar_t *mvar_PlaylistSetNew( char *name, playlist_t *p_pl )
836 {
837     mvar_t *s = mvar_New( name, "set" );
838     int    i;
839
840     fprintf( stderr," mvar_PlaylistSetNew: name=`%s'\n", name );
841
842     vlc_mutex_lock( &p_pl->object_lock );
843     for( i = 0; i < p_pl->i_size; i++ )
844     {
845         mvar_t *itm = mvar_New( name, "set" );
846         char   value[512];
847
848         sprintf( value, "%d", i == p_pl->i_index ? 1 : 0 );
849         mvar_AppendNewVar( itm, "current", value );
850
851         sprintf( value, "%d", i );
852         mvar_AppendNewVar( itm, "index", value );
853
854         mvar_AppendNewVar( itm, "name", p_pl->pp_items[i]->psz_name );
855
856         mvar_AppendVar( s, itm );
857     }
858     vlc_mutex_unlock( &p_pl->object_lock );
859
860     return s;
861 }
862
863 static mvar_t *mvar_InfoSetNew( char *name, input_thread_t *p_input )
864 {
865     mvar_t *s = mvar_New( name, "set" );
866
867     input_info_category_t * p_category;
868     input_info_t * p_info;
869
870     fprintf( stderr," mvar_InfoSetNew: name=`%s'\n", name );
871     if( p_input == NULL )
872     {
873         return s;
874     }
875
876     vlc_mutex_lock( &p_input->stream.stream_lock );
877     p_category = p_input->stream.p_info;
878     while ( p_category )
879     {
880         mvar_t *cat  = mvar_New( name, "set" );
881         mvar_t *iset = mvar_New( "info", "set" );
882
883         mvar_AppendNewVar( cat, "name", p_category->psz_name );
884         mvar_AppendVar( cat, iset );
885
886         p_info = p_category->p_info;
887         while ( p_info )
888         {
889             mvar_t *info = mvar_New( "info", "" );
890
891             msg_Dbg( p_input, "adding info name=%s value=%s", p_info->psz_name, p_info->psz_value );
892             mvar_AppendNewVar( info, "name",  p_info->psz_name );
893             mvar_AppendNewVar( info, "value", p_info->psz_value );
894             mvar_AppendVar( iset, info );
895             p_info = p_info->p_next;
896         }
897         mvar_AppendVar( s, cat );
898         p_category = p_category->p_next;
899     }
900     vlc_mutex_unlock( &p_input->stream.stream_lock );
901
902     return s;
903 }
904
905 static mvar_t *mvar_HttpdInfoSetNew( char *name, httpd_t *p_httpd, int i_type )
906 {
907     mvar_t       *s = mvar_New( name, "set" );
908     httpd_info_t info;
909     int          i;
910
911     fprintf( stderr," mvar_HttpdInfoSetNew: name=`%s'\n", name );
912     if( !p_httpd->pf_control( p_httpd, i_type, &info, NULL ) )
913     {
914         for( i= 0; i < info.i_count; )
915         {
916             mvar_t *inf;
917
918             inf = mvar_New( name, "set" );
919             do
920             {
921                 /* fprintf( stderr," mvar_HttpdInfoSetNew: append name=`%s' value=`%s'\n",
922                             info.info[i].psz_name, info.info[i].psz_value ); */
923                 mvar_AppendNewVar( inf,
924                                    info.info[i].psz_name,
925                                    info.info[i].psz_value );
926                 i++;
927             } while( i < info.i_count && strcmp( info.info[i].psz_name, "id" ) );
928             mvar_AppendVar( s, inf );
929         }
930     }
931
932     /* free mem */
933     for( i = 0; i < info.i_count; i++ )
934     {
935         free( info.info[i].psz_name );
936         free( info.info[i].psz_value );
937     }
938
939     return s;
940 }
941 static mvar_t *mvar_FileSetNew( char *name, char *psz_dir )
942 {
943     mvar_t *s = mvar_New( name, "set" );
944     char           tmp[MAX_DIR_SIZE], *p, *src;
945 #ifdef HAVE_SYS_STAT_H
946     struct stat   stat_info;
947 #endif
948     DIR           *p_dir;
949     struct dirent *p_dir_content;
950     char          sep;
951
952     /* convert all / to native separator */
953 #if defined( WIN32 )
954     while( (p = strchr( psz_dir, '/' )) )
955     {
956         *p = '\\';
957     }
958     sep = '\\';
959 #else
960     sep = '/';
961 #endif
962
963     /* remove trailling separoator */
964     while( strlen( psz_dir ) > 1 && psz_dir[strlen( psz_dir ) -1 ] == sep )
965     {
966         psz_dir[strlen( psz_dir ) -1 ]  ='\0';
967     }
968     /* remove double separator */
969     for( p = src = psz_dir; *src != '\0'; src++, p++ )
970     {
971         if( src[0] == sep && src[1] == sep )
972         {
973             src++;
974         }
975         *p = *src;
976     }
977     *p = '\0';
978
979     if( *psz_dir == '\0' )
980     {
981         return s;
982     }
983     /* first fix all .. dir */
984     p = src = psz_dir;
985     while( *src )
986     {
987         if( src[0] == '.' && src[1] == '.' )
988         {
989             src += 2;
990             if( p <= &psz_dir[1] )
991             {
992                 continue;
993             }
994
995             p -= 2;
996
997             while( p > &psz_dir[1] && *p != sep )
998             {
999                 p--;
1000             }
1001         }
1002         else if( *src == sep )
1003         {
1004             if( p > psz_dir && p[-1] == sep )
1005             {
1006                 src++;
1007             }
1008             else
1009             {
1010                 *p++ = *src++;
1011             }
1012         }
1013         else
1014         {
1015             do
1016             {
1017                 *p++ = *src++;
1018             } while( *src && *src != sep );
1019         }
1020     }
1021     *p = '\0';
1022
1023     fprintf( stderr," mvar_FileSetNew: name=`%s' dir=`%s'\n", name, psz_dir );
1024
1025 #ifdef HAVE_SYS_STAT_H
1026     if( stat( psz_dir, &stat_info ) == -1 || !S_ISDIR( stat_info.st_mode ) )
1027     {
1028         return s;
1029     }
1030 #endif
1031
1032     if( ( p_dir = opendir( psz_dir ) ) == NULL )
1033     {
1034         fprintf( stderr, "cannot open dir (%s)", psz_dir );
1035         return s;
1036     }
1037
1038     /* remove traling / or \ */
1039     for( p = &psz_dir[strlen( psz_dir) - 1]; p >= psz_dir && ( *p =='/' || *p =='\\' ); p-- )
1040     {
1041         *p = '\0';
1042     }
1043
1044     for( ;; )
1045     {
1046         mvar_t *f;
1047
1048         /* parse psz_src dir */
1049         if( ( p_dir_content = readdir( p_dir ) ) == NULL )
1050         {
1051             break;
1052         }
1053         if( !strcmp( p_dir_content->d_name, "." ) )
1054         {
1055             continue;
1056         }
1057
1058 #if defined( WIN32 )
1059         sprintf( tmp, "%s\\%s", psz_dir, p_dir_content->d_name );
1060 #else
1061         sprintf( tmp, "%s/%s", psz_dir, p_dir_content->d_name );
1062 #endif
1063
1064 #ifdef HAVE_SYS_STAT_H
1065         if( stat( tmp, &stat_info ) == -1 )
1066         {
1067             continue;
1068         }
1069 #endif
1070         f = mvar_New( name, "set" );
1071         mvar_AppendNewVar( f, "name", tmp );
1072 #ifdef HAVE_SYS_STAT_H
1073         if( S_ISDIR( stat_info.st_mode ) )
1074         {
1075             mvar_AppendNewVar( f, "type", "directory" );
1076         }
1077         else if( S_ISREG( stat_info.st_mode ) )
1078         {
1079             mvar_AppendNewVar( f, "type", "file" );
1080         }
1081         else
1082         {
1083             mvar_AppendNewVar( f, "type", "unknown" );
1084         }
1085
1086         sprintf( tmp, "%lld", stat_info.st_size );
1087         mvar_AppendNewVar( f, "size", tmp );
1088
1089         /* FIXME memory leak FIXME */
1090 #ifdef HAVE_CTIME_R
1091         ctime_r( &stat_info.st_mtime, tmp );
1092         mvar_AppendNewVar( f, "date", tmp );
1093 #else
1094         mvar_AppendNewVar( f, "date", ctime( &stat_info.st_mtime ) );
1095 #endif
1096
1097 #else
1098         mvar_AppendNewVar( f, "type", "unknown" );
1099         mvar_AppendNewVar( f, "size", "unknown" );
1100         mvar_AppendNewVar( f, "date", "unknown" );
1101 #endif
1102         mvar_AppendVar( s, f );
1103     }
1104
1105     return s;
1106 }
1107
1108 static void SSInit( rpn_stack_t * );
1109 static void SSClean( rpn_stack_t * );
1110 static void EvaluateRPN( mvar_t  *, rpn_stack_t *, char * );
1111
1112 static void SSPush  ( rpn_stack_t *, char * );
1113 static char *SSPop  ( rpn_stack_t * );
1114
1115 static void SSPushN ( rpn_stack_t *, int );
1116 static int  SSPopN  ( rpn_stack_t *, mvar_t  * );
1117
1118
1119 /****************************************************************************
1120  * Macro handling
1121  ****************************************************************************/
1122 typedef struct
1123 {
1124     char *id;
1125     char *param1;
1126     char *param2;
1127 } macro_t;
1128
1129 static int FileLoad( FILE *f, uint8_t **pp_data, int *pi_data )
1130 {
1131     int i_read;
1132
1133     /* just load the file */
1134     *pi_data = 0;
1135     *pp_data = malloc( 1025 );  /* +1 for \0 */
1136     while( ( i_read = fread( &(*pp_data)[*pi_data], 1, 1024, f ) ) == 1024 )
1137     {
1138         *pi_data += 1024;
1139         *pp_data = realloc( *pp_data, *pi_data  + 1025 );
1140     }
1141     if( i_read > 0 )
1142     {
1143         *pi_data += i_read;
1144     }
1145     (*pp_data)[*pi_data] = '\0';
1146
1147     return VLC_SUCCESS;
1148 }
1149
1150 static int MacroParse( macro_t *m, uint8_t *psz_src )
1151 {
1152     uint8_t *dup = strdup( psz_src );
1153     uint8_t *src = dup;
1154     uint8_t *p;
1155     int     i_skip;
1156
1157 #define EXTRACT( name, l ) \
1158         src += l;    \
1159         p = strchr( src, '"' );             \
1160         if( p )                             \
1161         {                                   \
1162             *p++ = '\0';                    \
1163         }                                   \
1164         m->name = strdup( src );            \
1165         if( !p )                            \
1166         {                                   \
1167             break;                          \
1168         }                                   \
1169         src = p;
1170
1171     /* init m */
1172     m->id = NULL;
1173     m->param1 = NULL;
1174     m->param2 = NULL;
1175
1176     /* parse */
1177     src += 4;
1178
1179     while( *src )
1180     {
1181         while( *src == ' ')
1182         {
1183             src++;
1184         }
1185         if( !strncmp( src, "id=\"", 4 ) )
1186         {
1187             EXTRACT( id, 4 );
1188         }
1189         else if( !strncmp( src, "param1=\"", 8 ) )
1190         {
1191             EXTRACT( param1, 8 );
1192         }
1193         else if( !strncmp( src, "param2=\"", 8 ) )
1194         {
1195             EXTRACT( param2, 8 );
1196         }
1197         else
1198         {
1199             break;
1200         }
1201     }
1202     if( strstr( src, "/>" ) )
1203     {
1204         src = strstr( src, "/>" ) + 2;
1205     }
1206     else
1207     {
1208         src += strlen( src );
1209     }
1210
1211     if( m->id == NULL )
1212     {
1213         m->id = strdup( "" );
1214     }
1215     if( m->param1 == NULL )
1216     {
1217         m->param1 = strdup( "" );
1218     }
1219     if( m->param2 == NULL )
1220     {
1221         m->param2 = strdup( "" );
1222     }
1223     i_skip = src - dup;
1224
1225     free( dup );
1226     return i_skip;
1227 #undef EXTRACT
1228 }
1229
1230 static void MacroClean( macro_t *m )
1231 {
1232     free( m->id );
1233     free( m->param1 );
1234     free( m->param2 );
1235 }
1236
1237 enum macroType
1238 {
1239     MVLC_UNKNOWN = 0,
1240     MVLC_CONTROL,
1241         MVLC_PLAY,
1242         MVLC_STOP,
1243         MVLC_PAUSE,
1244         MVLC_NEXT,
1245         MVLC_PREVIOUS,
1246         MVLC_ADD,
1247         MVLC_DEL,
1248         MVLC_EMPTY,
1249
1250         MVLC_CLOSE,
1251         MVLC_SHUTDOWN,
1252     MVLC_FOREACH,
1253     MVLC_IF,
1254     MVLC_RPN,
1255     MVLC_ELSE,
1256     MVLC_END,
1257     MVLC_GET,
1258     MVLC_SET,
1259         MVLC_INT,
1260         MVLC_FLOAT,
1261         MVLC_STRING,
1262
1263     MVLC_VALUE
1264 };
1265
1266 static struct
1267 {
1268     char *psz_name;
1269     int  i_type;
1270 }
1271 StrToMacroTypeTab [] =
1272 {
1273     { "control",    MVLC_CONTROL },
1274         /* player control */
1275         { "play",           MVLC_PLAY },
1276         { "stop",           MVLC_STOP },
1277         { "pause",          MVLC_PAUSE },
1278         { "next",           MVLC_NEXT },
1279         { "previous",       MVLC_PREVIOUS },
1280
1281         /* playlist management */
1282         { "add",            MVLC_ADD },
1283         { "del",            MVLC_DEL },
1284         { "empty",          MVLC_EMPTY },
1285
1286         /* admin control */
1287         { "close",          MVLC_CLOSE },
1288         { "shutdown",       MVLC_SHUTDOWN },
1289
1290     { "rpn",        MVLC_RPN },
1291
1292     { "foreach",    MVLC_FOREACH },
1293     { "value",      MVLC_VALUE },
1294
1295     { "if",         MVLC_IF },
1296     { "else",       MVLC_ELSE },
1297     { "end",        MVLC_END },
1298     { "get",         MVLC_GET },
1299     { "set",         MVLC_SET },
1300         { "int",            MVLC_INT },
1301         { "float",          MVLC_FLOAT },
1302         { "string",         MVLC_STRING },
1303
1304     /* end */
1305     { NULL,         MVLC_UNKNOWN }
1306 };
1307
1308 static int StrToMacroType( char *name )
1309 {
1310     int i;
1311
1312     if( !name || *name == '\0')
1313     {
1314         return MVLC_UNKNOWN;
1315     }
1316     for( i = 0; StrToMacroTypeTab[i].psz_name != NULL; i++ )
1317     {
1318         if( !strcmp( name, StrToMacroTypeTab[i].psz_name ) )
1319         {
1320             return StrToMacroTypeTab[i].i_type;
1321         }
1322     }
1323     return MVLC_UNKNOWN;
1324 }
1325
1326 static void MacroDo( httpd_file_callback_args_t *p_args,
1327                      macro_t *m,
1328                      uint8_t *p_request, int i_request,
1329                      uint8_t **pp_data,  int *pi_data,
1330                      uint8_t **pp_dst )
1331 {
1332     intf_thread_t  *p_intf = p_args->p_intf;
1333     intf_sys_t     *p_sys = p_args->p_intf->p_sys;
1334     char control[512];
1335
1336 #define ALLOC( l ) \
1337     {               \
1338         int __i__ = *pp_dst - *pp_data; \
1339         *pi_data += (l);                  \
1340         *pp_data = realloc( *pp_data, *pi_data );   \
1341         *pp_dst = (*pp_data) + __i__;   \
1342     }
1343 #define PRINT( str ) \
1344     ALLOC( strlen( str ) + 1 ); \
1345     *pp_dst += sprintf( *pp_dst, str );
1346
1347 #define PRINTS( str, s ) \
1348     ALLOC( strlen( str ) + strlen( s ) + 1 ); \
1349     *pp_dst += sprintf( *pp_dst, str, s );
1350
1351     switch( StrToMacroType( m->id ) )
1352     {
1353         case MVLC_CONTROL:
1354             if( i_request <= 0 )
1355             {
1356                 break;
1357             }
1358             uri_extract_value( p_request, "control", control, 512 );
1359             if( *m->param1 && !strstr( m->param1, control ) )
1360             {
1361                 msg_Warn( p_intf, "unauthorized control=%s", control );
1362                 break;
1363             }
1364             switch( StrToMacroType( control ) )
1365             {
1366                 case MVLC_PLAY:
1367                 {
1368                     int i_item;
1369                     char item[512];
1370
1371                     uri_extract_value( p_request, "item", item, 512 );
1372                     i_item = atoi( item );
1373                     playlist_Command( p_sys->p_playlist, PLAYLIST_GOTO, i_item );
1374                     msg_Dbg( p_intf, "requested playlist item: %i", i_item );
1375                     break;
1376                 }
1377                 case MVLC_STOP:
1378                     playlist_Command( p_sys->p_playlist, PLAYLIST_STOP, 0 );
1379                     msg_Dbg( p_intf, "requested playlist stop" );
1380                     break;
1381                 case MVLC_PAUSE:
1382                     playlist_Command( p_sys->p_playlist, PLAYLIST_PAUSE, 0 );
1383                     msg_Dbg( p_intf, "requested playlist pause" );
1384                     break;
1385                 case MVLC_NEXT:
1386                     playlist_Command( p_sys->p_playlist, PLAYLIST_GOTO,
1387                                       p_sys->p_playlist->i_index + 1 );
1388                     msg_Dbg( p_intf, "requested playlist next" );
1389                     break;
1390                 case MVLC_PREVIOUS:
1391                     playlist_Command( p_sys->p_playlist, PLAYLIST_GOTO,
1392                                       p_sys->p_playlist->i_index - 1 );
1393                     msg_Dbg( p_intf, "requested playlist next" );
1394                     break;
1395
1396                 /* playlist management */
1397                 case MVLC_ADD:
1398                 {
1399                     char mrl[512];
1400                     uri_extract_value( p_request, "mrl", mrl, 512 );
1401                     uri_decode_url_encoded( mrl );
1402                     playlist_Add( p_sys->p_playlist, mrl, NULL, 0,
1403                                   PLAYLIST_APPEND, PLAYLIST_END );
1404                     msg_Dbg( p_intf, "requested playlist add: %s", mrl );
1405                     break;
1406                 }
1407                 case MVLC_DEL:
1408                 {
1409                     int i_item;
1410                     char item[512];
1411
1412                     uri_extract_value( p_request, "item", item, 512 );
1413                     i_item = atoi( item );
1414
1415                     playlist_Delete( p_sys->p_playlist, i_item );
1416                     msg_Dbg( p_intf, "requested playlist del: %d", i_item );
1417                     break;
1418                 }
1419                 case MVLC_EMPTY:
1420                 {
1421                     while( p_sys->p_playlist->i_size > 0 )
1422                     {
1423                         playlist_Delete( p_sys->p_playlist, 0 );
1424                     }
1425                     msg_Dbg( p_intf, "requested playlist empty" );
1426                     break;
1427                 }
1428
1429                 /* admin function */
1430                 case MVLC_CLOSE:
1431                 {
1432                     char id[512];
1433                     uri_extract_value( p_request, "id", id, 512 );
1434                     msg_Dbg( p_intf, "requested close id=%s", id );
1435                     if( p_sys->p_httpd->pf_control( p_sys->p_httpd, HTTPD_SET_CLOSE, id, NULL ) )
1436                     {
1437                         msg_Warn( p_intf, "close failed for id=%s", id );
1438                     }
1439                     break;
1440                 }
1441                 case MVLC_SHUTDOWN:
1442                 {
1443                     msg_Dbg( p_intf, "requested shutdown" );
1444                     p_intf->p_vlc->b_die = VLC_TRUE;
1445                     break;
1446                 }
1447                 default:
1448                     PRINTS( "<!-- control param(%s) unsuported -->", control );
1449                     break;
1450             }
1451             break;
1452
1453         case MVLC_SET:
1454         {
1455             char    value[512];
1456             int     i;
1457             float   f;
1458
1459             if( i_request <= 0 ||
1460                 *m->param1  == '\0' ||
1461                 strstr( p_request, m->param1 ) == NULL )
1462             {
1463                 break;
1464             }
1465             uri_extract_value( p_request, m->param1,  value, 512 );
1466             uri_decode_url_encoded( value );
1467
1468             switch( StrToMacroType( m->param2 ) )
1469             {
1470                 case MVLC_INT:
1471                     i = atoi( value );
1472                     config_PutInt( p_intf, m->param1, i );
1473                     break;
1474                 case MVLC_FLOAT:
1475                     f = atof( value );
1476                     config_PutFloat( p_intf, m->param1, f );
1477                     break;
1478                 case MVLC_STRING:
1479                     config_PutPsz( p_intf, m->param1, value );
1480                     break;
1481                 default:
1482                     PRINTS( "<!-- invalid type(%s) in set -->", m->param2 )
1483             }
1484             break;
1485         }
1486         case MVLC_GET:
1487         {
1488             char    value[512];
1489             int     i;
1490             float   f;
1491             char    *psz;
1492
1493             if( *m->param1  == '\0' )
1494             {
1495                 break;
1496             }
1497
1498             switch( StrToMacroType( m->param2 ) )
1499             {
1500                 case MVLC_INT:
1501                     i = config_GetInt( p_intf, m->param1 );
1502                     sprintf( value, "%i", i );
1503                     break;
1504                 case MVLC_FLOAT:
1505                     f = config_GetFloat( p_intf, m->param1 );
1506                     sprintf( value, "%f", f );
1507                     break;
1508                 case MVLC_STRING:
1509                     psz = config_GetPsz( p_intf, m->param1 );
1510                     sprintf( value, "%s", psz ? psz : "" );
1511                     break;
1512                 default:
1513                     sprintf( value, "invalid type(%s) in set", m->param2 );
1514                     break;
1515             }
1516             msg_Dbg( p_intf, "get name=%s value=%s type=%s", m->param1, value, m->param2 );
1517             PRINTS( "%s", value );
1518             break;
1519         }
1520         case MVLC_VALUE:
1521         {
1522             char *s, *v;
1523
1524             if( m->param1 )
1525             {
1526                 EvaluateRPN( p_args->vars, &p_args->stack, m->param1 );
1527                 s = SSPop( &p_args->stack );
1528                 v = mvar_GetValue( p_args->vars, s );
1529             }
1530             else
1531             {
1532                 v = s = SSPop( &p_args->stack );
1533             }
1534
1535             PRINTS( "%s", v );
1536             free( s );
1537             break;
1538         }
1539         case MVLC_RPN:
1540             EvaluateRPN( p_args->vars, &p_args->stack, m->param1 );
1541             break;
1542         case MVLC_UNKNOWN:
1543         default:
1544             PRINTS( "<!-- invalid macro id=`%s' -->", m->id );
1545             msg_Dbg( p_intf, "invalid macro id=`%s'", m->id );
1546             break;
1547     }
1548 #undef PRINTS
1549 #undef PRINT
1550 #undef ALLOC
1551 }
1552
1553 static uint8_t *MacroSearch( uint8_t *src, uint8_t *end, int i_mvlc, vlc_bool_t b_after )
1554 {
1555     int     i_id;
1556     int     i_level = 0;
1557
1558     while( src < end )
1559     {
1560         if( src + 4 < end  && !strncmp( src, "<vlc", 4 ) )
1561         {
1562             int i_skip;
1563             macro_t m;
1564
1565             i_skip = MacroParse( &m, src );
1566
1567             i_id = StrToMacroType( m.id );
1568
1569             switch( i_id )
1570             {
1571                 case MVLC_IF:
1572                 case MVLC_FOREACH:
1573                     i_level++;
1574                     break;
1575                 case MVLC_END:
1576                     i_level--;
1577                     break;
1578                 default:
1579                     break;
1580             }
1581
1582             if( ( i_mvlc == MVLC_END && i_level == -1 ) ||
1583                 ( i_mvlc != MVLC_END && i_level == 0 && i_mvlc == i_id ) )
1584             {
1585                 return src + ( b_after ? i_skip : 0 );
1586             }
1587             else if( i_level < 0 )
1588             {
1589                 return NULL;
1590             }
1591
1592             src += i_skip;
1593         }
1594         else
1595         {
1596             src++;
1597         }
1598     }
1599
1600     return NULL;
1601 }
1602
1603 static void Execute( httpd_file_callback_args_t *p_args,
1604                      uint8_t *p_request, int i_request,
1605                      uint8_t **pp_data, int *pi_data,
1606                      uint8_t **pp_dst,
1607                      uint8_t *_src, uint8_t *_end )
1608 {
1609     intf_thread_t  *p_intf = p_args->p_intf;
1610
1611     uint8_t *src, *dup, *end;
1612     uint8_t *dst = *pp_dst;
1613
1614     src = dup = malloc( _end - _src + 1 );
1615     end = src +( _end - _src );
1616
1617     memcpy( src, _src, _end - _src );
1618     *end = '\0';
1619
1620     /* we parse searching <vlc */
1621     while( src < end )
1622     {
1623         uint8_t *p;
1624         int i_copy;
1625
1626         p = strstr( src, "<vlc" );
1627         if( p < end && p == src )
1628         {
1629             macro_t m;
1630
1631             src += MacroParse( &m, src );
1632
1633             //msg_Dbg( p_intf, "macro_id=%s", m.id );
1634
1635             switch( StrToMacroType( m.id ) )
1636             {
1637                 case MVLC_IF:
1638                 {
1639                     vlc_bool_t i_test;
1640                     uint8_t    *endif;
1641
1642                     EvaluateRPN( p_args->vars, &p_args->stack, m.param1 );
1643                     if( SSPopN( &p_args->stack, p_args->vars ) )
1644                     {
1645                         i_test = 1;
1646                     }
1647                     else
1648                     {
1649                         i_test = 0;
1650                     }
1651                     endif = MacroSearch( src, end, MVLC_END, VLC_TRUE );
1652
1653                     if( i_test == 0 )
1654                     {
1655                         uint8_t *start = MacroSearch( src, endif, MVLC_ELSE, VLC_TRUE );
1656
1657                         if( start )
1658                         {
1659                             uint8_t *stop  = MacroSearch( start, endif, MVLC_END, VLC_FALSE );
1660                             if( stop )
1661                             {
1662                                 Execute( p_args, p_request, i_request, pp_data, pi_data, &dst, start, stop );
1663                             }
1664                         }
1665                     }
1666                     else if( i_test == 1 )
1667                     {
1668                         uint8_t *stop;
1669                         if( ( stop = MacroSearch( src, endif, MVLC_ELSE, VLC_FALSE ) ) == NULL )
1670                         {
1671                             stop = MacroSearch( src, endif, MVLC_END, VLC_FALSE );
1672                         }
1673                         if( stop )
1674                         {
1675                             Execute( p_args, p_request, i_request, pp_data, pi_data, &dst, src, stop );
1676                         }
1677                     }
1678
1679                     src = endif;
1680                     break;
1681                 }
1682                 case MVLC_FOREACH:
1683                 {
1684                     uint8_t *endfor = MacroSearch( src, end, MVLC_END, VLC_TRUE );
1685                     uint8_t *start = src;
1686                     uint8_t *stop = MacroSearch( src, end, MVLC_END, VLC_FALSE );
1687
1688                     if( stop )
1689                     {
1690                         mvar_t *index;
1691                         int    i_idx;
1692                         mvar_t *v;
1693                         if( !strcmp( m.param2, "integer" ) )
1694                         {
1695                             char *arg = SSPop( &p_args->stack );
1696                             index = mvar_IntegerSetNew( m.param1, arg );
1697                             free( arg );
1698                         }
1699                         else if( !strcmp( m.param2, "directory" ) )
1700                         {
1701                             char *arg = SSPop( &p_args->stack );
1702                             index = mvar_FileSetNew( m.param1, arg );
1703                             free( arg );
1704                         }
1705                         else if( !strcmp( m.param2, "playlist" ) )
1706                         {
1707                             index = mvar_PlaylistSetNew( m.param1, p_intf->p_sys->p_playlist );
1708                         }
1709                         else if( !strcmp( m.param2, "informations" ) )
1710                         {
1711                             index = mvar_InfoSetNew( m.param1, p_intf->p_sys->p_input );
1712                         }
1713                         else if( !strcmp( m.param2, "hosts" ) )
1714                         {
1715                             index = mvar_HttpdInfoSetNew( m.param1, p_intf->p_sys->p_httpd, HTTPD_GET_HOSTS );
1716                         }
1717                         else if( !strcmp( m.param2, "urls" ) )
1718                         {
1719                             index = mvar_HttpdInfoSetNew( m.param1, p_intf->p_sys->p_httpd, HTTPD_GET_URLS );
1720                         }
1721                         else if( !strcmp( m.param2, "connections" ) )
1722                         {
1723                             index = mvar_HttpdInfoSetNew(m.param1, p_intf->p_sys->p_httpd, HTTPD_GET_CONNECTIONS);
1724                         }
1725                         else if( ( v = mvar_GetVar( p_args->vars, m.param2 ) ) )
1726                         {
1727                             index = mvar_Duplicate( v );
1728                         }
1729                         else
1730                         {
1731                             msg_Dbg( p_intf, "invalid index constructor (%s)", m.param2 );
1732                             src = endfor;
1733                             break;
1734                         }
1735
1736                         for( i_idx = 0; i_idx < index->i_field; i_idx++ )
1737                         {
1738                             mvar_t *f = mvar_Duplicate( index->field[i_idx] );
1739
1740                             //msg_Dbg( p_intf, "foreach field[%d] name=%s value=%s", i_idx, f->name, f->value );
1741
1742                             free( f->name );
1743                             f->name = strdup( m.param1 );
1744
1745
1746                             mvar_PushVar( p_args->vars, f );
1747                             Execute( p_args, p_request, i_request, pp_data, pi_data, &dst, start, stop );
1748                             mvar_RemoveVar( p_args->vars, f );
1749
1750                             mvar_Delete( f );
1751                         }
1752                         mvar_Delete( index );
1753
1754                         src = endfor;
1755                     }
1756                     break;
1757                 }
1758                 default:
1759                     MacroDo( p_args, &m, p_request, i_request, pp_data, pi_data, &dst );
1760                     break;
1761             }
1762
1763             MacroClean( &m );
1764             continue;
1765         }
1766
1767         i_copy =   ( (p == NULL || p > end ) ? end : p  ) - src;
1768         if( i_copy > 0 )
1769         {
1770             int i_index = dst - *pp_data;
1771
1772             *pi_data += i_copy;
1773             *pp_data = realloc( *pp_data, *pi_data );
1774             dst = (*pp_data) + i_index;
1775
1776             memcpy( dst, src, i_copy );
1777             dst += i_copy;
1778             src += i_copy;
1779         }
1780     }
1781
1782     *pp_dst = dst;
1783     free( dup );
1784 }
1785
1786 /****************************************************************************
1787  * http_get:
1788  ****************************************************************************
1789  * a file with mime == text/html is parsed and all "macro" replaced
1790  * <vlc id="macro name" [param1="" [param2=""]] />
1791  * valid id are
1792  *
1793  ****************************************************************************/
1794 static int  http_get( httpd_file_callback_args_t *p_args,
1795                       uint8_t *p_request, int i_request,
1796                       uint8_t **pp_data, int *pi_data )
1797 {
1798     char *p;
1799     FILE *f;
1800
1801     if( ( f = fopen( p_args->file, "r" ) ) == NULL )
1802     {
1803         p = *pp_data = malloc( 10240 );
1804
1805         p += sprintf( p, "<html>\n" );
1806         p += sprintf( p, "<head>\n" );
1807         p += sprintf( p, "<title>Error loading %s</title>\n", p_args->file );
1808         p += sprintf( p, "</head>\n" );
1809         p += sprintf( p, "<body>\n" );
1810         p += sprintf( p, "<h1><center>Error loading %s for %s</center></h1>\n", p_args->file, p_args->name );
1811         p += sprintf( p, "<hr />\n" );
1812         p += sprintf( p, "<a href=\"http://www.videolan.org\">VideoLAN</a>\n" );
1813         p += sprintf( p, "</body>\n" );
1814         p += sprintf( p, "</html>\n" );
1815
1816         *pi_data = strlen( *pp_data ) + 1;
1817
1818         return VLC_SUCCESS;
1819     }
1820
1821     if( strcmp( p_args->mime, "text/html" ) )
1822     {
1823         FileLoad( f, pp_data, pi_data );
1824     }
1825     else
1826     {
1827         int  i_buffer;
1828         uint8_t *p_buffer;
1829         uint8_t *dst;
1830
1831         p_args->vars = mvar_New( "variables", "" );
1832         mvar_AppendNewVar( p_args->vars, "url_param", i_request > 0 ? "1" : "0" );
1833         mvar_AppendNewVar( p_args->vars, "url_value", p_request );
1834         mvar_AppendNewVar( p_args->vars, "version",   VERSION_MESSAGE );
1835         mvar_AppendNewVar( p_args->vars, "copyright", COPYRIGHT_MESSAGE );
1836
1837         SSInit( &p_args->stack );
1838
1839         /* first we load in a temporary buffer */
1840         FileLoad( f, &p_buffer, &i_buffer );
1841
1842         /* allocate output */
1843         *pi_data = i_buffer + 1000;
1844         dst = *pp_data = malloc( *pi_data );
1845
1846         /* we parse executing all  <vlc /> macros */
1847         Execute( p_args, p_request, i_request, pp_data, pi_data, &dst, &p_buffer[0], &p_buffer[i_buffer] );
1848
1849         *dst++ = '\0';
1850         *pi_data = dst - *pp_data;
1851
1852         SSClean( &p_args->stack );
1853         mvar_Delete( p_args->vars );
1854     }
1855
1856     fclose( f );
1857
1858     return VLC_SUCCESS;
1859 }
1860
1861 /****************************************************************************
1862  * uri parser
1863  ****************************************************************************/
1864 static void uri_extract_value( char *psz_uri, char *psz_name,
1865                                char *psz_value, int i_value_max )
1866 {
1867     char *p;
1868
1869     p = strstr( psz_uri, psz_name );
1870     if( p )
1871     {
1872         int i_len;
1873
1874         p += strlen( psz_name );
1875         if( *p == '=' ) p++;
1876
1877         if( strchr( p, '&' ) )
1878         {
1879             i_len = strchr( p, '&' ) - p;
1880         }
1881         else
1882         {
1883             /* for POST method */
1884             if( strchr( p, '\n' ) )
1885             {
1886                 i_len = strchr( p, '\n' ) - p;
1887                 if( i_len && *(p+i_len-1) == '\r' ) i_len--;
1888             }
1889             else
1890             {
1891                 i_len = strlen( p );
1892             }
1893         }
1894         i_len = __MIN( i_value_max - 1, i_len );
1895         if( i_len > 0 )
1896         {
1897             strncpy( psz_value, p, i_len );
1898             psz_value[i_len] = '\0';
1899         }
1900         else
1901         {
1902             strncpy( psz_value, "", i_value_max );
1903         }
1904     }
1905     else
1906     {
1907         strncpy( psz_value, "", i_value_max );
1908     }
1909 }
1910
1911 static void uri_decode_url_encoded( char *psz )
1912 {
1913     char *dup = strdup( psz );
1914     char *p = dup;
1915
1916     while( *p )
1917     {
1918         if( *p == '%' )
1919         {
1920             char val[3];
1921             p++;
1922             if( !*p )
1923             {
1924                 break;
1925             }
1926
1927             val[0] = *p++;
1928             val[1] = *p++;
1929             val[2] = '\0';
1930
1931             *psz++ = strtol( val, NULL, 16 );
1932         }
1933         else if( *p == '+' )
1934         {
1935             *psz++ = ' ';
1936             p++;
1937         }
1938         else
1939         {
1940             *psz++ = *p++;
1941         }
1942     }
1943     *psz++  ='\0';
1944     free( dup );
1945 }
1946
1947 /****************************************************************************
1948  * Light RPN evaluator
1949  ****************************************************************************/
1950 static void SSInit( rpn_stack_t *st )
1951 {
1952     st->i_stack = 0;
1953 }
1954
1955 static void SSClean( rpn_stack_t *st )
1956 {
1957     while( st->i_stack > 0 )
1958     {
1959         free( st->stack[--st->i_stack] );
1960     }
1961 }
1962
1963 static void SSPush( rpn_stack_t *st, char *s )
1964 {
1965     if( st->i_stack < STACK_MAX )
1966     {
1967         st->stack[st->i_stack++] = strdup( s );
1968     }
1969 }
1970
1971 static char * SSPop( rpn_stack_t *st )
1972 {
1973     if( st->i_stack <= 0 )
1974     {
1975         return strdup( "" );
1976     }
1977     else
1978     {
1979         return st->stack[--st->i_stack];
1980     }
1981 }
1982
1983 static int SSPopN( rpn_stack_t *st, mvar_t  *vars )
1984 {
1985     char *name;
1986     char *value;
1987
1988     char *end;
1989     int  i;
1990
1991     name = SSPop( st );
1992     i = strtol( name, &end, 0 );
1993     if( end == name )
1994     {
1995         value = mvar_GetValue( vars, name );
1996         i = atoi( value );
1997     }
1998     free( name );
1999
2000     return( i );
2001 }
2002
2003 static void SSPushN( rpn_stack_t *st, int i )
2004 {
2005     char v[512];
2006
2007     sprintf( v, "%d", i );
2008     SSPush( st, v );
2009 }
2010
2011 static void  EvaluateRPN( mvar_t  *vars, rpn_stack_t *st, char *exp )
2012 {
2013     for( ;; )
2014     {
2015         char s[100], *p;
2016
2017         /* skip spcae */
2018         while( *exp == ' ' )
2019         {
2020             exp++;
2021         }
2022
2023         if( *exp == '\'' )
2024         {
2025             /* extract string */
2026             p = &s[0];
2027             exp++;
2028             while( *exp && *exp != '\'' )
2029             {
2030                 *p++ = *exp++;
2031             }
2032             *p = '\0';
2033             exp++;
2034             SSPush( st, s );
2035             continue;
2036         }
2037
2038         /* extract token */
2039         p = strchr( exp, ' ' );
2040         if( !p )
2041         {
2042             strcpy( s, exp );
2043
2044             exp += strlen( exp );
2045         }
2046         else
2047         {
2048             int i = p -exp;
2049             strncpy( s, exp, i );
2050             s[i] = '\0';
2051
2052             exp = p + 1;
2053         }
2054
2055         if( *s == '\0' )
2056         {
2057             break;
2058         }
2059
2060         /* 1. Integer function */
2061         if( !strcmp( s, "!" ) )
2062         {
2063             SSPushN( st, ~SSPopN( st, vars ) );
2064         }
2065         else if( !strcmp( s, "^" ) )
2066         {
2067             SSPushN( st, SSPopN( st, vars ) ^ SSPopN( st, vars ) );
2068         }
2069         else if( !strcmp( s, "&" ) )
2070         {
2071             SSPushN( st, SSPopN( st, vars ) & SSPopN( st, vars ) );
2072         }
2073         else if( !strcmp( s, "|" ) )
2074         {
2075             SSPushN( st, SSPopN( st, vars ) | SSPopN( st, vars ) );
2076         }
2077         else if( !strcmp( s, "+" ) )
2078         {
2079             SSPushN( st, SSPopN( st, vars ) + SSPopN( st, vars ) );
2080         }
2081         else if( !strcmp( s, "-" ) )
2082         {
2083             int i = SSPopN( st, vars );
2084             int j = SSPopN( st, vars );
2085             SSPushN( st, i - j );
2086         }
2087         else if( !strcmp( s, "*" ) )
2088         {
2089             SSPushN( st, SSPopN( st, vars ) * SSPopN( st, vars ) );
2090         }
2091         else if( !strcmp( s, "/" ) )
2092         {
2093             int i, j;
2094
2095             i = SSPopN( st, vars );
2096             j = SSPopN( st, vars );
2097
2098             SSPushN( st, j != 0 ? i / j : 0 );
2099         }
2100         else if( !strcmp( s, "%" ) )
2101         {
2102             int i, j;
2103
2104             i = SSPopN( st, vars );
2105             j = SSPopN( st, vars );
2106
2107             SSPushN( st, j != 0 ? i % j : 0 );
2108         }
2109         /* 2. integer tests */
2110         else if( !strcmp( s, "=" ) )
2111         {
2112             SSPushN( st, SSPopN( st, vars ) == SSPopN( st, vars ) ? -1 : 0 );
2113         }
2114         else if( !strcmp( s, "<" ) )
2115         {
2116             int i = SSPopN( st, vars );
2117             int j = SSPopN( st, vars );
2118
2119             SSPushN( st, i < j ? -1 : 0 );
2120         }
2121         else if( !strcmp( s, ">" ) )
2122         {
2123             int i = SSPopN( st, vars );
2124             int j = SSPopN( st, vars );
2125
2126             SSPushN( st, i > j ? -1 : 0 );
2127         }
2128         else if( !strcmp( s, "<=" ) )
2129         {
2130             int i = SSPopN( st, vars );
2131             int j = SSPopN( st, vars );
2132
2133             SSPushN( st, i <= j ? -1 : 0 );
2134         }
2135         else if( !strcmp( s, ">=" ) )
2136         {
2137             int i = SSPopN( st, vars );
2138             int j = SSPopN( st, vars );
2139
2140             SSPushN( st, i >= j ? -1 : 0 );
2141         }
2142         /* 3. string functions */
2143         else if( !strcmp( s, "strcat" ) )
2144         {
2145             char *s1 = SSPop( st );
2146             char *s2 = SSPop( st );
2147             char *str = malloc( strlen( s1 ) + strlen( s2 ) + 1 );
2148
2149             strcpy( str, s1 );
2150             strcat( str, s2 );
2151
2152             SSPush( st, str );
2153             free( s1 );
2154             free( s2 );
2155             free( str );
2156         }
2157         else if( !strcmp( s, "strcmp" ) )
2158         {
2159             char *s1 = SSPop( st );
2160             char *s2 = SSPop( st );
2161
2162             SSPushN( st, strcmp( s1, s2 ) );
2163             free( s1 );
2164             free( s2 );
2165         }
2166         else if( !strcmp( s, "strlen" ) )
2167         {
2168             char *str = SSPop( st );
2169
2170             SSPushN( st, strlen( str ) );
2171             free( str );
2172         }
2173         /* 4. stack functions */
2174         else if( !strcmp( s, "dup" ) )
2175         {
2176             char *str = SSPop( st );
2177             SSPush( st, str );
2178             SSPush( st, str );
2179             free( str );
2180         }
2181         else if( !strcmp( s, "drop" ) )
2182         {
2183             char *str = SSPop( st );
2184             free( str );
2185         }
2186         else if( !strcmp( s, "swap" ) )
2187         {
2188             char *s1 = SSPop( st );
2189             char *s2 = SSPop( st );
2190
2191             SSPush( st, s1 );
2192             SSPush( st, s2 );
2193             free( s1 );
2194             free( s2 );
2195         }
2196         else if( !strcmp( s, "flush" ) )
2197         {
2198             SSClean( st );
2199             SSInit( st );
2200         }
2201         else if( !strcmp( s, "store" ) )
2202         {
2203             char *name  = SSPop( st );
2204             char *value = SSPop( st );
2205
2206             mvar_PushNewVar( vars, name, value );
2207             free( name );
2208             free( value );
2209         }
2210         else if( !strcmp( s, "value" ) )
2211         {
2212             char *name  = SSPop( st );
2213             char *value = mvar_GetValue( vars, name );
2214
2215             SSPush( st, value );
2216
2217             free( name );
2218         }
2219         else if( !strcmp( s, "url_extract" ) )
2220         {
2221             char *url = mvar_GetValue( vars, "url_value" );
2222             char *name = SSPop( st );
2223             char value[512];
2224
2225             uri_extract_value( url, name, value, 512 );
2226             uri_decode_url_encoded( value );
2227             SSPush( st, value );
2228         }
2229         else
2230         {
2231             SSPush( st, s );
2232         }
2233     }
2234 }
2235
2236
2237