sap.c 39 KB
Newer Older
1
2
3
/*****************************************************************************
 * sap.c :  SAP interface module
 *****************************************************************************
zorglub's avatar
zorglub committed
4
 * Copyright (C) 2004 VideoLAN
Carlo Calabrò's avatar
Carlo Calabrò committed
5
 * $Id$
6
 *
zorglub's avatar
zorglub committed
7
 * Authors: Clment Stenac <zorglub@videolan.org>
zorglub's avatar
zorglub 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.
 *****************************************************************************/

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

#include <vlc/vlc.h>
#include <vlc/intf.h>
zorglub's avatar
zorglub committed
31

32
#include <vlc/input.h>
33

zorglub's avatar
zorglub 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

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

zorglub's avatar
zorglub committed
49
50
51
52
/************************************************************************
 * Macros and definitions
 ************************************************************************/

53
54
#define MAX_LINE_LENGTH 256

55
/* SAP is always on that port */
zorglub's avatar
zorglub committed
56
#define SAP_PORT 9875
zorglub's avatar
zorglub 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_( \
zorglub's avatar
zorglub 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" )
zorglub's avatar
zorglub 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." )
zorglub's avatar
zorglub committed
97

zorglub's avatar
zorglub 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();
gbazin's avatar
   
gbazin committed
105
    set_description( _("SAP interface") );
zorglub's avatar
zorglub committed
106
107
    set_category( CAT_PLAYLIST );
    set_subcategory( SUBCAT_PLAYLIST_SD );
108

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

zorglub's avatar
zorglub committed
126
    set_capability( "services_discovery", 0 );
127
    set_callbacks( Open, Close );
zorglub's avatar
zorglub committed
128

129
    add_submodule();
zorglub's avatar
zorglub committed
130
        set_description( _("SDP file parser for UDP") );
131
132
        add_shortcut( "sdp" );
        set_capability( "demux2", 51 );
zorglub's avatar
zorglub committed
133
        set_callbacks( OpenDemux, CloseDemux );
134
135
vlc_module_end();

zorglub's avatar
zorglub committed
136

137
/*****************************************************************************
zorglub's avatar
zorglub committed
138
 * Local structures
139
140
 *****************************************************************************/

zorglub's avatar
zorglub committed
141
142
143
typedef struct sdp_t sdp_t;
typedef struct attribute_t attribute_t;
typedef struct sap_announce_t sap_announce_t;
144

zorglub's avatar
zorglub committed
145
146
147
148
/* The structure that contains sdp information */
struct  sdp_t
{
    char *psz_sdp;
149

zorglub's avatar
zorglub committed
150
151
    /* s= field */
    char *psz_sessionname;
152

zorglub's avatar
zorglub committed
153
154
155
    /* Raw m= and c= fields */
    char *psz_connection;
    char *psz_media;
156

157
158
159
160
161
    /* o field */
    char *psz_username;
    char *psz_network_type;
    char *psz_address_type;
    char *psz_address;
zorglub's avatar
zorglub committed
162
    int64_t i_session_id;
163

zorglub's avatar
zorglub committed
164
165
    /* "computed" URI */
    char *psz_uri;
166

zorglub's avatar
zorglub committed
167
    int         i_in; /* IP version */
zorglub's avatar
zorglub committed
168

169
    int           i_media;
170
    int           i_media_type;
171

zorglub's avatar
zorglub committed
172
173
    int           i_attributes;
    attribute_t  **pp_attributes;
174
};
zorglub's avatar
zorglub committed
175

zorglub's avatar
zorglub committed
176
struct attribute_t
177
178
179
180
{
    char *psz_field;
    char *psz_value;
};
zorglub's avatar
zorglub committed
181

182
183
struct sap_announce_t
{
zorglub's avatar
zorglub committed
184
    mtime_t i_last;
zorglub's avatar
zorglub committed
185
186
187
188
189
190
191
192

    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;
193
194
};

zorglub's avatar
zorglub committed
195
struct services_discovery_sys_t
196
{
zorglub's avatar
zorglub committed
197
198
199
    /* Socket descriptors */
    int i_fd;
    int *pi_fd;
200

zorglub's avatar
zorglub committed
201
202
    /* playlist node */
    playlist_item_t *p_node;
203
204
205
206

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

zorglub's avatar
zorglub committed
208
209
210
211
    /* Modes */
    vlc_bool_t  b_strict;
    vlc_bool_t  b_parse;

212
    int i_timeout;
213
};
214

215
216
217
218
219
struct demux_sys_t
{
    sdp_t *p_sdp;
};

zorglub's avatar
zorglub committed
220
221
222
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
223

sigmunau's avatar
sigmunau committed
224

zorglub's avatar
zorglub committed
225
226
227
/* Main functions */
    static int Demux( demux_t *p_demux );
    static int Control( demux_t *, int, va_list );
zorglub's avatar
zorglub committed
228
    static void Run    ( services_discovery_t *p_sd );
sigmunau's avatar
sigmunau committed
229

zorglub's avatar
zorglub committed
230
231
/* Main parsing functions */
    static int ParseConnection( vlc_object_t *p_obj, sdp_t *p_sdp );
zorglub's avatar
zorglub committed
232
233
234
235
    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 );
sigmunau's avatar
sigmunau committed
236

zorglub's avatar
zorglub committed
237
/* Cache */
zorglub's avatar
zorglub committed
238
239
    static void CacheLoad( services_discovery_t *p_sd );
    static void CacheSave( services_discovery_t *p_sd );
zorglub's avatar
zorglub committed
240
241
/* Helper functions */
   static char *GetAttribute( sdp_t *p_sdp, const char *psz_search );
242
   static vlc_bool_t IsSameSession( sdp_t *p_sdp1, sdp_t *p_sdp2 );
zorglub's avatar
zorglub committed
243
   static int InitSocket( services_discovery_t *p_sd, char *psz_address, int i_port );
zorglub's avatar
zorglub committed
244
245
#ifdef HAVE_ZLIB_H
   static int Decompress( unsigned char *psz_src, unsigned char **_dst, int i_len );
zorglub's avatar
zorglub committed
246
    static void FreeSDP( sdp_t *p_sdp );
sigmunau's avatar
sigmunau committed
247
248
#endif

zorglub's avatar
zorglub committed
249
250
251
/* Detect multicast addresses */
static int  ismult( char * );

zorglub's avatar
zorglub committed
252
253
#define FREE( p ) \
    if( p ) { free( p ); (p) = NULL; }
254
255
256
257
258
/*****************************************************************************
 * Open: initialize and create stuff
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
zorglub's avatar
zorglub committed
259
    services_discovery_t *p_sd = ( services_discovery_t* )p_this;
zorglub's avatar
zorglub committed
260
261
    services_discovery_sys_t *p_sys  = (services_discovery_sys_t *)
                                malloc( sizeof( services_discovery_sys_t ) );
262

263
    playlist_t          *p_playlist;
zorglub's avatar
zorglub committed
264
    playlist_view_t     *p_view;
265
    char                *psz_addr;
266
    vlc_value_t         val;
267

zorglub's avatar
zorglub committed
268
    p_sys->i_timeout = config_GetInt( p_sd,"sap-timeout" );
269

zorglub's avatar
zorglub committed
270
271
    p_sd->pf_run = Run;
    p_sd->p_sys  = p_sys;
zorglub's avatar
zorglub committed
272
273
274
275

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

276
    p_sys->b_strict = config_GetInt( p_sd, "sap-strict");
zorglub's avatar
zorglub committed
277
    p_sys->b_parse = config_GetInt( p_sd, "sap-parse" );
zorglub's avatar
zorglub committed
278

zorglub's avatar
zorglub committed
279
    if( config_GetInt( p_sd, "sap-cache" ) )
zorglub's avatar
zorglub committed
280
    {
zorglub's avatar
zorglub committed
281
        CacheLoad( p_sd );
282
    }
283

zorglub's avatar
zorglub committed
284
    if( config_GetInt( p_sd, "sap-ipv4" ) )
zorglub's avatar
zorglub committed
285
    {
zorglub's avatar
zorglub committed
286
        InitSocket( p_sd, SAP_V4_ADDRESS, SAP_PORT );
zorglub's avatar
zorglub committed
287
    }
zorglub's avatar
zorglub committed
288
    if( config_GetInt( p_sd, "sap-ipv6" ) )
289
    {
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
        /* [ + 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 );
306
    }
zorglub's avatar
zorglub committed
307

308
309
310
311
312
    psz_addr = config_GetPsz( p_sd, "sap-addr" );
    if( psz_addr && *psz_addr )
    {
        InitSocket( p_sd, psz_addr, SAP_PORT );
    }
zorglub's avatar
zorglub committed
313
314

    if( p_sys->i_fd == 0 )
315
    {
zorglub's avatar
zorglub committed
316
        msg_Err( p_sd, "unable to read on any address");
317
        return VLC_EGENERIC;
318
    }
319

zorglub's avatar
zorglub committed
320
    /* Create our playlist node */
zorglub's avatar
zorglub committed
321
    p_playlist = (playlist_t *)vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
322
                                                FIND_ANYWHERE );
zorglub's avatar
zorglub committed
323
    if( !p_playlist )
324
    {
zorglub's avatar
zorglub committed
325
        msg_Warn( p_sd, "unable to find playlist, cancelling SAP listening");
zorglub's avatar
zorglub committed
326
        return VLC_EGENERIC;
327
    }
328

zorglub's avatar
zorglub committed
329
330
331
    p_view = playlist_ViewFind( p_playlist, VIEW_CATEGORY );
    p_sys->p_node = playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
                                         _("SAP"), p_view->p_root );
zorglub's avatar
zorglub committed
332
    p_sys->p_node->i_flags |= PLAYLIST_RO_FLAG;
333
334
    val.b_bool = VLC_TRUE;
    var_Set( p_playlist, "intf-change", val );
zorglub's avatar
zorglub committed
335
336
337

    vlc_object_release( p_playlist );

338
339
340
    p_sys->i_announces = 0;
    p_sys->pp_announces = NULL;

zorglub's avatar
zorglub committed
341
342
343
344
345
346
347
348
349
350
    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;
351
352
353
354
    int i_max_sdp = 1024;
    int i_sdp = 0;
    char *psz_sdp = (char *)malloc( i_max_sdp );
    sdp_t *p_sdp = NULL;
zorglub's avatar
zorglub committed
355

356
357
358
359
360
    if( !psz_sdp )
    {
        return VLC_EGENERIC;
    }

zorglub's avatar
zorglub committed
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
    /* 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;
        }
    }

378
379
380
381
382
    /* Gather the complete sdp file */
    for( ;; )
    {
        int i_read = stream_Read( p_demux->s,
                                  &psz_sdp[i_sdp], i_max_sdp - i_sdp - 1 );
383

384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
        if( i_read < 0 )
        {
            msg_Err( p_demux, "failed to read SDP" );
            goto error;
        }

        i_sdp += i_read;

        if( i_read < i_max_sdp - i_sdp - 1 )
        {
            psz_sdp[i_sdp] = '\0';
            break;
        }

        i_max_sdp += 1000;
        psz_sdp = (uint8_t*)realloc( psz_sdp, i_max_sdp );
    }
401

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

404
405
406
407
408
    if( !p_sdp )
    {
        msg_Warn( p_demux, "invalid SDP");
        goto error;
    }
409

410
411
412
413
    if( p_sdp->i_media > 1 )
    {
        goto error;
    }
414

415
416
417
418
419
420
    if( ParseConnection( VLC_OBJECT( p_demux ), p_sdp ) )
    {
        p_sdp->psz_uri = NULL;
    }
    if( p_sdp->i_media_type != 33 && p_sdp->i_media_type != 32 && p_sdp->i_media_type != 14 )
        goto error;
421

422
423
424
425
    if( p_sdp->psz_uri == NULL ) goto error;

    p_demux->p_sys = (demux_sys_t *)malloc( sizeof(demux_sys_t) );
    p_demux->p_sys->p_sdp = p_sdp;
zorglub's avatar
zorglub committed
426
427
    p_demux->pf_control = Control;
    p_demux->pf_demux = Demux;
428

429
    free( psz_sdp );
430
    return VLC_SUCCESS;
431

432
433
434
435
436
error:
    free( psz_sdp );
    if( p_sdp ) FreeSDP( p_sdp );
    stream_Seek( p_demux->s, 0 );
    return VLC_EGENERIC;    
437
438
}

439
440
441
442
/*****************************************************************************
 * Close:
 *****************************************************************************/
static void Close( vlc_object_t *p_this )
443
{
zorglub's avatar
zorglub committed
444
445
    services_discovery_t *p_sd = ( services_discovery_t* )p_this;
    services_discovery_sys_t    *p_sys  = p_sd->p_sys;
zorglub's avatar
zorglub committed
446
447

    playlist_t *p_playlist;
zorglub's avatar
free    
zorglub committed
448
    int i;
449

zorglub's avatar
zorglub committed
450
    for( i = p_sys->i_fd-1 ; i >= 0 ; i-- )
451
    {
zorglub's avatar
zorglub committed
452
        net_Close( p_sys->pi_fd[i] );
453
    }
zorglub's avatar
zorglub committed
454

zorglub's avatar
zorglub committed
455
    if( config_GetInt( p_sd, "sap-cache" ) )
456
    {
zorglub's avatar
zorglub committed
457
        CacheSave( p_sd );
458
    }
459

zorglub's avatar
zorglub committed
460
    for( i = p_sys->i_announces  - 1;  i>= 0; i-- )
zorglub's avatar
free    
zorglub committed
461
    {
zorglub's avatar
zorglub committed
462
        RemoveAnnounce( p_sd, p_sys->pp_announces[i] );
zorglub's avatar
free    
zorglub committed
463
464
    }

zorglub's avatar
zorglub committed
465
466
467
468
469
470
471
472
473
    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 );
    }

474
475
    free( p_sys );
}
476

477
/*****************************************************************************
zorglub's avatar
zorglub committed
478
479
480
481
482
483
484
485
486
 * CloseDemux: Close the demuxer
 *****************************************************************************/
static void CloseDemux( vlc_object_t *p_this )
{

}

/*****************************************************************************
 * Run: main SAP thread
487
488
489
 *****************************************************************************
 * Listens to SAP packets, and sends them to packet_handle
 *****************************************************************************/
sigmunau's avatar
sigmunau committed
490
#define MAX_SAP_BUFFER 5000
491

zorglub's avatar
zorglub committed
492
static void Run( services_discovery_t *p_sd )
493
{
zorglub's avatar
zorglub committed
494
495
496
497
    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 );
498

499
    /* read SAP packets */
zorglub's avatar
zorglub committed
500
    while( !p_sd->b_die )
501
    {
502
        int i_read;
zorglub's avatar
zorglub committed
503
504
505
506
        p_buffer = (uint8_t *)malloc( MAX_SAP_BUFFER );

        if( !p_buffer )
        {
zorglub's avatar
zorglub committed
507
508
            msg_Err( p_sd, "out of memory");
            p_sd->b_die = VLC_TRUE;
zorglub's avatar
zorglub committed
509
510
            continue;
        }
zorglub's avatar
zorglub committed
511

512
513
514
        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 );
zorglub's avatar
zorglub committed
515
#if 0
zorglub's avatar
zorglub committed
516
        /* Check for items that need deletion */
zorglub's avatar
zorglub committed
517
        for( i = 0 ; i< p_sd->p_sys->i_announces ; i++ )
zorglub's avatar
zorglub committed
518
519
        {
           struct sap_announce_t *p_announce;
520
           mtime_t i_timeout = ( mtime_t ) 1000000*p_sys->i_timeout;
zorglub's avatar
zorglub committed
521
           if( mdate() - p_sd->p_sys->pp_announces[i]->i_last > i_timeout )
zorglub's avatar
zorglub committed
522
           {
zorglub's avatar
zorglub committed
523
524
525
               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 );
zorglub's avatar
zorglub committed
526

zorglub's avatar
zorglub committed
527
             /* Remove the playlist item */
zorglub's avatar
zorglub committed
528
               p_playlist = vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
zorglub's avatar
zorglub committed
529
530
531
532
                              FIND_ANYWHERE );
               if( p_playlist )
               {
                   int i_pos = playlist_GetPositionById( p_playlist,
zorglub's avatar
zorglub committed
533
                              p_sd->p_sys->pp_announces[i]->i_id );
zorglub's avatar
zorglub committed
534
                   playlist_Delete( p_playlist, i_pos );
535
                   vlc_object_release( p_playlist );
zorglub's avatar
zorglub committed
536
537
538
               }

               /* Free the p_announce */
zorglub's avatar
zorglub committed
539
               p_announce =  p_sd->p_sys->pp_announces[i];
zorglub's avatar
zorglub committed
540
541
542
543
544
545
               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 */
zorglub's avatar
zorglub committed
546
              REMOVE_ELEM( p_sd->p_sys->pp_announces,
547
                           p_sd->p_sys->i_announces, i );
zorglub's avatar
zorglub committed
548
549
550
551
552

              free( p_announce );

           }
        }
zorglub's avatar
zorglub committed
553
#endif
zorglub's avatar
zorglub committed
554

555
556
        /* Minimum length is > 6 */
        if( i_read <= 6 )
557
        {
558
559
            if( i_read < 0 )
            {
zorglub's avatar
zorglub committed
560
                msg_Warn( p_sd, "socket read error" );
561
            }
zorglub's avatar
zorglub committed
562
            free( p_buffer );
563
            continue;
564
565
        }

zorglub's avatar
zorglub committed
566
567
568
        p_buffer[i_read] = '\0';

        /* Parse the packet */
zorglub's avatar
zorglub committed
569
        ParseSAP( p_sd, p_buffer, i_read );
zorglub's avatar
zorglub committed
570
571

        free( p_buffer );
zorglub's avatar
zorglub committed
572
573
574
575
576
577
578
579
580
    }
}

/**********************************************************************
 * Demux: reads and demuxes data packets
 * Return -1 if error, 0 if EOF, 1 else
 **********************************************************************/
static int Demux( demux_t *p_demux )
{
581
582
    sdp_t *p_sdp = p_demux->p_sys->p_sdp;
    playlist_t *p_playlist;
zorglub's avatar
zorglub committed
583
584
585
586

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

zorglub's avatar
zorglub committed
587
588
   p_playlist->status.p_item->i_flags |= PLAYLIST_DEL_FLAG;

zorglub's avatar
zorglub committed
589
590
591
592
   playlist_Add( p_playlist, p_sdp->psz_uri, p_sdp->psz_sessionname,
                 PLAYLIST_APPEND, PLAYLIST_END );

   vlc_object_release( p_playlist );
593
    if( p_sdp ) FreeSDP( p_sdp );
zorglub's avatar
zorglub committed
594
595

   return VLC_SUCCESS;
596
597
}

zorglub's avatar
zorglub committed
598
599
600
601
602
603
604
605
static int Control( demux_t *p_demux, int i_query, va_list args )
{
    return VLC_EGENERIC;
}

/**************************************************************
 * Local functions
 **************************************************************/
606

zorglub's avatar
zorglub committed
607
/* i_read is at least > 6 */
zorglub's avatar
zorglub committed
608
static int ParseSAP( services_discovery_t *p_sd, uint8_t *p_buffer, int i_read )
609
{
zorglub's avatar
zorglub committed
610
611
    int                 i_version, i_address_type, i_hash, i;
    uint8_t             *psz_sdp;
zorglub's avatar
zorglub committed
612
    uint8_t             *psz_initial_sdp;
zorglub's avatar
zorglub committed
613
614
615
616
617
618
619
    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
620
    uint8_t             *psz_foo;
zorglub's avatar
zorglub committed
621
622
623
624
625

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

    if( i_version != 1 )
626
    {
zorglub's avatar
zorglub committed
627
       msg_Dbg( p_sd, "strange sap version %d found", i_version );
zorglub's avatar
zorglub committed
628
    }
629

zorglub's avatar
zorglub committed
630
    i_address_type = p_buffer[0] & 0x10;
631

zorglub's avatar
zorglub committed
632
633
    if( (p_buffer[0] & 0x08) != 0 )
    {
zorglub's avatar
zorglub committed
634
        msg_Dbg( p_sd, "reserved bit incorrectly set" );
zorglub's avatar
zorglub committed
635
        return VLC_EGENERIC;
636
    }
zorglub's avatar
zorglub committed
637
638

    if( (p_buffer[0] & 0x04) != 0 )
639
    {
zorglub's avatar
zorglub committed
640
        msg_Dbg( p_sd, "session deletion packet" );
zorglub's avatar
zorglub committed
641
        b_need_delete = VLC_TRUE;
642
    }
643

zorglub's avatar
zorglub committed
644
645
    if( p_buffer[0] & 0x02  )
    {
zorglub's avatar
zorglub committed
646
        msg_Dbg( p_sd, "encrypted packet, unsupported" );
zorglub's avatar
zorglub committed
647
648
        return VLC_EGENERIC;
    }
649

zorglub's avatar
zorglub committed
650
    b_compressed = p_buffer[0] & 0x01;
651

zorglub's avatar
zorglub committed
652
    i_hash = ( p_buffer[2] << 8 ) + p_buffer[3];
653

zorglub's avatar
zorglub committed
654
    if( p_sd->p_sys->b_strict && i_hash == 0 )
655
    {
zorglub's avatar
zorglub committed
656
        msg_Dbg( p_sd, "strict mode, discarding announce with null id hash");
zorglub's avatar
zorglub committed
657
658
659
660
        return VLC_EGENERIC;
    }

    psz_sdp  = &p_buffer[4];
zorglub's avatar
zorglub committed
661
    psz_initial_sdp = psz_sdp;
zorglub's avatar
zorglub committed
662
663
664
665

    if( i_address_type == 0 ) /* ipv4 source address */
    {
        psz_sdp += 4;
zorglub's avatar
zorglub committed
666
667
668
669
670
        if( i_read <= 9 )
        {
            msg_Warn( p_sd,"too short SAP packet\n" );
            return VLC_EGENERIC;
        }
zorglub's avatar
zorglub committed
671
672
673
674
    }
    else /* ipv6 source address */
    {
        psz_sdp += 16;
zorglub's avatar
zorglub committed
675
676
677
678
679
        if( i_read <= 21 )
        {
            msg_Warn( p_sd,"too short SAP packet\n" );
            return VLC_EGENERIC;
        }
zorglub's avatar
zorglub committed
680
681
682
683
684
685
686
687
    }

    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 )
688
        {
zorglub's avatar
zorglub committed
689
690
691
            memcpy( psz_sdp, p_decompressed_buffer, i_decompressed_size );
            psz_sdp[i_decompressed_size] = '\0';
            free( p_decompressed_buffer );
692
        }
zorglub's avatar
zorglub committed
693
#else
zorglub's avatar
zorglub committed
694
        msg_Warn( p_sd, "Ignoring compressed sap packet" );
zorglub's avatar
zorglub committed
695
696
        return VLC_EGENERIC;
#endif
697
    }
zorglub's avatar
zorglub committed
698
699

    /* Add the size of authentification info */
zorglub's avatar
zorglub committed
700
701
702
703
704
    if( i_read < p_buffer[1] + (psz_sdp - psz_initial_sdp ) )
    {
        msg_Warn( p_sd, "too short SAP packet\n");
        return VLC_EGENERIC;
    }
zorglub's avatar
zorglub committed
705
    psz_sdp += p_buffer[1];
706
    psz_foo = psz_sdp;
zorglub's avatar
zorglub committed
707
708
709
710

    /* Skip payload type */
    /* Handle announces without \0 between SAP and SDP */
    while( *psz_sdp != '\0' && ( psz_sdp[0] != 'v' && psz_sdp[1] != '=' ) )
711
    {
zorglub's avatar
zorglub committed
712
713
714
715
        if( psz_sdp - psz_initial_sdp >= i_read - 5 )
        {
            msg_Warn( p_sd, "empty SDP ?");
        }
zorglub's avatar
zorglub committed
716
        psz_sdp++;
717
718
    }

zorglub's avatar
zorglub committed
719
720
721
722
    if( *psz_sdp == '\0' )
    {
        psz_sdp++;
    }
723
724
725
726
727
728
729
730
731
    if( psz_sdp != psz_foo && strcasecmp( psz_foo, "application/sdp" ) )
    {
        msg_Dbg( p_sd, "unhandled content type: %s", psz_foo );        
    }
    if( psz_sdp -p_buffer >= i_read )
    {
        msg_Warn( p_sd, "package without content" );
        return VLC_EGENERIC;
    }
732

zorglub's avatar
zorglub committed
733
    /* Parse SDP info */
zorglub's avatar
zorglub committed
734
    p_sdp = ParseSDP( VLC_OBJECT(p_sd), psz_sdp );
735

zorglub's avatar
zorglub committed
736
    if( p_sdp == NULL )
sigmunau's avatar
sigmunau committed
737
    {
zorglub's avatar
zorglub committed
738
739
740
741
        return VLC_EGENERIC;
    }

    /* Decide whether we should add a playlist item for this SDP */
zorglub's avatar
zorglub committed
742
743
744
745
746
    /* Parse connection information (c= & m= ) */
    if( ParseConnection( VLC_OBJECT(p_sd), p_sdp ) )
    {
        p_sdp->psz_uri = NULL;
    }
zorglub's avatar
zorglub committed
747
748

    /* Multi-media or no-parse -> pass to LIVE.COM */
zorglub's avatar
zorglub committed
749
750
751
752
    if( p_sdp->i_media > 1 || ( p_sdp->i_media_type != 14 &&
                                p_sdp->i_media_type != 32 &&
                                p_sdp->i_media_type != 33) ||
        p_sd->p_sys->b_parse == VLC_FALSE )
zorglub's avatar
zorglub committed
753
    {
zorglub's avatar
zorglub committed
754
        if( p_sdp->psz_uri ) free( p_sdp->psz_uri );
zorglub's avatar
zorglub committed
755
756
757
758
759
        asprintf( &p_sdp->psz_uri, "sdp://%s", p_sdp->psz_sdp );
    }

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

zorglub's avatar
zorglub committed
760
    for( i = 0 ; i< p_sd->p_sys->i_announces ; i++ )
zorglub's avatar
zorglub committed
761
762
763
    {
        /* FIXME: slow */
        /* 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
        {
zorglub's avatar
zorglub committed
766
767
            if( b_need_delete )
            {
zorglub's avatar
zorglub committed
768
                RemoveAnnounce( p_sd, p_sd->p_sys->pp_announces[i]);
zorglub's avatar
zorglub committed
769
770
771
                return VLC_SUCCESS;
            }
            else
772
            {
zorglub's avatar
zorglub committed
773
                p_sd->p_sys->pp_announces[i]->i_last = mdate();
zorglub's avatar
zorglub committed
774
775
                FreeSDP( p_sdp );
                return VLC_SUCCESS;
776
777
            }
        }
zorglub's avatar
zorglub committed
778
779
780
781
    }
    /* Add item */
    if( p_sdp->i_media > 1 )
    {
zorglub's avatar
zorglub committed
782
        msg_Dbg( p_sd, "passing to LIVE.COM" );
zorglub's avatar
zorglub committed
783
    }
zorglub's avatar
zorglub committed
784

zorglub's avatar
zorglub committed
785
    CreateAnnounce( p_sd, i_hash, p_sdp );
zorglub's avatar
zorglub committed
786
787
788
789

    return VLC_SUCCESS;
}

zorglub's avatar
zorglub committed
790
sap_announce_t *CreateAnnounce( services_discovery_t *p_sd, uint16_t i_hash,
zorglub's avatar
zorglub committed
791
792
793
794
795
796
797
798
799
                                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 )
    {
zorglub's avatar
zorglub committed
800
801
        msg_Err( p_sd, "out of memory");
        p_sd->b_die = VLC_TRUE;
zorglub's avatar
zorglub committed
802
803
804
805
806
807
        return NULL;
    }
    p_sap->i_last = mdate();
    p_sap->i_hash = i_hash;
    p_sap->p_sdp = p_sdp;
    p_sap->p_item = NULL;
808

zorglub's avatar
zorglub committed
809
    /* Create the playlist item here */
zorglub's avatar
zorglub committed
810
    p_item = playlist_ItemNew( p_sd, p_sap->p_sdp->psz_uri,
zorglub's avatar
zorglub committed
811
                               p_sap->p_sdp->psz_sessionname );
zorglub's avatar
zorglub committed
812

zorglub's avatar
zorglub committed
813
814
815
    if( !p_item )
    {
        return NULL;
816
817
    }

zorglub's avatar
zorglub committed
818
819
820
821
822
823
    psz_value = GetAttribute( p_sap->p_sdp, "x-plgroup" );

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

zorglub's avatar
zorglub committed
825
    p_playlist = (playlist_t *)vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
zorglub's avatar
zorglub committed
826
827
                                                FIND_ANYWHERE );
    if( !p_playlist )
828
    {
zorglub's avatar
zorglub committed
829
        msg_Err( p_sd, "playlist not found" );
zorglub's avatar
zorglub committed
830
831
        FREE( psz_value );
        free( p_sap );
zorglub's avatar
zorglub committed
832
833
        return NULL;
    }
834

zorglub's avatar
zorglub committed
835
836
    if( psz_value != NULL )
    {
zorglub's avatar
zorglub committed
837
        p_child = playlist_ChildSearchName( p_sd->p_sys->p_node, psz_value );
838

zorglub's avatar
zorglub committed
839
        if( p_child == NULL )
840
        {
zorglub's avatar
zorglub committed
841
            p_child = playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
zorglub's avatar
zorglub committed
842
                                           psz_value, p_sd->p_sys->p_node );
843
        }
zorglub's avatar
zorglub committed
844
845
846
    }
    else
    {
zorglub's avatar
zorglub committed
847
        p_child = p_sd->p_sys->p_node;
zorglub's avatar
zorglub committed
848
849
850
851
852
853
854
855
856
857
858
    }

    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;

zorglub's avatar
zorglub committed
859
    TAB_APPEND( p_sd->p_sys->i_announces,
860
                p_sd->p_sys->pp_announces, p_sap );
zorglub's avatar
zorglub committed
861
862
863

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

zorglub's avatar
zorglub committed
865
866
867
static char *GetAttribute( sdp_t *p_sdp, const char *psz_search )
{
    int i;
868

zorglub's avatar
zorglub committed
869
870
871
872
    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 ) ) )
873
        {
zorglub's avatar
zorglub committed
874
            return p_sdp->pp_attributes[i]->psz_value;
875
        }
zorglub's avatar
zorglub committed
876
877
878
    }
    return NULL;
}
879

zorglub's avatar
zorglub committed
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897

/* 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 )
898
        {
zorglub's avatar
zorglub committed
899
900
            *psz_eof = '\0';
            psz_parse = psz_eof + 1;
901
902
903
        }
        else
        {
zorglub's avatar
zorglub committed
904
905
            msg_Warn( p_obj, "unable to parse c field (1)");
            return VLC_EGENERIC;
906
        }
907

zorglub's avatar
zorglub committed
908
        psz_eof = strchr( psz_parse, ' ' );
909

zorglub's avatar
zorglub committed
910
        if( psz_eof )
911
        {
zorglub's avatar
zorglub committed
912
913
            *psz_eof = '\0';
            if( !strncmp( psz_parse, "IP4", 3 ) )
914
            {
zorglub's avatar
zorglub committed
915
                p_sdp->i_in = 4;
916
            }
zorglub's avatar
zorglub committed
917
            else if( !strncmp( psz_parse, "IP6", 3 ) )
918
            {
zorglub's avatar
zorglub committed
919
                p_sdp->i_in = 6;
920
921
922
            }
            else
            {
zorglub's avatar
zorglub committed
923
                p_sdp->i_in = 0;
924
            }
zorglub's avatar
zorglub committed
925
            psz_parse = psz_eof + 1;
926
927
928
        }
        else
        {
zorglub's avatar
zorglub committed
929
930
            msg_Warn( p_obj, "unable to parse c field (2)");
            return VLC_EGENERIC;
931
        }
932

zorglub's avatar
zorglub committed
933
        psz_eof = strchr( psz_parse, '/' );
zorglub's avatar
zorglub committed
934

zorglub's avatar
zorglub committed
935
936
937
938
939
940
        if( psz_eof )
        {
            *psz_eof = 0;
        }
        else
        {
sigmunau's avatar
sigmunau committed
941
            msg_Dbg( p_obj, "incorrect c field");
942
        }
sigmunau's avatar
sigmunau committed
943
        psz_uri = strdup( psz_parse );
944

zorglub's avatar
zorglub committed
945
    }
946

zorglub's avatar