bf8b40a32b1e868a9bed41b03881d1fb18d49209
[vlc.git] / 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         /* FIXME: Workarround a stupid assert in input_GetItem */
604         input_item_t *p_item = p_sys->p_input && p_sys->p_input->p
605                                ? input_GetItem( p_sys->p_input )
606                                : NULL;
607
608         if( p_item )
609         {
610             vlc_mutex_lock( &p_item->p_stats->lock );
611 #define STATS_INT( n ) sprintf( stats, "%d", p_item->p_stats->i_ ## n ); \
612                        E_(mvar_AppendNewVar)( p_args->vars, #n, stats );
613 #define STATS_FLOAT( n ) sprintf( stats, "%f", p_item->p_stats->f_ ## n ); \
614                        E_(mvar_AppendNewVar)( p_args->vars, #n, stats );
615             STATS_INT( read_bytes )
616             STATS_FLOAT( input_bitrate )
617             STATS_INT( demux_read_bytes )
618             STATS_FLOAT( demux_bitrate )
619             STATS_INT( decoded_video )
620             STATS_INT( displayed_pictures )
621             STATS_INT( lost_pictures )
622             STATS_INT( decoded_audio )
623             STATS_INT( played_abuffers )
624             STATS_INT( lost_abuffers )
625             STATS_INT( sent_packets )
626             STATS_INT( sent_bytes )
627             STATS_FLOAT( send_bitrate )
628 #undef STATS_INT
629 #undef STATS_FLOAT
630             vlc_mutex_unlock( &p_item->p_stats->lock );
631         }
632     }
633 #undef p_sys
634
635     E_(SSInit)( &p_args->stack );
636
637     /* allocate output */
638     *pi_data = i_buffer + 1000;
639     dst = *pp_data = malloc( *pi_data );
640
641     /* we parse executing all  <vlc /> macros */
642     E_(Execute)( p_args, p_request, i_request, pp_data, pi_data, &dst,
643                  &p_buffer[0], &p_buffer[i_buffer] );
644
645     *dst     = '\0';
646     *pi_data = dst - *pp_data;
647
648     E_(SSClean)( &p_args->stack );
649     E_(mvar_Delete)( p_args->vars );
650 }
651
652 int  E_(HttpCallback)( httpd_file_sys_t *p_args,
653                        httpd_file_t *p_file,
654                        uint8_t *_p_request,
655                        uint8_t **_pp_data, int *pi_data )
656 {
657     char *p_request = (char *)_p_request;
658     char **pp_data = (char **)_pp_data;
659     FILE *f;
660
661     if( ( f = utf8_fopen( p_args->file, "r" ) ) == NULL )
662     {
663         Callback404( p_args, pp_data, pi_data );
664         return VLC_SUCCESS;
665     }
666
667     if( !p_args->b_html )
668     {
669         E_(FileLoad)( f, pp_data, pi_data );
670     }
671     else
672     {
673         int  i_buffer;
674         char *p_buffer;
675
676         /* first we load in a temporary buffer */
677         E_(FileLoad)( f, &p_buffer, &i_buffer );
678
679         ParseExecute( p_args, p_buffer, i_buffer, p_request, pp_data, pi_data );
680
681         free( p_buffer );
682     }
683
684     fclose( f );
685
686     return VLC_SUCCESS;
687 }
688
689 /****************************************************************************
690  * HandlerCallback:
691  ****************************************************************************
692  * call the external handler and parse vlc macros if Content-Type is HTML
693  ****************************************************************************/
694 int  E_(HandlerCallback)( httpd_handler_sys_t *p_args,
695                           httpd_handler_t *p_handler, char *_p_url,
696                           uint8_t *_p_request, int i_type,
697                           uint8_t *_p_in, int i_in,
698                           char *psz_remote_addr, char *psz_remote_host,
699                           uint8_t **_pp_data, int *pi_data )
700 {
701     char *p_url = (char *)_p_url;
702     char *p_request = (char *)_p_request;
703     char **pp_data = (char **)_pp_data;
704     char *p_in = (char *)p_in;
705     int i_request = p_request != NULL ? strlen( p_request ) : 0;
706     char *p;
707     int i_env = 0;
708     char **ppsz_env = NULL;
709     char *psz_tmp;
710     char sep;
711     int  i_buffer;
712     char *p_buffer;
713     char *psz_cwd, *psz_file = NULL;
714     int i_ret;
715
716 #ifdef WIN32
717     sep = '\\';
718 #else
719     sep = '/';
720 #endif
721
722     /* Create environment for the CGI */
723     TAB_APPEND( i_env, ppsz_env, strdup("GATEWAY_INTERFACE=CGI/1.1") );
724     TAB_APPEND( i_env, ppsz_env, strdup("SERVER_PROTOCOL=HTTP/1.1") );
725     TAB_APPEND( i_env, ppsz_env, strdup("SERVER_SOFTWARE=" COPYRIGHT_MESSAGE) );
726
727     switch( i_type )
728     {
729     case HTTPD_MSG_GET:
730         TAB_APPEND( i_env, ppsz_env, strdup("REQUEST_METHOD=GET") );
731         break;
732     case HTTPD_MSG_POST:
733         TAB_APPEND( i_env, ppsz_env, strdup("REQUEST_METHOD=POST") );
734         break;
735     case HTTPD_MSG_HEAD:
736         TAB_APPEND( i_env, ppsz_env, strdup("REQUEST_METHOD=HEAD") );
737         break;
738     default:
739         break;
740     }
741
742     if( i_request )
743     {
744         psz_tmp = malloc( sizeof("QUERY_STRING=") + i_request );
745         sprintf( psz_tmp, "QUERY_STRING=%s", p_request );
746         TAB_APPEND( i_env, ppsz_env, psz_tmp );
747
748         psz_tmp = malloc( sizeof("REQUEST_URI=?") + strlen(p_url)
749                            + i_request );
750         sprintf( psz_tmp, "REQUEST_URI=%s?%s", p_url, p_request );
751         TAB_APPEND( i_env, ppsz_env, psz_tmp );
752     }
753     else
754     {
755         psz_tmp = malloc( sizeof("REQUEST_URI=") + strlen(p_url) );
756         sprintf( psz_tmp, "REQUEST_URI=%s", p_url );
757         TAB_APPEND( i_env, ppsz_env, psz_tmp );
758     }
759
760     psz_tmp = malloc( sizeof("SCRIPT_NAME=") + strlen(p_url) );
761     sprintf( psz_tmp, "SCRIPT_NAME=%s", p_url );
762     TAB_APPEND( i_env, ppsz_env, psz_tmp );
763
764 #define p_sys p_args->file.p_intf->p_sys
765     psz_tmp = malloc( sizeof("SERVER_NAME=") + strlen(p_sys->psz_address) );
766     sprintf( psz_tmp, "SERVER_NAME=%s", p_sys->psz_address );
767     TAB_APPEND( i_env, ppsz_env, psz_tmp );
768
769     psz_tmp = malloc( sizeof("SERVER_PORT=") + 5 );
770     sprintf( psz_tmp, "SERVER_PORT=%u", p_sys->i_port );
771     TAB_APPEND( i_env, ppsz_env, psz_tmp );
772 #undef p_sys
773
774     p = getenv( "PATH" );
775     if( p != NULL )
776     {
777         psz_tmp = malloc( sizeof("PATH=") + strlen(p) );
778         sprintf( psz_tmp, "PATH=%s", p );
779         TAB_APPEND( i_env, ppsz_env, psz_tmp );
780     }
781
782 #ifdef WIN32
783     p = getenv( "windir" );
784     if( p != NULL )
785     {
786         psz_tmp = malloc( sizeof("SYSTEMROOT=") + strlen(p) );
787         sprintf( psz_tmp, "SYSTEMROOT=%s", p );
788         TAB_APPEND( i_env, ppsz_env, psz_tmp );
789     }
790 #endif
791
792     if( psz_remote_addr != NULL && *psz_remote_addr )
793     {
794         psz_tmp = malloc( sizeof("REMOTE_ADDR=") + strlen(psz_remote_addr) );
795         sprintf( psz_tmp, "REMOTE_ADDR=%s", psz_remote_addr );
796         TAB_APPEND( i_env, ppsz_env, psz_tmp );
797     }
798
799     if( psz_remote_host != NULL && *psz_remote_host )
800     {
801         psz_tmp = malloc( sizeof("REMOTE_HOST=") + strlen(psz_remote_host) );
802         sprintf( psz_tmp, "REMOTE_HOST=%s", psz_remote_host );
803         TAB_APPEND( i_env, ppsz_env, psz_tmp );
804     }
805
806     if( i_in )
807     {
808         p = p_in;
809         for ( ; ; )
810         {
811             if( !strncasecmp( p, "Content-Type: ", strlen("Content-Type: ") ) )
812             {
813                 char *end = strchr( p, '\r' );
814                 if( end == NULL )
815                     break;
816                 *end = '\0';
817                 psz_tmp = malloc( sizeof("CONTENT_TYPE=") + strlen(p) );
818                 sprintf( psz_tmp, "CONTENT_TYPE=%s", p );
819                 TAB_APPEND( i_env, ppsz_env, psz_tmp );
820                 *end = '\r';
821             }
822             if( !strncasecmp( p, "Content-Length: ",
823                               strlen("Content-Length: ") ) )
824             {
825                 char *end = strchr( p, '\r' );
826                 if( end == NULL )
827                     break;
828                 *end = '\0';
829                 psz_tmp = malloc( sizeof("CONTENT_LENGTH=") + strlen(p) );
830                 sprintf( psz_tmp, "CONTENT_LENGTH=%s", p );
831                 TAB_APPEND( i_env, ppsz_env, psz_tmp );
832                 *end = '\r';
833             }
834
835             p = strchr( p, '\n' );
836             if( p == NULL || p[1] == '\r' )
837             {
838                 p = NULL;
839                 break;
840             }
841             p++;
842         }
843     }
844
845     psz_file = strrchr( p_args->file.file, sep );
846     if( psz_file != NULL )
847     {
848         psz_file++;
849         psz_tmp = malloc( sizeof("SCRIPT_FILENAME=") + strlen(psz_file) );
850         sprintf( psz_tmp, "SCRIPT_FILENAME=%s", psz_file );
851         TAB_APPEND( i_env, ppsz_env, psz_tmp );
852
853         TAB_APPEND( p_args->p_association->i_argc,
854                     p_args->p_association->ppsz_argv, psz_file );
855     }
856
857     TAB_APPEND( i_env, ppsz_env, NULL );
858
859     TAB_APPEND( p_args->p_association->i_argc, p_args->p_association->ppsz_argv,
860                 NULL );
861
862     psz_tmp = strdup( p_args->file.file );
863     p = strrchr( psz_tmp, sep );
864     if( p != NULL )
865     {
866         *p = '\0';
867         psz_cwd = psz_tmp;
868     }
869     else
870     {
871         free( psz_tmp );
872         psz_cwd = NULL;
873     }
874
875     i_ret = vlc_execve( p_args->file.p_intf, p_args->p_association->i_argc,
876                         p_args->p_association->ppsz_argv, ppsz_env, psz_cwd,
877                         (char *)p_in, i_in, &p_buffer, &i_buffer );
878     TAB_REMOVE( p_args->p_association->i_argc, p_args->p_association->ppsz_argv,
879                 NULL );
880     TAB_REMOVE( p_args->p_association->i_argc, p_args->p_association->ppsz_argv,
881                 psz_file );
882     if( psz_cwd != NULL )
883         free( psz_cwd );
884     while( i_env )
885         TAB_REMOVE( i_env, ppsz_env, ppsz_env[0] );
886
887     if( i_ret == -1 )
888     {
889         Callback404( (httpd_file_sys_t *)p_args, pp_data, pi_data );
890         return VLC_SUCCESS;
891     }
892     p = p_buffer;
893     while( strncasecmp( p, "Content-Type: text/html",
894                         strlen("Content-Type: text/html") ) )
895     {
896         p = strchr( p, '\n' );
897         if( p == NULL || p[1] == '\r' )
898         {
899             p = NULL;
900             break;
901         }
902         p++;
903     }
904
905     if( p == NULL )
906     {
907         *pp_data = p_buffer;
908         *pi_data = i_buffer;
909     }
910     else
911     {
912         ParseExecute( (httpd_file_sys_t *)p_args, p_buffer, i_buffer,
913                       p_request, pp_data, pi_data );
914
915         free( p_buffer );
916     }
917
918     return VLC_SUCCESS;
919 }
920
921 int  E_(ArtCallback)( httpd_handler_sys_t *p_args,
922                           httpd_handler_t *p_handler, char *_p_url,
923                           uint8_t *p_request, int i_type,
924                           uint8_t *p_in, int i_in,
925                           char *psz_remote_addr, char *psz_remote_host,
926                           uint8_t **pp_data, int *pi_data )
927 {
928     uint8_t *p_data = NULL;
929     int i_data = 0;
930     char *psz_art = NULL;
931     intf_thread_t *p_intf = p_args->file.p_intf;
932     intf_sys_t *p_sys = p_intf->p_sys;
933     int psz_id[16];
934     input_item_t *p_item = NULL;
935     int i_id;
936
937     psz_id[0] = '\0';
938     if( p_request )
939         E_(ExtractURIValue)( p_request, "id", psz_id, 15 );
940     i_id = atoi( psz_id );
941     if( i_id )
942     {
943         playlist_item_t *p_pl_item = playlist_ItemGetById( p_sys->p_playlist,
944                                                            i_id, VLC_FALSE );
945         if( p_pl_item )
946             p_item = p_pl_item->p_input;
947     }
948     else
949     {
950         /* FIXME: Workarround a stupid assert in input_GetItem */
951         if( p_sys->p_input && p_sys->p_input->p )
952             p_item = input_GetItem( p_sys->p_input );
953     }
954
955     if( p_item )
956     {
957         psz_art = input_item_GetArtURL( p_item );
958     }
959
960     if( psz_art && !strncmp( psz_art, "file://", strlen( "file://" ) ) )
961     {
962         FILE *f;
963         char *psz_ext;
964         char *psz_header;
965         int i_header_size;
966
967         if( ( f = utf8_fopen( psz_art + strlen( "file://" ), "r" ) ) == NULL )
968         {
969             msg_Dbg( p_intf, "Couldn't open album art file %s",
970                      psz_art + strlen( "file://" ) );
971             Callback404( &p_args->file, (char**)pp_data, pi_data );
972             free( psz_art );
973             return VLC_SUCCESS;
974         }
975
976         E_(FileLoad)( f, &p_data, &i_data );
977
978         fclose( f );
979
980         psz_ext = strrchr( psz_art, '.' );
981         if( psz_ext ) psz_ext++;
982
983 #define HEADER  "Content-Type: image/%s\n" \
984                 "Content-Length: %d\n" \
985                 "\n"
986         i_header_size = asprintf( &psz_header, HEADER, psz_ext, i_data );
987 #undef HEADER
988
989         *pi_data = i_header_size + i_data;
990         *pp_data = (uint8_t*)malloc( *pi_data );
991         memcpy( *pp_data, psz_header, i_header_size );
992         memcpy( *pp_data+i_header_size, p_data, i_data );
993         free( psz_header );
994         free( p_data );
995     }
996     else
997     {
998         msg_Dbg( p_intf, "No album art found" );
999         Callback404( &p_args->file, (char**)pp_data, pi_data );
1000     }
1001
1002     free( psz_art );
1003
1004     return VLC_SUCCESS;
1005 }