http.c 24.1 KB
Newer Older
1
/*****************************************************************************
gbazin's avatar
   
gbazin 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 * );

gbazin's avatar
   
gbazin committed
42
#define PROXY_TEXT N_("HTTP proxy")
gbazin's avatar
   
gbazin committed
43
#define PROXY_LONGTEXT N_( \
gbazin's avatar
   
gbazin 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")
gbazin's avatar
   
gbazin 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." )
gbazin's avatar
   
gbazin committed
52

gbazin's avatar
   
gbazin 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).")
gbazin's avatar
   
gbazin 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();
gbazin's avatar
   
gbazin committed
70
    set_description( _("HTTP input") );
71
    set_capability( "access2", 0 );
gbazin's avatar
   
gbazin 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 );
gbazin's avatar
   
gbazin 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 )
gbazin's avatar
   
gbazin 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",
gbazin's avatar
   
gbazin 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,
gbazin's avatar
   
gbazin 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'",
gbazin's avatar
   
gbazin 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;
gbazin's avatar
   
gbazin committed
266

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

gbazin's avatar
gbazin 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
    {
gbazin's avatar
gbazin 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
319
#if 0   /* Doesn't work really well because of the pre-buffering in shoutcast
         * servers (the buffer content will be sent as fast as possible). */
320
        p_sys->b_pace_control = VLC_FALSE;
321
#endif
322
    }
323

324
325
326
327
    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 );
328

329
    return VLC_SUCCESS;
330

331
332
333
334
error:
    vlc_UrlClean( &p_sys->url );
    vlc_UrlClean( &p_sys->proxy );
    if( p_sys->psz_mime ) free( p_sys->psz_mime );
335
    if( p_sys->psz_pragma ) free( p_sys->psz_pragma );
336
337
338
339
    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 );
340

341
    if( p_sys->fd > 0 )
342
    {
343
        net_Close( p_sys->fd );
344
    }
345
346
347
    free( p_sys );
    return VLC_EGENERIC;
}
348

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

357
358
359
360
361
362
363
    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 );
364
    if( p_sys->psz_pragma ) free( p_sys->psz_pragma );
365
366
367
368
369
    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 )
370
    {
371
        net_Close( p_sys->fd );
372
    }
373
374
375
376
377
378
379
    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
 *****************************************************************************/
380
static int Read( access_t *p_access, uint8_t *p_buffer, int i_len )
381
{
382
383
    access_sys_t *p_sys = p_access->p_sys;
    int i_read;
384
385

    if( p_sys->fd < 0 )
386
    {
387
        p_access->info.b_eof = VLC_TRUE;
388
        return 0;
389
    }
390

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

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

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

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

434

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

        if( p_sys->b_chunked )
        {
            p_sys->i_chunk -= i_read;
            if( p_sys->i_chunk <= 0 )
            {
                /* read the empty line */
446
                char *psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd );
447
                if( psz ) free( psz );
448
449
            }
        }
450
    }
451
452
    else if( i_read == 0 )
    {
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
        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;
470
    }
471

472
    return i_read;
473
474
}

475
476
477
478
479
480
481
482
483
484
485
486
487
488
/*****************************************************************************
 * 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" );
489
        p_access->info.b_eof = VLC_TRUE;
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
516
517
518
        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* );
519
            *pb_bool = p_sys->b_pace_control;
520
521
522
523
524
525
526
527
528
529
            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 * );
530
            *pi_64 = (int64_t)var_GetInteger( p_access, "http-caching" ) * 1000;
531
532
533
534
535
536
            break;

        /* */
        case ACCESS_SET_PAUSE_STATE:
            break;

537
538
539
        case ACCESS_GET_TITLE_INFO:
        case ACCESS_SET_TITLE:
        case ACCESS_SET_SEEKPOINT:
540
        case ACCESS_SET_PRIVATE_ID_STATE:
541
542
            return VLC_EGENERIC;

543
        default:
544
            msg_Warn( p_access, "unimplemented query in control" );
545
546
547
548
549
550
            return VLC_EGENERIC;

    }
    return VLC_SUCCESS;
}

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

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

567
568
    /* Parse auth */
    if( ( p = strchr( psz, '@' ) ) )
Sam Hocevar's avatar
Sam Hocevar committed
569
    {
gbazin's avatar
gbazin committed
570
571
        char *comma;

572
        *p++ = '\0';
gbazin's avatar
gbazin committed
573
574
        comma = strchr( psz, ':' );

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

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

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

596
    free( psz_dup );
Sam Hocevar's avatar
Sam Hocevar committed
597
}
598

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

608
609
610
    /* Clean info */
    if( p_sys->psz_location ) free( p_sys->psz_location );
    if( p_sys->psz_mime ) free( p_sys->psz_mime );
611
    if( p_sys->psz_pragma ) free( p_sys->psz_pragma );
612
613
614

    p_sys->psz_location = NULL;
    p_sys->psz_mime = NULL;
615
    p_sys->psz_pragma = NULL;
616
    p_sys->b_mms = VLC_FALSE;
617
618
    p_sys->b_chunked = VLC_FALSE;
    p_sys->i_chunk = 0;
619
620
621
622

    p_access->info.i_size = 0;
    p_access->info.i_pos  = i_tell;
    p_access->info.b_eof  = VLC_FALSE;
623
624
625


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

633
    if( p_sys->b_proxy )
634
    {
635
636
637
        if( p_sys->url.psz_path )
        {
            net_Printf( VLC_OBJECT(p_access), p_sys->fd,
Sam Hocevar's avatar
Sam Hocevar committed
638
                        "GET http://%s:%d%s HTTP/1.%d\r\n",
639
640
641
642
643
644
645
646
647
648
                        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 );
        }
649
    }
650
    else
651
    {
652
653
654
655
656
        char *psz_path = p_sys->url.psz_path;
        if( !psz_path || !*psz_path )
        {
            psz_path = "/";
        }
657
658
659
660
661
662
663
664
665
666
667
668
669
        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 );
        }
670
    }
671
    /* User Agent */
672
    net_Printf( VLC_OBJECT(p_access), p_sys->fd, "User-Agent: %s\r\n",
gbazin's avatar
   
gbazin committed
673
                p_sys->psz_user_agent );
674
675
    /* Offset */
    if( p_sys->i_version == 1 )
676
    {
677
        net_Printf( VLC_OBJECT(p_access), p_sys->fd,
678
                    "Range: bytes="I64Fd"-\r\n", i_tell );
679
    }
680
681
682
    /* Authentification */
    if( p_sys->psz_user && *p_sys->psz_user )
    {
gbazin's avatar
gbazin committed
683
        char *buf;
684
        char *b64;
gbazin's avatar
gbazin committed
685

686
        asprintf( &buf, "%s:%s", p_sys->psz_user,
gbazin's avatar
gbazin committed
687
                   p_sys->psz_passwd ? p_sys->psz_passwd : "" );
688

689
        b64 = vlc_b64_encode( buf );
690

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

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

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

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

750
        if( psz == NULL )
751
        {
752
            msg_Err( p_access, "failed to read answer" );
753
            goto error;
754
        }
gbazin's avatar
   
gbazin committed
755

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

763
764

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

773
        if( !strcasecmp( psz, "Content-Length" ) )
774
        {
775
776
            p_access->info.i_size = i_tell + atoll( p );
            msg_Dbg( p_access, "stream size="I64Fd, p_access->info.i_size );
777
        }
778
        else if( !strcasecmp( psz, "Location" ) )
779
        {
780
781
            if( p_sys->psz_location ) free( p_sys->psz_location );
            p_sys->psz_location = strdup( p );
782
        }
783
        else if( !strcasecmp( psz, "Content-Type" ) )
784
        {
785
786
            if( p_sys->psz_mime ) free( p_sys->psz_mime );
            p_sys->psz_mime = strdup( p );
787
            msg_Dbg( p_access, "Content-Type: %s", p_sys->psz_mime );
788
        }
789
790
        else if( !strcasecmp( psz, "Pragma" ) )
        {
791
792
            if( !strcasecmp( psz, "Pragma: features" ) )
            	p_sys->b_mms = VLC_TRUE;
793
794
795
796
            if( p_sys->psz_pragma ) free( p_sys->psz_pragma );
            p_sys->psz_pragma = strdup( p );
            msg_Dbg( p_access, "Pragma: %s", p_sys->psz_pragma );
        }
797
798
799
800
        else if( !strcasecmp( psz, "Server" ) &&
                 !strncasecmp( p, "Icecast", 7 ) )
        {
            p_sys->b_reconnect = VLC_TRUE;
801
            p_sys->b_pace_control = VLC_FALSE;
802
803
            msg_Dbg( p_access, "Server: %s", p );
        }
804
805
        else if( !strcasecmp( psz, "Transfer-Encoding" ) )
        {
806
            msg_Dbg( p_access, "Transfer-Encoding: %s", p );
807
808
809
810
811
            if( !strncasecmp( p, "chunked", 7 ) )
            {
                p_sys->b_chunked = VLC_TRUE;
            }
        }
812

813
        free( psz );
814
    }
815
    return VLC_SUCCESS;
816

817
818
819
error:
    net_Close( p_sys->fd ); p_sys->fd = -1;
    return VLC_EGENERIC;
820
821
}