X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fmisc%2Fsap.c;h=f51503417d83572f3dd918e9bc3f3a9fa6751b50;hb=499a384591d4238957f08348471fab5f36747fe7;hp=6614fc505657fad8b95134af26250564297d3066;hpb=5a13b475ceef886157d0d87decab17a3b0e354f8;p=vlc diff --git a/modules/misc/sap.c b/modules/misc/sap.c index 6614fc5056..f51503417d 100644 --- a/modules/misc/sap.c +++ b/modules/misc/sap.c @@ -2,9 +2,12 @@ * sap.c : SAP interface module ***************************************************************************** * Copyright (C) 2001 VideoLAN - * $Id: sap.c,v 1.8 2003/02/20 01:52:46 sigmunau Exp $ + * $Id: sap.c,v 1.52 2004/01/29 17:51:08 zorglub Exp $ * * Authors: Arnaud Schauly + * Clément Stenac + * Damien Lucas + * Laurent Aimar * * 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 @@ -25,26 +28,19 @@ * Preamble *****************************************************************************/ #include /* malloc(), free() */ -#include - -#include /* ENOMEM */ -#include -#include -#include #include #include -#include + +#include /* ENOMEM */ +#include #ifdef HAVE_UNISTD_H # include #endif - #ifdef HAVE_SYS_TIME_H # include #endif -#include - #ifdef WIN32 # include @@ -62,8 +58,18 @@ # endif #endif +#ifdef UNDER_CE +# define close(a) CloseHandle(a) +#elif defined( WIN32 ) +# define close(a) closesocket(a) +#endif + #include "network.h" +#ifdef HAVE_ZLIB_H +# include +#endif + #define MAX_LINE_LENGTH 256 /* SAP is always on that port */ @@ -71,305 +77,449 @@ #define HELLO_GROUP "224.2.127.254" #define ADD_SESSION 1 +#define IPV6_ADDR_1 "FF0" /* Scope is inserted between them */ +#define IPV6_ADDR_2 "::2:7FFE" + + /***************************************************************************** - * Local prototypes + * Module descriptor *****************************************************************************/ +#define SAP_ADDR_TEXT N_("SAP multicast address") +#define SAP_ADDR_LONGTEXT N_("SAP multicast address") +#define SAP_IPV4_TEXT N_("IPv4-SAP listening") +#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") +#define SAP_SCOPE_TEXT N_("IPv6 SAP scope") +#define SAP_SCOPE_LONGTEXT N_( \ + "Sets the scope for IPv6 announces (default is 8)") +#define SAP_TIMEOUT_TEXT N_("SAP timeout (seconds)") +#define SAP_TIMEOUT_LONGTEXT N_( \ + "Sets the time before SAP items get deleted if no new announce" \ + "is received") + +static int Open ( vlc_object_t * ); +static void Close( vlc_object_t * ); -typedef struct media_descr_t media_descr_t; -typedef struct sess_descr_t sess_descr_t; - -static int Activate ( vlc_object_t * ); -static void Run ( intf_thread_t *p_intf ); -static int Kill ( intf_thread_t * ); +vlc_module_begin(); + set_description( _("SAP interface") ); + + add_string( "sap-addr", NULL, NULL, + SAP_ADDR_TEXT, SAP_ADDR_LONGTEXT, VLC_TRUE ); + add_bool( "sap-ipv4", 1 , NULL, + SAP_IPV4_TEXT,SAP_IPV4_LONGTEXT, VLC_TRUE); + add_bool( "sap-ipv6", 0 , NULL, + SAP_IPV6_TEXT, SAP_IPV6_LONGTEXT, VLC_TRUE); + add_string( "sap-ipv6-scope", "8" , NULL, + SAP_SCOPE_TEXT, SAP_SCOPE_LONGTEXT, VLC_TRUE); + add_integer( "sap-timeout", 1800, NULL, + SAP_TIMEOUT_TEXT, SAP_TIMEOUT_LONGTEXT, VLC_TRUE); -static ssize_t NetRead ( intf_thread_t*, int , byte_t *, size_t ); + set_capability( "interface", 0 ); + set_callbacks( Open, Close ); +vlc_module_end(); -/* playlist related functions */ -static int sess_toitem( intf_thread_t *, sess_descr_t * ); +/***************************************************************************** + * Local prototypes + *****************************************************************************/ -/* sap/sdp related functions */ -static int parse_sap ( char ** ); -static int packet_handle ( intf_thread_t *, char ** ); -static sess_descr_t * parse_sdp( char *,intf_thread_t * ) ; +static void Run ( intf_thread_t *p_intf ); +static ssize_t NetRead( intf_thread_t *, int fd[2], uint8_t *, int ); -/* specific sdp fields parsing */ +typedef struct media_descr_t media_descr_t; +typedef struct sess_descr_t sess_descr_t; +typedef struct attr_descr_t attr_descr_t; -static void cfield_parse( char *, char ** ); -static void mfield_parse( char *psz_mfield, char **ppsz_proto, - char **ppsz_port ); +static void sess_toitem( intf_thread_t *, sess_descr_t * ); +static sess_descr_t * parse_sdp( intf_thread_t *, char * ) ; static void free_sd( sess_descr_t * ); + +/* Detect multicast addresses */ static int ismult( char * ); /* The struct that contains sdp informations */ -struct sess_descr_t { - char *psz_version; - char *psz_origin; +struct sess_descr_t +{ + int i_version; char *psz_sessionname; - char *psz_information; - char *psz_uri; - char *psz_emails; - char *psz_phone; - char *psz_time; - char *psz_repeat; - char *psz_attribute; char *psz_connection; - int i_media; - media_descr_t ** pp_media; + char *psz_sdp; + + int i_media; + media_descr_t **pp_media; + int i_attributes; + attr_descr_t **pp_attributes; }; /* All this informations are not useful yet. */ -struct media_descr_t { +struct media_descr_t +{ char *psz_medianame; char *psz_mediaconnection; }; -/***************************************************************************** - * Module descriptor - *****************************************************************************/ -vlc_module_begin(); - add_category_hint( N_("SAP"), NULL, VLC_TRUE ); - add_string( "sap-addr", NULL, NULL, - "SAP multicast address", "SAP multicast address", VLC_TRUE ); - set_description( _("SAP interface module") ); - set_capability( "interface", 0 ); - set_callbacks( Activate, NULL); -vlc_module_end(); - -/***************************************************************************** - * Activate: initialize and create stuff - *****************************************************************************/ -static int Activate( vlc_object_t *p_this ) +struct attr_descr_t { - intf_thread_t *p_intf = (intf_thread_t*)p_this; - - p_intf->pf_run = Run; - - return VLC_SUCCESS; -} - -/***************************************************************************** - * Run: sap thread - ***************************************************************************** - * Listens to SAP packets, and sends them to packet_handle - *****************************************************************************/ + char *psz_field; + char *psz_value; +}; -static void Run( intf_thread_t *p_intf ) +struct sap_announce_t { - char *psz_addr; - char *psz_network = NULL; - struct sockaddr_in addr; - int fd,addrlen; - char *psz_buf; + mtime_t i_last; + int i_id; + char *psz_name; + char *psz_uri; +}; - module_t *p_network; - network_socket_t socket_desc; +struct intf_sys_t +{ + /* IPV4 and IPV6 */ + int fd[2]; - psz_buf = NULL; + /* playlist group */ + int i_group; - if( !(psz_addr = config_GetPsz( p_intf, "sap-addr" ) ) ) - { - psz_addr = strdup( HELLO_GROUP ); - } + /* Table of announces */ + int i_announces; + struct sap_announce_t **pp_announces; - /* Prepare the network_socket_t structure */ - socket_desc.i_type = NETWORK_UDP; - socket_desc.psz_bind_addr = psz_addr; - socket_desc.i_bind_port = HELLO_PORT; - socket_desc.psz_server_addr = ""; - socket_desc.i_server_port = 0; - p_intf->p_private = (void*) &socket_desc; + int i_timeout; +}; - psz_network = "ipv4"; // FIXME +#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)); - /* Create, Bind the socket, ... with the appropriate module */ + dstsize = d_stream.total_out; + inflateEnd(&d_stream); - if( !( p_network = module_Need( p_intf, "network", psz_network ) ) ) - { - msg_Warn( p_intf, "failed to open a connection (udp)" ); - return; - } - module_Unneed( p_intf, p_network ); + *_dst = (unsigned char *)realloc(dst, dstsize); - fd = socket_desc.i_handle; + return dstsize; +} +#endif +/***************************************************************************** + * 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 ) ); - /* read SAP packets */ + playlist_t *p_playlist; - psz_buf = malloc( 2000 ); - if( psz_buf == NULL ) + p_sys->i_timeout = config_GetInt(p_intf,"sap-timeout"); + p_sys->fd[0] = -1; + p_sys->fd[1] = -1; + if( config_GetInt( p_intf, "sap-ipv4" ) ) { - msg_Err( p_intf, "Not enough memory for psz_buf in Run()" ); - return; + char *psz_address = config_GetPsz( p_intf, "sap-addr" ); + network_socket_t sock; + module_t *p_network; + if( psz_address == NULL || *psz_address == '\0' ) + { + psz_address = strdup( HELLO_GROUP ); + } + + /* Prepare the network_socket_t structure */ + 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; + + p_network = module_Need( p_intf, "network", "ipv4" ); + if( p_network ) + { + p_sys->fd[0] = sock.i_handle; + module_Unneed( p_intf, p_network ); + } + else + { + msg_Warn( p_intf, "failed to open %s:%d", psz_address, HELLO_PORT ); + } + free( psz_address ); } - while( !p_intf->b_die ) + if( config_GetInt( p_intf, "sap-ipv6" ) ) { - int i_read; - addrlen=sizeof(addr); - - - memset(psz_buf, 0, 2000); - - i_read = NetRead( p_intf, fd, psz_buf, 2000 ); + char psz_address[100]; + char *psz_scope = config_GetPsz( p_intf, "sap-ipv6-scope" ); + network_socket_t sock; + module_t *p_network; - if( i_read < 0 ) + if( psz_scope == NULL || *psz_scope == '\0' ) { - msg_Err( p_intf, "Cannot read in the socket" ); + psz_scope = strdup( "8" ); } - if( i_read == 0 ) + snprintf( psz_address, 100, "[%s%c%s]",IPV6_ADDR_1, + psz_scope[0], IPV6_ADDR_2 ); + free( psz_scope ); + + 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; + + p_network = module_Need( p_intf, "network", "ipv6" ); + if( p_network ) { - continue; + p_sys->fd[1] = sock.i_handle; + module_Unneed( p_intf, p_network ); } + else + { + msg_Warn( p_intf, "failed to open %s:%d", psz_address, HELLO_PORT ); + } + } + if( p_sys->fd[0] <= 0 && p_sys->fd[1] <= 0 ) + { + msg_Warn( p_intf, "IPV4 and IPV6 failed" ); + free( p_sys ); + return VLC_EGENERIC; + } - - packet_handle( p_intf, &psz_buf ); - + /* Create our playlist group */ + p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, + FIND_ANYWHERE ); + if( p_playlist ) + { + playlist_group_t *p_group = playlist_CreateGroup( p_playlist , "SAP" ); + p_sys->i_group = p_group->i_id; + vlc_object_release( p_playlist ); } - free( psz_buf ); - /* Closing socket */ + p_sys->i_announces = 0; + p_sys->pp_announces = NULL; -#ifdef UNDER_CE - CloseHandle( socket_desc.i_handle ); -#elif defined( WIN32 ) - closesocket( socket_desc.i_handle ); -#else - close( socket_desc.i_handle ); -#endif + p_intf->pf_run = Run; + p_intf->p_sys = p_sys; + return VLC_SUCCESS; } -/******************************************************************** - * Kill - ******************************************************************* - * Kills the SAP interface. - ********************************************************************/ -static int Kill( intf_thread_t *p_intf ) +/***************************************************************************** + * Close: + *****************************************************************************/ +static void Close( vlc_object_t *p_this ) { + intf_thread_t *p_intf = (intf_thread_t*)p_this; + intf_sys_t *p_sys = p_intf->p_sys; + int i; + + if( p_sys->fd[0] > 0 ) + { + close( p_sys->fd[0] ); + } + if( p_sys->fd[1] > 0 ) + { + close( p_sys->fd[1] ); + } - p_intf->b_die = VLC_TRUE; + 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] ); + } - return VLC_SUCCESS; + free( p_sys ); } -/******************************************************************* - * 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. - *******************************************************************/ +/***************************************************************************** + * Run: sap thread + ***************************************************************************** + * Listens to SAP packets, and sends them to packet_handle + *****************************************************************************/ +#define MAX_SAP_BUFFER 5000 -static int sess_toitem( intf_thread_t * p_intf, sess_descr_t * p_sd ) +static void Run( intf_thread_t *p_intf ) { - playlist_item_t * p_item; - char *psz_uri, *psz_proto; - char *psz_port; - char *psz_uri_default; - int i_multicast; - int i_count; - playlist_t *p_playlist; - - psz_uri_default = NULL; - cfield_parse( p_sd->psz_connection, &psz_uri_default ); + intf_sys_t *p_sys = p_intf->p_sys; + uint8_t buffer[MAX_SAP_BUFFER + 1]; + uint8_t *p_end; - for( i_count=0 ; i_count <= p_sd->i_media ; i_count ++ ) + /* read SAP packets */ + while( !p_intf->b_die ) { - p_item = malloc( sizeof( playlist_item_t ) ); - if( p_item == NULL ) + playlist_t *p_playlist= NULL; + int i; + int i_read = NetRead( p_intf, p_sys->fd, buffer, MAX_SAP_BUFFER ); + uint8_t *p_sdp; + 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; + + /* Check for items that need deletion */ + for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ ) { - msg_Err( p_intf, "Not enough memory for p_item in sesstoitem()" ); - return 0; + struct sap_announce_t *p_announce; + mtime_t i_timeout = (mtime_t)1000000*p_sys->i_timeout; + if( mdate() - p_intf->p_sys->pp_announces[i]->i_last > i_timeout ) + { + msg_Dbg(p_intf,"Time out for %s, deleting (%i/%i)", + p_intf->p_sys->pp_announces[i]->psz_name, + i , p_intf->p_sys->i_announces ); + + /* Remove the playlist item */ + p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, + FIND_ANYWHERE ); + if( p_playlist ) + { + int i_pos = playlist_GetPositionById( p_playlist, + p_intf->p_sys->pp_announces[i]->i_id ); + playlist_Delete( p_playlist, i_pos ); + } + + vlc_object_release( p_playlist ); + + /* Free the p_announce */ + p_announce = p_intf->p_sys->pp_announces[i]; + 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 */ + REMOVE_ELEM( p_intf->p_sys->pp_announces, + p_intf->p_sys->i_announces, + i ); + + free( p_announce ); + + } } - p_item->psz_name = strdup( p_sd->psz_sessionname ); - p_item->i_type = 0; - p_item->i_status = 0; - p_item->b_autodeletion = VLC_FALSE; - p_item->psz_uri = NULL; - - psz_uri = NULL; - - /* Build what we have to put in p_item->psz_uri, with the m and - * c fields */ - if( !p_sd->pp_media[i_count] ) + /* Minimum length is > 6 */ + if( i_read <= 6 ) { - return 0; + if( i_read < 0 ) + { + msg_Warn( p_intf, "Cannot read in the socket" ); + } + continue; } - mfield_parse( p_sd->pp_media[i_count]->psz_medianame, - & psz_proto, & psz_port ); + buffer[i_read] = '\0'; + p_end = &buffer[i_read]; - if( !psz_proto || !psz_port ) + /* Parse the SAP header */ + i_version = buffer[0] >> 5; + if( i_version != 1 ) { - return 0; + msg_Warn( p_intf, "strange sap version %d found", i_version ); } - - if( p_sd->pp_media[i_count]->psz_mediaconnection ) + i_address_type = buffer[0] & 0x10; + b_reserved = buffer[0] & 0x08; + if( b_reserved != 0 ) { - cfield_parse( p_sd->pp_media[i_count]->psz_mediaconnection, - & psz_uri ); + msg_Warn( p_intf, "reserved bit incorrectly set" ); } - else + b_message_type = buffer[0] & 0x04; + if( b_message_type != 0 ) { - psz_uri = NULL; + msg_Warn( p_intf, "got session deletion packet" ); } - - if( psz_uri == NULL ) + b_encrypted = buffer[0] & 0x02; + if( b_encrypted ) { - if( psz_uri_default ) - { - psz_uri = psz_uri_default; - } - else + msg_Warn( p_intf, "encrypted packet" ); + } + b_compressed = buffer[0] & 0x01; + p_sdp = &buffer[4]; + 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 ) ); + if( i_decompressed_size > 0 && i_decompressed_size < MAX_SAP_BUFFER ) { - return 0; + 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 } - - - /* Filling p_item->psz_uri */ - i_multicast = ismult( psz_uri ); - - p_item->psz_uri = malloc( strlen( psz_proto ) + strlen( psz_uri ) + - strlen( psz_port ) + 5 +i_multicast ); - if( p_item->psz_uri == NULL ) + p_sdp += buffer[1]; /* size of signature */ + while( p_sdp < p_end - 1 && *p_sdp != '\0' && p_sdp[0] != 'v' && p_sdp[1] != '=' ) { - msg_Err( p_intf, "Not enough memory"); - free( p_item ); - return 0; + p_sdp++; } - - if( i_multicast == 1) + if( *p_sdp == '\0' ) { - sprintf( p_item->psz_uri, "%s://@%s:%s", psz_proto, - psz_uri, psz_port ); + p_sdp++; } - else + + if( p_sdp < p_end ) { - sprintf( p_item->psz_uri, "%s://%s:%s", psz_proto, - psz_uri, psz_port ); + sess_descr_t *p_sd = parse_sdp( p_intf, p_sdp ); + if( p_sd ) + { + sess_toitem ( p_intf, p_sd ); + free_sd ( p_sd ); + } } - - /* Enqueueing p_item in the playlist */ - - if( p_item ) + else { - 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 ); + msg_Warn( p_intf, "ditching sap packet" ); } - } - - - return 1; - } /********************************************************************** @@ -425,16 +575,28 @@ static void mfield_parse( char *psz_mfield, char **ppsz_proto, char **ppsz_port ) { char *psz_pos; + char *psz_media; if( psz_mfield ) { psz_pos = psz_mfield; + psz_media = psz_mfield; while( *psz_pos != '\0' && *psz_pos != ' ' ) { psz_pos++; } + if( *psz_pos != '\0' ) + { + *psz_pos = '\0'; + if( strcmp( psz_media, "video" ) && strcmp( psz_media, "audio" ) ) + { + *ppsz_proto = NULL; + *ppsz_port = NULL; + return; + } + } psz_pos++; *ppsz_port = psz_pos; - while( *psz_pos != '\0' && *psz_pos && *psz_pos !=' ' && *psz_pos!='/' ) + while( *psz_pos != '\0' && *psz_pos !=' ' && *psz_pos!='/' ) { psz_pos++; } @@ -466,42 +628,283 @@ static void mfield_parse( char *psz_mfield, char **ppsz_proto, return; } -/*********************************************************************** - * parse_sap : Takes care of the SAP headers - *********************************************************************** - * checks if the packet has the true headers ; - ***********************************************************************/ -static int parse_sap( char ** ppsz_sa_packet ) { /* Dummy Parser : does nothing !*/ - if( *ppsz_sa_packet ) return ADD_SESSION; //Add this packet - return 0; /* FIXME */ -} +/******************************************************************* + * 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. + *******************************************************************/ -/************************************************************************* - * packet_handle : handle the received packet and enques the - * the understated session - *************************************************************************/ +static void sess_toitem( intf_thread_t * p_intf, sess_descr_t * p_sd ) +{ + struct sap_announce_t *p_announce; + char *psz_uri, *psz_proto, *psz_item_uri; + char *psz_port; + char *psz_uri_default; + int i_count , i , i_pos , i_id = 0; + vlc_bool_t b_http = VLC_FALSE; + char *psz_http_path = NULL; + playlist_t *p_playlist = NULL; + playlist_item_t *p_item; -static int packet_handle( intf_thread_t * p_intf, char ** ppsz_packet ) { - int j=0; - sess_descr_t * p_sd; + psz_uri_default = NULL; + if( p_sd->i_media > 1 ) + { + asprintf( &psz_uri, "sdp://%s", p_sd->psz_sdp ); + /* Check if we have already added the item */ + for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ ) + { + if( !strcmp(p_intf->p_sys->pp_announces[i]->psz_uri, + psz_uri ) ) + { + p_intf->p_sys->pp_announces[i]->i_last = mdate(); + free(psz_uri); + return; + } + } + /* Add it to the playlist */ + p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, + FIND_ANYWHERE ); + i_id = playlist_Add( p_playlist, psz_uri, p_sd->psz_sessionname , + PLAYLIST_CHECK_INSERT, PLAYLIST_END ); + if( i_id != -1 ) + { + playlist_item_t *p_item = playlist_ItemGetById( p_playlist, i_id ); + playlist_ItemSetGroup( p_item, p_intf->p_sys->i_group ); + } - j=parse_sap( ppsz_packet ); + /* Remember it */ + p_announce = (struct sap_announce_t *)malloc( + sizeof(struct sap_announce_t) ); + if( p_sd->psz_sessionname ) + { + p_announce->psz_name = strdup( p_sd->psz_sessionname ); + } + else + { + p_announce->psz_name = strdup( "" ); + } + if( psz_uri ) + { + p_announce->psz_uri = strdup( psz_uri ); + } + else + { + p_announce->psz_uri = strdup( "" ); + } + p_announce->i_id = i_id; + p_announce->i_last = mdate(); - if(j != 0) { + INSERT_ELEM( p_intf->p_sys->pp_announces, + p_intf->p_sys->i_announces, + p_intf->p_sys->i_announces, + p_announce ); - p_sd = parse_sdp( *ppsz_packet, p_intf ); + vlc_object_release( p_playlist ); + free( psz_uri ); + return; + } - sess_toitem ( p_intf, p_sd ); + cfield_parse( p_sd->psz_connection, &psz_uri_default ); - free_sd ( p_sd ); - return 1; - } - return 0; // Invalid Packet -} + for( i_count = 0 ; i_count < p_sd->i_media ; i_count++ ) + { + int i_group = p_intf->p_sys->i_group; + /* Build what we have to put in psz_item_uri, with the m and + * c fields */ + if( !p_sd->pp_media[i_count] ) + { + return; + } + 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; + } + + 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 ); + } + if(!strcasecmp( p_sd->pp_attributes[i]->psz_field , "plgroup")) + { + int i_group_id; + p_playlist = + (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, + FIND_ANYWHERE ); + if( p_playlist == NULL ) + { + return; + } + + i_group_id = playlist_GroupToId( p_playlist, + p_sd->pp_attributes[i]->psz_value); + if( i_group_id != 0 ) + { + i_group = i_group_id; + } + else + { + playlist_group_t *p_group = + playlist_CreateGroup( p_playlist, + p_sd->pp_attributes[i]->psz_value); + i_group = p_group->i_id; + } + vlc_object_release( p_playlist ); + } + } + + /* Filling psz_uri */ + if( b_http == VLC_FALSE ) + { + psz_item_uri = malloc( strlen( psz_proto ) + strlen( psz_uri ) + + strlen( psz_port ) + 7 ); + if( ismult( psz_uri ) ) + { + sprintf( psz_item_uri, "%s://@%s:%s", + psz_proto, psz_uri, psz_port ); + } + else + { + sprintf( psz_item_uri, "%s://%s:%s", + psz_proto, psz_uri, psz_port ); + } + } + else + { + if( psz_http_path == NULL ) + { + psz_http_path = strdup( "/" ); + } + + psz_item_uri = malloc( strlen( psz_proto ) + strlen( psz_uri ) + + strlen( psz_port ) + strlen(psz_http_path) + + 5 ); + sprintf( psz_item_uri, "%s://%s:%s%s", psz_proto, + psz_uri, psz_port,psz_http_path ); + + if( psz_http_path ) + { + free( psz_http_path ); + } + } + + /* Check if we already know this item */ + for( i = 0 ; i< p_intf->p_sys->i_announces ; i++ ) + { + if( !strcmp( p_intf->p_sys->pp_announces[i]->psz_uri, + psz_item_uri ) ) + { + p_intf->p_sys->pp_announces[i]->i_last = mdate(); + + /* Check if the name changed */ + if( strcmp( p_intf->p_sys->pp_announces[i]->psz_name, + p_sd->psz_sessionname ) ) + { + playlist_item_t *p_item; + p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, + FIND_ANYWHERE ); + + msg_Dbg(p_intf, "Name changed (%s -> %s) for %s", + p_intf->p_sys->pp_announces[i]->psz_name, + p_sd->psz_sessionname, + psz_item_uri ); + + p_item = playlist_ItemGetById( p_playlist, + p_intf->p_sys->pp_announces[i]->i_id ); + + /* Change the name in the item */ + if( p_item->psz_name ) + free( p_item->psz_name ); + p_item->psz_name = strdup( p_sd->psz_sessionname); + + /* Update the stored name */ + if( p_intf->p_sys->pp_announces[i]->psz_name ) + free( p_intf->p_sys->pp_announces[i]->psz_name ); + p_intf->p_sys->pp_announces[i]->psz_name = + strdup( p_sd->psz_sessionname ); + + vlc_object_release( p_playlist ); + } + return; + } + } + + /* Add the item in the playlist */ + p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, + FIND_ANYWHERE ); + i_id = playlist_Add ( p_playlist, psz_item_uri , + p_sd->psz_sessionname, + PLAYLIST_CHECK_INSERT, PLAYLIST_END ); + p_item = playlist_ItemGetById( p_playlist, i_id ); + if( p_item ) + { + vlc_mutex_lock( &p_item->lock ); + playlist_ItemSetGroup( p_item, i_group ); + vlc_mutex_unlock( &p_item->lock ); + } + + /* Then remember it */ + p_announce = (struct sap_announce_t *)malloc( + sizeof(struct sap_announce_t) ); + if( p_sd->psz_sessionname ) + { + p_announce->psz_name = strdup( p_sd->psz_sessionname ); + } + else + { + p_announce->psz_name = strdup( "" ); + } + if( psz_item_uri ) + { + p_announce->psz_uri = strdup( psz_item_uri ); + } + else + { + p_announce->psz_uri = strdup( "" ); + } + p_announce->i_id = i_id; + + p_announce->i_last = mdate(); + + vlc_object_release( p_playlist ); + + INSERT_ELEM( p_intf->p_sys->pp_announces, + p_intf->p_sys->i_announces, + p_intf->p_sys->i_announces, + p_announce ); + free( psz_item_uri ); + } +} /*********************************************************************** * parse_sdp : SDP parsing @@ -509,157 +912,120 @@ static int packet_handle( intf_thread_t * p_intf, char ** ppsz_packet ) { * Make a sess_descr_t with a psz ***********************************************************************/ -static sess_descr_t * parse_sdp( char * psz_pct, intf_thread_t * p_intf ) +static sess_descr_t * parse_sdp( intf_thread_t * p_intf, char *p_packet ) { - int j,k; - char ** ppsz_fill=NULL; sess_descr_t * sd; - sd = malloc( sizeof(sess_descr_t) ); - if( sd == NULL ) + if( p_packet[0] != 'v' || p_packet[1] != '=' ) { - msg_Err( p_intf, "Not enough memory for sd in parse_sdp()" ); + msg_Warn(p_intf, "bad SDP packet"); + return NULL; } - else + + sd = malloc( sizeof( sess_descr_t ) ); + sd->psz_sessionname = NULL; + sd->psz_connection = NULL; + sd->psz_sdp = strdup( p_packet ); + + sd->i_media = 0; + sd->pp_media = NULL; + sd->i_attributes = 0; + sd->pp_attributes = NULL; + + while( *p_packet != '\0' ) { - sd->pp_media = NULL; - sd->psz_origin = NULL; - sd->psz_sessionname = NULL; - sd->psz_information = NULL; - sd->psz_uri = NULL; - sd->psz_emails = NULL; - sd->psz_phone = NULL; - sd->psz_time = NULL; - sd->psz_repeat = NULL; - sd->psz_attribute = NULL; - sd->psz_connection = NULL; - - sd->i_media=-1; - j=0; - while( psz_pct[j]!=EOF && psz_pct[j] != '\0' ) - { - j++; - if (psz_pct[j] == '=') - { - switch(psz_pct[(j-1)]) { - case 'v' : { - ppsz_fill = & sd->psz_version; - break; - } - case 'o' : { - ppsz_fill = & sd->psz_origin; - break; - } - case 's' : { - ppsz_fill = & sd->psz_sessionname; - break; - } - case 'i' : { - ppsz_fill = & sd->psz_information; - break; - } - case 'u' : { - ppsz_fill = & sd->psz_uri; - break; - } - case 'e' : { - ppsz_fill = & sd->psz_emails; - break; - } - case 'p' : { - ppsz_fill = & sd->psz_phone; - break; - } - case 't' : { - ppsz_fill = & sd->psz_time; - break; - } - case 'r' : { - ppsz_fill = & sd->psz_repeat; - break; - } - case 'a' : { - ppsz_fill = & sd->psz_attribute; - break; - } - case 'm' : { - sd->i_media++; - if( sd->pp_media ) { - sd->pp_media = realloc( sd->pp_media, - ( sizeof( void * ) * (sd->i_media + 1)) ); - } - else - { - sd->pp_media = malloc( sizeof ( void * ) ); - if( sd->pp_media == NULL ) - { - msg_Err( p_intf, "Not enough memory for " \ - "sd->pp_media in parse_sdp()" ); - free_sd( sd ); - return NULL; - } - } - sd->pp_media[sd->i_media] = - malloc( sizeof( media_descr_t ) ); - if( sd->pp_media[sd->i_media] == NULL ) - { - sd->i_media--; - msg_Err( p_intf, "Not enough memory for " \ - "sd->pp_media[sd->i_media] " \ - "in parse_sdp()" ); - free_sd( sd ); - return NULL; - } - - sd->pp_media[sd->i_media]->psz_medianame = NULL; - sd->pp_media[sd->i_media]->psz_mediaconnection = NULL; - - ppsz_fill = & sd->pp_media[sd->i_media]->psz_medianame; - break; - } - case ('c') : { - if( sd->i_media == -1 ) - { - ppsz_fill = & sd->psz_connection; - } - else - { - ppsz_fill = & sd->pp_media[sd->i_media]-> - psz_mediaconnection; - } - break; + char *psz_end; + + /* Search begin of field */ + while( *p_packet == '\n' || *p_packet == ' ' || *p_packet == '\t' ) + { + p_packet++; + } + /* search end of line */ + if( ( psz_end = strchr( p_packet, '\n' ) ) == NULL ) + { + psz_end = p_packet + strlen( p_packet ); + } + if( psz_end > p_packet && *(psz_end - 1 ) == '\r' ) + { + psz_end--; + } + + if( psz_end <= p_packet ) + { + break; + } + *psz_end++ = '\0'; + + if( p_packet[1] != '=' ) + { + msg_Warn( p_intf, "packet invalid" ); + free_sd( sd ); + return NULL; + } + + switch( p_packet[0] ) + { + 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' ): + { + 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; + } - default : { - ppsz_fill = NULL; + 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; + } - } - k=0;j++; - - while (psz_pct[j] != '\n'&& psz_pct[j] != EOF) { - k++; j++; - } - j--; - - if( ppsz_fill != NULL ) - { - *ppsz_fill = malloc( sizeof(char) * (k + 1) ); - if( *ppsz_fill == NULL ) - { - msg_Err( p_intf, "Not enough memory for " \ - "*ppsz_fill in parse_sdp()" ); - free_sd( sd ); - return NULL; - } - memcpy(*ppsz_fill, &(psz_pct[j-k+1]), k ); - (*ppsz_fill)[k]='\0'; - } - ppsz_fill = NULL; - } // if - } //for - } //if + p_packet = psz_end; + } return sd; } @@ -669,98 +1035,84 @@ static sess_descr_t * parse_sdp( char * psz_pct, intf_thread_t * p_intf ) static void free_sd( sess_descr_t * p_sd ) { int i; - if( p_sd ) + + FREE( p_sd->psz_sessionname ); + FREE( p_sd->psz_connection ); + FREE( p_sd->psz_sdp ); + + for( i = 0; i < p_sd->i_media ; i++ ) { - FREE( p_sd->psz_origin ); - FREE( p_sd->psz_sessionname ); - FREE( p_sd->psz_information ); - FREE( p_sd->psz_uri ); - FREE( p_sd->psz_emails ); - FREE( p_sd->psz_phone ); - FREE( p_sd->psz_time ); - FREE( p_sd->psz_repeat ); - FREE( p_sd->psz_attribute ); - FREE( p_sd->psz_connection ); - - if( p_sd->i_media >= 0 && p_sd->pp_media ) - { - 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 ); - } - FREE( p_sd->pp_media ); - } - free( p_sd ); + FREE( p_sd->pp_media[i]->psz_medianame ); + FREE( p_sd->pp_media[i]->psz_mediaconnection ); + FREE( p_sd->pp_media[i] ); } - else + for( i = 0; i < p_sd->i_attributes ; i++ ) { - ; + FREE( p_sd->pp_attributes[i]->psz_field ); + FREE( p_sd->pp_attributes[i]->psz_value ); + FREE( p_sd->pp_attributes[i] ); } - return; + FREE( p_sd->pp_attributes ); + FREE( p_sd->pp_media ); + + free( p_sd ); } /*********************************************************************** - * ismult + * ismult: returns true if we have a multicast address ***********************************************************************/ static int ismult( char *psz_uri ) { - char *psz_c; - int i; + char *psz_end; + int i_value; - psz_c = malloc( 3 ); - if( psz_c == NULL ) - { -/* msg_Err( p_intf, "Not enough memory for psz_c in ismult()" ); */ - return 0; - } + i_value = strtol( psz_uri, &psz_end, 0 ); - memcpy( psz_c, psz_uri, 3 ); - if( psz_c[2] == '.' || psz_c[1] == '.' ) + /* IPv6 */ + if( psz_uri[0] == '[') { - free( psz_c ); - return 0; + if( strncasecmp( &psz_uri[1], "FF0" , 3) || + strncasecmp( &psz_uri[2], "FF0" , 3)) + return( VLC_TRUE ); + else + return( VLC_FALSE ); } - i = atoi( psz_c ); - if( i < 224 ) - { - free( psz_c ); - return 0; - } + if( *psz_end != '.' ) { return( VLC_FALSE ); } - free( psz_c ); - return 1; + return( i_value < 224 ? VLC_FALSE : VLC_TRUE ); } + + /***************************************************************************** * Read: read on a file descriptor, checking b_die periodically ***************************************************************************** * Taken from udp.c - ******************************************************************************/ + *****************************************************************************/ static ssize_t NetRead( intf_thread_t *p_intf, - int i_handle, byte_t *p_buffer, size_t i_len) + int fd[2], uint8_t *p_buffer, int i_len ) { #ifdef UNDER_CE return -1; - #else struct timeval timeout; fd_set fds; int i_ret; + int i_handle_max = __MAX( fd[0], fd[1] ); /* Initialize file descriptor set */ FD_ZERO( &fds ); - FD_SET( i_handle, &fds ); + if( fd[0] > 0 ) FD_SET( fd[0], &fds ); + if( fd[1] > 0 ) FD_SET( fd[1], &fds ); /* We'll wait 0.5 second if nothing happens */ timeout.tv_sec = 0; timeout.tv_usec = 500000; /* Find if some data is available */ - i_ret = select( i_handle + 1, &fds, - NULL, NULL, &timeout ); + i_ret = select( i_handle_max + 1, &fds, NULL, NULL, &timeout ); if( i_ret == -1 && errno != EINTR ) { @@ -768,18 +1120,15 @@ static ssize_t NetRead( intf_thread_t *p_intf, } else if( i_ret > 0 ) { - ssize_t i_recv = recv( i_handle, p_buffer, i_len, 0 ); - - if( i_recv < 0 ) + if( fd[0] > 0 && FD_ISSET( fd[0], &fds ) ) { - msg_Err( p_intf, "recv failed (%s)", strerror(errno) ); + return recv( fd[0], p_buffer, i_len, 0 ); + } + else if( fd[1] > 0 && FD_ISSET( fd[1], &fds ) ) + { + return recv( fd[1], p_buffer, i_len, 0 ); } - - return i_recv; } - return 0; - #endif } -