]> git.sesse.net Git - vlc/blob - modules/services_discovery/sap.c
* Playlist :
[vlc] / modules / services_discovery / sap.c
1 /*****************************************************************************
2  * sap.c :  SAP interface module
3  *****************************************************************************
4  * Copyright (C) 2004-2005 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 #include "charset.h"
36
37 #include <errno.h>                                                 /* ENOMEM */
38
39 #ifdef HAVE_UNISTD_H
40 #    include <unistd.h>
41 #endif
42 #ifdef HAVE_SYS_TIME_H
43 #    include <sys/time.h>
44 #endif
45
46 #ifdef HAVE_ZLIB_H
47 #   include <zlib.h>
48 #endif
49
50 /************************************************************************
51  * Macros and definitions
52  ************************************************************************/
53
54 #define MAX_LINE_LENGTH 256
55
56 /* SAP is always on that port */
57 #define SAP_PORT 9875
58 #define SAP_V4_ADDRESS "224.2.127.254"
59 #define ADD_SESSION 1
60
61 #define IPV6_ADDR_1 "FF0"  /* Scope is inserted between them */
62 #define IPV6_ADDR_2 "::2:7FFE"
63
64
65 /*****************************************************************************
66  * Module descriptor
67  *****************************************************************************/
68 #define SAP_ADDR_TEXT N_( "SAP multicast address" )
69 #define SAP_ADDR_LONGTEXT N_( "Listen for SAP announces on another address" )
70 #define SAP_IPV4_TEXT N_( "IPv4-SAP listening" )
71 #define SAP_IPV4_LONGTEXT N_( \
72       "Set this if you want the SAP module to listen to IPv4 announces " \
73       "on the standard address" )
74 #define SAP_IPV6_TEXT N_( "IPv6-SAP listening" )
75 #define SAP_IPV6_LONGTEXT N_( \
76       "Set this if you want the SAP module to listen to IPv6 announces " \
77       "on the standard address" )
78 #define SAP_SCOPE_TEXT N_( "IPv6 SAP scope" )
79 #define SAP_SCOPE_LONGTEXT N_( \
80        "Sets the scope for IPv6 announces (default is 8)" )
81 #define SAP_TIMEOUT_TEXT N_( "SAP timeout (seconds)" )
82 #define SAP_TIMEOUT_LONGTEXT N_( \
83        "Sets the time before SAP items get deleted if no new announce " \
84        "is received." )
85 #define SAP_PARSE_TEXT N_( "Try to parse the SAP" )
86 #define SAP_PARSE_LONGTEXT N_( \
87        "When SAP can it will try to parse the SAP. If you don't select " \
88        "this, all announces will be parsed by the livedotcom module" )
89 #define SAP_STRICT_TEXT N_( "SAP Strict mode" )
90 #define SAP_STRICT_LONGTEXT N_( \
91        "When this is set, the SAP parser will discard some non-compliant " \
92        "announces" )
93 #define SAP_CACHE_TEXT N_("Use SAP cache")
94 #define SAP_CACHE_LONGTEXT N_( \
95        "If this option is selected, a SAP caching mechanism will be used." \
96        "This will result in lower SAP startup time, but you could end up " \
97         "with items corresponding to legacy streams." )
98
99 /* Callbacks */
100     static int  Open ( vlc_object_t * );
101     static void Close( vlc_object_t * );
102     static int  OpenDemux ( vlc_object_t * );
103     static void CloseDemux ( vlc_object_t * );
104
105 vlc_module_begin();
106     set_shortname( _("SAP"));
107     set_description( _("SAP announces") );
108     set_category( CAT_PLAYLIST );
109     set_subcategory( SUBCAT_PLAYLIST_SD );
110
111     add_string( "sap-addr", NULL, NULL,
112                 SAP_ADDR_TEXT, SAP_ADDR_LONGTEXT, VLC_TRUE );
113     add_bool( "sap-ipv4", 1 , NULL,
114                SAP_IPV4_TEXT,SAP_IPV4_LONGTEXT, VLC_TRUE );
115     add_bool( "sap-ipv6", 0 , NULL,
116               SAP_IPV6_TEXT, SAP_IPV6_LONGTEXT, VLC_TRUE );
117     add_string( "sap-ipv6-scope", "8" , NULL,
118                 SAP_SCOPE_TEXT, SAP_SCOPE_LONGTEXT, VLC_TRUE);
119     add_integer( "sap-timeout", 1800, NULL,
120                  SAP_TIMEOUT_TEXT, SAP_TIMEOUT_LONGTEXT, VLC_TRUE );
121     add_bool( "sap-parse", 1 , NULL,
122                SAP_PARSE_TEXT,SAP_PARSE_LONGTEXT, VLC_TRUE );
123     add_bool( "sap-strict", 0 , NULL,
124                SAP_STRICT_TEXT,SAP_STRICT_LONGTEXT, VLC_TRUE );
125     add_bool( "sap-cache", 0 , NULL,
126                SAP_CACHE_TEXT,SAP_CACHE_LONGTEXT, VLC_TRUE );
127
128     set_capability( "services_discovery", 0 );
129     set_callbacks( Open, Close );
130
131     add_submodule();
132         set_description( _("SDP file parser for UDP") );
133         add_shortcut( "sdp" );
134         set_capability( "demux2", 51 );
135         set_callbacks( OpenDemux, CloseDemux );
136 vlc_module_end();
137
138
139 /*****************************************************************************
140  * Local structures
141  *****************************************************************************/
142
143 typedef struct sdp_t sdp_t;
144 typedef struct attribute_t attribute_t;
145 typedef struct sap_announce_t sap_announce_t;
146
147 /* The structure that contains sdp information */
148 struct  sdp_t
149 {
150     char *psz_sdp;
151
152     /* s= field */
153     char *psz_sessionname;
154
155     /* Raw m= and c= fields */
156     char *psz_connection;
157     char *psz_media;
158
159     /* o field */
160     char *psz_username;
161     char *psz_network_type;
162     char *psz_address_type;
163     char *psz_address;
164     int64_t i_session_id;
165
166     /* "computed" URI */
167     char *psz_uri;
168
169     int         i_in; /* IP version */
170
171     int           i_media;
172     int           i_media_type;
173
174     int           i_attributes;
175     attribute_t  **pp_attributes;
176 };
177
178 struct attribute_t
179 {
180     char *psz_field;
181     char *psz_value;
182 };
183
184 struct sap_announce_t
185 {
186     mtime_t i_last;
187
188     uint16_t    i_hash;
189     uint32_t    i_source[4];
190
191     /* SAP annnounces must only contain one SDP */
192     sdp_t       *p_sdp;
193
194     int i_item_id;
195 //    playlist_item_t *p_item;
196 };
197
198 struct services_discovery_sys_t
199 {
200     /* Socket descriptors */
201     int i_fd;
202     int *pi_fd;
203
204     /* playlist node */
205     playlist_item_t *p_node;
206
207     /* charset conversion */
208     vlc_iconv_t iconvHandle;
209
210     /* Table of announces */
211     int i_announces;
212     struct sap_announce_t **pp_announces;
213
214     /* Modes */
215     vlc_bool_t  b_strict;
216     vlc_bool_t  b_parse;
217
218     int i_timeout;
219 };
220
221 struct demux_sys_t
222 {
223     sdp_t *p_sdp;
224 };
225
226 /*****************************************************************************
227  * Local prototypes
228  *****************************************************************************/
229
230
231 /* Main functions */
232     static int Demux( demux_t *p_demux );
233     static int Control( demux_t *, int, va_list );
234     static void Run    ( services_discovery_t *p_sd );
235
236 /* Main parsing functions */
237     static int ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp );
238     static int ParseSAP( services_discovery_t *p_sd, uint8_t *p_buffer, int i_read );
239     static sdp_t *  ParseSDP( vlc_object_t *p_sd, char* psz_sdp );
240     static sap_announce_t *CreateAnnounce( services_discovery_t *, uint16_t, sdp_t * );
241     static int RemoveAnnounce( services_discovery_t *p_sd, sap_announce_t *p_announce );
242
243 /* Cache */
244     static void CacheLoad( services_discovery_t *p_sd );
245     static void CacheSave( services_discovery_t *p_sd );
246 /* Helper functions */
247     static char *GetAttribute( sdp_t *p_sdp, const char *psz_search );
248     static vlc_bool_t IsSameSession( sdp_t *p_sdp1, sdp_t *p_sdp2 );
249     static char *convert_from_utf8( struct services_discovery_t *p_sd,
250                                    char *psz_unicode );
251     static int InitSocket( services_discovery_t *p_sd, char *psz_address, int i_port );
252 #ifdef HAVE_ZLIB_H
253     static int Decompress( unsigned char *psz_src, unsigned char **_dst, int i_len );
254     static void FreeSDP( sdp_t *p_sdp );
255 #endif
256
257 /* Detect multicast addresses */
258 static int  ismult( char * );
259
260 #define FREE( p ) \
261     if( p ) { free( p ); (p) = NULL; }
262 /*****************************************************************************
263  * Open: initialize and create stuff
264  *****************************************************************************/
265 static int Open( vlc_object_t *p_this )
266 {
267     services_discovery_t *p_sd = ( services_discovery_t* )p_this;
268     services_discovery_sys_t *p_sys  = (services_discovery_sys_t *)
269                                 malloc( sizeof( services_discovery_sys_t ) );
270
271     playlist_t          *p_playlist;
272     playlist_view_t     *p_view;
273     char                *psz_addr, *psz_charset;
274     vlc_value_t         val;
275
276     p_sys->i_timeout = var_CreateGetInteger( p_sd, "sap-timeout" );
277
278     vlc_current_charset( &psz_charset );
279     p_sys->iconvHandle = vlc_iconv_open( psz_charset, "UTF-8" );
280     free( psz_charset );
281     if( p_sys->iconvHandle == (vlc_iconv_t)(-1) )
282     {
283         msg_Warn( p_sd, "Unable to do requested conversion" );
284     }
285
286     p_sd->pf_run = Run;
287     p_sd->p_sys  = p_sys;
288
289     p_sys->pi_fd = NULL;
290     p_sys->i_fd = 0;
291
292     p_sys->b_strict = var_CreateGetInteger( p_sd, "sap-strict");
293     p_sys->b_parse = var_CreateGetInteger( p_sd, "sap-parse" );
294
295     if( var_CreateGetInteger( p_sd, "sap-cache" ) )
296     {
297         CacheLoad( p_sd );
298     }
299
300     if( var_CreateGetInteger( p_sd, "sap-ipv4" ) )
301     {
302         InitSocket( p_sd, SAP_V4_ADDRESS, SAP_PORT );
303     }
304     if( var_CreateGetInteger( p_sd, "sap-ipv6" ) )
305     {
306         /* [ + 8x4+7*':' + ] */
307         char psz_address[42];
308         char c_scope;
309         char *psz_scope = var_CreateGetString( p_sd, "sap-ipv6-scope" );
310
311         if( psz_scope == NULL || *psz_scope == '\0')
312         {
313             c_scope = '8';
314         }
315         else
316         {
317             c_scope = psz_scope[0];
318         }
319         snprintf( psz_address, 42, "[%s%c%s]", IPV6_ADDR_1, c_scope,
320                                                IPV6_ADDR_2 );
321         InitSocket( p_sd, psz_address, SAP_PORT );
322     }
323
324     psz_addr = var_CreateGetString( p_sd, "sap-addr" );
325     if( psz_addr && *psz_addr )
326     {
327         InitSocket( p_sd, psz_addr, SAP_PORT );
328     }
329
330     if( p_sys->i_fd == 0 )
331     {
332         msg_Err( p_sd, "unable to read on any address" );
333         return VLC_EGENERIC;
334     }
335
336     /* Create our playlist node */
337     p_playlist = (playlist_t *)vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
338                                                 FIND_ANYWHERE );
339     if( !p_playlist )
340     {
341         msg_Warn( p_sd, "unable to find playlist, cancelling SAP listening");
342         return VLC_EGENERIC;
343     }
344
345     p_view = playlist_ViewFind( p_playlist, VIEW_CATEGORY );
346     p_sys->p_node = playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
347                                          _("SAP"), p_view->p_root );
348     p_sys->p_node->i_flags |= PLAYLIST_RO_FLAG;
349     val.b_bool = VLC_TRUE;
350     var_Set( p_playlist, "intf-change", val );
351
352     vlc_object_release( p_playlist );
353
354     p_sys->i_announces = 0;
355     p_sys->pp_announces = NULL;
356
357     return VLC_SUCCESS;
358 }
359
360 /*****************************************************************************
361  * OpenDemux: initialize and create stuff
362  *****************************************************************************/
363 static int OpenDemux( vlc_object_t *p_this )
364 {
365     demux_t *p_demux = (demux_t *)p_this;
366     uint8_t *p_peek;
367     int i_max_sdp = 1024;
368     int i_sdp = 0;
369     char *psz_sdp = NULL;
370     sdp_t *p_sdp = NULL;
371
372     /* Probe for SDP */
373     if( p_demux->s )
374     {
375         if( stream_Peek( p_demux->s, &p_peek, 7 ) < 7 ) return VLC_EGENERIC;
376
377         if( strncmp( (char*)p_peek, "v=0\r\n", 5 ) &&
378             strncmp( (char*)p_peek, "v=0\n", 4 ) &&
379             ( p_peek[0] < 'a' || p_peek[0] > 'z' || p_peek[1] != '=' ) )
380         {
381             return VLC_EGENERIC;
382         }
383     }
384
385     psz_sdp = (char *)malloc( i_max_sdp );
386     if( !psz_sdp ) return VLC_EGENERIC;
387
388     /* Gather the complete sdp file */
389     for( ;; )
390     {
391         int i_read = stream_Read( p_demux->s,
392                                   &psz_sdp[i_sdp], i_max_sdp - i_sdp - 1 );
393
394         if( i_read < 0 )
395         {
396             msg_Err( p_demux, "failed to read SDP" );
397             goto error;
398         }
399
400         i_sdp += i_read;
401
402         if( i_read < i_max_sdp - i_sdp - 1 )
403         {
404             psz_sdp[i_sdp] = '\0';
405             break;
406         }
407
408         i_max_sdp += 1000;
409         psz_sdp = (uint8_t*)realloc( psz_sdp, i_max_sdp );
410     }
411
412     p_sdp = ParseSDP( VLC_OBJECT(p_demux), psz_sdp );
413
414     if( !p_sdp )
415     {
416         msg_Warn( p_demux, "invalid SDP");
417         goto error;
418     }
419
420     if( p_sdp->i_media > 1 )
421     {
422         goto error;
423     }
424
425     if( ParseConnection( VLC_OBJECT( p_demux ), p_sdp ) )
426     {
427         p_sdp->psz_uri = NULL;
428     }
429     if( p_sdp->i_media_type != 33 && p_sdp->i_media_type != 32 &&
430         p_sdp->i_media_type != 14 )
431         goto error;
432
433     if( p_sdp->psz_uri == NULL ) goto error;
434
435     p_demux->p_sys = (demux_sys_t *)malloc( sizeof(demux_sys_t) );
436     p_demux->p_sys->p_sdp = p_sdp;
437     p_demux->pf_control = Control;
438     p_demux->pf_demux = Demux;
439
440     free( psz_sdp );
441     return VLC_SUCCESS;
442
443 error:
444     free( psz_sdp );
445     if( p_sdp ) FreeSDP( p_sdp );
446     stream_Seek( p_demux->s, 0 );
447     return VLC_EGENERIC;    
448 }
449
450 /*****************************************************************************
451  * Close:
452  *****************************************************************************/
453 static void Close( vlc_object_t *p_this )
454 {
455     services_discovery_t *p_sd = ( services_discovery_t* )p_this;
456     services_discovery_sys_t    *p_sys  = p_sd->p_sys;
457
458     playlist_t *p_playlist;
459     int i;
460
461     for( i = p_sys->i_fd-1 ; i >= 0 ; i-- )
462     {
463         net_Close( p_sys->pi_fd[i] );
464     }
465     FREE( p_sys->pi_fd );
466
467     if( config_GetInt( p_sd, "sap-cache" ) )
468     {
469         CacheSave( p_sd );
470     }
471
472     for( i = p_sys->i_announces  - 1;  i>= 0; i-- )
473     {
474         RemoveAnnounce( p_sd, p_sys->pp_announces[i] );
475     }
476     FREE( p_sys->pp_announces );
477
478     p_playlist = (playlist_t *) vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
479                                                  FIND_ANYWHERE );
480
481     if( p_playlist )
482     {
483         playlist_NodeDelete( p_playlist, p_sys->p_node, VLC_TRUE , VLC_TRUE );
484         vlc_object_release( p_playlist );
485     }
486
487     if( p_sys->iconvHandle != (vlc_iconv_t)(-1) )
488         vlc_iconv_close( p_sys->iconvHandle );
489
490     free( p_sys );
491 }
492
493 /*****************************************************************************
494  * CloseDemux: Close the demuxer
495  *****************************************************************************/
496 static void CloseDemux( vlc_object_t *p_this )
497 {
498
499 }
500
501 /*****************************************************************************
502  * Run: main SAP thread
503  *****************************************************************************
504  * Listens to SAP packets, and sends them to packet_handle
505  *****************************************************************************/
506 #define MAX_SAP_BUFFER 5000
507
508 static void Run( services_discovery_t *p_sd )
509 {
510     int         i;
511     playlist_t  *p_playlist;
512
513     /* read SAP packets */
514     while( !p_sd->b_die )
515     {
516         int i_read;
517         uint8_t p_buffer[MAX_SAP_BUFFER];
518
519         i_read = net_Select( p_sd, p_sd->p_sys->pi_fd, NULL,
520                              p_sd->p_sys->i_fd, p_buffer,
521                              MAX_SAP_BUFFER, 500000 );
522
523         /* Check for items that need deletion */
524         for( i = 0; i < p_sd->p_sys->i_announces; i++ )
525         {
526             mtime_t i_timeout = ( mtime_t ) 1000000 * p_sd->p_sys->i_timeout;
527
528             if( mdate() - p_sd->p_sys->pp_announces[i]->i_last > i_timeout )
529             {
530                 struct sap_announce_t *p_announce;
531                 playlist_item_t * p_item;
532                 p_announce = p_sd->p_sys->pp_announces[i];
533
534                 /* Remove the playlist item */
535                 p_playlist = vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
536                               FIND_ANYWHERE );
537                 if( p_playlist )
538                 {
539                     p_item = playlist_ItemGetById( p_playlist,
540                                                    p_announce->i_item_id );
541                     if( !p_item ) continue;
542
543                     msg_Dbg( p_sd, "Time out for %s, deleting (%i/%i)",
544                                    p_item->input.psz_name,
545                                    i , p_sd->p_sys->i_announces );
546
547                     playlist_Delete( p_playlist, p_announce->i_item_id );
548                     vlc_object_release( p_playlist );
549                 }
550
551                 /* Remove the sap_announce from the array */
552                 REMOVE_ELEM( p_sd->p_sys->pp_announces,
553                            p_sd->p_sys->i_announces, i );
554
555                 free( p_announce );
556             }
557         }
558
559         /* Minimum length is > 6 */
560         if( i_read <= 6 )
561         {
562             if( i_read < 0 )
563             {
564                 msg_Warn( p_sd, "socket read error" );
565             }
566             continue;
567         }
568
569         p_buffer[i_read] = '\0';
570
571         /* Parse the packet */
572         ParseSAP( p_sd, p_buffer, i_read );
573     }
574 }
575
576 /**********************************************************************
577  * Demux: reads and demuxes data packets
578  * Return -1 if error, 0 if EOF, 1 else
579  **********************************************************************/
580 static int Demux( demux_t *p_demux )
581 {
582     sdp_t *p_sdp = p_demux->p_sys->p_sdp;
583     playlist_t *p_playlist;
584
585     p_playlist = (playlist_t *)vlc_object_find( p_demux, VLC_OBJECT_PLAYLIST,
586                                                FIND_ANYWHERE );
587
588     p_playlist->status.p_item->i_flags |= PLAYLIST_DEL_FLAG;
589
590     playlist_Add( p_playlist, p_sdp->psz_uri, p_sdp->psz_sessionname,
591                  PLAYLIST_APPEND, PLAYLIST_END );
592
593     vlc_object_release( p_playlist );
594     if( p_sdp ) FreeSDP( p_sdp );
595
596     return VLC_SUCCESS;
597 }
598
599 static int Control( demux_t *p_demux, int i_query, va_list args )
600 {
601     return VLC_EGENERIC;
602 }
603
604 /**************************************************************
605  * Local functions
606  **************************************************************/
607
608 /* i_read is at least > 6 */
609 static int ParseSAP( services_discovery_t *p_sd, uint8_t *p_buffer, int i_read )
610 {
611     int                 i_version, i_address_type, i_hash, i;
612     uint8_t             *psz_sdp;
613     uint8_t             *psz_initial_sdp;
614     sdp_t               *p_sdp;
615     vlc_bool_t          b_compressed;
616     vlc_bool_t          b_need_delete = VLC_FALSE;
617 #ifdef HAVE_ZLIB_H
618     int                 i_decompressed_size;
619     uint8_t             *p_decompressed_buffer;
620 #endif
621     uint8_t             *psz_foo;
622
623     /* First, check the sap announce is correct */
624     i_version = p_buffer[0] >> 5;
625     if( i_version != 1 )
626     {
627        msg_Dbg( p_sd, "strange sap version %d found", i_version );
628     }
629
630     i_address_type = p_buffer[0] & 0x10;
631
632     if( (p_buffer[0] & 0x08) != 0 )
633     {
634         msg_Dbg( p_sd, "reserved bit incorrectly set" );
635         return VLC_EGENERIC;
636     }
637
638     if( (p_buffer[0] & 0x04) != 0 )
639     {
640         msg_Dbg( p_sd, "session deletion packet" );
641         b_need_delete = VLC_TRUE;
642     }
643
644     if( p_buffer[0] & 0x02  )
645     {
646         msg_Dbg( p_sd, "encrypted packet, unsupported" );
647         return VLC_EGENERIC;
648     }
649
650     b_compressed = p_buffer[0] & 0x01;
651
652     i_hash = ( p_buffer[2] << 8 ) + p_buffer[3];
653
654     if( p_sd->p_sys->b_strict && i_hash == 0 )
655     {
656         msg_Dbg( p_sd, "strict mode, discarding announce with null id hash");
657         return VLC_EGENERIC;
658     }
659
660     psz_sdp  = &p_buffer[4];
661     psz_initial_sdp = psz_sdp;
662
663     if( i_address_type == 0 ) /* ipv4 source address */
664     {
665         psz_sdp += 4;
666         if( i_read <= 9 )
667         {
668             msg_Warn( p_sd, "too short SAP packet\n" );
669             return VLC_EGENERIC;
670         }
671     }
672     else /* ipv6 source address */
673     {
674         psz_sdp += 16;
675         if( i_read <= 21 )
676         {
677             msg_Warn( p_sd, "too short SAP packet\n" );
678             return VLC_EGENERIC;
679         }
680     }
681
682     if( b_compressed )
683     {
684 #ifdef HAVE_ZLIB_H
685         i_decompressed_size = Decompress( psz_sdp,
686                    &p_decompressed_buffer,i_read - ( psz_sdp - p_buffer ) );
687         if( i_decompressed_size > 0 && i_decompressed_size < MAX_SAP_BUFFER )
688         {
689             memcpy( psz_sdp, p_decompressed_buffer, i_decompressed_size );
690             psz_sdp[i_decompressed_size] = '\0';
691             free( p_decompressed_buffer );
692         }
693 #else
694         msg_Warn( p_sd, "Ignoring compressed sap packet" );
695         return VLC_EGENERIC;
696 #endif
697     }
698
699     /* Add the size of authentification info */
700     if( i_read < p_buffer[1] + (psz_sdp - psz_initial_sdp ) )
701     {
702         msg_Warn( p_sd, "too short SAP packet\n");
703         return VLC_EGENERIC;
704     }
705     psz_sdp += p_buffer[1];
706     psz_foo = psz_sdp;
707
708     /* Skip payload type */
709     /* Handle announces without \0 between SAP and SDP */
710     while( *psz_sdp != '\0' && ( psz_sdp[0] != 'v' && psz_sdp[1] != '=' ) )
711     {
712         if( psz_sdp - psz_initial_sdp >= i_read - 5 )
713         {
714             msg_Warn( p_sd, "empty SDP ?");
715         }
716         psz_sdp++;
717     }
718
719     if( *psz_sdp == '\0' )
720     {
721         psz_sdp++;
722     }
723     if( psz_sdp != psz_foo && strcasecmp( psz_foo, "application/sdp" ) )
724     {
725         msg_Dbg( p_sd, "unhandled content type: %s", psz_foo );        
726     }
727     if( psz_sdp -p_buffer >= i_read )
728     {
729         msg_Warn( p_sd, "package without content" );
730         return VLC_EGENERIC;
731     }
732
733     /* Parse SDP info */
734     p_sdp = ParseSDP( VLC_OBJECT(p_sd), psz_sdp );
735
736     if( p_sdp == NULL )
737     {
738         return VLC_EGENERIC;
739     }
740
741     /* Decide whether we should add a playlist item for this SDP */
742     /* Parse connection information (c= & m= ) */
743     if( ParseConnection( VLC_OBJECT(p_sd), p_sdp ) )
744     {
745         p_sdp->psz_uri = NULL;
746     }
747
748     /* Multi-media or no-parse -> pass to LIVE.COM */
749     if( p_sdp->i_media > 1 || ( p_sdp->i_media_type != 14 &&
750                                 p_sdp->i_media_type != 32 &&
751                                 p_sdp->i_media_type != 33) ||
752         p_sd->p_sys->b_parse == VLC_FALSE )
753     {
754         if( p_sdp->psz_uri ) free( p_sdp->psz_uri );
755         asprintf( &p_sdp->psz_uri, "sdp://%s", p_sdp->psz_sdp );
756     }
757
758     if( p_sdp->psz_uri == NULL ) return VLC_EGENERIC;
759
760     for( i = 0 ; i< p_sd->p_sys->i_announces ; i++ )
761     {
762         /* FIXME: slow */
763         /* FIXME: we create a new announce each time the sdp changes */
764         if( IsSameSession( p_sd->p_sys->pp_announces[i]->p_sdp, p_sdp ) )
765         {
766             if( b_need_delete )
767             {
768                 RemoveAnnounce( p_sd, p_sd->p_sys->pp_announces[i]);
769                 return VLC_SUCCESS;
770             }
771             else
772             {
773                 p_sd->p_sys->pp_announces[i]->i_last = mdate();
774                 FreeSDP( p_sdp );
775                 return VLC_SUCCESS;
776             }
777         }
778     }
779     /* Add item */
780     if( p_sdp->i_media > 1 )
781     {
782         msg_Dbg( p_sd, "passing to LIVE.COM" );
783     }
784
785     CreateAnnounce( p_sd, i_hash, p_sdp );
786
787     return VLC_SUCCESS;
788 }
789
790 sap_announce_t *CreateAnnounce( services_discovery_t *p_sd, uint16_t i_hash,
791                                 sdp_t *p_sdp )
792 {
793     playlist_t          *p_playlist;
794     playlist_item_t     *p_item, *p_child;
795     char                *psz_value;
796     sap_announce_t *p_sap = (sap_announce_t *)malloc(
797                                         sizeof(sap_announce_t ) );
798
799     psz_value = convert_from_utf8( p_sd, p_sdp->psz_sessionname );
800     if( p_sap == NULL || psz_value == NULL )
801     {
802         msg_Err( p_sd, "out of memory");
803         FREE( p_sap );
804         FREE( psz_value );
805         p_sd->b_die = VLC_TRUE;
806         return NULL;
807     }
808     p_sap->i_last = mdate();
809     p_sap->i_hash = i_hash;
810     p_sap->p_sdp = p_sdp;
811     p_sap->i_item_id = -1;
812
813     /* Create the playlist item here */
814     p_item = playlist_ItemNew( p_sd, p_sap->p_sdp->psz_uri, psz_value );
815     free( psz_value );
816
817     if( !p_item )
818     {
819         free( p_sap );
820         return NULL;
821     }
822
823     psz_value = GetAttribute( p_sap->p_sdp, "tool" );
824     if( psz_value != NULL )
825     {
826         vlc_input_item_AddInfo( &p_item->input, _("Session"),
827                                 _("Tool"), psz_value );
828     }
829     if( strcmp( p_sdp->psz_username, "-" ) )
830     {
831         vlc_input_item_AddInfo( &p_item->input, _("Session"),
832                                 _("User"), p_sdp->psz_username );
833     }
834
835     psz_value = GetAttribute( p_sap->p_sdp, "x-plgroup" );
836
837     if( psz_value == NULL )
838     {
839         psz_value = GetAttribute( p_sap->p_sdp, "plgroup" );
840     }
841
842     p_playlist = (playlist_t *)vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
843                                                 FIND_ANYWHERE );
844     if( !p_playlist )
845     {
846         msg_Err( p_sd, "playlist not found" );
847         free( p_sap );
848         return NULL;
849     }
850
851     if( psz_value != NULL )
852     {
853         char *psz_grp = convert_from_utf8( p_sd, psz_value );
854
855         if( psz_grp != NULL )
856         {
857             p_child = playlist_ChildSearchName( p_sd->p_sys->p_node,
858                                                 psz_grp );
859
860             if( p_child == NULL )
861                 p_child = playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
862                                                psz_grp, p_sd->p_sys->p_node );
863             free( psz_grp );
864         }
865         else
866         {
867             vlc_object_release( p_playlist );
868             msg_Err( p_sd, "out of memory");
869             free( p_sap );
870             return NULL;
871         }
872     }
873     else
874     {
875         p_child = p_sd->p_sys->p_node;
876     }
877
878     p_item->i_flags &= ~PLAYLIST_SKIP_FLAG;
879     p_item->i_flags &= ~PLAYLIST_SAVE_FLAG;
880
881     playlist_NodeAddItem( p_playlist, p_item, VIEW_CATEGORY, p_child,
882                           PLAYLIST_APPEND, PLAYLIST_END );
883
884     vlc_object_release( p_playlist );
885
886     p_sap->i_item_id = p_item->input.i_id;
887
888     TAB_APPEND( p_sd->p_sys->i_announces,
889                 p_sd->p_sys->pp_announces, p_sap );
890
891     return p_sap;
892 }
893
894 static char *GetAttribute( sdp_t *p_sdp, const char *psz_search )
895 {
896     int i;
897
898     for( i = 0 ; i< p_sdp->i_attributes; i++ )
899     {
900         if( !strncmp( p_sdp->pp_attributes[i]->psz_field, psz_search,
901                       strlen( p_sdp->pp_attributes[i]->psz_field ) ) )
902         {
903             return p_sdp->pp_attributes[i]->psz_value;
904         }
905     }
906     return NULL;
907 }
908
909
910 /* Fill p_sdp->psz_uri */
911 static int ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp )
912 {
913     char *psz_eof;
914     char *psz_parse;
915     char *psz_uri = NULL;
916     char *psz_proto = NULL;
917     int i_port = 0;
918
919     /* Parse c= field */
920     if( p_sdp->psz_connection )
921     {
922         psz_parse = p_sdp->psz_connection;
923
924         psz_eof = strchr( psz_parse, ' ' );
925
926         if( psz_eof )
927         {
928             *psz_eof = '\0';
929             psz_parse = psz_eof + 1;
930         }
931         else
932         {
933             msg_Warn( p_obj, "unable to parse c field (1)");
934             return VLC_EGENERIC;
935         }
936
937         psz_eof = strchr( psz_parse, ' ' );
938
939         if( psz_eof )
940         {
941             *psz_eof = '\0';
942             if( !strncmp( psz_parse, "IP4", 3 ) )
943             {
944                 p_sdp->i_in = 4;
945             }
946             else if( !strncmp( psz_parse, "IP6", 3 ) )
947             {
948                 p_sdp->i_in = 6;
949             }
950             else
951             {
952                 p_sdp->i_in = 0;
953             }
954             psz_parse = psz_eof + 1;
955         }
956         else
957         {
958             msg_Warn( p_obj, "unable to parse c field (2)");
959             return VLC_EGENERIC;
960         }
961
962         psz_eof = strchr( psz_parse, '/' );
963
964         if( psz_eof )
965         {
966             *psz_eof = 0;
967         }
968         else
969         {
970             msg_Dbg( p_obj, "incorrect c field, %s", p_sdp->psz_connection );
971         }
972         psz_uri = strdup( psz_parse );
973
974     }
975
976     /* Parse m= field */
977     if( p_sdp->psz_media )
978     {
979         psz_parse = p_sdp->psz_media;
980
981         psz_eof = strchr( psz_parse, ' ' );
982
983         if( psz_eof )
984         {
985             *psz_eof = '\0';
986
987             if( strncmp( psz_parse, "audio", 5 )  &&
988                 strncmp( psz_parse, "video",5 ) )
989             {
990                 msg_Warn( p_obj, "unhandled media type -%s-", psz_parse );
991                 FREE( psz_uri );
992                 return VLC_EGENERIC;
993             }
994
995             psz_parse = psz_eof + 1;
996         }
997         else
998         {
999             msg_Warn( p_obj, "unable to parse m field (1)");
1000             FREE( psz_uri );
1001             return VLC_EGENERIC;
1002         }
1003
1004         psz_eof = strchr( psz_parse, ' ' );
1005
1006         if( psz_eof )
1007         {
1008             *psz_eof = '\0';
1009
1010             /* FIXME : multiple port ! */
1011             i_port = atoi( psz_parse );
1012
1013             if( i_port <= 0 || i_port >= 65536 )
1014             {
1015                 msg_Warn( p_obj, "invalid transport port %i", i_port );
1016             }
1017
1018             psz_parse = psz_eof + 1;
1019         }
1020         else
1021         {
1022             msg_Warn( p_obj, "unable to parse m field (2)");
1023             FREE( psz_uri );
1024             return VLC_EGENERIC;
1025         }
1026
1027         psz_eof = strchr( psz_parse, ' ' );
1028
1029         if( psz_eof )
1030         {
1031             *psz_eof = '\0';
1032             psz_proto = strdup( psz_parse );
1033
1034             psz_parse = psz_eof + 1;
1035             p_sdp->i_media_type = atoi( psz_parse );
1036             
1037         }
1038         else
1039         {
1040             msg_Dbg( p_obj, "incorrect m field, %s", p_sdp->psz_media );
1041             p_sdp->i_media_type = 33;
1042             psz_proto = strdup( psz_parse );
1043         }
1044     }
1045
1046     if( psz_proto && !strncmp( psz_proto, "RTP/AVP", 7 ) )
1047     {
1048         free( psz_proto );
1049         psz_proto = strdup( "rtp" );
1050     }
1051     if( psz_proto && !strncasecmp( psz_proto, "UDP", 3 ) )
1052     {
1053         free( psz_proto );
1054         psz_proto = strdup( "udp" );
1055     }
1056
1057     /* FIXME: HTTP support */
1058
1059     if( i_port == 0 )
1060     {
1061         i_port = 1234;
1062     }
1063
1064     if( ismult( psz_uri ) )
1065     {
1066         asprintf( &p_sdp->psz_uri, "%s://@%s:%i", psz_proto, psz_uri, i_port );
1067     }
1068     else
1069     {
1070         asprintf( &p_sdp->psz_uri, "%s://%s:%i", psz_proto, psz_uri, i_port );
1071     }
1072     FREE( psz_uri );
1073     FREE( psz_proto );
1074     return VLC_SUCCESS;
1075 }
1076
1077 /***********************************************************************
1078  * ParseSDP : SDP parsing
1079  * *********************************************************************
1080  * Validate SDP and parse all fields
1081  ***********************************************************************/
1082 static sdp_t *  ParseSDP( vlc_object_t *p_obj, char* psz_sdp )
1083 {
1084     sdp_t *p_sdp;
1085     vlc_bool_t b_invalid = VLC_FALSE;
1086     vlc_bool_t b_end = VLC_FALSE;
1087     if( psz_sdp == NULL )
1088     {
1089         return NULL;
1090     }
1091
1092     if( psz_sdp[0] != 'v' || psz_sdp[1] != '=' )
1093     {
1094         msg_Warn( p_obj, "Bad packet" );
1095         return NULL;
1096     }
1097
1098     p_sdp = (sdp_t *)malloc( sizeof( sdp_t ) );
1099
1100     p_sdp->psz_sdp = strdup( psz_sdp );
1101
1102     p_sdp->psz_sessionname = NULL;
1103     p_sdp->psz_media       = NULL;
1104     p_sdp->psz_connection  = NULL;
1105     p_sdp->psz_uri         = NULL;
1106     p_sdp->psz_address     = NULL;
1107     p_sdp->psz_address_type= NULL;
1108
1109     p_sdp->i_media         = 0;
1110     p_sdp->i_attributes    = 0;
1111     p_sdp->pp_attributes   = NULL;
1112
1113     while( *psz_sdp != '\0' && b_end == VLC_FALSE  )
1114     {
1115         char *psz_eol;
1116         char *psz_eof;
1117         char *psz_parse;
1118         char *psz_sess_id;
1119
1120         while( *psz_sdp == '\r' || *psz_sdp == '\n' ||
1121                *psz_sdp == ' ' || *psz_sdp == '\t' )
1122         {
1123             psz_sdp++;
1124         }
1125
1126         if( ( psz_eol = strchr( psz_sdp, '\n' ) ) == NULL )
1127         {
1128             psz_eol = psz_sdp + strlen( psz_sdp );
1129             b_end = VLC_TRUE;
1130         }
1131         if( psz_eol > psz_sdp && *( psz_eol - 1 ) == '\r' )
1132         {
1133             psz_eol--;
1134         }
1135
1136         if( psz_eol <= psz_sdp )
1137         {
1138             break;
1139         }
1140         *psz_eol++ = '\0';
1141
1142         /* no space allowed between fields */
1143         if( psz_sdp[1] != '=' )
1144         {
1145             msg_Warn( p_obj, "invalid packet" ) ;
1146             /* MEMLEAK ! */
1147             return NULL;
1148         }
1149
1150         /* Now parse each line */
1151         switch( psz_sdp[0] )
1152         {
1153             case( 'v' ):
1154                 break;
1155             case( 's' ):
1156                 p_sdp->psz_sessionname = strdup( &psz_sdp[2] );
1157                 break;
1158             case ( 'o' ):
1159             {
1160                 int i_field = 0;
1161                 /* o field is <username> <session id> <version>
1162                  *  <network type> <address type> <address> */
1163
1164 #define GET_FIELD( store ) \
1165                 psz_eof = strchr( psz_parse, ' ' ); \
1166                 if( psz_eof ) \
1167                 { \
1168                     *psz_eof=0; store = strdup( psz_parse ); \
1169                 } \
1170                 else \
1171                 { \
1172                     if( i_field != 5 ) \
1173                     { \
1174                         b_invalid = VLC_TRUE; break; \
1175                     } \
1176                     else \
1177                     { \
1178                         store = strdup( psz_parse ); \
1179                     } \
1180                 }; \
1181                 psz_parse = psz_eof + 1; i_field++;
1182
1183
1184                 psz_parse = &psz_sdp[2];
1185                 GET_FIELD( p_sdp->psz_username );
1186                 GET_FIELD( psz_sess_id );
1187
1188                 p_sdp->i_session_id = atoll( psz_sess_id );
1189
1190                 FREE( psz_sess_id );
1191
1192                 GET_FIELD( psz_sess_id );
1193                 FREE( psz_sess_id );
1194
1195                 GET_FIELD( p_sdp->psz_network_type );
1196                 GET_FIELD( p_sdp->psz_address_type );
1197                 GET_FIELD( p_sdp->psz_address );
1198
1199                 break;
1200             }
1201             case( 'i' ):
1202             case( 'u' ):
1203             case( 'e' ):
1204             case( 'p' ):
1205             case( 't' ):
1206             case( 'r' ):
1207                 break;
1208             case( 'a' ): /* attribute */
1209             {
1210                 char *psz_eon = strchr( &psz_sdp[2], ':' );
1211                 attribute_t *p_attr = malloc( sizeof( attribute_t ) );
1212
1213                 /* Attribute with value */
1214                 if( psz_eon )
1215                 {
1216                     *psz_eon++ = '\0';
1217
1218                     p_attr->psz_field = strdup( &psz_sdp[2] );
1219                     p_attr->psz_value = strdup( psz_eon );
1220                 }
1221                 else /* Attribute without value */
1222                 {
1223                     p_attr->psz_field = strdup( &psz_sdp[2] );
1224                     p_attr->psz_value = NULL;
1225                 }
1226
1227                 TAB_APPEND( p_sdp->i_attributes, p_sdp->pp_attributes, p_attr );
1228                 break;
1229             }
1230
1231             case( 'm' ): /* Media announcement */
1232             {
1233                 /* If we have several medias, we pass the announcement to
1234                  * LIVE.COM, so just count them */
1235                 p_sdp->i_media++;
1236                 if( p_sdp->i_media == 1 )
1237                 {
1238                     p_sdp->psz_media = strdup( &psz_sdp[2] );
1239                 }
1240                 break;
1241             }
1242
1243             case( 'c' ):
1244             {
1245                 if( p_sdp->i_media > 1 )
1246                     break;
1247
1248                 p_sdp->psz_connection = strdup( &psz_sdp[2] );
1249                 break;
1250             }
1251
1252             default:
1253                break;
1254         }
1255
1256         if( b_invalid )
1257         {
1258             FreeSDP( p_sdp );
1259             return NULL;
1260         }
1261
1262         psz_sdp = psz_eol;
1263     }
1264
1265     return p_sdp;
1266 }
1267
1268
1269 static char *convert_from_utf8( struct services_discovery_t *p_sd,
1270                                 char *psz_unicode )
1271 {
1272     char *psz_local, *psz_in, *psz_out;
1273     size_t ret, i_in, i_out;
1274
1275     if( psz_unicode == NULL )
1276         return NULL;
1277     if ( p_sd->p_sys->iconvHandle == (vlc_iconv_t)(-1) )
1278         return strdup( psz_unicode );
1279
1280     psz_in = psz_unicode;
1281     i_in = strlen( psz_unicode );
1282
1283     i_out = 2 * i_in;
1284     psz_local = malloc( i_out + 1 );
1285     if( psz_local == NULL )
1286         return strdup( psz_unicode );
1287     psz_out = psz_local;
1288
1289     ret = vlc_iconv( p_sd->p_sys->iconvHandle,
1290                      &psz_in, &i_in, &psz_out, &i_out);
1291     if( ret == (size_t)(-1) || i_in )
1292     {
1293         msg_Warn( p_sd, "Failed to convert \"%s\" from UTF-8", psz_unicode );
1294         free( psz_local );
1295         return strdup( psz_unicode );
1296     }
1297     *psz_out = '\0';
1298     return psz_local;
1299 }
1300
1301
1302 /***********************************************************************
1303  * ismult: returns true if we have a multicast address
1304  ***********************************************************************/
1305 static int ismult( char *psz_uri )
1306 {
1307     char *psz_end;
1308     int  i_value;
1309
1310     i_value = strtol( psz_uri, &psz_end, 0 );
1311
1312     /* IPv6 */
1313     if( psz_uri[0] == '[')
1314     {
1315       if( strncasecmp( &psz_uri[1], "FF0" , 3) ||
1316           strncasecmp( &psz_uri[2], "FF0" , 3))
1317             return( VLC_TRUE );
1318         else
1319             return( VLC_FALSE );
1320     }
1321
1322     if( *psz_end != '.' ) { return( VLC_FALSE ); }
1323
1324     return( i_value < 224 ? VLC_FALSE : VLC_TRUE );
1325 }
1326
1327 static int InitSocket( services_discovery_t *p_sd, char *psz_address,
1328                        int i_port )
1329 {
1330     int i_fd = net_OpenUDP( p_sd, psz_address, i_port, "", 0 );
1331
1332     if( i_fd != -1 )
1333     {
1334         INSERT_ELEM(  p_sd->p_sys->pi_fd, p_sd->p_sys->i_fd,
1335                       p_sd->p_sys->i_fd, i_fd );
1336         return VLC_SUCCESS;
1337     }
1338
1339     return VLC_EGENERIC;
1340 }
1341
1342 #ifdef HAVE_ZLIB_H
1343 static int Decompress( unsigned char *psz_src, unsigned char **_dst, int i_len )
1344 {
1345     int i_result, i_dstsize, n;
1346     unsigned char *psz_dst;
1347     z_stream d_stream;
1348
1349     d_stream.zalloc = (alloc_func)0;
1350     d_stream.zfree = (free_func)0;
1351     d_stream.opaque = (voidpf)0;
1352
1353     i_result = inflateInit(&d_stream);
1354     if( i_result != Z_OK )
1355     {
1356         printf( "inflateInit() failed. Result: %d\n", i_result );
1357         return( -1 );
1358     }
1359 #if 0
1360     p_playlist->pp_items[p_playlist->i_index]->b_autodeletion = VLC_TRUE;
1361     i_position = p_playlist->i_index;
1362
1363     /* Gather the complete sdp file */
1364     for( ;; )
1365     {
1366         int i_read = stream_Read( p_demux->s, &p_sdp[i_sdp], i_sdp_max - i_sdp - 1 );
1367 #endif
1368     d_stream.next_in = (Bytef *)psz_src;
1369     d_stream.avail_in = i_len;
1370     n = 0;
1371
1372     psz_dst = NULL;
1373
1374     do
1375     {
1376         n++;
1377         psz_dst = (unsigned char *)realloc( psz_dst, n * 1000 );
1378         d_stream.next_out = (Bytef *)&psz_dst[(n - 1) * 1000];
1379         d_stream.avail_out = 1000;
1380
1381         i_result = inflate(&d_stream, Z_NO_FLUSH);
1382         if( ( i_result != Z_OK ) && ( i_result != Z_STREAM_END ) )
1383         {
1384             printf( "Zlib decompression failed. Result: %d\n", i_result );
1385             return( -1 );
1386         }
1387     }
1388     while( ( d_stream.avail_out == 0 ) && ( d_stream.avail_in != 0 ) &&
1389            ( i_result != Z_STREAM_END ) );
1390
1391     i_dstsize = d_stream.total_out;
1392     inflateEnd( &d_stream );
1393
1394     *_dst = (unsigned char *)realloc( psz_dst, i_dstsize );
1395
1396     return i_dstsize;
1397 }
1398 #endif
1399
1400
1401 static void FreeSDP( sdp_t *p_sdp )
1402 {
1403     int i;
1404     FREE( p_sdp->psz_sdp );
1405     FREE( p_sdp->psz_sessionname );
1406     FREE( p_sdp->psz_connection );
1407     FREE( p_sdp->psz_media );
1408     FREE( p_sdp->psz_uri );
1409     FREE( p_sdp->psz_username );
1410     FREE( p_sdp->psz_network_type );
1411
1412     FREE( p_sdp->psz_address );
1413     FREE( p_sdp->psz_address_type );
1414
1415     for( i= p_sdp->i_attributes - 1; i >= 0 ; i-- )
1416     {
1417         struct attribute_t *p_attr = p_sdp->pp_attributes[i];
1418         FREE( p_sdp->pp_attributes[i]->psz_field );
1419         FREE( p_sdp->pp_attributes[i]->psz_value );
1420         REMOVE_ELEM( p_sdp->pp_attributes, p_sdp->i_attributes, i);
1421         FREE( p_attr );
1422     }
1423     free( p_sdp );
1424 }
1425
1426 static int RemoveAnnounce( services_discovery_t *p_sd,
1427                            sap_announce_t *p_announce )
1428 {
1429     int i;
1430     playlist_t *p_playlist = (playlist_t *)vlc_object_find( p_sd,
1431                                           VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
1432
1433     if( p_announce->p_sdp ) FreeSDP( p_announce->p_sdp );
1434
1435     if( !p_playlist )
1436     {
1437         free( p_announce );
1438         return VLC_EGENERIC;
1439     }
1440
1441     if( p_announce->i_item_id > -1 )
1442     {
1443         playlist_LockDelete( p_playlist, p_announce->i_item_id );
1444     }
1445
1446     for( i = 0; i< p_sd->p_sys->i_announces; i++)
1447     {
1448         if( p_sd->p_sys->pp_announces[i] == p_announce )
1449         {
1450             REMOVE_ELEM( p_sd->p_sys->pp_announces, p_sd->p_sys->i_announces,
1451                          i);
1452             break;
1453         }
1454     }
1455
1456     vlc_object_release( p_playlist );
1457
1458     free( p_announce );
1459
1460     return VLC_SUCCESS;
1461 }
1462
1463 static vlc_bool_t IsSameSession( sdp_t *p_sdp1, sdp_t *p_sdp2 )
1464 {
1465     /* A session is identified by
1466      * username, session_id, network type, address type and address */
1467     if( p_sdp1->psz_username && p_sdp2->psz_username &&
1468         p_sdp1->psz_network_type && p_sdp2->psz_network_type &&
1469         p_sdp1->psz_address_type && p_sdp2->psz_address_type &&
1470         p_sdp1->psz_address &&  p_sdp2->psz_address )
1471     {
1472         if(!strcmp( p_sdp1->psz_username , p_sdp2->psz_username ) &&
1473            !strcmp( p_sdp1->psz_network_type , p_sdp2->psz_network_type ) &&
1474            !strcmp( p_sdp1->psz_address_type , p_sdp2->psz_address_type ) &&
1475            !strcmp( p_sdp1->psz_address , p_sdp2->psz_address ) &&
1476            p_sdp1->i_session_id == p_sdp2->i_session_id )
1477         {
1478             return VLC_TRUE;
1479         }
1480         else
1481         {
1482             return VLC_FALSE;
1483         }
1484     }
1485     else
1486     {
1487         return VLC_FALSE;
1488     }
1489 }
1490
1491
1492 static void CacheLoad( services_discovery_t *p_sd )
1493 {
1494     msg_Warn( p_sd, "Cache not implemented") ;
1495 }
1496
1497 static void CacheSave( services_discovery_t *p_sd )
1498 {
1499     msg_Warn( p_sd, "Cache not implemented") ;
1500 }