X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fmisc%2Fsap.c;h=f51503417d83572f3dd918e9bc3f3a9fa6751b50;hb=499a384591d4238957f08348471fab5f36747fe7;hp=15e0e441de19f6660a13c37adc0aa0460d3c1ce3;hpb=84b846bf8c510666af2b7436ce4563025e6b6063;p=vlc diff --git a/modules/misc/sap.c b/modules/misc/sap.c index 15e0e441de..f51503417d 100644 --- a/modules/misc/sap.c +++ b/modules/misc/sap.c @@ -2,11 +2,12 @@ * sap.c : SAP interface module ***************************************************************************** * Copyright (C) 2001 VideoLAN - * $Id: sap.c,v 1.28 2003/10/29 17:32:55 zorglub 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 @@ -27,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 @@ -72,6 +66,10 @@ #include "network.h" +#ifdef HAVE_ZLIB_H +# include +#endif + #define MAX_LINE_LENGTH 256 /* SAP is always on that port */ @@ -82,63 +80,78 @@ #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") -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 int Open ( vlc_object_t * ); +static void Close( vlc_object_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") ); -static ssize_t NetRead ( intf_thread_t*, int, int , byte_t *, size_t ); + 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); -/* playlist related functions */ -static int sess_toitem( intf_thread_t *, sess_descr_t * ); + set_capability( "interface", 0 ); + set_callbacks( Open, Close ); +vlc_module_end(); -/* sap/sdp related functions */ -static int parse_sap ( char * ); -static int packet_handle ( intf_thread_t *, char *, int ); -static sess_descr_t * parse_sdp( intf_thread_t *, char * ) ; +/***************************************************************************** + * Local prototypes + *****************************************************************************/ -/* specific sdp fields parsing */ +static void Run ( intf_thread_t *p_intf ); +static ssize_t NetRead( intf_thread_t *, int fd[2], uint8_t *, int ); -static void cfield_parse( char *, char ** ); -static void mfield_parse( char *psz_mfield, char **ppsz_proto, - char **ppsz_port ); +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 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 * ); -/* Our custom structure */ -struct intf_sys_t -{ - int i_group; -}; - /* The struct that contains sdp informations */ struct sess_descr_t { - char *psz_version; - char *psz_origin; + 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; - int i_attributes; - media_descr_t ** pp_media; - attr_descr_t ** pp_attributes; + 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. */ @@ -154,374 +167,359 @@ struct attr_descr_t char *psz_value; }; -/***************************************************************************** - * 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 SAP to listen for IPv4 announces") -#define SAP_IPV6_TEXT N_("IPv6-SAP listening") -#define SAP_IPV6_LONGTEXT N_("Set this if you want SAP to listen for IPv6 announces") -#define SAP_SCOPE_TEXT N_("IPv6 SAP scope") -#define SAP_SCOPE_LONGTEXT N_("Sets the scope for IPv6 announces (default is 8)") +struct sap_announce_t +{ + mtime_t i_last; + int i_id; + char *psz_name; + char *psz_uri; +}; -vlc_module_begin(); - add_category_hint( N_("SAP"), NULL, VLC_TRUE ); - add_string( "sap-addr", NULL, NULL, - SAP_ADDR_TEXT, SAP_ADDR_LONGTEXT, VLC_TRUE ); +struct intf_sys_t +{ + /* IPV4 and IPV6 */ + int fd[2]; - add_bool( "sap-ipv4", 1 , NULL, - SAP_IPV4_TEXT,SAP_IPV4_LONGTEXT, VLC_TRUE); + /* playlist group */ + int i_group; - add_bool( "sap-ipv6", 0 , NULL, - SAP_IPV6_TEXT, SAP_IPV6_LONGTEXT, VLC_TRUE); + /* Table of announces */ + int i_announces; + struct sap_announce_t **pp_announces; - add_string( "sap-ipv6-scope", "8" , NULL, - SAP_SCOPE_TEXT, SAP_SCOPE_LONGTEXT, VLC_TRUE); + int i_timeout; +}; - set_description( _("SAP interface") ); - set_capability( "interface", 0 ); - set_callbacks( Activate, NULL); -vlc_module_end(); +#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)); -/***************************************************************************** - * Activate: initialize and create stuff - *****************************************************************************/ -static int Activate( vlc_object_t *p_this ) -{ - intf_thread_t *p_intf = (intf_thread_t*)p_this; + dstsize = d_stream.total_out; + inflateEnd(&d_stream); - p_intf->pf_run = Run; + *_dst = (unsigned char *)realloc(dst, dstsize); - return VLC_SUCCESS; + return dstsize; } +#endif /***************************************************************************** - * Run: sap thread - ***************************************************************************** - * Listens to SAP packets, and sends them to packet_handle + * Open: initialize and create stuff *****************************************************************************/ -#define MAX_SAP_BUFFER 2000 - -static void Run( intf_thread_t *p_intf ) +static int Open( vlc_object_t *p_this ) { - char *psz_addr; - char *psz_addrv6; - char *psz_network = NULL; - int fd = - 1; - int fdv6 = -1; - - int sap_ipv4 = config_GetInt( p_intf, "sap-ipv4" ); - int sap_ipv6 = config_GetInt( p_intf, "sap-ipv6" ); - char *sap_ipv6_scope = config_GetPsz( p_intf, "sap-ipv6-scope" ); - - char buffer[MAX_SAP_BUFFER + 1]; + intf_thread_t *p_intf = (intf_thread_t*)p_this; + intf_sys_t *p_sys = malloc( sizeof( intf_sys_t ) ); - module_t *p_network; - network_socket_t socket_desc; playlist_t *p_playlist; - playlist_group_t *p_group; - if( sap_ipv4 == -1 || sap_ipv6 == -1 || sap_ipv6_scope == 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_Warn( p_intf, "Unable to parse module configuration" ); - return; - } - - - p_intf->p_sys = malloc( sizeof( intf_sys_t ) ); - if( !p_intf->p_sys ) - { - msg_Err( p_intf, "Out of memory !"); - return VLC_EGENERIC; - } - /* Create our playlist group */ - p_playlist = - (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, - FIND_ANYWHERE ); - p_group = playlist_CreateGroup( p_playlist , "SAP" ); - p_intf->p_sys->i_group = p_group->i_id; - - vlc_object_release( p_playlist ); - - /* Prepare IPv4 Networking */ - if ( sap_ipv4 == 1) - { - if( !(psz_addr = config_GetPsz( p_intf, "sap-addr" ) ) ) + 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_addr = strdup( HELLO_GROUP ); + psz_address = strdup( HELLO_GROUP ); } /* 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; - socket_desc.i_ttl = 0; - p_intf->p_private = (void*) &socket_desc; - - psz_network = "ipv4"; - - /* Create, Bind the socket, ... with the appropriate module */ - - if( !( p_network = module_Need( p_intf, "network", psz_network ) ) ) + 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 ) { - msg_Warn( p_intf, "failed to open a connection (udp)" ); - return; + p_sys->fd[0] = sock.i_handle; + module_Unneed( p_intf, p_network ); } - module_Unneed( p_intf, p_network ); - - fd = socket_desc.i_handle; + else + { + msg_Warn( p_intf, "failed to open %s:%d", psz_address, HELLO_PORT ); + } + free( psz_address ); } - /* Prepare IPv6 Networking */ - if ( sap_ipv6 > 0) + if( config_GetInt( p_intf, "sap-ipv6" ) ) { - /* Prepare the network_socket_t structure */ + char psz_address[100]; + char *psz_scope = config_GetPsz( p_intf, "sap-ipv6-scope" ); + network_socket_t sock; + module_t *p_network; - psz_addrv6=(char *)malloc(sizeof(char)*38); - - if( !psz_addrv6) + if( psz_scope == NULL || *psz_scope == '\0' ) { - msg_Warn( p_intf, "Out of memory !" ); + psz_scope = strdup( "8" ); } - /* Max size of an IPv6 address */ - - sprintf(psz_addrv6,"[%s%c%s]",IPV6_ADDR_1, - sap_ipv6_scope[0],IPV6_ADDR_2); - - socket_desc.i_type = NETWORK_UDP; - socket_desc.psz_bind_addr = psz_addrv6; - socket_desc.i_bind_port = HELLO_PORT; - socket_desc.psz_server_addr = ""; - socket_desc.i_server_port = 0; - socket_desc.i_ttl = 0; - p_intf->p_private = (void*) &socket_desc; - - psz_network = "ipv6"; - - /* Create, Bind the socket, ... with the appropriate module */ - - if( !( p_network = module_Need( p_intf, "network", psz_network ) ) ) + 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 ) { - msg_Warn( p_intf, "failed to open a connection (udp)" ); - return; + 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 ); } - module_Unneed( p_intf, p_network ); - - fdv6 = socket_desc.i_handle; } - - - /* read SAP packets */ - while( !p_intf->b_die ) + if( p_sys->fd[0] <= 0 && p_sys->fd[1] <= 0 ) { - int i_read; + msg_Warn( p_intf, "IPV4 and IPV6 failed" ); + free( p_sys ); + return VLC_EGENERIC; + } - //memset( buffer, 0, MAX_SAP_BUFFER + 1); + /* 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 ); + } - i_read = NetRead( p_intf, fd, fdv6, buffer, MAX_SAP_BUFFER ); + p_sys->i_announces = 0; + p_sys->pp_announces = NULL; - if( i_read < 0 ) - { - msg_Err( p_intf, "Cannot read in the socket" ); - } - if( i_read == 0 ) - { - continue; - } - buffer[i_read] = '\0'; + p_intf->pf_run = Run; + p_intf->p_sys = p_sys; - packet_handle( p_intf, buffer, i_read ); + return VLC_SUCCESS; +} - } +/***************************************************************************** + * 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; - /* Closing socket */ - if( fd > 0) + if( p_sys->fd[0] > 0 ) { - if( close( fd ) ) - { - msg_Warn( p_intf, "Ohoh, unable to close the socket" ); - } + close( p_sys->fd[0] ); } - if( fdv6 > 0) + if( p_sys->fd[1] > 0 ) { - if( close( fdv6 ) ) - { - msg_Warn( p_intf, "Ohoh, unable to close the socket" ); - } + close( p_sys->fd[1] ); } -} - -/******************************************************************** - * Kill - ******************************************************************* - * Kills the SAP interface. - ********************************************************************/ -static int Kill( intf_thread_t *p_intf ) -{ - 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 , i; - vlc_bool_t b_http = VLC_FALSE; - char *psz_http_path = NULL; - playlist_t *p_playlist; + intf_sys_t *p_sys = p_intf->p_sys; + uint8_t buffer[MAX_SAP_BUFFER + 1]; + uint8_t *p_end; - psz_uri_default = NULL; - cfield_parse( p_sd->psz_connection, &psz_uri_default ); - - 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; - p_item->ppsz_options = NULL; - p_item->i_options = 0; - - 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 = psz_uri_default; + msg_Warn( p_intf, "got session deletion packet" ); } - - if( psz_uri == NULL ) + b_encrypted = buffer[0] & 0x02; + if( b_encrypted ) { - return 0; + msg_Warn( p_intf, "encrypted packet" ); } - - for( i = 0 ; i< p_sd->i_attributes ; i++ ) + b_compressed = buffer[0] & 0x01; + p_sdp = &buffer[4]; + if( i_address_type == 0 ) /* ipv4 source address */ { - 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 ); - - } + p_sdp += 4; } - - - /* Filling p_item->psz_uri */ - if( b_http == VLC_FALSE ) + else /* ipv6 source address */ { - 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 ) - { - msg_Err( p_intf, "Not enough memory"); - free( p_item ); - return 0; - } - - if( i_multicast == 1) - { - sprintf( p_item->psz_uri, "%s://@%s:%s", psz_proto, - psz_uri, psz_port ); - } - else + 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 ) { - sprintf( p_item->psz_uri, "%s://%s:%s", psz_proto, - psz_uri, psz_port ); + 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 } - else + p_sdp += buffer[1]; /* size of signature */ + while( p_sdp < p_end - 1 && *p_sdp != '\0' && p_sdp[0] != 'v' && p_sdp[1] != '=' ) { - if( psz_http_path == NULL ) - psz_http_path = strdup("/"); - - p_item->psz_uri = malloc( strlen( psz_proto ) + strlen( psz_uri ) + - strlen( psz_port ) + 3 + strlen(psz_http_path) ); + p_sdp++; + } + if( *p_sdp == '\0' ) + { + p_sdp++; + } - if( p_item->psz_uri == NULL ) + if( p_sdp < p_end ) + { + sess_descr_t *p_sd = parse_sdp( p_intf, p_sdp ); + if( p_sd ) { - msg_Err( p_intf, "Not enough memory"); - free( p_item ); - return 0; + sess_toitem ( p_intf, p_sd ); + free_sd ( p_sd ); } - - sprintf( p_item->psz_uri, "%s://%s:%s%s", psz_proto, - psz_uri, psz_port,psz_http_path ); - } - /* Enqueueing p_item in the playlist */ - - if( p_item ) + else { - p_item->i_group = p_intf->p_sys->i_group; - p_item->b_enabled = VLC_TRUE; - p_item->psz_author = NULL; - 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" ); } - if( psz_http_path ) - free(psz_http_path); } - - return 1; - } /********************************************************************** @@ -577,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++; } @@ -618,62 +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 ; - * returns the SAP header lenhth - ***********************************************************************/ -static int parse_sap( char *p_packet ) +/******************************************************************* + * 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 ) { - // According to RFC 2974 - int i_hlen = 4; // Minimum header length is 4 - i_hlen += (p_packet[0] & 0x10) ? 16 : 4; // Address type IPv6=16bytes - i_hlen += p_packet[1]; // Authentification length - - //Looks for the first '\0' byte after length - for(;p_packet[i_hlen]!='\0'; i_hlen++); - - if( i_hlen > 50 ) /* Definitely too long... - Maybe we have a fucked up packet without \0 */ - { /* As a workaround, we search for "v=" */ - i_hlen = 4; - for(;p_packet[i_hlen] != 'v' && p_packet[i_hlen+1] != '=' ; i_hlen++); - return i_hlen-1; - } + 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; - return(i_hlen); -} + 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 ); + } -/************************************************************************* - * packet_handle : handle the received packet and enques the - * the understated session - *************************************************************************/ + /* 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(); -static int packet_handle( intf_thread_t * p_intf, char *p_packet, int i_len ) -{ - sess_descr_t * p_sd; - int i_hlen; // Header length + INSERT_ELEM( p_intf->p_sys->pp_announces, + p_intf->p_sys->i_announces, + p_intf->p_sys->i_announces, + p_announce ); + + vlc_object_release( p_playlist ); + free( psz_uri ); + return; + } - i_hlen = parse_sap(p_packet); + cfield_parse( p_sd->psz_connection, &psz_uri_default ); - if( (i_hlen > 0) && (i_hlen < i_len) ) + for( i_count = 0 ; i_count < p_sd->i_media ; i_count++ ) { - p_sd = parse_sdp( p_intf, p_packet + i_hlen +1); - if(p_sd) + 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] ) { - sess_toitem ( p_intf, p_sd ); - free_sd ( p_sd ); - return VLC_TRUE; + return; } - } - return VLC_FALSE; // Invalid Packet -} + 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 @@ -684,189 +915,116 @@ static int packet_handle( intf_thread_t * p_intf, char *p_packet, int i_len ) static sess_descr_t * parse_sdp( intf_thread_t * p_intf, char *p_packet ) { sess_descr_t * sd; - char *psz_eof; - unsigned int i; - // According to RFC 2327, the first bytes should be exactly "v=" - if((p_packet[0] != 'v') || (p_packet[1] != '=')) + if( p_packet[0] != 'v' || p_packet[1] != '=' ) { - msg_Warn(p_intf, "Bad SDP packet"); + msg_Warn(p_intf, "bad SDP packet"); return NULL; } - if( ( sd = malloc( sizeof(sess_descr_t) ) ) == NULL ) - { - msg_Err( p_intf, "Not enough memory for sd in parse_sdp()" ); - return( NULL ); - } - - sd->pp_media = NULL; - sd->pp_attributes = NULL; - sd->psz_origin = NULL; + sd = malloc( sizeof( sess_descr_t ) ); 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->psz_connection = NULL; + sd->psz_sdp = strdup( p_packet ); - sd->i_media = 0; - sd->i_attributes = 0; + sd->i_media = 0; + sd->pp_media = NULL; + sd->i_attributes = 0; + sd->pp_attributes = NULL; while( *p_packet != '\0' ) { -#define FIELD_COPY( p ) \ - p = strndup( &p_packet[2], i_field_len ); - char *psz_end; - int i_field_len; + /* Search begin of field */ while( *p_packet == '\n' || *p_packet == ' ' || *p_packet == '\t' ) { p_packet++; } - if( *p_packet == '\0' ) + /* 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( ( psz_end = strchr( p_packet, '\n' ) ) == NULL ) + if( p_packet[1] != '=' ) { - psz_end = p_packet + strlen( p_packet ); + msg_Warn( p_intf, "packet invalid" ); + free_sd( sd ); + return NULL; } - i_field_len = psz_end - &p_packet[2]; - if( p_packet[1] == '=' && i_field_len > 0) + switch( p_packet[0] ) { - switch( *p_packet ) + 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' ): { - case( 'v' ): - FIELD_COPY( sd->psz_version ); - break; - case ( 'o' ): - FIELD_COPY( sd->psz_origin ); - break; - case( 's' ): - FIELD_COPY( sd->psz_sessionname ); - break; - case( 'i' ): - FIELD_COPY( sd->psz_information ); - break; - case( 'u' ): - FIELD_COPY( sd->psz_uri ); - break; - case( 'e' ): - FIELD_COPY( sd->psz_emails ); - break; - case( 'p' ): - FIELD_COPY( sd->psz_phone ); - break; - case( 't' ): - FIELD_COPY( sd->psz_time ); - break; - case( 'r' ): - FIELD_COPY( sd->psz_repeat ); - break; - case( 'a' ): - if( sd->pp_attributes ) - { - sd->pp_attributes = - realloc( sd->pp_attributes, - sizeof( attr_descr_t ) * - ( sd->i_attributes +1 ) ); - } - else - { - sd->pp_attributes = malloc( sizeof( void * ) ); - } - if( !sd->pp_attributes ) - { - msg_Warn( p_intf, "Out of memory !" ); - return NULL; - } - sd->pp_attributes[sd->i_attributes] = - malloc( sizeof( attr_descr_t ) ); - if( ! sd->pp_attributes[sd->i_attributes]) - { - msg_Warn( p_intf, "Out of memory !" ); - return NULL; - } - - p_packet += 2; - psz_eof = strchr( p_packet, ':'); - if(psz_eof) - *psz_eof = '\0'; - sd->pp_attributes[sd->i_attributes]->psz_field = - strdup( p_packet ); - if( psz_eof + 1 ) - { - sd->pp_attributes[sd->i_attributes]->psz_value = - strdup( ++psz_eof ); - } - else - { - if( sd->pp_attributes[sd->i_attributes]->psz_field ) - free( sd->pp_attributes[sd->i_attributes] - ->psz_field ); - break; - } - for( i=0 ; i< - strlen(sd->pp_attributes[sd->i_attributes]->psz_value) ; - i++ ) - { - if(sd->pp_attributes[sd->i_attributes]->psz_value[i] - =='\n' ) - sd->pp_attributes[sd->i_attributes]->psz_value[i] =0; - } - sd->i_attributes++; - break; - - case( 'm' ): - if( sd->pp_media ) - { - sd->pp_media = - realloc( sd->pp_media, - sizeof( media_descr_t ) * ( sd->i_media + 1 ) ); - } - else - { - sd->pp_media = malloc( sizeof( void * ) ); - } - if( !sd->pp_media ) - { - msg_Warn( p_intf, "Out of memory !" ); - return NULL; - } - sd->pp_media[sd->i_media] = - malloc( sizeof( media_descr_t ) ); - sd->pp_media[sd->i_media]->psz_medianame = NULL; - sd->pp_media[sd->i_media]->psz_mediaconnection = NULL; - sd->pp_media[sd->i_media]->psz_medianame = - strndup( &p_packet[2], i_field_len ); - - sd->i_media++; - break; - - case( 'c' ): - if( sd->i_media <= 0 ) - { - FIELD_COPY(sd->psz_connection); - } - else - { - FIELD_COPY(sd->pp_media[sd->i_media - 1]->psz_mediaconnection); - } - break; - default: - break; + 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; } + + 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; } + p_packet = psz_end; -#undef FIELD_COPY } return sd; @@ -877,39 +1035,27 @@ static sess_descr_t * parse_sdp( intf_thread_t * p_intf, char *p_packet ) static void free_sd( sess_descr_t * p_sd ) { int i; - if( p_sd ) - { - 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 ); - 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 ); - } - 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 ); - FREE( p_sd->pp_media ); + FREE( p_sd->psz_sessionname ); + FREE( p_sd->psz_connection ); + FREE( p_sd->psz_sdp ); - free( p_sd ); + 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[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 ); } /*********************************************************************** @@ -944,74 +1090,45 @@ static int ismult( char *psz_uri ) * 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, int i_handle_v6, - 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_max_handle; - - ssize_t i_recv=-1; - - /* Get the max handle for select */ - if( i_handle_v6 > i_handle ) - i_max_handle = i_handle_v6; - else - i_max_handle = i_handle; - + int i_handle_max = __MAX( fd[0], fd[1] ); /* Initialize file descriptor set */ FD_ZERO( &fds ); - if( i_handle > 0 ) FD_SET( i_handle, &fds ); - if( i_handle_v6 > 0 ) FD_SET( i_handle_v6, &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_max_handle + 1, &fds, - NULL, NULL, &timeout ); + i_ret = select( i_handle_max + 1, &fds, NULL, NULL, &timeout ); if( i_ret == -1 && errno != EINTR ) { msg_Err( p_intf, "network select error (%s)", strerror(errno) ); } else if( i_ret > 0 ) - { - /* Get the data */ - if(i_handle >0) - { - if(FD_ISSET( i_handle, &fds )) - { - i_recv = recv( i_handle, p_buffer, i_len, 0 ); - } - } - if(i_handle_v6 >0) - { - if(FD_ISSET( i_handle_v6, &fds )) - { - i_recv = recv( i_handle_v6, p_buffer, i_len, 0 ); - } - } - - if( i_recv < 0 ) + { + if( fd[0] > 0 && FD_ISSET( fd[0], &fds ) ) + { + return recv( fd[0], p_buffer, i_len, 0 ); + } + else if( fd[1] > 0 && FD_ISSET( fd[1], &fds ) ) { - msg_Err( p_intf, "recv failed (%s)", strerror(errno) ); + return recv( fd[1], p_buffer, i_len, 0 ); } - return i_recv; } - return 0; - #endif } -