]> git.sesse.net Git - vlc/blob - modules/control/http/http.c
Preliminary album art support code for the HTTP interface.
[vlc] / modules / control / http / http.c
1 /*****************************************************************************
2  * http.c : HTTP/HTTPS Remote control interface
3  *****************************************************************************
4  * Copyright (C) 2001-2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Gildas Bazin <gbazin@netcourrier.com>
8  *          Laurent Aimar <fenrir@via.ecp.fr>
9  *          Christophe Massiot <massiot@via.ecp.fr>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 #include "http.h"
27
28 /*****************************************************************************
29  * Module descriptor
30  *****************************************************************************/
31 static int  Open ( vlc_object_t * );
32 static void Close( vlc_object_t * );
33
34 #define HOST_TEXT N_( "Host address" )
35 #define HOST_LONGTEXT N_( \
36     "Address and port the HTTP interface will listen on. It defaults to " \
37     "all network interfaces (0.0.0.0)." \
38     " If you want the HTTP interface to be available only on the local " \
39     "machine, enter 127.0.0.1" )
40 #define SRC_TEXT N_( "Source directory" )
41 #define SRC_LONGTEXT N_( "Source directory" )
42 #define CHARSET_TEXT N_( "Charset" )
43 #define CHARSET_LONGTEXT N_( \
44         "Charset declared in Content-Type header (default UTF-8)." )
45 #define HANDLERS_TEXT N_( "Handlers" )
46 #define HANDLERS_LONGTEXT N_( \
47         "List of handler extensions and executable paths (for instance: " \
48         "php=/usr/bin/php,pl=/usr/bin/perl)." )
49 #define CERT_TEXT N_( "Certificate file" )
50 #define CERT_LONGTEXT N_( "HTTP interface x509 PEM certificate file " \
51                           "(enables SSL)." )
52 #define KEY_TEXT N_( "Private key file" )
53 #define KEY_LONGTEXT N_( "HTTP interface x509 PEM private key file." )
54 #define CA_TEXT N_( "Root CA file" )
55 #define CA_LONGTEXT N_( "HTTP interface x509 PEM trusted root CA " \
56                         "certificates file." )
57 #define CRL_TEXT N_( "CRL file" )
58 #define CRL_LONGTEXT N_( "HTTP interace Certificates Revocation List file." )
59
60 vlc_module_begin();
61     set_shortname( _("HTTP"));
62     set_description( _("HTTP remote control interface") );
63     set_category( CAT_INTERFACE );
64     set_subcategory( SUBCAT_INTERFACE_MAIN );
65         add_string ( "http-host", NULL, NULL, HOST_TEXT, HOST_LONGTEXT, VLC_TRUE );
66         add_string ( "http-src",  NULL, NULL, SRC_TEXT,  SRC_LONGTEXT,  VLC_TRUE );
67         add_string ( "http-charset", "UTF-8", NULL, CHARSET_TEXT, CHARSET_LONGTEXT, VLC_TRUE );
68 #if defined( HAVE_FORK ) || defined( WIN32 )
69         add_string ( "http-handlers", NULL, NULL, HANDLERS_TEXT, HANDLERS_LONGTEXT, VLC_TRUE );
70 #endif
71         set_section( N_("HTTP SSL" ), 0 );
72         add_string ( "http-intf-cert", NULL, NULL, CERT_TEXT, CERT_LONGTEXT, VLC_TRUE );
73         add_string ( "http-intf-key",  NULL, NULL, KEY_TEXT,  KEY_LONGTEXT,  VLC_TRUE );
74         add_string ( "http-intf-ca",   NULL, NULL, CA_TEXT,   CA_LONGTEXT,   VLC_TRUE );
75         add_string ( "http-intf-crl",  NULL, NULL, CRL_TEXT,  CRL_LONGTEXT,  VLC_TRUE );
76     set_capability( "interface", 0 );
77     set_callbacks( Open, Close );
78 vlc_module_end();
79
80
81 /*****************************************************************************
82  * Local prototypes
83  *****************************************************************************/
84 static void Run          ( intf_thread_t *p_intf );
85 int  E_(ArtCallback)( httpd_handler_sys_t *p_args,
86                           httpd_handler_t *p_handler, char *_p_url,
87                           uint8_t *_p_request, int i_type,
88                           uint8_t *_p_in, int i_in,
89                           char *psz_remote_addr, char *psz_remote_host,
90                           uint8_t **pp_data, int *pi_data );
91
92 /*****************************************************************************
93  * Local functions
94  *****************************************************************************/
95 #if !defined(__APPLE__) && !defined(SYS_BEOS) && !defined(WIN32)
96 static int DirectoryCheck( const char *psz_dir )
97 {
98     DIR           *p_dir;
99
100 #ifdef HAVE_SYS_STAT_H
101     struct stat   stat_info;
102
103     if( ( utf8_stat( psz_dir, &stat_info ) == -1 )
104       || !S_ISDIR( stat_info.st_mode ) )
105     {
106         return VLC_EGENERIC;
107     }
108 #endif
109
110     if( ( p_dir = utf8_opendir( psz_dir ) ) == NULL )
111     {
112         return VLC_EGENERIC;
113     }
114     closedir( p_dir );
115
116     return VLC_SUCCESS;
117 }
118 #endif
119
120
121 /*****************************************************************************
122  * Activate: initialize and create stuff
123  *****************************************************************************/
124 static int Open( vlc_object_t *p_this )
125 {
126     intf_thread_t *p_intf = (intf_thread_t*)p_this;
127     intf_sys_t    *p_sys;
128     char          *psz_address;
129     const char    *psz_cert = NULL, *psz_key = NULL, *psz_ca = NULL,
130                   *psz_crl = NULL;
131     int           i_port       = 0;
132     char          *psz_src;
133
134     var_Create(p_intf->p_libvlc, "http-host", VLC_VAR_STRING );
135     psz_address = var_GetString(p_intf->p_libvlc, "http-host");
136     if( !psz_address || !*psz_address )
137     {
138         psz_address = config_GetPsz( p_intf, "http-host" );
139     }
140     if( psz_address != NULL )
141     {
142         char *psz_parser = strchr( psz_address, ':' );
143         if( psz_parser )
144         {
145             *psz_parser++ = '\0';
146             i_port = atoi( psz_parser );
147         }
148     }
149     else
150         psz_address = strdup("");
151
152     p_intf->p_sys = p_sys = malloc( sizeof( intf_sys_t ) );
153     if( !p_intf->p_sys )
154     {
155         return( VLC_ENOMEM );
156     }
157     p_sys->p_playlist = NULL;
158     p_sys->p_input    = NULL;
159     p_sys->p_vlm      = NULL;
160     p_sys->psz_address = psz_address;
161     p_sys->i_port     = i_port;
162
163     /* determine Content-Type value for HTML pages */
164     psz_src = config_GetPsz( p_intf, "http-charset" );
165     if( psz_src == NULL || !*psz_src )
166     {
167         if( psz_src != NULL ) free( psz_src );
168         psz_src = strdup("UTF-8");
169     }
170
171     p_sys->psz_html_type = malloc( 20 + strlen( psz_src ) );
172     if( p_sys->psz_html_type == NULL )
173     {
174         free( p_sys->psz_address );
175         free( p_sys );
176         free( psz_src );
177         return VLC_ENOMEM ;
178     }
179     sprintf( p_sys->psz_html_type, "text/html; charset=%s", psz_src );
180     msg_Dbg( p_intf, "using charset=%s", psz_src );
181
182     if( strcmp( psz_src, "UTF-8" ) )
183     {
184         char psz_encoding[strlen( psz_src ) + sizeof( "//translit")];
185         sprintf( psz_encoding, "%s//translit", psz_src);
186
187         p_sys->iconv_from_utf8 = vlc_iconv_open( psz_encoding, "UTF-8" );
188         if( p_sys->iconv_from_utf8 == (vlc_iconv_t)-1 )
189             msg_Warn( p_intf, "unable to perform charset conversion to %s",
190                       psz_encoding );
191         else
192         {
193             p_sys->iconv_to_utf8 = vlc_iconv_open( "UTF-8", psz_src );
194             if( p_sys->iconv_to_utf8 == (vlc_iconv_t)-1 )
195                 msg_Warn( p_intf,
196                           "unable to perform charset conversion from %s",
197                           psz_src );
198         }
199     }
200     else
201     {
202         p_sys->iconv_from_utf8 = p_sys->iconv_to_utf8 = (vlc_iconv_t)-1;
203     }
204
205     p_sys->psz_charset = psz_src;
206     psz_src = NULL;
207
208     /* determine file handler associations */
209     p_sys->i_handlers = 0;
210     p_sys->pp_handlers = NULL;
211 #if defined( HAVE_FORK ) || defined( WIN32 )
212     psz_src = config_GetPsz( p_intf, "http-handlers" );
213     if( psz_src != NULL && *psz_src )
214     {
215         char *p = psz_src;
216         while( p != NULL )
217         {
218             http_association_t *p_handler;
219             char *psz_ext = p;
220             char *psz_program, *psz_options;
221             p = strchr( p, '=' );
222             if( p == NULL ) break;
223             *p++ = '\0';
224             psz_program = p;
225             p = strchr( p, ',' );
226             if( p != NULL )
227                 *p++ = '\0';
228
229             p_handler = malloc( sizeof( http_association_t ) );
230             p_handler->psz_ext = strdup( psz_ext );
231             psz_options = E_(FirstWord)( psz_program, psz_program );
232             p_handler->i_argc = 0;
233             p_handler->ppsz_argv = NULL;
234             TAB_APPEND( p_handler->i_argc, p_handler->ppsz_argv,
235                         strdup( psz_program ) );
236             while( psz_options != NULL && *psz_options )
237             {
238                 char *psz_next = E_(FirstWord)( psz_options, psz_options );
239                 TAB_APPEND( p_handler->i_argc, p_handler->ppsz_argv,
240                             strdup( psz_options ) );
241                 psz_options = psz_next;
242             }
243             /* NULL will be appended later on */
244
245             TAB_APPEND( p_sys->i_handlers, p_sys->pp_handlers, p_handler );
246         }
247     }
248     if( psz_src != NULL )
249         free( psz_src );
250 #endif
251
252     /* determine SSL configuration */
253     psz_cert = config_GetPsz( p_intf, "http-intf-cert" );
254     if ( psz_cert != NULL )
255     {
256         msg_Dbg( p_intf, "enabling TLS for HTTP interface (cert file: %s)",
257                  psz_cert );
258         psz_key = config_GetPsz( p_intf, "http-intf-key" );
259         psz_ca = config_GetPsz( p_intf, "http-intf-ca" );
260         psz_crl = config_GetPsz( p_intf, "http-intf-crl" );
261
262         if( i_port <= 0 )
263             i_port = 8443;
264     }
265     else
266     {
267         if( i_port <= 0 )
268             i_port= 8080;
269     }
270
271     msg_Dbg( p_intf, "base %s:%d", psz_address, i_port );
272
273     p_sys->p_httpd_host = httpd_TLSHostNew( VLC_OBJECT(p_intf), psz_address,
274                                             i_port, psz_cert, psz_key, psz_ca,
275                                             psz_crl );
276     if( p_sys->p_httpd_host == NULL )
277     {
278         msg_Err( p_intf, "cannot listen on %s:%d", psz_address, i_port );
279         free( p_sys->psz_html_type );
280         free( p_sys->psz_address );
281         free( p_sys );
282         return VLC_EGENERIC;
283     }
284     else
285     {
286         char psz_tmp[NI_MAXHOST + 6];
287
288         /* Ugly hack to run several HTTP servers on different ports */
289         snprintf( psz_tmp, sizeof (psz_tmp), "%s:%d", psz_address, i_port + 1 );
290         var_SetString( p_intf->p_libvlc, "http-host", psz_tmp );
291     }
292
293     p_sys->i_files  = 0;
294     p_sys->pp_files = NULL;
295
296 #if defined(__APPLE__) || defined(SYS_BEOS) || defined(WIN32)
297     if ( ( psz_src = config_GetPsz( p_intf, "http-src" )) == NULL )
298     {
299         const char * psz_vlcpath = config_GetDataDir();
300         psz_src = malloc( strlen(psz_vlcpath) + strlen("/http" ) + 1 );
301         if( !psz_src ) return VLC_ENOMEM;
302         sprintf( psz_src, "%s/http", psz_vlcpath );
303     }
304 #else
305     psz_src = config_GetPsz( p_intf, "http-src" );
306
307     if( ( psz_src == NULL ) || ( *psz_src == '\0' ) )
308     {
309         static char const* ppsz_paths[] = {
310             "share/http",
311             "../share/http",
312             DATA_PATH"/http",
313             NULL
314         };
315         unsigned i;
316
317         if( psz_src != NULL )
318         {
319             free( psz_src );
320             psz_src = NULL;
321         }
322
323         for( i = 0; ppsz_paths[i] != NULL; i++ )
324             if( !DirectoryCheck( ppsz_paths[i] ) )
325             {
326                 psz_src = strdup( ppsz_paths[i] );
327                 break;
328             }
329     }
330 #endif
331
332     if( !psz_src || *psz_src == '\0' )
333     {
334         msg_Err( p_intf, "invalid web interface source directory" );
335         goto failed;
336     }
337
338     /* remove trainling \ or / */
339     if( psz_src[strlen( psz_src ) - 1] == '\\' ||
340         psz_src[strlen( psz_src ) - 1] == '/' )
341     {
342         psz_src[strlen( psz_src ) - 1] = '\0';
343     }
344
345     E_(ParseDirectory)( p_intf, psz_src, psz_src );
346
347     /* FIXME: we're leaking h */
348     httpd_handler_sys_t *h = malloc( sizeof( httpd_handler_sys_t ) );
349     if( !h )
350     {
351         msg_Err( p_intf, "not enough memory to allocate album art handler" );
352         goto failed;
353     }
354     h->file.p_intf = p_intf;
355     h->file.file = NULL;
356     h->file.name = NULL;
357     /* TODO: use ACL and login/password stuff here too */
358     h->p_handler = httpd_HandlerNew( p_sys->p_httpd_host,
359                                      "/art", NULL, NULL, NULL,
360                                      E_(ArtCallback), h );
361     TAB_APPEND( p_sys->i_handlers, p_sys->pp_handlers, h->p_handler );
362
363     if( p_sys->i_files <= 0 )
364     {
365         msg_Err( p_intf, "cannot find any file in directory %s", psz_src );
366         goto failed;
367     }
368     p_intf->pf_run = Run;
369     free( psz_src );
370
371     return VLC_SUCCESS;
372
373 failed:
374     if( psz_src ) free( psz_src );
375     if( p_sys->pp_files )
376     {
377         free( p_sys->pp_files );
378     }
379     httpd_HostDelete( p_sys->p_httpd_host );
380     free( p_sys->psz_address );
381     free( p_sys->psz_html_type );
382     if( p_sys->iconv_from_utf8 != (vlc_iconv_t)-1 )
383         vlc_iconv_close( p_sys->iconv_from_utf8 );
384     if( p_sys->iconv_to_utf8 != (vlc_iconv_t)-1 )
385         vlc_iconv_close( p_sys->iconv_to_utf8 );
386     free( p_sys );
387     return VLC_EGENERIC;
388 }
389
390 /*****************************************************************************
391  * Close: destroy interface
392  *****************************************************************************/
393 static void Close ( vlc_object_t *p_this )
394 {
395     intf_thread_t *p_intf = (intf_thread_t *)p_this;
396     intf_sys_t    *p_sys = p_intf->p_sys;
397
398     int i;
399
400     if( p_sys->p_vlm )
401     {
402         vlm_Delete( p_sys->p_vlm );
403     }
404     for( i = 0; i < p_sys->i_files; i++ )
405     {
406         if( p_sys->pp_files[i]->b_handler )
407             httpd_HandlerDelete( ((httpd_handler_sys_t *)p_sys->pp_files[i])->p_handler );
408         else
409             httpd_FileDelete( p_sys->pp_files[i]->p_file );
410         if( p_sys->pp_files[i]->p_redir )
411             httpd_RedirectDelete( p_sys->pp_files[i]->p_redir );
412         if( p_sys->pp_files[i]->p_redir2 )
413             httpd_RedirectDelete( p_sys->pp_files[i]->p_redir2 );
414
415         free( p_sys->pp_files[i]->file );
416         free( p_sys->pp_files[i]->name );
417         free( p_sys->pp_files[i] );
418     }
419     if( p_sys->pp_files )
420     {
421         free( p_sys->pp_files );
422     }
423     for( i = 0; i < p_sys->i_handlers; i++ )
424     {
425         http_association_t *p_handler = p_sys->pp_handlers[i];
426         int j;
427         free( p_handler->psz_ext );
428         for( j = 0; j < p_handler->i_argc; j++ )
429             free( p_handler->ppsz_argv[j] );
430         if( p_handler->i_argc )
431             free( p_handler->ppsz_argv );
432         free( p_handler );
433     }
434     if( p_sys->i_handlers )
435         free( p_sys->pp_handlers );
436     httpd_HostDelete( p_sys->p_httpd_host );
437     free( p_sys->psz_address );
438     free( p_sys->psz_html_type );
439
440     if( p_sys->iconv_from_utf8 != (vlc_iconv_t)-1 )
441         vlc_iconv_close( p_sys->iconv_from_utf8 );
442     if( p_sys->iconv_to_utf8 != (vlc_iconv_t)-1 )
443         vlc_iconv_close( p_sys->iconv_to_utf8 );
444     free( p_sys );
445 }
446
447 /*****************************************************************************
448  * Run: http interface thread
449  *****************************************************************************/
450 static void Run( intf_thread_t *p_intf )
451 {
452     intf_sys_t     *p_sys = p_intf->p_sys;
453
454     while( !intf_ShouldDie( p_intf ) )
455     {
456         /* get the playlist */
457         if( p_sys->p_playlist == NULL )
458         {
459             p_sys->p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
460         }
461
462         /* Manage the input part */
463         if( p_sys->p_input == NULL )
464         {
465             if( p_sys->p_playlist )
466             {
467                 p_sys->p_input = p_sys->p_playlist->p_input;
468             }
469         }
470         else if( p_sys->p_input->b_dead || p_sys->p_input->b_die )
471         {
472             p_sys->p_input = NULL;
473         }
474
475
476         /* Wait a bit */
477         msleep( INTF_IDLE_SLEEP );
478     }
479
480     if( p_sys->p_input )
481     {
482         vlc_object_release( p_sys->p_input );
483         p_sys->p_input = NULL;
484     }
485
486     if( p_sys->p_playlist )
487     {
488         vlc_object_release( p_sys->p_playlist );
489         p_sys->p_playlist = NULL;
490     }
491 }
492
493 /****************************************************************************
494  * HttpCallback:
495  ****************************************************************************
496  * a file with b_html is parsed and all "macro" replaced
497  ****************************************************************************/
498 static void Callback404( httpd_file_sys_t *p_args, char **pp_data,
499                          int *pi_data )
500 {
501     char *p = *pp_data = malloc( 10240 );
502     if( !p )
503     {
504         return;
505     }
506     p += sprintf( p, "Content-Type: text/html\n" );
507     p += sprintf( p, "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" );
508     p += sprintf( p, "<head>\n" );
509     p += sprintf( p, "<title>Error loading %s</title>\n", p_args->file );
510     p += sprintf( p, "</head>\n" );
511     p += sprintf( p, "<body>\n" );
512     p += sprintf( p, "<h1><center>Error loading %s for %s</center></h1>\n", p_args->file, p_args->name );
513     p += sprintf( p, "<a href=\"http://www.videolan.org/\">VideoLAN</a>\n" );
514     p += sprintf( p, "</body>\n" );
515     p += sprintf( p, "</html>\n" );
516
517     *pi_data = strlen( *pp_data );
518 }
519
520 static void ParseExecute( httpd_file_sys_t *p_args, char *p_buffer,
521                           int i_buffer, char *p_request,
522                           char **pp_data, int *pi_data )
523 {
524     int i_request = p_request != NULL ? strlen( p_request ) : 0;
525     char *dst;
526     vlc_value_t val;
527     char position[4]; /* percentage */
528     char time[12]; /* in seconds */
529     char length[12]; /* in seconds */
530     audio_volume_t i_volume;
531     char volume[5];
532     char state[8];
533     char stats[20];
534
535 #define p_sys p_args->p_intf->p_sys
536     if( p_sys->p_input )
537     {
538         var_Get( p_sys->p_input, "position", &val);
539         sprintf( position, "%d" , (int)((val.f_float) * 100.0));
540         var_Get( p_sys->p_input, "time", &val);
541         sprintf( time, I64Fi, (int64_t)val.i_time / I64C(1000000) );
542         var_Get( p_sys->p_input, "length", &val);
543         sprintf( length, I64Fi, (int64_t)val.i_time / I64C(1000000) );
544
545         var_Get( p_sys->p_input, "state", &val );
546         if( val.i_int == PLAYING_S )
547         {
548             sprintf( state, "playing" );
549         }
550         else if( val.i_int == OPENING_S )
551         {
552             sprintf( state, "opening/connecting" );
553         }
554         else if( val.i_int == BUFFERING_S )
555         {
556             sprintf( state, "buffering" );
557         }
558         else if( val.i_int == PAUSE_S )
559         {
560             sprintf( state, "paused" );
561         }
562         else
563         {
564             sprintf( state, "stop" );
565         }
566     }
567     else
568     {
569         sprintf( position, "%d", 0 );
570         sprintf( time, "%d", 0 );
571         sprintf( length, "%d", 0 );
572         sprintf( state, "stop" );
573     }
574 #undef p_sys
575
576     aout_VolumeGet( p_args->p_intf, &i_volume );
577     sprintf( volume, "%d", (int)i_volume );
578
579     p_args->vars = E_(mvar_New)( "variables", "" );
580     E_(mvar_AppendNewVar)( p_args->vars, "url_param",
581                            i_request > 0 ? "1" : "0" );
582     E_(mvar_AppendNewVar)( p_args->vars, "url_value", p_request );
583     E_(mvar_AppendNewVar)( p_args->vars, "version", VLC_Version() );
584     E_(mvar_AppendNewVar)( p_args->vars, "copyright", COPYRIGHT_MESSAGE );
585     E_(mvar_AppendNewVar)( p_args->vars, "vlc_compile_by", VLC_CompileBy() );
586     E_(mvar_AppendNewVar)( p_args->vars, "vlc_compile_host",
587                            VLC_CompileHost() );
588     E_(mvar_AppendNewVar)( p_args->vars, "vlc_compile_domain",
589                            VLC_CompileDomain() );
590     E_(mvar_AppendNewVar)( p_args->vars, "vlc_compiler", VLC_Compiler() );
591     E_(mvar_AppendNewVar)( p_args->vars, "vlc_changeset", VLC_Changeset() );
592     E_(mvar_AppendNewVar)( p_args->vars, "stream_position", position );
593     E_(mvar_AppendNewVar)( p_args->vars, "stream_time", time );
594     E_(mvar_AppendNewVar)( p_args->vars, "stream_length", length );
595     E_(mvar_AppendNewVar)( p_args->vars, "volume", volume );
596     E_(mvar_AppendNewVar)( p_args->vars, "stream_state", state );
597     E_(mvar_AppendNewVar)( p_args->vars, "charset", ((intf_sys_t *)p_args->p_intf->p_sys)->psz_charset );
598
599     /* Stats */
600 #define p_sys p_args->p_intf->p_sys
601     if( p_sys->p_input )
602     {
603         input_item_t *p_item = input_GetItem( p_sys->p_input );
604         if( p_item )
605         {
606             vlc_mutex_lock( &p_item->p_stats->lock );
607 #define STATS_INT( n ) sprintf( stats, "%d", p_item->p_stats->i_ ## n ); \
608                        E_(mvar_AppendNewVar)( p_args->vars, #n, stats );
609 #define STATS_FLOAT( n ) sprintf( stats, "%f", p_item->p_stats->f_ ## n ); \
610                        E_(mvar_AppendNewVar)( p_args->vars, #n, stats );
611             STATS_INT( read_bytes )
612             STATS_FLOAT( input_bitrate )
613             STATS_INT( demux_read_bytes )
614             STATS_FLOAT( demux_bitrate )
615             STATS_INT( decoded_video )
616             STATS_INT( displayed_pictures )
617             STATS_INT( lost_pictures )
618             STATS_INT( decoded_audio )
619             STATS_INT( played_abuffers )
620             STATS_INT( lost_abuffers )
621             STATS_INT( sent_packets )
622             STATS_INT( sent_bytes )
623             STATS_FLOAT( send_bitrate )
624 #undef STATS_INT
625 #undef STATS_FLOAT
626             vlc_mutex_unlock( &p_item->p_stats->lock );
627         }
628     }
629 #undef p_sys
630
631     E_(SSInit)( &p_args->stack );
632
633     /* allocate output */
634     *pi_data = i_buffer + 1000;
635     dst = *pp_data = malloc( *pi_data );
636
637     /* we parse executing all  <vlc /> macros */
638     E_(Execute)( p_args, p_request, i_request, pp_data, pi_data, &dst,
639                  &p_buffer[0], &p_buffer[i_buffer] );
640
641     *dst     = '\0';
642     *pi_data = dst - *pp_data;
643
644     E_(SSClean)( &p_args->stack );
645     E_(mvar_Delete)( p_args->vars );
646 }
647
648 int  E_(HttpCallback)( httpd_file_sys_t *p_args,
649                        httpd_file_t *p_file,
650                        uint8_t *_p_request,
651                        uint8_t **_pp_data, int *pi_data )
652 {
653     char *p_request = (char *)_p_request;
654     char **pp_data = (char **)_pp_data;
655     FILE *f;
656
657     if( ( f = utf8_fopen( p_args->file, "r" ) ) == NULL )
658     {
659         Callback404( p_args, pp_data, pi_data );
660         return VLC_SUCCESS;
661     }
662
663     if( !p_args->b_html )
664     {
665         E_(FileLoad)( f, pp_data, pi_data );
666     }
667     else
668     {
669         int  i_buffer;
670         char *p_buffer;
671
672         /* first we load in a temporary buffer */
673         E_(FileLoad)( f, &p_buffer, &i_buffer );
674
675         ParseExecute( p_args, p_buffer, i_buffer, p_request, pp_data, pi_data );
676
677         free( p_buffer );
678     }
679
680     fclose( f );
681
682     return VLC_SUCCESS;
683 }
684
685 /****************************************************************************
686  * HandlerCallback:
687  ****************************************************************************
688  * call the external handler and parse vlc macros if Content-Type is HTML
689  ****************************************************************************/
690 int  E_(HandlerCallback)( httpd_handler_sys_t *p_args,
691                           httpd_handler_t *p_handler, char *_p_url,
692                           uint8_t *_p_request, int i_type,
693                           uint8_t *_p_in, int i_in,
694                           char *psz_remote_addr, char *psz_remote_host,
695                           uint8_t **_pp_data, int *pi_data )
696 {
697     char *p_url = (char *)_p_url;
698     char *p_request = (char *)_p_request;
699     char **pp_data = (char **)_pp_data;
700     char *p_in = (char *)p_in;
701     int i_request = p_request != NULL ? strlen( p_request ) : 0;
702     char *p;
703     int i_env = 0;
704     char **ppsz_env = NULL;
705     char *psz_tmp;
706     char sep;
707     int  i_buffer;
708     char *p_buffer;
709     char *psz_cwd, *psz_file = NULL;
710     int i_ret;
711
712 #ifdef WIN32
713     sep = '\\';
714 #else
715     sep = '/';
716 #endif
717
718     /* Create environment for the CGI */
719     TAB_APPEND( i_env, ppsz_env, strdup("GATEWAY_INTERFACE=CGI/1.1") );
720     TAB_APPEND( i_env, ppsz_env, strdup("SERVER_PROTOCOL=HTTP/1.1") );
721     TAB_APPEND( i_env, ppsz_env, strdup("SERVER_SOFTWARE=" COPYRIGHT_MESSAGE) );
722
723     switch( i_type )
724     {
725     case HTTPD_MSG_GET:
726         TAB_APPEND( i_env, ppsz_env, strdup("REQUEST_METHOD=GET") );
727         break;
728     case HTTPD_MSG_POST:
729         TAB_APPEND( i_env, ppsz_env, strdup("REQUEST_METHOD=POST") );
730         break;
731     case HTTPD_MSG_HEAD:
732         TAB_APPEND( i_env, ppsz_env, strdup("REQUEST_METHOD=HEAD") );
733         break;
734     default:
735         break;
736     }
737
738     if( i_request )
739     {
740         psz_tmp = malloc( sizeof("QUERY_STRING=") + i_request );
741         sprintf( psz_tmp, "QUERY_STRING=%s", p_request );
742         TAB_APPEND( i_env, ppsz_env, psz_tmp );
743
744         psz_tmp = malloc( sizeof("REQUEST_URI=?") + strlen(p_url)
745                            + i_request );
746         sprintf( psz_tmp, "REQUEST_URI=%s?%s", p_url, p_request );
747         TAB_APPEND( i_env, ppsz_env, psz_tmp );
748     }
749     else
750     {
751         psz_tmp = malloc( sizeof("REQUEST_URI=") + strlen(p_url) );
752         sprintf( psz_tmp, "REQUEST_URI=%s", p_url );
753         TAB_APPEND( i_env, ppsz_env, psz_tmp );
754     }
755
756     psz_tmp = malloc( sizeof("SCRIPT_NAME=") + strlen(p_url) );
757     sprintf( psz_tmp, "SCRIPT_NAME=%s", p_url );
758     TAB_APPEND( i_env, ppsz_env, psz_tmp );
759
760 #define p_sys p_args->file.p_intf->p_sys
761     psz_tmp = malloc( sizeof("SERVER_NAME=") + strlen(p_sys->psz_address) );
762     sprintf( psz_tmp, "SERVER_NAME=%s", p_sys->psz_address );
763     TAB_APPEND( i_env, ppsz_env, psz_tmp );
764
765     psz_tmp = malloc( sizeof("SERVER_PORT=") + 5 );
766     sprintf( psz_tmp, "SERVER_PORT=%u", p_sys->i_port );
767     TAB_APPEND( i_env, ppsz_env, psz_tmp );
768 #undef p_sys
769
770     p = getenv( "PATH" );
771     if( p != NULL )
772     {
773         psz_tmp = malloc( sizeof("PATH=") + strlen(p) );
774         sprintf( psz_tmp, "PATH=%s", p );
775         TAB_APPEND( i_env, ppsz_env, psz_tmp );
776     }
777
778 #ifdef WIN32
779     p = getenv( "windir" );
780     if( p != NULL )
781     {
782         psz_tmp = malloc( sizeof("SYSTEMROOT=") + strlen(p) );
783         sprintf( psz_tmp, "SYSTEMROOT=%s", p );
784         TAB_APPEND( i_env, ppsz_env, psz_tmp );
785     }
786 #endif
787
788     if( psz_remote_addr != NULL && *psz_remote_addr )
789     {
790         psz_tmp = malloc( sizeof("REMOTE_ADDR=") + strlen(psz_remote_addr) );
791         sprintf( psz_tmp, "REMOTE_ADDR=%s", psz_remote_addr );
792         TAB_APPEND( i_env, ppsz_env, psz_tmp );
793     }
794
795     if( psz_remote_host != NULL && *psz_remote_host )
796     {
797         psz_tmp = malloc( sizeof("REMOTE_HOST=") + strlen(psz_remote_host) );
798         sprintf( psz_tmp, "REMOTE_HOST=%s", psz_remote_host );
799         TAB_APPEND( i_env, ppsz_env, psz_tmp );
800     }
801
802     if( i_in )
803     {
804         p = p_in;
805         for ( ; ; )
806         {
807             if( !strncasecmp( p, "Content-Type: ", strlen("Content-Type: ") ) )
808             {
809                 char *end = strchr( p, '\r' );
810                 if( end == NULL )
811                     break;
812                 *end = '\0';
813                 psz_tmp = malloc( sizeof("CONTENT_TYPE=") + strlen(p) );
814                 sprintf( psz_tmp, "CONTENT_TYPE=%s", p );
815                 TAB_APPEND( i_env, ppsz_env, psz_tmp );
816                 *end = '\r';
817             }
818             if( !strncasecmp( p, "Content-Length: ",
819                               strlen("Content-Length: ") ) )
820             {
821                 char *end = strchr( p, '\r' );
822                 if( end == NULL )
823                     break;
824                 *end = '\0';
825                 psz_tmp = malloc( sizeof("CONTENT_LENGTH=") + strlen(p) );
826                 sprintf( psz_tmp, "CONTENT_LENGTH=%s", p );
827                 TAB_APPEND( i_env, ppsz_env, psz_tmp );
828                 *end = '\r';
829             }
830
831             p = strchr( p, '\n' );
832             if( p == NULL || p[1] == '\r' )
833             {
834                 p = NULL;
835                 break;
836             }
837             p++;
838         }
839     }
840
841     psz_file = strrchr( p_args->file.file, sep );
842     if( psz_file != NULL )
843     {
844         psz_file++;
845         psz_tmp = malloc( sizeof("SCRIPT_FILENAME=") + strlen(psz_file) );
846         sprintf( psz_tmp, "SCRIPT_FILENAME=%s", psz_file );
847         TAB_APPEND( i_env, ppsz_env, psz_tmp );
848
849         TAB_APPEND( p_args->p_association->i_argc,
850                     p_args->p_association->ppsz_argv, psz_file );
851     }
852
853     TAB_APPEND( i_env, ppsz_env, NULL );
854
855     TAB_APPEND( p_args->p_association->i_argc, p_args->p_association->ppsz_argv,
856                 NULL );
857
858     psz_tmp = strdup( p_args->file.file );
859     p = strrchr( psz_tmp, sep );
860     if( p != NULL )
861     {
862         *p = '\0';
863         psz_cwd = psz_tmp;
864     }
865     else
866     {
867         free( psz_tmp );
868         psz_cwd = NULL;
869     }
870
871     i_ret = vlc_execve( p_args->file.p_intf, p_args->p_association->i_argc,
872                         p_args->p_association->ppsz_argv, ppsz_env, psz_cwd,
873                         (char *)p_in, i_in, &p_buffer, &i_buffer );
874     TAB_REMOVE( p_args->p_association->i_argc, p_args->p_association->ppsz_argv,
875                 NULL );
876     TAB_REMOVE( p_args->p_association->i_argc, p_args->p_association->ppsz_argv,
877                 psz_file );
878     if( psz_cwd != NULL )
879         free( psz_cwd );
880     while( i_env )
881         TAB_REMOVE( i_env, ppsz_env, ppsz_env[0] );
882
883     if( i_ret == -1 )
884     {
885         Callback404( (httpd_file_sys_t *)p_args, pp_data, pi_data );
886         return VLC_SUCCESS;
887     }
888     p = p_buffer;
889     while( strncasecmp( p, "Content-Type: text/html",
890                         strlen("Content-Type: text/html") ) )
891     {
892         p = strchr( p, '\n' );
893         if( p == NULL || p[1] == '\r' )
894         {
895             p = NULL;
896             break;
897         }
898         p++;
899     }
900
901     if( p == NULL )
902     {
903         *pp_data = p_buffer;
904         *pi_data = i_buffer;
905     }
906     else
907     {
908         ParseExecute( (httpd_file_sys_t *)p_args, p_buffer, i_buffer,
909                       p_request, pp_data, pi_data );
910
911         free( p_buffer );
912     }
913
914     return VLC_SUCCESS;
915 }
916
917 int  E_(ArtCallback)( httpd_handler_sys_t *p_args,
918                           httpd_handler_t *p_handler, char *_p_url,
919                           uint8_t *_p_request, int i_type,
920                           uint8_t *_p_in, int i_in,
921                           char *psz_remote_addr, char *psz_remote_host,
922                           uint8_t **pp_data, int *pi_data )
923 {
924     uint8_t *p_data = NULL;
925     int i_data = 0;
926     char *psz_art = NULL;
927     intf_thread_t *p_intf = p_args->file.p_intf;
928     intf_sys_t *p_sys = p_intf->p_sys;
929
930     if( p_sys->p_input )
931     {
932         psz_art = input_item_GetArtURL( input_GetItem( p_sys->p_input ) );
933     }
934
935     if( psz_art && !strncmp( psz_art, "file://", strlen( "file://" ) ) )
936     {
937         FILE *f;
938         char *psz_ext;
939         char *psz_header;
940         int i_header_size;
941
942         if( ( f = utf8_fopen( psz_art + strlen( "file://" ), "r" ) ) == NULL )
943         {
944             msg_Dbg( p_intf, "Couldn't open album art file %s",
945                      psz_art + strlen( "file://" ) );
946             Callback404( &p_args->file, (char**)pp_data, pi_data );
947             free( psz_art );
948             return VLC_SUCCESS;
949         }
950
951         E_(FileLoad)( f, &p_data, &i_data );
952
953         fclose( f );
954
955         psz_ext = strrchr( psz_art, '.' );
956         if( psz_ext ) psz_ext++;
957
958 #define HEADER  "Content-Type: image/%s\n" \
959                 "Content-Length: %d\n" \
960                 "\n"
961         i_header_size = asprintf( &psz_header, HEADER, psz_ext, i_data );
962 #undef HEADER
963
964         *pi_data = i_header_size + i_data;
965         *pp_data = (uint8_t*)malloc( *pi_data );
966         memcpy( *pp_data, psz_header, i_header_size );
967         memcpy( *pp_data+i_header_size, p_data, i_data );
968         free( psz_header );
969         free( p_data );
970     }
971     else
972     {
973         msg_Dbg( p_intf, "No album art found" );
974         Callback404( &p_args->file, (char**)pp_data, pi_data );
975     }
976
977     free( psz_art );
978
979     return VLC_SUCCESS;
980 }