sap.c 29.7 KB
Newer Older
1
2
3
4
/*****************************************************************************
 * sap.c :  SAP interface module
 *****************************************************************************
 * Copyright (C) 2001 VideoLAN
zorglub's avatar
free    
zorglub committed
5
 * $Id: sap.c,v 1.42 2003/12/11 20:40:37 zorglub Exp $
6
7
 *
 * Authors: Arnaud Schauly <gitan@via.ecp.fr>
zorglub's avatar
zorglub committed
8
9
 *          Clment Stenac <zorglub@via.ecp.fr>
 *          Damien Lucas <nitrox@videolan.org>
10
 *          Laurent Aimar <fenrir@via.ecp.fr>
zorglub's avatar
zorglub committed
11
 *
12
13
14
15
 * 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.
16
 *
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
 * 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.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>                                      /* malloc(), free() */

#include <vlc/vlc.h>
#include <vlc/intf.h>
34
35
36

#include <errno.h>                                                 /* ENOMEM */
#include <ctype.h>
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

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

#ifdef WIN32
#   include <winsock2.h>
#   include <ws2tcpip.h>
#   ifndef IN_MULTICAST
#       define IN_MULTICAST(a) IN_CLASSD(a)
#   endif
#else
#   include <sys/socket.h>
#   include <netinet/in.h>
54
55
56
57
58
#   if HAVE_ARPA_INET_H
#      include <arpa/inet.h>
#   elif defined( SYS_BEOS )
#      include <net/netdb.h>
#   endif
59
#endif
60

gbazin's avatar
   
gbazin committed
61
#ifdef UNDER_CE
gbazin's avatar
   
gbazin committed
62
#   define close(a) CloseHandle(a)
gbazin's avatar
   
gbazin committed
63
#elif defined( WIN32 )
gbazin's avatar
   
gbazin committed
64
#   define close(a) closesocket(a)
gbazin's avatar
   
gbazin committed
65
66
#endif

67
68
#include "network.h"

sigmunau's avatar
sigmunau committed
69
70
71
72
#ifdef HAVE_ZLIB_H
#   include <zlib.h>
#endif

73
74
#define MAX_LINE_LENGTH 256

75
/* SAP is always on that port */
76
77
#define HELLO_PORT 9875
#define HELLO_GROUP "224.2.127.254"
78
#define ADD_SESSION 1
79

80
81
82
#define IPV6_ADDR_1 "FF0"  /* Scope is inserted between them */
#define IPV6_ADDR_2 "::2:7FFE"

83

84
85
86
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
zorglub's avatar
zorglub committed
87
88
#define SAP_ADDR_TEXT N_("SAP multicast address")
#define SAP_ADDR_LONGTEXT N_("SAP multicast address")
89
#define SAP_IPV4_TEXT N_("IPv4-SAP listening")
90
91
92
93
94
#define SAP_IPV4_LONGTEXT N_( \
      "Set this if you want the SAP module to listen to IPv4 announces")
#define SAP_IPV6_TEXT N_( "IPv6-SAP listening")
#define SAP_IPV6_LONGTEXT N_(\
      "Set this if you want the SAP module to listen to IPv6 announces")
zorglub's avatar
zorglub committed
95
#define SAP_SCOPE_TEXT N_("IPv6 SAP scope")
96
97
#define SAP_SCOPE_LONGTEXT N_( \
       "Sets the scope for IPv6 announces (default is 8)")
zorglub's avatar
zorglub committed
98

99
100
101
static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );

102
vlc_module_begin();
103
    add_category_hint( N_("SAP"), NULL, VLC_TRUE );
104
        add_string( "sap-addr", NULL, NULL,
zorglub's avatar
zorglub committed
105
                     SAP_ADDR_TEXT, SAP_ADDR_LONGTEXT, VLC_TRUE );
zorglub's avatar
zorglub committed
106

107
        add_bool( "sap-ipv4", 1 , NULL,
zorglub's avatar
zorglub committed
108
                     SAP_IPV4_TEXT,SAP_IPV4_LONGTEXT, VLC_TRUE);
109
110

        add_bool( "sap-ipv6", 0 , NULL,
zorglub's avatar
zorglub committed
111
                   SAP_IPV6_TEXT, SAP_IPV6_LONGTEXT, VLC_TRUE);
112

113
        add_string( "sap-ipv6-scope", "8" , NULL,
zorglub's avatar
zorglub committed
114
                    SAP_SCOPE_TEXT, SAP_SCOPE_LONGTEXT, VLC_TRUE);
115

gbazin's avatar
   
gbazin committed
116
    set_description( _("SAP interface") );
117
    set_capability( "interface", 0 );
118
    set_callbacks( Open, Close );
119
120
121
vlc_module_end();

/*****************************************************************************
122
 * Local prototypes
123
124
 *****************************************************************************/

125
static void Run    ( intf_thread_t *p_intf );
126
static ssize_t NetRead( intf_thread_t *, int fd[2], uint8_t *, int );
127

128
129
130
typedef struct media_descr_t media_descr_t;
typedef struct sess_descr_t sess_descr_t;
typedef struct attr_descr_t attr_descr_t;
131

132
static void sess_toitem( intf_thread_t *, sess_descr_t * );
133

134
135
static sess_descr_t *  parse_sdp( intf_thread_t *, char * ) ;
static void free_sd( sess_descr_t * );
136

137
138
/* Detect multicast addresses */
static int  ismult( char * );
139

140
141
142
143
144
145
/* The struct that contains sdp informations */
struct  sess_descr_t
{
    int  i_version;
    char *psz_sessionname;
    char *psz_connection;
sigmunau's avatar
sigmunau committed
146
    char *psz_sdp;
zorglub's avatar
zorglub committed
147

148
149
150
151
152
    int           i_media;
    media_descr_t **pp_media;
    int           i_attributes;
    attr_descr_t  **pp_attributes;
};
153

154
155
156
157
158
159
/* All this informations are not useful yet.  */
struct media_descr_t
{
    char *psz_medianame;
    char *psz_mediaconnection;
};
zorglub's avatar
zorglub committed
160

161
162
163
164
165
struct attr_descr_t
{
    char *psz_field;
    char *psz_value;
};
zorglub's avatar
zorglub committed
166

167
168
169
170
171
172
struct sap_announce_t
{
    char *psz_name;
    char *psz_uri;
};

173
174
175
176
struct intf_sys_t
{
    /* IPV4 and IPV6 */
    int fd[2];
177

178
179
    /* playlist group */
    int i_group;
180
181
182
183

    /* Table of announces */
    int i_announces;
    struct sap_announce_t **pp_announces;
184
};
185

sigmunau's avatar
sigmunau committed
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
#ifdef HAVE_ZLIB_H
int do_decompress(unsigned char *src, unsigned char **_dst, int slen) {
  int result, dstsize, n;
  unsigned char *dst;
  z_stream d_stream;

  d_stream.zalloc = (alloc_func)0;
  d_stream.zfree = (free_func)0;
  d_stream.opaque = (voidpf)0;
  result = inflateInit(&d_stream);
  if (result != Z_OK) {
    printf("inflateInit() failed. Result: %d\n", result);
    return(-1);
  }

  d_stream.next_in = (Bytef *)src;
  d_stream.avail_in = slen;
  n = 0;
  dst = NULL;
  do {
    n++;
    dst = (unsigned char *)realloc(dst, n * 1000);
    d_stream.next_out = (Bytef *)&dst[(n - 1) * 1000];
    d_stream.avail_out = 1000;
    result = inflate(&d_stream, Z_NO_FLUSH);
    if ((result != Z_OK) && (result != Z_STREAM_END)) {
      printf("Zlib decompression failed. Result: %d\n", result);
      return(-1);
    }
  } while ((d_stream.avail_out == 0) && (d_stream.avail_in != 0) &&
           (result != Z_STREAM_END));

  dstsize = d_stream.total_out;
  inflateEnd(&d_stream);

  *_dst = (unsigned char *)realloc(dst, dstsize);

  return dstsize;
}
#endif

227
228
229
230
231
232
233
/*****************************************************************************
 * Open: initialize and create stuff
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
    intf_thread_t *p_intf = (intf_thread_t*)p_this;
    intf_sys_t    *p_sys  = malloc( sizeof( intf_sys_t ) );
234

235
    playlist_t          *p_playlist;
236

237
238
239
240
241
242
243
244
    p_sys->fd[0] = -1;
    p_sys->fd[1] = -1;
    if( config_GetInt( p_intf, "sap-ipv4" ) )
    {
        char *psz_address = config_GetPsz( p_intf, "sap-addr" );
        network_socket_t    sock;
        module_t            *p_network;
        if( psz_address == NULL || *psz_address == '\0' )
245
        {
246
            psz_address = strdup( HELLO_GROUP );
247
248
249
        }

        /* Prepare the network_socket_t structure */
250
251
252
253
254
255
256
        sock.i_type            = NETWORK_UDP;
        sock.psz_bind_addr     = psz_address;
        sock.i_bind_port       = HELLO_PORT;
        sock.psz_server_addr   = "";
        sock.i_server_port     = 0;
        sock.i_ttl             = 0;
        p_intf->p_private = (void*) &sock;
257

258
259
        p_network = module_Need( p_intf, "network", "ipv4" );
        if( p_network )
260
        {
261
262
            p_sys->fd[0] = sock.i_handle;
            module_Unneed( p_intf, p_network );
263
        }
264
        else
265
        {
266
            msg_Warn( p_intf, "failed to open %s:%d", psz_address, HELLO_PORT );
267
        }
268
        free( psz_address );
269
    }
270

271
    if( config_GetInt( p_intf, "sap-ipv6" ) )
272
    {
273
274
275
276
        char psz_address[100];
        char *psz_scope = config_GetPsz( p_intf, "sap-ipv6-scope" );
        network_socket_t    sock;
        module_t            *p_network;
277

278
279
280
281
282
283
        if( psz_scope == NULL || *psz_scope == '\0' )
        {
            psz_scope = strdup( "8" );
        }
        snprintf( psz_address, 100, "[%s%c%s]",IPV6_ADDR_1, psz_scope[0], IPV6_ADDR_2 );
        free( psz_scope );
284

285
286
287
288
289
290
291
        sock.i_type            = NETWORK_UDP;
        sock.psz_bind_addr     = psz_address;
        sock.i_bind_port       = HELLO_PORT;
        sock.psz_server_addr   = "";
        sock.i_server_port     = 0;
        sock.i_ttl             = 0;
        p_intf->p_private = (void*) &sock;
292

293
294
        p_network = module_Need( p_intf, "network", "ipv6" );
        if( p_network )
295
        {
296
297
            p_sys->fd[1] = sock.i_handle;
            module_Unneed( p_intf, p_network );
298
        }
299
        else
300
        {
301
            msg_Warn( p_intf, "failed to open %s:%d", psz_address, HELLO_PORT );
302
303
        }
    }
304
    if( p_sys->fd[0] <= 0 && p_sys->fd[1] <= 0 )
305
    {
306
307
308
        msg_Warn( p_intf, "IPV4 and IPV6 failed" );
        free( p_sys );
        return VLC_EGENERIC;
309
    }
310
311
312
313
314

    /* Create our playlist group */
    p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
                                                FIND_ANYWHERE );
    if( p_playlist )
315
    {
316
317
318
        playlist_group_t *p_group = playlist_CreateGroup( p_playlist , "SAP" );
        p_sys->i_group = p_group->i_id;
        vlc_object_release( p_playlist );
319
    }
320

321
322
323
    p_sys->i_announces = 0;
    p_sys->pp_announces = NULL;

324
325
    p_intf->pf_run = Run;
    p_intf->p_sys  = p_sys;
326
327
328
329

    return VLC_SUCCESS;
}

330
331
332
333
/*****************************************************************************
 * Close:
 *****************************************************************************/
static void Close( vlc_object_t *p_this )
334
{
335
336
    intf_thread_t *p_intf = (intf_thread_t*)p_this;
    intf_sys_t    *p_sys  = p_intf->p_sys;
zorglub's avatar
free    
zorglub committed
337
    int i;
338

339
    if( p_sys->fd[0] > 0 )
340
    {
341
342
343
344
345
346
        close( p_sys->fd[0] );
    }
    if( p_sys->fd[1] > 0 )
    {
        close( p_sys->fd[1] );
    }
347

zorglub's avatar
free    
zorglub committed
348
349
350
351
352
353
354
355
356
    for( i = 0 ; i< p_sys->i_announces ; i++ )
    {
        if( p_sys->pp_announces[i]->psz_name )
           free( p_sys->pp_announces[i]->psz_name );
        if( p_sys->pp_announces[i]->psz_uri )
           free( p_sys->pp_announces[i]->psz_uri );
        free( p_sys->pp_announces[i] );
    }

357
358
    free( p_sys );
}
359

360
361
362
363
364
/*****************************************************************************
 * Run: sap thread
 *****************************************************************************
 * Listens to SAP packets, and sends them to packet_handle
 *****************************************************************************/
sigmunau's avatar
sigmunau committed
365
#define MAX_SAP_BUFFER 5000
366

367
368
369
370
static void Run( intf_thread_t *p_intf )
{
    intf_sys_t *p_sys  = p_intf->p_sys;
    uint8_t     buffer[MAX_SAP_BUFFER + 1];
sigmunau's avatar
sigmunau committed
371
    uint8_t    *p_end;
372

373
374
375
376
377
    /* read SAP packets */
    while( !p_intf->b_die )
    {
        int i_read = NetRead( p_intf, p_sys->fd, buffer, MAX_SAP_BUFFER );
        uint8_t *p_sdp;
sigmunau's avatar
sigmunau committed
378
379
380
381
382
383
384
385
386
        int i_version;
        int i_address_type;
        int b_reserved;
        int b_message_type;
        int b_encrypted;
        int b_compressed;
        unsigned char *p_decompressed_buffer;
        int i_decompressed_size;
        
387
388
        /* Minimum length is > 6 */
        if( i_read <= 6 )
389
        {
390
391
392
393
394
            if( i_read < 0 )
            {
                msg_Warn( p_intf, "Cannot read in the socket" );
            }
            continue;
395
396
        }

397
        buffer[i_read] = '\0';
sigmunau's avatar
sigmunau committed
398
        p_end = &buffer[i_read];
399

400
        /* Parse the SAP header */
sigmunau's avatar
sigmunau committed
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
        i_version = buffer[0] >> 5;
        if( i_version != 1 )
        {
            msg_Warn( p_intf, "strange sap version %d found", i_version );
        }
        i_address_type = buffer[0] & 0x10;
        b_reserved = buffer[0] & 0x08;
        if( b_reserved != 0 )
        {
            msg_Warn( p_intf, "reserved bit incorrectly set" );
        }
        b_message_type = buffer[0] & 0x04;
        if( b_message_type != 0 )
        {
            msg_Warn( p_intf, "got session deletion packet" );
        }
        b_encrypted = buffer[0] & 0x02;
        if( b_encrypted )
        {
            msg_Warn( p_intf, "encrypted packet" );
        }
        b_compressed = buffer[0] & 0x01;
423
        p_sdp  = &buffer[4];
sigmunau's avatar
sigmunau committed
424
425
426
427
428
429
430
431
432
433
434
435
        if( i_address_type == 0 ) /* ipv4 source address */
        {
            p_sdp += 4;
        }
        else /* ipv6 source address */
        {
            p_sdp += 16;
        }
        if( b_compressed )
        {
#ifdef HAVE_ZLIB_H
            i_decompressed_size = do_decompress( p_sdp, &p_decompressed_buffer, i_read - ( p_sdp - buffer ) );
sigmunau's avatar
sigmunau committed
436
            if( i_decompressed_size > 0 && i_decompressed_size < MAX_SAP_BUFFER )
sigmunau's avatar
sigmunau committed
437
438
439
440
441
442
443
444
445
446
447
448
            {
                memcpy( p_sdp, p_decompressed_buffer, i_decompressed_size );
                p_sdp[i_decompressed_size] = '\0';
                p_end = &p_sdp[i_decompressed_size];
                free( p_decompressed_buffer );
            }
#else
            msg_Warn( p_intf, "Ignoring compressed sap packet" );
#endif
        }
        p_sdp += buffer[1]; /* size of signature */
        while( p_sdp < p_end - 1 && *p_sdp != '\0' && p_sdp[0] != 'v' && p_sdp[1] != '=' )
449
        {
450
            p_sdp++;
451
        }
452
        if( *p_sdp == '\0' )
453
        {
454
            p_sdp++;
455
        }
sigmunau's avatar
sigmunau committed
456
457

        if( p_sdp < p_end )
458
        {
459
460
            sess_descr_t *p_sd = parse_sdp( p_intf, p_sdp );
            if( p_sd )
461
            {
462
463
                sess_toitem ( p_intf, p_sd );
                free_sd ( p_sd );
464
            }
465
        }
sigmunau's avatar
sigmunau committed
466
467
468
469
        else
        {
            msg_Warn( p_intf, "ditching sap packet" );
        }
470
    }
471
472
}

473
474
475
/**********************************************************************
 * cfield_parse
 *********************************************************************
476
 * put into *ppsz_uri, the the uri in the cfield, psz_cfield.
477
478
479
480
481
 *********************************************************************/

static void cfield_parse( char *psz_cfield, char **ppsz_uri )
{

482
    char *psz_pos;
483
484
485
    if( psz_cfield )
    {
        psz_pos = psz_cfield;
486
487

        while( *psz_pos != ' ' && *psz_pos !='\0' )
488
489
490
491
        {
            psz_pos++;
        }
        psz_pos++;
492
        while( *psz_pos != ' ' && *psz_pos !='\0' )
493
494
495
496
        {
            psz_pos++;
        }
        psz_pos++;
497
498
        *ppsz_uri = psz_pos;
        while( *psz_pos != ' ' && *psz_pos !='/'
499
500
501
502
503
504
505
506
507
508
509
                        && *psz_pos != '\0' )
        {
            psz_pos++;
        }
        *psz_pos = '\0';

    }
    else
    {
        ppsz_uri = NULL;
    }
510
511

    return;
512
513
514
515

}

/**********************************************************************
516
 * mfield_parse
517
 *********************************************************************
518
 * put into *ppsz_proto, and *ppsz_port, the protocol and the port.
519
520
521
 *********************************************************************/


522
static void mfield_parse( char *psz_mfield, char **ppsz_proto,
523
524
               char **ppsz_port )
{
525
    char *psz_pos;
526
    char *psz_media;
527
528
529
    if( psz_mfield )
    {
        psz_pos = psz_mfield;
530
        psz_media = psz_mfield;
531
        while( *psz_pos != '\0' && *psz_pos != ' ' )
532
533
534
        {
            psz_pos++;
        }
535
536
537
538
539
540
541
542
543
544
        if( *psz_pos != '\0' )
        {
            *psz_pos = '\0';
            if( strcmp( psz_media, "video" ) && strcmp( psz_media, "audio" ) )
            {
                *ppsz_proto = NULL;
                *ppsz_port = NULL;
                return;
            }
        }
545
546
        psz_pos++;
        *ppsz_port = psz_pos;
547
        while( *psz_pos != '\0' && *psz_pos !=' ' && *psz_pos!='/' )
548
        {
549
            psz_pos++;
550
551
552
553
        }
        if( *psz_pos == '/' )  // FIXME does not support multi-port
        {
            *psz_pos = '\0';
554
555
            psz_pos++;
            while( *psz_pos != '\0' && *psz_pos !=' ' )
556
557
558
            {
            psz_pos++;
            }
559
        }
560
561
562
        *psz_pos = '\0';
        psz_pos++;
        *ppsz_proto = psz_pos;
563
        while( *psz_pos!='\0' && *psz_pos !=' ' &&
564
565
566
                        *psz_pos!='/' )
        {
            *psz_pos = tolower( *psz_pos );
567
            psz_pos++;
568
569
570
571
572
573
574
575
576
577
578
        }
        *psz_pos = '\0';
    }
    else
    {
        *ppsz_proto = NULL;
        *ppsz_port = NULL;
    }
    return;
}

579

580
581
582
583
584
585
586
587
588
/*******************************************************************
 * sess_toitem : changes a sess_descr_t into a hurd of
 * playlist_item_t, which are enqueued.
 *******************************************************************
 * Note : does not support sessions that take place on consecutive
 * port or adresses yet.
 *******************************************************************/

static void sess_toitem( intf_thread_t * p_intf, sess_descr_t * p_sd )
589
{
590
    playlist_item_t * p_item;
591
    struct sap_announce_t *p_announce;
592
593
594
595
596
597
    char *psz_uri, *psz_proto;
    char *psz_port;
    char *psz_uri_default;
    int i_count , i;
    vlc_bool_t b_http = VLC_FALSE;
    char *psz_http_path = NULL;
598
    playlist_t *p_playlist = NULL;
599

600
    psz_uri_default = NULL;
sigmunau's avatar
sigmunau committed
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
    if( p_sd->i_media > 1 )
    {
        p_item = malloc( sizeof( playlist_item_t ) );
        if( p_item == NULL )
        {
            msg_Warn( p_intf, "out of memory" );
            return;
        }
        p_item->psz_name    = strdup( p_sd->psz_sessionname );
        p_item->psz_uri     = NULL;
        p_item->i_duration  = -1;
        p_item->ppsz_options= NULL;
        p_item->i_options   = 0;

        p_item->i_type      = 0;
        p_item->i_status    = 0;
        p_item->b_autodeletion = VLC_FALSE;
        p_item->b_enabled   = VLC_TRUE;
        p_item->i_group     = p_intf->p_sys->i_group;
        p_item->psz_author  = strdup( "" );
        psz_uri = malloc( strlen( p_sd->psz_sdp ) + 7 );
        if( psz_uri == NULL )
        {
            msg_Warn( p_intf, "out of memory" );
            free( p_item->psz_name );
            free( p_item->psz_author );
            free( p_item );
            return;
        }
        p_item->psz_uri = psz_uri;
        memcpy( psz_uri, "sdp://", 6 );
        psz_uri += 6;
        memcpy( psz_uri, p_sd->psz_sdp, strlen( p_sd->psz_sdp ) + 1 );
        /* Enqueueing p_item in the playlist */
635
636
637
638
639
640
641
642
643
644
645

        for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ )
        {
            if( !strcmp(p_intf->p_sys->pp_announces[i]->psz_name,
                                    p_item->psz_name ) &&
                !strcmp(p_intf->p_sys->pp_announces[i]->psz_uri,
                                    p_item->psz_uri ) )
            {
                return;
            }
        }
sigmunau's avatar
sigmunau committed
646
647
648
        p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
        playlist_AddItem ( p_playlist, p_item, PLAYLIST_CHECK_INSERT, PLAYLIST_END );
        vlc_object_release( p_playlist );
649
650
651
652
653
654
655
656
657
        p_announce = (struct sap_announce_t *)malloc(
                      sizeof(struct sap_announce_t *) );
        p_announce->psz_name = strdup( p_item->psz_name );
        p_announce->psz_uri = strdup( p_item->psz_uri );

        INSERT_ELEM( p_intf->p_sys->pp_announces,
                     p_intf->p_sys->i_announces,
                     p_intf->p_sys->i_announces,
                     p_announce );
sigmunau's avatar
sigmunau committed
658
        return;
659
660
    }

661
    cfield_parse( p_sd->psz_connection, &psz_uri_default );
662

663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
    for( i_count = 0 ; i_count < p_sd->i_media ; i_count++ )
    {
        p_item = malloc( sizeof( playlist_item_t ) );
        p_item->psz_name    = strdup( p_sd->psz_sessionname );
        p_item->psz_uri     = NULL;
        p_item->i_duration  = -1;
        p_item->ppsz_options= NULL;
        p_item->i_options   = 0;

        p_item->i_type      = 0;
        p_item->i_status    = 0;
        p_item->b_autodeletion = VLC_FALSE;
        p_item->b_enabled   = VLC_TRUE;
        p_item->i_group     = p_intf->p_sys->i_group;
        p_item->psz_author  = strdup( "" );
678

679
        psz_uri = NULL;
680

681
682
        /* Build what we have to put in p_item->psz_uri, with the m and
         *  c fields  */
683

684
        if( !p_sd->pp_media[i_count] )
685
        {
686
            return;
687
        }
Laurent Aimar's avatar
Laurent Aimar committed
688

689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
        mfield_parse( p_sd->pp_media[i_count]->psz_medianame,
                        & psz_proto, & psz_port );

        if( !psz_proto || !psz_port )
        {
            return;
        }

        if( p_sd->pp_media[i_count]->psz_mediaconnection )
        {
            cfield_parse( p_sd->pp_media[i_count]->psz_mediaconnection,
                            & psz_uri );
        }
        else
        {
            psz_uri = psz_uri_default;
        }
706

707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
        if( psz_uri == NULL )
        {
            return;
        }

        for( i = 0 ; i< p_sd->i_attributes ; i++ )
        {
            if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "type") &&
                strstr( p_sd->pp_attributes[i]->psz_value, "http") )
            {
                b_http = VLC_TRUE;
            }
            if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "http-path"))
            {
                psz_http_path = strdup(  p_sd->pp_attributes[i]->psz_value );
            }
723
724
            if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "plgroup"))
            {
gbazin's avatar
   
gbazin committed
725
                int i_id;
726
727
728
729
730
731
732
733
                p_playlist =
                (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
                                                        FIND_ANYWHERE );
                if( p_playlist == NULL )
                {
                    return;
                }

gbazin's avatar
   
gbazin committed
734
735
                i_id = playlist_GroupToId( p_playlist,
                                           p_sd->pp_attributes[i]->psz_value);
736
737
738
739
740
741
742
743
744
745
746
747
748
                if( i_id != 0 )
                {
                    p_item->i_group = i_id;
                }
                else
                {
                    playlist_group_t *p_group =
                            playlist_CreateGroup( p_playlist,
                                       p_sd->pp_attributes[i]->psz_value);
                    p_item->i_group = p_group->i_id;
                }
                vlc_object_release( p_playlist );
            }
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
        }


        /* Filling p_item->psz_uri */
        if( b_http == VLC_FALSE )
        {
            p_item->psz_uri = malloc( strlen( psz_proto ) + strlen( psz_uri ) +
                                      strlen( psz_port ) + 7 );
            if( ismult( psz_uri ) )
            {
                sprintf( p_item->psz_uri, "%s://@%s:%s",
                         psz_proto, psz_uri, psz_port );
            }
            else
            {
                sprintf( p_item->psz_uri, "%s://%s:%s",
                         psz_proto, psz_uri, psz_port );
            }
        }
        else
        {
            if( psz_http_path == NULL )
            {
                psz_http_path = strdup( "/" );
            }

            p_item->psz_uri = malloc( strlen( psz_proto ) + strlen( psz_uri ) +
                                      strlen( psz_port ) + strlen(psz_http_path) + 5 );
            sprintf( p_item->psz_uri, "%s://%s:%s%s", psz_proto,
                            psz_uri, psz_port,psz_http_path );
779

780
781
782
783
784
            if( psz_http_path )
            {
                free( psz_http_path );
            }
        }
785

786
787
788
789
790
791
792
793
794
795
796
         for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ )
         {
            if( !strcmp(p_intf->p_sys->pp_announces[i]->psz_name,
                                    p_item->psz_name ) &&
                !strcmp(p_intf->p_sys->pp_announces[i]->psz_uri,
                                    p_item->psz_uri ) )
            {
                return;
            }
        }

797
798
799
        /* Enqueueing p_item in the playlist */
        p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
        playlist_AddItem ( p_playlist, p_item, PLAYLIST_CHECK_INSERT, PLAYLIST_END );
800
801
802
803
804
805
806
807
808
        p_announce = (struct sap_announce_t *)malloc(
                      sizeof(struct sap_announce_t *) );
        p_announce->psz_name = strdup( p_item->psz_name );
        p_announce->psz_uri = strdup( p_item->psz_uri );

        INSERT_ELEM( p_intf->p_sys->pp_announces,
                     p_intf->p_sys->i_announces,
                     p_intf->p_sys->i_announces,
                     p_announce );
809
810
811
        vlc_object_release( p_playlist );
    }
}
812

813
814
815
/***********************************************************************
 * parse_sdp : SDP parsing
 * *********************************************************************
816
 * Make a sess_descr_t with a psz
817
 ***********************************************************************/
818

Laurent Aimar's avatar
Laurent Aimar committed
819
static sess_descr_t *  parse_sdp( intf_thread_t * p_intf, char *p_packet )
820
821
{
    sess_descr_t *  sd;
822

823
    if( p_packet[0] != 'v' || p_packet[1] != '=' )
824
    {
825
        msg_Warn(p_intf, "bad SDP packet");
826
827
828
        return NULL;
    }

829
    sd = malloc( sizeof( sess_descr_t ) );
Laurent Aimar's avatar
Laurent Aimar committed
830
    sd->psz_sessionname = NULL;
831
    sd->psz_connection  = NULL;
sigmunau's avatar
sigmunau committed
832
833
    sd->psz_sdp         = strdup( p_packet );
    
834
835
836
837
    sd->i_media         = 0;
    sd->pp_media        = NULL;
    sd->i_attributes    = 0;
    sd->pp_attributes   = NULL;
838

Laurent Aimar's avatar
Laurent Aimar committed
839
    while( *p_packet != '\0'  )
840
    {
Laurent Aimar's avatar
Laurent Aimar committed
841
842
        char *psz_end;

843
        /* Search begin of field */
Laurent Aimar's avatar
Laurent Aimar committed
844
        while( *p_packet == '\n' || *p_packet == ' ' || *p_packet == '\t' )
845
        {
Laurent Aimar's avatar
Laurent Aimar committed
846
847
            p_packet++;
        }
848
849
850
851
852
        /* search end of line */
        if( ( psz_end = strchr( p_packet, '\n' ) ) == NULL )
        {
            psz_end = p_packet + strlen( p_packet );
        }
sigmunau's avatar
sigmunau committed
853
854
855
856
        if( psz_end > p_packet && *(psz_end - 1 ) == '\r' )
        {
            psz_end--;
        }
857

858
        if( psz_end <= p_packet )
Laurent Aimar's avatar
Laurent Aimar committed
859
860
861
        {
            break;
        }
862
        *psz_end++ = '\0';
Laurent Aimar's avatar
Laurent Aimar committed
863

864
        if( p_packet[1] != '=' )
Laurent Aimar's avatar
Laurent Aimar committed
865
        {
866
867
868
            msg_Warn( p_intf, "packet invalid" );
            free_sd( sd );
            return NULL;
Laurent Aimar's avatar
Laurent Aimar committed
869
870
        }

871
        switch( p_packet[0] )
Laurent Aimar's avatar
Laurent Aimar committed
872
        {
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
            case( 'v' ):
                sd->i_version = atoi( &p_packet[2] );
                break;
            case( 's' ):
                sd->psz_sessionname = strdup( &p_packet[2] );
                break;
            case ( 'o' ):
            case( 'i' ):
            case( 'u' ):
            case( 'e' ):
            case( 'p' ):
            case( 't' ):
            case( 'r' ):
                break;
            case( 'a' ):
Laurent Aimar's avatar
Laurent Aimar committed
888
            {
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
                char *psz_eof = strchr( &p_packet[2], ':' );

                if( psz_eof && psz_eof[1] != '\0' )
                {
                    attr_descr_t *attr = malloc( sizeof( attr_descr_t ) );

                    *psz_eof++ = '\0';

                    attr->psz_field = strdup( &p_packet[2] );
                    attr->psz_value = strdup( psz_eof );

                    TAB_APPEND( sd->i_attributes, sd->pp_attributes, attr );
                }
                break;
            }

            case( 'm' ):
            {
                media_descr_t *media = malloc( sizeof( media_descr_t ) );

                media->psz_medianame = strdup( &p_packet[2] );
                media->psz_mediaconnection = NULL;

                TAB_APPEND( sd->i_media, sd->pp_media, media );
                break;
Laurent Aimar's avatar
Laurent Aimar committed
914
            }
915
916
917
918
919
920
921
922
923
924
925
926
927
928

            case( 'c' ):
                if( sd->i_media <= 0 )
                {
                    sd->psz_connection = strdup( &p_packet[2] );
                }
                else
                {
                    sd->pp_media[sd->i_media-1]->psz_mediaconnection = strdup( &p_packet[2] );
                }
               break;

            default:
               break;
Laurent Aimar's avatar
Laurent Aimar committed
929
        }
930

Laurent Aimar's avatar
Laurent Aimar committed
931
932
        p_packet = psz_end;
    }
933
934
935

    return sd;
}
936

937
938
#define FREE( p ) \
    if( p ) { free( p ); (p) = NULL; }
939
static void free_sd( sess_descr_t * p_sd )
940
941
{
    int i;
Laurent Aimar's avatar
Laurent Aimar committed
942

943
944
    FREE( p_sd->psz_sessionname );
    FREE( p_sd->psz_connection );
sigmunau's avatar
sigmunau committed
945
    FREE( p_sd->psz_sdp );
946
947
948
949
950

    for( i = 0; i < p_sd->i_media ; i++ )
    {
        FREE( p_sd->pp_media[i]->psz_medianame );
        FREE( p_sd->pp_media[i]->psz_mediaconnection );
sigmunau's avatar
sigmunau committed
951
        FREE( p_sd->pp_media[i] );
952
    }
953
    for( i = 0; i < p_sd->i_attributes ; i++ )
954
    {
955
956
        FREE( p_sd->pp_attributes[i]->psz_field );
        FREE( p_sd->pp_attributes[i]->psz_value );
sigmunau's avatar
sigmunau committed
957
        FREE( p_sd->pp_attributes[i] );
958
    }
959
960
961
962
    FREE( p_sd->pp_attributes );
    FREE( p_sd->pp_media );

    free( p_sd );
963
964
}

965
/***********************************************************************
966
 * ismult: returns true if we have a multicast address
967
968
969
970
 ***********************************************************************/

static int ismult( char *psz_uri )
{
Laurent Aimar's avatar
Laurent Aimar committed
971
972
    char *psz_end;
    int  i_value;
973

Laurent Aimar's avatar
Laurent Aimar committed
974
    i_value = strtol( psz_uri, &psz_end, 0 );
975

zorglub's avatar
zorglub committed
976
    /* IPv6 */
977
    if( psz_uri[0] == '[')
zorglub's avatar
zorglub committed
978
    {
979
980
981
982
983
      if( strncasecmp( &psz_uri[1], "FF0" , 3) ||
          strncasecmp( &psz_uri[2], "FF0" , 3))
            return( VLC_TRUE );
        else
            return( VLC_FALSE );
984
    }
985

986
    if( *psz_end != '.' ) { return( VLC_FALSE ); }
987

988
    return( i_value < 224 ? VLC_FALSE : VLC_TRUE );
989
}
990

zorglub's avatar
zorglub committed
991
992


993
994
995
996
/*****************************************************************************
 * Read: read on a file descriptor, checking b_die periodically
 *****************************************************************************
 * Taken from udp.c
997
 *****************************************************************************/
998
static ssize_t NetRead( intf_thread_t *p_intf,
999
                        int fd[2], uint8_t *p_buffer, int i_len )
1000
1001
1002
1003
1004
1005
1006
{
#ifdef UNDER_CE
    return -1;
#else
    struct timeval  timeout;
    fd_set          fds;
    int             i_ret;
1007
    int             i_handle_max = __MAX( fd[0], fd[1] );
1008

1009
1010
    /* Initialize file descriptor set */
    FD_ZERO( &fds );
1011
1012
    if( fd[0] > 0 ) FD_SET( fd[0], &fds );
    if( fd[1] > 0 ) FD_SET( fd[1], &fds );
1013

1014
1015
1016
    /* We'll wait 0.5 second if nothing happens */
    timeout.tv_sec = 0;
    timeout.tv_usec = 500000;
1017

1018
    /* Find if some data is available */
1019
    i_ret = select( i_handle_max + 1, &fds, NULL, NULL, &timeout );
1020

1021
1022
1023
1024
1025
    if( i_ret == -1 && errno != EINTR )
    {
        msg_Err( p_intf, "network select error (%s)", strerror(errno) );
    }
    else if( i_ret > 0 )
1026
    {
1027
        if( fd[0] > 0 && FD_ISSET( fd[0], &fds ) )
1028
        {
1029
             return recv( fd[0], p_buffer, i_len, 0 );
1030
        }
1031
        else if( fd[1] > 0 && FD_ISSET( fd[1], &fds ) )
1032
        {
1033
             return recv( fd[1], p_buffer, i_len, 0 );
1034
1035
1036
1037
1038
        }
    }
    return 0;
#endif
}