sap.c 37.7 KB
Newer Older
1 2 3
/*****************************************************************************
 * sap.c :  SAP interface module
 *****************************************************************************
Clément Stenac's avatar
Clément Stenac committed
4
 * Copyright (C) 2004 VideoLAN
Carlo Calabrò's avatar
Carlo Calabrò committed
5
 * $Id$
6
 *
Clément Stenac's avatar
Clément Stenac committed
7
 * Authors: Clment Stenac <zorglub@videolan.org>
Clément Stenac's avatar
Clément Stenac committed
8
 *
9 10 11 12
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
13
 *
14 15 16 17 18 19 20 21 22 23 24
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
Clément Stenac's avatar
Clément Stenac committed
25
 * Includes
26 27 28 29 30
 *****************************************************************************/
#include <stdlib.h>                                      /* malloc(), free() */

#include <vlc/vlc.h>
#include <vlc/intf.h>
Clément Stenac's avatar
Clément Stenac committed
31

32
#include <vlc/input.h>
33

Clément Stenac's avatar
Clément Stenac committed
34 35
#include "network.h"

36
#include <errno.h>                                                 /* ENOMEM */
37 38 39 40 41 42 43 44

#ifdef HAVE_UNISTD_H
#    include <unistd.h>
#endif
#ifdef HAVE_SYS_TIME_H
#    include <sys/time.h>
#endif

Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
45 46 47 48
#ifdef HAVE_ZLIB_H
#   include <zlib.h>
#endif

Clément Stenac's avatar
Clément Stenac committed
49 50 51 52
/************************************************************************
 * Macros and definitions
 ************************************************************************/

53 54
#define MAX_LINE_LENGTH 256

55
/* SAP is always on that port */
Clément Stenac's avatar
Clément Stenac committed
56
#define SAP_PORT 9875
Clément Stenac's avatar
Clément Stenac committed
57
#define SAP_V4_ADDRESS "224.2.127.254"
58
#define ADD_SESSION 1
59

60 61 62
#define IPV6_ADDR_1 "FF0"  /* Scope is inserted between them */
#define IPV6_ADDR_2 "::2:7FFE"

63

64 65 66
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
67
#define SAP_ADDR_TEXT N_( "SAP multicast address" )
68
#define SAP_ADDR_LONGTEXT N_( "Listen for SAP announces on another address" )
69
#define SAP_IPV4_TEXT N_( "IPv4-SAP listening" )
70
#define SAP_IPV4_LONGTEXT N_( \
71 72
      "Set this if you want the SAP module to listen to IPv4 announces " \
      "on the standard address" )
73 74
#define SAP_IPV6_TEXT N_( "IPv6-SAP listening" )
#define SAP_IPV6_LONGTEXT N_( \
75 76
      "Set this if you want the SAP module to listen to IPv6 announces " \
      "on the standard address" )
77
#define SAP_SCOPE_TEXT N_( "IPv6 SAP scope" )
78
#define SAP_SCOPE_LONGTEXT N_( \
79 80
       "Sets the scope for IPv6 announces (default is 8)" )
#define SAP_TIMEOUT_TEXT N_( "SAP timeout (seconds)" )
81
#define SAP_TIMEOUT_LONGTEXT N_( \
82
       "Sets the time before SAP items get deleted if no new announce " \
83 84
       "is received." )
#define SAP_PARSE_TEXT N_( "Try to parse the SAP" )
85
#define SAP_PARSE_LONGTEXT N_( \
Clément Stenac's avatar
Clément Stenac committed
86 87
       "When SAP can it will try to parse the SAP. If you don't select " \
       "this, all announces will be parsed by the livedotcom module" )
88 89 90 91
#define SAP_STRICT_TEXT N_( "SAP Strict mode" )
#define SAP_STRICT_LONGTEXT N_( \
       "When this is set, the SAP parser will discard some non-compliant " \
       "announces" )
Clément Stenac's avatar
Clément Stenac committed
92 93 94 95 96
#define SAP_CACHE_TEXT N_("Use SAP cache")
#define SAP_CACHE_LONGTEXT N_( \
       "If this option is selected, a SAP caching mechanism will be used." \
       "This will result in lower SAP startup time, but you could end up " \
        "with items corresponding to legacy streams." )
97

Clément Stenac's avatar
Clément Stenac committed
98 99 100 101 102
/* Callbacks */
    static int  Open ( vlc_object_t * );
    static void Close( vlc_object_t * );
    static int  OpenDemux ( vlc_object_t * );
    static void CloseDemux ( vlc_object_t * );
103

104
vlc_module_begin();
Gildas Bazin's avatar
 
Gildas Bazin committed
105
    set_description( _("SAP interface") );
106

Gildas Bazin's avatar
 
Gildas Bazin committed
107 108 109
    add_string( "sap-addr", NULL, NULL,
                SAP_ADDR_TEXT, SAP_ADDR_LONGTEXT, VLC_TRUE );
    add_bool( "sap-ipv4", 1 , NULL,
110
               SAP_IPV4_TEXT,SAP_IPV4_LONGTEXT, VLC_TRUE );
Gildas Bazin's avatar
 
Gildas Bazin committed
111
    add_bool( "sap-ipv6", 0 , NULL,
112
              SAP_IPV6_TEXT, SAP_IPV6_LONGTEXT, VLC_TRUE );
Gildas Bazin's avatar
 
Gildas Bazin committed
113 114 115
    add_string( "sap-ipv6-scope", "8" , NULL,
                SAP_SCOPE_TEXT, SAP_SCOPE_LONGTEXT, VLC_TRUE);
    add_integer( "sap-timeout", 1800, NULL,
116
                 SAP_TIMEOUT_TEXT, SAP_TIMEOUT_LONGTEXT, VLC_TRUE );
117
    add_bool( "sap-parse", 1 , NULL,
118
               SAP_PARSE_TEXT,SAP_PARSE_LONGTEXT, VLC_TRUE );
119 120
    add_bool( "sap-strict", 0 , NULL,
               SAP_STRICT_TEXT,SAP_STRICT_LONGTEXT, VLC_TRUE );
Clément Stenac's avatar
Clément Stenac committed
121 122
    add_bool( "sap-cache", 0 , NULL,
               SAP_CACHE_TEXT,SAP_CACHE_LONGTEXT, VLC_TRUE );
123

124
    set_capability( "services_discovery", 0 );
125
    set_callbacks( Open, Close );
Clément Stenac's avatar
Clément Stenac committed
126

127
    add_submodule();
Clément Stenac's avatar
Clément Stenac committed
128
        set_description( _("SDP file parser for UDP") );
129 130
        add_shortcut( "sdp" );
        set_capability( "demux2", 51 );
Clément Stenac's avatar
Clément Stenac committed
131
        set_callbacks( OpenDemux, CloseDemux );
132 133
vlc_module_end();

Clément Stenac's avatar
Clément Stenac committed
134

135
/*****************************************************************************
Clément Stenac's avatar
Clément Stenac committed
136
 * Local structures
137 138
 *****************************************************************************/

Clément Stenac's avatar
Clément Stenac committed
139 140 141
typedef struct sdp_t sdp_t;
typedef struct attribute_t attribute_t;
typedef struct sap_announce_t sap_announce_t;
142

Clément Stenac's avatar
Clément Stenac committed
143 144 145 146
/* The structure that contains sdp information */
struct  sdp_t
{
    char *psz_sdp;
147

Clément Stenac's avatar
Clément Stenac committed
148 149
    /* s= field */
    char *psz_sessionname;
150

Clément Stenac's avatar
Clément Stenac committed
151 152 153
    /* Raw m= and c= fields */
    char *psz_connection;
    char *psz_media;
154

155 156 157 158 159
    /* o field */
    char *psz_username;
    char *psz_network_type;
    char *psz_address_type;
    char *psz_address;
160
    int64_t i_session_id;
161

Clément Stenac's avatar
Clément Stenac committed
162 163
    /* "computed" URI */
    char *psz_uri;
164

Clément Stenac's avatar
Clément Stenac committed
165
    int         i_in; /* IP version */
166

167
    int           i_media;
168

Clément Stenac's avatar
Clément Stenac committed
169 170
    int           i_attributes;
    attribute_t  **pp_attributes;
171
};
172

Clément Stenac's avatar
Clément Stenac committed
173
struct attribute_t
174 175 176 177
{
    char *psz_field;
    char *psz_value;
};
178

179 180
struct sap_announce_t
{
Clément Stenac's avatar
Clément Stenac committed
181
    mtime_t i_last;
Clément Stenac's avatar
Clément Stenac committed
182 183 184 185 186 187 188 189

    uint16_t    i_hash;
    uint32_t    i_source[4];

    /* SAP annnounces must only contain one SDP */
    sdp_t       *p_sdp;

    playlist_item_t *p_item;
190 191
};

192
struct services_discovery_sys_t
193
{
Clément Stenac's avatar
Clément Stenac committed
194 195 196
    /* Socket descriptors */
    int i_fd;
    int *pi_fd;
197

Clément Stenac's avatar
Clément Stenac committed
198 199
    /* playlist node */
    playlist_item_t *p_node;
200 201 202 203

    /* Table of announces */
    int i_announces;
    struct sap_announce_t **pp_announces;
204

Clément Stenac's avatar
Clément Stenac committed
205 206 207 208
    /* Modes */
    vlc_bool_t  b_strict;
    vlc_bool_t  b_parse;

209
    int i_timeout;
210
};
211

Clément Stenac's avatar
Clément Stenac committed
212 213 214
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
215

Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
216

Clément Stenac's avatar
Clément Stenac committed
217 218 219
/* Main functions */
    static int Demux( demux_t *p_demux );
    static int Control( demux_t *, int, va_list );
220
    static void Run    ( services_discovery_t *p_sd );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
221

Clément Stenac's avatar
Clément Stenac committed
222 223
/* Main parsing functions */
    static int ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp );
224 225 226 227
    static int ParseSAP( services_discovery_t *p_sd, uint8_t *p_buffer, int i_read );
    static sdp_t *  ParseSDP( vlc_object_t *p_sd, char* psz_sdp );
    static sap_announce_t *CreateAnnounce( services_discovery_t *, uint16_t, sdp_t * );
    static int RemoveAnnounce( services_discovery_t *p_sd, sap_announce_t *p_announce );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
228

Clément Stenac's avatar
Clément Stenac committed
229
/* Cache */
230 231
    static void CacheLoad( services_discovery_t *p_sd );
    static void CacheSave( services_discovery_t *p_sd );
Clément Stenac's avatar
Clément Stenac committed
232 233
/* Helper functions */
   static char *GetAttribute( sdp_t *p_sdp, const char *psz_search );
234
   static vlc_bool_t IsSameSession( sdp_t *p_sdp1, sdp_t *p_sdp2 );
235
   static int InitSocket( services_discovery_t *p_sd, char *psz_address, int i_port );
Clément Stenac's avatar
Clément Stenac committed
236 237
#ifdef HAVE_ZLIB_H
   static int Decompress( unsigned char *psz_src, unsigned char **_dst, int i_len );
Clément Stenac's avatar
Clément Stenac committed
238
    static void FreeSDP( sdp_t *p_sdp );
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
239 240
#endif

Clément Stenac's avatar
Clément Stenac committed
241 242 243
/* Detect multicast addresses */
static int  ismult( char * );

Clément Stenac's avatar
Clément Stenac committed
244 245
#define FREE( p ) \
    if( p ) { free( p ); (p) = NULL; }
246 247 248 249 250
/*****************************************************************************
 * Open: initialize and create stuff
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
251
    services_discovery_t *p_sd = ( services_discovery_t* )p_this;
Clément Stenac's avatar
Clément Stenac committed
252 253
    services_discovery_sys_t *p_sys  = (services_discovery_sys_t *)
                                malloc( sizeof( services_discovery_sys_t ) );
254

255
    playlist_t          *p_playlist;
Clément Stenac's avatar
Clément Stenac committed
256
    playlist_view_t     *p_view;
257
    char                *psz_addr;
258
    vlc_value_t         val;
259

260
    p_sys->i_timeout = config_GetInt( p_sd,"sap-timeout" );
261

262 263
    p_sd->pf_run = Run;
    p_sd->p_sys  = p_sys;
Clément Stenac's avatar
Clément Stenac committed
264 265 266 267

    p_sys->pi_fd = NULL;
    p_sys->i_fd = 0;

268
    p_sys->b_strict = config_GetInt( p_sd, "sap-strict");
Clément Stenac's avatar
Clément Stenac committed
269
    p_sys->b_parse = config_GetInt( p_sd, "sap-parse" );
Clément Stenac's avatar
Clément Stenac committed
270

Clément Stenac's avatar
Clément Stenac committed
271
    if( config_GetInt( p_sd, "sap-cache" ) )
Clément Stenac's avatar
Clément Stenac committed
272
    {
273
        CacheLoad( p_sd );
274
    }
275

276
    if( config_GetInt( p_sd, "sap-ipv4" ) )
Clément Stenac's avatar
Clément Stenac committed
277
    {
278
        InitSocket( p_sd, SAP_V4_ADDRESS, SAP_PORT );
Clément Stenac's avatar
Clément Stenac committed
279
    }
280
    if( config_GetInt( p_sd, "sap-ipv6" ) )
281
    {
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
        /* [ + 8x4+7*':' + ] */
        char psz_address[42];
        char c_scope;
        char *psz_scope = config_GetPsz( p_sd, "sap-ipv6-scope" );

        if( psz_scope == NULL || *psz_scope == '\0')
        {
            c_scope = '8';
        }
        else
        {
            c_scope = psz_scope[0];
        }
        snprintf( psz_address, 42, "[%s%c%s]", IPV6_ADDR_1, c_scope,
                                               IPV6_ADDR_2 );
        InitSocket( p_sd, psz_address, SAP_PORT );
298
    }
Clément Stenac's avatar
Clément Stenac committed
299

300 301 302 303 304
    psz_addr = config_GetPsz( p_sd, "sap-addr" );
    if( psz_addr && *psz_addr )
    {
        InitSocket( p_sd, psz_addr, SAP_PORT );
    }
Clément Stenac's avatar
Clément Stenac committed
305 306

    if( p_sys->i_fd == 0 )
307
    {
308
        msg_Err( p_sd, "unable to read on any address");
309
        return VLC_EGENERIC;
310
    }
311

Clément Stenac's avatar
Clément Stenac committed
312
    /* Create our playlist node */
313
    p_playlist = (playlist_t *)vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
314
                                                FIND_ANYWHERE );
Clément Stenac's avatar
Clément Stenac committed
315
    if( !p_playlist )
316
    {
317
        msg_Warn( p_sd, "unable to find playlist, cancelling SAP listening");
Clément Stenac's avatar
Clément Stenac committed
318
        return VLC_EGENERIC;
319
    }
320

Clément Stenac's avatar
Clément Stenac committed
321 322 323
    p_view = playlist_ViewFind( p_playlist, VIEW_CATEGORY );
    p_sys->p_node = playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
                                         _("SAP"), p_view->p_root );
324 325
    val.b_bool = VLC_TRUE;
    var_Set( p_playlist, "intf-change", val );
Clément Stenac's avatar
Clément Stenac committed
326 327 328

    vlc_object_release( p_playlist );

329 330 331
    p_sys->i_announces = 0;
    p_sys->pp_announces = NULL;

Clément Stenac's avatar
Clément Stenac committed
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
    return VLC_SUCCESS;
}

/*****************************************************************************
 * OpenDemux: initialize and create stuff
 *****************************************************************************/
static int OpenDemux( vlc_object_t *p_this )
{
    demux_t *p_demux = (demux_t *)p_this;
    uint8_t *p_peek;

    /* Probe for SDP */
    if( p_demux->s )
    {
        if( stream_Peek( p_demux->s, &p_peek, 7 ) < 7 )
        {
            msg_Err( p_demux, "cannot peek" );
            return VLC_EGENERIC;
        }
        if( strncmp( (char*)p_peek, "v=0\r\n", 5 ) &&
            strncmp( (char*)p_peek, "v=0\n", 4 ) &&
            ( p_peek[0] < 'a' || p_peek[0] > 'z' || p_peek[1] != '=' ) )
        {
            msg_Warn( p_demux, "SDP (UDP) module discarded" );
            return VLC_EGENERIC;
        }
    }

    p_demux->pf_control = Control;
    p_demux->pf_demux = Demux;
362 363 364 365

    return VLC_SUCCESS;
}

366 367 368 369
/*****************************************************************************
 * Close:
 *****************************************************************************/
static void Close( vlc_object_t *p_this )
370
{
371 372
    services_discovery_t *p_sd = ( services_discovery_t* )p_this;
    services_discovery_sys_t    *p_sys  = p_sd->p_sys;
Clément Stenac's avatar
Clément Stenac committed
373 374

    playlist_t *p_playlist;
Clément Stenac's avatar
free  
Clément Stenac committed
375
    int i;
376

Clément Stenac's avatar
Clément Stenac committed
377
    for( i = p_sys->i_fd-1 ; i >= 0 ; i-- )
378
    {
Clément Stenac's avatar
Clément Stenac committed
379
        net_Close( p_sys->pi_fd[i] );
380
    }
Clément Stenac's avatar
Clément Stenac committed
381

Clément Stenac's avatar
Clément Stenac committed
382
    if( config_GetInt( p_sd, "sap-cache" ) )
383
    {
384
        CacheSave( p_sd );
385
    }
386

Clément Stenac's avatar
Clément Stenac committed
387
    for( i = p_sys->i_announces  - 1;  i>= 0; i-- )
Clément Stenac's avatar
free  
Clément Stenac committed
388
    {
389
        RemoveAnnounce( p_sd, p_sys->pp_announces[i] );
Clément Stenac's avatar
free  
Clément Stenac committed
390 391
    }

Clément Stenac's avatar
Clément Stenac committed
392 393 394 395 396 397 398 399 400
    p_playlist = (playlist_t *) vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
                                                 FIND_ANYWHERE );

    if( p_playlist )
    {
        playlist_NodeDelete( p_playlist, p_sys->p_node, VLC_TRUE );
        vlc_object_release( p_playlist );
    }

401 402
    free( p_sys );
}
403

404
/*****************************************************************************
Clément Stenac's avatar
Clément Stenac committed
405 406 407 408 409 410 411 412 413
 * CloseDemux: Close the demuxer
 *****************************************************************************/
static void CloseDemux( vlc_object_t *p_this )
{

}

/*****************************************************************************
 * Run: main SAP thread
414 415 416
 *****************************************************************************
 * Listens to SAP packets, and sends them to packet_handle
 *****************************************************************************/
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
417
#define MAX_SAP_BUFFER 5000
418

419
static void Run( services_discovery_t *p_sd )
420
{
Clément Stenac's avatar
Clément Stenac committed
421 422 423 424
    uint8_t     *p_buffer;
    /* Dirty hack to slow down the startup of the sap interface */
    /* Unneeded now : our node is in no_select mode */
    //    msleep( 500000 );
425

426
    /* read SAP packets */
427
    while( !p_sd->b_die )
428
    {
429
        int i_read;
Clément Stenac's avatar
Clément Stenac committed
430 431 432 433
        p_buffer = (uint8_t *)malloc( MAX_SAP_BUFFER );

        if( !p_buffer )
        {
434 435
            msg_Err( p_sd, "out of memory");
            p_sd->b_die = VLC_TRUE;
Clément Stenac's avatar
Clément Stenac committed
436 437
            continue;
        }
Clément Stenac's avatar
Clément Stenac committed
438

439 440 441
        i_read = net_Select( p_sd, p_sd->p_sys->pi_fd, NULL,
                             p_sd->p_sys->i_fd, p_buffer,
                             MAX_SAP_BUFFER, 500000 );
Clément Stenac's avatar
Clément Stenac committed
442
#if 0
Clément Stenac's avatar
Clément Stenac committed
443
        /* Check for items that need deletion */
444
        for( i = 0 ; i< p_sd->p_sys->i_announces ; i++ )
Clément Stenac's avatar
Clément Stenac committed
445 446
        {
           struct sap_announce_t *p_announce;
447
           mtime_t i_timeout = ( mtime_t ) 1000000*p_sys->i_timeout;
448
           if( mdate() - p_sd->p_sys->pp_announces[i]->i_last > i_timeout )
Clément Stenac's avatar
Clément Stenac committed
449
           {
450 451 452
               msg_Dbg( p_sd,"Time out for %s, deleting (%i/%i)",
                        p_sd->p_sys->pp_announces[i]->psz_name,
                        i , p_sd->p_sys->i_announces );
Clément Stenac's avatar
Clément Stenac committed
453

Clément Stenac's avatar
Clément Stenac committed
454
             /* Remove the playlist item */
455
               p_playlist = vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
Clément Stenac's avatar
Clément Stenac committed
456 457 458 459
                              FIND_ANYWHERE );
               if( p_playlist )
               {
                   int i_pos = playlist_GetPositionById( p_playlist,
460
                              p_sd->p_sys->pp_announces[i]->i_id );
Clément Stenac's avatar
Clément Stenac committed
461
                   playlist_Delete( p_playlist, i_pos );
462
                   vlc_object_release( p_playlist );
Clément Stenac's avatar
Clément Stenac committed
463 464 465
               }

               /* Free the p_announce */
466
               p_announce =  p_sd->p_sys->pp_announces[i];
Clément Stenac's avatar
Clément Stenac committed
467 468 469 470 471 472
               if( p_announce->psz_name )
                  free(  p_announce->psz_name );
               if( p_announce->psz_uri )
                  free(  p_announce->psz_uri );

              /* Remove the sap_announce from the array */
473
              REMOVE_ELEM( p_sd->p_sys->pp_announces,
474
                           p_sd->p_sys->i_announces, i );
Clément Stenac's avatar
Clément Stenac committed
475 476 477 478 479

              free( p_announce );

           }
        }
Clément Stenac's avatar
Clément Stenac committed
480
#endif
Clément Stenac's avatar
Clément Stenac committed
481

482 483
        /* Minimum length is > 6 */
        if( i_read <= 6 )
484
        {
485 486
            if( i_read < 0 )
            {
487
                msg_Warn( p_sd, "socket read error" );
488
            }
Clément Stenac's avatar
Clément Stenac committed
489
            free( p_buffer );
490
            continue;
491 492
        }

Clément Stenac's avatar
Clément Stenac committed
493 494 495
        p_buffer[i_read] = '\0';

        /* Parse the packet */
496
        ParseSAP( p_sd, p_buffer, i_read );
Clément Stenac's avatar
Clément Stenac committed
497 498

        free( p_buffer );
Clément Stenac's avatar
Clément Stenac committed
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514
    }
}

/**********************************************************************
 * Demux: reads and demuxes data packets
 * Return -1 if error, 0 if EOF, 1 else
 **********************************************************************/
static int Demux( demux_t *p_demux )
{
   int i_max_sdp = 1024;
   int i_sdp = 0;
   char *psz_sdp = (char *)malloc( i_max_sdp );
   sdp_t *p_sdp;

   playlist_t *p_playlist;

515
   if( !psz_sdp ) return -1;
Clément Stenac's avatar
Clément Stenac committed
516

Clément Stenac's avatar
Clément Stenac committed
517 518 519 520 521
   /* Gather the complete sdp file */
   for( ;; )
   {
        int i_read = stream_Read( p_demux->s,
                                  &psz_sdp[i_sdp], i_max_sdp - i_sdp - 1 );
522

Clément Stenac's avatar
Clément Stenac committed
523 524
        if( i_read < 0 )

525
        {
Clément Stenac's avatar
Clément Stenac committed
526 527
            msg_Err( p_demux, "failed to read SDP" );
            return VLC_EGENERIC;
528
        }
Clément Stenac's avatar
Clément Stenac committed
529 530 531 532

        i_sdp += i_read;

        if( i_read < i_max_sdp - i_sdp - 1 )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
533
        {
Clément Stenac's avatar
Clément Stenac committed
534 535
            psz_sdp[i_sdp] = '\0';
            break;
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
536
        }
Clément Stenac's avatar
Clément Stenac committed
537

Clément Stenac's avatar
Clément Stenac committed
538 539 540 541 542 543
        i_max_sdp += 1000;
        psz_sdp = (uint8_t*)realloc( psz_sdp, i_max_sdp );
   }

   p_sdp = ParseSDP( VLC_OBJECT(p_demux), psz_sdp );

544 545 546 547 548
   if( !p_sdp )
   {
       msg_Warn( p_demux, "invalid SDP");
       return -1;
   }
Clément Stenac's avatar
Clément Stenac committed
549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564

   if( p_sdp->i_media > 1 )
   {
        return -1;
   }

   if( ParseConnection( VLC_OBJECT( p_demux ), p_sdp ) )
   {
       p_sdp->psz_uri = NULL;
   }

   if( p_sdp->psz_uri == NULL ) return VLC_EGENERIC;

   p_playlist = (playlist_t *)vlc_object_find( p_demux, VLC_OBJECT_PLAYLIST,
                                               FIND_ANYWHERE );

Clément Stenac's avatar
Clément Stenac committed
565 566
   p_playlist->status.p_item->i_flags |= PLAYLIST_DEL_FLAG;

Clément Stenac's avatar
Clément Stenac committed
567 568 569 570 571
   playlist_Add( p_playlist, p_sdp->psz_uri, p_sdp->psz_sessionname,
                 PLAYLIST_APPEND, PLAYLIST_END );

   vlc_object_release( p_playlist );

Clément Stenac's avatar
Clément Stenac committed
572 573 574
   FreeSDP( p_sdp );
   free( psz_sdp );

Clément Stenac's avatar
Clément Stenac committed
575
   return VLC_SUCCESS;
576 577
}

Clément Stenac's avatar
Clément Stenac committed
578 579 580 581 582 583 584 585
static int Control( demux_t *p_demux, int i_query, va_list args )
{
    return VLC_EGENERIC;
}

/**************************************************************
 * Local functions
 **************************************************************/
586

Clément Stenac's avatar
Clément Stenac committed
587
/* i_read is at least > 6 */
588
static int ParseSAP( services_discovery_t *p_sd, uint8_t *p_buffer, int i_read )
589
{
Clément Stenac's avatar
Clément Stenac committed
590 591
    int                 i_version, i_address_type, i_hash, i;
    uint8_t             *psz_sdp;
Clément Stenac's avatar
Clément Stenac committed
592
    uint8_t             *psz_initial_sdp;
Clément Stenac's avatar
Clément Stenac committed
593 594 595 596 597 598 599 600 601 602 603 604
    sdp_t               *p_sdp;
    vlc_bool_t          b_compressed;
    vlc_bool_t          b_need_delete = VLC_FALSE;
#ifdef HAVE_ZLIB_H
    int                 i_decompressed_size;
    uint8_t             *p_decompressed_buffer;
#endif

    /* First, check the sap announce is correct */
    i_version = p_buffer[0] >> 5;

    if( i_version != 1 )
605
    {
606
       msg_Dbg( p_sd, "strange sap version %d found", i_version );
Clément Stenac's avatar
Clément Stenac committed
607
    }
608

Clément Stenac's avatar
Clément Stenac committed
609
    i_address_type = p_buffer[0] & 0x10;
610

Clément Stenac's avatar
Clément Stenac committed
611 612
    if( (p_buffer[0] & 0x08) != 0 )
    {
613
        msg_Dbg( p_sd, "reserved bit incorrectly set" );
Clément Stenac's avatar
Clément Stenac committed
614
        return VLC_EGENERIC;
615
    }
Clément Stenac's avatar
Clément Stenac committed
616 617

    if( (p_buffer[0] & 0x04) != 0 )
618
    {
619
        msg_Dbg( p_sd, "session deletion packet" );
Clément Stenac's avatar
Clément Stenac committed
620
        b_need_delete = VLC_TRUE;
621
    }
622

Clément Stenac's avatar
Clément Stenac committed
623 624
    if( p_buffer[0] & 0x02  )
    {
625
        msg_Dbg( p_sd, "encrypted packet, unsupported" );
Clément Stenac's avatar
Clément Stenac committed
626 627
        return VLC_EGENERIC;
    }
628

Clément Stenac's avatar
Clément Stenac committed
629
    b_compressed = p_buffer[0] & 0x01;
630

Clément Stenac's avatar
Clément Stenac committed
631
    i_hash = ( p_buffer[2] << 8 ) + p_buffer[3];
632

633
    if( p_sd->p_sys->b_strict && i_hash == 0 )
634
    {
635
        msg_Dbg( p_sd, "strict mode, discarding announce with null id hash");
Clément Stenac's avatar
Clément Stenac committed
636 637 638 639
        return VLC_EGENERIC;
    }

    psz_sdp  = &p_buffer[4];
Clément Stenac's avatar
Clément Stenac committed
640
    psz_initial_sdp = psz_sdp;
Clément Stenac's avatar
Clément Stenac committed
641 642 643 644

    if( i_address_type == 0 ) /* ipv4 source address */
    {
        psz_sdp += 4;
Clément Stenac's avatar
Clément Stenac committed
645 646 647 648 649
        if( i_read <= 9 )
        {
            msg_Warn( p_sd,"too short SAP packet\n" );
            return VLC_EGENERIC;
        }
Clément Stenac's avatar
Clément Stenac committed
650 651 652 653
    }
    else /* ipv6 source address */
    {
        psz_sdp += 16;
Clément Stenac's avatar
Clément Stenac committed
654 655 656 657 658
        if( i_read <= 21 )
        {
            msg_Warn( p_sd,"too short SAP packet\n" );
            return VLC_EGENERIC;
        }
Clément Stenac's avatar
Clément Stenac committed
659 660 661 662 663 664 665 666
    }

    if( b_compressed )
    {
#ifdef HAVE_ZLIB_H
        i_decompressed_size = Decompress( psz_sdp,
                   &p_decompressed_buffer,i_read - ( psz_sdp - p_buffer ) );
        if( i_decompressed_size > 0 && i_decompressed_size < MAX_SAP_BUFFER )
667
        {
Clément Stenac's avatar
Clément Stenac committed
668 669 670
            memcpy( psz_sdp, p_decompressed_buffer, i_decompressed_size );
            psz_sdp[i_decompressed_size] = '\0';
            free( p_decompressed_buffer );
671
        }
Clément Stenac's avatar
Clément Stenac committed
672
#else
673
        msg_Warn( p_sd, "Ignoring compressed sap packet" );
Clément Stenac's avatar
Clément Stenac committed
674 675
        return VLC_EGENERIC;
#endif
676
    }
Clément Stenac's avatar
Clément Stenac committed
677 678

    /* Add the size of authentification info */
Clément Stenac's avatar
Clément Stenac committed
679 680 681 682 683
    if( i_read < p_buffer[1] + (psz_sdp - psz_initial_sdp ) )
    {
        msg_Warn( p_sd, "too short SAP packet\n");
        return VLC_EGENERIC;
    }
Clément Stenac's avatar
Clément Stenac committed
684 685 686 687 688
    psz_sdp += p_buffer[1];

    /* Skip payload type */
    /* Handle announces without \0 between SAP and SDP */
    while( *psz_sdp != '\0' && ( psz_sdp[0] != 'v' && psz_sdp[1] != '=' ) )
689
    {
Clément Stenac's avatar
Clément Stenac committed
690 691 692 693
        if( psz_sdp - psz_initial_sdp >= i_read - 5 )
        {
            msg_Warn( p_sd, "empty SDP ?");
        }
Clément Stenac's avatar
Clément Stenac committed
694
        psz_sdp++;
695 696
    }

Clément Stenac's avatar
Clément Stenac committed
697 698 699 700
    if( *psz_sdp == '\0' )
    {
        psz_sdp++;
    }
701

Clément Stenac's avatar
Clément Stenac committed
702
    /* Parse SDP info */
703
    p_sdp = ParseSDP( VLC_OBJECT(p_sd), psz_sdp );
704

Clément Stenac's avatar
Clément Stenac committed
705
    if( p_sdp == NULL )
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
706
    {
Clément Stenac's avatar
Clément Stenac committed
707 708 709 710 711 712
        return VLC_EGENERIC;
    }

    /* Decide whether we should add a playlist item for this SDP */

    /* Multi-media or no-parse -> pass to LIVE.COM */
713
    if( p_sdp->i_media > 1 || p_sd->p_sys->b_parse == VLC_FALSE )
Clément Stenac's avatar
Clément Stenac committed
714 715 716 717 718 719
    {
        asprintf( &p_sdp->psz_uri, "sdp://%s", p_sdp->psz_sdp );
    }
    else
    {
        /* Parse connection information (c= & m= ) */
720
        if( ParseConnection( VLC_OBJECT(p_sd), p_sdp ) )
Clément Stenac's avatar
Clément Stenac committed
721 722 723 724 725 726 727
        {
            p_sdp->psz_uri = NULL;
        }
    }

    if( p_sdp->psz_uri == NULL ) return VLC_EGENERIC;

728
    for( i = 0 ; i< p_sd->p_sys->i_announces ; i++ )
Clément Stenac's avatar
Clément Stenac committed
729 730 731
    {
        /* FIXME: slow */
        /* FIXME: we create a new announce each time the sdp changes */
732
        if( IsSameSession( p_sd->p_sys->pp_announces[i]->p_sdp, p_sdp ) )
733
        {
Clément Stenac's avatar
Clément Stenac committed
734 735
            if( b_need_delete )
            {
736
                RemoveAnnounce( p_sd, p_sd->p_sys->pp_announces[i]);
Clément Stenac's avatar
Clément Stenac committed
737 738 739
                return VLC_SUCCESS;
            }
            else
740
            {
741
                p_sd->p_sys->pp_announces[i]->i_last = mdate();
Clément Stenac's avatar
Clément Stenac committed
742 743
                FreeSDP( p_sdp );
                return VLC_SUCCESS;
744 745
            }
        }
Clément Stenac's avatar
Clément Stenac committed
746 747 748 749
    }
    /* Add item */
    if( p_sdp->i_media > 1 )
    {
750
        msg_Dbg( p_sd, "passing to LIVE.COM" );
Clément Stenac's avatar
Clément Stenac committed
751
    }
Clément Stenac's avatar
Clément Stenac committed
752

753
    CreateAnnounce( p_sd, i_hash, p_sdp );
Clément Stenac's avatar
Clément Stenac committed
754 755 756 757

    return VLC_SUCCESS;
}

758
sap_announce_t *CreateAnnounce( services_discovery_t *p_sd, uint16_t i_hash,
Clément Stenac's avatar
Clément Stenac committed
759 760 761 762 763 764 765 766 767
                                sdp_t *p_sdp )
{
    playlist_t          *p_playlist;
    playlist_item_t     *p_item, *p_child;
    char                *psz_value;
    sap_announce_t *p_sap = (sap_announce_t *)malloc(
                                        sizeof(sap_announce_t ) );
    if( !p_sap )
    {
768 769
        msg_Err( p_sd, "out of memory");
        p_sd->b_die = VLC_TRUE;
Clément Stenac's avatar
Clément Stenac committed
770 771 772 773 774 775
        return NULL;
    }
    p_sap->i_last = mdate();
    p_sap->i_hash = i_hash;
    p_sap->p_sdp = p_sdp;
    p_sap->p_item = NULL;
776

Clément Stenac's avatar
Clément Stenac committed
777
    /* Create the playlist item here */
778
    p_item = playlist_ItemNew( p_sd, p_sap->p_sdp->psz_uri,
Clément Stenac's avatar
Clément Stenac committed
779
                               p_sap->p_sdp->psz_sessionname );
Clément Stenac's avatar
Clément Stenac committed
780

Clément Stenac's avatar
Clément Stenac committed
781 782 783
    if( !p_item )
    {
        return NULL;
784 785
    }

Clément Stenac's avatar
Clément Stenac committed
786 787 788 789 790 791
    psz_value = GetAttribute( p_sap->p_sdp, "x-plgroup" );

    if( psz_value == NULL )
    {
        psz_value = GetAttribute( p_sap->p_sdp, "plgroup" );
    }
792

793
    p_playlist = (playlist_t *)vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
Clément Stenac's avatar
Clément Stenac committed
794 795
                                                FIND_ANYWHERE );
    if( !p_playlist )
796
    {
797
        msg_Err( p_sd, "playlist not found" );
Clément Stenac's avatar
Clément Stenac committed
798 799
        FREE( psz_value );
        free( p_sap );
Clément Stenac's avatar
Clément Stenac committed
800 801
        return NULL;
    }
802

Clément Stenac's avatar
Clément Stenac committed
803 804
    if( psz_value != NULL )
    {
805
        p_child = playlist_ChildSearchName( p_sd->p_sys->p_node, psz_value );
806

Clément Stenac's avatar
Clément Stenac committed
807
        if( p_child == NULL )
808
        {
Clément Stenac's avatar
Clément Stenac committed
809
            p_child = playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
810
                                           psz_value, p_sd->p_sys->p_node );
811
        }
Clément Stenac's avatar
Clément Stenac committed
812 813 814
    }
    else
    {
815
        p_child = p_sd->p_sys->p_node;
Clément Stenac's avatar
Clément Stenac committed
816 817 818 819 820 821 822 823 824 825 826
    }

    p_item->i_flags &= ~PLAYLIST_SKIP_FLAG;

    playlist_NodeAddItem( p_playlist, p_item, VIEW_CATEGORY, p_child,
                          PLAYLIST_APPEND, PLAYLIST_END );

    vlc_object_release( p_playlist );

    p_sap->p_item = p_item;

827
    TAB_APPEND( p_sd->p_sys->i_announces,
828
                p_sd->p_sys->pp_announces, p_sap );
Clément Stenac's avatar
Clément Stenac committed
829 830 831

    return p_sap;
}
Laurent Aimar's avatar
Laurent Aimar committed
832

Clément Stenac's avatar
Clément Stenac committed
833 834 835
static char *GetAttribute( sdp_t *p_sdp, const char *psz_search )
{
    int i;
836

Clément Stenac's avatar
Clément Stenac committed
837 838 839 840
    for( i = 0 ; i< p_sdp->i_attributes; i++ )
    {
        if( !strncmp( p_sdp->pp_attributes[i]->psz_field, psz_search,
                      strlen( p_sdp->pp_attributes[i]->psz_field ) ) )
841
        {
Clément Stenac's avatar
Clément Stenac committed
842
            return p_sdp->pp_attributes[i]->psz_value;
843
        }
Clément Stenac's avatar
Clément Stenac committed
844 845 846
    }
    return NULL;
}
847

Clément Stenac's avatar
Clément Stenac committed
848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865

/* Fill p_sdp->psz_uri */
static int ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp )
{
    char *psz_eof;
    char *psz_parse;
    char *psz_uri = NULL;
    char *psz_proto = NULL;
    int i_port = 0;

    /* Parse c= field */
    if( p_sdp->psz_connection )
    {
        psz_parse = p_sdp->psz_connection;

        psz_eof = strchr( psz_parse, ' ' );

        if( psz_eof )
866
        {
Clément Stenac's avatar
Clément Stenac committed
867 868
            *psz_eof = '\0';
            psz_parse = psz_eof + 1;
869 870 871
        }
        else
        {
Clément Stenac's avatar
Clément Stenac committed
872 873
            msg_Warn( p_obj, "unable to parse c field (1)");
            return VLC_EGENERIC;
874
        }
875

Clément Stenac's avatar
Clément Stenac committed
876
        psz_eof = strchr( psz_parse, ' ' );