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