* mmsh: a little clean, and fixed 1/2 potential segfaults.
[vlc.git] / modules / access / mms / mmsh.c
1 /*****************************************************************************
2  * mmsh.c:
3  *****************************************************************************
4  * Copyright (C) 2001, 2002 VideoLAN
5  * $Id: mmsh.c,v 1.6 2003/08/26 00:51:19 fenrir Exp $
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*
25  * TODO:
26  *  * http_proxy
27  *
28  */
29
30 /*****************************************************************************
31  * Preamble
32  *****************************************************************************/
33 #include <stdlib.h>
34
35 #include <vlc/vlc.h>
36 #include <vlc/input.h>
37
38 #ifdef HAVE_ERRNO_H
39 #   include <errno.h>
40 #endif
41 #ifdef HAVE_FCNTL_H
42 #   include <fcntl.h>
43 #endif
44 #ifdef HAVE_SYS_TIME_H
45 #    include <sys/time.h>
46 #endif
47
48 #ifdef HAVE_UNISTD_H
49 #   include <unistd.h>
50 #endif
51
52 #if defined( UNDER_CE )
53 #   include <winsock.h>
54 #elif defined( WIN32 )
55 #   include <winsock2.h>
56 #   include <ws2tcpip.h>
57 #   ifndef IN_MULTICAST
58 #       define IN_MULTICAST(a) IN_CLASSD(a)
59 #   endif
60 #else
61 #   include <sys/socket.h>
62 #endif
63
64 #include "network.h"
65 #include "asf.h"
66 #include "buffer.h"
67
68 #include "mms.h"
69 #include "mmsh.h"
70
71 /*****************************************************************************
72  * Local prototypes
73  *****************************************************************************/
74 int  E_( MMSHOpen )  ( input_thread_t * );
75 void E_( MMSHClose ) ( input_thread_t * );
76
77 static ssize_t Read        ( input_thread_t * p_input, byte_t * p_buffer,
78                              size_t i_len );
79 static void    Seek        ( input_thread_t *, off_t );
80
81 /****************************************************************************
82  ****************************************************************************
83  *******************                                      *******************
84  *******************       Main functions                 *******************
85  *******************                                      *******************
86  ****************************************************************************
87  ****************************************************************************/
88
89 /****************************************************************************
90  * Open: connect to ftp server and ask for file
91  ****************************************************************************/
92 int  E_( MMSHOpen )  ( input_thread_t *p_input )
93 {
94     access_sys_t    *p_sys;
95
96     uint8_t         *p;
97     http_answer_t   *p_ans;
98     http_field_t    *p_field;
99     chunk_t         ck;
100
101     /* init p_sys */
102     p_input->p_access_data = p_sys = malloc( sizeof( access_sys_t ) );
103     p_sys->i_proto = MMS_PROTO_HTTP;
104
105     p_sys->p_socket = NULL;
106     p_sys->i_request_context = 1;
107     p_sys->i_buffer = 0;
108     p_sys->i_buffer_pos = 0;
109     p_sys->b_broadcast = VLC_TRUE;
110     p_sys->p_packet = NULL;
111     p_sys->i_packet_sequence = 0;
112     p_sys->i_packet_used = 0;
113     p_sys->i_packet_length = 0;
114     p_sys->i_pos = 0;
115     p_sys->i_request_context = 1;
116     E_( GenerateGuid )( &p_sys->guid );
117
118     /* open a tcp connection */
119     p_sys->p_url = E_( url_new )( p_input->psz_name );
120
121     if( *p_sys->p_url->psz_host == '\0' )
122     {
123         msg_Err( p_input, "invalid server addresse" );
124         goto exit_error;
125     }
126     if( p_sys->p_url->i_port <= 0 )
127     {
128         p_sys->p_url->i_port = 80;
129     }
130
131     if( ( p_sys->p_socket = NetOpenTCP( p_input, p_sys->p_url ) ) == NULL )
132     {
133         msg_Err( p_input, "cannot connect" );
134         goto exit_error;
135     }
136
137     /* *** send first request *** */
138     p = &p_sys->buffer[0];
139     p += sprintf( p, "GET %s HTTP/1.0\r\n", p_sys->p_url->psz_path );
140     p += sprintf( p,"Accept: */*\r\n" );
141     p += sprintf( p, "User-Agent: NSPlayer/4.1.0.3856\r\n" );
142     p += sprintf( p, "Host: %s:%d\r\n",
143                   p_sys->p_url->psz_host, p_sys->p_url->i_port );
144     p += sprintf( p, "Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=0:0,request-context=%d,max-duration=0\r\n",
145                   p_sys->i_request_context++ );
146     p += sprintf( p, "Pragma: xClientGUID={"GUID_FMT"}\r\n",
147                   GUID_PRINT( p_sys->guid ) );
148     p += sprintf( p, "Connection: Close\r\n\r\n" );
149     NetWrite( p_input, p_sys->p_socket, p_sys->buffer,  p - p_sys->buffer );
150
151
152     if( NetFill ( p_input, p_sys, BUFFER_SIZE ) <= 0 )
153     {
154         msg_Err( p_input, "cannot read answer" );
155         goto exit_error;
156     }
157     NetClose( p_input, p_sys->p_socket );
158     p_sys->p_socket = NULL;
159
160     p_ans = http_answer_parse( p_sys->buffer, p_sys->i_buffer );
161     if( !p_ans )
162     {
163         msg_Err( p_input, "cannot parse answer" );
164         goto exit_error;
165     }
166
167     if( p_ans->i_error >= 400 )
168     {
169         msg_Err( p_input, "error %d (server return=`%s')",
170                  p_ans->i_error, p_ans->psz_answer );
171         http_answer_free( p_ans );
172         goto exit_error;
173     }
174     else if( p_ans->i_error >= 300 )
175     {
176         msg_Err( p_input, "FIXME redirect unsuported %d (server return=`%s')",
177                  p_ans->i_error, p_ans->psz_answer );
178         http_answer_free( p_ans );
179         goto exit_error;
180     }
181     else if( p_ans->i_body <= 0 )
182     {
183         msg_Err( p_input, "empty answer" );
184         http_answer_free( p_ans );
185         goto exit_error;
186     }
187
188     /* now get features */
189     /* FIXME FIXME test Content-Type to see if it's a plain stream or an
190      * asx FIXME */
191     for( p_field = p_ans->p_fields;
192          p_field != NULL;
193          p_field = http_field_find( p_field->p_next, "Pragma" ) )
194     {
195         if( !strncasecmp( p_field->psz_value, "features", 8 ) )
196         {
197             if( strstr( p_field->psz_value, "broadcast" ) )
198             {
199                 msg_Dbg( p_input, "stream type = broadcast" );
200                 p_sys->b_broadcast = VLC_TRUE;
201             }
202             else if( strstr( p_field->psz_value, "seekable" ) )
203             {
204                 msg_Dbg( p_input, "stream type = seekable" );
205                 p_sys->b_broadcast = VLC_FALSE;
206             }
207             else
208             {
209                 msg_Warn( p_input, "unknow stream types (%s)",
210                           p_field->psz_value );
211                 p_sys->b_broadcast = VLC_FALSE;
212             }
213         }
214     }
215
216     /* gather header */
217     p_sys->i_header = 0;
218     p_sys->p_header = malloc( p_ans->i_body );
219     do
220     {
221         if( chunk_parse( &ck, p_ans->p_body, p_ans->i_body ) )
222         {
223             msg_Err( p_input, "invalid chunk answer" );
224             goto exit_error;
225         }
226         if( ck.i_type != 0x4824 )
227         {
228             msg_Err( p_input, "invalid chunk (0x%x)", ck.i_type );
229             break;
230         }
231         if( ck.i_data > 0 )
232         {
233             memcpy( &p_sys->p_header[p_sys->i_header],
234                     ck.p_data,
235                     ck.i_data );
236
237             p_sys->i_header += ck.i_data;
238         }
239
240         /* BEURK */
241         p_ans->p_body   += 12 + ck.i_data;
242         p_ans->i_body   -= 12 + ck.i_data;
243
244     } while( p_ans->i_body > 12 );
245
246     http_answer_free( p_ans );
247
248     msg_Dbg( p_input, "complete header size=%d", p_sys->i_header );
249     if( p_sys->i_header <= 0 )
250     {
251         msg_Err( p_input, "header size == 0" );
252         goto exit_error;
253     }
254     /* *** parse header and get stream and their id *** */
255     /* get all streams properties,
256      *
257      * TODO : stream bitrates properties(optional)
258      *        and bitrate mutual exclusion(optional) */
259     E_( asf_HeaderParse )( &p_sys->asfh,
260                            p_sys->p_header, p_sys->i_header );
261     msg_Dbg( p_input, "packet count=%lld packet size=%d",
262              p_sys->asfh.i_data_packets_count,
263              p_sys->asfh.i_min_data_packet_size );
264
265     E_( asf_StreamSelect)( &p_sys->asfh,
266                            config_GetInt( p_input, "mms-maxbitrate" ),
267                            config_GetInt( p_input, "mms-all" ),
268                            config_GetInt( p_input, "audio" ),
269                            config_GetInt( p_input, "video" ) );
270
271     if( mmsh_start( p_input, 0 ) )
272     {
273         msg_Err( p_input, "cannot start stream" );
274         goto exit_error;
275     }
276
277     /* *** set exported functions *** */
278     p_input->pf_read = Read;
279     p_input->pf_seek = Seek;
280     p_input->pf_set_program = input_SetProgram;
281     p_input->pf_set_area = NULL;
282
283     p_input->p_private = NULL;
284     p_input->i_mtu = 3 * p_sys->asfh.i_min_data_packet_size;
285
286     /* *** finished to set some variable *** */
287     vlc_mutex_lock( &p_input->stream.stream_lock );
288     p_input->stream.b_pace_control = 0;
289     if( p_sys->b_broadcast )
290     {
291         p_input->stream.p_selected_area->i_size = 0;
292         p_input->stream.b_seekable = 0;
293     }
294     else
295     {
296         p_input->stream.p_selected_area->i_size = p_sys->asfh.i_file_size;
297         p_input->stream.b_seekable = 1;
298     }
299     p_input->stream.p_selected_area->i_tell = 0;
300     p_input->stream.i_method = INPUT_METHOD_NETWORK;
301     vlc_mutex_unlock( &p_input->stream.stream_lock );
302
303     /* Update default_pts to a suitable value for ftp access */
304     p_input->i_pts_delay = config_GetInt( p_input, "mms-caching" ) * 1000;
305
306
307     return( VLC_SUCCESS );
308
309 exit_error:
310     E_( url_free )( p_sys->p_url );
311
312     if( p_sys->p_socket )
313     {
314         NetClose( p_input, p_sys->p_socket );
315     }
316     free( p_sys );
317     return( VLC_EGENERIC );
318 }
319
320 /*****************************************************************************
321  * Close: free unused data structures
322  *****************************************************************************/
323 void E_( MMSHClose ) ( input_thread_t *p_input )
324 {
325     access_sys_t    *p_sys   = p_input->p_access_data;
326
327     msg_Dbg( p_input, "stopping stream" );
328
329     mmsh_stop( p_input );
330
331     free( p_sys );
332 }
333
334 static int mmsh_get_packet( input_thread_t * p_input,
335                             chunk_t *p_ck )
336 {
337     access_sys_t *p_sys = p_input->p_access_data;
338
339     int i_mov = p_sys->i_buffer - p_sys->i_buffer_pos;
340
341     if( p_sys->i_buffer_pos > BUFFER_SIZE / 2 )
342     {
343         if( i_mov > 0 )
344         {
345             memmove( &p_sys->buffer[0],
346                      &p_sys->buffer[p_sys->i_buffer_pos],
347                      i_mov );
348         }
349
350         p_sys->i_buffer     = i_mov;
351         p_sys->i_buffer_pos = 0;
352     }
353
354     if( NetFill( p_input, p_sys, 12 ) < 12 )
355     {
356         msg_Warn( p_input, "cannot fill buffer" );
357         return VLC_EGENERIC;
358     }
359
360     chunk_parse( p_ck, &p_sys->buffer[p_sys->i_buffer_pos],
361                  p_sys->i_buffer - p_sys->i_buffer_pos );
362
363     if( p_ck->i_type == 0x4524 )   // Transfer complete
364     {
365         msg_Warn( p_input, "EOF" );
366         return VLC_EGENERIC;
367     }
368     else if( p_ck->i_type != 0x4824 && p_ck->i_type != 0x4424 )
369     {
370         msg_Err( p_input, "invalid chunk FATAL" );
371         return VLC_EGENERIC;
372     }
373
374     if( p_ck->i_data < p_ck->i_size2 - 8 )
375     {
376         if( NetFill( p_input, p_sys, p_ck->i_size2 - 8 - p_ck->i_data ) <= 0 )
377         {
378             msg_Warn( p_input, "cannot fill buffer" );
379             return VLC_EGENERIC;
380         }
381         chunk_parse( p_ck, &p_sys->buffer[p_sys->i_buffer_pos],
382                      p_sys->i_buffer - p_sys->i_buffer_pos );
383     }
384
385     if( p_sys->i_packet_sequence != 0 &&
386         p_ck->i_sequence != p_sys->i_packet_sequence )
387     {
388         msg_Warn( p_input, "packet lost ?" );
389     }
390
391     p_sys->i_packet_sequence = p_ck->i_sequence + 1;
392     p_sys->i_packet_used   = 0;
393     p_sys->i_packet_length = p_ck->i_data;
394     p_sys->p_packet        = p_ck->p_data;
395
396     p_sys->i_buffer_pos += 12 + p_ck->i_data;
397
398     return VLC_SUCCESS;
399 }
400
401
402 /*****************************************************************************
403  * Seek: try to go at the right place
404  *****************************************************************************/
405 static void Seek( input_thread_t * p_input, off_t i_pos )
406 {
407     access_sys_t *p_sys = p_input->p_access_data;
408     chunk_t      ck;
409     off_t        i_offset;
410     off_t        i_packet;
411
412     i_packet = ( i_pos - p_sys->i_header ) / p_sys->asfh.i_min_data_packet_size;
413     i_offset = ( i_pos - p_sys->i_header ) % p_sys->asfh.i_min_data_packet_size;
414
415     msg_Err( p_input, "seeking to "I64Fd, i_pos );
416
417     vlc_mutex_lock( &p_input->stream.stream_lock );
418
419     mmsh_stop( p_input );
420     mmsh_start( p_input, i_packet * p_sys->asfh.i_min_data_packet_size );
421
422     for( ;; )
423     {
424         if( mmsh_get_packet( p_input, &ck ) )
425         {
426             break;
427         }
428
429         /* skip headers */
430         if( ck.i_type != 0x4824 )
431         {
432             break;
433         }
434         msg_Warn( p_input, "skipping header" );
435     }
436
437     p_sys->i_pos = i_pos;
438     p_sys->i_packet_used += i_offset;
439
440
441     p_input->stream.p_selected_area->i_tell = i_pos;
442     vlc_mutex_unlock( &p_input->stream.stream_lock );
443
444 }
445
446 /*****************************************************************************
447  * Read:
448  *****************************************************************************/
449 static ssize_t Read        ( input_thread_t * p_input, byte_t * p_buffer,
450                              size_t i_len )
451 {
452     access_sys_t *p_sys = p_input->p_access_data;
453     size_t       i_copy;
454     size_t       i_data = 0;
455
456     while( i_data < i_len )
457     {
458         if( p_sys->i_packet_used < p_sys->i_packet_length )
459         {
460             i_copy = __MIN( p_sys->i_packet_length - p_sys->i_packet_used,
461                             i_len - i_data );
462
463             memcpy( &p_buffer[i_data],
464                     &p_sys->p_packet[p_sys->i_packet_used],
465                     i_copy );
466
467             i_data += i_copy;
468             p_sys->i_packet_used += i_copy;
469         }
470         else if( p_sys->i_pos + i_data > p_sys->i_header &&
471                  (int)p_sys->i_packet_used < p_sys->asfh.i_min_data_packet_size )
472         {
473             i_copy = __MIN( p_sys->asfh.i_min_data_packet_size - p_sys->i_packet_used,
474                             i_len - i_data );
475
476             memset( &p_buffer[i_data], 0, i_copy );
477
478             i_data += i_copy;
479             p_sys->i_packet_used += i_copy;
480         }
481         else
482         {
483             chunk_t ck;
484             /* get a new packet */
485             /* fill enought data (>12) */
486             msg_Dbg( p_input, "waiting data (buffer = %d bytes)",
487                      p_sys->i_buffer );
488
489             if( mmsh_get_packet( p_input, &ck ) )
490             {
491                 return 0;
492             }
493         }
494     }
495
496     p_sys->i_pos += i_data;
497
498     return( i_data );
499 }
500
501
502 /****************************************************************************/
503 /****************************************************************************/
504 /****************************************************************************/
505 /****************************************************************************/
506 /****************************************************************************/
507
508 static int mmsh_start( input_thread_t *p_input,
509                        off_t i_pos )
510 {
511     access_sys_t *p_sys = p_input->p_access_data;
512     uint8_t *p;
513     int i_streams = 0;
514     int i;
515     http_answer_t *p_ans;
516
517     msg_Dbg( p_input, "starting stream" );
518
519     if( ( p_sys->p_socket = NetOpenTCP( p_input, p_sys->p_url ) ) == NULL )
520     {
521         /* should not occur */
522         msg_Err( p_input, "cannot connect to the server" );
523         return VLC_EGENERIC;
524     }
525
526     for( i = 1; i < 128; i++ )
527     {
528         if( p_sys->asfh.stream[i].i_selected )
529         {
530             i_streams++;
531         }
532     }
533
534     if( i_streams <= 0 )
535     {
536         msg_Err( p_input, "no stream selected" );
537         return VLC_EGENERIC;
538     }
539
540     p = &p_sys->buffer[0];
541     p += sprintf( p, "GET %s HTTP/1.0\r\n", p_sys->p_url->psz_path );
542     p += sprintf( p,"Accept: */*\r\n" );
543     p += sprintf( p, "User-Agent: NSPlayer/4.1.0.3856\r\n" );
544     p += sprintf( p, "Host: %s:%d\r\n",
545                   p_sys->p_url->psz_host, p_sys->p_url->i_port );
546     if( p_sys->b_broadcast )
547     {
548         p += sprintf( p,"Pragma: no-cache,rate=1.000000,request-context=%d\r\n",
549                       p_sys->i_request_context++ );
550     }
551     else
552     {
553         p += sprintf( p, "Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=%u:%u,request-context=%d,max-duration=0\r\n",
554                          (uint32_t)((i_pos >> 32)&0xffffffff),
555                          (uint32_t)(i_pos&0xffffffff),
556                          p_sys->i_request_context++ );
557     }
558     p += sprintf( p, "Pragma: xPlayStrm=1\r\n" );
559     p += sprintf( p, "Pragma: xClientGUID={"GUID_FMT"}\r\n",
560                   GUID_PRINT( p_sys->guid ) );
561     p += sprintf( p, "Pragma: stream-switch-count=%d\r\n", i_streams );
562     p += sprintf( p, "Pragma: stream-switch-entry=" );
563     for( i = 0; i < i_streams; i++ )
564     {
565         if( p_sys->asfh.stream[i].i_selected )
566         {
567             p += sprintf( p, "ffff:%d:0 ", p_sys->asfh.stream[i].i_id );
568         }
569         else
570         {
571             p += sprintf( p, "ffff:%d:2 ", p_sys->asfh.stream[i].i_id );
572         }
573     }
574     p += sprintf( p, "\r\n" );
575     p += sprintf( p, "Connection: Close\r\n\r\n" );
576
577
578     NetWrite( p_input, p_sys->p_socket, p_sys->buffer,  p - p_sys->buffer );
579
580     msg_Dbg( p_input, "filling buffer" );
581     /* we read until we found a \r\n\r\n or \n\n */
582     p_sys->i_buffer = 0;
583     p_sys->i_buffer_pos = 0;
584     for( ;; )
585     {
586         int     i_try = 0;
587         int     i_read;
588         uint8_t *p;
589
590         p = &p_sys->buffer[p_sys->i_buffer];
591         i_read =
592             NetRead( p_input, p_sys->p_socket,
593                      &p_sys->buffer[p_sys->i_buffer],
594                       1024 );
595
596         if( i_read == 0 )
597         {
598             if( i_try++ > 12 )
599             {
600                 break;
601             }
602             msg_Dbg( p_input, "another try (%d/12)", i_try );
603             continue;
604         }
605
606         if( i_read <= 0 || p_input->b_die || p_input->b_error )
607         {
608             break;
609         }
610         p_sys->i_buffer += i_read;
611         p_sys->buffer[p_sys->i_buffer] = '\0';
612
613         if( strstr( p, "\r\n\r\n" ) || strstr( p, "\n\n" ) )
614         {
615             msg_Dbg( p_input, "body found" );
616             break;
617         }
618         if( p_sys->i_buffer >= BUFFER_SIZE - 1024 )
619         {
620             msg_Dbg( p_input, "buffer size exeded" );
621             break;
622         }
623     }
624
625     p_ans = http_answer_parse( p_sys->buffer, p_sys->i_buffer );
626     if( !p_ans )
627     {
628         msg_Err( p_input, "cannot parse answer" );
629         return VLC_EGENERIC;
630     }
631
632     if( p_ans->i_error < 200 || p_ans->i_error >= 300 )
633     {
634         msg_Err( p_input, "error %d (server return=`%s')",
635                  p_ans->i_error, p_ans->psz_answer );
636         http_answer_free( p_ans );
637         return VLC_EGENERIC;
638     }
639
640     if( !p_ans->p_body )
641     {
642         p_sys->i_buffer_pos = 0;
643         p_sys->i_buffer = 0;
644     }
645     else
646     {
647         p_sys->i_buffer_pos = p_ans->p_body - p_sys->buffer;
648     }
649     http_answer_free( p_ans );
650
651     return VLC_SUCCESS;
652 }
653
654 static void mmsh_stop( input_thread_t *p_input )
655 {
656     access_sys_t *p_sys = p_input->p_access_data;
657
658     msg_Dbg( p_input, "closing stream" );
659     NetClose( p_input, p_sys->p_socket );
660     p_sys->p_socket = NULL;
661 }
662
663 static ssize_t NetFill( input_thread_t *p_input,
664                         access_sys_t   *p_sys, int i_size )
665 {
666     int i_try   = 0;
667     int i_total = 0;
668
669     i_size = __MIN( i_size, BUFFER_SIZE - p_sys->i_buffer );
670     if( i_size <= 0 )
671     {
672         return 0;
673     }
674
675     for( ;; )
676     {
677         int i_read;
678
679         i_read = NetRead( p_input, p_sys->p_socket,
680                           &p_sys->buffer[p_sys->i_buffer], i_size );
681
682         if( i_read == 0 )
683         {
684             if( i_try++ > 2 )
685             {
686                 break;
687             }
688             msg_Dbg( p_input, "another try %d/2", i_try );
689             continue;
690         }
691
692         if( i_read < 0 || p_input->b_die || p_input->b_error )
693         {
694             break;
695         }
696         i_total += i_read;
697
698         p_sys->i_buffer += i_read;
699         if( i_total >= i_size )
700         {
701             break;
702         }
703     }
704
705     p_sys->buffer[p_sys->i_buffer] = '\0';
706
707     return i_total;
708 }
709
710 /****************************************************************************
711  * NetOpenTCP:
712  ****************************************************************************/
713 static input_socket_t * NetOpenTCP( input_thread_t *p_input, url_t *p_url )
714 {
715     input_socket_t   *p_socket;
716     char             *psz_network;
717     module_t         *p_network;
718     network_socket_t socket_desc;
719
720
721     p_socket = malloc( sizeof( input_socket_t ) );
722     memset( p_socket, 0, sizeof( input_socket_t ) );
723
724     psz_network = "";
725     if( config_GetInt( p_input, "ipv4" ) )
726     {
727         psz_network = "ipv4";
728     }
729     else if( config_GetInt( p_input, "ipv6" ) )
730     {
731         psz_network = "ipv6";
732     }
733
734     msg_Dbg( p_input, "waiting for connection..." );
735
736     socket_desc.i_type = NETWORK_TCP;
737     socket_desc.psz_server_addr = p_url->psz_host;
738     socket_desc.i_server_port   = p_url->i_port;
739     socket_desc.psz_bind_addr   = "";
740     socket_desc.i_bind_port     = 0;
741     socket_desc.i_ttl           = 0;
742     p_input->p_private = (void*)&socket_desc;
743     if( !( p_network = module_Need( p_input, "network", psz_network ) ) )
744     {
745         msg_Err( p_input, "failed to connect with server" );
746         return NULL;
747     }
748     module_Unneed( p_input, p_network );
749     p_socket->i_handle = socket_desc.i_handle;
750     p_input->i_mtu     = socket_desc.i_mtu;
751
752     msg_Dbg( p_input,
753              "connection with \"%s:%d\" successful",
754              p_url->psz_host,
755              p_url->i_port );
756
757     return p_socket;
758 }
759
760 /*****************************************************************************
761  * Read: read on a file descriptor, checking b_die periodically
762  *****************************************************************************/
763 static ssize_t NetRead( input_thread_t *p_input,
764                         input_socket_t *p_socket,
765                         byte_t *p_buffer, size_t i_len )
766 {
767     struct timeval  timeout;
768     fd_set          fds;
769     ssize_t         i_recv;
770     int             i_ret;
771
772     /* Initialize file descriptor set */
773     FD_ZERO( &fds );
774     FD_SET( p_socket->i_handle, &fds );
775
776     /* We'll wait 1 second if nothing happens */
777     timeout.tv_sec  = 1;
778     timeout.tv_usec = 0;
779
780     /* Find if some data is available */
781     while( ( i_ret = select( p_socket->i_handle + 1, &fds,
782                              NULL, NULL, &timeout )) == 0 ||
783 #ifdef HAVE_ERRNO_H
784            ( i_ret < 0 && errno == EINTR )
785 #endif
786          )
787     {
788         FD_ZERO( &fds );
789         FD_SET( p_socket->i_handle, &fds );
790         timeout.tv_sec  = 1;
791         timeout.tv_usec = 0;
792
793         if( p_input->b_die || p_input->b_error )
794         {
795             return 0;
796         }
797     }
798
799     if( i_ret < 0 )
800     {
801         msg_Err( p_input, "network select error (%s)", strerror(errno) );
802         return -1;
803     }
804
805     i_recv = recv( p_socket->i_handle, p_buffer, i_len, 0 );
806
807     if( i_recv < 0 )
808     {
809         msg_Err( p_input, "recv failed (%s)", strerror(errno) );
810     }
811
812     return i_recv;
813 }
814
815 static ssize_t NetWrite( input_thread_t *p_input,
816                          input_socket_t *p_socket,
817                          byte_t *p_buffer, size_t i_len )
818 {
819     struct timeval  timeout;
820     fd_set          fds;
821     ssize_t         i_send;
822     int             i_ret;
823
824     /* Initialize file descriptor set */
825     FD_ZERO( &fds );
826     FD_SET( p_socket->i_handle, &fds );
827
828     /* We'll wait 1 second if nothing happens */
829     timeout.tv_sec  = 1;
830     timeout.tv_usec = 0;
831
832     /* Find if some data is available */
833     while( ( i_ret = select( p_socket->i_handle + 1, NULL, &fds, NULL, &timeout ) ) == 0 ||
834 #ifdef HAVE_ERRNO_H
835            ( i_ret < 0 && errno == EINTR )
836 #endif
837          )
838     {
839         FD_ZERO( &fds );
840         FD_SET( p_socket->i_handle, &fds );
841         timeout.tv_sec  = 1;
842         timeout.tv_usec = 0;
843
844         if( p_input->b_die || p_input->b_error )
845         {
846             return 0;
847         }
848     }
849
850     if( i_ret < 0 )
851     {
852         msg_Err( p_input, "network select error (%s)", strerror(errno) );
853         return -1;
854     }
855
856     i_send = send( p_socket->i_handle, p_buffer, i_len, 0 );
857
858     if( i_send < 0 )
859     {
860         msg_Err( p_input, "send failed (%s)", strerror(errno) );
861     }
862
863     return i_send;
864 }
865
866 static void NetClose( input_thread_t *p_input, input_socket_t *p_socket )
867 {
868 #if defined( WIN32 ) || defined( UNDER_CE )
869     closesocket( p_socket->i_handle );
870 #else
871     close( p_socket->i_handle );
872 #endif
873
874     free( p_socket );
875 }
876
877 static int http_next_line( uint8_t **pp_data, int *pi_data )
878 {
879     char *p, *p_end = *pp_data + *pi_data;
880
881     for( p = *pp_data; p < p_end; p++ )
882     {
883         if( p + 1 < p_end && *p == '\n' )
884         {
885             *pi_data = p_end - p - 1;
886             *pp_data = p + 1;
887             return VLC_SUCCESS;
888         }
889         if( p + 2 < p_end && p[0] == '\r' && p[1] == '\n' )
890         {
891             *pi_data = p_end - p - 2;
892             *pp_data = p + 2;
893             return VLC_SUCCESS;
894         }
895     }
896     *pi_data = 0;
897     *pp_data = p_end;
898     return VLC_EGENERIC;
899 }
900
901 static http_answer_t *http_answer_parse( uint8_t *p_data, int i_data )
902 {
903     http_answer_t *ans = malloc( sizeof( http_answer_t ) );
904     http_field_t  **pp_last;
905     char          *p, *end;
906
907     if( strncmp( p_data, "HTTP/1.", 7 ) )
908     {
909         free( ans );
910         return NULL;
911     }
912     ans->i_version = atoi( &p_data[7] );
913     ans->i_error   = strtol( p_data + 8, &p, 0 );
914     while( *p == ' ' )
915     {
916         p++;
917     }
918     if( ( ( end = strchr( p, '\r' ) ) == NULL )&&
919         ( ( end = strchr( p, '\n' ) ) == NULL ) )
920     {
921         end = &p_data[i_data];
922     }
923
924     ans->psz_answer = strndup( p, end - p );
925
926     fprintf( stderr, "version=%d error=%d answer=%s\n",
927              ans->i_version, ans->i_error, ans->psz_answer );
928     ans->p_fields = NULL;
929     ans->i_body   = 0;
930     ans->p_body   = 0;
931
932     pp_last = &ans->p_fields;
933
934     for( ;; )
935     {
936         http_field_t  *p_field;
937         uint8_t       *colon;
938
939         if( http_next_line( &p_data, &i_data ) )
940         {
941             return ans;
942         }
943         if( !strncmp( p_data, "\r\n", 2 ) || !strncmp( p_data, "\n", 1 ) )
944         {
945             break;
946         }
947
948         colon = strstr( p_data, ": " );
949         if( colon )
950         {
951             uint8_t *end;
952
953             end = strstr( colon, "\n" ) - 1;
954             if( *end != '\r' )
955             {
956                 end++;
957             }
958
959             p_field             = malloc( sizeof( http_field_t ) );
960             p_field->psz_name   = strndup( p_data, colon - p_data );
961             p_field->psz_value  = strndup( colon + 2, end - colon - 2 );
962             p_field->p_next     = NULL;
963
964             *pp_last = p_field;
965             pp_last = &p_field->p_next;
966
967             fprintf( stderr, "field name=`%s' value=`%s'\n",
968                      p_field->psz_name, p_field->psz_value );
969         }
970     }
971
972     if( http_next_line( &p_data, &i_data ) )
973     {
974         return ans;
975     }
976
977     ans->p_body = p_data;
978     ans->i_body = i_data;
979     fprintf( stderr, "body size=%d\n", i_data );
980
981     return ans;
982 }
983
984 static void http_answer_free( http_answer_t *ans )
985 {
986     http_field_t  *p_field = ans->p_fields;
987
988     while( p_field )
989     {
990         http_field_t *p_next;
991
992         p_next = p_field->p_next;
993         free( p_field->psz_name );
994         free( p_field->psz_value );
995         free( p_field );
996
997         p_field = p_next;
998     }
999
1000     free( ans->psz_answer );
1001     free( ans );
1002 }
1003
1004 static http_field_t *http_field_find( http_field_t *p_field, char *psz_name )
1005 {
1006
1007     while( p_field )
1008     {
1009         if( !strcasecmp( p_field->psz_name, psz_name ) )
1010         {
1011             return p_field;
1012         }
1013
1014         p_field = p_field->p_next;
1015     }
1016
1017     return NULL;
1018 }
1019
1020 static int chunk_parse( chunk_t *ck, uint8_t *p_data, int i_data )
1021 {
1022     if( i_data < 12 )
1023     {
1024         return VLC_EGENERIC;
1025     }
1026
1027     ck->i_type      = GetWLE( p_data );
1028     ck->i_size      = GetWLE( p_data + 2);
1029     ck->i_sequence  = GetDWLE( p_data  + 4);
1030     ck->i_unknown   = GetWLE( p_data + 8);
1031     ck->i_size2     = GetWLE( p_data + 10);
1032
1033     ck->p_data      = p_data + 12;
1034     ck->i_data      = __MIN( i_data - 12, ck->i_size2 - 8 );
1035
1036     return VLC_SUCCESS;
1037 }
1038