]> git.sesse.net Git - vlc/blob - plugins/access/http.c
* ALL: internationalized all configuration strings.
[vlc] / plugins / access / http.c
1 /*****************************************************************************
2  * http.c: HTTP access plug-in
3  *****************************************************************************
4  * Copyright (C) 2001, 2002 VideoLAN
5  * $Id: http.c,v 1.8 2002/04/19 13:56:10 sam Exp $
6  *
7  * Authors: Christophe Massiot <massiot@via.ecp.fr>
8  *
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.
13  * 
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.
18  *
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  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <fcntl.h>
33
34 #include <videolan/vlc.h>
35
36 #ifdef HAVE_UNISTD_H
37 #   include <unistd.h>
38 #elif defined( _MSC_VER ) && defined( _WIN32 )
39 #   include <io.h>
40 #endif
41
42 #include "stream_control.h"
43 #include "input_ext-intf.h"
44 #include "input_ext-dec.h"
45 #include "input_ext-plugins.h"
46
47 #include "network.h"
48
49 /*****************************************************************************
50  * Local prototypes
51  *****************************************************************************/
52 static void input_getfunctions( function_list_t * );
53 static int  HTTPOpen       ( struct input_thread_s * );
54 static void HTTPClose      ( struct input_thread_s * );
55 static int  HTTPSetProgram ( struct input_thread_s * , pgrm_descriptor_t * );  
56 static void HTTPSeek       ( struct input_thread_s *, off_t );
57
58 /*****************************************************************************
59  * Build configuration tree.
60  *****************************************************************************/
61 MODULE_CONFIG_START
62 MODULE_CONFIG_STOP
63  
64 MODULE_INIT_START
65     SET_DESCRIPTION( _("HTTP access plug-in") )
66     ADD_CAPABILITY( ACCESS, 0 )
67     ADD_SHORTCUT( "http" )
68     ADD_SHORTCUT( "http4" )
69     ADD_SHORTCUT( "http6" )
70 MODULE_INIT_STOP
71  
72 MODULE_ACTIVATE_START
73     input_getfunctions( &p_module->p_functions->access );
74 MODULE_ACTIVATE_STOP
75  
76 MODULE_DEACTIVATE_START
77 MODULE_DEACTIVATE_STOP
78
79 /*****************************************************************************
80  * Functions exported as capabilities. They are declared as static so that
81  * we don't pollute the namespace too much.
82  *****************************************************************************/
83 static void input_getfunctions( function_list_t * p_function_list )
84 {
85 #define input p_function_list->functions.access
86     input.pf_open             = HTTPOpen;
87     input.pf_read             = input_FDNetworkRead;
88     input.pf_close            = HTTPClose;
89     input.pf_set_program      = HTTPSetProgram;
90     input.pf_set_area         = NULL;
91     input.pf_seek             = HTTPSeek;
92 #undef input
93 }
94
95 /*****************************************************************************
96  * _input_socket_t: private access plug-in data, modified to add private
97  *                  fields
98  *****************************************************************************/
99 typedef struct _input_socket_s
100 {
101     input_socket_t      _socket;
102
103     char *              psz_network;
104     network_socket_t    socket_desc;
105     char                psz_buffer[256];
106     char *              psz_name;
107 } _input_socket_t;
108
109 /*****************************************************************************
110  * HTTPConnect: connect to the server and seek to i_tell
111  *****************************************************************************/
112 static int HTTPConnect( input_thread_t * p_input, off_t i_tell )
113 {
114     _input_socket_t *   p_access_data = p_input->p_access_data;
115     struct module_s *   p_network;
116     char                psz_buffer[256];
117     byte_t *            psz_parser;
118
119     /* Find an appropriate network module */
120     p_network = module_Need( MODULE_CAPABILITY_NETWORK,
121                              p_access_data->psz_network,
122                              &p_access_data->socket_desc );
123     if( p_network == NULL )
124     {
125         free( p_access_data );
126         return( -1 );
127     }
128     module_Unneed( p_network );
129
130     p_access_data->_socket.i_handle = p_access_data->socket_desc.i_handle;
131
132 #   define HTTP_USERAGENT "User-Agent: " COPYRIGHT_MESSAGE "\r\n"
133 #   define HTTP_END       "\r\n"
134  
135     snprintf( psz_buffer, sizeof(psz_buffer),
136               "%s"
137               "Range: bytes=%lld-\r\n"
138               HTTP_USERAGENT HTTP_END,
139               p_access_data->psz_buffer, i_tell );
140     psz_buffer[sizeof(psz_buffer) - 1] = '\0';
141
142     /* Send GET ... */
143     if( write( p_access_data->_socket.i_handle, psz_buffer,
144                strlen( psz_buffer ) ) == (-1) )
145     {
146         intf_ErrMsg( "http error: cannot send request (%s)", strerror(errno) );
147         input_FDClose( p_input );
148         return( -1 );
149     }
150
151     /* Prepare the input thread for reading. */ 
152     p_input->i_bufsize = INPUT_DEFAULT_BUFSIZE;
153     /* FIXME: we shouldn't have to do that ! */
154     p_input->pf_read = input_FDNetworkRead;
155
156     while( !input_FillBuffer( p_input ) )
157     {
158         if( p_input->b_die || p_input->b_error )
159         {
160             input_FDClose( p_input );
161             return( -1 );
162         }
163     }
164
165     /* Parse HTTP header. */
166 #define MAX_LINE 1024
167     for( ; ; ) 
168     {
169         if( input_Peek( p_input, &psz_parser, MAX_LINE ) <= 0 )
170         {
171             intf_ErrMsg( "http error: not enough data" );
172             input_FDClose( p_input );
173             return( -1 );
174         }
175
176         if( psz_parser[0] == '\r' && psz_parser[1] == '\n' )
177         {
178             /* End of header. */
179             p_input->p_current_data += 2;
180             break;
181         }
182
183         if( !strncmp( psz_parser, "Content-Length: ",
184                       strlen("Content-Length: ") ) )
185         {
186             psz_parser += strlen("Content-Length: ");
187             /* FIXME : this won't work for 64-bit lengths */
188             vlc_mutex_lock( &p_input->stream.stream_lock );
189             p_input->stream.p_selected_area->i_size = atoi( psz_parser )
190                                                         + i_tell;
191             vlc_mutex_unlock( &p_input->stream.stream_lock );
192         }
193
194         while( *psz_parser != '\r' && psz_parser < p_input->p_last_data )
195         {
196             psz_parser++;
197         }
198         p_input->p_current_data = psz_parser + 2;
199     }
200
201     if( p_input->stream.p_selected_area->i_size )
202     {
203         vlc_mutex_lock( &p_input->stream.stream_lock );
204         p_input->stream.p_selected_area->i_tell = i_tell
205             + (p_input->p_last_data - p_input->p_current_data);
206         p_input->stream.b_seekable = 1;
207         p_input->stream.b_changed = 1;
208         vlc_mutex_unlock( &p_input->stream.stream_lock );
209     }
210
211     return( 0 );
212 }
213
214 /*****************************************************************************
215  * HTTPOpen: parse URL and open the remote file at the beginning
216  *****************************************************************************/
217 static int HTTPOpen( input_thread_t * p_input )
218 {
219     _input_socket_t *   p_access_data;
220     char *              psz_name = strdup(p_input->psz_name);
221     char *              psz_parser = psz_name;
222     char *              psz_server_addr = "";
223     char *              psz_server_port = "";
224     char *              psz_path = "";
225     char *              psz_proxy;
226     int                 i_server_port = 0;
227
228     p_access_data = p_input->p_access_data = malloc( sizeof(_input_socket_t) );
229     if( p_access_data == NULL )
230     {
231         intf_ErrMsg( "http error: Out of memory" );
232         free(psz_name);
233         return( -1 );
234     }
235
236     p_access_data->psz_name = psz_name;
237     p_access_data->psz_network = "";
238     if( config_GetIntVariable( "ipv4" ) )
239     {
240         p_access_data->psz_network = "ipv4";
241     }
242     if( config_GetIntVariable( "ipv6" ) )
243     {
244         p_access_data->psz_network = "ipv6";
245     }
246     if( *p_input->psz_access )
247     {
248         /* Find out which shortcut was used */
249         if( !strncmp( p_input->psz_access, "http6", 6 ) )
250         {
251             p_access_data->psz_network = "ipv6";
252         }
253         else if( !strncmp( p_input->psz_access, "http4", 6 ) )
254         {
255             p_access_data->psz_network = "ipv4";
256         }
257     }
258
259     /* Parse psz_name syntax :
260      * //<hostname>[:<port>][/<path>] */
261     while( *psz_parser == '/' )
262     {
263         psz_parser++;
264     }
265     psz_server_addr = psz_parser;
266
267     while( *psz_parser && *psz_parser != ':' && *psz_parser != '/' )
268     {
269         psz_parser++;
270     }
271
272     if ( *psz_parser == ':' )
273     {
274         *psz_parser = '\0';
275         psz_parser++;
276         psz_server_port = psz_parser;
277
278         while( *psz_parser && *psz_parser != '/' )
279         {
280             psz_parser++;
281         }
282     }
283
284     if( *psz_parser == '/' )
285     {
286         *psz_parser = '\0';
287         psz_parser++;
288         psz_path = psz_parser;
289     }
290
291     /* Convert port format */
292     if( *psz_server_port )
293     {
294         i_server_port = strtol( psz_server_port, &psz_parser, 10 );
295         if( *psz_parser )
296         {
297             intf_ErrMsg( "input error: cannot parse server port near %s",
298                          psz_parser );
299             free( p_input->p_access_data );
300             free( psz_name );
301             return( -1 );
302         }
303     }
304
305     if( i_server_port == 0 )
306     {
307         i_server_port = 80;
308     }
309
310     if( !*psz_server_addr )
311     {
312         intf_ErrMsg( "input error: no server given" );
313         free( p_input->p_access_data );
314         free( psz_name );
315         return( -1 );
316     }
317
318     /* Check proxy */
319     if( (psz_proxy = getenv( "http_proxy" )) != NULL && *psz_proxy )
320     {
321         /* http://myproxy.mydomain:myport/ */
322         int                 i_proxy_port = 0;
323  
324         /* Skip the protocol name */
325         while( *psz_proxy && *psz_proxy != ':' )
326         {
327             psz_proxy++;
328         }
329  
330         /* Skip the "://" part */
331         while( *psz_proxy && (*psz_proxy == ':' || *psz_proxy == '/') )
332         {
333             psz_proxy++;
334         }
335  
336         /* Found a proxy name */
337         if( *psz_proxy )
338         {
339             char *psz_port = psz_proxy;
340  
341             /* Skip the hostname part */
342             while( *psz_port && *psz_port != ':' && *psz_port != '/' )
343             {
344                 psz_port++;
345             }
346  
347             /* Found a port name */
348             if( *psz_port )
349             {
350                 char * psz_junk;
351  
352                 /* Replace ':' with '\0' */
353                 *psz_port = '\0';
354                 psz_port++;
355  
356                 psz_junk = psz_port;
357                 while( *psz_junk && *psz_junk != '/' )
358                 {
359                     psz_junk++;
360                 }
361  
362                 if( *psz_junk )
363                 {
364                     *psz_junk = '\0';
365                 }
366  
367                 if( *psz_port != '\0' )
368                 {
369                     i_proxy_port = atoi( psz_port );
370                 }
371             }
372         }
373         else
374         {
375             intf_ErrMsg( "input error: http_proxy environment variable is invalid !" );
376             free( p_input->p_access_data );
377             free( psz_name );
378             return( -1 );
379         }
380
381         p_access_data->socket_desc.i_type = NETWORK_TCP;
382         p_access_data->socket_desc.psz_server_addr = psz_proxy;
383         p_access_data->socket_desc.i_server_port = i_proxy_port;
384
385         snprintf( p_access_data->psz_buffer, sizeof(p_access_data->psz_buffer),
386                   "GET http://%s:%d/%s HTTP/1.1\r\n",
387                   psz_server_addr, i_server_port, psz_path );
388     }
389     else
390     {
391         /* No proxy, direct connection. */
392         p_access_data->socket_desc.i_type = NETWORK_TCP;
393         p_access_data->socket_desc.psz_server_addr = psz_server_addr;
394         p_access_data->socket_desc.i_server_port = i_server_port;
395
396         snprintf( p_access_data->psz_buffer, sizeof(p_access_data->psz_buffer),
397                   "GET /%s HTTP/1.1\r\nHost: %s\r\n",
398                   psz_path, psz_server_addr );
399     }
400     p_access_data->psz_buffer[sizeof(p_access_data->psz_buffer) - 1] = '\0';
401
402     intf_WarnMsg( 2, "input: opening server=%s port=%d path=%s",
403                   psz_server_addr, i_server_port, psz_path );
404
405     vlc_mutex_lock( &p_input->stream.stream_lock );
406     p_input->stream.b_pace_control = 1;
407     p_input->stream.b_seekable = 0;
408     p_input->stream.p_selected_area->i_tell = 0;
409     p_input->stream.p_selected_area->i_size = 0;
410     p_input->stream.i_method = INPUT_METHOD_NETWORK;
411     vlc_mutex_unlock( &p_input->stream.stream_lock );
412     p_input->i_mtu = 0;
413  
414     return( HTTPConnect( p_input, 0 ) );
415 }
416
417 /*****************************************************************************
418  * HTTPClose: free unused data structures
419  *****************************************************************************/
420 static void HTTPClose( input_thread_t * p_input )
421 {
422     input_FDClose( p_input );
423 }
424
425 /*****************************************************************************
426  * HTTPSetProgram: do nothing
427  *****************************************************************************/
428 static int HTTPSetProgram( input_thread_t * p_input,
429                            pgrm_descriptor_t * p_program )
430 {
431     return( 0 );
432 }
433
434 /*****************************************************************************
435  * HTTPSeek: close and re-open a connection at the right place
436  *****************************************************************************/
437 static void HTTPSeek( input_thread_t * p_input, off_t i_pos )
438 {
439     _input_socket_t *   p_access_data = p_input->p_access_data;
440     close( p_access_data->_socket.i_handle );
441     intf_WarnMsg( 2, "http: seeking to position %lld", i_pos );
442     HTTPConnect( p_input, i_pos );
443 }
444