]> git.sesse.net Git - vlc/blob - modules/misc/sap.c
* mp4: fix for alaw/ulaw. Clever people that designed .mov decided that
[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 behavior 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     /* Dirty hack to slow down the startup of the sap interface*/ 
390     msleep(500000);    
391
392     /* read SAP packets */
393     while( !p_intf->b_die )
394     {
395         playlist_t *p_playlist= NULL;
396         int i;
397         int i_read = NetRead( p_intf, p_sys->fd, buffer, MAX_SAP_BUFFER );
398         uint8_t *p_sdp;
399         int i_version;
400         int i_address_type;
401         int b_reserved;
402         int b_message_type;
403         int b_encrypted;
404         int b_compressed;
405         unsigned char *p_decompressed_buffer;
406         int i_decompressed_size;
407
408         /* Check for items that need deletion */
409         for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ )
410         {
411            struct sap_announce_t *p_announce;
412            mtime_t i_timeout = (mtime_t)1000000*p_sys->i_timeout;
413            if( mdate() - p_intf->p_sys->pp_announces[i]->i_last > i_timeout )
414            {
415                msg_Dbg(p_intf,"Time out for %s, deleting (%i/%i)",
416                        p_intf->p_sys->pp_announces[i]->psz_name,
417                        i , p_intf->p_sys->i_announces );
418
419                /* Remove the playlist item */
420                p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
421                               FIND_ANYWHERE );
422                if( p_playlist )
423                {
424                    int i_pos = playlist_GetPositionById( p_playlist,
425                               p_intf->p_sys->pp_announces[i]->i_id );
426                    playlist_Delete( p_playlist, i_pos );
427                }
428
429                vlc_object_release( p_playlist );
430
431                /* Free the p_announce */
432                p_announce =  p_intf->p_sys->pp_announces[i];
433                if( p_announce->psz_name )
434                   free(  p_announce->psz_name );
435                if( p_announce->psz_uri )
436                   free(  p_announce->psz_uri );
437
438               /* Remove the sap_announce from the array */
439               REMOVE_ELEM( p_intf->p_sys->pp_announces,
440                            p_intf->p_sys->i_announces,
441                            i );
442
443               free( p_announce );
444
445            }
446         }
447
448         /* Minimum length is > 6 */
449         if( i_read <= 6 )
450         {
451             if( i_read < 0 )
452             {
453                 msg_Warn( p_intf, "Cannot read in the socket" );
454             }
455             continue;
456         }
457
458         buffer[i_read] = '\0';
459         p_end = &buffer[i_read];
460
461         /* Parse the SAP header */
462         i_version = buffer[0] >> 5;
463         if( i_version != 1 )
464         {
465             msg_Warn( p_intf, "strange sap version %d found", i_version );
466         }
467         i_address_type = buffer[0] & 0x10;
468         b_reserved = buffer[0] & 0x08;
469         if( b_reserved != 0 )
470         {
471             msg_Warn( p_intf, "reserved bit incorrectly set" );
472         }
473         b_message_type = buffer[0] & 0x04;
474         if( b_message_type != 0 )
475         {
476             msg_Warn( p_intf, "got session deletion packet" );
477         }
478         b_encrypted = buffer[0] & 0x02;
479         if( b_encrypted )
480         {
481             msg_Warn( p_intf, "encrypted packet" );
482         }
483         b_compressed = buffer[0] & 0x01;
484         p_sdp  = &buffer[4];
485         if( i_address_type == 0 ) /* ipv4 source address */
486         {
487             p_sdp += 4;
488         }
489         else /* ipv6 source address */
490         {
491             p_sdp += 16;
492         }
493         if( b_compressed )
494         {
495 #ifdef HAVE_ZLIB_H
496             i_decompressed_size = do_decompress( p_sdp, &p_decompressed_buffer, i_read - ( p_sdp - buffer ) );
497             if( i_decompressed_size > 0 && i_decompressed_size < MAX_SAP_BUFFER )
498             {
499                 memcpy( p_sdp, p_decompressed_buffer, i_decompressed_size );
500                 p_sdp[i_decompressed_size] = '\0';
501                 p_end = &p_sdp[i_decompressed_size];
502                 free( p_decompressed_buffer );
503             }
504 #else
505             msg_Warn( p_intf, "Ignoring compressed sap packet" );
506 #endif
507         }
508         p_sdp += buffer[1]; /* size of signature */
509         while( p_sdp < p_end - 1 && *p_sdp != '\0' && p_sdp[0] != 'v' && p_sdp[1] != '=' )
510         {
511             p_sdp++;
512         }
513         if( *p_sdp == '\0' )
514         {
515             p_sdp++;
516         }
517
518         if( p_sdp < p_end )
519         {
520             sess_descr_t *p_sd = parse_sdp( p_intf, p_sdp );
521             if( p_sd )
522             {
523                 sess_toitem ( p_intf, p_sd );
524                 free_sd ( p_sd );
525             }
526         }
527         else
528         {
529             msg_Warn( p_intf, "ditching sap packet" );
530         }
531
532         memset( buffer, 0, MAX_SAP_BUFFER );
533     }
534 }
535
536 /**********************************************************************
537  * cfield_parse
538  *********************************************************************
539  * put into *ppsz_uri, the the uri in the cfield, psz_cfield.
540  *********************************************************************/
541
542 static void cfield_parse( char *psz_cfield, char **ppsz_uri )
543 {
544
545     char *psz_pos;
546     if( psz_cfield )
547     {
548         psz_pos = psz_cfield;
549
550         while( *psz_pos != ' ' && *psz_pos !='\0' )
551         {
552             psz_pos++;
553         }
554         psz_pos++;
555         while( *psz_pos != ' ' && *psz_pos !='\0' )
556         {
557             psz_pos++;
558         }
559         psz_pos++;
560         *ppsz_uri = psz_pos;
561         while( *psz_pos != ' ' && *psz_pos !='/'
562                         && *psz_pos != '\0' )
563         {
564             psz_pos++;
565         }
566         *psz_pos = '\0';
567
568     }
569     else
570     {
571         ppsz_uri = NULL;
572     }
573
574     return;
575
576 }
577
578 /**********************************************************************
579  * mfield_parse
580  *********************************************************************
581  * put into *ppsz_proto, and *ppsz_port, the protocol and the port.
582  *********************************************************************/
583
584
585 static void mfield_parse( char *psz_mfield, char **ppsz_proto,
586                char **ppsz_port )
587 {
588     char *psz_pos;
589     char *psz_media;
590     if( psz_mfield )
591     {
592         psz_pos = psz_mfield;
593         psz_media = psz_mfield;
594         while( *psz_pos != '\0' && *psz_pos != ' ' )
595         {
596             psz_pos++;
597         }
598         if( *psz_pos != '\0' )
599         {
600             *psz_pos = '\0';
601             if( strcmp( psz_media, "video" ) && strcmp( psz_media, "audio" ) )
602             {
603                 *ppsz_proto = NULL;
604                 *ppsz_port = NULL;
605                 return;
606             }
607         }
608         psz_pos++;
609         *ppsz_port = psz_pos;
610         while( *psz_pos != '\0' && *psz_pos !=' ' && *psz_pos!='/' )
611         {
612             psz_pos++;
613         }
614         if( *psz_pos == '/' )  // FIXME does not support multi-port
615         {
616             *psz_pos = '\0';
617             psz_pos++;
618             while( *psz_pos != '\0' && *psz_pos !=' ' )
619             {
620             psz_pos++;
621             }
622         }
623         *psz_pos = '\0';
624         psz_pos++;
625         *ppsz_proto = psz_pos;
626         while( *psz_pos!='\0' && *psz_pos !=' ' &&
627                         *psz_pos!='/' )
628         {
629             *psz_pos = tolower( *psz_pos );
630             psz_pos++;
631         }
632         *psz_pos = '\0';
633     }
634     else
635     {
636         *ppsz_proto = NULL;
637         *ppsz_port = NULL;
638     }
639     return;
640 }
641
642
643 /*******************************************************************
644  * sess_toitem : changes a sess_descr_t into a hurd of
645  * playlist_item_t, which are enqueued.
646  *******************************************************************
647  * Note : does not support sessions that take place on consecutive
648  * port or adresses yet.
649  *******************************************************************/
650
651 static void sess_toitem( intf_thread_t * p_intf, sess_descr_t * p_sd )
652 {
653     struct sap_announce_t *p_announce;
654     char *psz_uri, *psz_proto, *psz_item_uri;
655     char *psz_port;
656     char *psz_uri_default;
657     int i_count, i, i_id = 0;
658     vlc_bool_t b_http = VLC_FALSE;
659     char *psz_http_path = NULL;
660     playlist_t *p_playlist = NULL;
661     playlist_item_t *p_item;
662
663     psz_uri_default = NULL;
664     if( p_sd->i_media > 1 || !config_GetInt( p_intf, "sap-parse" ) )
665     {
666         asprintf( &psz_uri, "sdp://%s", p_sd->psz_sdp );
667         /* Check if we have already added the item */
668         for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ )
669         {
670             if( !strcmp(p_intf->p_sys->pp_announces[i]->psz_uri,
671                         psz_uri ) )
672             {
673                 p_intf->p_sys->pp_announces[i]->i_last = mdate();
674                 free(psz_uri);
675                 return;
676             }
677         }
678         /* Add it to the playlist */
679         p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
680                                       FIND_ANYWHERE );
681         i_id = playlist_Add( p_playlist, psz_uri, p_sd->psz_sessionname ,
682                       PLAYLIST_CHECK_INSERT, PLAYLIST_END );
683         if( i_id != -1 )
684         {
685             playlist_item_t *p_item = playlist_ItemGetById( p_playlist, i_id );
686             playlist_ItemSetGroup( p_item, p_intf->p_sys->i_group );
687         }
688
689         /* Remember it */
690         p_announce = (struct sap_announce_t *)malloc(
691                       sizeof(struct sap_announce_t) );
692         if( p_sd->psz_sessionname )
693         {
694             p_announce->psz_name = strdup(  p_sd->psz_sessionname );
695         }
696         else
697         {
698             p_announce->psz_name = strdup( "" );
699         }
700         if( psz_uri )
701         {
702             p_announce->psz_uri  = strdup( psz_uri );
703         }
704         else
705         {
706             p_announce->psz_uri = strdup( "" );
707         }
708         p_announce->i_id = i_id;
709         p_announce->i_last = mdate();
710
711         INSERT_ELEM( p_intf->p_sys->pp_announces,
712                      p_intf->p_sys->i_announces,
713                      p_intf->p_sys->i_announces,
714                      p_announce );
715
716         vlc_object_release( p_playlist );
717         free( psz_uri );
718         return;
719     }
720
721     cfield_parse( p_sd->psz_connection, &psz_uri_default );
722
723     for( i_count = 0 ; i_count < p_sd->i_media ; i_count++ )
724     {
725         int i_group = p_intf->p_sys->i_group;
726
727         /* Build what we have to put in psz_item_uri, with the m and
728          * c fields  */
729
730         if( !p_sd->pp_media[i_count] )
731         {
732             return;
733         }
734
735         mfield_parse( p_sd->pp_media[i_count]->psz_medianame,
736                         & psz_proto, & psz_port );
737
738         if( !psz_proto || !psz_port )
739         {
740             return;
741         }
742
743         if( p_sd->pp_media[i_count]->psz_mediaconnection )
744         {
745             cfield_parse( p_sd->pp_media[i_count]->psz_mediaconnection,
746                             & psz_uri );
747         }
748         else
749         {
750             psz_uri = psz_uri_default;
751         }
752
753         if( psz_uri == NULL )
754         {
755             return;
756         }
757
758         for( i = 0 ; i< p_sd->i_attributes ; i++ )
759         {
760             if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "type") &&
761                 strstr( p_sd->pp_attributes[i]->psz_value, "http") )
762             {
763                 b_http = VLC_TRUE;
764             }
765             if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "http-path"))
766             {
767                 psz_http_path = strdup(  p_sd->pp_attributes[i]->psz_value );
768             }
769             if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "plgroup"))
770             {
771                 int i_group_id;
772                 p_playlist =
773                 (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
774                                                         FIND_ANYWHERE );
775                 if( p_playlist == NULL )
776                 {
777                     return;
778                 }
779
780                 i_group_id = playlist_GroupToId( p_playlist,
781                                  p_sd->pp_attributes[i]->psz_value);
782                 if( i_group_id != 0 )
783                 {
784                     i_group = i_group_id;
785                 }
786                 else
787                 {
788                     playlist_group_t *p_group =
789                             playlist_CreateGroup( p_playlist,
790                                        p_sd->pp_attributes[i]->psz_value);
791                     i_group = p_group->i_id;
792                 }
793                 vlc_object_release( p_playlist );
794             }
795         }
796
797         /* Filling psz_uri */
798         if( b_http == VLC_FALSE )
799         {
800             psz_item_uri = malloc( strlen( psz_proto ) + strlen( psz_uri ) +
801                                  strlen( psz_port ) + 7 );
802             if( ismult( psz_uri ) )
803             {
804                 sprintf( psz_item_uri, "%s://@%s:%s",
805                          psz_proto, psz_uri, psz_port );
806             }
807             else
808             {
809                 sprintf( psz_item_uri, "%s://%s:%s",
810                          psz_proto, psz_uri, psz_port );
811             }
812         }
813         else
814         {
815             if( psz_http_path == NULL )
816             {
817                 psz_http_path = strdup( "/" );
818             }
819             if( *psz_http_path == '/' )
820             {
821                 asprintf( &psz_item_uri, "%s://%s:%s%s", psz_proto,
822                          psz_uri, psz_port,psz_http_path );
823             }
824             else
825             {
826                 asprintf( &psz_item_uri, "%s://%s:%s/%s", psz_proto, psz_uri,
827                           psz_port, psz_http_path );
828             }
829
830             if( psz_http_path )
831             {
832                 free( psz_http_path );
833             }
834         }
835
836         /* Check if we already know this item */
837          for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ )
838          {
839             if( !strcmp( p_intf->p_sys->pp_announces[i]->psz_uri,
840                          psz_item_uri ) )
841             {
842                 p_intf->p_sys->pp_announces[i]->i_last = mdate();
843
844                 /* Check if the name changed */
845                 if( strcmp( p_intf->p_sys->pp_announces[i]->psz_name,
846                              p_sd->psz_sessionname ) )
847                 {
848                     playlist_item_t *p_item;
849                     p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
850                                                   FIND_ANYWHERE );
851
852                     msg_Dbg(p_intf, "Name changed (%s -> %s) for %s",
853                             p_intf->p_sys->pp_announces[i]->psz_name,
854                             p_sd->psz_sessionname,
855                             psz_item_uri );
856
857                     p_item = playlist_ItemGetById( p_playlist,
858                                     p_intf->p_sys->pp_announces[i]->i_id );
859
860                     /* Change the name in the item */
861                     if( p_item->input.psz_name )
862                         free( p_item->input.psz_name );
863                     p_item->input.psz_name = strdup( p_sd->psz_sessionname);
864
865                     /* Update the stored name */
866                     if( p_intf->p_sys->pp_announces[i]->psz_name )
867                         free( p_intf->p_sys->pp_announces[i]->psz_name );
868                     p_intf->p_sys->pp_announces[i]->psz_name =
869                                    strdup( p_sd->psz_sessionname );
870
871                     vlc_object_release( p_playlist );
872                 }
873                 free( psz_item_uri );
874                 return;
875             }
876         }
877
878         /* Add the item in the playlist */
879         p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
880                                       FIND_ANYWHERE );
881         i_id = playlist_Add ( p_playlist, psz_item_uri ,
882                                p_sd->psz_sessionname,
883                                PLAYLIST_CHECK_INSERT, PLAYLIST_END );
884         p_item = playlist_ItemGetById( p_playlist, i_id );
885         if( p_item )
886         {
887             vlc_mutex_lock( &p_item->input.lock );
888             playlist_ItemSetGroup( p_item, i_group );
889             vlc_mutex_unlock( &p_item->input.lock );
890         }
891
892         /* Then remember it */
893         p_announce = (struct sap_announce_t *)malloc(
894                       sizeof(struct sap_announce_t) );
895         if(  p_sd->psz_sessionname )
896         {
897             p_announce->psz_name = strdup(  p_sd->psz_sessionname );
898         }
899         else
900         {
901             p_announce->psz_name = strdup( "" );
902         }
903         if( psz_item_uri )
904         {
905             p_announce->psz_uri  = strdup( psz_item_uri );
906         }
907         else
908         {
909             p_announce->psz_uri = strdup( "" );
910         }
911         p_announce->i_id = i_id;
912
913         p_announce->i_last = mdate();
914
915         vlc_object_release( p_playlist );
916
917         INSERT_ELEM( p_intf->p_sys->pp_announces,
918                      p_intf->p_sys->i_announces,
919                      p_intf->p_sys->i_announces,
920                      p_announce );
921         free( psz_item_uri );
922     }
923 }
924
925 /***********************************************************************
926  * parse_sdp : SDP parsing
927  * *********************************************************************
928  * Make a sess_descr_t with a psz
929  ***********************************************************************/
930
931 static sess_descr_t *  parse_sdp( intf_thread_t * p_intf, char *p_packet )
932 {
933     sess_descr_t *  sd;
934
935     if( p_packet[0] != 'v' || p_packet[1] != '=' )
936     {
937         msg_Warn(p_intf, "bad SDP packet");
938         return NULL;
939     }
940
941     sd = malloc( sizeof( sess_descr_t ) );
942     sd->psz_sessionname = NULL;
943     sd->psz_connection  = NULL;
944     sd->psz_sdp         = strdup( p_packet );
945
946     sd->i_media         = 0;
947     sd->pp_media        = NULL;
948     sd->i_attributes    = 0;
949     sd->pp_attributes   = NULL;
950
951     while( *p_packet != '\0'  )
952     {
953         char *psz_end;
954
955         /* Search begin of field */
956         while( *p_packet == '\n' || *p_packet == ' ' || *p_packet == '\t' )
957         {
958             p_packet++;
959         }
960         /* search end of line */
961         if( ( psz_end = strchr( p_packet, '\n' ) ) == NULL )
962         {
963             psz_end = p_packet + strlen( p_packet );
964         }
965         if( psz_end > p_packet && *(psz_end - 1 ) == '\r' )
966         {
967             psz_end--;
968         }
969
970         if( psz_end <= p_packet )
971         {
972             break;
973         }
974         *psz_end++ = '\0';
975
976         if( p_packet[1] != '=' )
977         {
978             msg_Warn( p_intf, "invalid packet") ;
979             free_sd( sd );
980             return NULL;
981         }
982
983         switch( p_packet[0] )
984         {
985             case( 'v' ):
986                 sd->i_version = atoi( &p_packet[2] );
987                 break;
988             case( 's' ):
989                 sd->psz_sessionname = strdup( &p_packet[2] );
990                 break;
991             case ( 'o' ):
992             case( 'i' ):
993             case( 'u' ):
994             case( 'e' ):
995             case( 'p' ):
996             case( 't' ):
997             case( 'r' ):
998                 break;
999             case( 'a' ):
1000             {
1001                 char *psz_eof = strchr( &p_packet[2], ':' );
1002
1003                 if( psz_eof && psz_eof[1] != '\0' )
1004                 {
1005                     attr_descr_t *attr = malloc( sizeof( attr_descr_t ) );
1006
1007                     *psz_eof++ = '\0';
1008
1009                     attr->psz_field = strdup( &p_packet[2] );
1010                     attr->psz_value = strdup( psz_eof );
1011
1012                     TAB_APPEND( sd->i_attributes, sd->pp_attributes, attr );
1013                 }
1014                 break;
1015             }
1016
1017             case( 'm' ):
1018             {
1019                 media_descr_t *media = malloc( sizeof( media_descr_t ) );
1020
1021                 media->psz_medianame = strdup( &p_packet[2] );
1022                 media->psz_mediaconnection = NULL;
1023
1024                 TAB_APPEND( sd->i_media, sd->pp_media, media );
1025                 break;
1026             }
1027
1028             case( 'c' ):
1029                 if( sd->i_media <= 0 )
1030                 {
1031                     sd->psz_connection = strdup( &p_packet[2] );
1032                 }
1033                 else
1034                 {
1035                     sd->pp_media[sd->i_media-1]->psz_mediaconnection = strdup( &p_packet[2] );
1036                 }
1037                break;
1038
1039             default:
1040                break;
1041         }
1042
1043         p_packet = psz_end;
1044     }
1045
1046     return sd;
1047 }
1048
1049 #define FREE( p ) \
1050     if( p ) { free( p ); (p) = NULL; }
1051 static void free_sd( sess_descr_t * p_sd )
1052 {
1053     int i;
1054
1055     FREE( p_sd->psz_sessionname );
1056     FREE( p_sd->psz_connection );
1057     FREE( p_sd->psz_sdp );
1058
1059     for( i = 0; i < p_sd->i_media ; i++ )
1060     {
1061         FREE( p_sd->pp_media[i]->psz_medianame );
1062         FREE( p_sd->pp_media[i]->psz_mediaconnection );
1063         FREE( p_sd->pp_media[i] );
1064     }
1065     for( i = 0; i < p_sd->i_attributes ; i++ )
1066     {
1067         FREE( p_sd->pp_attributes[i]->psz_field );
1068         FREE( p_sd->pp_attributes[i]->psz_value );
1069         FREE( p_sd->pp_attributes[i] );
1070     }
1071     FREE( p_sd->pp_attributes );
1072     FREE( p_sd->pp_media );
1073
1074     free( p_sd );
1075 }
1076
1077 /***********************************************************************
1078  * ismult: returns true if we have a multicast address
1079  ***********************************************************************/
1080
1081 static int ismult( char *psz_uri )
1082 {
1083     char *psz_end;
1084     int  i_value;
1085
1086     i_value = strtol( psz_uri, &psz_end, 0 );
1087
1088     /* IPv6 */
1089     if( psz_uri[0] == '[')
1090     {
1091       if( strncasecmp( &psz_uri[1], "FF0" , 3) ||
1092           strncasecmp( &psz_uri[2], "FF0" , 3))
1093             return( VLC_TRUE );
1094         else
1095             return( VLC_FALSE );
1096     }
1097
1098     if( *psz_end != '.' ) { return( VLC_FALSE ); }
1099
1100     return( i_value < 224 ? VLC_FALSE : VLC_TRUE );
1101 }
1102
1103
1104
1105 /*****************************************************************************
1106  * Read: read on a file descriptor, checking b_die periodically
1107  *****************************************************************************
1108  * Taken from udp.c
1109  *****************************************************************************/
1110 static ssize_t NetRead( intf_thread_t *p_intf,
1111                         int fd[2], uint8_t *p_buffer, int i_len )
1112 {
1113 #ifdef UNDER_CE
1114     return -1;
1115 #else
1116     struct timeval  timeout;
1117     fd_set          fds;
1118     int             i_ret;
1119     int             i_handle_max = __MAX( fd[0], fd[1] );
1120
1121     /* Initialize file descriptor set */
1122     FD_ZERO( &fds );
1123     if( fd[0] > 0 ) FD_SET( fd[0], &fds );
1124     if( fd[1] > 0 ) FD_SET( fd[1], &fds );
1125
1126     /* We'll wait 0.5 second if nothing happens */
1127     timeout.tv_sec = 0;
1128     timeout.tv_usec = 500000;
1129
1130     /* Find if some data is available */
1131     i_ret = select( i_handle_max + 1, &fds, NULL, NULL, &timeout );
1132
1133     if( i_ret == -1 && errno != EINTR )
1134     {
1135         msg_Err( p_intf, "network select error (%s)", strerror(errno) );
1136     }
1137     else if( i_ret > 0 )
1138     {
1139         if( fd[0] > 0 && FD_ISSET( fd[0], &fds ) )
1140         {
1141              return recv( fd[0], p_buffer, i_len, 0 );
1142         }
1143         else if( fd[1] > 0 && FD_ISSET( fd[1], &fds ) )
1144         {
1145              return recv( fd[1], p_buffer, i_len, 0 );
1146         }
1147     }
1148     return 0;
1149 #endif
1150 }