]> git.sesse.net Git - vlc/blob - modules/access/http.c
Add gzip support to the http access. We never request it, we just know how to handle...
[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         printf("redirection to %s", p_sys->psz_location );
373
374
375         /* Do not accept redirection outside of HTTP works */
376         if( strncmp( p_sys->psz_location, "http", 4 )
377          || ( ( p_sys->psz_location[4] != ':' ) /* HTTP */
378            && strncmp( p_sys->psz_location + 4, "s:", 2 ) /* HTTP/SSL */ ) )
379         {
380             msg_Err( p_access, "insecure redirection ignored" );
381             goto error;
382         }
383         free( p_access->psz_path );
384         p_access->psz_path = strdup( p_sys->psz_location );
385         /* Clean up current Open() run */
386         vlc_UrlClean( &p_sys->url );
387         vlc_UrlClean( &p_sys->proxy );
388         free( p_sys->psz_mime );
389         free( p_sys->psz_pragma );
390         free( p_sys->psz_location );
391         free( p_sys->psz_user_agent );
392
393         Disconnect( p_access );
394         cookies = p_sys->cookies;
395         free( p_sys );
396
397         /* Do new Open() run with new data */
398         return OpenWithCookies( p_this, cookies );
399     }
400
401     if( p_sys->b_mms )
402     {
403         msg_Dbg( p_access, "this is actually a live mms server, BAIL" );
404         goto error;
405     }
406
407     if( !strcmp( p_sys->psz_protocol, "ICY" ) || p_sys->b_icecast )
408     {
409         if( p_sys->psz_mime && strcasecmp( p_sys->psz_mime, "application/ogg" ) )
410         {
411             if( !strcasecmp( p_sys->psz_mime, "video/nsv" ) ||
412                 !strcasecmp( p_sys->psz_mime, "video/nsa" ) )
413                 p_access->psz_demux = strdup( "nsv" );
414             else if( !strcasecmp( p_sys->psz_mime, "audio/aac" ) ||
415                      !strcasecmp( p_sys->psz_mime, "audio/aacp" ) )
416                 p_access->psz_demux = strdup( "m4a" );
417             else if( !strcasecmp( p_sys->psz_mime, "audio/mpeg" ) )
418                 p_access->psz_demux = strdup( "mp3" );
419
420             msg_Info( p_access, "Raw-audio server found, %s demuxer selected",
421                       p_access->psz_demux );
422
423 #if 0       /* Doesn't work really well because of the pre-buffering in
424              * shoutcast servers (the buffer content will be sent as fast as
425              * possible). */
426             p_sys->b_pace_control = VLC_FALSE;
427 #endif
428         }
429         else if( !p_sys->psz_mime )
430         {
431              /* Shoutcast */
432              p_access->psz_demux = strdup( "mp3" );
433         }
434         /* else probably Ogg Vorbis */
435     }
436     else if( !strcasecmp( p_access->psz_access, "unsv" ) &&
437              p_sys->psz_mime &&
438              !strcasecmp( p_sys->psz_mime, "misc/ultravox" ) )
439     {
440         /* Grrrr! detect ultravox server and force NSV demuxer */
441         p_access->psz_demux = strdup( "nsv" );
442     }
443     else if( !strcmp( p_access->psz_access, "itpc" ) )
444     {
445         p_access->psz_demux = strdup( "podcast" );
446     }
447     else if( p_sys->psz_mime &&
448              !strncasecmp( p_sys->psz_mime, "application/xspf+xml", 20 ) &&
449              ( memchr( " ;\t", p_sys->psz_mime[20], 4 ) != NULL ) )
450         p_access->psz_demux = strdup( "xspf-open" );
451
452     if( p_sys->b_reconnect ) msg_Dbg( p_access, "auto re-connect enabled" );
453
454     /* PTS delay */
455     var_Create( p_access, "http-caching", VLC_VAR_INTEGER |VLC_VAR_DOINHERIT );
456
457     return VLC_SUCCESS;
458
459 error:
460     vlc_UrlClean( &p_sys->url );
461     vlc_UrlClean( &p_sys->proxy );
462     free( p_sys->psz_mime );
463     free( p_sys->psz_pragma );
464     free( p_sys->psz_location );
465     free( p_sys->psz_user_agent );
466
467     Disconnect( p_access );
468     free( p_sys );
469     return VLC_EGENERIC;
470 }
471
472 /*****************************************************************************
473  * Close:
474  *****************************************************************************/
475 static void Close( vlc_object_t *p_this )
476 {
477     access_t     *p_access = (access_t*)p_this;
478     access_sys_t *p_sys = p_access->p_sys;
479
480     vlc_UrlClean( &p_sys->url );
481     vlc_UrlClean( &p_sys->proxy );
482
483     free( p_sys->psz_mime );
484     free( p_sys->psz_pragma );
485     free( p_sys->psz_location );
486
487     free( p_sys->psz_icy_name );
488     free( p_sys->psz_icy_genre );
489     free( p_sys->psz_icy_title );
490
491     free( p_sys->psz_user_agent );
492
493     Disconnect( p_access );
494
495     if( p_sys->cookies )
496     {
497         int i;
498         for( i = 0; i < vlc_array_count( p_sys->cookies ); i++ )
499             free(vlc_array_item_at_index( p_sys->cookies, i ));
500         vlc_array_destroy( p_sys->cookies );
501     }
502
503 #ifdef HAVE_ZLIB_H
504     inflateEnd( &p_sys->inflate.stream );
505     free( p_sys->inflate.p_buffer );
506 #endif
507
508     free( p_sys );
509 }
510
511 /*****************************************************************************
512  * Read: Read up to i_len bytes from the http connection and place in
513  * p_buffer. Return the actual number of bytes read
514  *****************************************************************************/
515 static int ReadICYMeta( access_t *p_access );
516 static ssize_t Read( access_t *p_access, uint8_t *p_buffer, size_t i_len )
517 {
518     access_sys_t *p_sys = p_access->p_sys;
519     int i_read;
520
521     if( p_sys->fd < 0 )
522     {
523         p_access->info.b_eof = VLC_TRUE;
524         return 0;
525     }
526
527     if( p_access->info.i_size > 0 &&
528         i_len + p_access->info.i_pos > p_access->info.i_size )
529     {
530         if( ( i_len = p_access->info.i_size - p_access->info.i_pos ) == 0 )
531         {
532             p_access->info.b_eof = VLC_TRUE;
533             return 0;
534         }
535     }
536
537     if( p_sys->b_chunked )
538     {
539         if( p_sys->i_chunk < 0 )
540         {
541             p_access->info.b_eof = VLC_TRUE;
542             return 0;
543         }
544
545         if( p_sys->i_chunk <= 0 )
546         {
547             char *psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, p_sys->p_vs );
548             /* read the chunk header */
549             if( psz == NULL )
550             {
551                 /* fatal error - end of file */
552                 msg_Dbg( p_access, "failed reading chunk-header line" );
553                 return 0;
554             }
555             p_sys->i_chunk = strtoll( psz, NULL, 16 );
556             free( psz );
557
558             if( p_sys->i_chunk <= 0 )   /* eof */
559             {
560                 p_sys->i_chunk = -1;
561                 p_access->info.b_eof = VLC_TRUE;
562                 return 0;
563             }
564         }
565
566         if( i_len > p_sys->i_chunk )
567         {
568             i_len = p_sys->i_chunk;
569         }
570     }
571
572     if( p_sys->b_continuous && (ssize_t)i_len > p_sys->i_remaining )
573     {
574         /* Only ask for the remaining length */
575         int i_new_len = p_sys->i_remaining;
576         if( i_new_len == 0 )
577         {
578             Request( p_access, 0 );
579             i_read = Read( p_access, p_buffer, i_len );
580             return i_read;
581         }
582         i_len = i_new_len;
583     }
584
585     if( p_sys->i_icy_meta > 0 && p_access->info.i_pos > 0 )
586     {
587         int64_t i_next = p_sys->i_icy_meta -
588                                     p_access->info.i_pos % p_sys->i_icy_meta;
589
590         if( i_next == p_sys->i_icy_meta )
591         {
592             if( ReadICYMeta( p_access ) )
593             {
594                 p_access->info.b_eof = VLC_TRUE;
595                 return -1;
596             }
597         }
598         if( i_len > i_next )
599             i_len = i_next;
600     }
601
602     i_read = net_Read( p_access, p_sys->fd, p_sys->p_vs, p_buffer, i_len, VLC_FALSE );
603
604     if( i_read > 0 )
605     {
606         p_access->info.i_pos += i_read;
607
608         if( p_sys->b_chunked )
609         {
610             p_sys->i_chunk -= i_read;
611             if( p_sys->i_chunk <= 0 )
612             {
613                 /* read the empty line */
614                 char *psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, p_sys->p_vs );
615                 if( psz ) free( psz );
616             }
617         }
618     }
619     else if( i_read == 0 )
620     {
621         /*
622          * I very much doubt that this will work.
623          * If i_read == 0, the connection *IS* dead, so the only
624          * sensible thing to do is Disconnect() and then retry.
625          * Otherwise, I got recv() completely wrong. -- Courmisch
626          */
627         if( p_sys->b_continuous )
628         {
629             Request( p_access, 0 );
630             p_sys->b_continuous = VLC_FALSE;
631             i_read = Read( p_access, p_buffer, i_len );
632             p_sys->b_continuous = VLC_TRUE;
633         }
634         Disconnect( p_access );
635         if( p_sys->b_reconnect )
636         {
637             msg_Dbg( p_access, "got disconnected, trying to reconnect" );
638             if( Connect( p_access, p_access->info.i_pos ) )
639             {
640                 msg_Dbg( p_access, "reconnection failed" );
641             }
642             else
643             {
644                 p_sys->b_reconnect = VLC_FALSE;
645                 i_read = Read( p_access, p_buffer, i_len );
646                 p_sys->b_reconnect = VLC_TRUE;
647             }
648         }
649
650         if( i_read == 0 ) p_access->info.b_eof = VLC_TRUE;
651     }
652
653     if( p_sys->b_continuous )
654     {
655         p_sys->i_remaining -= i_read;
656     }
657
658     return i_read;
659 }
660
661 static int ReadICYMeta( access_t *p_access )
662 {
663     access_sys_t *p_sys = p_access->p_sys;
664
665     uint8_t buffer;
666     char *p, *psz_meta;
667     int i_read;
668
669     /* Read meta data length */
670     i_read = net_Read( p_access, p_sys->fd, p_sys->p_vs, &buffer, 1,
671                        VLC_TRUE );
672     if( i_read <= 0 )
673         return VLC_EGENERIC;
674     if( buffer == 0 )
675         return VLC_SUCCESS;
676
677     i_read = buffer << 4;
678     /* msg_Dbg( p_access, "ICY meta size=%u", i_read); */
679
680     psz_meta = malloc( i_read + 1 );
681     if( net_Read( p_access, p_sys->fd, p_sys->p_vs,
682                   (uint8_t *)psz_meta, i_read, VLC_TRUE ) != i_read )
683         return VLC_EGENERIC;
684
685     psz_meta[i_read] = '\0'; /* Just in case */
686
687     /* msg_Dbg( p_access, "icy-meta=%s", psz_meta ); */
688
689     /* Now parse the meta */
690     /* Look for StreamTitle= */
691     p = strcasestr( (char *)psz_meta, "StreamTitle=" );
692     if( p )
693     {
694         p += strlen( "StreamTitle=" );
695         if( *p == '\'' || *p == '"' )
696         {
697             char closing[] = { p[0], ';', '\0' };
698             char *psz = strstr( &p[1], closing );
699             if( !psz )
700                 psz = strchr( &p[1], ';' );
701
702             if( psz ) *psz = '\0';
703         }
704         else
705         {
706             char *psz = strchr( &p[1], ';' );
707             if( psz ) *psz = '\0';
708         }
709
710         if( !p_sys->psz_icy_title ||
711             strcmp( p_sys->psz_icy_title, &p[1] ) )
712         {
713             if( p_sys->psz_icy_title )
714                 free( p_sys->psz_icy_title );
715             p_sys->psz_icy_title = strdup( &p[1] );
716             p_access->info.i_update |= INPUT_UPDATE_META;
717
718             msg_Dbg( p_access, "New Title=%s", p_sys->psz_icy_title );
719         }
720     }
721     free( psz_meta );
722
723     return VLC_SUCCESS;
724 }
725
726 #ifdef HAVE_ZLIB_H
727 static ssize_t ReadCompressed( access_t *p_access, uint8_t *p_buffer,
728                                size_t i_len )
729 {
730     access_sys_t *p_sys = p_access->p_sys;
731
732     if( p_sys->b_compressed )
733     {
734         int i_ret;
735
736         if( !p_sys->inflate.p_buffer )
737             p_sys->inflate.p_buffer = malloc( 256 * 1024 );
738
739         if( p_sys->inflate.stream.avail_in == 0 )
740         {
741             ssize_t i_read = Read( p_access, p_sys->inflate.p_buffer + p_sys->inflate.stream.avail_in, 256 * 1024 );
742             if( i_read <= 0 ) return i_read;
743             p_sys->inflate.stream.next_in = p_sys->inflate.p_buffer;
744             p_sys->inflate.stream.avail_in = i_read;
745         }
746
747         p_sys->inflate.stream.avail_out = i_len;
748         p_sys->inflate.stream.next_out = p_buffer;
749
750         i_ret = inflate( &p_sys->inflate.stream, Z_SYNC_FLUSH );
751         msg_Warn( p_access, "inflate return value: %d, %s", i_ret, p_sys->inflate.stream.msg );
752
753         return i_len - p_sys->inflate.stream.avail_out;
754     }
755     else
756     {
757         return Read( p_access, p_buffer, i_len );
758     }
759 }
760 #endif
761
762 /*****************************************************************************
763  * Seek: close and re-open a connection at the right place
764  *****************************************************************************/
765 static int Seek( access_t *p_access, int64_t i_pos )
766 {
767     msg_Dbg( p_access, "trying to seek to "I64Fd, i_pos );
768
769     Disconnect( p_access );
770
771     if( Connect( p_access, i_pos ) )
772     {
773         msg_Err( p_access, "seek failed" );
774         p_access->info.b_eof = VLC_TRUE;
775         return VLC_EGENERIC;
776     }
777     return VLC_SUCCESS;
778 }
779
780 /*****************************************************************************
781  * Control:
782  *****************************************************************************/
783 static int Control( access_t *p_access, int i_query, va_list args )
784 {
785     access_sys_t *p_sys = p_access->p_sys;
786     vlc_bool_t   *pb_bool;
787     int          *pi_int;
788     int64_t      *pi_64;
789     vlc_meta_t   *p_meta;
790
791     switch( i_query )
792     {
793         /* */
794         case ACCESS_CAN_SEEK:
795             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
796             *pb_bool = p_sys->b_seekable;
797             break;
798         case ACCESS_CAN_FASTSEEK:
799             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
800             *pb_bool = VLC_FALSE;
801             break;
802         case ACCESS_CAN_PAUSE:
803         case ACCESS_CAN_CONTROL_PACE:
804             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
805
806 #if 0       /* Disable for now until we have a clock synchro algo
807              * which works with something else than MPEG over UDP */
808             *pb_bool = p_sys->b_pace_control;
809 #endif
810             *pb_bool = VLC_TRUE;
811             break;
812
813         /* */
814         case ACCESS_GET_MTU:
815             pi_int = (int*)va_arg( args, int * );
816             *pi_int = 0;
817             break;
818
819         case ACCESS_GET_PTS_DELAY:
820             pi_64 = (int64_t*)va_arg( args, int64_t * );
821             *pi_64 = (int64_t)var_GetInteger( p_access, "http-caching" ) * 1000;
822             break;
823
824         /* */
825         case ACCESS_SET_PAUSE_STATE:
826             break;
827
828         case ACCESS_GET_META:
829             p_meta = (vlc_meta_t*)va_arg( args, vlc_meta_t* );
830
831             if( p_sys->psz_icy_name )
832                 vlc_meta_Set( p_meta, vlc_meta_Title, p_sys->psz_icy_name );
833             if( p_sys->psz_icy_genre )
834                 vlc_meta_Set( p_meta, vlc_meta_Genre, p_sys->psz_icy_genre );
835             if( p_sys->psz_icy_title )
836                 vlc_meta_Set( p_meta, vlc_meta_NowPlaying, p_sys->psz_icy_title );
837             break;
838
839         case ACCESS_GET_CONTENT_TYPE:
840             *va_arg( args, char ** ) =
841                 p_sys->psz_mime ? strdup( p_sys->psz_mime ) : NULL;
842             break;
843
844         case ACCESS_GET_TITLE_INFO:
845         case ACCESS_SET_TITLE:
846         case ACCESS_SET_SEEKPOINT:
847         case ACCESS_SET_PRIVATE_ID_STATE:
848             return VLC_EGENERIC;
849
850         default:
851             msg_Warn( p_access, "unimplemented query in control" );
852             return VLC_EGENERIC;
853
854     }
855     return VLC_SUCCESS;
856 }
857
858 /*****************************************************************************
859  * Connect:
860  *****************************************************************************/
861 static int Connect( access_t *p_access, int64_t i_tell )
862 {
863     access_sys_t   *p_sys = p_access->p_sys;
864     vlc_url_t      srv = p_sys->b_proxy ? p_sys->proxy : p_sys->url;
865
866     /* Clean info */
867     free( p_sys->psz_location );
868     free( p_sys->psz_mime );
869     free( p_sys->psz_pragma );
870
871     free( p_sys->psz_icy_genre );
872     free( p_sys->psz_icy_name );
873     free( p_sys->psz_icy_title );
874
875
876     p_sys->psz_location = NULL;
877     p_sys->psz_mime = NULL;
878     p_sys->psz_pragma = NULL;
879     p_sys->b_mms = VLC_FALSE;
880     p_sys->b_chunked = VLC_FALSE;
881     p_sys->i_chunk = 0;
882     p_sys->i_icy_meta = 0;
883     p_sys->psz_icy_name = NULL;
884     p_sys->psz_icy_genre = NULL;
885     p_sys->psz_icy_title = NULL;
886
887     p_access->info.i_size = 0;
888     p_access->info.i_pos  = i_tell;
889     p_access->info.b_eof  = VLC_FALSE;
890
891
892     /* Open connection */
893     p_sys->fd = net_ConnectTCP( p_access, srv.psz_host, srv.i_port );
894     if( p_sys->fd == -1 )
895     {
896         msg_Err( p_access, "cannot connect to %s:%d", srv.psz_host, srv.i_port );
897         return -1;
898     }
899
900     /* Initialize TLS/SSL session */
901     if( p_sys->b_ssl == VLC_TRUE )
902     {
903         /* CONNECT to establish TLS tunnel through HTTP proxy */
904         if( p_sys->b_proxy )
905         {
906             char *psz;
907             unsigned i_status = 0;
908
909             if( p_sys->i_version == 0 )
910             {
911                 /* CONNECT is not in HTTP/1.0 */
912                 Disconnect( p_access );
913                 return -1;
914             }
915
916             net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
917                         "CONNECT %s:%d HTTP/1.%d\r\nHost: %s:%d\r\n\r\n",
918                         p_sys->url.psz_host, p_sys->url.i_port,
919                         p_sys->i_version,
920                         p_sys->url.psz_host, p_sys->url.i_port);
921
922             psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, NULL );
923             if( psz == NULL )
924             {
925                 msg_Err( p_access, "cannot establish HTTP/TLS tunnel" );
926                 Disconnect( p_access );
927                 return -1;
928             }
929
930             sscanf( psz, "HTTP/%*u.%*u %3u", &i_status );
931             free( psz );
932
933             if( ( i_status / 100 ) != 2 )
934             {
935                 msg_Err( p_access, "HTTP/TLS tunnel through proxy denied" );
936                 Disconnect( p_access );
937                 return -1;
938             }
939
940             do
941             {
942                 psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, NULL );
943                 if( psz == NULL )
944                 {
945                     msg_Err( p_access, "HTTP proxy connection failed" );
946                     Disconnect( p_access );
947                     return -1;
948                 }
949
950                 if( *psz == '\0' )
951                     i_status = 0;
952
953                 free( psz );
954             }
955             while( i_status );
956         }
957
958         /* TLS/SSL handshake */
959         p_sys->p_tls = tls_ClientCreate( VLC_OBJECT(p_access), p_sys->fd,
960                                          srv.psz_host );
961         if( p_sys->p_tls == NULL )
962         {
963             msg_Err( p_access, "cannot establish HTTP/TLS session" );
964             Disconnect( p_access );
965             return -1;
966         }
967         p_sys->p_vs = &p_sys->p_tls->sock;
968     }
969
970     return Request( p_access, i_tell ) ? -2 : 0;
971 }
972
973
974 static int Request( access_t *p_access, int64_t i_tell )
975 {
976     access_sys_t   *p_sys = p_access->p_sys;
977     char           *psz ;
978     v_socket_t     *pvs = p_sys->p_vs;
979
980     if( p_sys->b_proxy )
981     {
982         if( p_sys->url.psz_path )
983         {
984             net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
985                         "GET http://%s:%d%s HTTP/1.%d\r\n",
986                         p_sys->url.psz_host, p_sys->url.i_port,
987                         p_sys->url.psz_path, p_sys->i_version );
988         }
989         else
990         {
991             net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
992                         "GET http://%s:%d/ HTTP/1.%d\r\n",
993                         p_sys->url.psz_host, p_sys->url.i_port,
994                         p_sys->i_version );
995         }
996     }
997     else
998     {
999         const char *psz_path = p_sys->url.psz_path;
1000         if( !psz_path || !*psz_path )
1001         {
1002             psz_path = "/";
1003         }
1004         if( p_sys->url.i_port != 80)
1005         {
1006             net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs,
1007                         "GET %s HTTP/1.%d\r\nHost: %s:%d\r\n",
1008                         psz_path, p_sys->i_version, p_sys->url.psz_host,
1009                         p_sys->url.i_port );
1010         }
1011         else
1012         {
1013             net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs,
1014                         "GET %s HTTP/1.%d\r\nHost: %s\r\n",
1015                         psz_path, p_sys->i_version, p_sys->url.psz_host );
1016         }
1017     }
1018     /* User Agent */
1019     net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs, "User-Agent: %s\r\n",
1020                 p_sys->psz_user_agent );
1021     /* Offset */
1022     if( p_sys->i_version == 1 )
1023     {
1024         net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs,
1025                     "Range: bytes="I64Fd"-\r\n", i_tell );
1026     }
1027
1028     /* Cookies */
1029     if( p_sys->cookies )
1030     {
1031         int i;
1032         for( i = 0; i < vlc_array_count( p_sys->cookies ); i++ )
1033         {
1034             const char * cookie = vlc_array_item_at_index( p_sys->cookies, i );
1035             char * psz_cookie_content = cookie_get_content( cookie );
1036             char * psz_cookie_domain = cookie_get_domain( cookie );
1037
1038             assert( psz_cookie_content );
1039
1040             /* FIXME: This is clearly not conforming to the rfc */
1041             vlc_bool_t is_in_right_domain = (!psz_cookie_domain || strstr( p_sys->url.psz_host, psz_cookie_domain ));
1042
1043             if( is_in_right_domain )
1044             {
1045                 msg_Dbg( p_access, "Sending Cookie %s", psz_cookie_content );
1046                 if( net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs, "Cookie: %s\r\n", psz_cookie_content ) < 0 )
1047                     msg_Err( p_access, "failed to send Cookie" );
1048             }
1049             free( psz_cookie_content );
1050             free( psz_cookie_domain );
1051         }
1052     }
1053
1054     /* Authentication */
1055     if( p_sys->url.psz_username || p_sys->url.psz_password )
1056     {
1057         char buf[strlen( p_sys->url.psz_username ?: "" )
1058                   + strlen( p_sys->url.psz_password ?: "" ) + 2];
1059         char *b64;
1060
1061         snprintf( buf, sizeof( buf ), "%s:%s", p_sys->url.psz_username ?: "",
1062                   p_sys->url.psz_password ?: "" );
1063         b64 = vlc_b64_encode( buf );
1064
1065         if( b64 != NULL )
1066         {
1067              net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs,
1068                          "Authorization: Basic %s\r\n", b64 );
1069              free( b64 );
1070         }
1071     }
1072
1073     /* Proxy Authentication */
1074     if( p_sys->proxy.psz_username || p_sys->proxy.psz_password )
1075     {
1076         char buf[strlen( p_sys->proxy.psz_username ?: "" )
1077                   + strlen( p_sys->proxy.psz_password ?: "" )];
1078         char *b64;
1079
1080         snprintf( buf, sizeof( buf ), "%s:%s", p_sys->proxy.psz_username ?: "",
1081                   p_sys->proxy.psz_password ?: "" );
1082         b64 = vlc_b64_encode( buf );
1083
1084         if( b64 != NULL)
1085         {
1086             net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs,
1087                         "Proxy-Authorization: Basic %s\r\n", b64 );
1088             free( b64 );
1089         }
1090     }
1091
1092     /* ICY meta data request */
1093     net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs, "Icy-MetaData: 1\r\n" );
1094
1095
1096     if( p_sys->b_continuous )
1097     {
1098         net_Printf( VLC_OBJECT( p_access ), p_sys->fd, pvs,
1099                     "Connection: Keep-Alive\r\n" );
1100     }
1101     else if( p_sys->i_version == 1 )
1102     {
1103         net_Printf( VLC_OBJECT( p_access ), p_sys->fd, pvs,
1104                     "Connection: Close\r\n");
1105     }
1106
1107     if( net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs, "\r\n" ) < 0 )
1108     {
1109         msg_Err( p_access, "failed to send request" );
1110         Disconnect( p_access );
1111         return VLC_EGENERIC;
1112     }
1113
1114     /* Read Answer */
1115     if( ( psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, pvs ) ) == NULL )
1116     {
1117         msg_Err( p_access, "failed to read answer" );
1118         goto error;
1119     }
1120     if( !strncmp( psz, "HTTP/1.", 7 ) )
1121     {
1122         p_sys->psz_protocol = "HTTP";
1123         p_sys->i_code = atoi( &psz[9] );
1124     }
1125     else if( !strncmp( psz, "ICY", 3 ) )
1126     {
1127         p_sys->psz_protocol = "ICY";
1128         p_sys->i_code = atoi( &psz[4] );
1129         p_sys->b_reconnect = VLC_TRUE;
1130     }
1131     else
1132     {
1133         msg_Err( p_access, "invalid HTTP reply '%s'", psz );
1134         free( psz );
1135         goto error;
1136     }
1137     msg_Dbg( p_access, "protocol '%s' answer code %d",
1138              p_sys->psz_protocol, p_sys->i_code );
1139     if( !strcmp( p_sys->psz_protocol, "ICY" ) )
1140     {
1141         p_sys->b_seekable = VLC_FALSE;
1142     }
1143     if( p_sys->i_code != 206 && p_sys->i_code != 401 )
1144     {
1145         p_sys->b_seekable = VLC_FALSE;
1146     }
1147     /* Authentication error - We'll have to display the dialog */
1148     if( p_sys->i_code == 401 )
1149     {
1150
1151     }
1152     /* Other fatal error */
1153     else if( p_sys->i_code >= 400 )
1154     {
1155         msg_Err( p_access, "error: %s", psz );
1156         free( psz );
1157         goto error;
1158     }
1159     free( psz );
1160
1161     for( ;; )
1162     {
1163         char *psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, pvs );
1164         char *p;
1165
1166         if( psz == NULL )
1167         {
1168             msg_Err( p_access, "failed to read answer" );
1169             goto error;
1170         }
1171
1172         /* msg_Dbg( p_input, "Line=%s", psz ); */
1173         if( *psz == '\0' )
1174         {
1175             free( psz );
1176             break;
1177         }
1178
1179
1180         if( ( p = strchr( psz, ':' ) ) == NULL )
1181         {
1182             msg_Err( p_access, "malformed header line: %s", psz );
1183             free( psz );
1184             goto error;
1185         }
1186         *p++ = '\0';
1187         while( *p == ' ' ) p++;
1188
1189         if( !strcasecmp( psz, "Content-Length" ) )
1190         {
1191             if( p_sys->b_continuous )
1192             {
1193                 p_access->info.i_size = -1;
1194                 msg_Dbg( p_access, "this frame size=%lld", atoll(p ) );
1195                 p_sys->i_remaining = atoll( p );
1196             }
1197             else
1198             {
1199                 p_access->info.i_size = i_tell + atoll( p );
1200                 msg_Dbg( p_access, "stream size="I64Fd, p_access->info.i_size );
1201             }
1202         }
1203         else if( !strcasecmp( psz, "Location" ) )
1204         {
1205             char * psz_new_loc;
1206
1207             /* This does not follow RFC 2068, but yet if the url is not absolute,
1208              * handle it as everyone does. */
1209             if( p[0] == '/' )
1210             {
1211                 const char *psz_http_ext = p_sys->b_ssl ? "s" : "" ;
1212
1213                 if( p_sys->url.i_port == ( p_sys->b_ssl ? 443 : 80 ) )
1214                 {
1215                     if( asprintf(&psz_new_loc, "http%s://%s%s", psz_http_ext,
1216                                  p_sys->url.psz_host, p) < 0 )
1217                         goto error;
1218                 }
1219                 else
1220                 {
1221                     if( asprintf(&psz_new_loc, "http%s://%s:%d%s", psz_http_ext,
1222                                  p_sys->url.psz_host, p_sys->url.i_port, p) < 0 )
1223                         goto error;
1224                 }
1225             }
1226             else
1227             {
1228                 psz_new_loc = strdup( p );
1229             }
1230
1231             if( p_sys->psz_location ) free( p_sys->psz_location );
1232             p_sys->psz_location = psz_new_loc;
1233         }
1234         else if( !strcasecmp( psz, "Content-Type" ) )
1235         {
1236             if( p_sys->psz_mime ) free( p_sys->psz_mime );
1237             p_sys->psz_mime = strdup( p );
1238             msg_Dbg( p_access, "Content-Type: %s", p_sys->psz_mime );
1239         }
1240         else if( !strcasecmp( psz, "Content-Encoding" ) )
1241         {
1242             msg_Dbg( p_access, "Content-Encoding: %s", p );
1243             if( strcasecmp( p, "identity" ) )
1244 #ifdef HAVE_ZLIB_H
1245                 p_sys->b_compressed = VLC_TRUE;
1246 #else
1247                 msg_Warn( p_access, "Compressed content not supported. Rebuild with zlib support." );
1248 #endif
1249         }
1250         else if( !strcasecmp( psz, "Pragma" ) )
1251         {
1252             if( !strcasecmp( psz, "Pragma: features" ) )
1253                 p_sys->b_mms = VLC_TRUE;
1254             if( p_sys->psz_pragma ) free( p_sys->psz_pragma );
1255             p_sys->psz_pragma = strdup( p );
1256             msg_Dbg( p_access, "Pragma: %s", p_sys->psz_pragma );
1257         }
1258         else if( !strcasecmp( psz, "Server" ) )
1259         {
1260             msg_Dbg( p_access, "Server: %s", p );
1261             if( !strncasecmp( p, "Icecast", 7 ) ||
1262                 !strncasecmp( p, "Nanocaster", 10 ) )
1263             {
1264                 /* Remember if this is Icecast
1265                  * we need to force demux in this case without breaking
1266                  *  autodetection */
1267
1268                 /* Let live 365 streams (nanocaster) piggyback on the icecast
1269                  * routine. They look very similar */
1270
1271                 p_sys->b_reconnect = VLC_TRUE;
1272                 p_sys->b_pace_control = VLC_FALSE;
1273                 p_sys->b_icecast = VLC_TRUE;
1274             }
1275         }
1276         else if( !strcasecmp( psz, "Transfer-Encoding" ) )
1277         {
1278             msg_Dbg( p_access, "Transfer-Encoding: %s", p );
1279             if( !strncasecmp( p, "chunked", 7 ) )
1280             {
1281                 p_sys->b_chunked = VLC_TRUE;
1282             }
1283         }
1284         else if( !strcasecmp( psz, "Icy-MetaInt" ) )
1285         {
1286             msg_Dbg( p_access, "Icy-MetaInt: %s", p );
1287             p_sys->i_icy_meta = atoi( p );
1288             if( p_sys->i_icy_meta < 0 )
1289                 p_sys->i_icy_meta = 0;
1290
1291             msg_Warn( p_access, "ICY metaint=%d", p_sys->i_icy_meta );
1292         }
1293         else if( !strcasecmp( psz, "Icy-Name" ) )
1294         {
1295             if( p_sys->psz_icy_name ) free( p_sys->psz_icy_name );
1296             p_sys->psz_icy_name = strdup( p );
1297             msg_Dbg( p_access, "Icy-Name: %s", p_sys->psz_icy_name );
1298
1299             p_sys->b_icecast = VLC_TRUE; /* be on the safeside. set it here as well. */
1300             p_sys->b_reconnect = VLC_TRUE;
1301             p_sys->b_pace_control = VLC_FALSE;
1302         }
1303         else if( !strcasecmp( psz, "Icy-Genre" ) )
1304         {
1305             if( p_sys->psz_icy_genre ) free( p_sys->psz_icy_genre );
1306             p_sys->psz_icy_genre = strdup( p );
1307             msg_Dbg( p_access, "Icy-Genre: %s", p_sys->psz_icy_genre );
1308         }
1309         else if( !strncasecmp( psz, "Icy-Notice", 10 ) )
1310         {
1311             msg_Dbg( p_access, "Icy-Notice: %s", p );
1312         }
1313         else if( !strncasecmp( psz, "icy-", 4 ) ||
1314                  !strncasecmp( psz, "ice-", 4 ) ||
1315                  !strncasecmp( psz, "x-audiocast", 11 ) )
1316         {
1317             msg_Dbg( p_access, "Meta-Info: %s: %s", psz, p );
1318         } else if( !strcasecmp( psz, "Set-Cookie" ) )
1319         {
1320             if( p_sys->cookies )
1321             {
1322                 msg_Dbg( p_access, "Accepting Cookie: %s", p );
1323                 cookie_append( p_sys->cookies, strdup(p) );
1324             }
1325             else
1326                 msg_Dbg( p_access, "We have a Cookie we won't remember: %s", p );
1327         }
1328
1329         free( psz );
1330     }
1331     return VLC_SUCCESS;
1332
1333 error:
1334     Disconnect( p_access );
1335     return VLC_EGENERIC;
1336 }
1337
1338 /*****************************************************************************
1339  * Disconnect:
1340  *****************************************************************************/
1341 static void Disconnect( access_t *p_access )
1342 {
1343     access_sys_t *p_sys = p_access->p_sys;
1344
1345     if( p_sys->p_tls != NULL)
1346     {
1347         tls_ClientDelete( p_sys->p_tls );
1348         p_sys->p_tls = NULL;
1349         p_sys->p_vs = NULL;
1350     }
1351     if( p_sys->fd != -1)
1352     {
1353         net_Close(p_sys->fd);
1354         p_sys->fd = -1;
1355     }
1356
1357 }
1358
1359 /*****************************************************************************
1360  * Cookies (FIXME: we may want to rewrite that using a nice structure to hold
1361  * them) (FIXME: only support the "domain=" param)
1362  *****************************************************************************/
1363
1364 /* Get the NAME=VALUE part of the Cookie */
1365 static char * cookie_get_content( const char * cookie )
1366 {
1367     char * ret = strdup( cookie );
1368     if( !ret ) return NULL;
1369     char * str = ret;
1370     /* Look for a ';' */
1371     while( *str && *str != ';' ) str++;
1372     /* Replace it by a end-char */
1373     if( *str == ';' ) *str = 0;
1374     return ret;
1375 }
1376
1377 /* Get the domain where the cookie is stored */
1378 static char * cookie_get_domain( const char * cookie )
1379 {
1380     const char * str = cookie;
1381     static const char domain[] = "domain=";
1382     if( !str )
1383         return NULL;
1384     /* Look for a ';' */
1385     while( *str )
1386     {
1387         if( !strncmp( str, domain, sizeof(domain) - 1 /* minus \0 */ ) )
1388         {
1389             str += sizeof(domain) - 1 /* minus \0 */;
1390             char * ret = strdup( str );
1391             /* Now remove the next ';' if present */
1392             char * ret_iter = ret;
1393             while( *ret_iter && *ret_iter != ';' ) ret_iter++;
1394             if( *ret_iter == ';' )
1395                 *ret_iter = 0;
1396             return ret;
1397         }
1398         /* Go to next ';' field */
1399         while( *str && *str != ';' ) str++;
1400         if( *str == ';' ) str++;
1401         /* skip blank */
1402         while( *str && *str == ' ' ) str++;
1403     }
1404     return NULL;
1405 }
1406
1407 /* Get NAME in the NAME=VALUE field */
1408 static char * cookie_get_name( const char * cookie )
1409 {
1410     char * ret = cookie_get_content( cookie ); /* NAME=VALUE */
1411     if( !ret ) return NULL;
1412     char * str = ret;
1413     while( *str && *str != '=' ) str++;
1414     *str = 0;
1415     return ret;
1416 }
1417
1418 /* Add a cookie in cookies, checking to see how it should be added */
1419 static void cookie_append( vlc_array_t * cookies, char * cookie )
1420 {
1421     int i;
1422
1423     if( !cookie )
1424         return;
1425
1426     char * cookie_name = cookie_get_name( cookie );
1427
1428     /* Don't send invalid cookies */
1429     if( !cookie_name )
1430         return;
1431
1432     char * cookie_domain = cookie_get_domain( cookie );
1433     for( i = 0; i < vlc_array_count( cookies ); i++ )
1434     {
1435         char * current_cookie = vlc_array_item_at_index( cookies, i );
1436         char * current_cookie_name = cookie_get_name( current_cookie );
1437         char * current_cookie_domain = cookie_get_domain( current_cookie );
1438
1439         assert( current_cookie_name );
1440
1441         vlc_bool_t is_domain_matching = ( cookie_domain && current_cookie_domain &&
1442                                          !strcmp( cookie_domain, current_cookie_domain ) );
1443
1444         if( is_domain_matching && !strcmp( cookie_name, current_cookie_name )  )
1445         {
1446             /* Remove previous value for this cookie */
1447             free( current_cookie );
1448             vlc_array_remove( cookies, i );
1449
1450             /* Clean */
1451             free( current_cookie_name );
1452             free( current_cookie_domain );
1453             break;
1454         }
1455         free( current_cookie_name );
1456         free( current_cookie_domain );
1457     }
1458     free( cookie_name );
1459     free( cookie_domain );
1460     vlc_array_append( cookies, cookie );
1461 }
1462