~ubuntu-branches/debian/wheezy/vlc/wheezy

« back to all changes in this revision

Viewing changes to modules/misc/sap.c

Tags: upstream-0.7.2.final
ImportĀ upstreamĀ versionĀ 0.7.2.final

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*****************************************************************************
 
2
 * sap.c :  SAP interface module
 
3
 *****************************************************************************
 
4
 * Copyright (C) 2001 VideoLAN
 
5
 * $Id: sap.c 7689 2004-05-16 18:08:41Z anil $
 
6
 *
 
7
 * Authors: Arnaud Schauly <gitan@via.ecp.fr>
 
8
 *          Clļæ½ment Stenac <zorglub@via.ecp.fr>
 
9
 *          Damien Lucas <nitrox@videolan.org>
 
10
 *          Laurent Aimar <fenrir@via.ecp.fr>
 
11
 *
 
12
 * This program is free software; you can redistribute it and/or modify
 
13
 * it under the terms of the GNU General Public License as published by
 
14
 * the Free Software Foundation; either version 2 of the License, or
 
15
 * (at your option) any later version.
 
16
 *
 
17
 * This program is distributed in the hope that it will be useful,
 
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
20
 * GNU General Public License for more details.
 
21
 *
 
22
 * You should have received a copy of the GNU General Public License
 
23
 * along with this program; if not, write to the Free Software
 
24
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 
25
 *****************************************************************************/
 
26
 
 
27
/*****************************************************************************
 
28
 * Preamble
 
29
 *****************************************************************************/
 
30
#include <stdlib.h>                                      /* malloc(), free() */
 
31
 
 
32
#include <vlc/vlc.h>
 
33
#include <vlc/intf.h>
 
34
 
 
35
#include <errno.h>                                                 /* ENOMEM */
 
36
#include <ctype.h>
 
37
 
 
38
#ifdef HAVE_UNISTD_H
 
39
#    include <unistd.h>
 
40
#endif
 
41
#ifdef HAVE_SYS_TIME_H
 
42
#    include <sys/time.h>
 
43
#endif
 
44
 
 
45
#ifdef WIN32
 
46
#   include <winsock2.h>
 
47
#   include <ws2tcpip.h>
 
48
#   ifndef IN_MULTICAST
 
49
#       define IN_MULTICAST(a) IN_CLASSD(a)
 
50
#   endif
 
51
#else
 
52
#   include <sys/socket.h>
 
53
#   include <netinet/in.h>
 
54
#   if HAVE_ARPA_INET_H
 
55
#      include <arpa/inet.h>
 
56
#   elif defined( SYS_BEOS )
 
57
#      include <net/netdb.h>
 
58
#   endif
 
59
#endif
 
60
 
 
61
#ifdef UNDER_CE
 
62
#   define close(a) CloseHandle(a)
 
63
#elif defined( WIN32 )
 
64
#   define close(a) closesocket(a)
 
65
#endif
 
66
 
 
67
#include "network.h"
 
68
 
 
69
#ifdef HAVE_ZLIB_H
 
70
#   include <zlib.h>
 
71
#endif
 
72
 
 
73
#define MAX_LINE_LENGTH 256
 
74
 
 
75
/* SAP is always on that port */
 
76
#define HELLO_PORT 9875
 
77
#define HELLO_GROUP "224.2.127.254"
 
78
#define ADD_SESSION 1
 
79
 
 
80
#define IPV6_ADDR_1 "FF0"  /* Scope is inserted between them */
 
81
#define IPV6_ADDR_2 "::2:7FFE"
 
82
 
 
83
 
 
84
/*****************************************************************************
 
85
 * Module descriptor
 
86
 *****************************************************************************/
 
87
#define SAP_ADDR_TEXT N_("SAP multicast address")
 
88
#define SAP_ADDR_LONGTEXT N_("SAP multicast address")
 
89
#define SAP_IPV4_TEXT N_("IPv4-SAP listening")
 
90
#define SAP_IPV4_LONGTEXT N_( \
 
91
      "Set this if you want the SAP module to listen to IPv4 announces")
 
92
#define SAP_IPV6_TEXT N_( "IPv6-SAP listening")
 
93
#define SAP_IPV6_LONGTEXT N_(\
 
94
      "Set this if you want the SAP module to listen to IPv6 announces")
 
95
#define SAP_SCOPE_TEXT N_("IPv6 SAP scope")
 
96
#define SAP_SCOPE_LONGTEXT N_( \
 
97
       "Sets the scope for IPv6 announces (default is 8)")
 
98
#define SAP_TIMEOUT_TEXT N_("SAP timeout (seconds)")
 
99
#define SAP_TIMEOUT_LONGTEXT N_( \
 
100
       "Sets the time before SAP items get deleted if no new announce " \
 
101
       "is received.")
 
102
#define SAP_PARSE_TEXT N_("Try to parse the SAP")
 
103
#define SAP_PARSE_LONGTEXT N_( \
 
104
       "When SAP can it will try to parse the SAP. Normal behavior is " \
 
105
       "to have livedotcom parse the announce." )
 
106
 
 
107
static int  Open ( vlc_object_t * );
 
108
static void Close( vlc_object_t * );
 
109
 
 
110
vlc_module_begin();
 
111
    set_description( _("SAP interface") );
 
112
 
 
113
    add_string( "sap-addr", NULL, NULL,
 
114
                SAP_ADDR_TEXT, SAP_ADDR_LONGTEXT, VLC_TRUE );
 
115
    add_bool( "sap-ipv4", 1 , NULL,
 
116
               SAP_IPV4_TEXT,SAP_IPV4_LONGTEXT, VLC_TRUE);
 
117
    add_bool( "sap-ipv6", 0 , NULL,
 
118
              SAP_IPV6_TEXT, SAP_IPV6_LONGTEXT, VLC_TRUE);
 
119
    add_string( "sap-ipv6-scope", "8" , NULL,
 
120
                SAP_SCOPE_TEXT, SAP_SCOPE_LONGTEXT, VLC_TRUE);
 
121
    add_integer( "sap-timeout", 1800, NULL,
 
122
                 SAP_TIMEOUT_TEXT, SAP_TIMEOUT_LONGTEXT, VLC_TRUE);
 
123
    add_bool( "sap-parse", 1 , NULL,
 
124
               SAP_PARSE_TEXT,SAP_PARSE_LONGTEXT, VLC_TRUE);
 
125
 
 
126
    set_capability( "interface", 0 );
 
127
    set_callbacks( Open, Close );
 
128
vlc_module_end();
 
129
 
 
130
/*****************************************************************************
 
131
 * Local prototypes
 
132
 *****************************************************************************/
 
133
 
 
134
static void Run    ( intf_thread_t *p_intf );
 
135
static ssize_t NetRead( intf_thread_t *, int fd[2], uint8_t *, int );
 
136
 
 
137
typedef struct media_descr_t media_descr_t;
 
138
typedef struct sess_descr_t sess_descr_t;
 
139
typedef struct attr_descr_t attr_descr_t;
 
140
 
 
141
static void sess_toitem( intf_thread_t *, sess_descr_t * );
 
142
 
 
143
static sess_descr_t *  parse_sdp( intf_thread_t *, char * ) ;
 
144
static void free_sd( sess_descr_t * );
 
145
 
 
146
/* Detect multicast addresses */
 
147
static int  ismult( char * );
 
148
 
 
149
/* The struct that contains sdp informations */
 
150
struct  sess_descr_t
 
151
{
 
152
    int  i_version;
 
153
    char *psz_sessionname;
 
154
    char *psz_connection;
 
155
    char *psz_sdp;
 
156
 
 
157
    int           i_media;
 
158
    media_descr_t **pp_media;
 
159
    int           i_attributes;
 
160
    attr_descr_t  **pp_attributes;
 
161
};
 
162
 
 
163
/* All this informations are not useful yet.  */
 
164
struct media_descr_t
 
165
{
 
166
    char *psz_medianame;
 
167
    char *psz_mediaconnection;
 
168
};
 
169
 
 
170
struct attr_descr_t
 
171
{
 
172
    char *psz_field;
 
173
    char *psz_value;
 
174
};
 
175
 
 
176
struct sap_announce_t
 
177
{
 
178
    mtime_t i_last;
 
179
    int i_id;
 
180
    char *psz_name;
 
181
    char *psz_uri;
 
182
};
 
183
 
 
184
struct intf_sys_t
 
185
{
 
186
    /* IPV4 and IPV6 */
 
187
    int fd[2];
 
188
 
 
189
    /* playlist group */
 
190
    int i_group;
 
191
 
 
192
    /* Table of announces */
 
193
    int i_announces;
 
194
    struct sap_announce_t **pp_announces;
 
195
 
 
196
    int i_timeout;
 
197
};
 
198
 
 
199
#ifdef HAVE_ZLIB_H
 
200
int do_decompress(unsigned char *src, unsigned char **_dst, int slen) {
 
201
  int result, dstsize, n;
 
202
  unsigned char *dst;
 
203
  z_stream d_stream;
 
204
 
 
205
  d_stream.zalloc = (alloc_func)0;
 
206
  d_stream.zfree = (free_func)0;
 
207
  d_stream.opaque = (voidpf)0;
 
208
  result = inflateInit(&d_stream);
 
209
  if (result != Z_OK) {
 
210
    printf("inflateInit() failed. Result: %d\n", result);
 
211
    return(-1);
 
212
  }
 
213
 
 
214
  d_stream.next_in = (Bytef *)src;
 
215
  d_stream.avail_in = slen;
 
216
  n = 0;
 
217
  dst = NULL;
 
218
  do {
 
219
    n++;
 
220
    dst = (unsigned char *)realloc(dst, n * 1000);
 
221
    d_stream.next_out = (Bytef *)&dst[(n - 1) * 1000];
 
222
    d_stream.avail_out = 1000;
 
223
    result = inflate(&d_stream, Z_NO_FLUSH);
 
224
    if ((result != Z_OK) && (result != Z_STREAM_END)) {
 
225
      printf("Zlib decompression failed. Result: %d\n", result);
 
226
      return(-1);
 
227
    }
 
228
  } while ((d_stream.avail_out == 0) && (d_stream.avail_in != 0) &&
 
229
           (result != Z_STREAM_END));
 
230
 
 
231
  dstsize = d_stream.total_out;
 
232
  inflateEnd(&d_stream);
 
233
 
 
234
  *_dst = (unsigned char *)realloc(dst, dstsize);
 
235
 
 
236
  return dstsize;
 
237
}
 
238
#endif
 
239
 
 
240
/*****************************************************************************
 
241
 * Open: initialize and create stuff
 
242
 *****************************************************************************/
 
243
static int Open( vlc_object_t *p_this )
 
244
{
 
245
    intf_thread_t *p_intf = (intf_thread_t*)p_this;
 
246
    intf_sys_t    *p_sys  = malloc( sizeof( intf_sys_t ) );
 
247
 
 
248
    playlist_t          *p_playlist;
 
249
 
 
250
    p_sys->i_timeout = config_GetInt(p_intf,"sap-timeout");
 
251
    p_sys->fd[0] = -1;
 
252
    p_sys->fd[1] = -1;
 
253
    if( config_GetInt( p_intf, "sap-ipv4" ) )
 
254
    {
 
255
        char *psz_address = config_GetPsz( p_intf, "sap-addr" );
 
256
        network_socket_t    sock;
 
257
        module_t            *p_network;
 
258
        if( psz_address == NULL || *psz_address == '\0' )
 
259
        {
 
260
            psz_address = strdup( HELLO_GROUP );
 
261
        }
 
262
 
 
263
        /* Prepare the network_socket_t structure */
 
264
        sock.i_type            = NETWORK_UDP;
 
265
        sock.psz_bind_addr     = psz_address;
 
266
        sock.i_bind_port       = HELLO_PORT;
 
267
        sock.psz_server_addr   = "";
 
268
        sock.i_server_port     = 0;
 
269
        sock.i_ttl             = 0;
 
270
        p_intf->p_private = (void*) &sock;
 
271
 
 
272
        p_network = module_Need( p_intf, "network", "ipv4", VLC_TRUE );
 
273
        if( p_network )
 
274
        {
 
275
            p_sys->fd[0] = sock.i_handle;
 
276
            module_Unneed( p_intf, p_network );
 
277
        }
 
278
        else
 
279
        {
 
280
            msg_Warn( p_intf, "failed to open %s:%d", psz_address, HELLO_PORT );
 
281
        }
 
282
        free( psz_address );
 
283
    }
 
284
 
 
285
    if( config_GetInt( p_intf, "sap-ipv6" ) )
 
286
    {
 
287
        char psz_address[100];
 
288
        char *psz_scope = config_GetPsz( p_intf, "sap-ipv6-scope" );
 
289
        network_socket_t    sock;
 
290
        module_t            *p_network;
 
291
 
 
292
        if( psz_scope == NULL || *psz_scope == '\0' )
 
293
        {
 
294
            psz_scope = strdup( "8" );
 
295
        }
 
296
        snprintf( psz_address, 100, "[%s%c%s]",IPV6_ADDR_1,
 
297
                  psz_scope[0], IPV6_ADDR_2 );
 
298
        free( psz_scope );
 
299
 
 
300
        sock.i_type            = NETWORK_UDP;
 
301
        sock.psz_bind_addr     = psz_address;
 
302
        sock.i_bind_port       = HELLO_PORT;
 
303
        sock.psz_server_addr   = "";
 
304
        sock.i_server_port     = 0;
 
305
        sock.i_ttl             = 0;
 
306
        p_intf->p_private = (void*) &sock;
 
307
 
 
308
        p_network = module_Need( p_intf, "network", "ipv6", VLC_TRUE );
 
309
        if( p_network )
 
310
        {
 
311
            p_sys->fd[1] = sock.i_handle;
 
312
            module_Unneed( p_intf, p_network );
 
313
        }
 
314
        else
 
315
        {
 
316
            msg_Warn( p_intf, "failed to open %s:%d", psz_address, HELLO_PORT );
 
317
        }
 
318
    }
 
319
    if( p_sys->fd[0] <= 0 && p_sys->fd[1] <= 0 )
 
320
    {
 
321
        msg_Warn( p_intf, "IPV4 and IPV6 failed" );
 
322
        free( p_sys );
 
323
        return VLC_EGENERIC;
 
324
    }
 
325
 
 
326
    /* Create our playlist group */
 
327
    p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
 
328
                                                FIND_ANYWHERE );
 
329
    if( p_playlist )
 
330
    {
 
331
        playlist_group_t *p_group = playlist_CreateGroup( p_playlist , "SAP" );
 
332
        p_sys->i_group = p_group->i_id;
 
333
        vlc_object_release( p_playlist );
 
334
    }
 
335
 
 
336
    p_sys->i_announces = 0;
 
337
    p_sys->pp_announces = NULL;
 
338
 
 
339
    p_intf->pf_run = Run;
 
340
    p_intf->p_sys  = p_sys;
 
341
 
 
342
    return VLC_SUCCESS;
 
343
}
 
344
 
 
345
/*****************************************************************************
 
346
 * Close:
 
347
 *****************************************************************************/
 
348
static void Close( vlc_object_t *p_this )
 
349
{
 
350
    intf_thread_t *p_intf = (intf_thread_t*)p_this;
 
351
    intf_sys_t    *p_sys  = p_intf->p_sys;
 
352
    int i;
 
353
 
 
354
    if( p_sys->fd[0] > 0 )
 
355
    {
 
356
        close( p_sys->fd[0] );
 
357
    }
 
358
    if( p_sys->fd[1] > 0 )
 
359
    {
 
360
        close( p_sys->fd[1] );
 
361
    }
 
362
 
 
363
    for( i = 0 ; i< p_sys->i_announces ; i++ )
 
364
    {
 
365
        if( p_sys->pp_announces[i]->psz_name )
 
366
           free( p_sys->pp_announces[i]->psz_name );
 
367
        if( p_sys->pp_announces[i]->psz_uri )
 
368
           free( p_sys->pp_announces[i]->psz_uri );
 
369
        free( p_sys->pp_announces[i] );
 
370
    }
 
371
    free( p_sys->pp_announces );
 
372
 
 
373
    free( p_sys );
 
374
}
 
375
 
 
376
/*****************************************************************************
 
377
 * Run: sap thread
 
378
 *****************************************************************************
 
379
 * Listens to SAP packets, and sends them to packet_handle
 
380
 *****************************************************************************/
 
381
#define MAX_SAP_BUFFER 5000
 
382
 
 
383
static void Run( intf_thread_t *p_intf )
 
384
{
 
385
    intf_sys_t *p_sys  = p_intf->p_sys;
 
386
    uint8_t     buffer[MAX_SAP_BUFFER + 1];
 
387
    uint8_t    *p_end;
 
388
    
 
389
    /* Dirty hack to slow down the startup of the sap interface*/ 
 
390
    msleep(500000);    
 
391
 
 
392
    /* read SAP packets */
 
393
    while( !p_intf->b_die )
 
394
    {
 
395
        playlist_t *p_playlist= NULL;
 
396
        int i;
 
397
        int i_read = NetRead( p_intf, p_sys->fd, buffer, MAX_SAP_BUFFER );
 
398
        uint8_t *p_sdp;
 
399
        int i_version;
 
400
        int i_address_type;
 
401
        int b_reserved;
 
402
        int b_message_type;
 
403
        int b_encrypted;
 
404
        int b_compressed;
 
405
        unsigned char *p_decompressed_buffer;
 
406
        int i_decompressed_size;
 
407
 
 
408
        /* Check for items that need deletion */
 
409
        for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ )
 
410
        {
 
411
           struct sap_announce_t *p_announce;
 
412
           mtime_t i_timeout = (mtime_t)1000000*p_sys->i_timeout;
 
413
           if( mdate() - p_intf->p_sys->pp_announces[i]->i_last > i_timeout )
 
414
           {
 
415
               msg_Dbg(p_intf,"Time out for %s, deleting (%i/%i)",
 
416
                       p_intf->p_sys->pp_announces[i]->psz_name,
 
417
                       i , p_intf->p_sys->i_announces );
 
418
 
 
419
               /* Remove the playlist item */
 
420
               p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
 
421
                              FIND_ANYWHERE );
 
422
               if( p_playlist )
 
423
               {
 
424
                   int i_pos = playlist_GetPositionById( p_playlist,
 
425
                              p_intf->p_sys->pp_announces[i]->i_id );
 
426
                   playlist_Delete( p_playlist, i_pos );
 
427
               }
 
428
 
 
429
               vlc_object_release( p_playlist );
 
430
 
 
431
               /* Free the p_announce */
 
432
               p_announce =  p_intf->p_sys->pp_announces[i];
 
433
               if( p_announce->psz_name )
 
434
                  free(  p_announce->psz_name );
 
435
               if( p_announce->psz_uri )
 
436
                  free(  p_announce->psz_uri );
 
437
 
 
438
              /* Remove the sap_announce from the array */
 
439
              REMOVE_ELEM( p_intf->p_sys->pp_announces,
 
440
                           p_intf->p_sys->i_announces,
 
441
                           i );
 
442
 
 
443
              free( p_announce );
 
444
 
 
445
           }
 
446
        }
 
447
 
 
448
        /* Minimum length is > 6 */
 
449
        if( i_read <= 6 )
 
450
        {
 
451
            if( i_read < 0 )
 
452
            {
 
453
                msg_Warn( p_intf, "Cannot read in the socket" );
 
454
            }
 
455
            continue;
 
456
        }
 
457
 
 
458
        buffer[i_read] = '\0';
 
459
        p_end = &buffer[i_read];
 
460
 
 
461
        /* Parse the SAP header */
 
462
        i_version = buffer[0] >> 5;
 
463
        if( i_version != 1 )
 
464
        {
 
465
            msg_Warn( p_intf, "strange sap version %d found", i_version );
 
466
        }
 
467
        i_address_type = buffer[0] & 0x10;
 
468
        b_reserved = buffer[0] & 0x08;
 
469
        if( b_reserved != 0 )
 
470
        {
 
471
            msg_Warn( p_intf, "reserved bit incorrectly set" );
 
472
        }
 
473
        b_message_type = buffer[0] & 0x04;
 
474
        if( b_message_type != 0 )
 
475
        {
 
476
            msg_Warn( p_intf, "got session deletion packet" );
 
477
        }
 
478
        b_encrypted = buffer[0] & 0x02;
 
479
        if( b_encrypted )
 
480
        {
 
481
            msg_Warn( p_intf, "encrypted packet" );
 
482
        }
 
483
        b_compressed = buffer[0] & 0x01;
 
484
        p_sdp  = &buffer[4];
 
485
        if( i_address_type == 0 ) /* ipv4 source address */
 
486
        {
 
487
            p_sdp += 4;
 
488
        }
 
489
        else /* ipv6 source address */
 
490
        {
 
491
            p_sdp += 16;
 
492
        }
 
493
        if( b_compressed )
 
494
        {
 
495
#ifdef HAVE_ZLIB_H
 
496
            i_decompressed_size = do_decompress( p_sdp, &p_decompressed_buffer, i_read - ( p_sdp - buffer ) );
 
497
            if( i_decompressed_size > 0 && i_decompressed_size < MAX_SAP_BUFFER )
 
498
            {
 
499
                memcpy( p_sdp, p_decompressed_buffer, i_decompressed_size );
 
500
                p_sdp[i_decompressed_size] = '\0';
 
501
                p_end = &p_sdp[i_decompressed_size];
 
502
                free( p_decompressed_buffer );
 
503
            }
 
504
#else
 
505
            msg_Warn( p_intf, "Ignoring compressed sap packet" );
 
506
#endif
 
507
        }
 
508
        p_sdp += buffer[1]; /* size of signature */
 
509
        while( p_sdp < p_end - 1 && *p_sdp != '\0' && p_sdp[0] != 'v' && p_sdp[1] != '=' )
 
510
        {
 
511
            p_sdp++;
 
512
        }
 
513
        if( *p_sdp == '\0' )
 
514
        {
 
515
            p_sdp++;
 
516
        }
 
517
 
 
518
        if( p_sdp < p_end )
 
519
        {
 
520
            sess_descr_t *p_sd = parse_sdp( p_intf, p_sdp );
 
521
            if( p_sd )
 
522
            {
 
523
                sess_toitem ( p_intf, p_sd );
 
524
                free_sd ( p_sd );
 
525
            }
 
526
        }
 
527
        else
 
528
        {
 
529
            msg_Warn( p_intf, "ditching sap packet" );
 
530
        }
 
531
 
 
532
        memset( buffer, 0, MAX_SAP_BUFFER );
 
533
    }
 
534
}
 
535
 
 
536
/**********************************************************************
 
537
 * cfield_parse
 
538
 *********************************************************************
 
539
 * put into *ppsz_uri, the the uri in the cfield, psz_cfield.
 
540
 *********************************************************************/
 
541
 
 
542
static void cfield_parse( char *psz_cfield, char **ppsz_uri )
 
543
{
 
544
 
 
545
    char *psz_pos;
 
546
    if( psz_cfield )
 
547
    {
 
548
        psz_pos = psz_cfield;
 
549
 
 
550
        while( *psz_pos != ' ' && *psz_pos !='\0' )
 
551
        {
 
552
            psz_pos++;
 
553
        }
 
554
        psz_pos++;
 
555
        while( *psz_pos != ' ' && *psz_pos !='\0' )
 
556
        {
 
557
            psz_pos++;
 
558
        }
 
559
        psz_pos++;
 
560
        *ppsz_uri = psz_pos;
 
561
        while( *psz_pos != ' ' && *psz_pos !='/'
 
562
                        && *psz_pos != '\0' )
 
563
        {
 
564
            psz_pos++;
 
565
        }
 
566
        *psz_pos = '\0';
 
567
 
 
568
    }
 
569
    else
 
570
    {
 
571
        ppsz_uri = NULL;
 
572
    }
 
573
 
 
574
    return;
 
575
 
 
576
}
 
577
 
 
578
/**********************************************************************
 
579
 * mfield_parse
 
580
 *********************************************************************
 
581
 * put into *ppsz_proto, and *ppsz_port, the protocol and the port.
 
582
 *********************************************************************/
 
583
 
 
584
 
 
585
static void mfield_parse( char *psz_mfield, char **ppsz_proto,
 
586
               char **ppsz_port )
 
587
{
 
588
    char *psz_pos;
 
589
    char *psz_media;
 
590
    if( psz_mfield )
 
591
    {
 
592
        psz_pos = psz_mfield;
 
593
        psz_media = psz_mfield;
 
594
        while( *psz_pos != '\0' && *psz_pos != ' ' )
 
595
        {
 
596
            psz_pos++;
 
597
        }
 
598
        if( *psz_pos != '\0' )
 
599
        {
 
600
            *psz_pos = '\0';
 
601
            if( strcmp( psz_media, "video" ) && strcmp( psz_media, "audio" ) )
 
602
            {
 
603
                *ppsz_proto = NULL;
 
604
                *ppsz_port = NULL;
 
605
                return;
 
606
            }
 
607
        }
 
608
        psz_pos++;
 
609
        *ppsz_port = psz_pos;
 
610
        while( *psz_pos != '\0' && *psz_pos !=' ' && *psz_pos!='/' )
 
611
        {
 
612
            psz_pos++;
 
613
        }
 
614
        if( *psz_pos == '/' )  // FIXME does not support multi-port
 
615
        {
 
616
            *psz_pos = '\0';
 
617
            psz_pos++;
 
618
            while( *psz_pos != '\0' && *psz_pos !=' ' )
 
619
            {
 
620
            psz_pos++;
 
621
            }
 
622
        }
 
623
        *psz_pos = '\0';
 
624
        psz_pos++;
 
625
        *ppsz_proto = psz_pos;
 
626
        while( *psz_pos!='\0' && *psz_pos !=' ' &&
 
627
                        *psz_pos!='/' )
 
628
        {
 
629
            *psz_pos = tolower( *psz_pos );
 
630
            psz_pos++;
 
631
        }
 
632
        *psz_pos = '\0';
 
633
    }
 
634
    else
 
635
    {
 
636
        *ppsz_proto = NULL;
 
637
        *ppsz_port = NULL;
 
638
    }
 
639
    return;
 
640
}
 
641
 
 
642
 
 
643
/*******************************************************************
 
644
 * sess_toitem : changes a sess_descr_t into a hurd of
 
645
 * playlist_item_t, which are enqueued.
 
646
 *******************************************************************
 
647
 * Note : does not support sessions that take place on consecutive
 
648
 * port or adresses yet.
 
649
 *******************************************************************/
 
650
 
 
651
static void sess_toitem( intf_thread_t * p_intf, sess_descr_t * p_sd )
 
652
{
 
653
    struct sap_announce_t *p_announce;
 
654
    char *psz_uri, *psz_proto, *psz_item_uri;
 
655
    char *psz_port;
 
656
    char *psz_uri_default;
 
657
    int i_count, i, i_id = 0;
 
658
    vlc_bool_t b_http = VLC_FALSE;
 
659
    char *psz_http_path = NULL;
 
660
    playlist_t *p_playlist = NULL;
 
661
    playlist_item_t *p_item;
 
662
 
 
663
    psz_uri_default = NULL;
 
664
    if( p_sd->i_media > 1 || !config_GetInt( p_intf, "sap-parse" ) )
 
665
    {
 
666
        asprintf( &psz_uri, "sdp://%s", p_sd->psz_sdp );
 
667
        /* Check if we have already added the item */
 
668
        for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ )
 
669
        {
 
670
            if( !strcmp(p_intf->p_sys->pp_announces[i]->psz_uri,
 
671
                        psz_uri ) )
 
672
            {
 
673
                p_intf->p_sys->pp_announces[i]->i_last = mdate();
 
674
                free(psz_uri);
 
675
                return;
 
676
            }
 
677
        }
 
678
        /* Add it to the playlist */
 
679
        p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
 
680
                                      FIND_ANYWHERE );
 
681
        i_id = playlist_Add( p_playlist, psz_uri, p_sd->psz_sessionname ,
 
682
                      PLAYLIST_CHECK_INSERT, PLAYLIST_END );
 
683
        if( i_id != -1 )
 
684
        {
 
685
            playlist_item_t *p_item = playlist_ItemGetById( p_playlist, i_id );
 
686
            playlist_ItemSetGroup( p_item, p_intf->p_sys->i_group );
 
687
        }
 
688
 
 
689
        /* Remember it */
 
690
        p_announce = (struct sap_announce_t *)malloc(
 
691
                      sizeof(struct sap_announce_t) );
 
692
        if( p_sd->psz_sessionname )
 
693
        {
 
694
            p_announce->psz_name = strdup(  p_sd->psz_sessionname );
 
695
        }
 
696
        else
 
697
        {
 
698
            p_announce->psz_name = strdup( "" );
 
699
        }
 
700
        if( psz_uri )
 
701
        {
 
702
            p_announce->psz_uri  = strdup( psz_uri );
 
703
        }
 
704
        else
 
705
        {
 
706
            p_announce->psz_uri = strdup( "" );
 
707
        }
 
708
        p_announce->i_id = i_id;
 
709
        p_announce->i_last = mdate();
 
710
 
 
711
        INSERT_ELEM( p_intf->p_sys->pp_announces,
 
712
                     p_intf->p_sys->i_announces,
 
713
                     p_intf->p_sys->i_announces,
 
714
                     p_announce );
 
715
 
 
716
        vlc_object_release( p_playlist );
 
717
        free( psz_uri );
 
718
        return;
 
719
    }
 
720
 
 
721
    cfield_parse( p_sd->psz_connection, &psz_uri_default );
 
722
 
 
723
    for( i_count = 0 ; i_count < p_sd->i_media ; i_count++ )
 
724
    {
 
725
        int i_group = p_intf->p_sys->i_group;
 
726
 
 
727
        /* Build what we have to put in psz_item_uri, with the m and
 
728
         * c fields  */
 
729
 
 
730
        if( !p_sd->pp_media[i_count] )
 
731
        {
 
732
            return;
 
733
        }
 
734
 
 
735
        mfield_parse( p_sd->pp_media[i_count]->psz_medianame,
 
736
                        & psz_proto, & psz_port );
 
737
 
 
738
        if( !psz_proto || !psz_port )
 
739
        {
 
740
            return;
 
741
        }
 
742
 
 
743
        if( p_sd->pp_media[i_count]->psz_mediaconnection )
 
744
        {
 
745
            cfield_parse( p_sd->pp_media[i_count]->psz_mediaconnection,
 
746
                            & psz_uri );
 
747
        }
 
748
        else
 
749
        {
 
750
            psz_uri = psz_uri_default;
 
751
        }
 
752
 
 
753
        if( psz_uri == NULL )
 
754
        {
 
755
            return;
 
756
        }
 
757
 
 
758
        for( i = 0 ; i< p_sd->i_attributes ; i++ )
 
759
        {
 
760
            if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "type") &&
 
761
                strstr( p_sd->pp_attributes[i]->psz_value, "http") )
 
762
            {
 
763
                b_http = VLC_TRUE;
 
764
            }
 
765
            if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "http-path"))
 
766
            {
 
767
                psz_http_path = strdup(  p_sd->pp_attributes[i]->psz_value );
 
768
            }
 
769
            if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "plgroup"))
 
770
            {
 
771
                int i_group_id;
 
772
                p_playlist =
 
773
                (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
 
774
                                                        FIND_ANYWHERE );
 
775
                if( p_playlist == NULL )
 
776
                {
 
777
                    return;
 
778
                }
 
779
 
 
780
                i_group_id = playlist_GroupToId( p_playlist,
 
781
                                 p_sd->pp_attributes[i]->psz_value);
 
782
                if( i_group_id != 0 )
 
783
                {
 
784
                    i_group = i_group_id;
 
785
                }
 
786
                else
 
787
                {
 
788
                    playlist_group_t *p_group =
 
789
                            playlist_CreateGroup( p_playlist,
 
790
                                       p_sd->pp_attributes[i]->psz_value);
 
791
                    i_group = p_group->i_id;
 
792
                }
 
793
                vlc_object_release( p_playlist );
 
794
            }
 
795
        }
 
796
 
 
797
        /* Filling psz_uri */
 
798
        if( b_http == VLC_FALSE )
 
799
        {
 
800
            psz_item_uri = malloc( strlen( psz_proto ) + strlen( psz_uri ) +
 
801
                                 strlen( psz_port ) + 7 );
 
802
            if( ismult( psz_uri ) )
 
803
            {
 
804
                sprintf( psz_item_uri, "%s://@%s:%s",
 
805
                         psz_proto, psz_uri, psz_port );
 
806
            }
 
807
            else
 
808
            {
 
809
                sprintf( psz_item_uri, "%s://%s:%s",
 
810
                         psz_proto, psz_uri, psz_port );
 
811
            }
 
812
        }
 
813
        else
 
814
        {
 
815
            if( psz_http_path == NULL )
 
816
            {
 
817
                psz_http_path = strdup( "/" );
 
818
            }
 
819
            if( *psz_http_path == '/' )
 
820
            {
 
821
                asprintf( &psz_item_uri, "%s://%s:%s%s", psz_proto,
 
822
                         psz_uri, psz_port,psz_http_path );
 
823
            }
 
824
            else
 
825
            {
 
826
                asprintf( &psz_item_uri, "%s://%s:%s/%s", psz_proto, psz_uri,
 
827
                          psz_port, psz_http_path );
 
828
            }
 
829
 
 
830
            if( psz_http_path )
 
831
            {
 
832
                free( psz_http_path );
 
833
            }
 
834
        }
 
835
 
 
836
        /* Check if we already know this item */
 
837
         for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ )
 
838
         {
 
839
            if( !strcmp( p_intf->p_sys->pp_announces[i]->psz_uri,
 
840
                         psz_item_uri ) )
 
841
            {
 
842
                p_intf->p_sys->pp_announces[i]->i_last = mdate();
 
843
 
 
844
                /* Check if the name changed */
 
845
                if( strcmp( p_intf->p_sys->pp_announces[i]->psz_name,
 
846
                             p_sd->psz_sessionname ) )
 
847
                {
 
848
                    playlist_item_t *p_item;
 
849
                    p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
 
850
                                                  FIND_ANYWHERE );
 
851
 
 
852
                    msg_Dbg(p_intf, "Name changed (%s -> %s) for %s",
 
853
                            p_intf->p_sys->pp_announces[i]->psz_name,
 
854
                            p_sd->psz_sessionname,
 
855
                            psz_item_uri );
 
856
 
 
857
                    p_item = playlist_ItemGetById( p_playlist,
 
858
                                    p_intf->p_sys->pp_announces[i]->i_id );
 
859
 
 
860
                    /* Change the name in the item */
 
861
                    if( p_item->input.psz_name )
 
862
                        free( p_item->input.psz_name );
 
863
                    p_item->input.psz_name = strdup( p_sd->psz_sessionname);
 
864
 
 
865
                    /* Update the stored name */
 
866
                    if( p_intf->p_sys->pp_announces[i]->psz_name )
 
867
                        free( p_intf->p_sys->pp_announces[i]->psz_name );
 
868
                    p_intf->p_sys->pp_announces[i]->psz_name =
 
869
                                   strdup( p_sd->psz_sessionname );
 
870
 
 
871
                    vlc_object_release( p_playlist );
 
872
                }
 
873
                free( psz_item_uri );
 
874
                return;
 
875
            }
 
876
        }
 
877
 
 
878
        /* Add the item in the playlist */
 
879
        p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
 
880
                                      FIND_ANYWHERE );
 
881
        i_id = playlist_Add ( p_playlist, psz_item_uri ,
 
882
                               p_sd->psz_sessionname,
 
883
                               PLAYLIST_CHECK_INSERT, PLAYLIST_END );
 
884
        p_item = playlist_ItemGetById( p_playlist, i_id );
 
885
        if( p_item )
 
886
        {
 
887
            vlc_mutex_lock( &p_item->input.lock );
 
888
            playlist_ItemSetGroup( p_item, i_group );
 
889
            vlc_mutex_unlock( &p_item->input.lock );
 
890
        }
 
891
 
 
892
        /* Then remember it */
 
893
        p_announce = (struct sap_announce_t *)malloc(
 
894
                      sizeof(struct sap_announce_t) );
 
895
        if(  p_sd->psz_sessionname )
 
896
        {
 
897
            p_announce->psz_name = strdup(  p_sd->psz_sessionname );
 
898
        }
 
899
        else
 
900
        {
 
901
            p_announce->psz_name = strdup( "" );
 
902
        }
 
903
        if( psz_item_uri )
 
904
        {
 
905
            p_announce->psz_uri  = strdup( psz_item_uri );
 
906
        }
 
907
        else
 
908
        {
 
909
            p_announce->psz_uri = strdup( "" );
 
910
        }
 
911
        p_announce->i_id = i_id;
 
912
 
 
913
        p_announce->i_last = mdate();
 
914
 
 
915
        vlc_object_release( p_playlist );
 
916
 
 
917
        INSERT_ELEM( p_intf->p_sys->pp_announces,
 
918
                     p_intf->p_sys->i_announces,
 
919
                     p_intf->p_sys->i_announces,
 
920
                     p_announce );
 
921
        free( psz_item_uri );
 
922
    }
 
923
}
 
924
 
 
925
/***********************************************************************
 
926
 * parse_sdp : SDP parsing
 
927
 * *********************************************************************
 
928
 * Make a sess_descr_t with a psz
 
929
 ***********************************************************************/
 
930
 
 
931
static sess_descr_t *  parse_sdp( intf_thread_t * p_intf, char *p_packet )
 
932
{
 
933
    sess_descr_t *  sd;
 
934
 
 
935
    if( p_packet[0] != 'v' || p_packet[1] != '=' )
 
936
    {
 
937
        msg_Warn(p_intf, "bad SDP packet");
 
938
        return NULL;
 
939
    }
 
940
 
 
941
    sd = malloc( sizeof( sess_descr_t ) );
 
942
    sd->psz_sessionname = NULL;
 
943
    sd->psz_connection  = NULL;
 
944
    sd->psz_sdp         = strdup( p_packet );
 
945
 
 
946
    sd->i_media         = 0;
 
947
    sd->pp_media        = NULL;
 
948
    sd->i_attributes    = 0;
 
949
    sd->pp_attributes   = NULL;
 
950
 
 
951
    while( *p_packet != '\0'  )
 
952
    {
 
953
        char *psz_end;
 
954
 
 
955
        /* Search begin of field */
 
956
        while( *p_packet == '\n' || *p_packet == ' ' || *p_packet == '\t' )
 
957
        {
 
958
            p_packet++;
 
959
        }
 
960
        /* search end of line */
 
961
        if( ( psz_end = strchr( p_packet, '\n' ) ) == NULL )
 
962
        {
 
963
            psz_end = p_packet + strlen( p_packet );
 
964
        }
 
965
        if( psz_end > p_packet && *(psz_end - 1 ) == '\r' )
 
966
        {
 
967
            psz_end--;
 
968
        }
 
969
 
 
970
        if( psz_end <= p_packet )
 
971
        {
 
972
            break;
 
973
        }
 
974
        *psz_end++ = '\0';
 
975
 
 
976
        if( p_packet[1] != '=' )
 
977
        {
 
978
            msg_Warn( p_intf, "invalid packet") ;
 
979
            free_sd( sd );
 
980
            return NULL;
 
981
        }
 
982
 
 
983
        switch( p_packet[0] )
 
984
        {
 
985
            case( 'v' ):
 
986
                sd->i_version = atoi( &p_packet[2] );
 
987
                break;
 
988
            case( 's' ):
 
989
                sd->psz_sessionname = strdup( &p_packet[2] );
 
990
                break;
 
991
            case ( 'o' ):
 
992
            case( 'i' ):
 
993
            case( 'u' ):
 
994
            case( 'e' ):
 
995
            case( 'p' ):
 
996
            case( 't' ):
 
997
            case( 'r' ):
 
998
                break;
 
999
            case( 'a' ):
 
1000
            {
 
1001
                char *psz_eof = strchr( &p_packet[2], ':' );
 
1002
 
 
1003
                if( psz_eof && psz_eof[1] != '\0' )
 
1004
                {
 
1005
                    attr_descr_t *attr = malloc( sizeof( attr_descr_t ) );
 
1006
 
 
1007
                    *psz_eof++ = '\0';
 
1008
 
 
1009
                    attr->psz_field = strdup( &p_packet[2] );
 
1010
                    attr->psz_value = strdup( psz_eof );
 
1011
 
 
1012
                    TAB_APPEND( sd->i_attributes, sd->pp_attributes, attr );
 
1013
                }
 
1014
                break;
 
1015
            }
 
1016
 
 
1017
            case( 'm' ):
 
1018
            {
 
1019
                media_descr_t *media = malloc( sizeof( media_descr_t ) );
 
1020
 
 
1021
                media->psz_medianame = strdup( &p_packet[2] );
 
1022
                media->psz_mediaconnection = NULL;
 
1023
 
 
1024
                TAB_APPEND( sd->i_media, sd->pp_media, media );
 
1025
                break;
 
1026
            }
 
1027
 
 
1028
            case( 'c' ):
 
1029
                if( sd->i_media <= 0 )
 
1030
                {
 
1031
                    sd->psz_connection = strdup( &p_packet[2] );
 
1032
                }
 
1033
                else
 
1034
                {
 
1035
                    sd->pp_media[sd->i_media-1]->psz_mediaconnection = strdup( &p_packet[2] );
 
1036
                }
 
1037
               break;
 
1038
 
 
1039
            default:
 
1040
               break;
 
1041
        }
 
1042
 
 
1043
        p_packet = psz_end;
 
1044
    }
 
1045
 
 
1046
    return sd;
 
1047
}
 
1048
 
 
1049
#define FREE( p ) \
 
1050
    if( p ) { free( p ); (p) = NULL; }
 
1051
static void free_sd( sess_descr_t * p_sd )
 
1052
{
 
1053
    int i;
 
1054
 
 
1055
    FREE( p_sd->psz_sessionname );
 
1056
    FREE( p_sd->psz_connection );
 
1057
    FREE( p_sd->psz_sdp );
 
1058
 
 
1059
    for( i = 0; i < p_sd->i_media ; i++ )
 
1060
    {
 
1061
        FREE( p_sd->pp_media[i]->psz_medianame );
 
1062
        FREE( p_sd->pp_media[i]->psz_mediaconnection );
 
1063
        FREE( p_sd->pp_media[i] );
 
1064
    }
 
1065
    for( i = 0; i < p_sd->i_attributes ; i++ )
 
1066
    {
 
1067
        FREE( p_sd->pp_attributes[i]->psz_field );
 
1068
        FREE( p_sd->pp_attributes[i]->psz_value );
 
1069
        FREE( p_sd->pp_attributes[i] );
 
1070
    }
 
1071
    FREE( p_sd->pp_attributes );
 
1072
    FREE( p_sd->pp_media );
 
1073
 
 
1074
    free( p_sd );
 
1075
}
 
1076
 
 
1077
/***********************************************************************
 
1078
 * ismult: returns true if we have a multicast address
 
1079
 ***********************************************************************/
 
1080
 
 
1081
static int ismult( char *psz_uri )
 
1082
{
 
1083
    char *psz_end;
 
1084
    int  i_value;
 
1085
 
 
1086
    i_value = strtol( psz_uri, &psz_end, 0 );
 
1087
 
 
1088
    /* IPv6 */
 
1089
    if( psz_uri[0] == '[')
 
1090
    {
 
1091
      if( strncasecmp( &psz_uri[1], "FF0" , 3) ||
 
1092
          strncasecmp( &psz_uri[2], "FF0" , 3))
 
1093
            return( VLC_TRUE );
 
1094
        else
 
1095
            return( VLC_FALSE );
 
1096
    }
 
1097
 
 
1098
    if( *psz_end != '.' ) { return( VLC_FALSE ); }
 
1099
 
 
1100
    return( i_value < 224 ? VLC_FALSE : VLC_TRUE );
 
1101
}
 
1102
 
 
1103
 
 
1104
 
 
1105
/*****************************************************************************
 
1106
 * Read: read on a file descriptor, checking b_die periodically
 
1107
 *****************************************************************************
 
1108
 * Taken from udp.c
 
1109
 *****************************************************************************/
 
1110
static ssize_t NetRead( intf_thread_t *p_intf,
 
1111
                        int fd[2], uint8_t *p_buffer, int i_len )
 
1112
{
 
1113
#ifdef UNDER_CE
 
1114
    return -1;
 
1115
#else
 
1116
    struct timeval  timeout;
 
1117
    fd_set          fds;
 
1118
    int             i_ret;
 
1119
    int             i_handle_max = __MAX( fd[0], fd[1] );
 
1120
 
 
1121
    /* Initialize file descriptor set */
 
1122
    FD_ZERO( &fds );
 
1123
    if( fd[0] > 0 ) FD_SET( fd[0], &fds );
 
1124
    if( fd[1] > 0 ) FD_SET( fd[1], &fds );
 
1125
 
 
1126
    /* We'll wait 0.5 second if nothing happens */
 
1127
    timeout.tv_sec = 0;
 
1128
    timeout.tv_usec = 500000;
 
1129
 
 
1130
    /* Find if some data is available */
 
1131
    i_ret = select( i_handle_max + 1, &fds, NULL, NULL, &timeout );
 
1132
 
 
1133
    if( i_ret == -1 && errno != EINTR )
 
1134
    {
 
1135
        msg_Err( p_intf, "network select error (%s)", strerror(errno) );
 
1136
    }
 
1137
    else if( i_ret > 0 )
 
1138
    {
 
1139
        if( fd[0] > 0 && FD_ISSET( fd[0], &fds ) )
 
1140
        {
 
1141
             return recv( fd[0], p_buffer, i_len, 0 );
 
1142
        }
 
1143
        else if( fd[1] > 0 && FD_ISSET( fd[1], &fds ) )
 
1144
        {
 
1145
             return recv( fd[1], p_buffer, i_len, 0 );
 
1146
        }
 
1147
    }
 
1148
    return 0;
 
1149
#endif
 
1150
}