]> git.sesse.net Git - vlc/blob - modules/misc/sap.c
b9642905e571c69fb0cf88b666e49ed46ae95568
[vlc] / modules / misc / sap.c
1 /*****************************************************************************
2  * sap.c :  SAP interface module
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  * $Id: sap.c,v 1.38 2003/11/22 12:33:04 sigmunau Exp $
6  *
7  * Authors: Arnaud Schauly <gitan@via.ecp.fr>
8  *          ClĂ©ment Stenac <zorglub@via.ecp.fr>
9  *          Damien Lucas <nitrox@videolan.org>
10  *          Laurent Aimar <fenrir@via.ecp.fr>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #include <stdlib.h>                                      /* malloc(), free() */
31
32 #include <vlc/vlc.h>
33 #include <vlc/intf.h>
34
35 #include <errno.h>                                                 /* ENOMEM */
36 #include <ctype.h>
37
38 #ifdef HAVE_UNISTD_H
39 #    include <unistd.h>
40 #endif
41 #ifdef HAVE_SYS_TIME_H
42 #    include <sys/time.h>
43 #endif
44
45 #ifdef WIN32
46 #   include <winsock2.h>
47 #   include <ws2tcpip.h>
48 #   ifndef IN_MULTICAST
49 #       define IN_MULTICAST(a) IN_CLASSD(a)
50 #   endif
51 #else
52 #   include <sys/socket.h>
53 #   include <netinet/in.h>
54 #   if HAVE_ARPA_INET_H
55 #      include <arpa/inet.h>
56 #   elif defined( SYS_BEOS )
57 #      include <net/netdb.h>
58 #   endif
59 #endif
60
61 #ifdef UNDER_CE
62 #   define close(a) CloseHandle(a)
63 #elif defined( WIN32 )
64 #   define close(a) closesocket(a)
65 #endif
66
67 #include "network.h"
68
69 #ifdef HAVE_ZLIB_H
70 #   include <zlib.h>
71 #endif
72
73 #define MAX_LINE_LENGTH 256
74
75 /* SAP is always on that port */
76 #define HELLO_PORT 9875
77 #define HELLO_GROUP "224.2.127.254"
78 #define ADD_SESSION 1
79
80 #define IPV6_ADDR_1 "FF0"  /* Scope is inserted between them */
81 #define IPV6_ADDR_2 "::2:7FFE"
82
83
84 /*****************************************************************************
85  * Module descriptor
86  *****************************************************************************/
87 #define SAP_ADDR_TEXT N_("SAP multicast address")
88 #define SAP_ADDR_LONGTEXT N_("SAP multicast address")
89 #define SAP_IPV4_TEXT N_("IPv4-SAP listening")
90 #define SAP_IPV4_LONGTEXT N_("Set this if you want SAP to listen for IPv4 announces")
91 #define SAP_IPV6_TEXT N_("IPv6-SAP listening")
92 #define SAP_IPV6_LONGTEXT N_("Set this if you want SAP to listen for IPv6 announces")
93 #define SAP_SCOPE_TEXT N_("IPv6 SAP scope")
94 #define SAP_SCOPE_LONGTEXT N_("Sets the scope for IPv6 announces (default is 8)")
95
96 static int  Open ( vlc_object_t * );
97 static void Close( vlc_object_t * );
98
99 vlc_module_begin();
100     add_category_hint( N_("SAP"), NULL, VLC_TRUE );
101         add_string( "sap-addr", NULL, NULL,
102                      SAP_ADDR_TEXT, SAP_ADDR_LONGTEXT, VLC_TRUE );
103
104         add_bool( "sap-ipv4", 1 , NULL,
105                      SAP_IPV4_TEXT,SAP_IPV4_LONGTEXT, VLC_TRUE);
106
107         add_bool( "sap-ipv6", 0 , NULL,
108                    SAP_IPV6_TEXT, SAP_IPV6_LONGTEXT, VLC_TRUE);
109
110         add_string( "sap-ipv6-scope", "8" , NULL,
111                     SAP_SCOPE_TEXT, SAP_SCOPE_LONGTEXT, VLC_TRUE);
112
113     set_description( _("SAP interface") );
114     set_capability( "interface", 0 );
115     set_callbacks( Open, Close );
116 vlc_module_end();
117
118 /*****************************************************************************
119  * Local prototypes
120  *****************************************************************************/
121
122 static void Run    ( intf_thread_t *p_intf );
123 static ssize_t NetRead( intf_thread_t *, int fd[2], uint8_t *, int );
124
125 typedef struct media_descr_t media_descr_t;
126 typedef struct sess_descr_t sess_descr_t;
127 typedef struct attr_descr_t attr_descr_t;
128
129 static void sess_toitem( intf_thread_t *, sess_descr_t * );
130
131 static sess_descr_t *  parse_sdp( intf_thread_t *, char * ) ;
132 static void free_sd( sess_descr_t * );
133
134 /* Detect multicast addresses */
135 static int  ismult( char * );
136
137 /* The struct that contains sdp informations */
138 struct  sess_descr_t
139 {
140     int  i_version;
141     char *psz_sessionname;
142     char *psz_connection;
143     char *psz_sdp;
144
145     int           i_media;
146     media_descr_t **pp_media;
147     int           i_attributes;
148     attr_descr_t  **pp_attributes;
149 };
150
151 /* All this informations are not useful yet.  */
152 struct media_descr_t
153 {
154     char *psz_medianame;
155     char *psz_mediaconnection;
156 };
157
158 struct attr_descr_t
159 {
160     char *psz_field;
161     char *psz_value;
162 };
163
164 struct intf_sys_t
165 {
166     /* IPV4 and IPV6 */
167     int fd[2];
168
169     /* playlist group */
170     int i_group;
171 };
172
173 #ifdef HAVE_ZLIB_H
174 int do_decompress(unsigned char *src, unsigned char **_dst, int slen) {
175   int result, dstsize, n;
176   unsigned char *dst;
177   z_stream d_stream;
178
179   d_stream.zalloc = (alloc_func)0;
180   d_stream.zfree = (free_func)0;
181   d_stream.opaque = (voidpf)0;
182   result = inflateInit(&d_stream);
183   if (result != Z_OK) {
184     printf("inflateInit() failed. Result: %d\n", result);
185     return(-1);
186   }
187
188   d_stream.next_in = (Bytef *)src;
189   d_stream.avail_in = slen;
190   n = 0;
191   dst = NULL;
192   do {
193     n++;
194     dst = (unsigned char *)realloc(dst, n * 1000);
195     d_stream.next_out = (Bytef *)&dst[(n - 1) * 1000];
196     d_stream.avail_out = 1000;
197     result = inflate(&d_stream, Z_NO_FLUSH);
198     if ((result != Z_OK) && (result != Z_STREAM_END)) {
199       printf("Zlib decompression failed. Result: %d\n", result);
200       return(-1);
201     }
202   } while ((d_stream.avail_out == 0) && (d_stream.avail_in != 0) &&
203            (result != Z_STREAM_END));
204
205   dstsize = d_stream.total_out;
206   inflateEnd(&d_stream);
207
208   *_dst = (unsigned char *)realloc(dst, dstsize);
209
210   return dstsize;
211 }
212 #endif
213
214 /*****************************************************************************
215  * Open: initialize and create stuff
216  *****************************************************************************/
217 static int Open( vlc_object_t *p_this )
218 {
219     intf_thread_t *p_intf = (intf_thread_t*)p_this;
220     intf_sys_t    *p_sys  = malloc( sizeof( intf_sys_t ) );
221
222     playlist_t          *p_playlist;
223
224     p_sys->fd[0] = -1;
225     p_sys->fd[1] = -1;
226     if( config_GetInt( p_intf, "sap-ipv4" ) )
227     {
228         char *psz_address = config_GetPsz( p_intf, "sap-addr" );
229         network_socket_t    sock;
230         module_t            *p_network;
231         if( psz_address == NULL || *psz_address == '\0' )
232         {
233             psz_address = strdup( HELLO_GROUP );
234         }
235
236         /* Prepare the network_socket_t structure */
237         sock.i_type            = NETWORK_UDP;
238         sock.psz_bind_addr     = psz_address;
239         sock.i_bind_port       = HELLO_PORT;
240         sock.psz_server_addr   = "";
241         sock.i_server_port     = 0;
242         sock.i_ttl             = 0;
243         p_intf->p_private = (void*) &sock;
244
245         p_network = module_Need( p_intf, "network", "ipv4" );
246         if( p_network )
247         {
248             p_sys->fd[0] = sock.i_handle;
249             module_Unneed( p_intf, p_network );
250         }
251         else
252         {
253             msg_Warn( p_intf, "failed to open %s:%d", psz_address, HELLO_PORT );
254         }
255         free( psz_address );
256     }
257
258     if( config_GetInt( p_intf, "sap-ipv6" ) )
259     {
260         char psz_address[100];
261         char *psz_scope = config_GetPsz( p_intf, "sap-ipv6-scope" );
262         network_socket_t    sock;
263         module_t            *p_network;
264
265         if( psz_scope == NULL || *psz_scope == '\0' )
266         {
267             psz_scope = strdup( "8" );
268         }
269         snprintf( psz_address, 100, "[%s%c%s]",IPV6_ADDR_1, psz_scope[0], IPV6_ADDR_2 );
270         free( psz_scope );
271
272         sock.i_type            = NETWORK_UDP;
273         sock.psz_bind_addr     = psz_address;
274         sock.i_bind_port       = HELLO_PORT;
275         sock.psz_server_addr   = "";
276         sock.i_server_port     = 0;
277         sock.i_ttl             = 0;
278         p_intf->p_private = (void*) &sock;
279
280         p_network = module_Need( p_intf, "network", "ipv6" );
281         if( p_network )
282         {
283             p_sys->fd[1] = sock.i_handle;
284             module_Unneed( p_intf, p_network );
285         }
286         else
287         {
288             msg_Warn( p_intf, "failed to open %s:%d", psz_address, HELLO_PORT );
289         }
290     }
291     if( p_sys->fd[0] <= 0 && p_sys->fd[1] <= 0 )
292     {
293         msg_Warn( p_intf, "IPV4 and IPV6 failed" );
294         free( p_sys );
295         return VLC_EGENERIC;
296     }
297
298     /* Create our playlist group */
299     p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
300                                                 FIND_ANYWHERE );
301     if( p_playlist )
302     {
303         playlist_group_t *p_group = playlist_CreateGroup( p_playlist , "SAP" );
304         p_sys->i_group = p_group->i_id;
305         vlc_object_release( p_playlist );
306     }
307
308     p_intf->pf_run = Run;
309     p_intf->p_sys  = p_sys;
310
311     return VLC_SUCCESS;
312 }
313
314 /*****************************************************************************
315  * Close:
316  *****************************************************************************/
317 static void Close( vlc_object_t *p_this )
318 {
319     intf_thread_t *p_intf = (intf_thread_t*)p_this;
320     intf_sys_t    *p_sys  = p_intf->p_sys;
321
322     if( p_sys->fd[0] > 0 )
323     {
324         close( p_sys->fd[0] );
325     }
326     if( p_sys->fd[1] > 0 )
327     {
328         close( p_sys->fd[1] );
329     }
330
331     free( p_sys );
332 }
333
334 /*****************************************************************************
335  * Run: sap thread
336  *****************************************************************************
337  * Listens to SAP packets, and sends them to packet_handle
338  *****************************************************************************/
339 #define MAX_SAP_BUFFER 5000
340
341 static void Run( intf_thread_t *p_intf )
342 {
343     intf_sys_t *p_sys  = p_intf->p_sys;
344     uint8_t     buffer[MAX_SAP_BUFFER + 1];
345     uint8_t    *p_end;
346
347     /* read SAP packets */
348     while( !p_intf->b_die )
349     {
350         int i_read = NetRead( p_intf, p_sys->fd, buffer, MAX_SAP_BUFFER );
351         uint8_t *p_sdp;
352         int i_version;
353         int i_address_type;
354         int b_reserved;
355         int b_message_type;
356         int b_encrypted;
357         int b_compressed;
358         int i_mesg_id_hash;
359         unsigned char *p_decompressed_buffer;
360         int i_decompressed_size;
361         
362         /* Minimum length is > 6 */
363         if( i_read <= 6 )
364         {
365             if( i_read < 0 )
366             {
367                 msg_Warn( p_intf, "Cannot read in the socket" );
368             }
369             continue;
370         }
371
372         buffer[i_read] = '\0';
373         p_end = &buffer[i_read];
374
375         /* Parse the SAP header */
376         i_version = buffer[0] >> 5;
377         if( i_version != 1 )
378         {
379             msg_Warn( p_intf, "strange sap version %d found", i_version );
380         }
381         i_address_type = buffer[0] & 0x10;
382         b_reserved = buffer[0] & 0x08;
383         if( b_reserved != 0 )
384         {
385             msg_Warn( p_intf, "reserved bit incorrectly set" );
386         }
387         b_message_type = buffer[0] & 0x04;
388         if( b_message_type != 0 )
389         {
390             msg_Warn( p_intf, "got session deletion packet" );
391         }
392         b_encrypted = buffer[0] & 0x02;
393         if( b_encrypted )
394         {
395             msg_Warn( p_intf, "encrypted packet" );
396         }
397         b_compressed = buffer[0] & 0x01;
398         p_sdp  = &buffer[4];
399         if( i_address_type == 0 ) /* ipv4 source address */
400         {
401             p_sdp += 4;
402         }
403         else /* ipv6 source address */
404         {
405             p_sdp += 16;
406         }
407         if( b_compressed )
408         {
409 #ifdef HAVE_ZLIB_H
410             i_decompressed_size = do_decompress( p_sdp, &p_decompressed_buffer, i_read - ( p_sdp - buffer ) );
411             if( i_decompressed_size > 0 && i_decompressed_size < MAX_SAP_BUFFER )
412             {
413                 memcpy( p_sdp, p_decompressed_buffer, i_decompressed_size );
414                 p_sdp[i_decompressed_size] = '\0';
415                 p_end = &p_sdp[i_decompressed_size];
416                 free( p_decompressed_buffer );
417             }
418 #else
419             msg_Warn( p_intf, "Ignoring compressed sap packet" );
420 #endif
421         }
422         p_sdp += buffer[1]; /* size of signature */
423         while( p_sdp < p_end - 1 && *p_sdp != '\0' && p_sdp[0] != 'v' && p_sdp[1] != '=' )
424         {
425             p_sdp++;
426         }
427         if( *p_sdp == '\0' )
428         {
429             p_sdp++;
430         }
431
432         if( p_sdp < p_end )
433         {
434             sess_descr_t *p_sd = parse_sdp( p_intf, p_sdp );
435             if( p_sd )
436             {
437                 sess_toitem ( p_intf, p_sd );
438                 free_sd ( p_sd );
439             }
440         }
441         else
442         {
443             msg_Warn( p_intf, "ditching sap packet" );
444         }
445     }
446 }
447
448 /**********************************************************************
449  * cfield_parse
450  *********************************************************************
451  * put into *ppsz_uri, the the uri in the cfield, psz_cfield.
452  *********************************************************************/
453
454 static void cfield_parse( char *psz_cfield, char **ppsz_uri )
455 {
456
457     char *psz_pos;
458     if( psz_cfield )
459     {
460         psz_pos = psz_cfield;
461
462         while( *psz_pos != ' ' && *psz_pos !='\0' )
463         {
464             psz_pos++;
465         }
466         psz_pos++;
467         while( *psz_pos != ' ' && *psz_pos !='\0' )
468         {
469             psz_pos++;
470         }
471         psz_pos++;
472         *ppsz_uri = psz_pos;
473         while( *psz_pos != ' ' && *psz_pos !='/'
474                         && *psz_pos != '\0' )
475         {
476             psz_pos++;
477         }
478         *psz_pos = '\0';
479
480     }
481     else
482     {
483         ppsz_uri = NULL;
484     }
485
486     return;
487
488 }
489
490 /**********************************************************************
491  * mfield_parse
492  *********************************************************************
493  * put into *ppsz_proto, and *ppsz_port, the protocol and the port.
494  *********************************************************************/
495
496
497 static void mfield_parse( char *psz_mfield, char **ppsz_proto,
498                char **ppsz_port )
499 {
500     char *psz_pos;
501     char *psz_media;
502     if( psz_mfield )
503     {
504         psz_pos = psz_mfield;
505         psz_media = psz_mfield;
506         while( *psz_pos != '\0' && *psz_pos != ' ' )
507         {
508             psz_pos++;
509         }
510         if( *psz_pos != '\0' )
511         {
512             *psz_pos = '\0';
513             if( strcmp( psz_media, "video" ) && strcmp( psz_media, "audio" ) )
514             {
515                 *ppsz_proto = NULL;
516                 *ppsz_port = NULL;
517                 return;
518             }
519         }
520         psz_pos++;
521         *ppsz_port = psz_pos;
522         while( *psz_pos != '\0' && *psz_pos !=' ' && *psz_pos!='/' )
523         {
524             psz_pos++;
525         }
526         if( *psz_pos == '/' )  // FIXME does not support multi-port
527         {
528             *psz_pos = '\0';
529             psz_pos++;
530             while( *psz_pos != '\0' && *psz_pos !=' ' )
531             {
532             psz_pos++;
533             }
534         }
535         *psz_pos = '\0';
536         psz_pos++;
537         *ppsz_proto = psz_pos;
538         while( *psz_pos!='\0' && *psz_pos !=' ' &&
539                         *psz_pos!='/' )
540         {
541             *psz_pos = tolower( *psz_pos );
542             psz_pos++;
543         }
544         *psz_pos = '\0';
545     }
546     else
547     {
548         *ppsz_proto = NULL;
549         *ppsz_port = NULL;
550     }
551     return;
552 }
553
554
555 /*******************************************************************
556  * sess_toitem : changes a sess_descr_t into a hurd of
557  * playlist_item_t, which are enqueued.
558  *******************************************************************
559  * Note : does not support sessions that take place on consecutive
560  * port or adresses yet.
561  *******************************************************************/
562
563 static void sess_toitem( intf_thread_t * p_intf, sess_descr_t * p_sd )
564 {
565     playlist_item_t * p_item;
566     char *psz_uri, *psz_proto;
567     char *psz_port;
568     char *psz_uri_default;
569     int i_count , i;
570     vlc_bool_t b_http = VLC_FALSE;
571     char *psz_http_path = NULL;
572     playlist_t *p_playlist = NULL;
573
574     psz_uri_default = NULL;
575     if( p_sd->i_media > 1 )
576     {
577         p_item = malloc( sizeof( playlist_item_t ) );
578         if( p_item == NULL )
579         {
580             msg_Warn( p_intf, "out of memory" );
581             return;
582         }
583         p_item->psz_name    = strdup( p_sd->psz_sessionname );
584         p_item->psz_uri     = NULL;
585         p_item->i_duration  = -1;
586         p_item->ppsz_options= NULL;
587         p_item->i_options   = 0;
588
589         p_item->i_type      = 0;
590         p_item->i_status    = 0;
591         p_item->b_autodeletion = VLC_FALSE;
592         p_item->b_enabled   = VLC_TRUE;
593         p_item->i_group     = p_intf->p_sys->i_group;
594         p_item->psz_author  = strdup( "" );
595         psz_uri = malloc( strlen( p_sd->psz_sdp ) + 7 );
596         if( psz_uri == NULL )
597         {
598             msg_Warn( p_intf, "out of memory" );
599             free( p_item->psz_name );
600             free( p_item->psz_author );
601             free( p_item );
602             return;
603         }
604         p_item->psz_uri = psz_uri;
605         memcpy( psz_uri, "sdp://", 6 );
606         psz_uri += 6;
607         memcpy( psz_uri, p_sd->psz_sdp, strlen( p_sd->psz_sdp ) + 1 );
608         /* Enqueueing p_item in the playlist */
609         p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
610         playlist_AddItem ( p_playlist, p_item, PLAYLIST_CHECK_INSERT, PLAYLIST_END );
611         vlc_object_release( p_playlist );
612         return;
613     }        
614         
615     cfield_parse( p_sd->psz_connection, &psz_uri_default );
616
617     for( i_count = 0 ; i_count < p_sd->i_media ; i_count++ )
618     {
619         p_item = malloc( sizeof( playlist_item_t ) );
620         p_item->psz_name    = strdup( p_sd->psz_sessionname );
621         p_item->psz_uri     = NULL;
622         p_item->i_duration  = -1;
623         p_item->ppsz_options= NULL;
624         p_item->i_options   = 0;
625
626         p_item->i_type      = 0;
627         p_item->i_status    = 0;
628         p_item->b_autodeletion = VLC_FALSE;
629         p_item->b_enabled   = VLC_TRUE;
630         p_item->i_group     = p_intf->p_sys->i_group;
631         p_item->psz_author  = strdup( "" );
632
633         psz_uri = NULL;
634
635         /* Build what we have to put in p_item->psz_uri, with the m and
636          *  c fields  */
637
638         if( !p_sd->pp_media[i_count] )
639         {
640             return;
641         }
642
643         mfield_parse( p_sd->pp_media[i_count]->psz_medianame,
644                         & psz_proto, & psz_port );
645
646         if( !psz_proto || !psz_port )
647         {
648             return;
649         }
650
651         if( p_sd->pp_media[i_count]->psz_mediaconnection )
652         {
653             cfield_parse( p_sd->pp_media[i_count]->psz_mediaconnection,
654                             & psz_uri );
655         }
656         else
657         {
658             psz_uri = psz_uri_default;
659         }
660
661         if( psz_uri == NULL )
662         {
663             return;
664         }
665
666         for( i = 0 ; i< p_sd->i_attributes ; i++ )
667         {
668             if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "type") &&
669                 strstr( p_sd->pp_attributes[i]->psz_value, "http") )
670             {
671                 b_http = VLC_TRUE;
672             }
673             if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "http-path"))
674             {
675                 psz_http_path = strdup(  p_sd->pp_attributes[i]->psz_value );
676             }
677             if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "plgroup"))
678             {
679                 int i_id;
680                 p_playlist =
681                 (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
682                                                         FIND_ANYWHERE );
683                 if( p_playlist == NULL )
684                 {
685                     return;
686                 }
687
688                 i_id = playlist_GroupToId( p_playlist,
689                                            p_sd->pp_attributes[i]->psz_value);
690                 if( i_id != 0 )
691                 {
692                     p_item->i_group = i_id;
693                 }
694                 else
695                 {
696                     playlist_group_t *p_group =
697                             playlist_CreateGroup( p_playlist,
698                                        p_sd->pp_attributes[i]->psz_value);
699                     p_item->i_group = p_group->i_id;
700                 }
701                 vlc_object_release( p_playlist );
702             }
703         }
704
705
706         /* Filling p_item->psz_uri */
707         if( b_http == VLC_FALSE )
708         {
709             p_item->psz_uri = malloc( strlen( psz_proto ) + strlen( psz_uri ) +
710                                       strlen( psz_port ) + 7 );
711             if( ismult( psz_uri ) )
712             {
713                 sprintf( p_item->psz_uri, "%s://@%s:%s",
714                          psz_proto, psz_uri, psz_port );
715             }
716             else
717             {
718                 sprintf( p_item->psz_uri, "%s://%s:%s",
719                          psz_proto, psz_uri, psz_port );
720             }
721         }
722         else
723         {
724             if( psz_http_path == NULL )
725             {
726                 psz_http_path = strdup( "/" );
727             }
728
729             p_item->psz_uri = malloc( strlen( psz_proto ) + strlen( psz_uri ) +
730                                       strlen( psz_port ) + strlen(psz_http_path) + 5 );
731             sprintf( p_item->psz_uri, "%s://%s:%s%s", psz_proto,
732                             psz_uri, psz_port,psz_http_path );
733
734             if( psz_http_path )
735             {
736                 free( psz_http_path );
737             }
738         }
739
740         /* Enqueueing p_item in the playlist */
741         p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
742         playlist_AddItem ( p_playlist, p_item, PLAYLIST_CHECK_INSERT, PLAYLIST_END );
743         vlc_object_release( p_playlist );
744     }
745 }
746
747 /***********************************************************************
748  * parse_sdp : SDP parsing
749  * *********************************************************************
750  * Make a sess_descr_t with a psz
751  ***********************************************************************/
752
753 static sess_descr_t *  parse_sdp( intf_thread_t * p_intf, char *p_packet )
754 {
755     sess_descr_t *  sd;
756
757     if( p_packet[0] != 'v' || p_packet[1] != '=' )
758     {
759         msg_Warn(p_intf, "bad SDP packet");
760         return NULL;
761     }
762
763     sd = malloc( sizeof( sess_descr_t ) );
764     sd->psz_sessionname = NULL;
765     sd->psz_connection  = NULL;
766     sd->psz_sdp         = strdup( p_packet );
767     
768     sd->i_media         = 0;
769     sd->pp_media        = NULL;
770     sd->i_attributes    = 0;
771     sd->pp_attributes   = NULL;
772
773     while( *p_packet != '\0'  )
774     {
775         char *psz_end;
776
777         /* Search begin of field */
778         while( *p_packet == '\n' || *p_packet == ' ' || *p_packet == '\t' )
779         {
780             p_packet++;
781         }
782         /* search end of line */
783         if( ( psz_end = strchr( p_packet, '\n' ) ) == NULL )
784         {
785             psz_end = p_packet + strlen( p_packet );
786         }
787         if( psz_end > p_packet && *(psz_end - 1 ) == '\r' )
788         {
789             psz_end--;
790         }
791
792         if( psz_end <= p_packet )
793         {
794             break;
795         }
796         *psz_end++ = '\0';
797
798         if( p_packet[1] != '=' )
799         {
800             msg_Warn( p_intf, "packet invalid" );
801             free_sd( sd );
802             return NULL;
803         }
804
805         switch( p_packet[0] )
806         {
807             case( 'v' ):
808                 sd->i_version = atoi( &p_packet[2] );
809                 break;
810             case( 's' ):
811                 sd->psz_sessionname = strdup( &p_packet[2] );
812                 break;
813             case ( 'o' ):
814             case( 'i' ):
815             case( 'u' ):
816             case( 'e' ):
817             case( 'p' ):
818             case( 't' ):
819             case( 'r' ):
820                 break;
821             case( 'a' ):
822             {
823                 char *psz_eof = strchr( &p_packet[2], ':' );
824
825                 if( psz_eof && psz_eof[1] != '\0' )
826                 {
827                     attr_descr_t *attr = malloc( sizeof( attr_descr_t ) );
828
829                     *psz_eof++ = '\0';
830
831                     attr->psz_field = strdup( &p_packet[2] );
832                     attr->psz_value = strdup( psz_eof );
833
834                     TAB_APPEND( sd->i_attributes, sd->pp_attributes, attr );
835                 }
836                 break;
837             }
838
839             case( 'm' ):
840             {
841                 media_descr_t *media = malloc( sizeof( media_descr_t ) );
842
843                 media->psz_medianame = strdup( &p_packet[2] );
844                 media->psz_mediaconnection = NULL;
845
846                 TAB_APPEND( sd->i_media, sd->pp_media, media );
847                 break;
848             }
849
850             case( 'c' ):
851                 if( sd->i_media <= 0 )
852                 {
853                     sd->psz_connection = strdup( &p_packet[2] );
854                 }
855                 else
856                 {
857                     sd->pp_media[sd->i_media-1]->psz_mediaconnection = strdup( &p_packet[2] );
858                 }
859                break;
860
861             default:
862                break;
863         }
864
865         p_packet = psz_end;
866     }
867
868     return sd;
869 }
870
871 #define FREE( p ) \
872     if( p ) { free( p ); (p) = NULL; }
873 static void free_sd( sess_descr_t * p_sd )
874 {
875     int i;
876
877     FREE( p_sd->psz_sessionname );
878     FREE( p_sd->psz_connection );
879     FREE( p_sd->psz_sdp );
880
881     for( i = 0; i < p_sd->i_media ; i++ )
882     {
883         FREE( p_sd->pp_media[i]->psz_medianame );
884         FREE( p_sd->pp_media[i]->psz_mediaconnection );
885         FREE( p_sd->pp_media[i] );
886     }
887     for( i = 0; i < p_sd->i_attributes ; i++ )
888     {
889         FREE( p_sd->pp_attributes[i]->psz_field );
890         FREE( p_sd->pp_attributes[i]->psz_value );
891         FREE( p_sd->pp_attributes[i] );
892     }
893     FREE( p_sd->pp_attributes );
894     FREE( p_sd->pp_media );
895
896     free( p_sd );
897 }
898
899 /***********************************************************************
900  * ismult: returns true if we have a multicast address
901  ***********************************************************************/
902
903 static int ismult( char *psz_uri )
904 {
905     char *psz_end;
906     int  i_value;
907
908     i_value = strtol( psz_uri, &psz_end, 0 );
909
910     /* IPv6 */
911     if( psz_uri[0] == '[')
912     {
913       if( strncasecmp( &psz_uri[1], "FF0" , 3) ||
914           strncasecmp( &psz_uri[2], "FF0" , 3))
915             return( VLC_TRUE );
916         else
917             return( VLC_FALSE );
918     }
919
920     if( *psz_end != '.' ) { return( VLC_FALSE ); }
921
922     return( i_value < 224 ? VLC_FALSE : VLC_TRUE );
923 }
924
925
926
927 /*****************************************************************************
928  * Read: read on a file descriptor, checking b_die periodically
929  *****************************************************************************
930  * Taken from udp.c
931  *****************************************************************************/
932 static ssize_t NetRead( intf_thread_t *p_intf,
933                         int fd[2], uint8_t *p_buffer, int i_len )
934 {
935 #ifdef UNDER_CE
936     return -1;
937 #else
938     struct timeval  timeout;
939     fd_set          fds;
940     int             i_ret;
941     int             i_handle_max = __MAX( fd[0], fd[1] );
942
943     /* Initialize file descriptor set */
944     FD_ZERO( &fds );
945     if( fd[0] > 0 ) FD_SET( fd[0], &fds );
946     if( fd[1] > 0 ) FD_SET( fd[1], &fds );
947
948     /* We'll wait 0.5 second if nothing happens */
949     timeout.tv_sec = 0;
950     timeout.tv_usec = 500000;
951
952     /* Find if some data is available */
953     i_ret = select( i_handle_max + 1, &fds, NULL, NULL, &timeout );
954
955     if( i_ret == -1 && errno != EINTR )
956     {
957         msg_Err( p_intf, "network select error (%s)", strerror(errno) );
958     }
959     else if( i_ret > 0 )
960     {
961         if( fd[0] > 0 && FD_ISSET( fd[0], &fds ) )
962         {
963              return recv( fd[0], p_buffer, i_len, 0 );
964         }
965         else if( fd[1] > 0 && FD_ISSET( fd[1], &fds ) )
966         {
967              return recv( fd[1], p_buffer, i_len, 0 );
968         }
969     }
970     return 0;
971 #endif
972 }