Main Page | Modules | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

sap.c

00001 /*****************************************************************************
00002  * sap.c :  SAP interface module
00003  *****************************************************************************
00004  * Copyright (C) 2004-2005 the VideoLAN team
00005  * $Id: sap.c 13637 2005-12-09 09:17:54Z md $
00006  *
00007  * Authors: ClĂ©ment Stenac <[email protected]>
00008  *
00009  * This program is free software; you can redistribute it and/or modify
00010  * it under the terms of the GNU General Public License as published by
00011  * the Free Software Foundation; either version 2 of the License, or
00012  * (at your option) any later version.
00013  *
00014  * This program is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  * GNU General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU General Public License
00020  * along with this program; if not, write to the Free Software
00021  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
00022  *****************************************************************************/
00023 
00024 /*****************************************************************************
00025  * Includes
00026  *****************************************************************************/
00027 #include <stdlib.h>                                      /* malloc(), free() */
00028 
00029 #include <vlc/vlc.h>
00030 #include <vlc/input.h>
00031 #include <vlc/intf.h>
00032 
00033 #include <network.h>
00034 #include <charset.h>
00035 
00036 #include <ctype.h>
00037 #include <errno.h>
00038 
00039 #ifdef HAVE_UNISTD_H
00040 #    include <unistd.h>
00041 #endif
00042 #ifdef HAVE_SYS_TIME_H
00043 #    include <sys/time.h>
00044 #endif
00045 
00046 #ifdef HAVE_ZLIB_H
00047 #   include <zlib.h>
00048 #endif
00049 
00050 /************************************************************************
00051  * Macros and definitions
00052  ************************************************************************/
00053 
00054 #define MAX_LINE_LENGTH 256
00055 
00056 /* SAP is always on that port */
00057 #define SAP_PORT 9875
00058 /* Global-scope SAP address */
00059 #define SAP_V4_GLOBAL_ADDRESS   "224.2.127.254"
00060 /* Organization-local SAP address */
00061 #define SAP_V4_ORG_ADDRESS      "239.195.255.255"
00062 /* Local (smallest non-link-local scope) SAP address */
00063 #define SAP_V4_LOCAL_ADDRESS    "239.255.255.255"
00064 /* Link-local SAP address */
00065 #define SAP_V4_LINK_ADDRESS     "224.0.0.255"
00066 #define ADD_SESSION 1
00067 
00068 #define SAP_V6_1 "FF0"
00069 /* Scope is inserted between them */
00070 #define SAP_V6_2 "::2:7FFE"
00071 /* See RFC3513 for list of valid scopes */
00072 /* FIXME: find a way to listen to link-local scope */
00073 static const char ipv6_scopes[] = "1456789ABCDE";
00074 
00075 
00076 /*****************************************************************************
00077  * Module descriptor
00078  *****************************************************************************/
00079 #define SAP_ADDR_TEXT N_( "SAP multicast address" )
00080 #define SAP_ADDR_LONGTEXT N_( "Listen for SAP announcements on another address" )
00081 #define SAP_IPV4_TEXT N_( "IPv4-SAP listening" )
00082 #define SAP_IPV4_LONGTEXT N_( \
00083       "Set this if you want the SAP module to listen to IPv4 announcements " \
00084       "on the standard address." )
00085 #define SAP_IPV6_TEXT N_( "IPv6-SAP listening" )
00086 #define SAP_IPV6_LONGTEXT N_( \
00087       "Set this if you want the SAP module to listen to IPv6 announcements " \
00088       "on the standard address." )
00089 #define SAP_SCOPE_TEXT N_( "IPv6 SAP scope" )
00090 #define SAP_SCOPE_LONGTEXT N_( \
00091        "Sets the scope for IPv6 announcements (default is 8)." )
00092 #define SAP_TIMEOUT_TEXT N_( "SAP timeout (seconds)" )
00093 #define SAP_TIMEOUT_LONGTEXT N_( \
00094        "Sets the time before SAP items get deleted if no new announcement " \
00095        "is received." )
00096 #define SAP_PARSE_TEXT N_( "Try to parse the SAP" )
00097 #define SAP_PARSE_LONGTEXT N_( \
00098        "When SAP can it will try to parse the SAP. If you don't select " \
00099        "this, all announcements will be parsed by the livedotcom module." )
00100 #define SAP_STRICT_TEXT N_( "SAP Strict mode" )
00101 #define SAP_STRICT_LONGTEXT N_( \
00102        "When this is set, the SAP parser will discard some non-compliant " \
00103        "announcements." )
00104 #define SAP_CACHE_TEXT N_("Use SAP cache")
00105 #define SAP_CACHE_LONGTEXT N_( \
00106        "If this option is selected, a SAP caching mechanism will be used. " \
00107        "This will result in lower SAP startup time, but you could end up " \
00108         "with items corresponding to legacy streams." )
00109 #define SAP_TIMESHIFT_TEXT N_("Allow timeshifting")
00110 #define SAP_TIMESHIFT_LONGTEXT N_( \
00111         "Enable timeshifting automatically for streams " \
00112         "discovered through SAP announcements." )
00113 
00114 /* Callbacks */
00115     static int  Open ( vlc_object_t * );
00116     static void Close( vlc_object_t * );
00117     static int  OpenDemux ( vlc_object_t * );
00118     static void CloseDemux ( vlc_object_t * );
00119 
00120 vlc_module_begin();
00121     set_shortname( _("SAP"));
00122     set_description( _("SAP Announcements") );
00123     set_category( CAT_PLAYLIST );
00124     set_subcategory( SUBCAT_PLAYLIST_SD );
00125 
00126     add_string( "sap-addr", NULL, NULL,
00127                 SAP_ADDR_TEXT, SAP_ADDR_LONGTEXT, VLC_TRUE );
00128     add_bool( "sap-ipv4", 1 , NULL,
00129                SAP_IPV4_TEXT,SAP_IPV4_LONGTEXT, VLC_TRUE );
00130     add_bool( "sap-ipv6", 1 , NULL,
00131               SAP_IPV6_TEXT, SAP_IPV6_LONGTEXT, VLC_TRUE );
00132     add_integer( "sap-timeout", 1800, NULL,
00133                  SAP_TIMEOUT_TEXT, SAP_TIMEOUT_LONGTEXT, VLC_TRUE );
00134     add_bool( "sap-parse", 1 , NULL,
00135                SAP_PARSE_TEXT,SAP_PARSE_LONGTEXT, VLC_TRUE );
00136     add_bool( "sap-strict", 0 , NULL,
00137                SAP_STRICT_TEXT,SAP_STRICT_LONGTEXT, VLC_TRUE );
00138     add_bool( "sap-cache", 0 , NULL,
00139                SAP_CACHE_TEXT,SAP_CACHE_LONGTEXT, VLC_TRUE );
00140     add_bool( "sap-timeshift", 0 , NULL,
00141               SAP_TIMESHIFT_TEXT,SAP_TIMESHIFT_LONGTEXT, VLC_FALSE );
00142 
00143     set_capability( "services_discovery", 0 );
00144     set_callbacks( Open, Close );
00145 
00146     add_submodule();
00147         set_description( _("SDP file parser for UDP") );
00148         add_shortcut( "sdp" );
00149         set_capability( "demux2", 51 );
00150         set_callbacks( OpenDemux, CloseDemux );
00151 vlc_module_end();
00152 
00153 
00154 /*****************************************************************************
00155  * Local structures
00156  *****************************************************************************/
00157 
00158 typedef struct sdp_t sdp_t;
00159 typedef struct attribute_t attribute_t;
00160 typedef struct sap_announce_t sap_announce_t;
00161 
00162 /* The structure that contains sdp information */
00163 struct  sdp_t
00164 {
00165     char *psz_sdp;
00166 
00167     /* s= field */
00168     char *psz_sessionname;
00169 
00170     /* Raw m= and c= fields */
00171     char *psz_connection;
00172     char *psz_media;
00173 
00174     /* o field */
00175     char *psz_username;
00176     char *psz_network_type;
00177     char *psz_address_type;
00178     char *psz_address;
00179     int64_t i_session_id;
00180 
00181     /* "computed" URI */
00182     char *psz_uri;
00183 
00184     int           i_in; /* IP version */
00185 
00186     int           i_media;
00187     int           i_media_type;
00188 
00189     int           i_attributes;
00190     attribute_t  **pp_attributes;
00191 };
00192 
00193 struct attribute_t
00194 {
00195     char *psz_field;
00196     char *psz_value;
00197 };
00198 
00199 struct sap_announce_t
00200 {
00201     mtime_t i_last;
00202 
00203     uint16_t    i_hash;
00204     uint32_t    i_source[4];
00205 
00206     /* SAP annnounces must only contain one SDP */
00207     sdp_t       *p_sdp;
00208 
00209     int i_item_id;
00210 //    playlist_item_t *p_item;
00211 };
00212 
00213 struct services_discovery_sys_t
00214 {
00215     /* Socket descriptors */
00216     int i_fd;
00217     int *pi_fd;
00218 
00219     /* playlist node */
00220     playlist_item_t *p_node;
00221     playlist_t *p_playlist;
00222 
00223     /* Table of announces */
00224     int i_announces;
00225     struct sap_announce_t **pp_announces;
00226 
00227     /* Modes */
00228     vlc_bool_t  b_strict;
00229     vlc_bool_t  b_parse;
00230     vlc_bool_t  b_timeshift;
00231 
00232     int i_timeout;
00233 };
00234 
00235 struct demux_sys_t
00236 {
00237     sdp_t *p_sdp;
00238 };
00239 
00240 /*****************************************************************************
00241  * Local prototypes
00242  *****************************************************************************/
00243 
00244 
00245 /* Main functions */
00246     static int Demux( demux_t *p_demux );
00247     static int Control( demux_t *, int, va_list );
00248     static void Run    ( services_discovery_t *p_sd );
00249 
00250 /* Main parsing functions */
00251     static int ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp );
00252     static int ParseSAP( services_discovery_t *p_sd, uint8_t *p_buffer, int i_read );
00253     static sdp_t *  ParseSDP( vlc_object_t *p_sd, char* psz_sdp );
00254     static sap_announce_t *CreateAnnounce( services_discovery_t *, uint16_t, sdp_t * );
00255     static int RemoveAnnounce( services_discovery_t *p_sd, sap_announce_t *p_announce );
00256 
00257 /* Cache */
00258     static void CacheLoad( services_discovery_t *p_sd );
00259     static void CacheSave( services_discovery_t *p_sd );
00260 /* Helper functions */
00261     static char *GetAttribute( sdp_t *p_sdp, const char *psz_search );
00262     static vlc_bool_t IsSameSession( sdp_t *p_sdp1, sdp_t *p_sdp2 );
00263     static int InitSocket( services_discovery_t *p_sd, char *psz_address, int i_port );
00264 #ifdef HAVE_ZLIB_H
00265     static int Decompress( unsigned char *psz_src, unsigned char **_dst, int i_len );
00266 #endif
00267     static void FreeSDP( sdp_t *p_sdp );
00268 
00269 
00270 #define FREE( p ) \
00271     if( p ) { free( p ); (p) = NULL; }
00272 /*****************************************************************************
00273  * Open: initialize and create stuff
00274  *****************************************************************************/
00275 static int Open( vlc_object_t *p_this )
00276 {
00277     services_discovery_t *p_sd = ( services_discovery_t* )p_this;
00278     services_discovery_sys_t *p_sys  = (services_discovery_sys_t *)
00279                                 malloc( sizeof( services_discovery_sys_t ) );
00280 
00281     playlist_view_t     *p_view;
00282     vlc_value_t         val;
00283 
00284     p_sys->i_timeout = var_CreateGetInteger( p_sd, "sap-timeout" );
00285 
00286     p_sd->pf_run = Run;
00287     p_sd->p_sys  = p_sys;
00288 
00289     p_sys->pi_fd = NULL;
00290     p_sys->i_fd = 0;
00291 
00292     p_sys->b_strict = var_CreateGetInteger( p_sd, "sap-strict");
00293     p_sys->b_parse = var_CreateGetInteger( p_sd, "sap-parse" );
00294 
00295     if( var_CreateGetInteger( p_sd, "sap-cache" ) )
00296     {
00297         CacheLoad( p_sd );
00298     }
00299 
00300     /* Cache sap_timeshift value */
00301     p_sys->b_timeshift = var_CreateGetInteger( p_sd, "sap-timeshift" )
00302             ? VLC_TRUE : VLC_FALSE;
00303 
00304     /* Create our playlist node */
00305     p_sys->p_playlist = (playlist_t *)vlc_object_find( p_sd,
00306                                                        VLC_OBJECT_PLAYLIST,
00307                                                        FIND_ANYWHERE );
00308     if( !p_sys->p_playlist )
00309     {
00310         msg_Warn( p_sd, "unable to find playlist, cancelling SAP listening");
00311         return VLC_EGENERIC;
00312     }
00313 
00314     p_view = playlist_ViewFind( p_sys->p_playlist, VIEW_CATEGORY );
00315     p_sys->p_node = playlist_NodeCreate( p_sys->p_playlist, VIEW_CATEGORY,
00316                                          _("Session Announcements (SAP)"), p_view->p_root );
00317     p_sys->p_node->i_flags |= PLAYLIST_RO_FLAG;
00318     p_sys->p_node->i_flags &= ~PLAYLIST_SKIP_FLAG;
00319     val.b_bool = VLC_TRUE;
00320     var_Set( p_sys->p_playlist, "intf-change", val );
00321 
00322     p_sys->i_announces = 0;
00323     p_sys->pp_announces = NULL;
00324 
00325     return VLC_SUCCESS;
00326 }
00327 
00328 /*****************************************************************************
00329  * OpenDemux: initialize and create stuff
00330  *****************************************************************************/
00331 static int OpenDemux( vlc_object_t *p_this )
00332 {
00333     demux_t *p_demux = (demux_t *)p_this;
00334     uint8_t *p_peek;
00335     int i_max_sdp = 1024;
00336     int i_sdp = 0;
00337     char *psz_sdp = NULL;
00338     sdp_t *p_sdp = NULL;
00339 
00340     if( !var_CreateGetInteger( p_demux, "sap-parse" ) )
00341     {
00342         /* We want livedotcom module to parse this SDP file */
00343         return VLC_EGENERIC;
00344     }
00345 
00346     /* Probe for SDP */
00347     if( p_demux->s )
00348     {
00349         if( stream_Peek( p_demux->s, &p_peek, 7 ) < 7 ) return VLC_EGENERIC;
00350 
00351         if( strncmp( (char*)p_peek, "v=0\r\n", 5 ) &&
00352             strncmp( (char*)p_peek, "v=0\n", 4 ) &&
00353             ( p_peek[0] < 'a' || p_peek[0] > 'z' || p_peek[1] != '=' ) )
00354         {
00355             return VLC_EGENERIC;
00356         }
00357     }
00358 
00359     psz_sdp = (char *)malloc( i_max_sdp );
00360     if( !psz_sdp ) return VLC_EGENERIC;
00361 
00362     /* Gather the complete sdp file */
00363     for( ;; )
00364     {
00365         int i_read = stream_Read( p_demux->s,
00366                                   &psz_sdp[i_sdp], i_max_sdp - i_sdp - 1 );
00367 
00368         if( i_read < 0 )
00369         {
00370             msg_Err( p_demux, "failed to read SDP" );
00371             goto error;
00372         }
00373 
00374         i_sdp += i_read;
00375 
00376         if( i_read < i_max_sdp - i_sdp - 1 )
00377         {
00378             psz_sdp[i_sdp] = '\0';
00379             break;
00380         }
00381 
00382         i_max_sdp += 1000;
00383         psz_sdp = (char *)realloc( psz_sdp, i_max_sdp );
00384     }
00385 
00386     p_sdp = ParseSDP( VLC_OBJECT(p_demux), psz_sdp );
00387 
00388     if( !p_sdp )
00389     {
00390         msg_Warn( p_demux, "invalid SDP");
00391         goto error;
00392     }
00393 
00394     if( p_sdp->i_media > 1 )
00395     {
00396         goto error;
00397     }
00398 
00399     if( ParseConnection( VLC_OBJECT( p_demux ), p_sdp ) )
00400     {
00401         p_sdp->psz_uri = NULL;
00402     }
00403     if( p_sdp->i_media_type != 33 && p_sdp->i_media_type != 32 &&
00404         p_sdp->i_media_type != 14 )
00405         goto error;
00406 
00407     if( p_sdp->psz_uri == NULL ) goto error;
00408 
00409     p_demux->p_sys = (demux_sys_t *)malloc( sizeof(demux_sys_t) );
00410     p_demux->p_sys->p_sdp = p_sdp;
00411     p_demux->pf_control = Control;
00412     p_demux->pf_demux = Demux;
00413 
00414     free( psz_sdp );
00415     return VLC_SUCCESS;
00416 
00417 error:
00418     free( psz_sdp );
00419     if( p_sdp ) FreeSDP( p_sdp );
00420     stream_Seek( p_demux->s, 0 );
00421     return VLC_EGENERIC;    
00422 }
00423 
00424 /*****************************************************************************
00425  * Close:
00426  *****************************************************************************/
00427 static void Close( vlc_object_t *p_this )
00428 {
00429     services_discovery_t *p_sd = ( services_discovery_t* )p_this;
00430     services_discovery_sys_t    *p_sys  = p_sd->p_sys;
00431 
00432     int i;
00433 
00434     for( i = p_sys->i_fd-1 ; i >= 0 ; i-- )
00435     {
00436         net_Close( p_sys->pi_fd[i] );
00437     }
00438     FREE( p_sys->pi_fd );
00439 
00440     if( config_GetInt( p_sd, "sap-cache" ) )
00441     {
00442         CacheSave( p_sd );
00443     }
00444 
00445     for( i = p_sys->i_announces  - 1;  i>= 0; i-- )
00446     {
00447         RemoveAnnounce( p_sd, p_sys->pp_announces[i] );
00448     }
00449     FREE( p_sys->pp_announces );
00450 
00451     if( p_sys->p_playlist )
00452     {
00453         playlist_NodeDelete( p_sys->p_playlist, p_sys->p_node, VLC_TRUE,
00454                              VLC_TRUE );
00455         vlc_object_release( p_sys->p_playlist );
00456     }
00457 
00458     free( p_sys );
00459 }
00460 
00461 /*****************************************************************************
00462  * CloseDemux: Close the demuxer
00463  *****************************************************************************/
00464 static void CloseDemux( vlc_object_t *p_this )
00465 {
00466 
00467 }
00468 
00469 /*****************************************************************************
00470  * Run: main SAP thread
00471  *****************************************************************************
00472  * Listens to SAP packets, and sends them to packet_handle
00473  *****************************************************************************/
00474 #define MAX_SAP_BUFFER 5000
00475 
00476 static void Run( services_discovery_t *p_sd )
00477 {
00478     char *psz_addr;
00479     int i;
00480 
00481     /* Braindead Winsock DNS resolver will get stuck over 2 seconds per failed
00482      * DNS queries, even if the DNS server returns an error with milliseconds.
00483      * You don't want to know why the bug (as of XP SP2) wasn't fixed since
00484      * Winsock 1.1 from Windows 95, if not Windows 3.1.
00485      * Anyway, to avoid a 30 seconds delay for failed IPv6 socket creation,
00486      * we have to open sockets in Run() rather than Open(). */
00487     if( var_CreateGetInteger( p_sd, "sap-ipv4" ) )
00488     {
00489         InitSocket( p_sd, SAP_V4_GLOBAL_ADDRESS, SAP_PORT );
00490         InitSocket( p_sd, SAP_V4_ORG_ADDRESS, SAP_PORT );
00491         InitSocket( p_sd, SAP_V4_LOCAL_ADDRESS, SAP_PORT );
00492         InitSocket( p_sd, SAP_V4_LINK_ADDRESS, SAP_PORT );
00493     }
00494     if( var_CreateGetInteger( p_sd, "sap-ipv6" ) )
00495     {
00496         char psz_address[] = SAP_V6_1"0"SAP_V6_2;
00497         const char *c_scope;
00498 
00499         for( c_scope = ipv6_scopes; *c_scope; c_scope++ )
00500         {
00501             psz_address[sizeof(SAP_V6_1) - 1] = *c_scope;
00502             InitSocket( p_sd, psz_address, SAP_PORT );
00503         }
00504     }
00505 
00506     psz_addr = var_CreateGetString( p_sd, "sap-addr" );
00507     if( psz_addr && *psz_addr )
00508     {
00509         InitSocket( p_sd, psz_addr, SAP_PORT );
00510     }
00511 
00512     if( p_sd->p_sys->i_fd == 0 )
00513     {
00514         msg_Err( p_sd, "unable to listen on any address" );
00515         return;
00516     }
00517 
00518     /* read SAP packets */
00519     while( !p_sd->b_die )
00520     {
00521         int i_read;
00522         uint8_t p_buffer[MAX_SAP_BUFFER+1];
00523 
00524         i_read = net_Select( p_sd, p_sd->p_sys->pi_fd, NULL,
00525                              p_sd->p_sys->i_fd, p_buffer,
00526                              MAX_SAP_BUFFER, 500000 );
00527 
00528         /* Check for items that need deletion */
00529         for( i = 0; i < p_sd->p_sys->i_announces; i++ )
00530         {
00531             mtime_t i_timeout = ( mtime_t ) 1000000 * p_sd->p_sys->i_timeout;
00532 
00533             if( mdate() - p_sd->p_sys->pp_announces[i]->i_last > i_timeout )
00534             {
00535                 struct sap_announce_t *p_announce;
00536                 p_announce = p_sd->p_sys->pp_announces[i];
00537 
00538                 /* Remove the playlist item */
00539                 playlist_LockDelete( p_sd->p_sys->p_playlist,
00540                                      p_announce->i_item_id );
00541 
00542                 /* Remove the sap_announce from the array */
00543                 REMOVE_ELEM( p_sd->p_sys->pp_announces,
00544                            p_sd->p_sys->i_announces, i );
00545 
00546                 free( p_announce );
00547             }
00548         }
00549 
00550         /* Minimum length is > 6 */
00551         if( i_read <= 6 )
00552         {
00553             if( i_read < 0 )
00554             {
00555                 msg_Warn( p_sd, "socket read error" );
00556             }
00557             continue;
00558         }
00559 
00560         p_buffer[i_read] = '\0';
00561 
00562         /* Parse the packet */
00563         ParseSAP( p_sd, p_buffer, i_read );
00564     }
00565 }
00566 
00567 /**********************************************************************
00568  * Demux: reads and demuxes data packets
00569  * Return -1 if error, 0 if EOF, 1 else
00570  **********************************************************************/
00571 static int Demux( demux_t *p_demux )
00572 {
00573     sdp_t *p_sdp = p_demux->p_sys->p_sdp;
00574     playlist_t *p_playlist;
00575 
00576     p_playlist = (playlist_t *)vlc_object_find( p_demux, VLC_OBJECT_PLAYLIST,
00577                                                FIND_ANYWHERE );
00578 
00579     p_playlist->status.p_item->i_flags |= PLAYLIST_DEL_FLAG;
00580 
00581     playlist_Add( p_playlist, p_sdp->psz_uri, p_sdp->psz_sessionname,
00582                  PLAYLIST_APPEND, PLAYLIST_END );
00583 
00584     vlc_object_release( p_playlist );
00585     if( p_sdp ) FreeSDP( p_sdp );
00586 
00587     return VLC_SUCCESS;
00588 }
00589 
00590 static int Control( demux_t *p_demux, int i_query, va_list args )
00591 {
00592     return VLC_EGENERIC;
00593 }
00594 
00595 /**************************************************************
00596  * Local functions
00597  **************************************************************/
00598 
00599 /* i_read is at least > 6 */
00600 static int ParseSAP( services_discovery_t *p_sd, uint8_t *p_buffer, int i_read )
00601 {
00602     int                 i_version, i_address_type, i_hash, i;
00603     char                *psz_sdp, *psz_foo, *psz_initial_sdp;
00604     sdp_t               *p_sdp;
00605     vlc_bool_t          b_compressed;
00606     vlc_bool_t          b_need_delete = VLC_FALSE;
00607 
00608     /* First, check the sap announce is correct */
00609     i_version = p_buffer[0] >> 5;
00610     if( i_version != 1 )
00611     {
00612        msg_Dbg( p_sd, "strange sap version %d found", i_version );
00613     }
00614 
00615     i_address_type = p_buffer[0] & 0x10;
00616 
00617     if( (p_buffer[0] & 0x08) != 0 )
00618     {
00619         msg_Dbg( p_sd, "reserved bit incorrectly set" );
00620         return VLC_EGENERIC;
00621     }
00622 
00623     if( (p_buffer[0] & 0x04) != 0 )
00624     {
00625         msg_Dbg( p_sd, "session deletion packet" );
00626         b_need_delete = VLC_TRUE;
00627     }
00628 
00629     if( p_buffer[0] & 0x02  )
00630     {
00631         msg_Dbg( p_sd, "encrypted packet, unsupported" );
00632         return VLC_EGENERIC;
00633     }
00634 
00635     b_compressed = p_buffer[0] & 0x01;
00636 
00637     i_hash = ( p_buffer[2] << 8 ) + p_buffer[3];
00638 
00639     if( p_sd->p_sys->b_strict && i_hash == 0 )
00640     {
00641         msg_Dbg( p_sd, "strict mode, discarding announce with null id hash");
00642         return VLC_EGENERIC;
00643     }
00644 
00645     psz_sdp  = (char *)p_buffer + 4;
00646     psz_initial_sdp = psz_sdp;
00647 
00648     if( i_address_type == 0 ) /* ipv4 source address */
00649     {
00650         psz_sdp += 4;
00651         if( i_read <= 9 )
00652         {
00653             msg_Warn( p_sd, "too short SAP packet\n" );
00654             return VLC_EGENERIC;
00655         }
00656     }
00657     else /* ipv6 source address */
00658     {
00659         psz_sdp += 16;
00660         if( i_read <= 21 )
00661         {
00662             msg_Warn( p_sd, "too short SAP packet\n" );
00663             return VLC_EGENERIC;
00664         }
00665     }
00666 
00667     if( b_compressed )
00668     {
00669 #ifdef HAVE_ZLIB_H
00670         uint8_t *p_decompressed_buffer = NULL;
00671         int      i_decompressed_size;
00672 
00673         i_decompressed_size = Decompress( (uint8_t *)psz_sdp,
00674                    &p_decompressed_buffer, i_read - ( psz_sdp - (char *)p_buffer ) );
00675         if( i_decompressed_size > 0 && i_decompressed_size < MAX_SAP_BUFFER )
00676         {
00677             memcpy( psz_sdp, p_decompressed_buffer, i_decompressed_size );
00678             psz_sdp[i_decompressed_size] = '\0';
00679             free( p_decompressed_buffer );
00680         }
00681 #else
00682         msg_Warn( p_sd, "Ignoring compressed sap packet" );
00683         return VLC_EGENERIC;
00684 #endif
00685     }
00686 
00687     /* Add the size of authentification info */
00688     if( i_read < p_buffer[1] + (psz_sdp - psz_initial_sdp ) )
00689     {
00690         msg_Warn( p_sd, "too short SAP packet\n");
00691         return VLC_EGENERIC;
00692     }
00693     psz_sdp += p_buffer[1];
00694     psz_foo = psz_sdp;
00695 
00696     /* Skip payload type */
00697     /* Handle announces without \0 between SAP and SDP */
00698     while( *psz_sdp != '\0' && ( psz_sdp[0] != 'v' && psz_sdp[1] != '=' ) )
00699     {
00700         if( psz_sdp - psz_initial_sdp >= i_read - 5 )
00701         {
00702             msg_Warn( p_sd, "empty SDP ?");
00703         }
00704         psz_sdp++;
00705     }
00706 
00707     if( *psz_sdp == '\0' )
00708     {
00709         psz_sdp++;
00710     }
00711     if( ( psz_sdp != psz_foo ) && strcasecmp( psz_foo, "application/sdp" ) )
00712     {
00713         msg_Dbg( p_sd, "unhandled content type: %s", psz_foo );        
00714     }
00715     if( ( psz_sdp - (char *)p_buffer ) >= i_read )
00716     {
00717         msg_Warn( p_sd, "package without content" );
00718         return VLC_EGENERIC;
00719     }
00720 
00721     /* Parse SDP info */
00722     p_sdp = ParseSDP( VLC_OBJECT(p_sd), psz_sdp );
00723 
00724     if( p_sdp == NULL )
00725     {
00726         return VLC_EGENERIC;
00727     }
00728 
00729     /* Decide whether we should add a playlist item for this SDP */
00730     /* Parse connection information (c= & m= ) */
00731     if( ParseConnection( VLC_OBJECT(p_sd), p_sdp ) )
00732     {
00733         p_sdp->psz_uri = NULL;
00734     }
00735 
00736     /* Multi-media or no-parse -> pass to LIVE.COM */
00737     if( p_sdp->i_media > 1 || ( p_sdp->i_media_type != 14 &&
00738                                 p_sdp->i_media_type != 32 &&
00739                                 p_sdp->i_media_type != 33) ||
00740         p_sd->p_sys->b_parse == VLC_FALSE )
00741     {
00742         if( p_sdp->psz_uri ) free( p_sdp->psz_uri );
00743         asprintf( &p_sdp->psz_uri, "sdp://%s", p_sdp->psz_sdp );
00744     }
00745 
00746     if( p_sdp->psz_uri == NULL ) return VLC_EGENERIC;
00747 
00748     for( i = 0 ; i< p_sd->p_sys->i_announces ; i++ )
00749     {
00750         /* FIXME: slow */
00751         /* FIXME: we create a new announce each time the sdp changes */
00752         if( IsSameSession( p_sd->p_sys->pp_announces[i]->p_sdp, p_sdp ) )
00753         {
00754             if( b_need_delete )
00755             {
00756                 RemoveAnnounce( p_sd, p_sd->p_sys->pp_announces[i]);
00757                 return VLC_SUCCESS;
00758             }
00759             else
00760             {
00761                 p_sd->p_sys->pp_announces[i]->i_last = mdate();
00762                 FreeSDP( p_sdp );
00763                 return VLC_SUCCESS;
00764             }
00765         }
00766     }
00767     /* Add item */
00768     if( p_sdp->i_media > 1 )
00769     {
00770         msg_Dbg( p_sd, "passing to LIVE.COM" );
00771     }
00772 
00773     CreateAnnounce( p_sd, i_hash, p_sdp );
00774 
00775     return VLC_SUCCESS;
00776 }
00777 
00778 sap_announce_t *CreateAnnounce( services_discovery_t *p_sd, uint16_t i_hash,
00779                                 sdp_t *p_sdp )
00780 {
00781     playlist_item_t     *p_item, *p_child;
00782     char *psz_value;
00783     sap_announce_t *p_sap = (sap_announce_t *)malloc(
00784                                         sizeof(sap_announce_t ) );
00785     if( p_sap == NULL )
00786         return NULL;
00787 
00788     EnsureUTF8( p_sdp->psz_sessionname );
00789     p_sap->i_last = mdate();
00790     p_sap->i_hash = i_hash;
00791     p_sap->p_sdp = p_sdp;
00792     p_sap->i_item_id = -1;
00793 
00794     /* Create the playlist item here */
00795     p_item = playlist_ItemNew( p_sd, p_sap->p_sdp->psz_uri,
00796                                p_sdp->psz_sessionname );
00797 
00798     if( !p_item )
00799     {
00800         free( p_sap );
00801         return NULL;
00802     }
00803 
00804     if( p_sd->p_sys->b_timeshift )
00805         playlist_ItemAddOption( p_item, ":access-filter=timeshift" );
00806 
00807     psz_value = GetAttribute( p_sap->p_sdp, "tool" );
00808     if( psz_value != NULL )
00809     {
00810         vlc_input_item_AddInfo( &p_item->input, _("Session"),
00811                                 _("Tool"), psz_value );
00812     }
00813     if( strcmp( p_sdp->psz_username, "-" ) )
00814     {
00815         vlc_input_item_AddInfo( &p_item->input, _("Session"),
00816                                 _("User"), p_sdp->psz_username );
00817     }
00818 
00819     psz_value = GetAttribute( p_sap->p_sdp, "x-plgroup" );
00820 
00821     if( psz_value == NULL )
00822     {
00823         psz_value = GetAttribute( p_sap->p_sdp, "plgroup" );
00824     }
00825 
00826     if( psz_value != NULL )
00827     {
00828         EnsureUTF8( psz_value );
00829 
00830         p_child = playlist_ChildSearchName( p_sd->p_sys->p_node, psz_value );
00831 
00832         if( p_child == NULL )
00833         {
00834             p_child = playlist_NodeCreate( p_sd->p_sys->p_playlist,
00835                                            VIEW_CATEGORY, psz_value,
00836                                            p_sd->p_sys->p_node );
00837             p_child->i_flags &= ~PLAYLIST_SKIP_FLAG;
00838         }
00839     }
00840     else
00841     {
00842         p_child = p_sd->p_sys->p_node;
00843     }
00844 
00845     p_item->i_flags &= ~PLAYLIST_SKIP_FLAG;
00846     p_item->i_flags &= ~PLAYLIST_SAVE_FLAG;
00847 
00848     playlist_NodeAddItem( p_sd->p_sys->p_playlist, p_item, VIEW_CATEGORY,
00849                               p_child, PLAYLIST_APPEND, PLAYLIST_END );
00850 
00851     p_sap->i_item_id = p_item->input.i_id;
00852 
00853     TAB_APPEND( p_sd->p_sys->i_announces,
00854                 p_sd->p_sys->pp_announces, p_sap );
00855 
00856     return p_sap;
00857 }
00858 
00859 static char *GetAttribute( sdp_t *p_sdp, const char *psz_search )
00860 {
00861     int i;
00862 
00863     for( i = 0 ; i< p_sdp->i_attributes; i++ )
00864     {
00865         if( !strncmp( p_sdp->pp_attributes[i]->psz_field, psz_search,
00866                       strlen( p_sdp->pp_attributes[i]->psz_field ) ) )
00867         {
00868             return p_sdp->pp_attributes[i]->psz_value;
00869         }
00870     }
00871     return NULL;
00872 }
00873 
00874 
00875 /* Fill p_sdp->psz_uri */
00876 static int ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp )
00877 {
00878     char *psz_eof;
00879     char *psz_parse;
00880     char *psz_uri = NULL;
00881     char *psz_proto = NULL;
00882     char psz_source[256];
00883     int i_port = 0;
00884 
00885     /* Parse c= field */
00886     if( p_sdp->psz_connection )
00887     {
00888         psz_parse = p_sdp->psz_connection;
00889 
00890         psz_eof = strchr( psz_parse, ' ' );
00891 
00892         if( psz_eof )
00893         {
00894             *psz_eof = '\0';
00895             psz_parse = psz_eof + 1;
00896         }
00897         else
00898         {
00899             msg_Warn( p_obj, "unable to parse c field (1)");
00900             return VLC_EGENERIC;
00901         }
00902 
00903         psz_eof = strchr( psz_parse, ' ' );
00904 
00905         if( psz_eof )
00906         {
00907             *psz_eof = '\0';
00908             if( !strncmp( psz_parse, "IP4", 3 ) )
00909             {
00910                 p_sdp->i_in = 4;
00911             }
00912             else if( !strncmp( psz_parse, "IP6", 3 ) )
00913             {
00914                 p_sdp->i_in = 6;
00915             }
00916             else
00917             {
00918                 p_sdp->i_in = 0;
00919             }
00920             psz_parse = psz_eof + 1;
00921         }
00922         else
00923         {
00924             msg_Warn( p_obj, "unable to parse c field (2)");
00925             return VLC_EGENERIC;
00926         }
00927 
00928         psz_eof = strchr( psz_parse, '/' );
00929 
00930         if( psz_eof )
00931         {
00932             *psz_eof = '\0';
00933         }
00934         else
00935         {
00936             msg_Dbg( p_obj, "incorrect c field, %s", p_sdp->psz_connection );
00937         }
00938         if( p_sdp->i_in == 6 && ( isxdigit( *psz_parse ) || *psz_parse == ':' ) )
00939         {
00940             asprintf( &psz_uri, "[%s]", psz_parse );
00941         }
00942         else psz_uri = strdup( psz_parse );
00943 
00944     }
00945 
00946     /* Parse m= field */
00947     if( p_sdp->psz_media )
00948     {
00949         psz_parse = p_sdp->psz_media;
00950 
00951         psz_eof = strchr( psz_parse, ' ' );
00952 
00953         if( psz_eof )
00954         {
00955             *psz_eof = '\0';
00956 
00957             if( strncmp( psz_parse, "audio", 5 )  &&
00958                 strncmp( psz_parse, "video", 5 ) )
00959             {
00960                 msg_Warn( p_obj, "unhandled media type -%s-", psz_parse );
00961                 FREE( psz_uri );
00962                 return VLC_EGENERIC;
00963             }
00964 
00965             psz_parse = psz_eof + 1;
00966         }
00967         else
00968         {
00969             msg_Warn( p_obj, "unable to parse m field (1)");
00970             FREE( psz_uri );
00971             return VLC_EGENERIC;
00972         }
00973 
00974         psz_eof = strchr( psz_parse, ' ' );
00975 
00976         if( psz_eof )
00977         {
00978             *psz_eof = '\0';
00979 
00980             /* FIXME : multiple port ! */
00981             i_port = atoi( psz_parse );
00982 
00983             if( i_port <= 0 || i_port >= 65536 )
00984             {
00985                 msg_Warn( p_obj, "invalid transport port %i", i_port );
00986             }
00987 
00988             psz_parse = psz_eof + 1;
00989         }
00990         else
00991         {
00992             msg_Warn( p_obj, "unable to parse m field (2)");
00993             FREE( psz_uri );
00994             return VLC_EGENERIC;
00995         }
00996 
00997         psz_eof = strchr( psz_parse, ' ' );
00998 
00999         if( psz_eof )
01000         {
01001             *psz_eof = '\0';
01002             psz_proto = strdup( psz_parse );
01003 
01004             psz_parse = psz_eof + 1;
01005             p_sdp->i_media_type = atoi( psz_parse );
01006             
01007         }
01008         else
01009         {
01010             msg_Dbg( p_obj, "incorrect m field, %s", p_sdp->psz_media );
01011             p_sdp->i_media_type = 33;
01012             psz_proto = strdup( psz_parse );
01013         }
01014     }
01015 
01016     if( psz_proto && !strncmp( psz_proto, "RTP/AVP", 7 ) )
01017     {
01018         free( psz_proto );
01019         psz_proto = strdup( "rtp" );
01020     }
01021     if( psz_proto && !strncasecmp( psz_proto, "UDP", 3 ) )
01022     {
01023         free( psz_proto );
01024         psz_proto = strdup( "udp" );
01025     }
01026 
01027     /* FIXME: HTTP support */
01028 
01029     if( i_port == 0 )
01030     {
01031         i_port = 1234;
01032     }
01033 
01034     /* handle SSM case */
01035     psz_parse = GetAttribute( p_sdp, "source-filter" );
01036     psz_source[0] = '\0';
01037 
01038     if( psz_parse ) sscanf( psz_parse, " incl IN IP%*s %*s %255s ", psz_source);
01039 
01040     asprintf( &p_sdp->psz_uri, "%s://%s@%s:%i", psz_proto, psz_source,
01041               psz_uri, i_port );
01042 
01043     FREE( psz_uri );
01044     FREE( psz_proto );
01045     return VLC_SUCCESS;
01046 }
01047 
01048 /***********************************************************************
01049  * ParseSDP : SDP parsing
01050  * *********************************************************************
01051  * Validate SDP and parse all fields
01052  ***********************************************************************/
01053 static sdp_t *  ParseSDP( vlc_object_t *p_obj, char* psz_sdp )
01054 {
01055     sdp_t *p_sdp;
01056     vlc_bool_t b_invalid = VLC_FALSE;
01057     vlc_bool_t b_end = VLC_FALSE;
01058     if( psz_sdp == NULL )
01059     {
01060         return NULL;
01061     }
01062 
01063     if( psz_sdp[0] != 'v' || psz_sdp[1] != '=' )
01064     {
01065         msg_Warn( p_obj, "Bad packet" );
01066         return NULL;
01067     }
01068 
01069     p_sdp = (sdp_t *)malloc( sizeof( sdp_t ) );
01070     if( p_sdp == NULL )
01071         return NULL;
01072 
01073     p_sdp->psz_sdp = strdup( psz_sdp );
01074     if( p_sdp->psz_sdp == NULL )
01075     {
01076         free( p_sdp );
01077         return NULL;
01078     }
01079 
01080     p_sdp->psz_sessionname = NULL;
01081     p_sdp->psz_media       = NULL;
01082     p_sdp->psz_connection  = NULL;
01083     p_sdp->psz_uri         = NULL;
01084     p_sdp->psz_address     = NULL;
01085     p_sdp->psz_address_type= NULL;
01086 
01087     p_sdp->i_media         = 0;
01088     p_sdp->i_attributes    = 0;
01089     p_sdp->pp_attributes   = NULL;
01090 
01091     while( *psz_sdp != '\0' && b_end == VLC_FALSE  )
01092     {
01093         char *psz_eol;
01094         char *psz_eof;
01095         char *psz_parse;
01096         char *psz_sess_id;
01097 
01098         while( *psz_sdp == '\r' || *psz_sdp == '\n' ||
01099                *psz_sdp == ' ' || *psz_sdp == '\t' )
01100         {
01101             psz_sdp++;
01102         }
01103 
01104         if( ( psz_eol = strchr( psz_sdp, '\n' ) ) == NULL )
01105         {
01106             psz_eol = psz_sdp + strlen( psz_sdp );
01107             b_end = VLC_TRUE;
01108         }
01109         if( psz_eol > psz_sdp && *( psz_eol - 1 ) == '\r' )
01110         {
01111             psz_eol--;
01112         }
01113 
01114         if( psz_eol <= psz_sdp )
01115         {
01116             break;
01117         }
01118         *psz_eol++ = '\0';
01119 
01120         /* no space allowed between fields */
01121         if( psz_sdp[1] != '=' )
01122         {
01123             msg_Warn( p_obj, "invalid packet" ) ;
01124             FreeSDP( p_sdp );
01125             return NULL;
01126         }
01127 
01128         /* Now parse each line */
01129         switch( psz_sdp[0] )
01130         {
01131             case( 'v' ):
01132                 break;
01133             case( 's' ):
01134                 p_sdp->psz_sessionname = strdup( &psz_sdp[2] );
01135                 break;
01136             case ( 'o' ):
01137             {
01138                 int i_field = 0;
01139                 /* o field is <username> <session id> <version>
01140                  *  <network type> <address type> <address> */
01141 
01142 #define GET_FIELD( store ) \
01143                 psz_eof = strchr( psz_parse, ' ' ); \
01144                 if( psz_eof ) \
01145                 { \
01146                     *psz_eof=0; store = strdup( psz_parse ); \
01147                 } \
01148                 else \
01149                 { \
01150                     if( i_field != 5 ) \
01151                     { \
01152                         b_invalid = VLC_TRUE; break; \
01153                     } \
01154                     else \
01155                     { \
01156                         store = strdup( psz_parse ); \
01157                     } \
01158                 }; \
01159                 psz_parse = psz_eof + 1; i_field++;
01160 
01161 
01162                 psz_parse = &psz_sdp[2];
01163                 GET_FIELD( p_sdp->psz_username );
01164                 GET_FIELD( psz_sess_id );
01165 
01166                 p_sdp->i_session_id = atoll( psz_sess_id );
01167 
01168                 FREE( psz_sess_id );
01169 
01170                 GET_FIELD( psz_sess_id );
01171                 FREE( psz_sess_id );
01172 
01173                 GET_FIELD( p_sdp->psz_network_type );
01174                 GET_FIELD( p_sdp->psz_address_type );
01175                 GET_FIELD( p_sdp->psz_address );
01176 
01177                 break;
01178             }
01179             case( 'i' ):
01180             case( 'u' ):
01181             case( 'e' ):
01182             case( 'p' ):
01183             case( 't' ):
01184             case( 'r' ):
01185                 break;
01186             case( 'a' ): /* attribute */
01187             {
01188                 char *psz_eon = strchr( &psz_sdp[2], ':' );
01189                 attribute_t *p_attr = malloc( sizeof( attribute_t ) );
01190 
01191                 /* Attribute with value */
01192                 if( psz_eon )
01193                 {
01194                     *psz_eon++ = '\0';
01195 
01196                     p_attr->psz_field = strdup( &psz_sdp[2] );
01197                     p_attr->psz_value = strdup( psz_eon );
01198                 }
01199                 else /* Attribute without value */
01200                 {
01201                     p_attr->psz_field = strdup( &psz_sdp[2] );
01202                     p_attr->psz_value = NULL;
01203                 }
01204 
01205                 TAB_APPEND( p_sdp->i_attributes, p_sdp->pp_attributes, p_attr );
01206                 break;
01207             }
01208 
01209             case( 'm' ): /* Media announcement */
01210             {
01211                 /* If we have several medias, we pass the announcement to
01212                  * LIVE.COM, so just count them */
01213                 p_sdp->i_media++;
01214                 if( p_sdp->i_media == 1 )
01215                 {
01216                     p_sdp->psz_media = strdup( &psz_sdp[2] );
01217                 }
01218                 break;
01219             }
01220 
01221             case( 'c' ):
01222             {
01223                 if( p_sdp->i_media > 1 )
01224                     break;
01225 
01226                 p_sdp->psz_connection = strdup( &psz_sdp[2] );
01227                 break;
01228             }
01229 
01230             default:
01231                break;
01232         }
01233 
01234         if( b_invalid )
01235         {
01236             FreeSDP( p_sdp );
01237             return NULL;
01238         }
01239 
01240         psz_sdp = psz_eol;
01241     }
01242 
01243     return p_sdp;
01244 }
01245 
01246 static int InitSocket( services_discovery_t *p_sd, char *psz_address,
01247                        int i_port )
01248 {
01249     int i_fd = net_OpenUDP( p_sd, psz_address, i_port, NULL, 0 );
01250 
01251     if( i_fd != -1 )
01252     {
01253         net_StopSend( i_fd );
01254         INSERT_ELEM(  p_sd->p_sys->pi_fd, p_sd->p_sys->i_fd,
01255                       p_sd->p_sys->i_fd, i_fd );
01256         return VLC_SUCCESS;
01257     }
01258 
01259     return VLC_EGENERIC;
01260 }
01261 
01262 #ifdef HAVE_ZLIB_H
01263 static int Decompress( unsigned char *psz_src, unsigned char **_dst, int i_len )
01264 {
01265     int i_result, i_dstsize, n;
01266     unsigned char *psz_dst;
01267     z_stream d_stream;
01268 
01269     d_stream.zalloc = (alloc_func)0;
01270     d_stream.zfree = (free_func)0;
01271     d_stream.opaque = (voidpf)0;
01272 
01273     i_result = inflateInit(&d_stream);
01274     if( i_result != Z_OK )
01275     {
01276         printf( "inflateInit() failed. Result: %d\n", i_result );
01277         return( -1 );
01278     }
01279 #if 0
01280     p_playlist->pp_items[p_playlist->i_index]->b_autodeletion = VLC_TRUE;
01281     i_position = p_playlist->i_index;
01282 
01283     /* Gather the complete sdp file */
01284     for( ;; )
01285     {
01286         int i_read = stream_Read( p_demux->s, &p_sdp[i_sdp], i_sdp_max - i_sdp - 1 );
01287 #endif
01288     d_stream.next_in = (Bytef *)psz_src;
01289     d_stream.avail_in = i_len;
01290     n = 0;
01291 
01292     psz_dst = NULL;
01293 
01294     do
01295     {
01296         n++;
01297         psz_dst = (unsigned char *)realloc( psz_dst, n * 1000 );
01298         d_stream.next_out = (Bytef *)&psz_dst[(n - 1) * 1000];
01299         d_stream.avail_out = 1000;
01300 
01301         i_result = inflate(&d_stream, Z_NO_FLUSH);
01302         if( ( i_result != Z_OK ) && ( i_result != Z_STREAM_END ) )
01303         {
01304             printf( "Zlib decompression failed. Result: %d\n", i_result );
01305             return( -1 );
01306         }
01307     }
01308     while( ( d_stream.avail_out == 0 ) && ( d_stream.avail_in != 0 ) &&
01309            ( i_result != Z_STREAM_END ) );
01310 
01311     i_dstsize = d_stream.total_out;
01312     inflateEnd( &d_stream );
01313 
01314     *_dst = (unsigned char *)realloc( psz_dst, i_dstsize );
01315 
01316     return i_dstsize;
01317 }
01318 #endif
01319 
01320 
01321 static void FreeSDP( sdp_t *p_sdp )
01322 {
01323     int i;
01324     FREE( p_sdp->psz_sdp );
01325     FREE( p_sdp->psz_sessionname );
01326     FREE( p_sdp->psz_connection );
01327     FREE( p_sdp->psz_media );
01328     FREE( p_sdp->psz_uri );
01329     FREE( p_sdp->psz_username );
01330     FREE( p_sdp->psz_network_type );
01331 
01332     FREE( p_sdp->psz_address );
01333     FREE( p_sdp->psz_address_type );
01334 
01335     for( i= p_sdp->i_attributes - 1; i >= 0 ; i-- )
01336     {
01337         struct attribute_t *p_attr = p_sdp->pp_attributes[i];
01338         FREE( p_sdp->pp_attributes[i]->psz_field );
01339         FREE( p_sdp->pp_attributes[i]->psz_value );
01340         REMOVE_ELEM( p_sdp->pp_attributes, p_sdp->i_attributes, i);
01341         FREE( p_attr );
01342     }
01343     free( p_sdp );
01344 }
01345 
01346 static int RemoveAnnounce( services_discovery_t *p_sd,
01347                            sap_announce_t *p_announce )
01348 {
01349     int i;
01350 
01351     if( p_announce->p_sdp ) FreeSDP( p_announce->p_sdp );
01352 
01353     if( p_announce->i_item_id > -1 )
01354     {
01355         playlist_LockDelete( p_sd->p_sys->p_playlist, p_announce->i_item_id );
01356     }
01357 
01358     for( i = 0; i< p_sd->p_sys->i_announces; i++)
01359     {
01360         if( p_sd->p_sys->pp_announces[i] == p_announce )
01361         {
01362             REMOVE_ELEM( p_sd->p_sys->pp_announces, p_sd->p_sys->i_announces,
01363                          i);
01364             break;
01365         }
01366     }
01367 
01368     free( p_announce );
01369 
01370     return VLC_SUCCESS;
01371 }
01372 
01373 static vlc_bool_t IsSameSession( sdp_t *p_sdp1, sdp_t *p_sdp2 )
01374 {
01375     /* A session is identified by
01376      * username, session_id, network type, address type and address */
01377     if( p_sdp1->psz_username && p_sdp2->psz_username &&
01378         p_sdp1->psz_network_type && p_sdp2->psz_network_type &&
01379         p_sdp1->psz_address_type && p_sdp2->psz_address_type &&
01380         p_sdp1->psz_address &&  p_sdp2->psz_address )
01381     {
01382         if(!strcmp( p_sdp1->psz_username , p_sdp2->psz_username ) &&
01383            !strcmp( p_sdp1->psz_network_type , p_sdp2->psz_network_type ) &&
01384            !strcmp( p_sdp1->psz_address_type , p_sdp2->psz_address_type ) &&
01385            !strcmp( p_sdp1->psz_address , p_sdp2->psz_address ) &&
01386            p_sdp1->i_session_id == p_sdp2->i_session_id )
01387         {
01388             return VLC_TRUE;
01389         }
01390         else
01391         {
01392             return VLC_FALSE;
01393         }
01394     }
01395     else
01396     {
01397         return VLC_FALSE;
01398     }
01399 }
01400 
01401 
01402 static void CacheLoad( services_discovery_t *p_sd )
01403 {
01404     msg_Warn( p_sd, "Cache not implemented") ;
01405 }
01406 
01407 static void CacheSave( services_discovery_t *p_sd )
01408 {
01409     msg_Warn( p_sd, "Cache not implemented") ;
01410 }

Generated on Tue Dec 20 10:14:49 2005 for vlc-0.8.4a by  doxygen 1.4.2