///////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2000-2003 Intel Corporation
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither name of Intel Corporation nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
///////////////////////////////////////////////////////////////////////////
#include "config.h"
#if EXCLUDE_GENA == 0
#ifdef INCLUDE_DEVICE_APIS
#include "gena.h"
#include "sysdep.h"
#include "uuid.h"
#include "upnpapi.h"
#include "parsetools.h"
#include "statcodes.h"
#include "httpparser.h"
#include "httpreadwrite.h"
#include "unixutil.h"
/************************************************************************
* Function : genaUnregisterDevice
*
* Parameters:
* IN UpnpDevice_Handle device_handle: Handle of the root device
*
* Description:
* This function cleans the service table of the device.
*
* Returns: int
* returns UPNP_E_SUCCESS if successful else returns GENA_E_BAD_HANDLE
****************************************************************************/
int
genaUnregisterDevice( IN UpnpDevice_Handle device_handle )
{
struct Handle_Info *handle_info;
HandleLock( );
if( GetHandleInfo( device_handle, &handle_info ) != HND_DEVICE ) {
DBGONLY( UpnpPrintf( UPNP_CRITICAL, GENA, __FILE__, __LINE__,
"genaUnregisterDevice : BAD Handle : %d\n",
device_handle ) );
HandleUnlock( );
return GENA_E_BAD_HANDLE;
}
freeServiceTable( &handle_info->ServiceTable );
HandleUnlock( );
return UPNP_E_SUCCESS;
}
/************************************************************************
* Function : GeneratePropertySet
*
* Parameters:
* IN char **names : Array of variable names (go in the event notify)
* IN char ** values : Array of variable values (go in the event notify)
* IN int count : number of variables
* OUT DOMString *out: PropertySet node in the string format
*
* Description:
* This function to generate XML propery Set for notifications
*
* Returns: int
* returns UPNP_E_SUCCESS if successful else returns GENA_E_BAD_HANDLE
*
* Note: XML_VERSION comment is NOT sent due to interop issues with other
* UPnP vendors
****************************************************************************/
static int
GeneratePropertySet( IN char **names,
IN char **values,
IN int count,
OUT DOMString * out )
{
char *buffer;
int counter = 0;
int size = 0;
int temp_counter = 0;
//size+=strlen(XML_VERSION); the XML_VERSION is not interopeable with
//other vendors
size += strlen( XML_PROPERTYSET_HEADER );
size += strlen( "\n\n" );
for( temp_counter = 0, counter = 0; counter < count; counter++ ) {
size += strlen( "\n\n" );
size +=
( 2 * strlen( names[counter] ) + strlen( values[counter] ) +
( strlen( "<>>\n" ) ) );
}
buffer = ( char * )malloc( size + 1 );
if( buffer == NULL ) {
return UPNP_E_OUTOF_MEMORY;
}
memset( buffer, 0, size + 1 );
//strcpy(buffer,XML_VERSION); the XML_VERSION is not interopeable with
//other vendors
strcpy( buffer, XML_PROPERTYSET_HEADER );
for( counter = 0; counter < count; counter++ ) {
strcat( buffer, "\n" );
sprintf( &buffer[strlen( buffer )],
"<%s>%s%s>\n\n", names[counter],
values[counter], names[counter] );
}
strcat( buffer, "\n\n" );
( *out ) = ixmlCloneDOMString( buffer );
free( buffer );
return XML_SUCCESS;
}
/************************************************************************
* Function : free_notify_struct
*
* Parameters:
* IN notify_thread_struct * input : Notify structure
*
* Description:
* This function frees memory used in notify_threads if the reference
* count is 0 otherwise decrements the refrence count
*
* Returns: VOID
*
****************************************************************************/
static void
free_notify_struct( IN notify_thread_struct * input )
{
( *input->reference_count )--;
if( ( *input->reference_count ) == 0 ) {
free( input->headers );
ixmlFreeDOMString( input->propertySet );
free( input->servId );
free( input->UDN );
free( input->reference_count );
}
free( input );
}
/****************************************************************************
* Function : notify_send_and_recv
*
* Parameters :
* IN uri_type* destination_url : subscription callback URL
* (URL of the control point)
* IN membuffer* mid_msg : Common HTTP headers
* IN char* propertySet : The evented XML
* OUT http_parser_t* response : The response from the control point.
*
* Description : This function sends the notify message and returns a
* reply.
*
* Return : int
* on success: returns UPNP_E_SUCCESS; else returns a UPNP error
*
* Note : called by genaNotify
****************************************************************************/
static XINLINE int
notify_send_and_recv( IN uri_type * destination_url,
IN membuffer * mid_msg,
IN char *propertySet,
OUT http_parser_t * response )
{
uri_type url;
int conn_fd;
membuffer start_msg;
int ret_code;
int err_code;
int timeout;
SOCKINFO info;
// connect
DBGONLY( UpnpPrintf( UPNP_ALL, GENA, __FILE__, __LINE__,
"gena notify to: %.*s\n",
destination_url->hostport.text.size,
destination_url->hostport.text.buff ); )
conn_fd = http_Connect( destination_url, &url );
if( conn_fd < 0 ) {
return conn_fd; // return UPNP error
}
if( ( ret_code = sock_init( &info, conn_fd ) ) != 0 ) {
sock_destroy( &info, SD_BOTH );
return ret_code;
}
// make start line and HOST header
membuffer_init( &start_msg );
if( http_MakeMessage( &start_msg, 1, 1,
"q" "s",
HTTPMETHOD_NOTIFY, &url, mid_msg->buf ) != 0 ) {
membuffer_destroy( &start_msg );
sock_destroy( &info, SD_BOTH );
return UPNP_E_OUTOF_MEMORY;
}
timeout = HTTP_DEFAULT_TIMEOUT;
// send msg (note +1 for propertyset; null-terminator is also sent)
if( ( ret_code = http_SendMessage( &info, &timeout,
"bb",
start_msg.buf, start_msg.length,
propertySet,
strlen( propertySet ) + 1 ) ) !=
0 ) {
membuffer_destroy( &start_msg );
sock_destroy( &info, SD_BOTH );
return ret_code;
}
if( ( ret_code = http_RecvMessage( &info, response,
HTTPMETHOD_NOTIFY, &timeout,
&err_code ) ) != 0 ) {
membuffer_destroy( &start_msg );
sock_destroy( &info, SD_BOTH );
httpmsg_destroy( &response->msg );
return ret_code;
}
sock_destroy( &info, SD_BOTH ); //should shutdown completely
//when closing socket
// sock_destroy( &info,SD_RECEIVE);
membuffer_destroy( &start_msg );
return UPNP_E_SUCCESS;
}
/****************************************************************************
* Function : genaNotify
*
* Parameters :
* IN char *headers : (null terminated) (includes all headers
* (including \r\n) except SID and SEQ)
* IN char *propertySet : The evented XML
* IN subscription* sub : subscription to be Notified,
* Assumes this is valid for life of function)
*
* Description : Function to Notify a particular subscription of a
* particular event. In general the service should NOT be
* blocked around this call. (this may cause deadlock
* with a client) NOTIFY http request is sent and the
* reply is processed.
*
* Return : int
* GENA_SUCCESS if the event was delivered else returns appropriate
* error
*
* Note :
****************************************************************************/
int
genaNotify( IN char *headers,
IN char *propertySet,
IN subscription * sub )
{
int i;
membuffer mid_msg;
membuffer endmsg;
uri_type *url;
http_parser_t response;
int return_code = -1;
membuffer_init( &mid_msg );
// make 'end' msg (the part that won't vary with the destination)
endmsg.size_inc = 30;
if( http_MakeMessage( &mid_msg, 1, 1,
"s" "ssc" "sdcc",
headers,
"SID: ", sub->sid,
"SEQ: ", sub->ToSendEventKey ) != 0 ) {
membuffer_destroy( &mid_msg );
return UPNP_E_OUTOF_MEMORY;
}
// send a notify to each url until one goes thru
for( i = 0; i < sub->DeliveryURLs.size; i++ ) {
url = &sub->DeliveryURLs.parsedURLs[i];
if( ( return_code = notify_send_and_recv( url,
&mid_msg, propertySet,
&response ) ) ==
UPNP_E_SUCCESS ) {
break;
}
}
membuffer_destroy( &mid_msg );
if( return_code == UPNP_E_SUCCESS ) {
if( response.msg.status_code == HTTP_OK ) {
return_code = GENA_SUCCESS;
} else {
if( response.msg.status_code == HTTP_PRECONDITION_FAILED ) {
//Invalid SID gets removed
return_code = GENA_E_NOTIFY_UNACCEPTED_REMOVE_SUB;
} else {
return_code = GENA_E_NOTIFY_UNACCEPTED;
}
}
httpmsg_destroy( &response.msg );
}
return return_code;
}
/****************************************************************************
* Function : genaNotifyThread
*
* Parameters :
* IN void * input : notify thread structure containing all the
* headers and property set info
*
* Description : Thread job to Notify a control point. It validates the
* subscription and copies the subscription. Also make sure that
* events are sent in order.
*
* Return : void
*
* Note : calls the genaNotify to do the actual work
****************************************************************************/
static void
genaNotifyThread( IN void *input )
{
subscription *sub;
service_info *service;
subscription sub_copy;
notify_thread_struct *in = ( notify_thread_struct * ) input;
int return_code;
struct Handle_Info *handle_info;
ThreadPoolJob job;
HandleLock( );
//validate context
if( GetHandleInfo( in->device_handle, &handle_info ) != HND_DEVICE ) {
free_notify_struct( in );
HandleUnlock( );
return;
}
if( ( ( service = FindServiceId( &handle_info->ServiceTable,
in->servId, in->UDN ) ) == NULL )
|| ( !service->active )
|| ( ( sub = GetSubscriptionSID( in->sid, service ) ) == NULL )
|| ( ( copy_subscription( sub, &sub_copy ) != HTTP_SUCCESS ) ) ) {
free_notify_struct( in );
HandleUnlock( );
return;
}
//If the event is out of order push it back to the job queue
if( in->eventKey != sub->ToSendEventKey ) {
TPJobInit( &job, ( start_routine ) genaNotifyThread, input );
TPJobSetFreeFunction( &job, ( free_function ) free_notify_struct );
TPJobSetPriority( &job, MED_PRIORITY );
ThreadPoolAdd( &gSendThreadPool, &job, NULL );
freeSubscription( &sub_copy );
HandleUnlock( );
return;
}
HandleUnlock( );
//send the notify
return_code = genaNotify( in->headers, in->propertySet, &sub_copy );
freeSubscription( &sub_copy );
HandleLock( );
if( GetHandleInfo( in->device_handle, &handle_info ) != HND_DEVICE ) {
free_notify_struct( in );
HandleUnlock( );
return;
}
//validate context
if( ( ( service = FindServiceId( &handle_info->ServiceTable,
in->servId, in->UDN ) ) == NULL )
|| ( !service->active )
|| ( ( sub = GetSubscriptionSID( in->sid, service ) ) == NULL ) ) {
free_notify_struct( in );
HandleUnlock( );
return;
}
sub->ToSendEventKey++;
if( sub->ToSendEventKey < 0 ) //wrap to 1 for overflow
sub->ToSendEventKey = 1;
if( return_code == GENA_E_NOTIFY_UNACCEPTED_REMOVE_SUB ) {
RemoveSubscriptionSID( in->sid, service );
}
free_notify_struct( in );
HandleUnlock( );
}
/****************************************************************************
* Function : genaInitNotify
*
* Parameters :
* IN UpnpDevice_Handle device_handle : Device handle
* IN char *UDN : Device udn
* IN char *servId : Service ID
* IN char **VarNames : Array of variable names
* IN char **VarValues : Array of variable values
* IN int var_count : array size
* IN Upnp_SID sid : subscription ID
*
* Description : This function sends the intial state table dump to
* newly subscribed control point.
*
* Return : int
* returns GENA_E_SUCCESS if successful else returns appropriate error
*
* Note : No other event will be sent to this control point before the
* intial state table dump.
****************************************************************************/
int
genaInitNotify( IN UpnpDevice_Handle device_handle,
IN char *UDN,
IN char *servId,
IN char **VarNames,
IN char **VarValues,
IN int var_count,
IN Upnp_SID sid )
{
char *UDN_copy = NULL;
char *servId_copy = NULL;
char *propertySet = NULL;
char *headers = NULL;
subscription *sub = NULL;
service_info *service = NULL;
int return_code = GENA_SUCCESS;
int headers_size;
int *reference_count = NULL;
struct Handle_Info *handle_info;
ThreadPoolJob job;
notify_thread_struct *thread_struct = NULL;
DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__,
"GENA BEGIN INITIAL NOTIFY " ) );
reference_count = ( int * )malloc( sizeof( int ) );
if( reference_count == NULL )
return UPNP_E_OUTOF_MEMORY;
( *reference_count ) = 0;
UDN_copy = ( char * )malloc( strlen( UDN ) + 1 );
if( UDN_copy == NULL ) {
free( reference_count );
return UPNP_E_OUTOF_MEMORY;
}
servId_copy = ( char * )malloc( strlen( servId ) + 1 );
if( servId_copy == NULL ) {
free( UDN_copy );
free( reference_count );
return UPNP_E_OUTOF_MEMORY;
}
strcpy( UDN_copy, UDN );
strcpy( servId_copy, servId );
HandleLock( );
if( GetHandleInfo( device_handle, &handle_info ) != HND_DEVICE ) {
free( UDN_copy );
free( reference_count );
free( servId_copy );
HandleUnlock( );
return GENA_E_BAD_HANDLE;
}
if( ( service = FindServiceId( &handle_info->ServiceTable,
servId, UDN ) ) == NULL ) {
free( UDN_copy );
free( reference_count );
free( servId_copy );
HandleUnlock( );
return GENA_E_BAD_SERVICE;
}
DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__,
"FOUND SERVICE IN INIT NOTFY: UDN %s, ServID: %d ",
UDN, servId ) );
if( ( ( sub = GetSubscriptionSID( sid, service ) ) == NULL ) ||
( sub->active ) ) {
free( UDN_copy );
free( reference_count );
free( servId_copy );
HandleUnlock( );
return GENA_E_BAD_SID;
}
DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__,
"FOUND SUBSCRIPTION IN INIT NOTIFY: SID %s ",
sid ) );
sub->active = 1;
if( ( return_code = GeneratePropertySet( VarNames, VarValues,
var_count,
&propertySet ) ) !=
XML_SUCCESS ) {
free( UDN_copy );
free( reference_count );
free( servId_copy );
HandleUnlock( );
return return_code;
}
DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__,
"GENERATED PROPERY SET IN INIT NOTIFY: \n'%s'\n",
propertySet ) );
headers_size = strlen( "CONTENT-TYPE text/xml\r\n" ) +
strlen( "CONTENT-LENGTH: \r\n" ) + MAX_CONTENT_LENGTH +
strlen( "NT: upnp:event\r\n" ) +
strlen( "NTS: upnp:propchange\r\n" ) + 1;
headers = ( char * )malloc( headers_size );
if( headers == NULL ) {
ixmlFreeDOMString( propertySet );
free( UDN_copy );
free( servId_copy );
free( reference_count );
HandleUnlock( );
return UPNP_E_OUTOF_MEMORY;
}
sprintf( headers, "CONTENT-TYPE: text/xml\r\nCONTENT-LENGTH: "
"%d\r\nNT: upnp:event\r\nNTS: upnp:propchange\r\n",
strlen( propertySet ) + 1 );
//schedule thread for initial notification
thread_struct =
( notify_thread_struct * )
malloc( sizeof( notify_thread_struct ) );
if( thread_struct == NULL ) {
return_code = UPNP_E_OUTOF_MEMORY;
} else {
( *reference_count ) = 1;
thread_struct->servId = servId_copy;
thread_struct->UDN = UDN_copy;
thread_struct->headers = headers;
thread_struct->propertySet = propertySet;
strcpy( thread_struct->sid, sid );
thread_struct->eventKey = sub->eventKey++;
thread_struct->reference_count = reference_count;
thread_struct->device_handle = device_handle;
TPJobInit( &job, ( start_routine ) genaNotifyThread,
thread_struct );
TPJobSetFreeFunction( &job, ( free_routine ) free_notify_struct );
TPJobSetPriority( &job, MED_PRIORITY );
if( ( return_code =
ThreadPoolAdd( &gSendThreadPool, &job, NULL ) ) != 0 ) {
if( return_code == EOUTOFMEM ) {
return_code = UPNP_E_OUTOF_MEMORY;
}
} else {
return_code = GENA_SUCCESS;
}
}
if( return_code != GENA_SUCCESS ) {
free( reference_count );
free( UDN_copy );
free( servId_copy );
free( thread_struct );
ixmlFreeDOMString( propertySet );
free( headers );
}
HandleUnlock( );
return return_code;
}
/****************************************************************************
* Function : genaInitNotifyExt
*
* Parameters :
* IN UpnpDevice_Handle device_handle : Device handle
* IN char *UDN : Device udn
* IN char *servId : Service ID
* IN IXML_Document *PropSet : Document of the state table
* IN Upnp_SID sid : subscription ID
*
* Description : This function is similar to the genaInitNofity. The only
* difference is that it takes the xml document for the state table and
* sends the intial state table dump to newly subscribed control point.
*
* Return : int
* returns GENA_E_SUCCESS if successful else returns appropriate error
*
* Note : No other event will be sent to this control point before the
* intial state table dump.
****************************************************************************/
int
genaInitNotifyExt( IN UpnpDevice_Handle device_handle,
IN char *UDN,
IN char *servId,
IN IXML_Document * PropSet,
IN Upnp_SID sid )
{
char *UDN_copy = NULL;
char *servId_copy = NULL;
char *headers = NULL;
subscription *sub = NULL;
service_info *service = NULL;
int return_code = GENA_SUCCESS;
int headers_size;
int *reference_count = NULL;
struct Handle_Info *handle_info;
DOMString propertySet = NULL;
ThreadPoolJob job;
notify_thread_struct *thread_struct = NULL;
DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__,
"GENA BEGIN INITIAL NOTIFY EXT" ) );
reference_count = ( int * )malloc( sizeof( int ) );
if( reference_count == NULL ) {
return UPNP_E_OUTOF_MEMORY;
}
( *reference_count ) = 0;
UDN_copy = ( char * )malloc( strlen( UDN ) + 1 );
if( UDN_copy == NULL ) {
free( reference_count );
return UPNP_E_OUTOF_MEMORY;
}
servId_copy = ( char * )malloc( strlen( servId ) + 1 );
if( servId_copy == NULL ) {
free( UDN_copy );
free( reference_count );
return UPNP_E_OUTOF_MEMORY;
}
strcpy( UDN_copy, UDN );
strcpy( servId_copy, servId );
HandleLock( );
if( GetHandleInfo( device_handle, &handle_info ) != HND_DEVICE ) {
free( UDN_copy );
free( reference_count );
free( servId_copy );
HandleUnlock( );
return GENA_E_BAD_HANDLE;
}
if( ( service = FindServiceId( &handle_info->ServiceTable,
servId, UDN ) ) == NULL ) {
free( UDN_copy );
free( reference_count );
free( servId_copy );
HandleUnlock( );
return GENA_E_BAD_SERVICE;
}
DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__,
"FOUND SERVICE IN INIT NOTFY EXT: UDN %s, ServID: %d\n",
UDN, servId ) );
if( ( ( sub = GetSubscriptionSID( sid, service ) ) == NULL ) ||
( sub->active ) ) {
free( UDN_copy );
free( reference_count );
free( servId_copy );
HandleUnlock( );
return GENA_E_BAD_SID;
}
DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__,
"FOUND SUBSCRIPTION IN INIT NOTIFY EXT: SID %s",
sid ) );
sub->active = 1;
propertySet = ixmlPrintDocument( PropSet );
if( propertySet == NULL ) {
free( UDN_copy );
free( reference_count );
free( servId_copy );
HandleUnlock( );
return UPNP_E_INVALID_PARAM;
}
DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__,
"GENERATED PROPERY SET IN INIT EXT NOTIFY: %s",
propertySet ) );
headers_size = strlen( "CONTENT-TYPE text/xml\r\n" ) +
strlen( "CONTENT-LENGTH: \r\n" ) + MAX_CONTENT_LENGTH +
strlen( "NT: upnp:event\r\n" ) +
strlen( "NTS: upnp:propchange\r\n" ) + 1;
headers = ( char * )malloc( headers_size );
if( headers == NULL ) {
free( UDN_copy );
free( servId_copy );
free( reference_count );
ixmlFreeDOMString( propertySet );
HandleUnlock( );
return UPNP_E_OUTOF_MEMORY;
}
sprintf( headers, "CONTENT-TYPE: text/xml\r\nCONTENT-LENGTH: "
"%d\r\nNT: upnp:event\r\nNTS: upnp:propchange\r\n",
strlen( propertySet ) + 1 );
//schedule thread for initial notification
thread_struct =
( notify_thread_struct * )
malloc( sizeof( notify_thread_struct ) );
if( thread_struct == NULL ) {
return_code = UPNP_E_OUTOF_MEMORY;
} else {
( *reference_count ) = 1;
thread_struct->servId = servId_copy;
thread_struct->UDN = UDN_copy;
thread_struct->headers = headers;
thread_struct->propertySet = propertySet;
strcpy( thread_struct->sid, sid );
thread_struct->eventKey = sub->eventKey++;
thread_struct->reference_count = reference_count;
thread_struct->device_handle = device_handle;
TPJobInit( &job, ( start_routine ) genaNotifyThread,
thread_struct );
TPJobSetFreeFunction( &job, ( free_routine ) free_notify_struct );
TPJobSetPriority( &job, MED_PRIORITY );
if( ( return_code =
ThreadPoolAdd( &gSendThreadPool, &job, NULL ) ) != 0 ) {
if( return_code == EOUTOFMEM ) {
return_code = UPNP_E_OUTOF_MEMORY;
}
} else {
return_code = GENA_SUCCESS;
}
}
if( return_code != GENA_SUCCESS ) {
ixmlFreeDOMString( propertySet );
free( reference_count );
free( UDN_copy );
free( servId_copy );
free( thread_struct );
free( headers );
}
HandleUnlock( );
return return_code;
}
/****************************************************************************
* Function : genaNotifyAllExt
*
* Parameters :
* IN UpnpDevice_Handle device_handle : Device handle
* IN char *UDN : Device udn
* IN char *servId : Service ID
* IN IXML_Document *PropSet : XML document Event varible property set
*
* Description : This function sends a notification to all the subscribed
* control points
*
* Return : int
*
* Note : This function is similar to the genaNotifyAll. the only difference
* is it takes the document instead of event variable array
****************************************************************************/
int
genaNotifyAllExt( IN UpnpDevice_Handle device_handle,
IN char *UDN,
IN char *servId,
IN IXML_Document * PropSet )
{
char *headers = NULL;
int headers_size;
int return_code = GENA_SUCCESS;
char *UDN_copy = NULL;
char *servId_copy = NULL;
int *reference_count = NULL;
struct Handle_Info *handle_info;
DOMString propertySet = NULL;
ThreadPoolJob job;
subscription *finger = NULL;
notify_thread_struct *thread_struct = NULL;
service_info *service = NULL;
reference_count = ( int * )malloc( sizeof( int ) );
if( reference_count == NULL )
return UPNP_E_OUTOF_MEMORY;
( *reference_count = 0 );
UDN_copy = ( char * )malloc( strlen( UDN ) + 1 );
if( UDN_copy == NULL ) {
free( reference_count );
return UPNP_E_OUTOF_MEMORY;
}
servId_copy = ( char * )malloc( strlen( servId ) + 1 );
if( servId_copy == NULL ) {
free( UDN_copy );
free( reference_count );
return UPNP_E_OUTOF_MEMORY;
}
strcpy( UDN_copy, UDN );
strcpy( servId_copy, servId );
propertySet = ixmlPrintDocument( PropSet );
if( propertySet == NULL ) {
free( UDN_copy );
free( servId_copy );
free( reference_count );
return UPNP_E_INVALID_PARAM;
}
headers_size = strlen( "CONTENT-TYPE text/xml\r\n" ) +
strlen( "CONTENT-LENGTH: \r\n" ) + MAX_CONTENT_LENGTH +
strlen( "NT: upnp:event\r\n" ) +
strlen( "NTS: upnp:propchange\r\n" ) + 1;
headers = ( char * )malloc( headers_size );
if( headers == NULL ) {
free( UDN_copy );
free( servId_copy );
ixmlFreeDOMString( propertySet );
free( reference_count );
return UPNP_E_OUTOF_MEMORY;
}
//changed to add null terminator at end of content
//content length = (length in bytes of property set) + null char
sprintf( headers, "CONTENT-TYPE: text/xml\r\nCONTENT-LENGTH: "
"%d\r\nNT: upnp:event\r\nNTS: upnp:propchange\r\n",
strlen( propertySet ) + 1 );
HandleLock( );
if( GetHandleInfo( device_handle, &handle_info ) != HND_DEVICE )
return_code = GENA_E_BAD_HANDLE;
else {
if( ( service = FindServiceId( &handle_info->ServiceTable,
servId, UDN ) ) != NULL ) {
finger = GetFirstSubscription( service );
while( finger ) {
thread_struct =
( notify_thread_struct * )
malloc( sizeof( notify_thread_struct ) );
if( thread_struct == NULL ) {
break;
return_code = UPNP_E_OUTOF_MEMORY;
}
( *reference_count )++;
thread_struct->reference_count = reference_count;
thread_struct->UDN = UDN_copy;
thread_struct->servId = servId_copy;
thread_struct->headers = headers;
thread_struct->propertySet = propertySet;
strcpy( thread_struct->sid, finger->sid );
thread_struct->eventKey = finger->eventKey++;
thread_struct->device_handle = device_handle;
//if overflow, wrap to 1
if( finger->eventKey < 0 ) {
finger->eventKey = 1;
}
TPJobInit( &job, ( start_routine ) genaNotifyThread,
thread_struct );
TPJobSetFreeFunction( &job,
( free_routine )
free_notify_struct );
TPJobSetPriority( &job, MED_PRIORITY );
if( ( return_code = ThreadPoolAdd( &gSendThreadPool,
&job, NULL ) ) != 0 ) {
if( return_code == EOUTOFMEM ) {
return_code = UPNP_E_OUTOF_MEMORY;
}
break;
}
finger = GetNextSubscription( service, finger );
}
} else
return_code = GENA_E_BAD_SERVICE;
}
if( ( *reference_count ) == 0 ) {
free( reference_count );
free( headers );
ixmlFreeDOMString( propertySet );
free( UDN_copy );
free( servId_copy );
}
HandleUnlock( );
return return_code;
}
/****************************************************************************
* Function : genaNotifyAll
*
* Parameters :
* IN UpnpDevice_Handle device_handle : Device handle
* IN char *UDN : Device udn
* IN char *servId : Service ID
* IN char **VarNames : array of varible names
* IN char **VarValues : array of variable values
* IN int var_count : number of variables
*
* Description : This function sends a notification to all the subscribed
* control points
*
* Return : int
*
* Note : This function is similar to the genaNotifyAllExt. The only difference
* is it takes event variable array instead of xml document.
****************************************************************************/
int
genaNotifyAll( IN UpnpDevice_Handle device_handle,
IN char *UDN,
IN char *servId,
IN char **VarNames,
IN char **VarValues,
IN int var_count )
{
char *headers = NULL;
char *propertySet = NULL;
int headers_size;
int return_code = GENA_SUCCESS;
char *UDN_copy = NULL;
char *servId_copy = NULL;
int *reference_count = NULL;
struct Handle_Info *handle_info;
ThreadPoolJob job;
subscription *finger = NULL;
notify_thread_struct *thread_struct = NULL;
service_info *service = NULL;
reference_count = ( int * )malloc( sizeof( int ) );
if( reference_count == NULL ) {
return UPNP_E_OUTOF_MEMORY;
}
( *reference_count = 0 );
UDN_copy = ( char * )malloc( strlen( UDN ) + 1 );
if( UDN_copy == NULL ) {
free( reference_count );
return UPNP_E_OUTOF_MEMORY;
}
servId_copy = ( char * )malloc( strlen( servId ) + 1 );
if( servId_copy == NULL ) {
free( UDN_copy );
free( reference_count );
return UPNP_E_OUTOF_MEMORY;
}
strcpy( UDN_copy, UDN );
strcpy( servId_copy, servId );
if( ( return_code = GeneratePropertySet( VarNames, VarValues,
var_count,
&propertySet ) ) !=
XML_SUCCESS ) {
free( UDN_copy );
free( servId_copy );
free( reference_count );
return return_code;
}
headers_size = strlen( "CONTENT-TYPE text/xml\r\n" ) +
strlen( "CONTENT-LENGTH: \r\n" ) + MAX_CONTENT_LENGTH +
strlen( "NT: upnp:event\r\n" ) +
strlen( "NTS: upnp:propchange\r\n" ) + 1;
headers = ( char * )malloc( headers_size );
if( headers == NULL ) {
free( UDN_copy );
free( servId_copy );
ixmlFreeDOMString( propertySet );
free( reference_count );
return UPNP_E_OUTOF_MEMORY;
}
//changed to add null terminator at end of content
//content length = (length in bytes of property set) + null char
sprintf( headers, "CONTENT-TYPE: text/xml\r\nCONTENT-LENGTH: %d\r\nNT:"
" upnp:event\r\nNTS: upnp:propchange\r\n",
strlen( propertySet ) + 1 );
HandleLock( );
if( GetHandleInfo( device_handle, &handle_info ) != HND_DEVICE ) {
return_code = GENA_E_BAD_HANDLE;
} else {
if( ( service = FindServiceId( &handle_info->ServiceTable,
servId, UDN ) ) != NULL ) {
finger = GetFirstSubscription( service );
while( finger ) {
thread_struct =
( notify_thread_struct * )
malloc( sizeof( notify_thread_struct ) );
if( thread_struct == NULL ) {
return_code = UPNP_E_OUTOF_MEMORY;
break;
}
( *reference_count )++;
thread_struct->reference_count = reference_count;
thread_struct->UDN = UDN_copy;
thread_struct->servId = servId_copy;
thread_struct->headers = headers;
thread_struct->propertySet = propertySet;
strcpy( thread_struct->sid, finger->sid );
thread_struct->eventKey = finger->eventKey++;
thread_struct->device_handle = device_handle;
//if overflow, wrap to 1
if( finger->eventKey < 0 ) {
finger->eventKey = 1;
}
TPJobInit( &job, ( start_routine ) genaNotifyThread,
thread_struct );
TPJobSetFreeFunction( &job,
( free_routine )
free_notify_struct );
TPJobSetPriority( &job, MED_PRIORITY );
if( ( return_code =
ThreadPoolAdd( &gSendThreadPool, &job, NULL ) )
!= 0 ) {
if( return_code == EOUTOFMEM ) {
return_code = UPNP_E_OUTOF_MEMORY;
break;
}
}
finger = GetNextSubscription( service, finger );
}
} else {
return_code = GENA_E_BAD_SERVICE;
}
}
if( ( *reference_count ) == 0 ) {
free( reference_count );
free( headers );
ixmlFreeDOMString( propertySet );
free( UDN_copy );
free( servId_copy );
}
HandleUnlock( );
return return_code;
}
/****************************************************************************
* Function : respond_ok
*
* Parameters :
* IN SOCKINFO *info : socket connection of request
* IN int time_out : accepted duration
* IN subscription *sub : accepted subscription
* IN http_message_t* request : http request
*
* Description : Function to return OK message in the case
* of a subscription request.
*
* Return : static int
* returns UPNP_E_SUCCESS if successful else returns appropriate error
* Note :
****************************************************************************/
static int
respond_ok( IN SOCKINFO * info,
IN int time_out,
IN subscription * sub,
IN http_message_t * request )
{
int major,
minor;
membuffer response;
int return_code;
char timeout_str[100];
int upnp_timeout_1 = UPNP_TIMEOUT;
http_CalcResponseVersion( request->major_version,
request->minor_version, &major, &minor );
if( time_out >= 0 ) {
sprintf( timeout_str, "TIMEOUT: Second-%d", time_out );
} else {
strcpy( timeout_str, "TIMEOUT: Second-infinite" );
}
membuffer_init( &response );
response.size_inc = 30;
if( http_MakeMessage( &response, major, minor,
"R" "D" "S" "ssc" "sc" "c",
HTTP_OK,
"SID: ", sub->sid, timeout_str ) != 0 ) {
membuffer_destroy( &response );
error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request );
return UPNP_E_OUTOF_MEMORY;
}
return_code = http_SendMessage( info, &upnp_timeout_1, "b",
response.buf, response.length );
membuffer_destroy( &response );
return return_code;
}
/****************************************************************************
* Function : create_url_list
*
* Parameters :
* IN memptr* url_list :
* OUT URL_list *out :
*
* Description : Function to parse the Callback header value in
* subscription requests takes in a buffer containing URLS delimited by
* '<' and '>'. The entire buffer is copied into dynamic memory
* and stored in the URL_list. Pointers to the individual urls within
* this buffer are allocated and stored in the URL_list. Only URLs with
* network addresses are considered(i.e. host:port or domain name)
*
* Return : int
* if successful returns the number of URLs parsed
* else UPNP_E_OUTOF_MEMORY
* Note :
****************************************************************************/
static int
create_url_list( IN memptr * url_list,
OUT URL_list * out )
{
int URLcount = 0;
int i;
int return_code = 0;
uri_type temp;
token urls;
token *URLS;
urls.buff = url_list->buf;
urls.size = url_list->length;
URLS = &urls;
out->size = 0;
out->URLs = NULL;
out->parsedURLs = NULL;
for( i = 0; i < URLS->size; i++ ) {
if( ( URLS->buff[i] == '<' ) && ( i + 1 < URLS->size ) ) {
if( ( ( return_code = parse_uri( &URLS->buff[i + 1],
URLS->size - i + 1,
&temp ) ) == HTTP_SUCCESS )
&& ( temp.hostport.text.size != 0 ) ) {
URLcount++;
} else {
if( return_code == UPNP_E_OUTOF_MEMORY ) {
return return_code;
}
}
}
}
if( URLcount > 0 ) {
out->URLs = ( char * )malloc( URLS->size + 1 );
out->parsedURLs =
( uri_type * ) malloc( sizeof( uri_type ) * URLcount );
if( ( out->URLs == NULL ) || ( out->parsedURLs == NULL ) ) {
free( out->URLs );
free( out->parsedURLs );
out->URLs = NULL;
out->parsedURLs = NULL;
return UPNP_E_OUTOF_MEMORY;
}
memcpy( out->URLs, URLS->buff, URLS->size );
out->URLs[URLS->size] = 0;
URLcount = 0;
for( i = 0; i < URLS->size; i++ ) {
if( ( URLS->buff[i] == '<' ) && ( i + 1 < URLS->size ) ) {
if( ( ( return_code =
parse_uri( &out->URLs[i + 1], URLS->size - i + 1,
&out->parsedURLs[URLcount] ) ) ==
HTTP_SUCCESS )
&& ( out->parsedURLs[URLcount].hostport.text.size !=
0 ) ) {
URLcount++;
} else {
if( return_code == UPNP_E_OUTOF_MEMORY ) {
free( out->URLs );
free( out->parsedURLs );
out->URLs = NULL;
out->parsedURLs = NULL;
return return_code;
}
}
}
}
}
out->size = URLcount;
return URLcount;
}
/****************************************************************************
* Function : gena_process_subscription_request
*
* Parameters :
* IN SOCKINFO *info : socket info of the device
* IN http_message_t* request : SUBSCRIPTION request from the control
* point
*
* Description : This function handles a subscription request from a
* ctrl point. The socket is not closed on return.
*
* Return : void
*
* Note :
****************************************************************************/
void
gena_process_subscription_request( IN SOCKINFO * info,
IN http_message_t * request )
{
Upnp_SID temp_sid;
int return_code = 1;
int time_out = 1801;
service_info *service;
struct Upnp_Subscription_Request request_struct;
subscription *sub;
uuid_upnp uid;
struct Handle_Info *handle_info;
void *cookie;
Upnp_FunPtr callback_fun;
UpnpDevice_Handle device_handle;
memptr nt_hdr;
char *event_url_path = NULL;
memptr callback_hdr;
memptr timeout_hdr;
DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__,
"Subscription Request Received:\n" ) );
if( httpmsg_find_hdr( request, HDR_NT, &nt_hdr ) == NULL ) {
error_respond( info, HTTP_BAD_REQUEST, request );
return;
}
// check NT header
//Windows Millenium Interoperability:
//we accept either upnp:event, or upnp:propchange for the NT header
if( memptr_cmp_nocase( &nt_hdr, "upnp:event" ) != 0 ) {
error_respond( info, HTTP_PRECONDITION_FAILED, request );
return;
}
// if a SID is present then the we have a bad request
// "incompatible headers"
if( httpmsg_find_hdr( request, HDR_SID, NULL ) != NULL ) {
error_respond( info, HTTP_BAD_REQUEST, request );
return;
}
//look up service by eventURL
if( ( event_url_path = str_alloc( request->uri.pathquery.buff,
request->uri.pathquery.size ) ) ==
NULL ) {
error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request );
return;
}
DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__,
"SubscriptionRequest for event URL path: %s\n",
event_url_path );
)
HandleLock( );
// CURRENTLY, ONLY ONE DEVICE
if( GetDeviceHandleInfo( &device_handle, &handle_info ) != HND_DEVICE ) {
free( event_url_path );
error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request );
HandleUnlock( );
return;
}
service = FindServiceEventURLPath( &handle_info->ServiceTable,
event_url_path );
free( event_url_path );
if( service == NULL || !service->active ) {
error_respond( info, HTTP_NOT_FOUND, request );
HandleUnlock( );
return;
}
DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__,
"Subscription Request: Number of Subscriptions already %d\n "
"Max Subscriptions allowed: %d\n",
service->TotalSubscriptions,
handle_info->MaxSubscriptions ) );
// too many subscriptions
if( handle_info->MaxSubscriptions != -1 &&
service->TotalSubscriptions >= handle_info->MaxSubscriptions ) {
error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request );
HandleUnlock( );
return;
}
// generate new subscription
sub = ( subscription * ) malloc( sizeof( subscription ) );
if( sub == NULL ) {
error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request );
HandleUnlock( );
return;
}
sub->eventKey = 0;
sub->ToSendEventKey = 0;
sub->active = 0;
sub->next = NULL;
sub->DeliveryURLs.size = 0;
sub->DeliveryURLs.URLs = NULL;
sub->DeliveryURLs.parsedURLs = NULL;
// check for valid callbacks
if( httpmsg_find_hdr( request, HDR_CALLBACK, &callback_hdr ) == NULL ||
( return_code = create_url_list( &callback_hdr,
&sub->DeliveryURLs ) ) == 0 ) {
error_respond( info, HTTP_PRECONDITION_FAILED, request );
freeSubscriptionList( sub );
HandleUnlock( );
return;
}
if( return_code == UPNP_E_OUTOF_MEMORY ) {
error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request );
freeSubscriptionList( sub );
HandleUnlock( );
return;
}
// set the timeout
if( httpmsg_find_hdr( request, HDR_TIMEOUT, &timeout_hdr ) != NULL ) {
if( matchstr( timeout_hdr.buf, timeout_hdr.length,
"%iSecond-%d%0", &time_out ) == PARSE_OK ) {
// nothing
} else if( memptr_cmp_nocase( &timeout_hdr, "Second-infinite" ) ==
0 ) {
time_out = -1; // infinite timeout
} else {
time_out = DEFAULT_TIMEOUT; // default is > 1800 seconds
}
}
// replace infinite timeout with max timeout, if possible
if( handle_info->MaxSubscriptionTimeOut != -1 ) {
if( time_out == -1 ||
time_out > handle_info->MaxSubscriptionTimeOut ) {
time_out = handle_info->MaxSubscriptionTimeOut;
}
}
if( time_out >= 0 ) {
sub->expireTime = time( NULL ) + time_out;
} else {
sub->expireTime = 0; // infinite time
}
//generate SID
uuid_create( &uid );
uuid_unpack( &uid, temp_sid );
sprintf( sub->sid, "uuid:%s", temp_sid );
// respond OK
if( respond_ok( info, time_out, sub, request ) != UPNP_E_SUCCESS ) {
freeSubscriptionList( sub );
HandleUnlock( );
return;
}
//add to subscription list
sub->next = service->subscriptionList;
service->subscriptionList = sub;
service->TotalSubscriptions++;
//finally generate callback for init table dump
request_struct.ServiceId = service->serviceId;
request_struct.UDN = service->UDN;
strcpy( ( char * )request_struct.Sid, sub->sid );
//copy callback
callback_fun = handle_info->Callback;
cookie = handle_info->Cookie;
HandleUnlock( );
//make call back with request struct
//in the future should find a way of mainting
//that the handle is not unregistered in the middle of a
//callback
callback_fun( UPNP_EVENT_SUBSCRIPTION_REQUEST,
&request_struct, cookie );
}
/****************************************************************************
* Function : gena_process_subscription_renewal_request
*
* Parameters :
* IN SOCKINFO *info : socket info of the device
* IN http_message_t* request : subscription renewal request from the
* control point
*
* Description : This function handles a subscription renewal request
* from a ctrl point. The connection is not destroyed on return.
*
* Return : void
*
* Note :
****************************************************************************/
void
gena_process_subscription_renewal_request( IN SOCKINFO * info,
IN http_message_t * request )
{
Upnp_SID sid;
subscription *sub;
int time_out = 1801;
service_info *service;
struct Handle_Info *handle_info;
UpnpDevice_Handle device_handle;
memptr temp_hdr;
membuffer event_url_path;
memptr timeout_hdr;
// if a CALLBACK or NT header is present, then it is an error
if( httpmsg_find_hdr( request, HDR_CALLBACK, NULL ) != NULL ||
httpmsg_find_hdr( request, HDR_NT, NULL ) != NULL ) {
error_respond( info, HTTP_BAD_REQUEST, request );
return;
}
// get SID
if( httpmsg_find_hdr( request, HDR_SID, &temp_hdr ) == NULL ||
temp_hdr.length > SID_SIZE ) {
error_respond( info, HTTP_PRECONDITION_FAILED, request );
return;
}
memcpy( sid, temp_hdr.buf, temp_hdr.length );
sid[temp_hdr.length] = '\0';
// lookup service by eventURL
membuffer_init( &event_url_path );
if( membuffer_append( &event_url_path, request->uri.pathquery.buff,
request->uri.pathquery.size ) != 0 ) {
error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request );
return;
}
HandleLock( );
// CURRENTLY, ONLY SUPPORT ONE DEVICE
if( GetDeviceHandleInfo( &device_handle, &handle_info ) != HND_DEVICE ) {
error_respond( info, HTTP_PRECONDITION_FAILED, request );
membuffer_destroy( &event_url_path );
return;
}
service = FindServiceEventURLPath( &handle_info->ServiceTable,
event_url_path.buf );
membuffer_destroy( &event_url_path );
// get subscription
if( service == NULL ||
!service->active ||
( ( sub = GetSubscriptionSID( sid, service ) ) == NULL ) ) {
error_respond( info, HTTP_PRECONDITION_FAILED, request );
HandleUnlock( );
return;
}
DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__,
"Renew request: Number of subscriptions already: %d\n "
"Max Subscriptions allowed:%d\n",
service->TotalSubscriptions,
handle_info->MaxSubscriptions );
)
// too many subscriptions
if( handle_info->MaxSubscriptions != -1 &&
service->TotalSubscriptions > handle_info->MaxSubscriptions ) {
error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request );
RemoveSubscriptionSID( sub->sid, service );
HandleUnlock( );
return;
}
// set the timeout
if( httpmsg_find_hdr( request, HDR_TIMEOUT, &timeout_hdr ) != NULL ) {
if( matchstr( timeout_hdr.buf, timeout_hdr.length,
"%iSecond-%d%0", &time_out ) == PARSE_OK ) {
//nothing
} else if( memptr_cmp_nocase( &timeout_hdr, "Second-infinite" ) ==
0 ) {
time_out = -1; // inifinite timeout
} else {
time_out = DEFAULT_TIMEOUT; // default is > 1800 seconds
}
}
// replace infinite timeout with max timeout, if possible
if( handle_info->MaxSubscriptionTimeOut != -1 ) {
if( time_out == -1 ||
time_out > handle_info->MaxSubscriptionTimeOut ) {
time_out = handle_info->MaxSubscriptionTimeOut;
}
}
if( time_out == -1 ) {
sub->expireTime = 0;
} else {
sub->expireTime = time( NULL ) + time_out;
}
if( respond_ok( info, time_out, sub, request ) != UPNP_E_SUCCESS ) {
RemoveSubscriptionSID( sub->sid, service );
}
HandleUnlock( );
}
/****************************************************************************
* Function : gena_process_unsubscribe_request
*
* Parameters :
* IN SOCKINFO *info : socket info of the device
* IN http_message_t* request : UNSUBSCRIBE request from the control
* point
*
* Description : This function Handles a subscription cancellation request
* from a ctrl point. The connection is not destroyed on return.
*
* Return : void
*
* Note :
****************************************************************************/
void
gena_process_unsubscribe_request( IN SOCKINFO * info,
IN http_message_t * request )
{
Upnp_SID sid;
service_info *service;
struct Handle_Info *handle_info;
UpnpDevice_Handle device_handle;
memptr temp_hdr;
membuffer event_url_path;
// if a CALLBACK or NT header is present, then it is an error
if( httpmsg_find_hdr( request, HDR_CALLBACK, NULL ) != NULL ||
httpmsg_find_hdr( request, HDR_NT, NULL ) != NULL ) {
error_respond( info, HTTP_BAD_REQUEST, request );
return;
}
// get SID
if( httpmsg_find_hdr( request, HDR_SID, &temp_hdr ) == NULL ||
temp_hdr.length > SID_SIZE ) {
error_respond( info, HTTP_PRECONDITION_FAILED, request );
return;
}
memcpy( sid, temp_hdr.buf, temp_hdr.length );
sid[temp_hdr.length] = '\0';
// lookup service by eventURL
membuffer_init( &event_url_path );
if( membuffer_append( &event_url_path, request->uri.pathquery.buff,
request->uri.pathquery.size ) != 0 ) {
error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request );
return;
}
HandleLock( );
// CURRENTLY, ONLY SUPPORT ONE DEVICE
if( GetDeviceHandleInfo( &device_handle, &handle_info ) != HND_DEVICE ) {
error_respond( info, HTTP_PRECONDITION_FAILED, request );
membuffer_destroy( &event_url_path );
HandleUnlock( );
return;
}
service = FindServiceEventURLPath( &handle_info->ServiceTable,
event_url_path.buf );
membuffer_destroy( &event_url_path );
// validate service
if( service == NULL ||
!service->active || GetSubscriptionSID( sid, service ) == NULL )
//CheckSubscriptionSID(sid, service) == NULL )
{
error_respond( info, HTTP_PRECONDITION_FAILED, request );
HandleUnlock( );
return;
}
RemoveSubscriptionSID( sid, service );
error_respond( info, HTTP_OK, request ); // success
HandleUnlock( );
}
#endif // INCLUDE_DEVICE_APIS
#endif // EXCLUDE_GENA