1 /*****************************************************************************
2 * http.c : http remote control plugin for vlc
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
5 * $Id: http.c,v 1.9 2003/05/23 23:53:53 sigmunau Exp $
7 * Authors: Gildas Bazin <gbazin@netcourrier.com>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
27 #include <stdlib.h> /* malloc(), free() */
30 #include <errno.h> /* ENOMEM */
42 #ifdef HAVE_SYS_TIME_H
43 # include <sys/time.h>
45 #include <sys/types.h>
49 /*****************************************************************************
51 *****************************************************************************/
52 static int Activate ( vlc_object_t * );
53 static void Close ( vlc_object_t * );
54 static void Run ( intf_thread_t *p_intf );
56 static int httpd_page_interface_get( httpd_file_callback_args_t *p_args,
57 uint8_t *p_request, int i_request,
58 uint8_t **pp_data, int *pi_data );
60 /*****************************************************************************
61 * intf_sys_t: description and status of interface
62 *****************************************************************************/
66 httpd_host_t *p_httpd_host;
68 input_thread_t * p_input;
71 /*****************************************************************************
73 *****************************************************************************/
74 #define PORT_TEXT N_( "HTTP interface bind port" )
75 #define PORT_LONGTEXT N_( \
76 "You can set the port on which the http interface will accept connections" )
77 #define ADDR_TEXT N_( "HTTP interface bind address" )
78 #define ADDR_LONGTEXT N_( \
79 "You can set the address on which the http interface will bind" )
82 add_category_hint( N_("HTTP remote control"), NULL, VLC_TRUE );
83 add_string( "http-addr", NULL, NULL, ADDR_TEXT, ADDR_LONGTEXT, VLC_TRUE );
84 add_integer( "http-port", 8080, NULL, PORT_TEXT, PORT_LONGTEXT, VLC_TRUE );
85 set_description( _("HTTP remote control interface") );
86 set_capability( "interface", 10 );
87 set_callbacks( Activate, Close );
90 /*****************************************************************************
91 * Activate: initialize and create stuff
92 *****************************************************************************/
93 static int Activate( vlc_object_t *p_this )
95 intf_thread_t *p_intf = (intf_thread_t*)p_this;
97 /* Allocate instance and initialize some members */
98 p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
99 if( p_intf->p_sys == NULL )
104 p_intf->p_sys->p_httpd = NULL;
105 p_intf->p_sys->p_httpd_host = NULL;
107 p_intf->pf_run = Run;
114 /*****************************************************************************
115 * CloseIntf: destroy interface
116 *****************************************************************************/
117 void Close ( vlc_object_t *p_this )
119 intf_thread_t *p_intf = (intf_thread_t *)p_this;
121 if( p_intf->p_sys->p_httpd_host )
122 p_intf->p_sys->p_httpd->pf_unregister_host( p_intf->p_sys->p_httpd,
123 p_intf->p_sys->p_httpd_host );
125 if( p_intf->p_sys->p_httpd )
126 httpd_Release( p_intf->p_sys->p_httpd );
128 /* Destroy structure */
129 free( p_intf->p_sys );
132 /*****************************************************************************
133 * Run: http interface thread
134 *****************************************************************************/
135 static void Run( intf_thread_t *p_intf )
137 input_thread_t *p_input = NULL;
138 playlist_t *p_playlist = NULL;
139 httpd_file_t *p_page_intf;
142 input_info_category_t * p_category;
143 input_info_t * p_info;
147 double f_ratio = 1.0;
150 /* Get bind address and port */
151 char *psz_bind_addr = config_GetPsz( p_intf, "http-addr" );
152 int i_bind_port = config_GetInt( p_intf, "http-port" );
153 if( !psz_bind_addr ) psz_bind_addr = strdup( "" );
155 p_intf->p_sys->p_httpd = httpd_Find( VLC_OBJECT(p_intf), VLC_TRUE );
156 if( !p_intf->p_sys->p_httpd )
158 msg_Err( p_intf, "cannot start httpd daemon" );
159 free( p_intf->p_sys );
163 p_intf->p_sys->p_httpd_host =
164 p_intf->p_sys->p_httpd->pf_register_host( p_intf->p_sys->p_httpd,
165 psz_bind_addr, i_bind_port );
167 if( !p_intf->p_sys->p_httpd_host )
169 msg_Err( p_intf, "cannot listen on %s:%d", psz_bind_addr, i_bind_port);
170 httpd_Release( p_intf->p_sys->p_httpd );
171 free( p_intf->p_sys );
175 msg_Info( p_intf, "http interface started on %s:%d",
176 *psz_bind_addr ? psz_bind_addr : "localhost" , i_bind_port );
179 * Register our interface page with the httpd daemon
181 p_page_intf = p_intf->p_sys->p_httpd->pf_register_file(
182 p_intf->p_sys->p_httpd, "/", "text/html",
183 NULL, NULL, httpd_page_interface_get,
184 httpd_page_interface_get,
185 (httpd_file_callback_args_t*)p_intf );
187 while( !p_intf->b_die )
190 /* Manage the input part */
191 if( p_input == NULL )
195 p_input = vlc_object_find( p_playlist, VLC_OBJECT_INPUT,
200 p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
204 p_playlist = vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
209 else if( p_input->b_dead )
211 vlc_object_release( p_input );
218 vlc_mutex_lock( &p_input->stream.stream_lock );
219 if( !p_input->b_die && p_input->stream.i_mux_rate )
222 #define A p_input->stream.p_selected_area
223 f_ratio = 1.0 / ( 50 * p_input->stream.i_mux_rate );
224 i_newpos = A->i_tell * f_ratio;
226 if( i_oldpos != i_newpos )
229 printf( "pos: %li s / %li s\n", (long int)i_newpos,
230 (long int)(f_ratio * A->i_size) );
235 vlc_mutex_unlock( &p_input->stream.stream_lock );
239 msleep( INTF_IDLE_SLEEP );
242 p_intf->p_sys->p_httpd->pf_unregister_file( p_intf->p_sys->p_httpd,
247 vlc_object_release( p_input );
253 vlc_object_release( p_playlist );
258 /*****************************************************************************
260 *****************************************************************************/
261 static int httpd_page_interface_update( intf_thread_t *p_intf,
262 playlist_t *p_playlist,
263 uint8_t **pp_data, int *pi_data, vlc_bool_t b_redirect );
265 static void uri_extract_value( char *psz_uri, char *psz_name,
266 char *psz_value, int i_value_max )
270 p = strstr( psz_uri, psz_name );
275 p += strlen( psz_name );
278 if( strchr( p, '&' ) )
280 i_len = strchr( p, '&' ) - p;
284 /* for POST method */
285 if( strchr( p, '\n' ) )
287 i_len = strchr( p, '\n' ) - p;
288 if( i_len && *(p+i_len-1) == '\r' ) i_len--;
295 i_len = __MIN( i_value_max - 1, i_len );
298 strncpy( psz_value, p, i_len );
299 psz_value[i_len] = '\0';
303 strncpy( psz_value, "", i_value_max );
308 strncpy( psz_value, "", i_value_max );
312 static void uri_decode_url_encoded( char *psz )
314 char *dup = strdup( psz );
332 *psz++ = strtol( val, NULL, 16 );
348 static int httpd_page_interface_get( httpd_file_callback_args_t *p_args,
349 uint8_t *p_request, int i_request,
350 uint8_t **pp_data, int *pi_data )
352 intf_thread_t *p_intf = (intf_thread_t *)p_args;
353 playlist_t *p_playlist;
356 p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
366 uri_extract_value( p_request, "action", action, 512 );
368 if( !strcmp( action, "play" ) )
371 uri_extract_value( p_request, "item", action, 512 );
372 i_item = atol( action );
373 playlist_Command( p_playlist, PLAYLIST_GOTO, i_item );
374 msg_Dbg( p_intf, "requested playlist item: %i", i_item );
376 else if( !strcmp( action, "stop" ) )
378 playlist_Command( p_playlist, PLAYLIST_STOP, 0 );
379 msg_Dbg( p_intf, "requested playlist stop" );
381 else if( !strcmp( action, "pause" ) )
383 playlist_Command( p_playlist, PLAYLIST_PAUSE, 0 );
384 msg_Dbg( p_intf, "requested playlist pause" );
386 else if( !strcmp( action, "next" ) )
388 playlist_Command( p_playlist, PLAYLIST_GOTO,
389 p_playlist->i_index + 1 );
390 msg_Dbg( p_intf, "requested playlist next" );
392 else if( !strcmp( action, "previous" ) )
394 playlist_Command( p_playlist, PLAYLIST_GOTO,
395 p_playlist->i_index - 1 );
396 msg_Dbg( p_intf, "requested playlist previous" );
398 else if( !strcmp( action, "add" ) )
400 uri_extract_value( p_request, "mrl", action, 512 );
401 uri_decode_url_encoded( action );
402 playlist_Add( p_playlist, action,
403 PLAYLIST_APPEND, PLAYLIST_END );
404 msg_Dbg( p_intf, "requested playlist add: %s", action );
408 i_ret = httpd_page_interface_update( p_intf, p_playlist, pp_data, pi_data, i_request ? VLC_TRUE : VLC_FALSE );
410 vlc_object_release( p_playlist );
415 static int httpd_page_interface_update( intf_thread_t *p_intf,
416 playlist_t *p_playlist,
417 uint8_t **pp_data, int *pi_data, vlc_bool_t b_redirect )
422 vlc_mutex_lock( &p_playlist->object_lock );
425 * Count playlist items for memory allocation
427 for ( i = 0; i < p_playlist->i_size; i++ )
429 i_size += sizeof("<a href=?action=play&item=?>? - </a><br />\n" );
430 i_size += strlen( p_playlist->pp_items[i]->psz_name );
432 /* add something for all the static strings below */
435 p = *pp_data = malloc( i_size );
436 p += sprintf( p, "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n" );
437 p += sprintf( p, "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n" );
438 p += sprintf( p, "<head>\n" );
439 p += sprintf( p, "<title>VLC Media Player</title>\n" );
442 p += sprintf( p, "<meta http-equiv=\"refresh\" content=\"0;URL=/\"/>\n" );
444 p += sprintf( p, "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\"/>\n" );
445 /* p += sprintf( p, "<link rel=\"shortcut icon\" href=\"http://www.videolan.org/favicon.ico\">\n" ); */
446 p += sprintf( p, "</head>\n" );
447 p += sprintf( p, "<body>\n" );
448 p += sprintf( p, "<h2 style=\"align: center;\"><a href=\"http://www.videolan.org\">"
449 "VLC Media Player</a> (http interface)</h2>\n" );
452 * Display the controls
454 p += sprintf( p, "<hr/>\n" );
456 p += sprintf( p, "<form method=\"get\" action=\"/\">"
457 "<p><input type=\"submit\" name=\"action\" value=\"stop\" />"
458 "<input type=\"submit\" name=\"action\" value=\"pause\" />"
459 "<input type=\"submit\" name=\"action\" value=\"previous\" />"
460 "<input type=\"submit\" name=\"action\" value=\"next\" /></p>"
463 p += sprintf( p, "<form method=\"get\" action=\"/\" "
464 "enctype=\"text/plain\" >"
465 "<p>Media Resource Locator:<br/>"
466 "<input type=\"text\" name=\"mrl\" size=\"40\" />"
467 "<input type=\"submit\" name=\"action\" value=\"add\" /></p>"
470 p += sprintf( p, "<hr/>\n" );
473 * Display the playlist items
475 if ( p_playlist->i_size )
477 p += sprintf( p, "<ol>\n" );
478 for ( i = 0; i < p_playlist->i_size; i++ )
480 if( i == p_playlist->i_index ) p += sprintf( p, "<b>" );
482 p += sprintf( p, "<li><a href=\"?action=play&item=%i\">", i );
483 p += sprintf( p, "%s", p_playlist->pp_items[i]->psz_name );
484 p += sprintf( p, "</a></li>" );
486 if( i == p_playlist->i_index ) p += sprintf( p, "</b>" );
487 p += sprintf( p, "\n" );
489 p += sprintf( p, "</ol>\n" );
493 p += sprintf( p, "<p>no entries</p>\n" );
496 p += sprintf( p, "</body>\n" );
497 p += sprintf( p, "</html>\n" );
499 *pi_data = strlen( *pp_data );
501 vlc_mutex_unlock( &p_playlist->object_lock );