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