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