http.c 23.9 KB
Newer Older
1
/*****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin committed
2
 * http.c: HTTP input module
3
 *****************************************************************************
4
 * Copyright (C) 2001-2004 VideoLAN
5
 * $Id$
6
 *
7 8
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
 *          Christophe Massiot <massiot@via.ecp.fr>
9 10 11 12 13
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
14
 *
15 16 17 18 19 20 21 22 23 24 25 26 27 28
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>
29

30 31 32
#include <vlc/vlc.h>
#include <vlc/input.h>

33
#include "vlc_playlist.h"
34 35 36 37 38
#include "network.h"

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
39 40 41
static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );

Gildas Bazin's avatar
 
Gildas Bazin committed
42
#define PROXY_TEXT N_("HTTP proxy")
Gildas Bazin's avatar
 
Gildas Bazin committed
43
#define PROXY_LONGTEXT N_( \
Gildas Bazin's avatar
 
Gildas Bazin committed
44
    "You can specify an HTTP proxy to use. It must be of the form " \
Sam Hocevar's avatar
Sam Hocevar committed
45
    "http://myproxy.mydomain:myport/. If none is specified, the HTTP_PROXY " \
46 47
    "environment variable will be tried." )

Christophe Massiot's avatar
Christophe Massiot committed
48
#define CACHING_TEXT N_("Caching value in ms")
Gildas Bazin's avatar
 
Gildas Bazin committed
49 50
#define CACHING_LONGTEXT N_( \
    "Allows you to modify the default caching value for http streams. This " \
51
    "value should be set in millisecond units." )
Gildas Bazin's avatar
 
Gildas Bazin committed
52

Gildas Bazin's avatar
 
Gildas Bazin committed
53 54
#define USER_TEXT N_("HTTP user name")
#define USER_LONGTEXT N_("Allows you to modify the user name that will " \
55
    "be used for the connection (Basic authentication only).")
Gildas Bazin's avatar
 
Gildas Bazin committed
56 57 58 59 60 61 62 63 64

#define PASS_TEXT N_("HTTP password")
#define PASS_LONGTEXT N_("Allows you to modify the password that will be " \
    "used for the connection.")

#define AGENT_TEXT N_("HTTP user agent")
#define AGENT_LONGTEXT N_("Allows you to modify the user agent that will be " \
    "used for the connection.")

65 66 67 68
#define RECONNECT_TEXT N_("Auto re-connect")
#define RECONNECT_LONGTEXT N_("Will automatically attempt a re-connection " \
    "in case it was untimely closed.")

69
vlc_module_begin();
Gildas Bazin's avatar
 
Gildas Bazin committed
70
    set_description( _("HTTP input") );
71
    set_capability( "access2", 0 );
Gildas Bazin's avatar
 
Gildas Bazin committed
72 73 74 75 76 77 78 79 80

    add_string( "http-proxy", NULL, NULL, PROXY_TEXT, PROXY_LONGTEXT,
                VLC_FALSE );
    add_integer( "http-caching", 4 * DEFAULT_PTS_DELAY / 1000, NULL,
                 CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
    add_string( "http-user", NULL, NULL, USER_TEXT, USER_LONGTEXT, VLC_FALSE );
    add_string( "http-pwd", NULL , NULL, PASS_TEXT, PASS_LONGTEXT, VLC_FALSE );
    add_string( "http-user-agent", COPYRIGHT_MESSAGE , NULL, AGENT_TEXT,
                AGENT_LONGTEXT, VLC_FALSE );
81 82
    add_bool( "http-reconnect", 0, NULL, RECONNECT_TEXT,
              RECONNECT_LONGTEXT, VLC_TRUE );
Gildas Bazin's avatar
 
Gildas Bazin committed
83

84
    add_shortcut( "http" );
85 86 87 88 89 90
    add_shortcut( "http4" );
    add_shortcut( "http6" );
    set_callbacks( Open, Close );
vlc_module_end();

/*****************************************************************************
91
 * Local prototypes
92
 *****************************************************************************/
93
struct access_sys_t
94
{
95
    int fd;
96

97 98 99 100 101
    /* From uri */
    vlc_url_t url;
    char    *psz_user;
    char    *psz_passwd;
    char    *psz_user_agent;
102

103 104 105
    /* Proxy */
    vlc_bool_t b_proxy;
    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

117 118
    vlc_bool_t b_chunked;
    int64_t    i_chunk;
119

120
    vlc_bool_t b_seekable;
121
    vlc_bool_t b_reconnect;
122
    vlc_bool_t b_pace_control;
123
};
124

125 126 127 128
/* */
static int Read( access_t *, uint8_t *, int );
static int Seek( access_t *, int64_t );
static int Control( access_t *, int, va_list );
129

130 131 132
/* */
static void ParseURL( access_sys_t *, char *psz_url );
static int  Connect( access_t *, int64_t );
133

134 135 136
/*****************************************************************************
 * Open:
 *****************************************************************************/
137
static int Open( vlc_object_t *p_this )
138
{
139 140
    access_t     *p_access = (access_t*)p_this;
    access_sys_t *p_sys;
141
    char         *psz;
142 143

    /* First set ipv4/ipv6 */
144 145
    var_Create( p_access, "ipv4", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
    var_Create( p_access, "ipv6", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
146

147
    if( *p_access->psz_access )
148
    {
149
        vlc_value_t val;
150
        /* Find out which shortcut was used */
151
        if( !strncmp( p_access->psz_access, "http4", 6 ) )
152 153
        {
            val.b_bool = VLC_TRUE;
154
            var_Set( p_access, "ipv4", val );
155 156

            val.b_bool = VLC_FALSE;
157
            var_Set( p_access, "ipv6", val );
158
        }
159
        else if( !strncmp( p_access->psz_access, "http6", 6 ) )
160
        {
161
            val.b_bool = VLC_TRUE;
162
            var_Set( p_access, "ipv6", val );
163 164

            val.b_bool = VLC_FALSE;
165
            var_Set( p_access, "ipv4", val );
166 167 168
        }
    }

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
    /* Set up p_access */
    p_access->pf_read = Read;
    p_access->pf_block = NULL;
    p_access->pf_control = Control;
    p_access->pf_seek = Seek;
    p_access->info.i_update = 0;
    p_access->info.i_size = 0;
    p_access->info.i_pos = 0;
    p_access->info.b_eof = VLC_FALSE;
    p_access->info.i_title = 0;
    p_access->info.i_seekpoint = 0;
    p_access->p_sys = p_sys = malloc( sizeof( access_sys_t ) );
    memset( p_sys, 0, sizeof( access_sys_t ) );
    p_sys->fd = -1;
    p_sys->b_proxy = VLC_FALSE;
    p_sys->i_version = 1;
    p_sys->b_seekable = VLC_TRUE;
    p_sys->psz_mime = NULL;
187
    p_sys->psz_pragma = NULL;
188
    p_sys->b_mms = VLC_FALSE;
189 190
    p_sys->psz_location = NULL;
    p_sys->psz_user_agent = NULL;
191
    p_sys->b_pace_control = VLC_TRUE;
192

193
    /* Parse URI */
194
    ParseURL( p_sys, p_access->psz_path );
195
    if( p_sys->url.psz_host == NULL || *p_sys->url.psz_host == '\0' )
196
    {
197
        msg_Warn( p_access, "invalid host" );
198
        goto error;
199
    }
200
    if( p_sys->url.i_port <= 0 )
201
    {
202 203 204 205
        p_sys->url.i_port = 80;
    }
    if( !p_sys->psz_user || *p_sys->psz_user == '\0' )
    {
206 207
        p_sys->psz_user = var_CreateGetString( p_access, "http-user" );
        p_sys->psz_passwd = var_CreateGetString( p_access, "http-pwd" );
208
    }
209 210

    /* Do user agent */
211
    p_sys->psz_user_agent = var_CreateGetString( p_access, "http-user-agent" );
212 213

    /* Check proxy */
214 215
    psz = var_CreateGetString( p_access, "http-proxy" );
    if( *psz )
216 217
    {
        p_sys->b_proxy = VLC_TRUE;
218
        vlc_UrlParse( &p_sys->proxy, psz, 0 );
219 220
    }
    else
221
    {
222 223
        char *psz_proxy = getenv( "http_proxy" );
        if( psz_proxy && *psz_proxy )
Gildas Bazin's avatar
 
Gildas Bazin committed
224
        {
225
            p_sys->b_proxy = VLC_TRUE;
226
            vlc_UrlParse( &p_sys->proxy, psz_proxy, 0 );
227 228 229
        }
        if( psz_proxy )
            free( psz_proxy );
230
    }
231
    free( psz );
232

233
    if( p_sys->b_proxy )
234
    {
235 236
        if( p_sys->proxy.psz_host == NULL || *p_sys->proxy.psz_host == '\0' )
        {
237
            msg_Warn( p_access, "invalid proxy host" );
238 239 240
            goto error;
        }
        if( p_sys->proxy.i_port <= 0 )
241
        {
242
            p_sys->proxy.i_port = 80;
243 244
        }
    }
245

246
    msg_Dbg( p_access, "http: server='%s' port=%d file='%s",
Gildas Bazin's avatar
 
Gildas Bazin committed
247
             p_sys->url.psz_host, p_sys->url.i_port, p_sys->url.psz_path );
248
    if( p_sys->b_proxy )
249
    {
250
        msg_Dbg( p_access, "      proxy %s:%d", p_sys->proxy.psz_host,
Gildas Bazin's avatar
 
Gildas Bazin committed
251
                 p_sys->proxy.i_port );
252
    }
253
    if( p_sys->psz_user && *p_sys->psz_user )
254
    {
255
        msg_Dbg( p_access, "      user='%s', pwd='%s'",
Gildas Bazin's avatar
 
Gildas Bazin committed
256
                 p_sys->psz_user, p_sys->psz_passwd );
257 258
    }

259 260
    p_sys->b_reconnect = var_CreateGetBool( p_access, "http-reconnect" );

261
    /* Connect */
262
    if( Connect( p_access, 0 ) )
263
    {
264 265
        /* Retry with http 1.0 */
        p_sys->i_version = 0;
Gildas Bazin's avatar
 
Gildas Bazin committed
266

267 268
        if( p_access->b_die ||
            Connect( p_access, 0 ) )
269 270 271 272
        {
            goto error;
        }
    }
273

Gildas Bazin's avatar
Gildas Bazin committed
274 275
    if( ( p_sys->i_code == 301 || p_sys->i_code == 302 ||
          p_sys->i_code == 303 || p_sys->i_code == 307 ) &&
276
        p_sys->psz_location && *p_sys->psz_location )
277
    {
Gildas Bazin's avatar
Gildas Bazin committed
278 279
        playlist_t * p_playlist;

280
        msg_Dbg( p_access, "redirection to %s", p_sys->psz_location );
281

282 283
        p_playlist = vlc_object_find( p_access, VLC_OBJECT_PLAYLIST,
                                      FIND_ANYWHERE );
284
        if( !p_playlist )
285
        {
286
            msg_Err( p_access, "redirection failed: can't find playlist" );
287
            goto error;
288
        }
289 290
        p_playlist->pp_items[p_playlist->i_index]->b_autodeletion = VLC_TRUE;
        playlist_Add( p_playlist, p_sys->psz_location, p_sys->psz_location,
291
                      PLAYLIST_INSERT,
292 293
                      p_playlist->i_index + 1 );
        vlc_object_release( p_playlist );
294

295
        p_access->info.i_size = 0;  /* Force to stop reading */
296 297
    }

298
    if( p_sys->b_mms )
299 300 301 302 303
    {
        msg_Dbg( p_access, "This is actually a live mms server, BAIL" );
        goto error;
    }

304
    if( !strcmp( p_sys->psz_protocol, "ICY" ) )
305
    {
306
        if( p_sys->psz_mime && !strcasecmp( p_sys->psz_mime, "video/nsv" ) )
307
            p_access->psz_demux = strdup( "nsv" );
308 309 310 311
        else if( p_sys->psz_mime &&
                 ( !strcasecmp( p_sys->psz_mime, "audio/aac" ) ||
                   !strcasecmp( p_sys->psz_mime, "audio/aacp" ) ) )
            p_access->psz_demux = strdup( "m4a" );
312
        else
313
            p_access->psz_demux = strdup( "mp3" );
314

315 316
        msg_Info( p_access, "ICY server found, %s demuxer selected",
                  p_access->psz_demux );
317 318

        p_sys->b_pace_control = VLC_FALSE;
319
    }
320

321 322 323 324
    if( p_sys->b_reconnect ) msg_Dbg( p_access, "auto re-connect enabled" );

    /* PTS delay */
    var_Create( p_access, "http-caching", VLC_VAR_INTEGER |VLC_VAR_DOINHERIT );
325

326
    return VLC_SUCCESS;
327

328 329 330 331
error:
    vlc_UrlClean( &p_sys->url );
    vlc_UrlClean( &p_sys->proxy );
    if( p_sys->psz_mime ) free( p_sys->psz_mime );
332
    if( p_sys->psz_pragma ) free( p_sys->psz_pragma );
333 334 335 336
    if( p_sys->psz_location ) free( p_sys->psz_location );
    if( p_sys->psz_user_agent ) free( p_sys->psz_user_agent );
    if( p_sys->psz_user ) free( p_sys->psz_user );
    if( p_sys->psz_passwd ) free( p_sys->psz_passwd );
337

338
    if( p_sys->fd > 0 )
339
    {
340
        net_Close( p_sys->fd );
341
    }
342 343 344
    free( p_sys );
    return VLC_EGENERIC;
}
345

346 347 348 349 350
/*****************************************************************************
 * Close:
 *****************************************************************************/
static void Close( vlc_object_t *p_this )
{
351 352
    access_t     *p_access = (access_t*)p_this;
    access_sys_t *p_sys = p_access->p_sys;
353

354 355 356 357 358 359 360
    vlc_UrlClean( &p_sys->url );
    vlc_UrlClean( &p_sys->proxy );

    if( p_sys->psz_user ) free( p_sys->psz_user );
    if( p_sys->psz_passwd ) free( p_sys->psz_passwd );

    if( p_sys->psz_mime ) free( p_sys->psz_mime );
361
    if( p_sys->psz_pragma ) free( p_sys->psz_pragma );
362 363 364 365 366
    if( p_sys->psz_location ) free( p_sys->psz_location );

    if( p_sys->psz_user_agent ) free( p_sys->psz_user_agent );

    if( p_sys->fd > 0 )
367
    {
368
        net_Close( p_sys->fd );
369
    }
370 371 372 373 374 375 376
    free( p_sys );
}

/*****************************************************************************
 * Read: Read up to i_len bytes from the http connection and place in
 * p_buffer. Return the actual number of bytes read
 *****************************************************************************/
377
static int Read( access_t *p_access, uint8_t *p_buffer, int i_len )
378
{
379 380
    access_sys_t *p_sys = p_access->p_sys;
    int i_read;
381 382

    if( p_sys->fd < 0 )
383
    {
384
        p_access->info.b_eof = VLC_TRUE;
385
        return 0;
386
    }
387

388 389
    if( p_access->info.i_size > 0 &&
        i_len + p_access->info.i_pos > p_access->info.i_size )
390
    {
391
        if( ( i_len = p_access->info.i_size - p_access->info.i_pos ) == 0 )
392
        {
393
            p_access->info.b_eof = VLC_TRUE;
394 395
            return 0;
        }
396
    }
397 398 399 400
    if( p_sys->b_chunked )
    {
        if( p_sys->i_chunk < 0 )
        {
401
            p_access->info.b_eof = VLC_TRUE;
402 403 404 405 406
            return 0;
        }

        if( p_sys->i_chunk <= 0 )
        {
407
            char *psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd );
408 409 410
            /* read the chunk header */
            if( psz == NULL )
            {
411
                msg_Dbg( p_access, "failed reading chunk-header line" );
412 413 414 415 416 417 418 419
                return -1;
            }
            p_sys->i_chunk = strtoll( psz, NULL, 16 );
            free( psz );

            if( p_sys->i_chunk <= 0 )   /* eof */
            {
                p_sys->i_chunk = -1;
420
                p_access->info.b_eof = VLC_TRUE;
421 422 423 424 425 426 427 428 429 430
                return 0;
            }
        }

        if( i_len > p_sys->i_chunk )
        {
            i_len = p_sys->i_chunk;
        }
    }

431

432
    i_read = net_Read( p_access, p_sys->fd, p_buffer, i_len, VLC_FALSE );
433
    if( i_read > 0 )
434
    {
435
        p_access->info.i_pos += i_read;
436 437 438 439 440 441 442

        if( p_sys->b_chunked )
        {
            p_sys->i_chunk -= i_read;
            if( p_sys->i_chunk <= 0 )
            {
                /* read the empty line */
443
                char *psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd );
444
                if( psz ) free( psz );
445 446
            }
        }
447
    }
448 449
    else if( i_read == 0 )
    {
450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466
        if( p_sys->b_reconnect )
        {
            msg_Dbg( p_access, "got disconnected, trying to reconnect" );
            net_Close( p_sys->fd ); p_sys->fd = -1;
            if( Connect( p_access, p_access->info.i_pos ) )
            {
                msg_Dbg( p_access, "reconnection failed" );
            }
            else
            {
                p_sys->b_reconnect = VLC_FALSE;
                i_read = Read( p_access, p_buffer, i_len );
                p_sys->b_reconnect = VLC_TRUE;
            }
        }

        if( i_read == 0 ) p_access->info.b_eof = VLC_TRUE;
467
    }
468

469
    return i_read;
470 471
}

472 473 474 475 476 477 478 479 480 481 482 483 484 485
/*****************************************************************************
 * Seek: close and re-open a connection at the right place
 *****************************************************************************/
static int Seek( access_t *p_access, int64_t i_pos )
{
    access_sys_t *p_sys = p_access->p_sys;

    msg_Dbg( p_access, "trying to seek to "I64Fd, i_pos );

    net_Close( p_sys->fd ); p_sys->fd = -1;

    if( Connect( p_access, i_pos ) )
    {
        msg_Err( p_access, "seek failed" );
486
        p_access->info.b_eof = VLC_TRUE;
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
        return VLC_EGENERIC;
    }
    return VLC_SUCCESS;
}

/*****************************************************************************
 * Control:
 *****************************************************************************/
static int Control( access_t *p_access, int i_query, va_list args )
{
    access_sys_t *p_sys = p_access->p_sys;
    vlc_bool_t   *pb_bool;
    int          *pi_int;
    int64_t      *pi_64;

    switch( i_query )
    {
        /* */
        case ACCESS_CAN_SEEK:
            pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
            *pb_bool = p_sys->b_seekable;
            break;
        case ACCESS_CAN_FASTSEEK:
            pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
            *pb_bool = VLC_FALSE;
            break;
        case ACCESS_CAN_PAUSE:
        case ACCESS_CAN_CONTROL_PACE:
            pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
516
            *pb_bool = p_sys->b_pace_control;
517 518 519 520 521 522 523 524 525 526
            break;

        /* */
        case ACCESS_GET_MTU:
            pi_int = (int*)va_arg( args, int * );
            *pi_int = 0;
            break;

        case ACCESS_GET_PTS_DELAY:
            pi_64 = (int64_t*)va_arg( args, int64_t * );
527
            *pi_64 = (int64_t)var_GetInteger( p_access, "http-caching" ) * 1000;
528 529 530 531 532 533
            break;

        /* */
        case ACCESS_SET_PAUSE_STATE:
            break;

534 535 536
        case ACCESS_GET_TITLE_INFO:
        case ACCESS_SET_TITLE:
        case ACCESS_SET_SEEKPOINT:
537
        case ACCESS_SET_PRIVATE_ID_STATE:
538 539
            return VLC_EGENERIC;

540
        default:
541
            msg_Warn( p_access, "unimplemented query in control" );
542 543 544 545 546 547
            return VLC_EGENERIC;

    }
    return VLC_SUCCESS;
}

548
/*****************************************************************************
549
 * ParseURL: extract user:password
550
 *****************************************************************************/
551
static void ParseURL( access_sys_t *p_sys, char *psz_url )
552
{
553 554 555
    char *psz_dup = strdup( psz_url );
    char *p = psz_dup;
    char *psz;
556

557 558
    /* Syntax //[user:password]@<hostname>[:<port>][/<path>] */
    while( *p == '/' )
Sam Hocevar's avatar
Sam Hocevar committed
559
    {
560
        p++;
Sam Hocevar's avatar
Sam Hocevar committed
561
    }
562
    psz = p;
Sam Hocevar's avatar
Sam Hocevar committed
563

564 565
    /* Parse auth */
    if( ( p = strchr( psz, '@' ) ) )
Sam Hocevar's avatar
Sam Hocevar committed
566
    {
Gildas Bazin's avatar
Gildas Bazin committed
567 568
        char *comma;

569
        *p++ = '\0';
Gildas Bazin's avatar
Gildas Bazin committed
570 571
        comma = strchr( psz, ':' );

572 573
        /* Retreive user:password */
        if( comma )
Sam Hocevar's avatar
Sam Hocevar committed
574
        {
575
            *comma++ = '\0';
Sam Hocevar's avatar
Sam Hocevar committed
576

577 578
            p_sys->psz_user = strdup( psz );
            p_sys->psz_passwd = strdup( comma );
579
        }
580
        else
Sam Hocevar's avatar
Sam Hocevar committed
581
        {
582
            p_sys->psz_user = strdup( psz );
Sam Hocevar's avatar
Sam Hocevar committed
583 584
        }
    }
585 586 587 588 589 590 591
    else
    {
        p = psz;
    }

    /* Parse uri */
    vlc_UrlParse( &p_sys->url, p, 0 );
Sam Hocevar's avatar
Sam Hocevar committed
592

593
    free( psz_dup );
Sam Hocevar's avatar
Sam Hocevar committed
594
}
595

596
/*****************************************************************************
597
 * Connect:
598
 *****************************************************************************/
599
static int Connect( access_t *p_access, int64_t i_tell )
600
{
601
    access_sys_t   *p_sys = p_access->p_sys;
602 603
    vlc_url_t      srv = p_sys->b_proxy ? p_sys->proxy : p_sys->url;
    char           *psz;
604

605 606 607
    /* Clean info */
    if( p_sys->psz_location ) free( p_sys->psz_location );
    if( p_sys->psz_mime ) free( p_sys->psz_mime );
608
    if( p_sys->psz_pragma ) free( p_sys->psz_pragma );
609 610 611

    p_sys->psz_location = NULL;
    p_sys->psz_mime = NULL;
612
    p_sys->psz_pragma = NULL;
613
    p_sys->b_mms = VLC_FALSE;
614 615
    p_sys->b_chunked = VLC_FALSE;
    p_sys->i_chunk = 0;
616 617 618 619

    p_access->info.i_size = 0;
    p_access->info.i_pos  = i_tell;
    p_access->info.b_eof  = VLC_FALSE;
620 621 622


    /* Open connection */
623
    p_sys->fd = net_OpenTCP( p_access, srv.psz_host, srv.i_port );
624
    if( p_sys->fd < 0 )
625
    {
626
        msg_Err( p_access, "cannot connect to %s:%d", srv.psz_host, srv.i_port );
627
        return VLC_EGENERIC;
628 629
    }

630
    if( p_sys->b_proxy )
631
    {
632 633 634 635 636 637 638 639 640 641 642 643 644 645 646
        if( p_sys->url.psz_path )
        {
            net_Printf( VLC_OBJECT(p_access), p_sys->fd,
                        "GET http://%s:%d/%s HTTP/1.%d\r\n",
                        p_sys->url.psz_host, p_sys->url.i_port,
                        p_sys->url.psz_path, p_sys->i_version );
        }
        else
        {
            net_Printf( VLC_OBJECT(p_access), p_sys->fd,
                        "GET http://%s:%d/ HTTP/1.%d\r\n",
                        p_sys->url.psz_host, p_sys->url.i_port,
                        p_sys->i_version );

        }
647
    }
648
    else
649
    {
650 651 652 653 654
        char *psz_path = p_sys->url.psz_path;
        if( !psz_path || !*psz_path )
        {
            psz_path = "/";
        }
655 656 657 658 659 660 661 662 663 664 665 666 667
        if( p_sys->url.i_port != 80)
        {
            net_Printf( VLC_OBJECT(p_access), p_sys->fd,
                        "GET %s HTTP/1.%d\r\nHost: %s:%d\r\n",
                        psz_path, p_sys->i_version, p_sys->url.psz_host,
                        p_sys->url.i_port );
        }
        else
        {        
            net_Printf( VLC_OBJECT(p_access), p_sys->fd,
                        "GET %s HTTP/1.%d\r\nHost: %s\r\n",
                        psz_path, p_sys->i_version, p_sys->url.psz_host );
        }
668
    }
669
    /* User Agent */
670
    net_Printf( VLC_OBJECT(p_access), p_sys->fd, "User-Agent: %s\r\n",
Gildas Bazin's avatar
 
Gildas Bazin committed
671
                p_sys->psz_user_agent );
672 673
    /* Offset */
    if( p_sys->i_version == 1 )
674
    {
675
        net_Printf( VLC_OBJECT(p_access), p_sys->fd,
676
                    "Range: bytes="I64Fd"-\r\n", i_tell );
677
    }
678 679 680
    /* Authentification */
    if( p_sys->psz_user && *p_sys->psz_user )
    {
Gildas Bazin's avatar
Gildas Bazin committed
681
        char *buf;
682
        char *b64;
Gildas Bazin's avatar
Gildas Bazin committed
683

684
        asprintf( &buf, "%s:%s", p_sys->psz_user,
Gildas Bazin's avatar
Gildas Bazin committed
685
                   p_sys->psz_passwd ? p_sys->psz_passwd : "" );
686

687
        b64 = vlc_b64_encode( buf );
688

689
        net_Printf( VLC_OBJECT(p_access), p_sys->fd,
690
                    "Authorization: Basic %s\r\n", b64 );
691
        free( b64 );
692
    }
693
    net_Printf( VLC_OBJECT(p_access), p_sys->fd, "Connection: Close\r\n" );
694

695
    if( net_Printf( VLC_OBJECT(p_access), p_sys->fd, "\r\n" ) < 0 )
696
    {
697
        msg_Err( p_access, "failed to send request" );
698 699
        net_Close( p_sys->fd ); p_sys->fd = -1;
        return VLC_EGENERIC;
700 701
    }

702
    /* Read Answer */
703
    if( ( psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd ) ) == NULL )
704
    {
705
        msg_Err( p_access, "failed to read answer" );
706
        goto error;
707
    }
708
    if( !strncmp( psz, "HTTP/1.", 7 ) )
709
    {
710 711
        p_sys->psz_protocol = "HTTP";
        p_sys->i_code = atoi( &psz[9] );
712
    }
713
    else if( !strncmp( psz, "ICY", 3 ) )
714
    {
715 716
        p_sys->psz_protocol = "ICY";
        p_sys->i_code = atoi( &psz[4] );
717
        p_sys->b_reconnect = VLC_TRUE;
718
    }
719
    else
720
    {
721
        msg_Err( p_access, "invalid HTTP reply '%s'", psz );
722 723
        free( psz );
        goto error;
724
    }
725
    msg_Dbg( p_access, "protocol '%s' answer code %d",
Gildas Bazin's avatar
 
Gildas Bazin committed
726
             p_sys->psz_protocol, p_sys->i_code );
727
    if( !strcmp( p_sys->psz_protocol, "ICY" ) )
728
    {
729
        p_sys->b_seekable = VLC_FALSE;
730
    }
731
    if( p_sys->i_code != 206 )
732
    {
733
        p_sys->b_seekable = VLC_FALSE;
734
    }
735
    if( p_sys->i_code >= 400 )
736
    {
737
        msg_Err( p_access, "error: %s", psz );
738 739
        free( psz );
        goto error;
Sam Hocevar's avatar
Sam Hocevar committed
740
    }
741
    free( psz );
742

743
    for( ;; )
744
    {
745
        char *psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd );
746
        char *p;
747

748
        if( psz == NULL )
749
        {
750
            msg_Err( p_access, "failed to read answer" );
751
            goto error;
752
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
753

754
        /* msg_Dbg( p_input, "Line=%s", psz ); */
755
        if( *psz == '\0' )
756
        {
757 758
            free( psz );
            break;
759
        }
760

761 762

        if( ( p = strchr( psz, ':' ) ) == NULL )
763
        {
764
            msg_Err( p_access, "malformed header line: %s", psz );
765 766
            free( psz );
            goto error;
767
        }
768
        *p++ = '\0';
769
        while( *p == ' ' ) p++;
770

771
        if( !strcasecmp( psz, "Content-Length" ) )
772
        {
773 774
            p_access->info.i_size = i_tell + atoll( p );
            msg_Dbg( p_access, "stream size="I64Fd, p_access->info.i_size );
775
        }
776
        else if( !strcasecmp( psz, "Location" ) )
777
        {
778 779
            if( p_sys->psz_location ) free( p_sys->psz_location );
            p_sys->psz_location = strdup( p );