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