]> git.sesse.net Git - vlc/blob - modules/misc/sap.c
String review, as promised :)
[vlc] / modules / misc / sap.c
1 /*****************************************************************************
2  * sap.c :  SAP interface module
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  * $Id$
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_( \
91       "Set this if you want the SAP module to listen to IPv4 announces")
92 #define SAP_IPV6_TEXT N_( "IPv6-SAP listening")
93 #define SAP_IPV6_LONGTEXT N_(\
94       "Set this if you want the SAP module to listen to IPv6 announces")
95 #define SAP_SCOPE_TEXT N_("IPv6 SAP scope")
96 #define SAP_SCOPE_LONGTEXT N_( \
97        "Sets the scope for IPv6 announces (default is 8)")
98 #define SAP_TIMEOUT_TEXT N_("SAP timeout (seconds)")
99 #define SAP_TIMEOUT_LONGTEXT N_( \
100        "Sets the time before SAP items get deleted if no new announce" \
101        "is received.")
102
103 static int  Open ( vlc_object_t * );
104 static void Close( vlc_object_t * );
105
106 vlc_module_begin();
107     set_description( _("SAP interface") );
108
109     add_string( "sap-addr", NULL, NULL,
110                 SAP_ADDR_TEXT, SAP_ADDR_LONGTEXT, VLC_TRUE );
111     add_bool( "sap-ipv4", 1 , NULL,
112                SAP_IPV4_TEXT,SAP_IPV4_LONGTEXT, VLC_TRUE);
113     add_bool( "sap-ipv6", 0 , NULL,
114               SAP_IPV6_TEXT, SAP_IPV6_LONGTEXT, VLC_TRUE);
115     add_string( "sap-ipv6-scope", "8" , NULL,
116                 SAP_SCOPE_TEXT, SAP_SCOPE_LONGTEXT, VLC_TRUE);
117     add_integer( "sap-timeout", 1800, NULL,
118                  SAP_TIMEOUT_TEXT, SAP_TIMEOUT_LONGTEXT, VLC_TRUE);
119
120     set_capability( "interface", 0 );
121     set_callbacks( Open, Close );
122 vlc_module_end();
123
124 /*****************************************************************************
125  * Local prototypes
126  *****************************************************************************/
127
128 static void Run    ( intf_thread_t *p_intf );
129 static ssize_t NetRead( intf_thread_t *, int fd[2], uint8_t *, int );
130
131 typedef struct media_descr_t media_descr_t;
132 typedef struct sess_descr_t sess_descr_t;
133 typedef struct attr_descr_t attr_descr_t;
134
135 static void sess_toitem( intf_thread_t *, sess_descr_t * );
136
137 static sess_descr_t *  parse_sdp( intf_thread_t *, char * ) ;
138 static void free_sd( sess_descr_t * );
139
140 /* Detect multicast addresses */
141 static int  ismult( char * );
142
143 /* The struct that contains sdp informations */
144 struct  sess_descr_t
145 {
146     int  i_version;
147     char *psz_sessionname;
148     char *psz_connection;
149     char *psz_sdp;
150
151     int           i_media;
152     media_descr_t **pp_media;
153     int           i_attributes;
154     attr_descr_t  **pp_attributes;
155 };
156
157 /* All this informations are not useful yet.  */
158 struct media_descr_t
159 {
160     char *psz_medianame;
161     char *psz_mediaconnection;
162 };
163
164 struct attr_descr_t
165 {
166     char *psz_field;
167     char *psz_value;
168 };
169
170 struct sap_announce_t
171 {
172     mtime_t i_last;
173     int i_id;
174     char *psz_name;
175     char *psz_uri;
176 };
177
178 struct intf_sys_t
179 {
180     /* IPV4 and IPV6 */
181     int fd[2];
182
183     /* playlist group */
184     int i_group;
185
186     /* Table of announces */
187     int i_announces;
188     struct sap_announce_t **pp_announces;
189
190     int i_timeout;
191 };
192
193 #ifdef HAVE_ZLIB_H
194 int do_decompress(unsigned char *src, unsigned char **_dst, int slen) {
195   int result, dstsize, n;
196   unsigned char *dst;
197   z_stream d_stream;
198
199   d_stream.zalloc = (alloc_func)0;
200   d_stream.zfree = (free_func)0;
201   d_stream.opaque = (voidpf)0;
202   result = inflateInit(&d_stream);
203   if (result != Z_OK) {
204     printf("inflateInit() failed. Result: %d\n", result);
205     return(-1);
206   }
207
208   d_stream.next_in = (Bytef *)src;
209   d_stream.avail_in = slen;
210   n = 0;
211   dst = NULL;
212   do {
213     n++;
214     dst = (unsigned char *)realloc(dst, n * 1000);
215     d_stream.next_out = (Bytef *)&dst[(n - 1) * 1000];
216     d_stream.avail_out = 1000;
217     result = inflate(&d_stream, Z_NO_FLUSH);
218     if ((result != Z_OK) && (result != Z_STREAM_END)) {
219       printf("Zlib decompression failed. Result: %d\n", result);
220       return(-1);
221     }
222   } while ((d_stream.avail_out == 0) && (d_stream.avail_in != 0) &&
223            (result != Z_STREAM_END));
224
225   dstsize = d_stream.total_out;
226   inflateEnd(&d_stream);
227
228   *_dst = (unsigned char *)realloc(dst, dstsize);
229
230   return dstsize;
231 }
232 #endif
233
234 /*****************************************************************************
235  * Open: initialize and create stuff
236  *****************************************************************************/
237 static int Open( vlc_object_t *p_this )
238 {
239     intf_thread_t *p_intf = (intf_thread_t*)p_this;
240     intf_sys_t    *p_sys  = malloc( sizeof( intf_sys_t ) );
241
242     playlist_t          *p_playlist;
243
244     p_sys->i_timeout = config_GetInt(p_intf,"sap-timeout");
245     p_sys->fd[0] = -1;
246     p_sys->fd[1] = -1;
247     if( config_GetInt( p_intf, "sap-ipv4" ) )
248     {
249         char *psz_address = config_GetPsz( p_intf, "sap-addr" );
250         network_socket_t    sock;
251         module_t            *p_network;
252         if( psz_address == NULL || *psz_address == '\0' )
253         {
254             psz_address = strdup( HELLO_GROUP );
255         }
256
257         /* Prepare the network_socket_t structure */
258         sock.i_type            = NETWORK_UDP;
259         sock.psz_bind_addr     = psz_address;
260         sock.i_bind_port       = HELLO_PORT;
261         sock.psz_server_addr   = "";
262         sock.i_server_port     = 0;
263         sock.i_ttl             = 0;
264         p_intf->p_private = (void*) &sock;
265
266         p_network = module_Need( p_intf, "network", "ipv4", VLC_TRUE );
267         if( p_network )
268         {
269             p_sys->fd[0] = sock.i_handle;
270             module_Unneed( p_intf, p_network );
271         }
272         else
273         {
274             msg_Warn( p_intf, "failed to open %s:%d", psz_address, HELLO_PORT );
275         }
276         free( psz_address );
277     }
278
279     if( config_GetInt( p_intf, "sap-ipv6" ) )
280     {
281         char psz_address[100];
282         char *psz_scope = config_GetPsz( p_intf, "sap-ipv6-scope" );
283         network_socket_t    sock;
284         module_t            *p_network;
285
286         if( psz_scope == NULL || *psz_scope == '\0' )
287         {
288             psz_scope = strdup( "8" );
289         }
290         snprintf( psz_address, 100, "[%s%c%s]",IPV6_ADDR_1,
291                   psz_scope[0], IPV6_ADDR_2 );
292         free( psz_scope );
293
294         sock.i_type            = NETWORK_UDP;
295         sock.psz_bind_addr     = psz_address;
296         sock.i_bind_port       = HELLO_PORT;
297         sock.psz_server_addr   = "";
298         sock.i_server_port     = 0;
299         sock.i_ttl             = 0;
300         p_intf->p_private = (void*) &sock;
301
302         p_network = module_Need( p_intf, "network", "ipv6", VLC_TRUE );
303         if( p_network )
304         {
305             p_sys->fd[1] = sock.i_handle;
306             module_Unneed( p_intf, p_network );
307         }
308         else
309         {
310             msg_Warn( p_intf, "failed to open %s:%d", psz_address, HELLO_PORT );
311         }
312     }
313     if( p_sys->fd[0] <= 0 && p_sys->fd[1] <= 0 )
314     {
315         msg_Warn( p_intf, "IPV4 and IPV6 failed" );
316         free( p_sys );
317         return VLC_EGENERIC;
318     }
319
320     /* Create our playlist group */
321     p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
322                                                 FIND_ANYWHERE );
323     if( p_playlist )
324     {
325         playlist_group_t *p_group = playlist_CreateGroup( p_playlist , "SAP" );
326         p_sys->i_group = p_group->i_id;
327         vlc_object_release( p_playlist );
328     }
329
330     p_sys->i_announces = 0;
331     p_sys->pp_announces = NULL;
332
333     p_intf->pf_run = Run;
334     p_intf->p_sys  = p_sys;
335
336     return VLC_SUCCESS;
337 }
338
339 /*****************************************************************************
340  * Close:
341  *****************************************************************************/
342 static void Close( vlc_object_t *p_this )
343 {
344     intf_thread_t *p_intf = (intf_thread_t*)p_this;
345     intf_sys_t    *p_sys  = p_intf->p_sys;
346     int i;
347
348     if( p_sys->fd[0] > 0 )
349     {
350         close( p_sys->fd[0] );
351     }
352     if( p_sys->fd[1] > 0 )
353     {
354         close( p_sys->fd[1] );
355     }
356
357     for( i = 0 ; i< p_sys->i_announces ; i++ )
358     {
359         if( p_sys->pp_announces[i]->psz_name )
360            free( p_sys->pp_announces[i]->psz_name );
361         if( p_sys->pp_announces[i]->psz_uri )
362            free( p_sys->pp_announces[i]->psz_uri );
363         free( p_sys->pp_announces[i] );
364     }
365     free( p_sys->pp_announces );
366
367     free( p_sys );
368 }
369
370 /*****************************************************************************
371  * Run: sap thread
372  *****************************************************************************
373  * Listens to SAP packets, and sends them to packet_handle
374  *****************************************************************************/
375 #define MAX_SAP_BUFFER 5000
376
377 static void Run( intf_thread_t *p_intf )
378 {
379     intf_sys_t *p_sys  = p_intf->p_sys;
380     uint8_t     buffer[MAX_SAP_BUFFER + 1];
381     uint8_t    *p_end;
382
383     /* read SAP packets */
384     while( !p_intf->b_die )
385     {
386         playlist_t *p_playlist= NULL;
387         int i;
388         int i_read = NetRead( p_intf, p_sys->fd, buffer, MAX_SAP_BUFFER );
389         uint8_t *p_sdp;
390         int i_version;
391         int i_address_type;
392         int b_reserved;
393         int b_message_type;
394         int b_encrypted;
395         int b_compressed;
396         unsigned char *p_decompressed_buffer;
397         int i_decompressed_size;
398
399         /* Check for items that need deletion */
400         for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ )
401         {
402            struct sap_announce_t *p_announce;
403            mtime_t i_timeout = (mtime_t)1000000*p_sys->i_timeout;
404            if( mdate() - p_intf->p_sys->pp_announces[i]->i_last > i_timeout )
405            {
406                msg_Dbg(p_intf,"Time out for %s, deleting (%i/%i)",
407                        p_intf->p_sys->pp_announces[i]->psz_name,
408                        i , p_intf->p_sys->i_announces );
409
410                /* Remove the playlist item */
411                p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
412                               FIND_ANYWHERE );
413                if( p_playlist )
414                {
415                    int i_pos = playlist_GetPositionById( p_playlist,
416                               p_intf->p_sys->pp_announces[i]->i_id );
417                    playlist_Delete( p_playlist, i_pos );
418                }
419
420                vlc_object_release( p_playlist );
421
422                /* Free the p_announce */
423                p_announce =  p_intf->p_sys->pp_announces[i];
424                if( p_announce->psz_name )
425                   free(  p_announce->psz_name );
426                if( p_announce->psz_uri )
427                   free(  p_announce->psz_uri );
428
429               /* Remove the sap_announce from the array */
430               REMOVE_ELEM( p_intf->p_sys->pp_announces,
431                            p_intf->p_sys->i_announces,
432                            i );
433
434               free( p_announce );
435
436            }
437         }
438
439         /* Minimum length is > 6 */
440         if( i_read <= 6 )
441         {
442             if( i_read < 0 )
443             {
444                 msg_Warn( p_intf, "Cannot read in the socket" );
445             }
446             continue;
447         }
448
449         buffer[i_read] = '\0';
450         p_end = &buffer[i_read];
451
452         /* Parse the SAP header */
453         i_version = buffer[0] >> 5;
454         if( i_version != 1 )
455         {
456             msg_Warn( p_intf, "strange sap version %d found", i_version );
457         }
458         i_address_type = buffer[0] & 0x10;
459         b_reserved = buffer[0] & 0x08;
460         if( b_reserved != 0 )
461         {
462             msg_Warn( p_intf, "reserved bit incorrectly set" );
463         }
464         b_message_type = buffer[0] & 0x04;
465         if( b_message_type != 0 )
466         {
467             msg_Warn( p_intf, "got session deletion packet" );
468         }
469         b_encrypted = buffer[0] & 0x02;
470         if( b_encrypted )
471         {
472             msg_Warn( p_intf, "encrypted packet" );
473         }
474         b_compressed = buffer[0] & 0x01;
475         p_sdp  = &buffer[4];
476         if( i_address_type == 0 ) /* ipv4 source address */
477         {
478             p_sdp += 4;
479         }
480         else /* ipv6 source address */
481         {
482             p_sdp += 16;
483         }
484         if( b_compressed )
485         {
486 #ifdef HAVE_ZLIB_H
487             i_decompressed_size = do_decompress( p_sdp, &p_decompressed_buffer, i_read - ( p_sdp - buffer ) );
488             if( i_decompressed_size > 0 && i_decompressed_size < MAX_SAP_BUFFER )
489             {
490                 memcpy( p_sdp, p_decompressed_buffer, i_decompressed_size );
491                 p_sdp[i_decompressed_size] = '\0';
492                 p_end = &p_sdp[i_decompressed_size];
493                 free( p_decompressed_buffer );
494             }
495 #else
496             msg_Warn( p_intf, "Ignoring compressed sap packet" );
497 #endif
498         }
499         p_sdp += buffer[1]; /* size of signature */
500         while( p_sdp < p_end - 1 && *p_sdp != '\0' && p_sdp[0] != 'v' && p_sdp[1] != '=' )
501         {
502             p_sdp++;
503         }
504         if( *p_sdp == '\0' )
505         {
506             p_sdp++;
507         }
508
509         if( p_sdp < p_end )
510         {
511             sess_descr_t *p_sd = parse_sdp( p_intf, p_sdp );
512             if( p_sd )
513             {
514                 sess_toitem ( p_intf, p_sd );
515                 free_sd ( p_sd );
516             }
517         }
518         else
519         {
520             msg_Warn( p_intf, "ditching sap packet" );
521         }
522
523     }
524 }
525
526 /**********************************************************************
527  * cfield_parse
528  *********************************************************************
529  * put into *ppsz_uri, the the uri in the cfield, psz_cfield.
530  *********************************************************************/
531
532 static void cfield_parse( char *psz_cfield, char **ppsz_uri )
533 {
534
535     char *psz_pos;
536     if( psz_cfield )
537     {
538         psz_pos = psz_cfield;
539
540         while( *psz_pos != ' ' && *psz_pos !='\0' )
541         {
542             psz_pos++;
543         }
544         psz_pos++;
545         while( *psz_pos != ' ' && *psz_pos !='\0' )
546         {
547             psz_pos++;
548         }
549         psz_pos++;
550         *ppsz_uri = psz_pos;
551         while( *psz_pos != ' ' && *psz_pos !='/'
552                         && *psz_pos != '\0' )
553         {
554             psz_pos++;
555         }
556         *psz_pos = '\0';
557
558     }
559     else
560     {
561         ppsz_uri = NULL;
562     }
563
564     return;
565
566 }
567
568 /**********************************************************************
569  * mfield_parse
570  *********************************************************************
571  * put into *ppsz_proto, and *ppsz_port, the protocol and the port.
572  *********************************************************************/
573
574
575 static void mfield_parse( char *psz_mfield, char **ppsz_proto,
576                char **ppsz_port )
577 {
578     char *psz_pos;
579     char *psz_media;
580     if( psz_mfield )
581     {
582         psz_pos = psz_mfield;
583         psz_media = psz_mfield;
584         while( *psz_pos != '\0' && *psz_pos != ' ' )
585         {
586             psz_pos++;
587         }
588         if( *psz_pos != '\0' )
589         {
590             *psz_pos = '\0';
591             if( strcmp( psz_media, "video" ) && strcmp( psz_media, "audio" ) )
592             {
593                 *ppsz_proto = NULL;
594                 *ppsz_port = NULL;
595                 return;
596             }
597         }
598         psz_pos++;
599         *ppsz_port = psz_pos;
600         while( *psz_pos != '\0' && *psz_pos !=' ' && *psz_pos!='/' )
601         {
602             psz_pos++;
603         }
604         if( *psz_pos == '/' )  // FIXME does not support multi-port
605         {
606             *psz_pos = '\0';
607             psz_pos++;
608             while( *psz_pos != '\0' && *psz_pos !=' ' )
609             {
610             psz_pos++;
611             }
612         }
613         *psz_pos = '\0';
614         psz_pos++;
615         *ppsz_proto = psz_pos;
616         while( *psz_pos!='\0' && *psz_pos !=' ' &&
617                         *psz_pos!='/' )
618         {
619             *psz_pos = tolower( *psz_pos );
620             psz_pos++;
621         }
622         *psz_pos = '\0';
623     }
624     else
625     {
626         *ppsz_proto = NULL;
627         *ppsz_port = NULL;
628     }
629     return;
630 }
631
632
633 /*******************************************************************
634  * sess_toitem : changes a sess_descr_t into a hurd of
635  * playlist_item_t, which are enqueued.
636  *******************************************************************
637  * Note : does not support sessions that take place on consecutive
638  * port or adresses yet.
639  *******************************************************************/
640
641 static void sess_toitem( intf_thread_t * p_intf, sess_descr_t * p_sd )
642 {
643     struct sap_announce_t *p_announce;
644     char *psz_uri, *psz_proto, *psz_item_uri;
645     char *psz_port;
646     char *psz_uri_default;
647     int i_count , i , i_pos , i_id = 0;
648     vlc_bool_t b_http = VLC_FALSE;
649     char *psz_http_path = NULL;
650     playlist_t *p_playlist = NULL;
651     playlist_item_t *p_item;
652
653     psz_uri_default = NULL;
654     if( p_sd->i_media > 1 )
655     {
656         asprintf( &psz_uri, "sdp://%s", p_sd->psz_sdp );
657         /* Check if we have already added the item */
658         for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ )
659         {
660             if( !strcmp(p_intf->p_sys->pp_announces[i]->psz_uri,
661                         psz_uri ) )
662             {
663                 p_intf->p_sys->pp_announces[i]->i_last = mdate();
664                 free(psz_uri);
665                 return;
666             }
667         }
668         /* Add it to the playlist */
669         p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
670                                       FIND_ANYWHERE );
671         i_id = playlist_Add( p_playlist, psz_uri, p_sd->psz_sessionname ,
672                       PLAYLIST_CHECK_INSERT, PLAYLIST_END );
673         if( i_id != -1 )
674         {
675             playlist_item_t *p_item = playlist_ItemGetById( p_playlist, i_id );
676             playlist_ItemSetGroup( p_item, p_intf->p_sys->i_group );
677         }
678
679         /* Remember it */
680         p_announce = (struct sap_announce_t *)malloc(
681                       sizeof(struct sap_announce_t) );
682         if( p_sd->psz_sessionname )
683         {
684             p_announce->psz_name = strdup(  p_sd->psz_sessionname );
685         }
686         else
687         {
688             p_announce->psz_name = strdup( "" );
689         }
690         if( psz_uri )
691         {
692             p_announce->psz_uri  = strdup( psz_uri );
693         }
694         else
695         {
696             p_announce->psz_uri = strdup( "" );
697         }
698         p_announce->i_id = i_id;
699         p_announce->i_last = mdate();
700
701         INSERT_ELEM( p_intf->p_sys->pp_announces,
702                      p_intf->p_sys->i_announces,
703                      p_intf->p_sys->i_announces,
704                      p_announce );
705
706         vlc_object_release( p_playlist );
707         free( psz_uri );
708         return;
709     }
710
711     cfield_parse( p_sd->psz_connection, &psz_uri_default );
712
713     for( i_count = 0 ; i_count < p_sd->i_media ; i_count++ )
714     {
715         int i_group = p_intf->p_sys->i_group;
716
717         /* Build what we have to put in psz_item_uri, with the m and
718          * c fields  */
719
720         if( !p_sd->pp_media[i_count] )
721         {
722             return;
723         }
724
725         mfield_parse( p_sd->pp_media[i_count]->psz_medianame,
726                         & psz_proto, & psz_port );
727
728         if( !psz_proto || !psz_port )
729         {
730             return;
731         }
732
733         if( p_sd->pp_media[i_count]->psz_mediaconnection )
734         {
735             cfield_parse( p_sd->pp_media[i_count]->psz_mediaconnection,
736                             & psz_uri );
737         }
738         else
739         {
740             psz_uri = psz_uri_default;
741         }
742
743         if( psz_uri == NULL )
744         {
745             return;
746         }
747
748         for( i = 0 ; i< p_sd->i_attributes ; i++ )
749         {
750             if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "type") &&
751                 strstr( p_sd->pp_attributes[i]->psz_value, "http") )
752             {
753                 b_http = VLC_TRUE;
754             }
755             if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "http-path"))
756             {
757                 psz_http_path = strdup(  p_sd->pp_attributes[i]->psz_value );
758             }
759             if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "plgroup"))
760             {
761                 int i_group_id;
762                 p_playlist =
763                 (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
764                                                         FIND_ANYWHERE );
765                 if( p_playlist == NULL )
766                 {
767                     return;
768                 }
769
770                 i_group_id = playlist_GroupToId( p_playlist,
771                                  p_sd->pp_attributes[i]->psz_value);
772                 if( i_group_id != 0 )
773                 {
774                     i_group = i_group_id;
775                 }
776                 else
777                 {
778                     playlist_group_t *p_group =
779                             playlist_CreateGroup( p_playlist,
780                                        p_sd->pp_attributes[i]->psz_value);
781                     i_group = p_group->i_id;
782                 }
783                 vlc_object_release( p_playlist );
784             }
785         }
786
787         /* Filling psz_uri */
788         if( b_http == VLC_FALSE )
789         {
790             psz_item_uri = malloc( strlen( psz_proto ) + strlen( psz_uri ) +
791                                  strlen( psz_port ) + 7 );
792             if( ismult( psz_uri ) )
793             {
794                 sprintf( psz_item_uri, "%s://@%s:%s",
795                          psz_proto, psz_uri, psz_port );
796             }
797             else
798             {
799                 sprintf( psz_item_uri, "%s://%s:%s",
800                          psz_proto, psz_uri, psz_port );
801             }
802         }
803         else
804         {
805             if( psz_http_path == NULL )
806             {
807                 psz_http_path = strdup( "/" );
808             }
809             if( *psz_http_path == '/' )
810             {
811                 asprintf( &psz_item_uri, "%s://%s:%s%s", psz_proto,
812                          psz_uri, psz_port,psz_http_path );
813             }
814             else
815             {
816                 asprintf( &psz_item_uri, "%s://%s:%s/%s", psz_proto, psz_uri,
817                           psz_port, psz_http_path );
818             }
819
820             if( psz_http_path )
821             {
822                 free( psz_http_path );
823             }
824         }
825
826         /* Check if we already know this item */
827          for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ )
828          {
829             if( !strcmp( p_intf->p_sys->pp_announces[i]->psz_uri,
830                          psz_item_uri ) )
831             {
832                 p_intf->p_sys->pp_announces[i]->i_last = mdate();
833
834                 /* Check if the name changed */
835                 if( strcmp( p_intf->p_sys->pp_announces[i]->psz_name,
836                              p_sd->psz_sessionname ) )
837                 {
838                     playlist_item_t *p_item;
839                     p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
840                                                   FIND_ANYWHERE );
841
842                     msg_Dbg(p_intf, "Name changed (%s -> %s) for %s",
843                             p_intf->p_sys->pp_announces[i]->psz_name,
844                             p_sd->psz_sessionname,
845                             psz_item_uri );
846
847                     p_item = playlist_ItemGetById( p_playlist,
848                                     p_intf->p_sys->pp_announces[i]->i_id );
849
850                     /* Change the name in the item */
851                     if( p_item->psz_name )
852                         free( p_item->psz_name );
853                     p_item->psz_name = strdup( p_sd->psz_sessionname);
854
855                     /* Update the stored name */
856                     if( p_intf->p_sys->pp_announces[i]->psz_name )
857                         free( p_intf->p_sys->pp_announces[i]->psz_name );
858                     p_intf->p_sys->pp_announces[i]->psz_name =
859                                    strdup( p_sd->psz_sessionname );
860
861                     vlc_object_release( p_playlist );
862                 }
863                 free( psz_item_uri );
864                 return;
865             }
866         }
867
868         /* Add the item in the playlist */
869         p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
870                                       FIND_ANYWHERE );
871         i_id = playlist_Add ( p_playlist, psz_item_uri ,
872                                p_sd->psz_sessionname,
873                                PLAYLIST_CHECK_INSERT, PLAYLIST_END );
874         p_item = playlist_ItemGetById( p_playlist, i_id );
875         if( p_item )
876         {
877             vlc_mutex_lock( &p_item->lock );
878             playlist_ItemSetGroup( p_item, i_group );
879             vlc_mutex_unlock( &p_item->lock );
880         }
881
882         /* Then remember it */
883         p_announce = (struct sap_announce_t *)malloc(
884                       sizeof(struct sap_announce_t) );
885         if(  p_sd->psz_sessionname )
886         {
887             p_announce->psz_name = strdup(  p_sd->psz_sessionname );
888         }
889         else
890         {
891             p_announce->psz_name = strdup( "" );
892         }
893         if( psz_item_uri )
894         {
895             p_announce->psz_uri  = strdup( psz_item_uri );
896         }
897         else
898         {
899             p_announce->psz_uri = strdup( "" );
900         }
901         p_announce->i_id = i_id;
902
903         p_announce->i_last = mdate();
904
905         vlc_object_release( p_playlist );
906
907         INSERT_ELEM( p_intf->p_sys->pp_announces,
908                      p_intf->p_sys->i_announces,
909                      p_intf->p_sys->i_announces,
910                      p_announce );
911         free( psz_item_uri );
912     }
913 }
914
915 /***********************************************************************
916  * parse_sdp : SDP parsing
917  * *********************************************************************
918  * Make a sess_descr_t with a psz
919  ***********************************************************************/
920
921 static sess_descr_t *  parse_sdp( intf_thread_t * p_intf, char *p_packet )
922 {
923     sess_descr_t *  sd;
924
925     if( p_packet[0] != 'v' || p_packet[1] != '=' )
926     {
927         msg_Warn(p_intf, "bad SDP packet");
928         return NULL;
929     }
930
931     sd = malloc( sizeof( sess_descr_t ) );
932     sd->psz_sessionname = NULL;
933     sd->psz_connection  = NULL;
934     sd->psz_sdp         = strdup( p_packet );
935
936     sd->i_media         = 0;
937     sd->pp_media        = NULL;
938     sd->i_attributes    = 0;
939     sd->pp_attributes   = NULL;
940
941     while( *p_packet != '\0'  )
942     {
943         char *psz_end;
944
945         /* Search begin of field */
946         while( *p_packet == '\n' || *p_packet == ' ' || *p_packet == '\t' )
947         {
948             p_packet++;
949         }
950         /* search end of line */
951         if( ( psz_end = strchr( p_packet, '\n' ) ) == NULL )
952         {
953             psz_end = p_packet + strlen( p_packet );
954         }
955         if( psz_end > p_packet && *(psz_end - 1 ) == '\r' )
956         {
957             psz_end--;
958         }
959
960         if( psz_end <= p_packet )
961         {
962             break;
963         }
964         *psz_end++ = '\0';
965
966         if( p_packet[1] != '=' )
967         {
968             msg_Warn( p_intf, "packet invalid" );
969             free_sd( sd );
970             return NULL;
971         }
972
973         switch( p_packet[0] )
974         {
975             case( 'v' ):
976                 sd->i_version = atoi( &p_packet[2] );
977                 break;
978             case( 's' ):
979                 sd->psz_sessionname = strdup( &p_packet[2] );
980                 break;
981             case ( 'o' ):
982             case( 'i' ):
983             case( 'u' ):
984             case( 'e' ):
985             case( 'p' ):
986             case( 't' ):
987             case( 'r' ):
988                 break;
989             case( 'a' ):
990             {
991                 char *psz_eof = strchr( &p_packet[2], ':' );
992
993                 if( psz_eof && psz_eof[1] != '\0' )
994                 {
995                     attr_descr_t *attr = malloc( sizeof( attr_descr_t ) );
996
997                     *psz_eof++ = '\0';
998
999                     attr->psz_field = strdup( &p_packet[2] );
1000                     attr->psz_value = strdup( psz_eof );
1001
1002                     TAB_APPEND( sd->i_attributes, sd->pp_attributes, attr );
1003                 }
1004                 break;
1005             }
1006
1007             case( 'm' ):
1008             {
1009                 media_descr_t *media = malloc( sizeof( media_descr_t ) );
1010
1011                 media->psz_medianame = strdup( &p_packet[2] );
1012                 media->psz_mediaconnection = NULL;
1013
1014                 TAB_APPEND( sd->i_media, sd->pp_media, media );
1015                 break;
1016             }
1017
1018             case( 'c' ):
1019                 if( sd->i_media <= 0 )
1020                 {
1021                     sd->psz_connection = strdup( &p_packet[2] );
1022                 }
1023                 else
1024                 {
1025                     sd->pp_media[sd->i_media-1]->psz_mediaconnection = strdup( &p_packet[2] );
1026                 }
1027                break;
1028
1029             default:
1030                break;
1031         }
1032
1033         p_packet = psz_end;
1034     }
1035
1036     return sd;
1037 }
1038
1039 #define FREE( p ) \
1040     if( p ) { free( p ); (p) = NULL; }
1041 static void free_sd( sess_descr_t * p_sd )
1042 {
1043     int i;
1044
1045     FREE( p_sd->psz_sessionname );
1046     FREE( p_sd->psz_connection );
1047     FREE( p_sd->psz_sdp );
1048
1049     for( i = 0; i < p_sd->i_media ; i++ )
1050     {
1051         FREE( p_sd->pp_media[i]->psz_medianame );
1052         FREE( p_sd->pp_media[i]->psz_mediaconnection );
1053         FREE( p_sd->pp_media[i] );
1054     }
1055     for( i = 0; i < p_sd->i_attributes ; i++ )
1056     {
1057         FREE( p_sd->pp_attributes[i]->psz_field );
1058         FREE( p_sd->pp_attributes[i]->psz_value );
1059         FREE( p_sd->pp_attributes[i] );
1060     }
1061     FREE( p_sd->pp_attributes );
1062     FREE( p_sd->pp_media );
1063
1064     free( p_sd );
1065 }
1066
1067 /***********************************************************************
1068  * ismult: returns true if we have a multicast address
1069  ***********************************************************************/
1070
1071 static int ismult( char *psz_uri )
1072 {
1073     char *psz_end;
1074     int  i_value;
1075
1076     i_value = strtol( psz_uri, &psz_end, 0 );
1077
1078     /* IPv6 */
1079     if( psz_uri[0] == '[')
1080     {
1081       if( strncasecmp( &psz_uri[1], "FF0" , 3) ||
1082           strncasecmp( &psz_uri[2], "FF0" , 3))
1083             return( VLC_TRUE );
1084         else
1085             return( VLC_FALSE );
1086     }
1087
1088     if( *psz_end != '.' ) { return( VLC_FALSE ); }
1089
1090     return( i_value < 224 ? VLC_FALSE : VLC_TRUE );
1091 }
1092
1093
1094
1095 /*****************************************************************************
1096  * Read: read on a file descriptor, checking b_die periodically
1097  *****************************************************************************
1098  * Taken from udp.c
1099  *****************************************************************************/
1100 static ssize_t NetRead( intf_thread_t *p_intf,
1101                         int fd[2], uint8_t *p_buffer, int i_len )
1102 {
1103 #ifdef UNDER_CE
1104     return -1;
1105 #else
1106     struct timeval  timeout;
1107     fd_set          fds;
1108     int             i_ret;
1109     int             i_handle_max = __MAX( fd[0], fd[1] );
1110
1111     /* Initialize file descriptor set */
1112     FD_ZERO( &fds );
1113     if( fd[0] > 0 ) FD_SET( fd[0], &fds );
1114     if( fd[1] > 0 ) FD_SET( fd[1], &fds );
1115
1116     /* We'll wait 0.5 second if nothing happens */
1117     timeout.tv_sec = 0;
1118     timeout.tv_usec = 500000;
1119
1120     /* Find if some data is available */
1121     i_ret = select( i_handle_max + 1, &fds, NULL, NULL, &timeout );
1122
1123     if( i_ret == -1 && errno != EINTR )
1124     {
1125         msg_Err( p_intf, "network select error (%s)", strerror(errno) );
1126     }
1127     else if( i_ret > 0 )
1128     {
1129         if( fd[0] > 0 && FD_ISSET( fd[0], &fds ) )
1130         {
1131              return recv( fd[0], p_buffer, i_len, 0 );
1132         }
1133         else if( fd[1] > 0 && FD_ISSET( fd[1], &fds ) )
1134         {
1135              return recv( fd[1], p_buffer, i_len, 0 );
1136         }
1137     }
1138     return 0;
1139 #endif
1140 }