]> git.sesse.net Git - vlc/blob - modules/misc/sap.c
e57b2163c079887e162431029d3b12da158c246f
[vlc] / modules / misc / sap.c
1 /*****************************************************************************
2  * sap.c :  SAP interface module
3  *****************************************************************************
4  * Copyright (C) 2004 VideoLAN
5  * $Id$
6  *
7  * Authors: ClĂ©ment Stenac <zorglub@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Includes
26  *****************************************************************************/
27 #include <stdlib.h>                                      /* malloc(), free() */
28
29 #include <vlc/vlc.h>
30 #include <vlc/intf.h>
31
32 #include <vlc/input.h>
33
34 #include "network.h"
35
36 #include <errno.h>                                                 /* ENOMEM */
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 HAVE_ZLIB_H
46 #   include <zlib.h>
47 #endif
48
49 /************************************************************************
50  * Macros and definitions
51  ************************************************************************/
52
53 #define MAX_LINE_LENGTH 256
54
55 /* SAP is always on that port */
56 #define SAP_PORT 9875
57 #define SAP_V4_ADDRESS "224.2.127.254"
58 #define ADD_SESSION 1
59
60 #define IPV6_ADDR_1 "FF0"  /* Scope is inserted between them */
61 #define IPV6_ADDR_2 "::2:7FFE"
62
63
64 /*****************************************************************************
65  * Module descriptor
66  *****************************************************************************/
67 #define SAP_ADDR_TEXT N_( "SAP multicast address" )
68 #define SAP_ADDR_LONGTEXT N_( "SAP multicast address" )
69 #define SAP_IPV4_TEXT N_( "IPv4-SAP listening" )
70 #define SAP_IPV4_LONGTEXT N_( \
71       "Set this if you want the SAP module to listen to IPv4 announces" )
72 #define SAP_IPV6_TEXT N_( "IPv6-SAP listening" )
73 #define SAP_IPV6_LONGTEXT N_( \
74       "Set this if you want the SAP module to listen to IPv6 announces" )
75 #define SAP_SCOPE_TEXT N_( "IPv6 SAP scope" )
76 #define SAP_SCOPE_LONGTEXT N_( \
77        "Sets the scope for IPv6 announces (default is 8)" )
78 #define SAP_TIMEOUT_TEXT N_( "SAP timeout (seconds)" )
79 #define SAP_TIMEOUT_LONGTEXT N_( \
80        "Sets the time before SAP items get deleted if no new announce " \
81        "is received." )
82 #define SAP_PARSE_TEXT N_( "Try to parse the SAP" )
83 #define SAP_PARSE_LONGTEXT N_( \
84        "When SAP can it will try to parse the SAP. If you don't select " \
85        "this, all announces will be parsed by the livedotcom module" )
86
87 /* Callbacks */
88     static int  Open ( vlc_object_t * );
89     static void Close( vlc_object_t * );
90     static int  OpenDemux ( vlc_object_t * );
91     static void CloseDemux ( vlc_object_t * );
92
93 vlc_module_begin();
94     set_description( _("SAP interface") );
95
96     add_string( "sap-addr", NULL, NULL,
97                 SAP_ADDR_TEXT, SAP_ADDR_LONGTEXT, VLC_TRUE );
98     add_bool( "sap-ipv4", 1 , NULL,
99                SAP_IPV4_TEXT,SAP_IPV4_LONGTEXT, VLC_TRUE );
100     add_bool( "sap-ipv6", 0 , NULL,
101               SAP_IPV6_TEXT, SAP_IPV6_LONGTEXT, VLC_TRUE );
102     add_string( "sap-ipv6-scope", "8" , NULL,
103                 SAP_SCOPE_TEXT, SAP_SCOPE_LONGTEXT, VLC_TRUE);
104     add_integer( "sap-timeout", 1800, NULL,
105                  SAP_TIMEOUT_TEXT, SAP_TIMEOUT_LONGTEXT, VLC_TRUE );
106     add_bool( "sap-parse", 1 , NULL,
107                SAP_PARSE_TEXT,SAP_PARSE_LONGTEXT, VLC_TRUE );
108
109     set_capability( "interface", 0 );
110     set_callbacks( Open, Close );
111
112     add_submodule();
113         set_description( _("SDP file parser for UDP") );
114         add_shortcut( "sdp" );
115         set_capability( "demux2", 51 );
116         set_callbacks( OpenDemux, CloseDemux );
117 vlc_module_end();
118
119
120 /*****************************************************************************
121  * Local structures
122  *****************************************************************************/
123
124 typedef struct sdp_t sdp_t;
125 typedef struct attribute_t attribute_t;
126 typedef struct sap_announce_t sap_announce_t;
127
128 /* The structure that contains sdp information */
129 struct  sdp_t
130 {
131     char *psz_sdp;
132
133     /* s= field */
134     char *psz_sessionname;
135
136     /* Raw m= and c= fields */
137     char *psz_connection;
138     char *psz_media;
139
140     /* "computed" URI */
141     char *psz_uri;
142
143     int         i_in; /* IP version */
144
145     int           i_media;
146
147     int           i_attributes;
148     attribute_t  **pp_attributes;
149 };
150
151 struct attribute_t
152 {
153     char *psz_field;
154     char *psz_value;
155 };
156
157 struct sap_announce_t
158 {
159     mtime_t i_last;
160
161     uint16_t    i_hash;
162     uint32_t    i_source[4];
163
164     /* SAP annnounces must only contain one SDP */
165     sdp_t       *p_sdp;
166
167     playlist_item_t *p_item;
168 };
169
170 struct intf_sys_t
171 {
172     /* Socket descriptors */
173     int i_fd;
174     int *pi_fd;
175
176     /* playlist node */
177     playlist_item_t *p_node;
178
179     /* Table of announces */
180     int i_announces;
181     struct sap_announce_t **pp_announces;
182
183     /* Modes */
184     vlc_bool_t  b_strict;
185     vlc_bool_t  b_parse;
186
187     int i_timeout;
188 };
189
190 /*****************************************************************************
191  * Local prototypes
192  *****************************************************************************/
193
194
195 /* Main functions */
196     static int Demux( demux_t *p_demux );
197     static int Control( demux_t *, int, va_list );
198     static void Run    ( intf_thread_t *p_intf );
199
200 /* Main parsing functions */
201     static int ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp );
202     static int ParseSAP( intf_thread_t *p_intf, uint8_t *p_buffer, int i_read );
203     static sdp_t *  ParseSDP( vlc_object_t *p_intf, char* psz_sdp );
204     static sap_announce_t *CreateAnnounce( intf_thread_t *, uint16_t, sdp_t * );
205
206 /* Cache */
207     static void CacheLoad( intf_thread_t *p_intf );
208     static void CacheSave( intf_thread_t *p_intf );
209 /* Helper functions */
210    static char *GetAttribute( sdp_t *p_sdp, const char *psz_search );
211    static int InitSocket( intf_thread_t *p_intf, char *psz_address, int i_port );
212 #ifdef HAVE_ZLIB_H
213    static int Decompress( unsigned char *psz_src, unsigned char **_dst, int i_len );
214     static void FreeSDP( sdp_t *p_sdp );
215 #endif
216
217 /* Detect multicast addresses */
218 static int  ismult( char * );
219
220 #define FREE( p ) \
221     if( p ) { free( p ); (p) = NULL; }
222 /*****************************************************************************
223  * Open: initialize and create stuff
224  *****************************************************************************/
225 static int Open( vlc_object_t *p_this )
226 {
227     intf_thread_t *p_intf = ( intf_thread_t* )p_this;
228     intf_sys_t    *p_sys  = malloc( sizeof( intf_sys_t ) );
229
230     playlist_t          *p_playlist;
231     playlist_view_t     *p_view;
232
233     p_sys->i_timeout = config_GetInt( p_intf,"sap-timeout" );
234
235
236     p_intf->pf_run = Run;
237     p_intf->p_sys  = p_sys;
238
239     p_sys->pi_fd = NULL;
240     p_sys->i_fd = 0;
241
242     /* FIXME */
243     p_sys->b_strict = VLC_FALSE;
244
245     if( config_GetInt( p_intf, "sap-use-cache" ) )
246     {
247         CacheLoad( p_intf );
248     }
249
250     if( config_GetInt( p_intf, "sap-ipv4" ) )
251     {
252         InitSocket( p_intf, SAP_V4_ADDRESS, SAP_PORT );
253     }
254     if( config_GetInt( p_intf, "sap-ipv6" ) )
255     {
256         /* TODO */
257     }
258
259     /* TODO : Handle additionnal adresses */
260
261     if( p_sys->i_fd == 0 )
262     {
263         msg_Err( p_intf, "unable to read on any address");
264         return VLC_EGENERIC;
265     }
266
267     /* Create our playlist node */
268     p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
269                                                 FIND_ANYWHERE );
270     if( !p_playlist )
271     {
272         msg_Warn( p_intf, "unable to find playlist, cancelling SAP listening");
273         return VLC_EGENERIC;
274     }
275
276     p_view = playlist_ViewFind( p_playlist, VIEW_CATEGORY );
277     p_sys->p_node = playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
278                                          _("SAP"), p_view->p_root );
279
280     vlc_object_release( p_playlist );
281
282     p_sys->i_announces = 0;
283     p_sys->pp_announces = NULL;
284
285     return VLC_SUCCESS;
286 }
287
288 /*****************************************************************************
289  * OpenDemux: initialize and create stuff
290  *****************************************************************************/
291 static int OpenDemux( vlc_object_t *p_this )
292 {
293     demux_t *p_demux = (demux_t *)p_this;
294     uint8_t *p_peek;
295
296     /* Probe for SDP */
297     if( p_demux->s )
298     {
299         if( stream_Peek( p_demux->s, &p_peek, 7 ) < 7 )
300         {
301             msg_Err( p_demux, "cannot peek" );
302             return VLC_EGENERIC;
303         }
304         if( strncmp( (char*)p_peek, "v=0\r\n", 5 ) &&
305             strncmp( (char*)p_peek, "v=0\n", 4 ) &&
306             ( p_peek[0] < 'a' || p_peek[0] > 'z' || p_peek[1] != '=' ) )
307         {
308             msg_Warn( p_demux, "SDP (UDP) module discarded" );
309             return VLC_EGENERIC;
310         }
311     }
312
313     p_demux->pf_control = Control;
314     p_demux->pf_demux = Demux;
315
316     return VLC_SUCCESS;
317 }
318
319 /*****************************************************************************
320  * Close:
321  *****************************************************************************/
322 static void Close( vlc_object_t *p_this )
323 {
324     intf_thread_t *p_intf = ( intf_thread_t* )p_this;
325     intf_sys_t    *p_sys  = p_intf->p_sys;
326     int i;
327
328     for( i = p_sys->i_fd-1 ; i >= 0 ; i-- )
329     {
330         net_Close( p_sys->pi_fd[i] );
331     }
332
333     if( config_GetInt( p_intf, "sap-use-cache" ) )
334     {
335         CacheSave( p_intf );
336     }
337
338     for( i = p_sys->i_announces  - 1;  i>= 0; i-- )
339     {
340         RemoveAnnounce( p_intf, p_sys->pp_announces[i] );
341     }
342
343     free( p_sys );
344 }
345
346 /*****************************************************************************
347  * CloseDemux: Close the demuxer
348  *****************************************************************************/
349 static void CloseDemux( vlc_object_t *p_this )
350 {
351
352 }
353
354 /*****************************************************************************
355  * Run: main SAP thread
356  *****************************************************************************
357  * Listens to SAP packets, and sends them to packet_handle
358  *****************************************************************************/
359 #define MAX_SAP_BUFFER 5000
360
361 static void Run( intf_thread_t *p_intf )
362 {
363     uint8_t     *p_buffer;
364     /* Dirty hack to slow down the startup of the sap interface */
365     /* Unneeded now : our node is in no_select mode */
366     //    msleep( 500000 );
367
368     /* read SAP packets */
369     while( !p_intf->b_die )
370     {
371         p_buffer = (uint8_t *)malloc( MAX_SAP_BUFFER );
372
373         if( !p_buffer )
374         {
375             msg_Err( p_intf, "out of memory");
376             p_intf->b_die = VLC_TRUE;
377             continue;
378         }
379
380         int i_read = net_Select( p_intf, p_intf->p_sys->pi_fd, NULL,
381                                  p_intf->p_sys->i_fd, p_buffer,
382                                  MAX_SAP_BUFFER, 500000 );
383 #if 0
384         /* Check for items that need deletion */
385         for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ )
386         {
387            struct sap_announce_t *p_announce;
388            mtime_t i_timeout = ( mtime_t ) 1000000*p_sys->i_timeout;
389            if( mdate() - p_intf->p_sys->pp_announces[i]->i_last > i_timeout )
390            {
391                msg_Dbg( p_intf,"Time out for %s, deleting (%i/%i)",
392                         p_intf->p_sys->pp_announces[i]->psz_name,
393                         i , p_intf->p_sys->i_announces );
394
395              /* Remove the playlist item */
396                p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
397                               FIND_ANYWHERE );
398                if( p_playlist )
399                {
400                    int i_pos = playlist_GetPositionById( p_playlist,
401                               p_intf->p_sys->pp_announces[i]->i_id );
402                    playlist_Delete( p_playlist, i_pos );
403                    vlc_object_release( p_playlist );
404                }
405
406                /* Free the p_announce */
407                p_announce =  p_intf->p_sys->pp_announces[i];
408                if( p_announce->psz_name )
409                   free(  p_announce->psz_name );
410                if( p_announce->psz_uri )
411                   free(  p_announce->psz_uri );
412
413               /* Remove the sap_announce from the array */
414               REMOVE_ELEM( p_intf->p_sys->pp_announces,
415                            p_intf->p_sys->i_announces,
416                            i );
417
418               free( p_announce );
419
420            }
421         }
422 #endif
423
424         /* Minimum length is > 6 */
425         if( i_read <= 6 )
426         {
427             if( i_read < 0 )
428             {
429                 msg_Warn( p_intf, "socket read error" );
430             }
431             continue;
432         }
433
434         p_buffer[i_read] = '\0';
435
436         /* Parse the packet */
437         ParseSAP( p_intf, p_buffer, i_read );
438
439         free( p_buffer );
440     }
441 }
442
443 /**********************************************************************
444  * Demux: reads and demuxes data packets
445  * Return -1 if error, 0 if EOF, 1 else
446  **********************************************************************/
447 static int Demux( demux_t *p_demux )
448 {
449    int i_max_sdp = 1024;
450    int i_sdp = 0;
451    char *psz_sdp = (char *)malloc( i_max_sdp );
452    sdp_t *p_sdp;
453
454    playlist_t *p_playlist;
455
456    /* Gather the complete sdp file */
457    for( ;; )
458    {
459         int i_read = stream_Read( p_demux->s,
460                                   &psz_sdp[i_sdp], i_max_sdp - i_sdp - 1 );
461
462
463         if( i_read < 0 )
464
465         {
466             msg_Err( p_demux, "failed to read SDP" );
467             return VLC_EGENERIC;
468         }
469
470         i_sdp += i_read;
471
472         if( i_read < i_max_sdp - i_sdp - 1 )
473         {
474             psz_sdp[i_sdp] = '\0';
475             break;
476         }
477
478         i_max_sdp += 1000;
479         psz_sdp = (uint8_t*)realloc( psz_sdp, i_max_sdp );
480    }
481
482    p_sdp = ParseSDP( VLC_OBJECT(p_demux), psz_sdp );
483
484    if( !p_sdp ) return -1;
485
486    if( p_sdp->i_media > 1 )
487    {
488         return -1;
489    }
490
491    if( ParseConnection( VLC_OBJECT( p_demux ), p_sdp ) )
492    {
493        p_sdp->psz_uri = NULL;
494    }
495
496    if( p_sdp->psz_uri == NULL ) return VLC_EGENERIC;
497
498    p_playlist = (playlist_t *)vlc_object_find( p_demux, VLC_OBJECT_PLAYLIST,
499                                                FIND_ANYWHERE );
500
501    p_playlist->status.p_item->i_flags |= PLAYLIST_DEL_FLAG;
502
503    playlist_Add( p_playlist, p_sdp->psz_uri, p_sdp->psz_sessionname,
504                  PLAYLIST_APPEND, PLAYLIST_END );
505
506    vlc_object_release( p_playlist );
507
508    FreeSDP( p_sdp );
509    free( psz_sdp );
510
511    return VLC_SUCCESS;
512 }
513
514 static int Control( demux_t *p_demux, int i_query, va_list args )
515 {
516     return VLC_EGENERIC;
517 }
518
519 /**************************************************************
520  * Local functions
521  **************************************************************/
522
523 static int ParseSAP( intf_thread_t *p_intf, uint8_t *p_buffer, int i_read )
524 {
525     int                 i_version, i_address_type, i_hash, i;
526     uint8_t             *psz_sdp;
527     sdp_t               *p_sdp;
528     vlc_bool_t          b_compressed;
529     vlc_bool_t          b_need_delete = VLC_FALSE;
530 #ifdef HAVE_ZLIB_H
531     int                 i_decompressed_size;
532     uint8_t             *p_decompressed_buffer;
533 #endif
534
535     /* First, check the sap announce is correct */
536     i_version = p_buffer[0] >> 5;
537
538     if( i_version != 1 )
539     {
540        msg_Dbg( p_intf, "strange sap version %d found", i_version );
541     }
542
543     i_address_type = p_buffer[0] & 0x10;
544
545     if( (p_buffer[0] & 0x08) != 0 )
546     {
547         msg_Dbg( p_intf, "reserved bit incorrectly set" );
548         return VLC_EGENERIC;
549     }
550
551     if( (p_buffer[0] & 0x04) != 0 )
552     {
553         msg_Dbg( p_intf, "session deletion packet" );
554         b_need_delete = VLC_TRUE;
555     }
556
557     if( p_buffer[0] & 0x02  )
558     {
559         msg_Dbg( p_intf, "encrypted packet, unsupported" );
560         return VLC_EGENERIC;
561     }
562
563     b_compressed = p_buffer[0] & 0x01;
564
565     i_hash = ( p_buffer[2] << 8 ) + p_buffer[3];
566
567     if( p_intf->p_sys->b_strict && i_hash == 0 )
568     {
569         msg_Dbg( p_intf, "strict mode, discarding announce with null id hash");
570         return VLC_EGENERIC;
571     }
572
573     psz_sdp  = &p_buffer[4];
574
575     if( i_address_type == 0 ) /* ipv4 source address */
576     {
577         psz_sdp += 4;
578     }
579     else /* ipv6 source address */
580     {
581         psz_sdp += 16;
582     }
583
584     if( b_compressed )
585     {
586 #ifdef HAVE_ZLIB_H
587         i_decompressed_size = Decompress( psz_sdp,
588                    &p_decompressed_buffer,i_read - ( psz_sdp - p_buffer ) );
589         if( i_decompressed_size > 0 && i_decompressed_size < MAX_SAP_BUFFER )
590         {
591             memcpy( psz_sdp, p_decompressed_buffer, i_decompressed_size );
592             psz_sdp[i_decompressed_size] = '\0';
593             free( p_decompressed_buffer );
594         }
595 #else
596         msg_Warn( p_intf, "Ignoring compressed sap packet" );
597         return VLC_EGENERIC;
598 #endif
599     }
600
601     /* Add the size of authentification info */
602     psz_sdp += p_buffer[1];
603
604     /* Skip payload type */
605     /* Handle announces without \0 between SAP and SDP */
606     while( *psz_sdp != '\0' && ( psz_sdp[0] != 'v' && psz_sdp[1] != '=' ) )
607     {
608         psz_sdp++;
609     }
610
611     if( *psz_sdp == '\0' )
612     {
613         psz_sdp++;
614     }
615
616     /* Parse SDP info */
617     p_sdp = ParseSDP( VLC_OBJECT(p_intf), psz_sdp );
618
619     if( p_sdp == NULL )
620     {
621         return VLC_EGENERIC;
622     }
623
624     /* Decide whether we should add a playlist item for this SDP */
625
626     /* Multi-media or no-parse -> pass to LIVE.COM */
627     if( p_sdp->i_media > 1 || p_intf->p_sys->b_parse == VLC_FALSE )
628     {
629         asprintf( &p_sdp->psz_uri, "sdp://%s", p_sdp->psz_sdp );
630     }
631     else
632     {
633         /* Parse connection information (c= & m= ) */
634         if( ParseConnection( VLC_OBJECT(p_intf), p_sdp ) )
635         {
636             p_sdp->psz_uri = NULL;
637         }
638     }
639
640     if( p_sdp->psz_uri == NULL ) return VLC_EGENERIC;
641
642     for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ )
643     {
644         /* FIXME: slow */
645         /* FIXME: we create a new announce each time the sdp changes */
646         if( !strcmp( p_intf->p_sys->pp_announces[i]->p_sdp->psz_sdp,
647                     p_sdp->psz_sdp ) )
648         {
649             if( b_need_delete )
650             {
651                 RemoveAnnounce( p_intf, p_intf->p_sys->pp_announces[i]);
652                 return VLC_SUCCESS;
653             }
654             else
655             {
656                 p_intf->p_sys->pp_announces[i]->i_last = mdate();
657                 FreeSDP( p_sdp );
658                 return VLC_SUCCESS;
659             }
660         }
661     }
662     /* Add item */
663     if( p_sdp->i_media > 1 )
664     {
665         msg_Dbg( p_intf, "passing to LIVE.COM" );
666     }
667
668     CreateAnnounce( p_intf, i_hash, p_sdp );
669
670     return VLC_SUCCESS;
671 }
672
673 sap_announce_t *CreateAnnounce( intf_thread_t *p_intf, uint16_t i_hash,
674                                 sdp_t *p_sdp )
675 {
676     playlist_t          *p_playlist;
677     playlist_item_t     *p_item, *p_child;
678     char                *psz_value;
679     sap_announce_t *p_sap = (sap_announce_t *)malloc(
680                                         sizeof(sap_announce_t ) );
681     if( !p_sap )
682     {
683         msg_Err( p_intf, "out of memory");
684         p_intf->b_die = VLC_TRUE;
685         return NULL;
686     }
687     p_sap->i_last = mdate();
688     p_sap->i_hash = i_hash;
689     p_sap->p_sdp = p_sdp;
690     p_sap->p_item = NULL;
691
692     /* Create the playlist item here */
693     p_item = playlist_ItemNew( p_intf, p_sap->p_sdp->psz_uri,
694                                p_sap->p_sdp->psz_sessionname );
695
696     if( !p_item )
697     {
698         return NULL;
699     }
700
701     psz_value = GetAttribute( p_sap->p_sdp, "x-plgroup" );
702
703     if( psz_value == NULL )
704     {
705         psz_value = GetAttribute( p_sap->p_sdp, "plgroup" );
706     }
707
708     p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
709                                                 FIND_ANYWHERE );
710     if( !p_playlist )
711     {
712         msg_Err( p_intf, "playlist not found" );
713         FREE( psz_value );
714         free( p_sap );
715         return NULL;
716     }
717
718     if( psz_value != NULL )
719     {
720         p_child = playlist_ChildSearchName( p_intf->p_sys->p_node, psz_value );
721
722         if( p_child == NULL )
723         {
724             p_child = playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
725                                            psz_value, p_intf->p_sys->p_node );
726         }
727     }
728     else
729     {
730         p_child = p_intf->p_sys->p_node;
731     }
732
733     p_item->i_flags &= ~PLAYLIST_SKIP_FLAG;
734
735     playlist_NodeAddItem( p_playlist, p_item, VIEW_CATEGORY, p_child,
736                           PLAYLIST_APPEND, PLAYLIST_END );
737
738     vlc_object_release( p_playlist );
739
740     p_sap->p_item = p_item;
741
742     TAB_APPEND( p_intf->p_sys->i_announces,
743                 p_intf->p_sys->pp_announces,
744                 p_sap );
745
746     return p_sap;
747 }
748
749 static char *GetAttribute( sdp_t *p_sdp, const char *psz_search )
750 {
751     int i;
752
753     for( i = 0 ; i< p_sdp->i_attributes; i++ )
754     {
755         if( !strncmp( p_sdp->pp_attributes[i]->psz_field, psz_search,
756                       strlen( p_sdp->pp_attributes[i]->psz_field ) ) )
757         {
758             return p_sdp->pp_attributes[i]->psz_value;
759         }
760     }
761     return NULL;
762 }
763
764
765 /* Fill p_sdp->psz_uri */
766 static int ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp )
767 {
768     char *psz_eof;
769     char *psz_parse;
770     char *psz_uri = NULL;
771     char *psz_proto = NULL;
772     int i_port = 0;
773
774     /* Parse c= field */
775     if( p_sdp->psz_connection )
776     {
777         psz_parse = p_sdp->psz_connection;
778
779         psz_eof = strchr( psz_parse, ' ' );
780
781         if( psz_eof )
782         {
783             *psz_eof = '\0';
784             psz_parse = psz_eof + 1;
785         }
786         else
787         {
788             msg_Warn( p_obj, "unable to parse c field (1)");
789             return VLC_EGENERIC;
790         }
791
792         psz_eof = strchr( psz_parse, ' ' );
793
794         if( psz_eof )
795         {
796             *psz_eof = '\0';
797             if( !strncmp( psz_parse, "IP4", 3 ) )
798             {
799                 p_sdp->i_in = 4;
800             }
801             else if( !strncmp( psz_parse, "IP6", 3 ) )
802             {
803                 p_sdp->i_in = 6;
804             }
805             else
806             {
807                 p_sdp->i_in = 0;
808             }
809             psz_parse = psz_eof + 1;
810         }
811         else
812         {
813             msg_Warn( p_obj, "unable to parse c field (2)");
814             return VLC_EGENERIC;
815         }
816
817         psz_eof = strchr( psz_parse, '/' );
818
819         if( psz_eof )
820         {
821             *psz_eof = 0;
822             psz_uri = strdup( psz_parse );
823         }
824
825         else
826         {
827             msg_Warn( p_obj, "unable to parse c field (3)");
828             return VLC_EGENERIC;
829         }
830
831     }
832
833     /* Parse m= field */
834     if( p_sdp->psz_media )
835     {
836         psz_parse = p_sdp->psz_media;
837
838         psz_eof = strchr( psz_parse, ' ' );
839
840         if( psz_eof )
841         {
842             *psz_eof = '\0';
843
844             if( strncmp( psz_parse, "audio", 5 )  &&
845                 strncmp( psz_parse, "video",5 ) )
846             {
847                 msg_Warn( p_obj, "unhandled media type -%s-", psz_parse );
848                 return VLC_EGENERIC;
849             }
850
851             psz_parse = psz_eof + 1;
852         }
853         else
854         {
855             msg_Warn( p_obj, "unable to parse m field (1)");
856             return VLC_EGENERIC;
857         }
858
859         psz_eof = strchr( psz_parse, ' ' );
860
861         if( psz_eof )
862         {
863             *psz_eof = '\0';
864
865             /* FIXME : multiple port ! */
866             i_port = atoi( psz_parse );
867
868             if( i_port <= 0 || i_port >= 65536 )
869             {
870                 msg_Warn( p_obj, "invalid transport port %i", i_port );
871             }
872
873             psz_parse = psz_eof + 1;
874         }
875         else
876         {
877             msg_Warn( p_obj, "unable to parse m field (2)");
878             return VLC_EGENERIC;
879         }
880
881         psz_eof = strchr( psz_parse, ' ' );
882
883         if( psz_eof )
884         {
885             *psz_eof = '\0';
886
887             psz_proto = strdup( psz_parse );
888         }
889         else
890         {
891             msg_Warn( p_obj, "unable to parse m field (3)");
892             return VLC_EGENERIC;
893         }
894     }
895
896     /* FIXME: HTTP support */
897
898     if( i_port == 0 )
899     {
900         i_port = 1234;
901     }
902
903     if( ismult( psz_uri ) )
904     {
905         asprintf( &p_sdp->psz_uri, "%s://@%s:%i", psz_proto, psz_uri, i_port );
906     }
907     else
908     {
909         asprintf( &p_sdp->psz_uri, "%s://%s:%i", psz_proto, psz_uri, i_port );
910     }
911     FREE( psz_uri );
912     FREE( psz_proto );
913     return VLC_SUCCESS;
914 }
915
916 /***********************************************************************
917  * ParseSDP : SDP parsing
918  * *********************************************************************
919  * Validate SDP and parse all fields
920  ***********************************************************************/
921 static sdp_t *  ParseSDP( vlc_object_t *p_obj, char* psz_sdp )
922 {
923     sdp_t *p_sdp;
924
925     if( psz_sdp[0] != 'v' || psz_sdp[1] != '=' )
926     {
927         msg_Warn( p_obj, "bad SDP packet" );
928         return NULL;
929     }
930
931     p_sdp = (sdp_t *)malloc( sizeof( sdp_t ) );
932
933     p_sdp->psz_sdp = strdup( psz_sdp );
934
935     p_sdp->psz_sessionname = NULL;
936     p_sdp->psz_media       = NULL;
937     p_sdp->psz_connection  = NULL;
938
939     p_sdp->i_media         = 0;
940     p_sdp->i_attributes    = 0;
941     p_sdp->pp_attributes   = NULL;
942
943     while( *psz_sdp != '\0'  )
944     {
945         char *psz_eol;
946         while( *psz_sdp == '\r' || *psz_sdp == '\n' || *psz_sdp == ' ' || *psz_sdp == '\t' )
947         {
948             psz_sdp++;
949         }
950
951         if( ( psz_eol = strchr( psz_sdp, '\n' ) ) == NULL )
952         {
953             psz_eol = psz_sdp + strlen( psz_sdp );
954         }
955         if( psz_eol > psz_sdp && *( psz_eol - 1 ) == '\r' )
956         {
957             psz_eol--;
958         }
959
960         if( psz_eol <= psz_sdp )
961         {
962             break;
963         }
964         *psz_eol++ = '\0';
965
966         /* no space allowed between fields */
967         if( psz_sdp[1] != '=' )
968         {
969             msg_Warn( p_obj, "invalid packet" ) ;
970             /* MEMLEAK ! */
971             return NULL;
972         }
973
974         /* Now parse each line */
975         switch( psz_sdp[0] )
976         {
977             case( 'v' ):
978                 break;
979             case( 's' ):
980                 p_sdp->psz_sessionname = strdup( &psz_sdp[2] );
981                 break;
982             case ( 'o' ):
983             case( 'i' ):
984             case( 'u' ):
985             case( 'e' ):
986             case( 'p' ):
987             case( 't' ):
988             case( 'r' ):
989                 break;
990             case( 'a' ): /* attribute */
991             {
992                 char *psz_eon = strchr( &psz_sdp[2], ':' );
993
994                  attribute_t *p_attr = (attribute_t *)malloc(
995                                         sizeof( attribute_t ) );
996
997                 /* Attribute with value */
998                 if( psz_eon )
999                 {
1000                     *psz_eon++ = '\0';
1001
1002                     p_attr->psz_field = strdup( &psz_sdp[2] );
1003                     p_attr->psz_value = strdup( psz_eon );
1004                 }
1005                 else /* Attribute without value */
1006                 {
1007                     p_attr->psz_field = strdup( &psz_sdp[2] );
1008                     p_attr->psz_value = NULL;
1009                 }
1010
1011                 TAB_APPEND( p_sdp->i_attributes, p_sdp->pp_attributes, p_attr );
1012                 break;
1013             }
1014
1015             case( 'm' ): /* Media announcement */
1016             {
1017                 /* If we have several medias, we pass the announcement to
1018                  * LIVE.COM, so just count them */
1019                 p_sdp->i_media++;
1020                 if( p_sdp->i_media == 1 )
1021                 {
1022                     p_sdp->psz_media = strdup( &psz_sdp[2] );
1023                 }
1024                 break;
1025             }
1026
1027             case( 'c' ):
1028             {
1029                 if( p_sdp->i_media > 1 )
1030                     break;
1031
1032                 p_sdp->psz_connection = strdup( &psz_sdp[2] );
1033                 break;
1034             }
1035
1036             default:
1037                break;
1038         }
1039
1040         psz_sdp = psz_eol;
1041     }
1042
1043     return p_sdp;
1044 }
1045
1046
1047 /***********************************************************************
1048  * ismult: returns true if we have a multicast address
1049  ***********************************************************************/
1050
1051 static int ismult( char *psz_uri )
1052 {
1053     char *psz_end;
1054     int  i_value;
1055
1056     i_value = strtol( psz_uri, &psz_end, 0 );
1057
1058     /* IPv6 */
1059     if( psz_uri[0] == '[')
1060     {
1061       if( strncasecmp( &psz_uri[1], "FF0" , 3) ||
1062           strncasecmp( &psz_uri[2], "FF0" , 3))
1063             return( VLC_TRUE );
1064         else
1065             return( VLC_FALSE );
1066     }
1067
1068     if( *psz_end != '.' ) { return( VLC_FALSE ); }
1069
1070     return( i_value < 224 ? VLC_FALSE : VLC_TRUE );
1071 }
1072
1073 static int InitSocket( intf_thread_t *p_intf, char *psz_address, int i_port )
1074 {
1075     int i_fd = net_OpenUDP( p_intf, psz_address, i_port, "", 0 );
1076
1077     if( i_fd != -1 )
1078     {
1079         INSERT_ELEM(  p_intf->p_sys->pi_fd,
1080                       p_intf->p_sys->i_fd,
1081                       p_intf->p_sys->i_fd,
1082                       i_fd );
1083         return VLC_SUCCESS;
1084     }
1085
1086     return VLC_EGENERIC;
1087 }
1088
1089 #ifdef HAVE_ZLIB_H
1090 static int Decompress( unsigned char *psz_src, unsigned char **_dst, int i_len )
1091 {
1092     int i_result, i_dstsize, n;
1093     unsigned char *psz_dst;
1094     z_stream d_stream;
1095
1096     d_stream.zalloc = (alloc_func)0;
1097     d_stream.zfree = (free_func)0;
1098     d_stream.opaque = (voidpf)0;
1099
1100     i_result = inflateInit(&d_stream);
1101     if( i_result != Z_OK )
1102     {
1103         printf( "inflateInit() failed. Result: %d\n", i_result );
1104         return( -1 );
1105     }
1106 #if 0
1107     p_playlist->pp_items[p_playlist->i_index]->b_autodeletion = VLC_TRUE;
1108     i_position = p_playlist->i_index;
1109     
1110     /* Gather the complete sdp file */
1111     for( ;; )
1112     {
1113         int i_read = stream_Read( p_demux->s, &p_sdp[i_sdp], i_sdp_max - i_sdp - 1 );
1114 #endif
1115     d_stream.next_in = (Bytef *)psz_src;
1116     d_stream.avail_in = i_len;
1117     n = 0;
1118
1119     psz_dst = NULL;
1120
1121     do
1122     {
1123         n++;
1124         psz_dst = (unsigned char *)realloc( psz_dst, n * 1000 );
1125         d_stream.next_out = (Bytef *)&psz_dst[(n - 1) * 1000];
1126         d_stream.avail_out = 1000;
1127
1128         i_result = inflate(&d_stream, Z_NO_FLUSH);
1129         if( ( i_result != Z_OK ) && ( i_result != Z_STREAM_END ) )
1130         {
1131             printf( "Zlib decompression failed. Result: %d\n", i_result );
1132             return( -1 );
1133         }
1134     }
1135     while( ( d_stream.avail_out == 0 ) && ( d_stream.avail_in != 0 ) &&
1136            ( i_result != Z_STREAM_END ) );
1137
1138     i_dstsize = d_stream.total_out;
1139     inflateEnd( &d_stream );
1140
1141     *_dst = (unsigned char *)realloc( psz_dst, i_dstsize );
1142
1143     return i_dstsize;
1144 }
1145 #endif
1146
1147
1148 static void FreeSDP( sdp_t *p_sdp )
1149 {
1150     int i;
1151     FREE( p_sdp->psz_sdp );
1152     FREE( p_sdp->psz_sessionname );
1153     FREE( p_sdp->psz_connection );
1154     FREE( p_sdp->psz_media );
1155     FREE( p_sdp->psz_uri );
1156     for( i= 0 ; i< p_sdp->i_attributes; i++ )
1157     {
1158         FREE( p_sdp->pp_attributes[i] );
1159     }
1160     free( p_sdp );
1161 }
1162
1163 static int RemoveAnnounce( intf_thread_t *p_intf, sap_announce_t *p_announce )
1164 {
1165     msg_Err( p_intf, "remove not implemented");
1166     return VLC_SUCCESS;
1167 }
1168
1169
1170 static void CacheLoad( intf_thread_t *p_intf )
1171 {
1172     msg_Warn( p_intf, "Cache not implemented") ;
1173 }
1174 static void CacheSave( intf_thread_t *p_intf )
1175 {
1176     msg_Warn( p_intf, "Cache not implemented") ;
1177 }