]> git.sesse.net Git - vlc/blob - modules/access/http.c
Remove bogus printf
[vlc] / modules / access / http.c
1 /*****************************************************************************
2  * http.c: HTTP input module
3  *****************************************************************************
4  * Copyright (C) 2001-2005 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *          Christophe Massiot <massiot@via.ecp.fr>
9  *          RĂ©mi Denis-Courmont <rem # videolan.org>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <vlc/vlc.h>
30
31
32 #include <vlc_access.h>
33
34 #include <vlc_interface.h>
35 #include <vlc_meta.h>
36 #include <vlc_network.h>
37 #include <vlc_url.h>
38 #include <vlc_tls.h>
39 #include <vlc_strings.h>
40 #include <vlc_input.h>
41
42 #ifdef HAVE_ZLIB_H
43 #   include <zlib.h>
44 #endif
45
46 /*****************************************************************************
47  * Module descriptor
48  *****************************************************************************/
49 static int  Open ( vlc_object_t * );
50 static void Close( vlc_object_t * );
51
52 #define PROXY_TEXT N_("HTTP proxy")
53 #define PROXY_LONGTEXT N_( \
54     "HTTP proxy to be used It must be of the form " \
55     "http://[user[:pass]@]myproxy.mydomain:myport/ ; " \
56     "if empty, the http_proxy environment variable will be tried." )
57
58 #define CACHING_TEXT N_("Caching value in ms")
59 #define CACHING_LONGTEXT N_( \
60     "Caching value for HTTP streams. This " \
61     "value should be set in milliseconds." )
62
63 #define AGENT_TEXT N_("HTTP user agent")
64 #define AGENT_LONGTEXT N_("User agent that will be " \
65     "used for the connection.")
66
67 #define RECONNECT_TEXT N_("Auto re-connect")
68 #define RECONNECT_LONGTEXT N_( \
69     "Automatically try to reconnect to the stream in case of a sudden " \
70     "disconnect." )
71
72 #define CONTINUOUS_TEXT N_("Continuous stream")
73 #define CONTINUOUS_LONGTEXT N_("Read a file that is " \
74     "being constantly updated (for example, a JPG file on a server). " \
75     "You should not globally enable this option as it will break all other " \
76     "types of HTTP streams." )
77
78 #define FORWARD_COOKIES_TEXT N_("Forward Cookies")
79 #define FORWARD_COOKIES_LONGTEXT N_("Forward Cookies Across http redirections ")
80
81 vlc_module_begin();
82     set_description( _("HTTP input") );
83     set_capability( "access2", 0 );
84     set_shortname( _( "HTTP(S)" ) );
85     set_category( CAT_INPUT );
86     set_subcategory( SUBCAT_INPUT_ACCESS );
87
88     add_string( "http-proxy", NULL, NULL, PROXY_TEXT, PROXY_LONGTEXT,
89                 VLC_FALSE );
90     add_integer( "http-caching", 4 * DEFAULT_PTS_DELAY / 1000, NULL,
91                  CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
92     add_string( "http-user-agent", COPYRIGHT_MESSAGE , NULL, AGENT_TEXT,
93                 AGENT_LONGTEXT, VLC_TRUE );
94     add_bool( "http-reconnect", 0, NULL, RECONNECT_TEXT,
95               RECONNECT_LONGTEXT, VLC_TRUE );
96     add_bool( "http-continuous", 0, NULL, CONTINUOUS_TEXT,
97               CONTINUOUS_LONGTEXT, VLC_TRUE );
98     add_bool( "http-forward-cookies", 0, NULL, FORWARD_COOKIES_TEXT,
99               FORWARD_COOKIES_LONGTEXT, VLC_TRUE );
100     add_obsolete_string("http-user");
101     add_obsolete_string("http-pwd");
102     add_shortcut( "http" );
103     add_shortcut( "https" );
104     add_shortcut( "unsv" );
105     add_shortcut( "itpc" ); /* iTunes Podcast */
106     set_callbacks( Open, Close );
107 vlc_module_end();
108
109 /*****************************************************************************
110  * Local prototypes
111  *****************************************************************************/
112 struct access_sys_t
113 {
114     int fd;
115     tls_session_t *p_tls;
116     v_socket_t    *p_vs;
117
118     /* From uri */
119     vlc_url_t url;
120     char    *psz_user_agent;
121
122     /* Proxy */
123     vlc_bool_t b_proxy;
124     vlc_url_t  proxy;
125
126     /* */
127     int        i_code;
128     const char *psz_protocol;
129     int        i_version;
130
131     char       *psz_mime;
132     char       *psz_pragma;
133     char       *psz_location;
134     vlc_bool_t b_mms;
135     vlc_bool_t b_icecast;
136     vlc_bool_t b_ssl;
137 #ifdef HAVE_ZLIB_H
138     vlc_bool_t b_compressed;
139     struct
140     {
141         z_stream   stream;
142         uint8_t   *p_buffer;
143     } inflate;
144 #endif
145
146     vlc_bool_t b_chunked;
147     int64_t    i_chunk;
148
149     int        i_icy_meta;
150     char       *psz_icy_name;
151     char       *psz_icy_genre;
152     char       *psz_icy_title;
153
154     int i_remaining;
155
156     vlc_bool_t b_seekable;
157     vlc_bool_t b_reconnect;
158     vlc_bool_t b_continuous;
159     vlc_bool_t b_pace_control;
160
161     vlc_array_t * cookies;
162 };
163
164 /* */
165 static int OpenWithCookies( vlc_object_t *p_this, vlc_array_t *cookies );
166
167 /* */
168 static ssize_t Read( access_t *, uint8_t *, size_t );
169 static ssize_t ReadCompressed( access_t *, uint8_t *, size_t );
170 static int Seek( access_t *, int64_t );
171 static int Control( access_t *, int, va_list );
172
173 /* */
174 static int Connect( access_t *, int64_t );
175 static int Request( access_t *p_access, int64_t i_tell );
176 static void Disconnect( access_t * );
177
178 /* Small Cookie utilities. Cookies support is partial. */
179 static char * cookie_get_content( const char * cookie );
180 static char * cookie_get_domain( const char * cookie );
181 static char * cookie_get_name( const char * cookie );
182 static void cookie_append( vlc_array_t * cookies, char * cookie );
183
184 /*****************************************************************************
185  * Open:
186  *****************************************************************************/
187 static int Open( vlc_object_t *p_this )
188 {
189     return OpenWithCookies( p_this, NULL );
190 }
191
192 static int OpenWithCookies( vlc_object_t *p_this, vlc_array_t *cookies )
193 {
194     access_t     *p_access = (access_t*)p_this;
195     access_sys_t *p_sys;
196     char         *psz, *p;
197     /* Only forward an store cookies if the corresponding option is activated */
198     vlc_bool_t   b_forward_cookies = var_CreateGetBool( p_access, "http-forward-cookies" );
199     vlc_array_t * saved_cookies = b_forward_cookies ? (cookies ?: vlc_array_new()) : NULL;
200
201     /* Set up p_access */
202     STANDARD_READ_ACCESS_INIT;
203 #ifdef HAVE_ZLIB_H
204     p_access->pf_read = ReadCompressed;
205 #endif
206     p_sys->fd = -1;
207     p_sys->b_proxy = VLC_FALSE;
208     p_sys->i_version = 1;
209     p_sys->b_seekable = VLC_TRUE;
210     p_sys->psz_mime = NULL;
211     p_sys->psz_pragma = NULL;
212     p_sys->b_mms = VLC_FALSE;
213     p_sys->b_icecast = VLC_FALSE;
214     p_sys->psz_location = NULL;
215     p_sys->psz_user_agent = NULL;
216     p_sys->b_pace_control = VLC_TRUE;
217     p_sys->b_ssl = VLC_FALSE;
218 #ifdef HAVE_ZLIB_H
219     p_sys->b_compressed = VLC_FALSE;
220     /* 15 is the max windowBits, +32 to enable optional gzip decoding */
221     if( inflateInit2( &p_sys->inflate.stream, 32+15 ) != Z_OK )
222         msg_Warn( p_access, "Error during zlib initialisation: %s",
223                   p_sys->inflate.stream.msg );
224     if( zlibCompileFlags() & (1<<17) )
225         msg_Warn( p_access, "Your zlib was compiled without gzip support." );
226     p_sys->inflate.p_buffer = NULL;
227 #endif
228     p_sys->p_tls = NULL;
229     p_sys->p_vs = NULL;
230     p_sys->i_icy_meta = 0;
231     p_sys->psz_icy_name = NULL;
232     p_sys->psz_icy_genre = NULL;
233     p_sys->psz_icy_title = NULL;
234     p_sys->i_remaining = 0;
235
236     p_sys->cookies = saved_cookies;
237
238     /* Parse URI - remove spaces */
239     p = psz = strdup( p_access->psz_path );
240     while( (p = strchr( p, ' ' )) != NULL )
241         *p = '+';
242     vlc_UrlParse( &p_sys->url, psz, 0 );
243     free( psz );
244
245     if( p_sys->url.psz_host == NULL || *p_sys->url.psz_host == '\0' )
246     {
247         msg_Warn( p_access, "invalid host" );
248         goto error;
249     }
250     if( !strncmp( p_access->psz_access, "https", 5 ) )
251     {
252         /* HTTP over SSL */
253         p_sys->b_ssl = VLC_TRUE;
254         if( p_sys->url.i_port <= 0 )
255             p_sys->url.i_port = 443;
256     }
257     else
258     {
259         if( p_sys->url.i_port <= 0 )
260             p_sys->url.i_port = 80;
261     }
262
263     /* Do user agent */
264     p_sys->psz_user_agent = var_CreateGetString( p_access, "http-user-agent" );
265
266     /* Check proxy */
267     psz = var_CreateGetString( p_access, "http-proxy" );
268     if( *psz )
269     {
270         p_sys->b_proxy = VLC_TRUE;
271         vlc_UrlParse( &p_sys->proxy, psz, 0 );
272     }
273 #ifdef HAVE_GETENV
274     else
275     {
276         char *psz_proxy = getenv( "http_proxy" );
277         if( psz_proxy && *psz_proxy )
278         {
279             p_sys->b_proxy = VLC_TRUE;
280             vlc_UrlParse( &p_sys->proxy, psz_proxy, 0 );
281         }
282     }
283 #endif
284     free( psz );
285
286     if( p_sys->b_proxy )
287     {
288         if( p_sys->proxy.psz_host == NULL || *p_sys->proxy.psz_host == '\0' )
289         {
290             msg_Warn( p_access, "invalid proxy host" );
291             goto error;
292         }
293         if( p_sys->proxy.i_port <= 0 )
294         {
295             p_sys->proxy.i_port = 80;
296         }
297     }
298
299     msg_Dbg( p_access, "http: server='%s' port=%d file='%s",
300              p_sys->url.psz_host, p_sys->url.i_port, p_sys->url.psz_path );
301     if( p_sys->b_proxy )
302     {
303         msg_Dbg( p_access, "      proxy %s:%d", p_sys->proxy.psz_host,
304                  p_sys->proxy.i_port );
305     }
306     if( p_sys->url.psz_username && *p_sys->url.psz_username )
307     {
308         msg_Dbg( p_access, "      user='%s', pwd='%s'",
309                  p_sys->url.psz_username, p_sys->url.psz_password );
310     }
311
312     p_sys->b_reconnect = var_CreateGetBool( p_access, "http-reconnect" );
313     p_sys->b_continuous = var_CreateGetBool( p_access, "http-continuous" );
314
315 connect:
316     /* Connect */
317     switch( Connect( p_access, 0 ) )
318     {
319         case -1:
320             goto error;
321
322         case -2:
323             /* Retry with http 1.0 */
324             msg_Dbg( p_access, "switching to HTTP version 1.0" );
325             p_sys->i_version = 0;
326             p_sys->b_seekable = VLC_FALSE;
327
328             if( p_access->b_die || Connect( p_access, 0 ) )
329                 goto error;
330
331 #ifdef DEBUG
332         case 0:
333             break;
334
335         default:
336             msg_Err( p_access, "You should not be here" );
337             abort();
338 #endif
339     }
340
341     if( p_sys->i_code == 401 )
342     {
343         char *psz_login = NULL; char *psz_password = NULL;
344         int i_ret;
345         msg_Dbg( p_access, "authentication failed" );
346         i_ret = intf_UserLoginPassword( p_access, _("HTTP authentication"),
347                         _("Please enter a valid login name and a password."),
348                                                 &psz_login, &psz_password );
349         if( i_ret == DIALOG_OK_YES )
350         {
351             msg_Dbg( p_access, "retrying with user=%s, pwd=%s",
352                         psz_login, psz_password );
353             if( psz_login ) p_sys->url.psz_username = strdup( psz_login );
354             if( psz_password ) p_sys->url.psz_password = strdup( psz_password );
355             if( psz_login ) free( psz_login );
356             if( psz_password ) free( psz_password );
357             goto connect;
358         }
359         else
360         {
361             if( psz_login ) free( psz_login );
362             if( psz_password ) free( psz_password );
363             goto error;
364         }
365     }
366
367     if( ( p_sys->i_code == 301 || p_sys->i_code == 302 ||
368           p_sys->i_code == 303 || p_sys->i_code == 307 ) &&
369         p_sys->psz_location && *p_sys->psz_location )
370     {
371         msg_Dbg( p_access, "redirection to %s", p_sys->psz_location );
372
373         /* Do not accept redirection outside of HTTP works */
374         if( strncmp( p_sys->psz_location, "http", 4 )
375          || ( ( p_sys->psz_location[4] != ':' ) /* HTTP */
376            && strncmp( p_sys->psz_location + 4, "s:", 2 ) /* HTTP/SSL */ ) )
377         {
378             msg_Err( p_access, "insecure redirection ignored" );
379             goto error;
380         }
381         free( p_access->psz_path );
382         p_access->psz_path = strdup( p_sys->psz_location );
383         /* Clean up current Open() run */
384         vlc_UrlClean( &p_sys->url );
385         vlc_UrlClean( &p_sys->proxy );
386         free( p_sys->psz_mime );
387         free( p_sys->psz_pragma );
388         free( p_sys->psz_location );
389         free( p_sys->psz_user_agent );
390
391         Disconnect( p_access );
392         cookies = p_sys->cookies;
393         free( p_sys );
394
395         /* Do new Open() run with new data */
396         return OpenWithCookies( p_this, cookies );
397     }
398
399     if( p_sys->b_mms )
400     {
401         msg_Dbg( p_access, "this is actually a live mms server, BAIL" );
402         goto error;
403     }
404
405     if( !strcmp( p_sys->psz_protocol, "ICY" ) || p_sys->b_icecast )
406     {
407         if( p_sys->psz_mime && strcasecmp( p_sys->psz_mime, "application/ogg" ) )
408         {
409             if( !strcasecmp( p_sys->psz_mime, "video/nsv" ) ||
410                 !strcasecmp( p_sys->psz_mime, "video/nsa" ) )
411                 p_access->psz_demux = strdup( "nsv" );
412             else if( !strcasecmp( p_sys->psz_mime, "audio/aac" ) ||
413                      !strcasecmp( p_sys->psz_mime, "audio/aacp" ) )
414                 p_access->psz_demux = strdup( "m4a" );
415             else if( !strcasecmp( p_sys->psz_mime, "audio/mpeg" ) )
416                 p_access->psz_demux = strdup( "mp3" );
417
418             msg_Info( p_access, "Raw-audio server found, %s demuxer selected",
419                       p_access->psz_demux );
420
421 #if 0       /* Doesn't work really well because of the pre-buffering in
422              * shoutcast servers (the buffer content will be sent as fast as
423              * possible). */
424             p_sys->b_pace_control = VLC_FALSE;
425 #endif
426         }
427         else if( !p_sys->psz_mime )
428         {
429              /* Shoutcast */
430              p_access->psz_demux = strdup( "mp3" );
431         }
432         /* else probably Ogg Vorbis */
433     }
434     else if( !strcasecmp( p_access->psz_access, "unsv" ) &&
435              p_sys->psz_mime &&
436              !strcasecmp( p_sys->psz_mime, "misc/ultravox" ) )
437     {
438         /* Grrrr! detect ultravox server and force NSV demuxer */
439         p_access->psz_demux = strdup( "nsv" );
440     }
441     else if( !strcmp( p_access->psz_access, "itpc" ) )
442     {
443         p_access->psz_demux = strdup( "podcast" );
444     }
445     else if( p_sys->psz_mime &&
446              !strncasecmp( p_sys->psz_mime, "application/xspf+xml", 20 ) &&
447              ( memchr( " ;\t", p_sys->psz_mime[20], 4 ) != NULL ) )
448         p_access->psz_demux = strdup( "xspf-open" );
449
450     if( p_sys->b_reconnect ) msg_Dbg( p_access, "auto re-connect enabled" );
451
452     /* PTS delay */
453     var_Create( p_access, "http-caching", VLC_VAR_INTEGER |VLC_VAR_DOINHERIT );
454
455     return VLC_SUCCESS;
456
457 error:
458     vlc_UrlClean( &p_sys->url );
459     vlc_UrlClean( &p_sys->proxy );
460     free( p_sys->psz_mime );
461     free( p_sys->psz_pragma );
462     free( p_sys->psz_location );
463     free( p_sys->psz_user_agent );
464
465     Disconnect( p_access );
466     free( p_sys );
467     return VLC_EGENERIC;
468 }
469
470 /*****************************************************************************
471  * Close:
472  *****************************************************************************/
473 static void Close( vlc_object_t *p_this )
474 {
475     access_t     *p_access = (access_t*)p_this;
476     access_sys_t *p_sys = p_access->p_sys;
477
478     vlc_UrlClean( &p_sys->url );
479     vlc_UrlClean( &p_sys->proxy );
480
481     free( p_sys->psz_mime );
482     free( p_sys->psz_pragma );
483     free( p_sys->psz_location );
484
485     free( p_sys->psz_icy_name );
486     free( p_sys->psz_icy_genre );
487     free( p_sys->psz_icy_title );
488
489     free( p_sys->psz_user_agent );
490
491     Disconnect( p_access );
492
493     if( p_sys->cookies )
494     {
495         int i;
496         for( i = 0; i < vlc_array_count( p_sys->cookies ); i++ )
497             free(vlc_array_item_at_index( p_sys->cookies, i ));
498         vlc_array_destroy( p_sys->cookies );
499     }
500
501 #ifdef HAVE_ZLIB_H
502     inflateEnd( &p_sys->inflate.stream );
503     free( p_sys->inflate.p_buffer );
504 #endif
505
506     free( p_sys );
507 }
508
509 /*****************************************************************************
510  * Read: Read up to i_len bytes from the http connection and place in
511  * p_buffer. Return the actual number of bytes read
512  *****************************************************************************/
513 static int ReadICYMeta( access_t *p_access );
514 static ssize_t Read( access_t *p_access, uint8_t *p_buffer, size_t i_len )
515 {
516     access_sys_t *p_sys = p_access->p_sys;
517     int i_read;
518
519     if( p_sys->fd < 0 )
520     {
521         p_access->info.b_eof = VLC_TRUE;
522         return 0;
523     }
524
525     if( p_access->info.i_size > 0 &&
526         i_len + p_access->info.i_pos > p_access->info.i_size )
527     {
528         if( ( i_len = p_access->info.i_size - p_access->info.i_pos ) == 0 )
529         {
530             p_access->info.b_eof = VLC_TRUE;
531             return 0;
532         }
533     }
534
535     if( p_sys->b_chunked )
536     {
537         if( p_sys->i_chunk < 0 )
538         {
539             p_access->info.b_eof = VLC_TRUE;
540             return 0;
541         }
542
543         if( p_sys->i_chunk <= 0 )
544         {
545             char *psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, p_sys->p_vs );
546             /* read the chunk header */
547             if( psz == NULL )
548             {
549                 /* fatal error - end of file */
550                 msg_Dbg( p_access, "failed reading chunk-header line" );
551                 return 0;
552             }
553             p_sys->i_chunk = strtoll( psz, NULL, 16 );
554             free( psz );
555
556             if( p_sys->i_chunk <= 0 )   /* eof */
557             {
558                 p_sys->i_chunk = -1;
559                 p_access->info.b_eof = VLC_TRUE;
560                 return 0;
561             }
562         }
563
564         if( i_len > p_sys->i_chunk )
565         {
566             i_len = p_sys->i_chunk;
567         }
568     }
569
570     if( p_sys->b_continuous && (ssize_t)i_len > p_sys->i_remaining )
571     {
572         /* Only ask for the remaining length */
573         int i_new_len = p_sys->i_remaining;
574         if( i_new_len == 0 )
575         {
576             Request( p_access, 0 );
577             i_read = Read( p_access, p_buffer, i_len );
578             return i_read;
579         }
580         i_len = i_new_len;
581     }
582
583     if( p_sys->i_icy_meta > 0 && p_access->info.i_pos > 0 )
584     {
585         int64_t i_next = p_sys->i_icy_meta -
586                                     p_access->info.i_pos % p_sys->i_icy_meta;
587
588         if( i_next == p_sys->i_icy_meta )
589         {
590             if( ReadICYMeta( p_access ) )
591             {
592                 p_access->info.b_eof = VLC_TRUE;
593                 return -1;
594             }
595         }
596         if( i_len > i_next )
597             i_len = i_next;
598     }
599
600     i_read = net_Read( p_access, p_sys->fd, p_sys->p_vs, p_buffer, i_len, VLC_FALSE );
601
602     if( i_read > 0 )
603     {
604         p_access->info.i_pos += i_read;
605
606         if( p_sys->b_chunked )
607         {
608             p_sys->i_chunk -= i_read;
609             if( p_sys->i_chunk <= 0 )
610             {
611                 /* read the empty line */
612                 char *psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, p_sys->p_vs );
613                 if( psz ) free( psz );
614             }
615         }
616     }
617     else if( i_read == 0 )
618     {
619         /*
620          * I very much doubt that this will work.
621          * If i_read == 0, the connection *IS* dead, so the only
622          * sensible thing to do is Disconnect() and then retry.
623          * Otherwise, I got recv() completely wrong. -- Courmisch
624          */
625         if( p_sys->b_continuous )
626         {
627             Request( p_access, 0 );
628             p_sys->b_continuous = VLC_FALSE;
629             i_read = Read( p_access, p_buffer, i_len );
630             p_sys->b_continuous = VLC_TRUE;
631         }
632         Disconnect( p_access );
633         if( p_sys->b_reconnect )
634         {
635             msg_Dbg( p_access, "got disconnected, trying to reconnect" );
636             if( Connect( p_access, p_access->info.i_pos ) )
637             {
638                 msg_Dbg( p_access, "reconnection failed" );
639             }
640             else
641             {
642                 p_sys->b_reconnect = VLC_FALSE;
643                 i_read = Read( p_access, p_buffer, i_len );
644                 p_sys->b_reconnect = VLC_TRUE;
645             }
646         }
647
648         if( i_read == 0 ) p_access->info.b_eof = VLC_TRUE;
649     }
650
651     if( p_sys->b_continuous )
652     {
653         p_sys->i_remaining -= i_read;
654     }
655
656     return i_read;
657 }
658
659 static int ReadICYMeta( access_t *p_access )
660 {
661     access_sys_t *p_sys = p_access->p_sys;
662
663     uint8_t buffer;
664     char *p, *psz_meta;
665     int i_read;
666
667     /* Read meta data length */
668     i_read = net_Read( p_access, p_sys->fd, p_sys->p_vs, &buffer, 1,
669                        VLC_TRUE );
670     if( i_read <= 0 )
671         return VLC_EGENERIC;
672     if( buffer == 0 )
673         return VLC_SUCCESS;
674
675     i_read = buffer << 4;
676     /* msg_Dbg( p_access, "ICY meta size=%u", i_read); */
677
678     psz_meta = malloc( i_read + 1 );
679     if( net_Read( p_access, p_sys->fd, p_sys->p_vs,
680                   (uint8_t *)psz_meta, i_read, VLC_TRUE ) != i_read )
681         return VLC_EGENERIC;
682
683     psz_meta[i_read] = '\0'; /* Just in case */
684
685     /* msg_Dbg( p_access, "icy-meta=%s", psz_meta ); */
686
687     /* Now parse the meta */
688     /* Look for StreamTitle= */
689     p = strcasestr( (char *)psz_meta, "StreamTitle=" );
690     if( p )
691     {
692         p += strlen( "StreamTitle=" );
693         if( *p == '\'' || *p == '"' )
694         {
695             char closing[] = { p[0], ';', '\0' };
696             char *psz = strstr( &p[1], closing );
697             if( !psz )
698                 psz = strchr( &p[1], ';' );
699
700             if( psz ) *psz = '\0';
701         }
702         else
703         {
704             char *psz = strchr( &p[1], ';' );
705             if( psz ) *psz = '\0';
706         }
707
708         if( !p_sys->psz_icy_title ||
709             strcmp( p_sys->psz_icy_title, &p[1] ) )
710         {
711             if( p_sys->psz_icy_title )
712                 free( p_sys->psz_icy_title );
713             p_sys->psz_icy_title = strdup( &p[1] );
714             p_access->info.i_update |= INPUT_UPDATE_META;
715
716             msg_Dbg( p_access, "New Title=%s", p_sys->psz_icy_title );
717         }
718     }
719     free( psz_meta );
720
721     return VLC_SUCCESS;
722 }
723
724 #ifdef HAVE_ZLIB_H
725 static ssize_t ReadCompressed( access_t *p_access, uint8_t *p_buffer,
726                                size_t i_len )
727 {
728     access_sys_t *p_sys = p_access->p_sys;
729
730     if( p_sys->b_compressed )
731     {
732         int i_ret;
733
734         if( !p_sys->inflate.p_buffer )
735             p_sys->inflate.p_buffer = malloc( 256 * 1024 );
736
737         if( p_sys->inflate.stream.avail_in == 0 )
738         {
739             ssize_t i_read = Read( p_access, p_sys->inflate.p_buffer + p_sys->inflate.stream.avail_in, 256 * 1024 );
740             if( i_read <= 0 ) return i_read;
741             p_sys->inflate.stream.next_in = p_sys->inflate.p_buffer;
742             p_sys->inflate.stream.avail_in = i_read;
743         }
744
745         p_sys->inflate.stream.avail_out = i_len;
746         p_sys->inflate.stream.next_out = p_buffer;
747
748         i_ret = inflate( &p_sys->inflate.stream, Z_SYNC_FLUSH );
749         msg_Warn( p_access, "inflate return value: %d, %s", i_ret, p_sys->inflate.stream.msg );
750
751         return i_len - p_sys->inflate.stream.avail_out;
752     }
753     else
754     {
755         return Read( p_access, p_buffer, i_len );
756     }
757 }
758 #endif
759
760 /*****************************************************************************
761  * Seek: close and re-open a connection at the right place
762  *****************************************************************************/
763 static int Seek( access_t *p_access, int64_t i_pos )
764 {
765     msg_Dbg( p_access, "trying to seek to "I64Fd, i_pos );
766
767     Disconnect( p_access );
768
769     if( Connect( p_access, i_pos ) )
770     {
771         msg_Err( p_access, "seek failed" );
772         p_access->info.b_eof = VLC_TRUE;
773         return VLC_EGENERIC;
774     }
775     return VLC_SUCCESS;
776 }
777
778 /*****************************************************************************
779  * Control:
780  *****************************************************************************/
781 static int Control( access_t *p_access, int i_query, va_list args )
782 {
783     access_sys_t *p_sys = p_access->p_sys;
784     vlc_bool_t   *pb_bool;
785     int          *pi_int;
786     int64_t      *pi_64;
787     vlc_meta_t   *p_meta;
788
789     switch( i_query )
790     {
791         /* */
792         case ACCESS_CAN_SEEK:
793             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
794             *pb_bool = p_sys->b_seekable;
795             break;
796         case ACCESS_CAN_FASTSEEK:
797             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
798             *pb_bool = VLC_FALSE;
799             break;
800         case ACCESS_CAN_PAUSE:
801         case ACCESS_CAN_CONTROL_PACE:
802             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
803
804 #if 0       /* Disable for now until we have a clock synchro algo
805              * which works with something else than MPEG over UDP */
806             *pb_bool = p_sys->b_pace_control;
807 #endif
808             *pb_bool = VLC_TRUE;
809             break;
810
811         /* */
812         case ACCESS_GET_MTU:
813             pi_int = (int*)va_arg( args, int * );
814             *pi_int = 0;
815             break;
816
817         case ACCESS_GET_PTS_DELAY:
818             pi_64 = (int64_t*)va_arg( args, int64_t * );
819             *pi_64 = (int64_t)var_GetInteger( p_access, "http-caching" ) * 1000;
820             break;
821
822         /* */
823         case ACCESS_SET_PAUSE_STATE:
824             break;
825
826         case ACCESS_GET_META:
827             p_meta = (vlc_meta_t*)va_arg( args, vlc_meta_t* );
828
829             if( p_sys->psz_icy_name )
830                 vlc_meta_Set( p_meta, vlc_meta_Title, p_sys->psz_icy_name );
831             if( p_sys->psz_icy_genre )
832                 vlc_meta_Set( p_meta, vlc_meta_Genre, p_sys->psz_icy_genre );
833             if( p_sys->psz_icy_title )
834                 vlc_meta_Set( p_meta, vlc_meta_NowPlaying, p_sys->psz_icy_title );
835             break;
836
837         case ACCESS_GET_CONTENT_TYPE:
838             *va_arg( args, char ** ) =
839                 p_sys->psz_mime ? strdup( p_sys->psz_mime ) : NULL;
840             break;
841
842         case ACCESS_GET_TITLE_INFO:
843         case ACCESS_SET_TITLE:
844         case ACCESS_SET_SEEKPOINT:
845         case ACCESS_SET_PRIVATE_ID_STATE:
846             return VLC_EGENERIC;
847
848         default:
849             msg_Warn( p_access, "unimplemented query in control" );
850             return VLC_EGENERIC;
851
852     }
853     return VLC_SUCCESS;
854 }
855
856 /*****************************************************************************
857  * Connect:
858  *****************************************************************************/
859 static int Connect( access_t *p_access, int64_t i_tell )
860 {
861     access_sys_t   *p_sys = p_access->p_sys;
862     vlc_url_t      srv = p_sys->b_proxy ? p_sys->proxy : p_sys->url;
863
864     /* Clean info */
865     free( p_sys->psz_location );
866     free( p_sys->psz_mime );
867     free( p_sys->psz_pragma );
868
869     free( p_sys->psz_icy_genre );
870     free( p_sys->psz_icy_name );
871     free( p_sys->psz_icy_title );
872
873
874     p_sys->psz_location = NULL;
875     p_sys->psz_mime = NULL;
876     p_sys->psz_pragma = NULL;
877     p_sys->b_mms = VLC_FALSE;
878     p_sys->b_chunked = VLC_FALSE;
879     p_sys->i_chunk = 0;
880     p_sys->i_icy_meta = 0;
881     p_sys->psz_icy_name = NULL;
882     p_sys->psz_icy_genre = NULL;
883     p_sys->psz_icy_title = NULL;
884
885     p_access->info.i_size = 0;
886     p_access->info.i_pos  = i_tell;
887     p_access->info.b_eof  = VLC_FALSE;
888
889
890     /* Open connection */
891     p_sys->fd = net_ConnectTCP( p_access, srv.psz_host, srv.i_port );
892     if( p_sys->fd == -1 )
893     {
894         msg_Err( p_access, "cannot connect to %s:%d", srv.psz_host, srv.i_port );
895         return -1;
896     }
897
898     /* Initialize TLS/SSL session */
899     if( p_sys->b_ssl == VLC_TRUE )
900     {
901         /* CONNECT to establish TLS tunnel through HTTP proxy */
902         if( p_sys->b_proxy )
903         {
904             char *psz;
905             unsigned i_status = 0;
906
907             if( p_sys->i_version == 0 )
908             {
909                 /* CONNECT is not in HTTP/1.0 */
910                 Disconnect( p_access );
911                 return -1;
912             }
913
914             net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
915                         "CONNECT %s:%d HTTP/1.%d\r\nHost: %s:%d\r\n\r\n",
916                         p_sys->url.psz_host, p_sys->url.i_port,
917                         p_sys->i_version,
918                         p_sys->url.psz_host, p_sys->url.i_port);
919
920             psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, NULL );
921             if( psz == NULL )
922             {
923                 msg_Err( p_access, "cannot establish HTTP/TLS tunnel" );
924                 Disconnect( p_access );
925                 return -1;
926             }
927
928             sscanf( psz, "HTTP/%*u.%*u %3u", &i_status );
929             free( psz );
930
931             if( ( i_status / 100 ) != 2 )
932             {
933                 msg_Err( p_access, "HTTP/TLS tunnel through proxy denied" );
934                 Disconnect( p_access );
935                 return -1;
936             }
937
938             do
939             {
940                 psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, NULL );
941                 if( psz == NULL )
942                 {
943                     msg_Err( p_access, "HTTP proxy connection failed" );
944                     Disconnect( p_access );
945                     return -1;
946                 }
947
948                 if( *psz == '\0' )
949                     i_status = 0;
950
951                 free( psz );
952             }
953             while( i_status );
954         }
955
956         /* TLS/SSL handshake */
957         p_sys->p_tls = tls_ClientCreate( VLC_OBJECT(p_access), p_sys->fd,
958                                          srv.psz_host );
959         if( p_sys->p_tls == NULL )
960         {
961             msg_Err( p_access, "cannot establish HTTP/TLS session" );
962             Disconnect( p_access );
963             return -1;
964         }
965         p_sys->p_vs = &p_sys->p_tls->sock;
966     }
967
968     return Request( p_access, i_tell ) ? -2 : 0;
969 }
970
971
972 static int Request( access_t *p_access, int64_t i_tell )
973 {
974     access_sys_t   *p_sys = p_access->p_sys;
975     char           *psz ;
976     v_socket_t     *pvs = p_sys->p_vs;
977
978     if( p_sys->b_proxy )
979     {
980         if( p_sys->url.psz_path )
981         {
982             net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
983                         "GET http://%s:%d%s HTTP/1.%d\r\n",
984                         p_sys->url.psz_host, p_sys->url.i_port,
985                         p_sys->url.psz_path, p_sys->i_version );
986         }
987         else
988         {
989             net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
990                         "GET http://%s:%d/ HTTP/1.%d\r\n",
991                         p_sys->url.psz_host, p_sys->url.i_port,
992                         p_sys->i_version );
993         }
994     }
995     else
996     {
997         const char *psz_path = p_sys->url.psz_path;
998         if( !psz_path || !*psz_path )
999         {
1000             psz_path = "/";
1001         }
1002         if( p_sys->url.i_port != 80)
1003         {
1004             net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs,
1005                         "GET %s HTTP/1.%d\r\nHost: %s:%d\r\n",
1006                         psz_path, p_sys->i_version, p_sys->url.psz_host,
1007                         p_sys->url.i_port );
1008         }
1009         else
1010         {
1011             net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs,
1012                         "GET %s HTTP/1.%d\r\nHost: %s\r\n",
1013                         psz_path, p_sys->i_version, p_sys->url.psz_host );
1014         }
1015     }
1016     /* User Agent */
1017     net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs, "User-Agent: %s\r\n",
1018                 p_sys->psz_user_agent );
1019     /* Offset */
1020     if( p_sys->i_version == 1 )
1021     {
1022         net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs,
1023                     "Range: bytes="I64Fd"-\r\n", i_tell );
1024     }
1025
1026     /* Cookies */
1027     if( p_sys->cookies )
1028     {
1029         int i;
1030         for( i = 0; i < vlc_array_count( p_sys->cookies ); i++ )
1031         {
1032             const char * cookie = vlc_array_item_at_index( p_sys->cookies, i );
1033             char * psz_cookie_content = cookie_get_content( cookie );
1034             char * psz_cookie_domain = cookie_get_domain( cookie );
1035
1036             assert( psz_cookie_content );
1037
1038             /* FIXME: This is clearly not conforming to the rfc */
1039             vlc_bool_t is_in_right_domain = (!psz_cookie_domain || strstr( p_sys->url.psz_host, psz_cookie_domain ));
1040
1041             if( is_in_right_domain )
1042             {
1043                 msg_Dbg( p_access, "Sending Cookie %s", psz_cookie_content );
1044                 if( net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs, "Cookie: %s\r\n", psz_cookie_content ) < 0 )
1045                     msg_Err( p_access, "failed to send Cookie" );
1046             }
1047             free( psz_cookie_content );
1048             free( psz_cookie_domain );
1049         }
1050     }
1051
1052     /* Authentication */
1053     if( p_sys->url.psz_username || p_sys->url.psz_password )
1054     {
1055         char buf[strlen( p_sys->url.psz_username ?: "" )
1056                   + strlen( p_sys->url.psz_password ?: "" ) + 2];
1057         char *b64;
1058
1059         snprintf( buf, sizeof( buf ), "%s:%s", p_sys->url.psz_username ?: "",
1060                   p_sys->url.psz_password ?: "" );
1061         b64 = vlc_b64_encode( buf );
1062
1063         if( b64 != NULL )
1064         {
1065              net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs,
1066                          "Authorization: Basic %s\r\n", b64 );
1067              free( b64 );
1068         }
1069     }
1070
1071     /* Proxy Authentication */
1072     if( p_sys->proxy.psz_username || p_sys->proxy.psz_password )
1073     {
1074         char buf[strlen( p_sys->proxy.psz_username ?: "" )
1075                   + strlen( p_sys->proxy.psz_password ?: "" )];
1076         char *b64;
1077
1078         snprintf( buf, sizeof( buf ), "%s:%s", p_sys->proxy.psz_username ?: "",
1079                   p_sys->proxy.psz_password ?: "" );
1080         b64 = vlc_b64_encode( buf );
1081
1082         if( b64 != NULL)
1083         {
1084             net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs,
1085                         "Proxy-Authorization: Basic %s\r\n", b64 );
1086             free( b64 );
1087         }
1088     }
1089
1090     /* ICY meta data request */
1091     net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs, "Icy-MetaData: 1\r\n" );
1092
1093
1094     if( p_sys->b_continuous )
1095     {
1096         net_Printf( VLC_OBJECT( p_access ), p_sys->fd, pvs,
1097                     "Connection: Keep-Alive\r\n" );
1098     }
1099     else if( p_sys->i_version == 1 )
1100     {
1101         net_Printf( VLC_OBJECT( p_access ), p_sys->fd, pvs,
1102                     "Connection: Close\r\n");
1103     }
1104
1105     if( net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs, "\r\n" ) < 0 )
1106     {
1107         msg_Err( p_access, "failed to send request" );
1108         Disconnect( p_access );
1109         return VLC_EGENERIC;
1110     }
1111
1112     /* Read Answer */
1113     if( ( psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, pvs ) ) == NULL )
1114     {
1115         msg_Err( p_access, "failed to read answer" );
1116         goto error;
1117     }
1118     if( !strncmp( psz, "HTTP/1.", 7 ) )
1119     {
1120         p_sys->psz_protocol = "HTTP";
1121         p_sys->i_code = atoi( &psz[9] );
1122     }
1123     else if( !strncmp( psz, "ICY", 3 ) )
1124     {
1125         p_sys->psz_protocol = "ICY";
1126         p_sys->i_code = atoi( &psz[4] );
1127         p_sys->b_reconnect = VLC_TRUE;
1128     }
1129     else
1130     {
1131         msg_Err( p_access, "invalid HTTP reply '%s'", psz );
1132         free( psz );
1133         goto error;
1134     }
1135     msg_Dbg( p_access, "protocol '%s' answer code %d",
1136              p_sys->psz_protocol, p_sys->i_code );
1137     if( !strcmp( p_sys->psz_protocol, "ICY" ) )
1138     {
1139         p_sys->b_seekable = VLC_FALSE;
1140     }
1141     if( p_sys->i_code != 206 && p_sys->i_code != 401 )
1142     {
1143         p_sys->b_seekable = VLC_FALSE;
1144     }
1145     /* Authentication error - We'll have to display the dialog */
1146     if( p_sys->i_code == 401 )
1147     {
1148
1149     }
1150     /* Other fatal error */
1151     else if( p_sys->i_code >= 400 )
1152     {
1153         msg_Err( p_access, "error: %s", psz );
1154         free( psz );
1155         goto error;
1156     }
1157     free( psz );
1158
1159     for( ;; )
1160     {
1161         char *psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, pvs );
1162         char *p;
1163
1164         if( psz == NULL )
1165         {
1166             msg_Err( p_access, "failed to read answer" );
1167             goto error;
1168         }
1169
1170         /* msg_Dbg( p_input, "Line=%s", psz ); */
1171         if( *psz == '\0' )
1172         {
1173             free( psz );
1174             break;
1175         }
1176
1177
1178         if( ( p = strchr( psz, ':' ) ) == NULL )
1179         {
1180             msg_Err( p_access, "malformed header line: %s", psz );
1181             free( psz );
1182             goto error;
1183         }
1184         *p++ = '\0';
1185         while( *p == ' ' ) p++;
1186
1187         if( !strcasecmp( psz, "Content-Length" ) )
1188         {
1189             if( p_sys->b_continuous )
1190             {
1191                 p_access->info.i_size = -1;
1192                 msg_Dbg( p_access, "this frame size=%lld", atoll(p ) );
1193                 p_sys->i_remaining = atoll( p );
1194             }
1195             else
1196             {
1197                 p_access->info.i_size = i_tell + atoll( p );
1198                 msg_Dbg( p_access, "stream size="I64Fd, p_access->info.i_size );
1199             }
1200         }
1201         else if( !strcasecmp( psz, "Location" ) )
1202         {
1203             char * psz_new_loc;
1204
1205             /* This does not follow RFC 2068, but yet if the url is not absolute,
1206              * handle it as everyone does. */
1207             if( p[0] == '/' )
1208             {
1209                 const char *psz_http_ext = p_sys->b_ssl ? "s" : "" ;
1210
1211                 if( p_sys->url.i_port == ( p_sys->b_ssl ? 443 : 80 ) )
1212                 {
1213                     if( asprintf(&psz_new_loc, "http%s://%s%s", psz_http_ext,
1214                                  p_sys->url.psz_host, p) < 0 )
1215                         goto error;
1216                 }
1217                 else
1218                 {
1219                     if( asprintf(&psz_new_loc, "http%s://%s:%d%s", psz_http_ext,
1220                                  p_sys->url.psz_host, p_sys->url.i_port, p) < 0 )
1221                         goto error;
1222                 }
1223             }
1224             else
1225             {
1226                 psz_new_loc = strdup( p );
1227             }
1228
1229             if( p_sys->psz_location ) free( p_sys->psz_location );
1230             p_sys->psz_location = psz_new_loc;
1231         }
1232         else if( !strcasecmp( psz, "Content-Type" ) )
1233         {
1234             if( p_sys->psz_mime ) free( p_sys->psz_mime );
1235             p_sys->psz_mime = strdup( p );
1236             msg_Dbg( p_access, "Content-Type: %s", p_sys->psz_mime );
1237         }
1238         else if( !strcasecmp( psz, "Content-Encoding" ) )
1239         {
1240             msg_Dbg( p_access, "Content-Encoding: %s", p );
1241             if( strcasecmp( p, "identity" ) )
1242 #ifdef HAVE_ZLIB_H
1243                 p_sys->b_compressed = VLC_TRUE;
1244 #else
1245                 msg_Warn( p_access, "Compressed content not supported. Rebuild with zlib support." );
1246 #endif
1247         }
1248         else if( !strcasecmp( psz, "Pragma" ) )
1249         {
1250             if( !strcasecmp( psz, "Pragma: features" ) )
1251                 p_sys->b_mms = VLC_TRUE;
1252             if( p_sys->psz_pragma ) free( p_sys->psz_pragma );
1253             p_sys->psz_pragma = strdup( p );
1254             msg_Dbg( p_access, "Pragma: %s", p_sys->psz_pragma );
1255         }
1256         else if( !strcasecmp( psz, "Server" ) )
1257         {
1258             msg_Dbg( p_access, "Server: %s", p );
1259             if( !strncasecmp( p, "Icecast", 7 ) ||
1260                 !strncasecmp( p, "Nanocaster", 10 ) )
1261             {
1262                 /* Remember if this is Icecast
1263                  * we need to force demux in this case without breaking
1264                  *  autodetection */
1265
1266                 /* Let live 365 streams (nanocaster) piggyback on the icecast
1267                  * routine. They look very similar */
1268
1269                 p_sys->b_reconnect = VLC_TRUE;
1270                 p_sys->b_pace_control = VLC_FALSE;
1271                 p_sys->b_icecast = VLC_TRUE;
1272             }
1273         }
1274         else if( !strcasecmp( psz, "Transfer-Encoding" ) )
1275         {
1276             msg_Dbg( p_access, "Transfer-Encoding: %s", p );
1277             if( !strncasecmp( p, "chunked", 7 ) )
1278             {
1279                 p_sys->b_chunked = VLC_TRUE;
1280             }
1281         }
1282         else if( !strcasecmp( psz, "Icy-MetaInt" ) )
1283         {
1284             msg_Dbg( p_access, "Icy-MetaInt: %s", p );
1285             p_sys->i_icy_meta = atoi( p );
1286             if( p_sys->i_icy_meta < 0 )
1287                 p_sys->i_icy_meta = 0;
1288
1289             msg_Warn( p_access, "ICY metaint=%d", p_sys->i_icy_meta );
1290         }
1291         else if( !strcasecmp( psz, "Icy-Name" ) )
1292         {
1293             if( p_sys->psz_icy_name ) free( p_sys->psz_icy_name );
1294             p_sys->psz_icy_name = strdup( p );
1295             msg_Dbg( p_access, "Icy-Name: %s", p_sys->psz_icy_name );
1296
1297             p_sys->b_icecast = VLC_TRUE; /* be on the safeside. set it here as well. */
1298             p_sys->b_reconnect = VLC_TRUE;
1299             p_sys->b_pace_control = VLC_FALSE;
1300         }
1301         else if( !strcasecmp( psz, "Icy-Genre" ) )
1302         {
1303             if( p_sys->psz_icy_genre ) free( p_sys->psz_icy_genre );
1304             p_sys->psz_icy_genre = strdup( p );
1305             msg_Dbg( p_access, "Icy-Genre: %s", p_sys->psz_icy_genre );
1306         }
1307         else if( !strncasecmp( psz, "Icy-Notice", 10 ) )
1308         {
1309             msg_Dbg( p_access, "Icy-Notice: %s", p );
1310         }
1311         else if( !strncasecmp( psz, "icy-", 4 ) ||
1312                  !strncasecmp( psz, "ice-", 4 ) ||
1313                  !strncasecmp( psz, "x-audiocast", 11 ) )
1314         {
1315             msg_Dbg( p_access, "Meta-Info: %s: %s", psz, p );
1316         } else if( !strcasecmp( psz, "Set-Cookie" ) )
1317         {
1318             if( p_sys->cookies )
1319             {
1320                 msg_Dbg( p_access, "Accepting Cookie: %s", p );
1321                 cookie_append( p_sys->cookies, strdup(p) );
1322             }
1323             else
1324                 msg_Dbg( p_access, "We have a Cookie we won't remember: %s", p );
1325         }
1326
1327         free( psz );
1328     }
1329     return VLC_SUCCESS;
1330
1331 error:
1332     Disconnect( p_access );
1333     return VLC_EGENERIC;
1334 }
1335
1336 /*****************************************************************************
1337  * Disconnect:
1338  *****************************************************************************/
1339 static void Disconnect( access_t *p_access )
1340 {
1341     access_sys_t *p_sys = p_access->p_sys;
1342
1343     if( p_sys->p_tls != NULL)
1344     {
1345         tls_ClientDelete( p_sys->p_tls );
1346         p_sys->p_tls = NULL;
1347         p_sys->p_vs = NULL;
1348     }
1349     if( p_sys->fd != -1)
1350     {
1351         net_Close(p_sys->fd);
1352         p_sys->fd = -1;
1353     }
1354
1355 }
1356
1357 /*****************************************************************************
1358  * Cookies (FIXME: we may want to rewrite that using a nice structure to hold
1359  * them) (FIXME: only support the "domain=" param)
1360  *****************************************************************************/
1361
1362 /* Get the NAME=VALUE part of the Cookie */
1363 static char * cookie_get_content( const char * cookie )
1364 {
1365     char * ret = strdup( cookie );
1366     if( !ret ) return NULL;
1367     char * str = ret;
1368     /* Look for a ';' */
1369     while( *str && *str != ';' ) str++;
1370     /* Replace it by a end-char */
1371     if( *str == ';' ) *str = 0;
1372     return ret;
1373 }
1374
1375 /* Get the domain where the cookie is stored */
1376 static char * cookie_get_domain( const char * cookie )
1377 {
1378     const char * str = cookie;
1379     static const char domain[] = "domain=";
1380     if( !str )
1381         return NULL;
1382     /* Look for a ';' */
1383     while( *str )
1384     {
1385         if( !strncmp( str, domain, sizeof(domain) - 1 /* minus \0 */ ) )
1386         {
1387             str += sizeof(domain) - 1 /* minus \0 */;
1388             char * ret = strdup( str );
1389             /* Now remove the next ';' if present */
1390             char * ret_iter = ret;
1391             while( *ret_iter && *ret_iter != ';' ) ret_iter++;
1392             if( *ret_iter == ';' )
1393                 *ret_iter = 0;
1394             return ret;
1395         }
1396         /* Go to next ';' field */
1397         while( *str && *str != ';' ) str++;
1398         if( *str == ';' ) str++;
1399         /* skip blank */
1400         while( *str && *str == ' ' ) str++;
1401     }
1402     return NULL;
1403 }
1404
1405 /* Get NAME in the NAME=VALUE field */
1406 static char * cookie_get_name( const char * cookie )
1407 {
1408     char * ret = cookie_get_content( cookie ); /* NAME=VALUE */
1409     if( !ret ) return NULL;
1410     char * str = ret;
1411     while( *str && *str != '=' ) str++;
1412     *str = 0;
1413     return ret;
1414 }
1415
1416 /* Add a cookie in cookies, checking to see how it should be added */
1417 static void cookie_append( vlc_array_t * cookies, char * cookie )
1418 {
1419     int i;
1420
1421     if( !cookie )
1422         return;
1423
1424     char * cookie_name = cookie_get_name( cookie );
1425
1426     /* Don't send invalid cookies */
1427     if( !cookie_name )
1428         return;
1429
1430     char * cookie_domain = cookie_get_domain( cookie );
1431     for( i = 0; i < vlc_array_count( cookies ); i++ )
1432     {
1433         char * current_cookie = vlc_array_item_at_index( cookies, i );
1434         char * current_cookie_name = cookie_get_name( current_cookie );
1435         char * current_cookie_domain = cookie_get_domain( current_cookie );
1436
1437         assert( current_cookie_name );
1438
1439         vlc_bool_t is_domain_matching = ( cookie_domain && current_cookie_domain &&
1440                                          !strcmp( cookie_domain, current_cookie_domain ) );
1441
1442         if( is_domain_matching && !strcmp( cookie_name, current_cookie_name )  )
1443         {
1444             /* Remove previous value for this cookie */
1445             free( current_cookie );
1446             vlc_array_remove( cookies, i );
1447
1448             /* Clean */
1449             free( current_cookie_name );
1450             free( current_cookie_domain );
1451             break;
1452         }
1453         free( current_cookie_name );
1454         free( current_cookie_domain );
1455     }
1456     free( cookie_name );
1457     free( cookie_domain );
1458     vlc_array_append( cookies, cookie );
1459 }
1460