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