1 /*****************************************************************************
2 * http.c : HTTP/HTTPS Remote control interface
3 *****************************************************************************
4 * Copyright (C) 2001-2005 the VideoLAN team
7 * Authors: Gildas Bazin <gbazin@netcourrier.com>
8 * Laurent Aimar <fenrir@via.ecp.fr>
9 * Christophe Massiot <massiot@via.ecp.fr>
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.
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.
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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
24 *****************************************************************************/
28 /*****************************************************************************
30 *****************************************************************************/
31 static int Open ( vlc_object_t * );
32 static void Close( vlc_object_t * );
34 #define HOST_TEXT N_( "Host address" )
35 #define HOST_LONGTEXT N_( \
36 "You can set the address and port the http interface will bind to." )
37 #define SRC_TEXT N_( "Source directory" )
38 #define SRC_LONGTEXT N_( "Source directory" )
39 #define CHARSET_TEXT N_( "Charset" )
40 #define CHARSET_LONGTEXT N_( \
41 "Charset declared in Content-Type header (default UTF-8)." )
42 #define CERT_TEXT N_( "Certificate file" )
43 #define CERT_LONGTEXT N_( "HTTP interface x509 PEM certificate file " \
45 #define KEY_TEXT N_( "Private key file" )
46 #define KEY_LONGTEXT N_( "HTTP interface x509 PEM private key file" )
47 #define CA_TEXT N_( "Root CA file" )
48 #define CA_LONGTEXT N_( "HTTP interface x509 PEM trusted root CA " \
50 #define CRL_TEXT N_( "CRL file" )
51 #define CRL_LONGTEXT N_( "HTTP interace Certificates Revocation List file" )
54 set_shortname( _("HTTP"));
55 set_description( _("HTTP remote control interface") );
56 set_category( CAT_INTERFACE );
57 set_subcategory( SUBCAT_INTERFACE_GENERAL );
58 add_string ( "http-host", NULL, NULL, HOST_TEXT, HOST_LONGTEXT, VLC_TRUE );
59 add_string ( "http-src", NULL, NULL, SRC_TEXT, SRC_LONGTEXT, VLC_TRUE );
60 add_string ( "http-charset", "UTF-8", NULL, CHARSET_TEXT, CHARSET_LONGTEXT, VLC_TRUE );
61 set_section( N_("HTTP SSL" ), 0 );
62 add_string ( "http-intf-cert", NULL, NULL, CERT_TEXT, CERT_LONGTEXT, VLC_TRUE );
63 add_string ( "http-intf-key", NULL, NULL, KEY_TEXT, KEY_LONGTEXT, VLC_TRUE );
64 add_string ( "http-intf-ca", NULL, NULL, CA_TEXT, CA_LONGTEXT, VLC_TRUE );
65 add_string ( "http-intf-crl", NULL, NULL, CRL_TEXT, CRL_LONGTEXT, VLC_TRUE );
66 set_capability( "interface", 0 );
67 set_callbacks( Open, Close );
71 /*****************************************************************************
73 *****************************************************************************/
74 static void Run ( intf_thread_t *p_intf );
76 /*****************************************************************************
78 *****************************************************************************/
79 #if !defined(SYS_DARWIN) && !defined(SYS_BEOS) && !defined(WIN32)
80 static int DirectoryCheck( char *psz_dir )
84 #ifdef HAVE_SYS_STAT_H
85 struct stat stat_info;
87 if( stat( psz_dir, &stat_info ) == -1 || !S_ISDIR( stat_info.st_mode ) )
93 if( ( p_dir = opendir( psz_dir ) ) == NULL )
104 /*****************************************************************************
105 * Activate: initialize and create stuff
106 *****************************************************************************/
107 static int Open( vlc_object_t *p_this )
109 intf_thread_t *p_intf = (intf_thread_t*)p_this;
112 char *psz_address = "";
113 const char *psz_cert = NULL, *psz_key = NULL, *psz_ca = NULL,
118 psz_host = config_GetPsz( p_intf, "http-host" );
122 psz_address = psz_host;
124 psz_parser = strchr( psz_host, ':' );
127 *psz_parser++ = '\0';
128 i_port = atoi( psz_parser );
132 p_intf->p_sys = p_sys = malloc( sizeof( intf_sys_t ) );
135 return( VLC_ENOMEM );
137 p_sys->p_playlist = NULL;
138 p_sys->p_input = NULL;
141 /* determine Content-Type value for HTML pages */
142 psz_src = config_GetPsz( p_intf, "http-charset" );
143 if( psz_src == NULL || !*psz_src )
145 if( psz_src != NULL ) free( psz_src );
146 psz_src = strdup("UTF-8");
149 p_sys->psz_html_type = malloc( 20 + strlen( psz_src ) );
150 if( p_sys->psz_html_type == NULL )
156 sprintf( p_sys->psz_html_type, "text/html; charset=%s", psz_src );
157 msg_Dbg( p_intf, "using charset=%s", psz_src );
159 if( strcmp( psz_src, "UTF-8" ) )
161 p_sys->iconv_from_utf8 = vlc_iconv_open( psz_src, "UTF-8" );
162 if( p_sys->iconv_from_utf8 == (vlc_iconv_t)-1 )
163 msg_Warn( p_intf, "unable to perform charset conversion to %s",
167 p_sys->iconv_to_utf8 = vlc_iconv_open( "UTF-8", psz_src );
168 if( p_sys->iconv_to_utf8 == (vlc_iconv_t)-1 )
170 "unable to perform charset conversion from %s",
176 p_sys->iconv_from_utf8 = p_sys->iconv_to_utf8 = (vlc_iconv_t)-1;
181 /* determine SSL configuration */
182 psz_cert = config_GetPsz( p_intf, "http-intf-cert" );
183 if ( psz_cert != NULL )
185 msg_Dbg( p_intf, "enabling TLS for HTTP interface (cert file: %s)",
187 psz_key = config_GetPsz( p_intf, "http-intf-key" );
188 psz_ca = config_GetPsz( p_intf, "http-intf-ca" );
189 psz_crl = config_GetPsz( p_intf, "http-intf-crl" );
200 msg_Dbg( p_intf, "base %s:%d", psz_address, i_port );
202 p_sys->p_httpd_host = httpd_TLSHostNew( VLC_OBJECT(p_intf), psz_address,
203 i_port, psz_cert, psz_key, psz_ca,
205 if( p_sys->p_httpd_host == NULL )
207 msg_Err( p_intf, "cannot listen on %s:%d", psz_address, i_port );
208 free( p_sys->psz_html_type );
219 p_sys->pp_files = NULL;
221 #if defined(SYS_DARWIN) || defined(SYS_BEOS) || defined(WIN32)
222 if ( ( psz_src = config_GetPsz( p_intf, "http-src" )) == NULL )
224 char * psz_vlcpath = p_intf->p_libvlc->psz_vlcpath;
225 psz_src = malloc( strlen(psz_vlcpath) + strlen("/share/http" ) + 1 );
226 if( !psz_src ) return VLC_ENOMEM;
228 sprintf( psz_src, "%s/http", psz_vlcpath);
230 sprintf( psz_src, "%s/share/http", psz_vlcpath);
234 psz_src = config_GetPsz( p_intf, "http-src" );
236 if( !psz_src || *psz_src == '\0' )
238 if( !DirectoryCheck( "share/http" ) )
240 psz_src = strdup( "share/http" );
242 else if( !DirectoryCheck( DATA_PATH "/http" ) )
244 psz_src = strdup( DATA_PATH "/http" );
249 if( !psz_src || *psz_src == '\0' )
251 msg_Err( p_intf, "invalid src dir" );
255 /* remove trainling \ or / */
256 if( psz_src[strlen( psz_src ) - 1] == '\\' ||
257 psz_src[strlen( psz_src ) - 1] == '/' )
259 psz_src[strlen( psz_src ) - 1] = '\0';
262 E_(ParseDirectory)( p_intf, psz_src, psz_src );
265 if( p_sys->i_files <= 0 )
267 msg_Err( p_intf, "cannot find any files (%s)", psz_src );
270 p_intf->pf_run = Run;
276 if( psz_src ) free( psz_src );
277 if( p_sys->pp_files )
279 free( p_sys->pp_files );
281 httpd_HostDelete( p_sys->p_httpd_host );
282 free( p_sys->psz_html_type );
283 if( p_sys->iconv_from_utf8 != (vlc_iconv_t)-1 )
284 vlc_iconv_close( p_sys->iconv_from_utf8 );
285 if( p_sys->iconv_to_utf8 != (vlc_iconv_t)-1 )
286 vlc_iconv_close( p_sys->iconv_to_utf8 );
291 /*****************************************************************************
292 * Close: destroy interface
293 *****************************************************************************/
294 static void Close ( vlc_object_t *p_this )
296 intf_thread_t *p_intf = (intf_thread_t *)p_this;
297 intf_sys_t *p_sys = p_intf->p_sys;
303 vlm_Delete( p_sys->p_vlm );
305 for( i = 0; i < p_sys->i_files; i++ )
307 httpd_FileDelete( p_sys->pp_files[i]->p_file );
308 if( p_sys->pp_files[i]->p_redir )
309 httpd_RedirectDelete( p_sys->pp_files[i]->p_redir );
310 if( p_sys->pp_files[i]->p_redir2 )
311 httpd_RedirectDelete( p_sys->pp_files[i]->p_redir2 );
313 free( p_sys->pp_files[i]->file );
314 free( p_sys->pp_files[i]->name );
315 free( p_sys->pp_files[i] );
317 if( p_sys->pp_files )
319 free( p_sys->pp_files );
321 httpd_HostDelete( p_sys->p_httpd_host );
322 free( p_sys->psz_html_type );
324 if( p_sys->iconv_from_utf8 != (vlc_iconv_t)-1 )
325 vlc_iconv_close( p_sys->iconv_from_utf8 );
326 if( p_sys->iconv_to_utf8 != (vlc_iconv_t)-1 )
327 vlc_iconv_close( p_sys->iconv_to_utf8 );
331 /*****************************************************************************
332 * Run: http interface thread
333 *****************************************************************************/
334 static void Run( intf_thread_t *p_intf )
336 intf_sys_t *p_sys = p_intf->p_sys;
338 while( !p_intf->b_die )
340 /* get the playlist */
341 if( p_sys->p_playlist == NULL )
343 p_sys->p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
346 /* Manage the input part */
347 if( p_sys->p_input == NULL )
349 if( p_sys->p_playlist )
352 vlc_object_find( p_sys->p_playlist,
357 else if( p_sys->p_input->b_dead )
359 vlc_object_release( p_sys->p_input );
360 p_sys->p_input = NULL;
365 msleep( INTF_IDLE_SLEEP );
370 vlc_object_release( p_sys->p_input );
371 p_sys->p_input = NULL;
374 if( p_sys->p_playlist )
376 vlc_object_release( p_sys->p_playlist );
377 p_sys->p_playlist = NULL;
381 /****************************************************************************
383 ****************************************************************************
384 * a file with b_html is parsed and all "macro" replaced
385 * <vlc id="macro name" [param1="" [param2=""]] />
388 ****************************************************************************/
389 int E_(HttpCallback)( httpd_file_sys_t *p_args,
390 httpd_file_t *p_file,
392 uint8_t **_pp_data, int *pi_data )
394 char *p_request = (char *)_p_request;
395 char **pp_data = (char **)_pp_data;
396 int i_request = p_request ? strlen( p_request ) : 0;
400 if( ( f = fopen( p_args->file, "r" ) ) == NULL )
402 p = *pp_data = malloc( 10240 );
407 p += sprintf( p, "<html>\n" );
408 p += sprintf( p, "<head>\n" );
409 p += sprintf( p, "<title>Error loading %s</title>\n", p_args->file );
410 p += sprintf( p, "</head>\n" );
411 p += sprintf( p, "<body>\n" );
412 p += sprintf( p, "<h1><center>Error loading %s for %s</center></h1>\n", p_args->file, p_args->name );
413 p += sprintf( p, "<hr />\n" );
414 p += sprintf( p, "<a href=\"http://www.videolan.org/\">VideoLAN</a>\n" );
415 p += sprintf( p, "</body>\n" );
416 p += sprintf( p, "</html>\n" );
418 *pi_data = strlen( *pp_data );
423 if( !p_args->b_html )
425 E_(FileLoad)( f, pp_data, pi_data );
433 char position[4]; /* percentage */
434 char time[12]; /* in seconds */
435 char length[12]; /* in seconds */
436 audio_volume_t i_volume;
440 #define p_sys p_args->p_intf->p_sys
443 var_Get( p_sys->p_input, "position", &val);
444 sprintf( position, "%d" , (int)((val.f_float) * 100.0));
445 var_Get( p_sys->p_input, "time", &val);
446 sprintf( time, "%d" , (int)(val.i_time / 1000000) );
447 var_Get( p_sys->p_input, "length", &val);
448 sprintf( length, "%d" , (int)(val.i_time / 1000000) );
450 var_Get( p_sys->p_input, "state", &val );
451 if( val.i_int == PLAYING_S )
453 sprintf( state, "playing" );
455 else if( val.i_int == PAUSE_S )
457 sprintf( state, "paused" );
461 sprintf( state, "stop" );
466 sprintf( position, "%d", 0 );
467 sprintf( time, "%d", 0 );
468 sprintf( length, "%d", 0 );
469 sprintf( state, "stop" );
473 aout_VolumeGet( p_args->p_intf , &i_volume );
474 sprintf( volume , "%d" , (int)i_volume );
476 p_args->vars = E_(mvar_New)( "variables", "" );
477 E_(mvar_AppendNewVar)( p_args->vars, "url_param",
478 i_request > 0 ? "1" : "0" );
479 E_(mvar_AppendNewVar)( p_args->vars, "url_value", p_request );
480 E_(mvar_AppendNewVar)( p_args->vars, "version", VLC_Version() );
481 E_(mvar_AppendNewVar)( p_args->vars, "copyright", COPYRIGHT_MESSAGE );
482 E_(mvar_AppendNewVar)( p_args->vars, "vlc_compile_by", VLC_CompileBy() );
483 E_(mvar_AppendNewVar)( p_args->vars, "vlc_compile_host",
485 E_(mvar_AppendNewVar)( p_args->vars, "vlc_compile_domain",
486 VLC_CompileDomain() );
487 E_(mvar_AppendNewVar)( p_args->vars, "vlc_compiler", VLC_Compiler() );
488 E_(mvar_AppendNewVar)( p_args->vars, "vlc_changeset", VLC_Changeset() );
489 E_(mvar_AppendNewVar)( p_args->vars, "stream_position", position );
490 E_(mvar_AppendNewVar)( p_args->vars, "stream_time", time );
491 E_(mvar_AppendNewVar)( p_args->vars, "stream_length", length );
492 E_(mvar_AppendNewVar)( p_args->vars, "volume", volume );
493 E_(mvar_AppendNewVar)( p_args->vars, "stream_state", state );
495 E_(SSInit)( &p_args->stack );
497 /* first we load in a temporary buffer */
498 E_(FileLoad)( f, &p_buffer, &i_buffer );
500 /* allocate output */
501 *pi_data = i_buffer + 1000;
502 dst = *pp_data = malloc( *pi_data );
504 /* we parse executing all <vlc /> macros */
505 E_(Execute)( p_args, p_request, i_request, pp_data, pi_data, &dst,
506 &p_buffer[0], &p_buffer[i_buffer] );
509 *pi_data = dst - *pp_data;
511 E_(SSClean)( &p_args->stack );
512 E_(mvar_Delete)( p_args->vars );