]> git.sesse.net Git - vlc/blob - modules/access/mms/mmsh.c
* mmsh: msg_Err -> msg_Dbg.
[vlc] / modules / access / mms / mmsh.c
1 /*****************************************************************************
2  * mmsh.c:
3  *****************************************************************************
4  * Copyright (C) 2001, 2002 VideoLAN
5  * $Id: mmsh.c,v 1.8 2004/01/26 16:30:34 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 #include "network.h"
39 #include "asf.h"
40 #include "buffer.h"
41
42 #include "mms.h"
43 #include "mmsh.h"
44
45 /*****************************************************************************
46  * Local prototypes
47  *****************************************************************************/
48 int  E_(MMSHOpen)  ( input_thread_t * );
49 void E_(MMSHClose) ( input_thread_t * );
50 static ssize_t Read( input_thread_t *, byte_t *, size_t );
51 static void    Seek( input_thread_t *, off_t );
52
53 static ssize_t NetFill( input_thread_t *, access_sys_t *, int );
54
55 static int  mmsh_start     ( input_thread_t *, off_t );
56 static void mmsh_stop      ( input_thread_t * );
57 static int  mmsh_get_packet( input_thread_t *, chunk_t * );
58
59 static http_answer_t *http_answer_parse( uint8_t *, int );
60 static void           http_answer_free ( http_answer_t * );
61 static http_field_t  *http_field_find  ( http_field_t *, char * );
62
63 static int chunk_parse( chunk_t *, uint8_t *, int );
64
65 /****************************************************************************
66  * Open: connect to ftp server and ask for file
67  ****************************************************************************/
68 int  E_( MMSHOpen )  ( input_thread_t *p_input )
69 {
70     access_sys_t    *p_sys;
71
72     uint8_t         *p;
73     http_answer_t   *p_ans;
74     http_field_t    *p_field;
75     chunk_t         ck;
76
77     vlc_value_t     val;
78
79     /* init p_sys */
80     p_input->p_access_data = p_sys = malloc( sizeof( access_sys_t ) );
81     p_sys->i_proto = MMS_PROTO_HTTP;
82
83     p_sys->fd       = -1;
84     p_sys->i_request_context = 1;
85     p_sys->i_buffer = 0;
86     p_sys->i_buffer_pos = 0;
87     p_sys->b_broadcast = VLC_TRUE;
88     p_sys->p_packet = NULL;
89     p_sys->i_packet_sequence = 0;
90     p_sys->i_packet_used = 0;
91     p_sys->i_packet_length = 0;
92     p_sys->i_pos = 0;
93     p_sys->i_request_context = 1;
94     E_( GenerateGuid )( &p_sys->guid );
95
96     /* open a tcp connection */
97     p_sys->p_url = E_( url_new )( p_input->psz_name );
98
99     if( *p_sys->p_url->psz_host == '\0' )
100     {
101         msg_Err( p_input, "invalid server addresse" );
102         goto exit_error;
103     }
104     if( p_sys->p_url->i_port <= 0 )
105     {
106         p_sys->p_url->i_port = 80;
107     }
108
109     if( ( p_sys->fd = net_OpenTCP( p_input, p_sys->p_url->psz_host,
110                                             p_sys->p_url->i_port ) ) < 0 )
111     {
112         msg_Err( p_input, "cannot connect" );
113         goto exit_error;
114     }
115
116     /* *** send first request *** */
117     p = &p_sys->buffer[0];
118     p += sprintf( p, "GET %s HTTP/1.0\r\n", p_sys->p_url->psz_path );
119     p += sprintf( p,"Accept: */*\r\n" );
120     p += sprintf( p, "User-Agent: NSPlayer/4.1.0.3856\r\n" );
121     p += sprintf( p, "Host: %s:%d\r\n",
122                   p_sys->p_url->psz_host, p_sys->p_url->i_port );
123     p += sprintf( p, "Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=0:0,request-context=%d,max-duration=0\r\n",
124                   p_sys->i_request_context++ );
125     p += sprintf( p, "Pragma: xClientGUID={"GUID_FMT"}\r\n",
126                   GUID_PRINT( p_sys->guid ) );
127     p += sprintf( p, "Connection: Close\r\n\r\n" );
128
129     net_Write( p_input, p_sys->fd, p_sys->buffer,  p - p_sys->buffer );
130
131     if( NetFill ( p_input, p_sys, BUFFER_SIZE ) <= 0 )
132     {
133         msg_Err( p_input, "cannot read answer" );
134         goto exit_error;
135     }
136     net_Close( p_sys->fd ); p_sys->fd = -1;
137
138     p_ans = http_answer_parse( p_sys->buffer, p_sys->i_buffer );
139     if( !p_ans )
140     {
141         msg_Err( p_input, "cannot parse answer" );
142         goto exit_error;
143     }
144
145     if( p_ans->i_error >= 400 )
146     {
147         msg_Err( p_input, "error %d (server return=`%s')",
148                  p_ans->i_error, p_ans->psz_answer );
149         http_answer_free( p_ans );
150         goto exit_error;
151     }
152     else if( p_ans->i_error >= 300 )
153     {
154         msg_Err( p_input, "FIXME redirect unsuported %d (server return=`%s')",
155                  p_ans->i_error, p_ans->psz_answer );
156         http_answer_free( p_ans );
157         goto exit_error;
158     }
159     else if( p_ans->i_body <= 0 )
160     {
161         msg_Err( p_input, "empty answer" );
162         http_answer_free( p_ans );
163         goto exit_error;
164     }
165
166     /* now get features */
167     /* FIXME FIXME test Content-Type to see if it's a plain stream or an
168      * asx FIXME */
169     for( p_field = p_ans->p_fields;
170          p_field != NULL;
171          p_field = http_field_find( p_field->p_next, "Pragma" ) )
172     {
173         if( !strncasecmp( p_field->psz_value, "features", 8 ) )
174         {
175             if( strstr( p_field->psz_value, "broadcast" ) )
176             {
177                 msg_Dbg( p_input, "stream type = broadcast" );
178                 p_sys->b_broadcast = VLC_TRUE;
179             }
180             else if( strstr( p_field->psz_value, "seekable" ) )
181             {
182                 msg_Dbg( p_input, "stream type = seekable" );
183                 p_sys->b_broadcast = VLC_FALSE;
184             }
185             else
186             {
187                 msg_Warn( p_input, "unknow stream types (%s)",
188                           p_field->psz_value );
189                 p_sys->b_broadcast = VLC_FALSE;
190             }
191         }
192     }
193
194     /* gather header */
195     p_sys->i_header = 0;
196     p_sys->p_header = malloc( p_ans->i_body );
197     do
198     {
199         if( chunk_parse( &ck, p_ans->p_body, p_ans->i_body ) )
200         {
201             msg_Err( p_input, "invalid chunk answer" );
202             goto exit_error;
203         }
204         if( ck.i_type != 0x4824 )
205         {
206             msg_Err( p_input, "invalid chunk (0x%x)", ck.i_type );
207             break;
208         }
209         if( ck.i_data > 0 )
210         {
211             memcpy( &p_sys->p_header[p_sys->i_header],
212                     ck.p_data,
213                     ck.i_data );
214
215             p_sys->i_header += ck.i_data;
216         }
217
218         /* BEURK */
219         p_ans->p_body   += 12 + ck.i_data;
220         p_ans->i_body   -= 12 + ck.i_data;
221
222     } while( p_ans->i_body > 12 );
223
224     http_answer_free( p_ans );
225
226     msg_Dbg( p_input, "complete header size=%d", p_sys->i_header );
227     if( p_sys->i_header <= 0 )
228     {
229         msg_Err( p_input, "header size == 0" );
230         goto exit_error;
231     }
232     /* *** parse header and get stream and their id *** */
233     /* get all streams properties,
234      *
235      * TODO : stream bitrates properties(optional)
236      *        and bitrate mutual exclusion(optional) */
237     E_( asf_HeaderParse )( &p_sys->asfh,
238                            p_sys->p_header, p_sys->i_header );
239     msg_Dbg( p_input, "packet count=%lld packet size=%d",
240              p_sys->asfh.i_data_packets_count,
241              p_sys->asfh.i_min_data_packet_size );
242
243     E_( asf_StreamSelect)( &p_sys->asfh,
244                            config_GetInt( p_input, "mms-maxbitrate" ),
245                            config_GetInt( p_input, "mms-all" ),
246                            config_GetInt( p_input, "audio" ),
247                            config_GetInt( p_input, "video" ) );
248
249     if( mmsh_start( p_input, 0 ) )
250     {
251         msg_Err( p_input, "cannot start stream" );
252         goto exit_error;
253     }
254
255     /* *** set exported functions *** */
256     p_input->pf_read = Read;
257     p_input->pf_seek = Seek;
258     p_input->pf_set_program = input_SetProgram;
259     p_input->pf_set_area = NULL;
260
261     p_input->p_private = NULL;
262     p_input->i_mtu = 3 * p_sys->asfh.i_min_data_packet_size;
263
264     /* *** finished to set some variable *** */
265     vlc_mutex_lock( &p_input->stream.stream_lock );
266     p_input->stream.b_pace_control = 0;
267     if( p_sys->b_broadcast )
268     {
269         p_input->stream.p_selected_area->i_size = 0;
270         p_input->stream.b_seekable = 0;
271     }
272     else
273     {
274         p_input->stream.p_selected_area->i_size = p_sys->asfh.i_file_size;
275         p_input->stream.b_seekable = 1;
276     }
277     p_input->stream.p_selected_area->i_tell = 0;
278     p_input->stream.i_method = INPUT_METHOD_NETWORK;
279     vlc_mutex_unlock( &p_input->stream.stream_lock );
280
281     /* Update default_pts to a suitable value for mms access */
282     var_Get( p_input, "mms-caching", &val );
283     p_input->i_pts_delay = val.i_int * 1000;
284
285     return VLC_SUCCESS;
286
287 exit_error:
288     E_( url_free )( p_sys->p_url );
289
290     if( p_sys->fd > 0 )
291     {
292         net_Close( p_sys->fd  );
293     }
294     free( p_sys );
295     return VLC_EGENERIC;
296 }
297
298 /*****************************************************************************
299  * Close: free unused data structures
300  *****************************************************************************/
301 void E_( MMSHClose ) ( input_thread_t *p_input )
302 {
303     access_sys_t    *p_sys   = p_input->p_access_data;
304
305     msg_Dbg( p_input, "stopping stream" );
306
307     mmsh_stop( p_input );
308
309     free( p_sys );
310 }
311
312 /*****************************************************************************
313  * Seek: try to go at the right place
314  *****************************************************************************/
315 static void Seek( input_thread_t * p_input, off_t i_pos )
316 {
317     access_sys_t *p_sys = p_input->p_access_data;
318     chunk_t      ck;
319     off_t        i_offset;
320     off_t        i_packet;
321
322     i_packet = ( i_pos - p_sys->i_header ) / p_sys->asfh.i_min_data_packet_size;
323     i_offset = ( i_pos - p_sys->i_header ) % p_sys->asfh.i_min_data_packet_size;
324
325     msg_Dbg( p_input, "seeking to "I64Fd, i_pos );
326
327     vlc_mutex_lock( &p_input->stream.stream_lock );
328
329     mmsh_stop( p_input );
330     mmsh_start( p_input, i_packet * p_sys->asfh.i_min_data_packet_size );
331
332     for( ;; )
333     {
334         if( mmsh_get_packet( p_input, &ck ) )
335         {
336             break;
337         }
338
339         /* skip headers */
340         if( ck.i_type != 0x4824 )
341         {
342             break;
343         }
344         msg_Warn( p_input, "skipping header" );
345     }
346
347     p_sys->i_pos = i_pos;
348     p_sys->i_packet_used += i_offset;
349
350     p_input->stream.p_selected_area->i_tell = i_pos;
351     vlc_mutex_unlock( &p_input->stream.stream_lock );
352 }
353
354 /*****************************************************************************
355  * Read:
356  *****************************************************************************/
357 static ssize_t Read        ( input_thread_t * p_input, byte_t * p_buffer,
358                              size_t i_len )
359 {
360     access_sys_t *p_sys = p_input->p_access_data;
361     size_t       i_copy;
362     size_t       i_data = 0;
363
364     while( i_data < i_len )
365     {
366         if( p_sys->i_packet_used < p_sys->i_packet_length )
367         {
368             i_copy = __MIN( p_sys->i_packet_length - p_sys->i_packet_used,
369                             i_len - i_data );
370
371             memcpy( &p_buffer[i_data],
372                     &p_sys->p_packet[p_sys->i_packet_used],
373                     i_copy );
374
375             i_data += i_copy;
376             p_sys->i_packet_used += i_copy;
377         }
378         else if( p_sys->i_pos + i_data > p_sys->i_header &&
379                  (int)p_sys->i_packet_used < p_sys->asfh.i_min_data_packet_size )
380         {
381             i_copy = __MIN( p_sys->asfh.i_min_data_packet_size - p_sys->i_packet_used,
382                             i_len - i_data );
383
384             memset( &p_buffer[i_data], 0, i_copy );
385
386             i_data += i_copy;
387             p_sys->i_packet_used += i_copy;
388         }
389         else
390         {
391             chunk_t ck;
392             /* get a new packet */
393             /* fill enought data (>12) */
394             msg_Dbg( p_input, "waiting data (buffer = %d bytes)",
395                      p_sys->i_buffer );
396
397             if( mmsh_get_packet( p_input, &ck ) )
398             {
399                 return 0;
400             }
401         }
402     }
403
404     p_sys->i_pos += i_data;
405
406     return( i_data );
407 }
408
409 /*****************************************************************************
410  * NetFill:
411  *****************************************************************************/
412 static ssize_t NetFill( input_thread_t *p_input, access_sys_t *p_sys, int i_size )
413 {
414     int i_try   = 0;
415     int i_total = 0;
416
417     i_size = __MIN( i_size, BUFFER_SIZE - p_sys->i_buffer );
418     if( i_size <= 0 )
419     {
420         return 0;
421     }
422
423     for( ;; )
424     {
425         int i_read;
426
427         i_read = net_Read( p_input, p_sys->fd,
428                           &p_sys->buffer[p_sys->i_buffer], i_size, VLC_FALSE );
429
430         if( i_read == 0 )
431         {
432             if( i_try++ > 2 )
433             {
434                 break;
435             }
436             msg_Dbg( p_input, "another try %d/2", i_try );
437             continue;
438         }
439
440         if( i_read < 0 || p_input->b_die || p_input->b_error )
441         {
442             break;
443         }
444         i_total += i_read;
445
446         p_sys->i_buffer += i_read;
447         if( i_total >= i_size )
448         {
449             break;
450         }
451     }
452
453     p_sys->buffer[p_sys->i_buffer] = '\0';
454
455     return i_total;
456 }
457
458
459 /*****************************************************************************
460  *
461  *****************************************************************************/
462 static int mmsh_start( input_thread_t *p_input, off_t i_pos )
463 {
464     access_sys_t *p_sys = p_input->p_access_data;
465     uint8_t *p;
466     int i_streams = 0;
467     int i;
468     http_answer_t *p_ans;
469
470     msg_Dbg( p_input, "starting stream" );
471
472     if( ( p_sys->fd = net_OpenTCP( p_input, p_sys->p_url->psz_host,
473                                             p_sys->p_url->i_port ) ) < 0 )
474     {
475         /* should not occur */
476         msg_Err( p_input, "cannot connect to the server" );
477         return VLC_EGENERIC;
478     }
479
480     for( i = 1; i < 128; i++ )
481     {
482         if( p_sys->asfh.stream[i].i_selected )
483         {
484             i_streams++;
485         }
486     }
487
488     if( i_streams <= 0 )
489     {
490         msg_Err( p_input, "no stream selected" );
491         return VLC_EGENERIC;
492     }
493
494     p = &p_sys->buffer[0];
495     p += sprintf( p, "GET %s HTTP/1.0\r\n", p_sys->p_url->psz_path );
496     p += sprintf( p,"Accept: */*\r\n" );
497     p += sprintf( p, "User-Agent: NSPlayer/4.1.0.3856\r\n" );
498     p += sprintf( p, "Host: %s:%d\r\n",
499                   p_sys->p_url->psz_host, p_sys->p_url->i_port );
500     if( p_sys->b_broadcast )
501     {
502         p += sprintf( p,"Pragma: no-cache,rate=1.000000,request-context=%d\r\n",
503                       p_sys->i_request_context++ );
504     }
505     else
506     {
507         p += sprintf( p, "Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=%u:%u,request-context=%d,max-duration=0\r\n",
508                          (uint32_t)((i_pos >> 32)&0xffffffff),
509                          (uint32_t)(i_pos&0xffffffff),
510                          p_sys->i_request_context++ );
511     }
512     p += sprintf( p, "Pragma: xPlayStrm=1\r\n" );
513     p += sprintf( p, "Pragma: xClientGUID={"GUID_FMT"}\r\n",
514                   GUID_PRINT( p_sys->guid ) );
515     p += sprintf( p, "Pragma: stream-switch-count=%d\r\n", i_streams );
516     p += sprintf( p, "Pragma: stream-switch-entry=" );
517     for( i = 0; i < i_streams; i++ )
518     {
519         if( p_sys->asfh.stream[i].i_selected )
520         {
521             p += sprintf( p, "ffff:%d:0 ", p_sys->asfh.stream[i].i_id );
522         }
523         else
524         {
525             p += sprintf( p, "ffff:%d:2 ", p_sys->asfh.stream[i].i_id );
526         }
527     }
528     p += sprintf( p, "\r\n" );
529     p += sprintf( p, "Connection: Close\r\n\r\n" );
530
531     net_Write( p_input, p_sys->fd, p_sys->buffer,  p - p_sys->buffer );
532
533     msg_Dbg( p_input, "filling buffer" );
534     /* we read until we found a \r\n\r\n or \n\n */
535     p_sys->i_buffer = 0;
536     p_sys->i_buffer_pos = 0;
537     for( ;; )
538     {
539         int     i_try = 0;
540         int     i_read;
541         uint8_t *p;
542
543         p = &p_sys->buffer[p_sys->i_buffer];
544         i_read = net_Read( p_input, p_sys->fd, &p_sys->buffer[p_sys->i_buffer], 1024, VLC_FALSE );
545
546         if( i_read == 0 )
547         {
548             if( i_try++ > 12 )
549             {
550                 break;
551             }
552             msg_Dbg( p_input, "another try (%d/12)", i_try );
553             continue;
554         }
555
556         if( i_read <= 0 || p_input->b_die || p_input->b_error )
557         {
558             break;
559         }
560         p_sys->i_buffer += i_read;
561         p_sys->buffer[p_sys->i_buffer] = '\0';
562
563         if( strstr( p, "\r\n\r\n" ) || strstr( p, "\n\n" ) )
564         {
565             msg_Dbg( p_input, "body found" );
566             break;
567         }
568         if( p_sys->i_buffer >= BUFFER_SIZE - 1024 )
569         {
570             msg_Dbg( p_input, "buffer size exeded" );
571             break;
572         }
573     }
574
575     p_ans = http_answer_parse( p_sys->buffer, p_sys->i_buffer );
576     if( !p_ans )
577     {
578         msg_Err( p_input, "cannot parse answer" );
579         return VLC_EGENERIC;
580     }
581
582     if( p_ans->i_error < 200 || p_ans->i_error >= 300 )
583     {
584         msg_Err( p_input, "error %d (server return=`%s')",
585                  p_ans->i_error, p_ans->psz_answer );
586         http_answer_free( p_ans );
587         return VLC_EGENERIC;
588     }
589
590     if( !p_ans->p_body )
591     {
592         p_sys->i_buffer_pos = 0;
593         p_sys->i_buffer = 0;
594     }
595     else
596     {
597         p_sys->i_buffer_pos = p_ans->p_body - p_sys->buffer;
598     }
599     http_answer_free( p_ans );
600
601     return VLC_SUCCESS;
602 }
603
604 /*****************************************************************************
605  *
606  *****************************************************************************/
607 static void mmsh_stop( input_thread_t *p_input )
608 {
609     access_sys_t *p_sys = p_input->p_access_data;
610
611     msg_Dbg( p_input, "closing stream" );
612     net_Close( p_sys->fd ); p_sys->fd = -1;
613 }
614
615 /*****************************************************************************
616  *
617  *****************************************************************************/
618 static int mmsh_get_packet( input_thread_t * p_input, chunk_t *p_ck )
619 {
620     access_sys_t *p_sys = p_input->p_access_data;
621
622     int i_mov = p_sys->i_buffer - p_sys->i_buffer_pos;
623
624     if( p_sys->i_buffer_pos > BUFFER_SIZE / 2 )
625     {
626         if( i_mov > 0 )
627         {
628             memmove( &p_sys->buffer[0],
629                      &p_sys->buffer[p_sys->i_buffer_pos],
630                      i_mov );
631         }
632
633         p_sys->i_buffer     = i_mov;
634         p_sys->i_buffer_pos = 0;
635     }
636
637     if( NetFill( p_input, p_sys, 12 ) < 12 )
638     {
639         msg_Warn( p_input, "cannot fill buffer" );
640         return VLC_EGENERIC;
641     }
642
643     chunk_parse( p_ck, &p_sys->buffer[p_sys->i_buffer_pos],
644                  p_sys->i_buffer - p_sys->i_buffer_pos );
645
646     if( p_ck->i_type == 0x4524 )   // Transfer complete
647     {
648         msg_Warn( p_input, "EOF" );
649         return VLC_EGENERIC;
650     }
651     else if( p_ck->i_type != 0x4824 && p_ck->i_type != 0x4424 )
652     {
653         msg_Err( p_input, "invalid chunk FATAL" );
654         return VLC_EGENERIC;
655     }
656
657     if( p_ck->i_data < p_ck->i_size2 - 8 )
658     {
659         if( NetFill( p_input, p_sys, p_ck->i_size2 - 8 - p_ck->i_data ) <= 0 )
660         {
661             msg_Warn( p_input, "cannot fill buffer" );
662             return VLC_EGENERIC;
663         }
664         chunk_parse( p_ck, &p_sys->buffer[p_sys->i_buffer_pos],
665                      p_sys->i_buffer - p_sys->i_buffer_pos );
666     }
667
668     if( p_sys->i_packet_sequence != 0 &&
669         p_ck->i_sequence != p_sys->i_packet_sequence )
670     {
671         msg_Warn( p_input, "packet lost ?" );
672     }
673
674     p_sys->i_packet_sequence = p_ck->i_sequence + 1;
675     p_sys->i_packet_used   = 0;
676     p_sys->i_packet_length = p_ck->i_data;
677     p_sys->p_packet        = p_ck->p_data;
678
679     p_sys->i_buffer_pos += 12 + p_ck->i_data;
680
681     return VLC_SUCCESS;
682 }
683
684 /*****************************************************************************
685  *
686  *****************************************************************************/
687 static int http_next_line( uint8_t **pp_data, int *pi_data )
688 {
689     char *p, *p_end = *pp_data + *pi_data;
690
691     for( p = *pp_data; p < p_end; p++ )
692     {
693         if( p + 1 < p_end && *p == '\n' )
694         {
695             *pi_data = p_end - p - 1;
696             *pp_data = p + 1;
697             return VLC_SUCCESS;
698         }
699         if( p + 2 < p_end && p[0] == '\r' && p[1] == '\n' )
700         {
701             *pi_data = p_end - p - 2;
702             *pp_data = p + 2;
703             return VLC_SUCCESS;
704         }
705     }
706     *pi_data = 0;
707     *pp_data = p_end;
708     return VLC_EGENERIC;
709 }
710
711 /*****************************************************************************
712  *
713  *****************************************************************************/
714 static http_answer_t *http_answer_parse( uint8_t *p_data, int i_data )
715 {
716     http_answer_t *ans = malloc( sizeof( http_answer_t ) );
717     http_field_t  **pp_last;
718     char          *p, *end;
719
720     if( strncmp( p_data, "HTTP/1.", 7 ) )
721     {
722         free( ans );
723         return NULL;
724     }
725     ans->i_version = atoi( &p_data[7] );
726     ans->i_error   = strtol( p_data + 8, &p, 0 );
727     while( *p == ' ' )
728     {
729         p++;
730     }
731     if( ( ( end = strchr( p, '\r' ) ) == NULL )&&
732         ( ( end = strchr( p, '\n' ) ) == NULL ) )
733     {
734         end = &p_data[i_data];
735     }
736
737     ans->psz_answer = strndup( p, end - p );
738
739     fprintf( stderr, "version=%d error=%d answer=%s\n",
740              ans->i_version, ans->i_error, ans->psz_answer );
741     ans->p_fields = NULL;
742     ans->i_body   = 0;
743     ans->p_body   = 0;
744
745     pp_last = &ans->p_fields;
746
747     for( ;; )
748     {
749         http_field_t  *p_field;
750         uint8_t       *colon;
751
752         if( http_next_line( &p_data, &i_data ) )
753         {
754             return ans;
755         }
756         if( !strncmp( p_data, "\r\n", 2 ) || !strncmp( p_data, "\n", 1 ) )
757         {
758             break;
759         }
760
761         colon = strstr( p_data, ": " );
762         if( colon )
763         {
764             uint8_t *end;
765
766             end = strstr( colon, "\n" ) - 1;
767             if( *end != '\r' )
768             {
769                 end++;
770             }
771
772             p_field             = malloc( sizeof( http_field_t ) );
773             p_field->psz_name   = strndup( p_data, colon - p_data );
774             p_field->psz_value  = strndup( colon + 2, end - colon - 2 );
775             p_field->p_next     = NULL;
776
777             *pp_last = p_field;
778             pp_last = &p_field->p_next;
779
780             fprintf( stderr, "field name=`%s' value=`%s'\n",
781                      p_field->psz_name, p_field->psz_value );
782         }
783     }
784
785     if( http_next_line( &p_data, &i_data ) )
786     {
787         return ans;
788     }
789
790     ans->p_body = p_data;
791     ans->i_body = i_data;
792     fprintf( stderr, "body size=%d\n", i_data );
793
794     return ans;
795 }
796
797 /*****************************************************************************
798  *
799  *****************************************************************************/
800 static void http_answer_free( http_answer_t *ans )
801 {
802     http_field_t  *p_field = ans->p_fields;
803
804     while( p_field )
805     {
806         http_field_t *p_next;
807
808         p_next = p_field->p_next;
809         free( p_field->psz_name );
810         free( p_field->psz_value );
811         free( p_field );
812
813         p_field = p_next;
814     }
815
816     free( ans->psz_answer );
817     free( ans );
818 }
819
820 /*****************************************************************************
821  *
822  *****************************************************************************/
823 static http_field_t *http_field_find( http_field_t *p_field, char *psz_name )
824 {
825
826     while( p_field )
827     {
828         if( !strcasecmp( p_field->psz_name, psz_name ) )
829         {
830             return p_field;
831         }
832
833         p_field = p_field->p_next;
834     }
835
836     return NULL;
837 }
838
839 /*****************************************************************************
840  *
841  *****************************************************************************/
842 static int chunk_parse( chunk_t *ck, uint8_t *p_data, int i_data )
843 {
844     if( i_data < 12 )
845     {
846         return VLC_EGENERIC;
847     }
848
849     ck->i_type      = GetWLE( p_data );
850     ck->i_size      = GetWLE( p_data + 2);
851     ck->i_sequence  = GetDWLE( p_data  + 4);
852     ck->i_unknown   = GetWLE( p_data + 8);
853     ck->i_size2     = GetWLE( p_data + 10);
854
855     ck->p_data      = p_data + 12;
856     ck->i_data      = __MIN( i_data - 12, ck->i_size2 - 8 );
857
858     return VLC_SUCCESS;
859 }
860