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