]> git.sesse.net Git - vlc/blob - modules/access/http.c
* input: access meta data support.
[vlc] / modules / access / http.c
1 /*****************************************************************************
2  * http.c: HTTP input module
3  *****************************************************************************
4  * Copyright (C) 2001-2004 VideoLAN
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *          Christophe Massiot <massiot@via.ecp.fr>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>
29
30 #include <vlc/vlc.h>
31 #include <vlc/input.h>
32
33 #include "vlc_playlist.h"
34 #include "network.h"
35
36 /*****************************************************************************
37  * Module descriptor
38  *****************************************************************************/
39 static int  Open ( vlc_object_t * );
40 static void Close( vlc_object_t * );
41
42 #define PROXY_TEXT N_("HTTP proxy")
43 #define PROXY_LONGTEXT N_( \
44     "You can specify an HTTP proxy to use. It must be of the form " \
45     "http://myproxy.mydomain:myport/. If none is specified, the HTTP_PROXY " \
46     "environment variable will be tried." )
47
48 #define CACHING_TEXT N_("Caching value in ms")
49 #define CACHING_LONGTEXT N_( \
50     "Allows you to modify the default caching value for http streams. This " \
51     "value should be set in millisecond units." )
52
53 #define USER_TEXT N_("HTTP user name")
54 #define USER_LONGTEXT N_("Allows you to modify the user name that will " \
55     "be used for the connection (Basic authentication only).")
56
57 #define PASS_TEXT N_("HTTP password")
58 #define PASS_LONGTEXT N_("Allows you to modify the password that will be " \
59     "used for the connection.")
60
61 #define AGENT_TEXT N_("HTTP user agent")
62 #define AGENT_LONGTEXT N_("Allows you to modify the user agent that will be " \
63     "used for the connection.")
64
65 #define RECONNECT_TEXT N_("Auto re-connect")
66 #define RECONNECT_LONGTEXT N_("Will automatically attempt a re-connection " \
67     "in case it was untimely closed.")
68
69 vlc_module_begin();
70     set_description( _("HTTP input") );
71     set_capability( "access2", 0 );
72
73     add_string( "http-proxy", NULL, NULL, PROXY_TEXT, PROXY_LONGTEXT,
74                 VLC_FALSE );
75     add_integer( "http-caching", 4 * DEFAULT_PTS_DELAY / 1000, NULL,
76                  CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
77     add_string( "http-user", NULL, NULL, USER_TEXT, USER_LONGTEXT, VLC_FALSE );
78     add_string( "http-pwd", NULL , NULL, PASS_TEXT, PASS_LONGTEXT, VLC_FALSE );
79     add_string( "http-user-agent", COPYRIGHT_MESSAGE , NULL, AGENT_TEXT,
80                 AGENT_LONGTEXT, VLC_FALSE );
81     add_bool( "http-reconnect", 0, NULL, RECONNECT_TEXT,
82               RECONNECT_LONGTEXT, VLC_TRUE );
83
84     add_shortcut( "http" );
85     add_shortcut( "http4" );
86     add_shortcut( "http6" );
87     set_callbacks( Open, Close );
88 vlc_module_end();
89
90 /*****************************************************************************
91  * Local prototypes
92  *****************************************************************************/
93 struct access_sys_t
94 {
95     int fd;
96
97     /* From uri */
98     vlc_url_t url;
99     char    *psz_user;
100     char    *psz_passwd;
101     char    *psz_user_agent;
102
103     /* Proxy */
104     vlc_bool_t b_proxy;
105     vlc_url_t  proxy;
106
107     /* */
108     int        i_code;
109     char       *psz_protocol;
110     int        i_version;
111
112     char       *psz_mime;
113     char       *psz_pragma;
114     char       *psz_location;
115     vlc_bool_t b_mms;
116     vlc_bool_t b_icecast;
117
118     vlc_bool_t b_chunked;
119     int64_t    i_chunk;
120
121     vlc_bool_t b_seekable;
122     vlc_bool_t b_reconnect;
123     vlc_bool_t b_pace_control;
124 };
125
126 /* */
127 static int Read( access_t *, uint8_t *, int );
128 static int Seek( access_t *, int64_t );
129 static int Control( access_t *, int, va_list );
130
131 /* */
132 static void ParseURL( access_sys_t *, char *psz_url );
133 static int  Connect( access_t *, int64_t );
134
135 /*****************************************************************************
136  * Open:
137  *****************************************************************************/
138 static int Open( vlc_object_t *p_this )
139 {
140     access_t     *p_access = (access_t*)p_this;
141     access_sys_t *p_sys;
142     char         *psz;
143
144     /* First set ipv4/ipv6 */
145     var_Create( p_access, "ipv4", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
146     var_Create( p_access, "ipv6", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
147
148     if( *p_access->psz_access )
149     {
150         vlc_value_t val;
151         /* Find out which shortcut was used */
152         if( !strncmp( p_access->psz_access, "http4", 6 ) )
153         {
154             val.b_bool = VLC_TRUE;
155             var_Set( p_access, "ipv4", val );
156
157             val.b_bool = VLC_FALSE;
158             var_Set( p_access, "ipv6", val );
159         }
160         else if( !strncmp( p_access->psz_access, "http6", 6 ) )
161         {
162             val.b_bool = VLC_TRUE;
163             var_Set( p_access, "ipv6", val );
164
165             val.b_bool = VLC_FALSE;
166             var_Set( p_access, "ipv4", val );
167         }
168     }
169
170     /* Set up p_access */
171     p_access->pf_read = Read;
172     p_access->pf_block = NULL;
173     p_access->pf_control = Control;
174     p_access->pf_seek = Seek;
175     p_access->info.i_update = 0;
176     p_access->info.i_size = 0;
177     p_access->info.i_pos = 0;
178     p_access->info.b_eof = VLC_FALSE;
179     p_access->info.i_title = 0;
180     p_access->info.i_seekpoint = 0;
181     p_access->p_sys = p_sys = malloc( sizeof( access_sys_t ) );
182     memset( p_sys, 0, sizeof( access_sys_t ) );
183     p_sys->fd = -1;
184     p_sys->b_proxy = VLC_FALSE;
185     p_sys->i_version = 1;
186     p_sys->b_seekable = VLC_TRUE;
187     p_sys->psz_mime = NULL;
188     p_sys->psz_pragma = NULL;
189     p_sys->b_mms = VLC_FALSE;
190     p_sys->b_icecast = VLC_FALSE;
191     p_sys->psz_location = NULL;
192     p_sys->psz_user_agent = NULL;
193     p_sys->b_pace_control = VLC_TRUE;
194
195     /* Parse URI */
196     ParseURL( p_sys, p_access->psz_path );
197     if( p_sys->url.psz_host == NULL || *p_sys->url.psz_host == '\0' )
198     {
199         msg_Warn( p_access, "invalid host" );
200         goto error;
201     }
202     if( p_sys->url.i_port <= 0 )
203     {
204         p_sys->url.i_port = 80;
205     }
206     if( !p_sys->psz_user || *p_sys->psz_user == '\0' )
207     {
208         p_sys->psz_user = var_CreateGetString( p_access, "http-user" );
209         p_sys->psz_passwd = var_CreateGetString( p_access, "http-pwd" );
210     }
211
212     /* Do user agent */
213     p_sys->psz_user_agent = var_CreateGetString( p_access, "http-user-agent" );
214
215     /* Check proxy */
216     psz = var_CreateGetString( p_access, "http-proxy" );
217     if( *psz )
218     {
219         p_sys->b_proxy = VLC_TRUE;
220         vlc_UrlParse( &p_sys->proxy, psz, 0 );
221     }
222     else
223     {
224         char *psz_proxy = getenv( "http_proxy" );
225         if( psz_proxy && *psz_proxy )
226         {
227             p_sys->b_proxy = VLC_TRUE;
228             vlc_UrlParse( &p_sys->proxy, psz_proxy, 0 );
229         }
230         if( psz_proxy )
231             free( psz_proxy );
232     }
233     free( psz );
234
235     if( p_sys->b_proxy )
236     {
237         if( p_sys->proxy.psz_host == NULL || *p_sys->proxy.psz_host == '\0' )
238         {
239             msg_Warn( p_access, "invalid proxy host" );
240             goto error;
241         }
242         if( p_sys->proxy.i_port <= 0 )
243         {
244             p_sys->proxy.i_port = 80;
245         }
246     }
247
248     msg_Dbg( p_access, "http: server='%s' port=%d file='%s",
249              p_sys->url.psz_host, p_sys->url.i_port, p_sys->url.psz_path );
250     if( p_sys->b_proxy )
251     {
252         msg_Dbg( p_access, "      proxy %s:%d", p_sys->proxy.psz_host,
253                  p_sys->proxy.i_port );
254     }
255     if( p_sys->psz_user && *p_sys->psz_user )
256     {
257         msg_Dbg( p_access, "      user='%s', pwd='%s'",
258                  p_sys->psz_user, p_sys->psz_passwd );
259     }
260
261     p_sys->b_reconnect = var_CreateGetBool( p_access, "http-reconnect" );
262
263     /* Connect */
264     if( Connect( p_access, 0 ) )
265     {
266         /* Retry with http 1.0 */
267         p_sys->i_version = 0;
268
269         if( p_access->b_die ||
270             Connect( p_access, 0 ) )
271         {
272             goto error;
273         }
274     }
275
276     if( ( p_sys->i_code == 301 || p_sys->i_code == 302 ||
277           p_sys->i_code == 303 || p_sys->i_code == 307 ) &&
278         p_sys->psz_location && *p_sys->psz_location )
279     {
280         playlist_t * p_playlist;
281
282         msg_Dbg( p_access, "redirection to %s", p_sys->psz_location );
283
284         p_playlist = vlc_object_find( p_access, VLC_OBJECT_PLAYLIST,
285                                       FIND_ANYWHERE );
286         if( !p_playlist )
287         {
288             msg_Err( p_access, "redirection failed: can't find playlist" );
289             goto error;
290         }
291         p_playlist->pp_items[p_playlist->i_index]->b_autodeletion = VLC_TRUE;
292         playlist_Add( p_playlist, p_sys->psz_location, p_sys->psz_location,
293                       PLAYLIST_INSERT,
294                       p_playlist->i_index + 1 );
295         vlc_object_release( p_playlist );
296
297         p_access->info.i_size = 0;  /* Force to stop reading */
298     }
299
300     if( p_sys->b_mms )
301     {
302         msg_Dbg( p_access, "This is actually a live mms server, BAIL" );
303         goto error;
304     }
305
306     if( p_sys->b_icecast )
307     {
308         if( p_sys->psz_mime && !strcasecmp( p_sys->psz_mime, "audio/mpeg" ) )
309             p_access->psz_demux = strdup( "mp3" );
310     }
311
312     if( !strcmp( p_sys->psz_protocol, "ICY" ) )
313     {
314         if( p_sys->psz_mime && !strcasecmp( p_sys->psz_mime, "video/nsv" ) )
315             p_access->psz_demux = strdup( "nsv" );
316         else if( p_sys->psz_mime &&
317                  ( !strcasecmp( p_sys->psz_mime, "audio/aac" ) ||
318                    !strcasecmp( p_sys->psz_mime, "audio/aacp" ) ) )
319             p_access->psz_demux = strdup( "m4a" );
320         else
321             p_access->psz_demux = strdup( "mp3" );
322
323         msg_Info( p_access, "ICY server found, %s demuxer selected",
324                   p_access->psz_demux );
325
326 #if 0   /* Doesn't work really well because of the pre-buffering in shoutcast
327          * servers (the buffer content will be sent as fast as possible). */
328         p_sys->b_pace_control = VLC_FALSE;
329 #endif
330     }
331
332     if( p_sys->b_reconnect ) msg_Dbg( p_access, "auto re-connect enabled" );
333
334     /* PTS delay */
335     var_Create( p_access, "http-caching", VLC_VAR_INTEGER |VLC_VAR_DOINHERIT );
336
337     return VLC_SUCCESS;
338
339 error:
340     vlc_UrlClean( &p_sys->url );
341     vlc_UrlClean( &p_sys->proxy );
342     if( p_sys->psz_mime ) free( p_sys->psz_mime );
343     if( p_sys->psz_pragma ) free( p_sys->psz_pragma );
344     if( p_sys->psz_location ) free( p_sys->psz_location );
345     if( p_sys->psz_user_agent ) free( p_sys->psz_user_agent );
346     if( p_sys->psz_user ) free( p_sys->psz_user );
347     if( p_sys->psz_passwd ) free( p_sys->psz_passwd );
348
349     if( p_sys->fd > 0 )
350     {
351         net_Close( p_sys->fd );
352     }
353     free( p_sys );
354     return VLC_EGENERIC;
355 }
356
357 /*****************************************************************************
358  * Close:
359  *****************************************************************************/
360 static void Close( vlc_object_t *p_this )
361 {
362     access_t     *p_access = (access_t*)p_this;
363     access_sys_t *p_sys = p_access->p_sys;
364
365     vlc_UrlClean( &p_sys->url );
366     vlc_UrlClean( &p_sys->proxy );
367
368     if( p_sys->psz_user ) free( p_sys->psz_user );
369     if( p_sys->psz_passwd ) free( p_sys->psz_passwd );
370
371     if( p_sys->psz_mime ) free( p_sys->psz_mime );
372     if( p_sys->psz_pragma ) free( p_sys->psz_pragma );
373     if( p_sys->psz_location ) free( p_sys->psz_location );
374
375     if( p_sys->psz_user_agent ) free( p_sys->psz_user_agent );
376
377     if( p_sys->fd > 0 )
378     {
379         net_Close( p_sys->fd );
380     }
381     free( p_sys );
382 }
383
384 /*****************************************************************************
385  * Read: Read up to i_len bytes from the http connection and place in
386  * p_buffer. Return the actual number of bytes read
387  *****************************************************************************/
388 static int Read( access_t *p_access, uint8_t *p_buffer, int i_len )
389 {
390     access_sys_t *p_sys = p_access->p_sys;
391     int i_read;
392
393     if( p_sys->fd < 0 )
394     {
395         p_access->info.b_eof = VLC_TRUE;
396         return 0;
397     }
398
399     if( p_access->info.i_size > 0 &&
400         i_len + p_access->info.i_pos > p_access->info.i_size )
401     {
402         if( ( i_len = p_access->info.i_size - p_access->info.i_pos ) == 0 )
403         {
404             p_access->info.b_eof = VLC_TRUE;
405             return 0;
406         }
407     }
408     if( p_sys->b_chunked )
409     {
410         if( p_sys->i_chunk < 0 )
411         {
412             p_access->info.b_eof = VLC_TRUE;
413             return 0;
414         }
415
416         if( p_sys->i_chunk <= 0 )
417         {
418             char *psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, NULL );
419             /* read the chunk header */
420             if( psz == NULL )
421             {
422                 msg_Dbg( p_access, "failed reading chunk-header line" );
423                 return -1;
424             }
425             p_sys->i_chunk = strtoll( psz, NULL, 16 );
426             free( psz );
427
428             if( p_sys->i_chunk <= 0 )   /* eof */
429             {
430                 p_sys->i_chunk = -1;
431                 p_access->info.b_eof = VLC_TRUE;
432                 return 0;
433             }
434         }
435
436         if( i_len > p_sys->i_chunk )
437         {
438             i_len = p_sys->i_chunk;
439         }
440     }
441
442
443     i_read = net_Read( p_access, p_sys->fd, NULL, p_buffer, i_len,
444                        VLC_FALSE );
445     if( i_read > 0 )
446     {
447         p_access->info.i_pos += i_read;
448
449         if( p_sys->b_chunked )
450         {
451             p_sys->i_chunk -= i_read;
452             if( p_sys->i_chunk <= 0 )
453             {
454                 /* read the empty line */
455                 char *psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, NULL );
456                 if( psz ) free( psz );
457             }
458         }
459     }
460     else if( i_read == 0 )
461     {
462         if( p_sys->b_reconnect )
463         {
464             msg_Dbg( p_access, "got disconnected, trying to reconnect" );
465             net_Close( p_sys->fd ); p_sys->fd = -1;
466             if( Connect( p_access, p_access->info.i_pos ) )
467             {
468                 msg_Dbg( p_access, "reconnection failed" );
469             }
470             else
471             {
472                 p_sys->b_reconnect = VLC_FALSE;
473                 i_read = Read( p_access, p_buffer, i_len );
474                 p_sys->b_reconnect = VLC_TRUE;
475             }
476         }
477
478         if( i_read == 0 ) p_access->info.b_eof = VLC_TRUE;
479     }
480
481     return i_read;
482 }
483
484 /*****************************************************************************
485  * Seek: close and re-open a connection at the right place
486  *****************************************************************************/
487 static int Seek( access_t *p_access, int64_t i_pos )
488 {
489     access_sys_t *p_sys = p_access->p_sys;
490
491     msg_Dbg( p_access, "trying to seek to "I64Fd, i_pos );
492
493     net_Close( p_sys->fd ); p_sys->fd = -1;
494
495     if( Connect( p_access, i_pos ) )
496     {
497         msg_Err( p_access, "seek failed" );
498         p_access->info.b_eof = VLC_TRUE;
499         return VLC_EGENERIC;
500     }
501     return VLC_SUCCESS;
502 }
503
504 /*****************************************************************************
505  * Control:
506  *****************************************************************************/
507 static int Control( access_t *p_access, int i_query, va_list args )
508 {
509     access_sys_t *p_sys = p_access->p_sys;
510     vlc_bool_t   *pb_bool;
511     int          *pi_int;
512     int64_t      *pi_64;
513
514     switch( i_query )
515     {
516         /* */
517         case ACCESS_CAN_SEEK:
518             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
519             *pb_bool = p_sys->b_seekable;
520             break;
521         case ACCESS_CAN_FASTSEEK:
522             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
523             *pb_bool = VLC_FALSE;
524             break;
525         case ACCESS_CAN_PAUSE:
526         case ACCESS_CAN_CONTROL_PACE:
527             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
528             *pb_bool = p_sys->b_pace_control;
529             break;
530
531         /* */
532         case ACCESS_GET_MTU:
533             pi_int = (int*)va_arg( args, int * );
534             *pi_int = 0;
535             break;
536
537         case ACCESS_GET_PTS_DELAY:
538             pi_64 = (int64_t*)va_arg( args, int64_t * );
539             *pi_64 = (int64_t)var_GetInteger( p_access, "http-caching" ) * 1000;
540             break;
541
542         /* */
543         case ACCESS_SET_PAUSE_STATE:
544             break;
545
546         case ACCESS_GET_TITLE_INFO:
547         case ACCESS_SET_TITLE:
548         case ACCESS_SET_SEEKPOINT:
549         case ACCESS_SET_PRIVATE_ID_STATE:
550             return VLC_EGENERIC;
551
552         default:
553             msg_Warn( p_access, "unimplemented query in control" );
554             return VLC_EGENERIC;
555
556     }
557     return VLC_SUCCESS;
558 }
559
560 /*****************************************************************************
561  * ParseURL: extract user:password
562  *****************************************************************************/
563 static void ParseURL( access_sys_t *p_sys, char *psz_url )
564 {
565     char *psz_dup = strdup( psz_url );
566     char *p = psz_dup;
567     char *psz;
568
569     /* Syntax //[user:password]@<hostname>[:<port>][/<path>] */
570     while( *p == '/' )
571     {
572         p++;
573     }
574     psz = p;
575
576     /* Parse auth */
577     if( ( p = strchr( psz, '@' ) ) )
578     {
579         char *comma;
580
581         *p++ = '\0';
582         comma = strchr( psz, ':' );
583
584         /* Retreive user:password */
585         if( comma )
586         {
587             *comma++ = '\0';
588
589             p_sys->psz_user = strdup( psz );
590             p_sys->psz_passwd = strdup( comma );
591         }
592         else
593         {
594             p_sys->psz_user = strdup( psz );
595         }
596     }
597     else
598     {
599         p = psz;
600     }
601
602     /* Parse uri */
603     vlc_UrlParse( &p_sys->url, p, 0 );
604
605     free( psz_dup );
606 }
607
608 /*****************************************************************************
609  * Connect:
610  *****************************************************************************/
611 static int Connect( access_t *p_access, int64_t i_tell )
612 {
613     access_sys_t   *p_sys = p_access->p_sys;
614     vlc_url_t      srv = p_sys->b_proxy ? p_sys->proxy : p_sys->url;
615     char           *psz;
616
617     /* Clean info */
618     if( p_sys->psz_location ) free( p_sys->psz_location );
619     if( p_sys->psz_mime ) free( p_sys->psz_mime );
620     if( p_sys->psz_pragma ) free( p_sys->psz_pragma );
621
622     p_sys->psz_location = NULL;
623     p_sys->psz_mime = NULL;
624     p_sys->psz_pragma = NULL;
625     p_sys->b_mms = VLC_FALSE;
626     p_sys->b_chunked = VLC_FALSE;
627     p_sys->i_chunk = 0;
628
629     p_access->info.i_size = 0;
630     p_access->info.i_pos  = i_tell;
631     p_access->info.b_eof  = VLC_FALSE;
632
633
634     /* Open connection */
635     p_sys->fd = net_OpenTCP( p_access, srv.psz_host, srv.i_port );
636     if( p_sys->fd < 0 )
637     {
638         msg_Err( p_access, "cannot connect to %s:%d", srv.psz_host, srv.i_port );
639         return VLC_EGENERIC;
640     }
641
642     if( p_sys->b_proxy )
643     {
644         if( p_sys->url.psz_path )
645         {
646             net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
647                         "GET http://%s:%d%s HTTP/1.%d\r\n",
648                         p_sys->url.psz_host, p_sys->url.i_port,
649                         p_sys->url.psz_path, p_sys->i_version );
650         }
651         else
652         {
653             net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
654                         "GET http://%s:%d/ HTTP/1.%d\r\n",
655                         p_sys->url.psz_host, p_sys->url.i_port,
656                         p_sys->i_version );
657         }
658     }
659     else
660     {
661         char *psz_path = p_sys->url.psz_path;
662         if( !psz_path || !*psz_path )
663         {
664             psz_path = "/";
665         }
666         if( p_sys->url.i_port != 80)
667         {
668             net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
669                         "GET %s HTTP/1.%d\r\nHost: %s:%d\r\n",
670                         psz_path, p_sys->i_version, p_sys->url.psz_host,
671                         p_sys->url.i_port );
672         }
673         else
674         {        
675             net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
676                         "GET %s HTTP/1.%d\r\nHost: %s\r\n",
677                         psz_path, p_sys->i_version, p_sys->url.psz_host );
678         }
679     }
680     /* User Agent */
681     net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL, "User-Agent: %s\r\n",
682                 p_sys->psz_user_agent );
683     /* Offset */
684     if( p_sys->i_version == 1 )
685     {
686         net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
687                     "Range: bytes="I64Fd"-\r\n", i_tell );
688     }
689     /* Authentification */
690     if( p_sys->psz_user && *p_sys->psz_user )
691     {
692         char *buf;
693         char *b64;
694
695         asprintf( &buf, "%s:%s", p_sys->psz_user,
696                    p_sys->psz_passwd ? p_sys->psz_passwd : "" );
697
698         b64 = vlc_b64_encode( buf );
699
700         net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
701                     "Authorization: Basic %s\r\n", b64 );
702         free( b64 );
703     }
704     net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
705                 "Connection: Close\r\n" );
706
707     if( net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL, "\r\n" ) < 0 )
708     {
709         msg_Err( p_access, "failed to send request" );
710         net_Close( p_sys->fd ); p_sys->fd = -1;
711         return VLC_EGENERIC;
712     }
713
714     /* Read Answer */
715     if( ( psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, NULL ) ) == NULL )
716     {
717         msg_Err( p_access, "failed to read answer" );
718         goto error;
719     }
720     if( !strncmp( psz, "HTTP/1.", 7 ) )
721     {
722         p_sys->psz_protocol = "HTTP";
723         p_sys->i_code = atoi( &psz[9] );
724     }
725     else if( !strncmp( psz, "ICY", 3 ) )
726     {
727         p_sys->psz_protocol = "ICY";
728         p_sys->i_code = atoi( &psz[4] );
729         p_sys->b_reconnect = VLC_TRUE;
730     }
731     else
732     {
733         msg_Err( p_access, "invalid HTTP reply '%s'", psz );
734         free( psz );
735         goto error;
736     }
737     msg_Dbg( p_access, "protocol '%s' answer code %d",
738              p_sys->psz_protocol, p_sys->i_code );
739     if( !strcmp( p_sys->psz_protocol, "ICY" ) )
740     {
741         p_sys->b_seekable = VLC_FALSE;
742     }
743     if( p_sys->i_code != 206 )
744     {
745         p_sys->b_seekable = VLC_FALSE;
746     }
747     if( p_sys->i_code >= 400 )
748     {
749         msg_Err( p_access, "error: %s", psz );
750         free( psz );
751         goto error;
752     }
753     free( psz );
754
755     for( ;; )
756     {
757         char *psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, NULL );
758         char *p;
759
760         if( psz == NULL )
761         {
762             msg_Err( p_access, "failed to read answer" );
763             goto error;
764         }
765
766         /* msg_Dbg( p_input, "Line=%s", psz ); */
767         if( *psz == '\0' )
768         {
769             free( psz );
770             break;
771         }
772
773
774         if( ( p = strchr( psz, ':' ) ) == NULL )
775         {
776             msg_Err( p_access, "malformed header line: %s", psz );
777             free( psz );
778             goto error;
779         }
780         *p++ = '\0';
781         while( *p == ' ' ) p++;
782
783         if( !strcasecmp( psz, "Content-Length" ) )
784         {
785             p_access->info.i_size = i_tell + atoll( p );
786             msg_Dbg( p_access, "stream size="I64Fd, p_access->info.i_size );
787         }
788         else if( !strcasecmp( psz, "Location" ) )
789         {
790             if( p_sys->psz_location ) free( p_sys->psz_location );
791             p_sys->psz_location = strdup( p );
792         }
793         else if( !strcasecmp( psz, "Content-Type" ) )
794         {
795             if( p_sys->psz_mime ) free( p_sys->psz_mime );
796             p_sys->psz_mime = strdup( p );
797             msg_Dbg( p_access, "Content-Type: %s", p_sys->psz_mime );
798         }
799         else if( !strcasecmp( psz, "Pragma" ) )
800         {
801             if( !strcasecmp( psz, "Pragma: features" ) )
802                 p_sys->b_mms = VLC_TRUE;
803             if( p_sys->psz_pragma ) free( p_sys->psz_pragma );
804             p_sys->psz_pragma = strdup( p );
805             msg_Dbg( p_access, "Pragma: %s", p_sys->psz_pragma );
806         }
807         else if( !strcasecmp( psz, "Server" ) )
808         {
809             msg_Dbg( p_access, "Server: %s", p );
810             if( !strncasecmp( p, "Icecast", 7 ) ||
811                 !strncasecmp( p, "Nanocaster", 10 ) )
812             {
813                 /* Remember if this is Icecast 
814                  * we need to force mp3 in some cases without breaking autodetection */
815
816                 /* Let live365 streams (nanocaster) piggyback on the icecast routine. 
817                  * They look very similar */
818
819                 p_sys->b_reconnect = VLC_TRUE;
820                 p_sys->b_pace_control = VLC_FALSE;
821                 p_sys->b_icecast = VLC_TRUE;
822             }
823         }
824         else if( !strcasecmp( psz, "Transfer-Encoding" ) )
825         {
826             msg_Dbg( p_access, "Transfer-Encoding: %s", p );
827             if( !strncasecmp( p, "chunked", 7 ) )
828             {
829                 p_sys->b_chunked = VLC_TRUE;
830             }
831         }
832         else if( !strncasecmp( psz, "icy-", 4 ) ||
833                  !strncasecmp( psz, "ice-", 4 ) ||
834                  !strncasecmp( psz, "x-audiocast", 11 ) )
835         {
836             msg_Dbg( p_access, "Meta-Info: %s: %s", psz, p );
837         }
838
839         free( psz );
840     }
841     return VLC_SUCCESS;
842
843 error:
844     net_Close( p_sys->fd ); p_sys->fd = -1;
845     return VLC_EGENERIC;
846 }
847