1 /*****************************************************************************
2 * http.c: HTTP access plug-in
3 *****************************************************************************
4 * Copyright (C) 2001, 2002 VideoLAN
5 * $Id: http.c,v 1.10 2002/05/22 23:11:00 massiot Exp $
7 * Authors: Christophe Massiot <massiot@via.ecp.fr>
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 *****************************************************************************/
28 #include <sys/types.h>
34 #include <videolan/vlc.h>
38 #elif defined( _MSC_VER ) && defined( _WIN32 )
43 # include <winsock2.h>
44 # include <ws2tcpip.h>
46 # define IN_MULTICAST(a) IN_CLASSD(a)
49 # include <sys/socket.h>
52 #include "stream_control.h"
53 #include "input_ext-intf.h"
54 #include "input_ext-dec.h"
55 #include "input_ext-plugins.h"
59 /*****************************************************************************
61 *****************************************************************************/
62 static void input_getfunctions( function_list_t * );
63 static int HTTPOpen ( struct input_thread_s * );
64 static void HTTPClose ( struct input_thread_s * );
65 static int HTTPSetProgram ( struct input_thread_s * , pgrm_descriptor_t * );
66 static void HTTPSeek ( struct input_thread_s *, off_t );
68 /*****************************************************************************
69 * Build configuration tree.
70 *****************************************************************************/
75 SET_DESCRIPTION( _("HTTP access plug-in") )
76 ADD_CAPABILITY( ACCESS, 0 )
77 ADD_SHORTCUT( "http" )
78 ADD_SHORTCUT( "http4" )
79 ADD_SHORTCUT( "http6" )
83 input_getfunctions( &p_module->p_functions->access );
86 MODULE_DEACTIVATE_START
87 MODULE_DEACTIVATE_STOP
89 /*****************************************************************************
90 * Functions exported as capabilities. They are declared as static so that
91 * we don't pollute the namespace too much.
92 *****************************************************************************/
93 static void input_getfunctions( function_list_t * p_function_list )
95 #define input p_function_list->functions.access
96 input.pf_open = HTTPOpen;
97 input.pf_read = input_FDNetworkRead;
98 input.pf_close = HTTPClose;
99 input.pf_set_program = HTTPSetProgram;
100 input.pf_set_area = NULL;
101 input.pf_seek = HTTPSeek;
105 /*****************************************************************************
106 * _input_socket_t: private access plug-in data, modified to add private
108 *****************************************************************************/
109 typedef struct _input_socket_s
111 input_socket_t _socket;
114 network_socket_t socket_desc;
115 char psz_buffer[256];
119 /*****************************************************************************
120 * HTTPConnect: connect to the server and seek to i_tell
121 *****************************************************************************/
122 static int HTTPConnect( input_thread_t * p_input, off_t i_tell )
124 _input_socket_t * p_access_data = p_input->p_access_data;
125 struct module_s * p_network;
126 char psz_buffer[256];
129 /* Find an appropriate network module */
130 p_network = module_Need( MODULE_CAPABILITY_NETWORK,
131 p_access_data->psz_network,
132 &p_access_data->socket_desc );
133 if( p_network == NULL )
135 free( p_access_data );
138 module_Unneed( p_network );
140 p_access_data->_socket.i_handle = p_access_data->socket_desc.i_handle;
142 # define HTTP_USERAGENT "User-Agent: " COPYRIGHT_MESSAGE "\r\n"
143 # define HTTP_END "\r\n"
145 snprintf( psz_buffer, sizeof(psz_buffer),
147 "Range: bytes=%lld-\r\n"
148 HTTP_USERAGENT HTTP_END,
149 p_access_data->psz_buffer, i_tell );
150 psz_buffer[sizeof(psz_buffer) - 1] = '\0';
153 if( send( p_access_data->_socket.i_handle, psz_buffer,
154 strlen( psz_buffer ), 0 ) == (-1) )
156 intf_ErrMsg( "http error: cannot send request (%s)", strerror(errno) );
157 input_FDNetworkClose( p_input );
161 /* Prepare the input thread for reading. */
162 p_input->i_bufsize = INPUT_DEFAULT_BUFSIZE;
163 /* FIXME: we shouldn't have to do that ! */
164 p_input->pf_read = input_FDNetworkRead;
166 while( !input_FillBuffer( p_input ) )
168 if( p_input->b_die || p_input->b_error )
170 input_FDNetworkClose( p_input );
175 /* Parse HTTP header. */
176 #define MAX_LINE 1024
179 if( input_Peek( p_input, &psz_parser, MAX_LINE ) <= 0 )
181 intf_ErrMsg( "http error: not enough data" );
182 input_FDNetworkClose( p_input );
186 if( psz_parser[0] == '\r' && psz_parser[1] == '\n' )
189 p_input->p_current_data += 2;
193 if( !strncmp( psz_parser, "Content-Length: ",
194 strlen("Content-Length: ") ) )
196 psz_parser += strlen("Content-Length: ");
197 /* FIXME : this won't work for 64-bit lengths */
198 vlc_mutex_lock( &p_input->stream.stream_lock );
199 p_input->stream.p_selected_area->i_size = atoi( psz_parser )
201 vlc_mutex_unlock( &p_input->stream.stream_lock );
204 while( *psz_parser != '\r' && psz_parser < p_input->p_last_data )
208 p_input->p_current_data = psz_parser + 2;
211 if( p_input->stream.p_selected_area->i_size )
213 vlc_mutex_lock( &p_input->stream.stream_lock );
214 p_input->stream.p_selected_area->i_tell = i_tell
215 + (p_input->p_last_data - p_input->p_current_data);
216 p_input->stream.b_seekable = 1;
217 p_input->stream.b_changed = 1;
218 vlc_mutex_unlock( &p_input->stream.stream_lock );
224 /*****************************************************************************
225 * HTTPOpen: parse URL and open the remote file at the beginning
226 *****************************************************************************/
227 static int HTTPOpen( input_thread_t * p_input )
229 _input_socket_t * p_access_data;
230 char * psz_name = strdup(p_input->psz_name);
231 char * psz_parser = psz_name;
232 char * psz_server_addr = "";
233 char * psz_server_port = "";
234 char * psz_path = "";
236 int i_server_port = 0;
238 p_access_data = p_input->p_access_data = malloc( sizeof(_input_socket_t) );
239 if( p_access_data == NULL )
241 intf_ErrMsg( "http error: Out of memory" );
246 p_access_data->psz_name = psz_name;
247 p_access_data->psz_network = "";
248 if( config_GetIntVariable( "ipv4" ) )
250 p_access_data->psz_network = "ipv4";
252 if( config_GetIntVariable( "ipv6" ) )
254 p_access_data->psz_network = "ipv6";
256 if( *p_input->psz_access )
258 /* Find out which shortcut was used */
259 if( !strncmp( p_input->psz_access, "http6", 6 ) )
261 p_access_data->psz_network = "ipv6";
263 else if( !strncmp( p_input->psz_access, "http4", 6 ) )
265 p_access_data->psz_network = "ipv4";
269 /* Parse psz_name syntax :
270 * //<hostname>[:<port>][/<path>] */
271 while( *psz_parser == '/' )
275 psz_server_addr = psz_parser;
277 while( *psz_parser && *psz_parser != ':' && *psz_parser != '/' )
282 if ( *psz_parser == ':' )
286 psz_server_port = psz_parser;
288 while( *psz_parser && *psz_parser != '/' )
294 if( *psz_parser == '/' )
298 psz_path = psz_parser;
301 /* Convert port format */
302 if( *psz_server_port )
304 i_server_port = strtol( psz_server_port, &psz_parser, 10 );
307 intf_ErrMsg( "input error: cannot parse server port near %s",
309 free( p_input->p_access_data );
315 if( i_server_port == 0 )
320 if( !*psz_server_addr )
322 intf_ErrMsg( "input error: no server given" );
323 free( p_input->p_access_data );
329 if( (psz_proxy = getenv( "http_proxy" )) != NULL && *psz_proxy )
331 /* http://myproxy.mydomain:myport/ */
332 int i_proxy_port = 0;
334 /* Skip the protocol name */
335 while( *psz_proxy && *psz_proxy != ':' )
340 /* Skip the "://" part */
341 while( *psz_proxy && (*psz_proxy == ':' || *psz_proxy == '/') )
346 /* Found a proxy name */
349 char *psz_port = psz_proxy;
351 /* Skip the hostname part */
352 while( *psz_port && *psz_port != ':' && *psz_port != '/' )
357 /* Found a port name */
362 /* Replace ':' with '\0' */
367 while( *psz_junk && *psz_junk != '/' )
377 if( *psz_port != '\0' )
379 i_proxy_port = atoi( psz_port );
385 intf_ErrMsg( "input error: http_proxy environment variable is invalid !" );
386 free( p_input->p_access_data );
391 p_access_data->socket_desc.i_type = NETWORK_TCP;
392 p_access_data->socket_desc.psz_server_addr = psz_proxy;
393 p_access_data->socket_desc.i_server_port = i_proxy_port;
395 snprintf( p_access_data->psz_buffer, sizeof(p_access_data->psz_buffer),
396 "GET http://%s:%d/%s HTTP/1.1\r\n",
397 psz_server_addr, i_server_port, psz_path );
401 /* No proxy, direct connection. */
402 p_access_data->socket_desc.i_type = NETWORK_TCP;
403 p_access_data->socket_desc.psz_server_addr = psz_server_addr;
404 p_access_data->socket_desc.i_server_port = i_server_port;
406 snprintf( p_access_data->psz_buffer, sizeof(p_access_data->psz_buffer),
407 "GET /%s HTTP/1.1\r\nHost: %s\r\n",
408 psz_path, psz_server_addr );
410 p_access_data->psz_buffer[sizeof(p_access_data->psz_buffer) - 1] = '\0';
412 intf_WarnMsg( 2, "input: opening server=%s port=%d path=%s",
413 psz_server_addr, i_server_port, psz_path );
415 vlc_mutex_lock( &p_input->stream.stream_lock );
416 p_input->stream.b_pace_control = 1;
417 p_input->stream.b_seekable = 0;
418 p_input->stream.p_selected_area->i_tell = 0;
419 p_input->stream.p_selected_area->i_size = 0;
420 p_input->stream.i_method = INPUT_METHOD_NETWORK;
421 vlc_mutex_unlock( &p_input->stream.stream_lock );
424 return( HTTPConnect( p_input, 0 ) );
427 /*****************************************************************************
428 * HTTPClose: free unused data structures
429 *****************************************************************************/
430 static void HTTPClose( input_thread_t * p_input )
432 input_FDNetworkClose( p_input );
435 /*****************************************************************************
436 * HTTPSetProgram: do nothing
437 *****************************************************************************/
438 static int HTTPSetProgram( input_thread_t * p_input,
439 pgrm_descriptor_t * p_program )
444 /*****************************************************************************
445 * HTTPSeek: close and re-open a connection at the right place
446 *****************************************************************************/
447 static void HTTPSeek( input_thread_t * p_input, off_t i_pos )
449 _input_socket_t * p_access_data = p_input->p_access_data;
450 close( p_access_data->_socket.i_handle );
451 intf_WarnMsg( 2, "http: seeking to position %lld", i_pos );
452 HTTPConnect( p_input, i_pos );