Files
libupnp/upnp/src/gena/gena_ctrlpt.c
2007-11-19 14:15:45 +00:00

885 lines
28 KiB
C

///////////////////////////////////////////////////////////////////////////
//
// 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_CLIENT_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"
extern ithread_mutex_t GlobalClientSubscribeMutex;
/************************************************************************
* Function : GenaAutoRenewSubscription
*
* Parameters:
* IN void *input: Thread data(upnp_timeout *) needed to send the renewal
*
* Description:
* This is a thread function to send the renewal just before the
* subscription times out.
*
* Returns: VOID
*
***************************************************************************/
static void
GenaAutoRenewSubscription( IN void *input )
{
upnp_timeout *event = ( upnp_timeout * ) input;
void *cookie;
Upnp_FunPtr callback_fun;
struct Handle_Info *handle_info;
struct Upnp_Event_Subscribe *sub_struct =
( struct Upnp_Event_Subscribe * )
event->Event;
int send_callback = 0;
int eventType = 0;
if( AUTO_RENEW_TIME == 0 ) {
UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__,
"GENA SUB EXPIRED" );
sub_struct->ErrCode = UPNP_E_SUCCESS;
send_callback = 1;
eventType = UPNP_EVENT_SUBSCRIPTION_EXPIRED;
} else {
UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__,
"GENA AUTO RENEW" );
if( ( ( sub_struct->ErrCode = genaRenewSubscription( event->handle,
sub_struct->
Sid,
&sub_struct->
TimeOut ) ) !=
UPNP_E_SUCCESS )
&& ( sub_struct->ErrCode != GENA_E_BAD_SID )
&& ( sub_struct->ErrCode != GENA_E_BAD_HANDLE ) ) {
send_callback = 1;
eventType = UPNP_EVENT_AUTORENEWAL_FAILED;
}
}
if( send_callback ) {
HandleReadLock();
if( GetHandleInfo( event->handle, &handle_info ) != HND_CLIENT ) {
HandleUnlock();
free_upnp_timeout( event );
return;
}
UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__,
"HANDLE IS VALID" );
callback_fun = handle_info->Callback;
cookie = handle_info->Cookie;
HandleUnlock();
//make callback
callback_fun( eventType, event->Event, cookie );
}
free_upnp_timeout( event );
}
/************************************************************************
* Function : ScheduleGenaAutoRenew
*
* Parameters:
* IN int client_handle: Handle that also contains the subscription list
* IN int TimeOut: The time out value of the subscription
* IN client_subscription * sub: Subscription being renewed
*
* Description:
* This function schedules a job to renew the subscription just before
* time out.
*
* Returns: int
* return GENA_E_SUCCESS if successful else returns appropriate error
***************************************************************************/
static int
ScheduleGenaAutoRenew( IN int client_handle,
IN int TimeOut,
IN client_subscription * sub )
{
struct Upnp_Event_Subscribe *RenewEventStruct = NULL;
upnp_timeout *RenewEvent = NULL;
int return_code = GENA_SUCCESS;
ThreadPoolJob job;
if( TimeOut == UPNP_INFINITE ) {
return GENA_SUCCESS;
}
RenewEventStruct = ( struct Upnp_Event_Subscribe * )malloc( sizeof
( struct
Upnp_Event_Subscribe ) );
if( RenewEventStruct == NULL ) {
return UPNP_E_OUTOF_MEMORY;
}
RenewEvent = ( upnp_timeout * ) malloc( sizeof( upnp_timeout ) );
if( RenewEvent == NULL ) {
free( RenewEventStruct );
return UPNP_E_OUTOF_MEMORY;
}
//schedule expire event
strcpy( RenewEventStruct->Sid, sub->sid );
RenewEventStruct->ErrCode = UPNP_E_SUCCESS;
strncpy( RenewEventStruct->PublisherUrl, sub->EventURL,
NAME_SIZE - 1 );
RenewEventStruct->TimeOut = TimeOut;
//RenewEvent->EventType=UPNP_EVENT_SUBSCRIPTION_EXPIRE;
RenewEvent->handle = client_handle;
RenewEvent->Event = RenewEventStruct;
TPJobInit( &job, ( start_routine ) GenaAutoRenewSubscription,
RenewEvent );
TPJobSetFreeFunction( &job, ( free_routine ) free_upnp_timeout );
TPJobSetPriority( &job, MED_PRIORITY );
//Schedule the job
if( ( return_code = TimerThreadSchedule( &gTimerThread,
TimeOut - AUTO_RENEW_TIME,
REL_SEC, &job, SHORT_TERM,
&( RenewEvent->
eventId ) ) ) !=
UPNP_E_SUCCESS ) {
free( RenewEvent );
free( RenewEventStruct );
return return_code;
}
sub->RenewEventId = RenewEvent->eventId;
return GENA_SUCCESS;
}
/************************************************************************
* Function : gena_unsubscribe
*
* Parameters:
* IN char *url: Event URL of the service
* IN char *sid: The subcription ID.
* OUT http_parser_t* response: The UNSUBCRIBE response from the device
*
* Description:
* This function sends the UNSUBCRIBE gena request and recieves the
* response from the device and returns it as a parameter
*
* Returns: int
* return 0 if successful else returns appropriate error
***************************************************************************/
static int
gena_unsubscribe( IN char *url,
IN char *sid,
OUT http_parser_t * response )
{
int return_code;
uri_type dest_url;
membuffer request;
// parse url
return_code = http_FixStrUrl( url, strlen( url ), &dest_url );
if( return_code != 0 ) {
return return_code;
}
// make request msg
membuffer_init( &request );
request.size_inc = 30;
return_code = http_MakeMessage(
&request, 1, 1,
"q" "ssc" "Uc",
HTTPMETHOD_UNSUBSCRIBE, &dest_url,
"SID: ", sid );
//Not able to make the message so destroy the existing buffer
if( return_code != 0 ) {
membuffer_destroy( &request );
return return_code;
}
// send request and get reply
return_code = http_RequestAndResponse( &dest_url, request.buf,
request.length,
HTTPMETHOD_UNSUBSCRIBE,
HTTP_DEFAULT_TIMEOUT,
response );
membuffer_destroy( &request );
if( return_code != 0 )
httpmsg_destroy( &response->msg );
if( return_code == 0 && response->msg.status_code != HTTP_OK ) {
return_code = UPNP_E_UNSUBSCRIBE_UNACCEPTED;
httpmsg_destroy( &response->msg );
}
return return_code;
}
/************************************************************************
* Function : gena_subscribe
*
* Parameters:
* IN char *url: url of service to subscribe
* INOUT int* timeout:subscription time desired (in secs)
* IN char* renewal_sid:for renewal, this contains a currently h
* held subscription SID. For first time
* subscription, this must be NULL
* OUT char** sid: SID returned by the subscription or renew msg
*
* Description:
* This function subscribes or renew subscription
*
* Returns: int
* return 0 if successful else returns appropriate error
***************************************************************************/
static int
gena_subscribe( IN char *url,
INOUT int *timeout,
IN char *renewal_sid,
OUT char **sid )
{
int return_code;
memptr sid_hdr,
timeout_hdr;
char timeout_str[25];
membuffer request;
uri_type dest_url;
http_parser_t response;
*sid = NULL; // init
// request timeout to string
if ( timeout == NULL ) {
timeout = (int *)malloc(sizeof(int));
if(timeout == 0) return UPNP_E_OUTOF_MEMORY;
sprintf( timeout_str, "%d", CP_MINIMUM_SUBSCRIPTION_TIME );
} else if( ( *timeout > 0 )&& ( *timeout < CP_MINIMUM_SUBSCRIPTION_TIME ) ) {
sprintf( timeout_str, "%d", CP_MINIMUM_SUBSCRIPTION_TIME );
} else if( *timeout >= 0 ) {
sprintf( timeout_str, "%d", *timeout );
} else {
strcpy( timeout_str, "infinite" );
}
// parse url
return_code = http_FixStrUrl( url, strlen( url ), &dest_url );
if( return_code != 0 ) {
return return_code;
}
// make request msg
membuffer_init( &request );
request.size_inc = 30;
if( renewal_sid ) {
// renew subscription
return_code = http_MakeMessage(
&request, 1, 1,
"q" "ssc" "sscc",
HTTPMETHOD_SUBSCRIBE, &dest_url,
"SID: ", renewal_sid,
"TIMEOUT: Second-", timeout_str );
} else {
// subscribe
return_code = http_MakeMessage(
&request, 1, 1,
"q" "sssdsc" "sc" "sscc",
HTTPMETHOD_SUBSCRIBE, &dest_url,
"CALLBACK: <http://", LOCAL_HOST, ":", LOCAL_PORT, "/>",
"NT: upnp:event",
"TIMEOUT: Second-", timeout_str );
}
if( return_code != 0 ) {
return return_code;
}
// send request and get reply
return_code = http_RequestAndResponse( &dest_url, request.buf,
request.length,
HTTPMETHOD_SUBSCRIBE,
HTTP_DEFAULT_TIMEOUT,
&response );
membuffer_destroy( &request );
if( return_code != 0 ) {
httpmsg_destroy( &response.msg );
return return_code;
}
if( response.msg.status_code != HTTP_OK ) {
httpmsg_destroy( &response.msg );
return UPNP_E_SUBSCRIBE_UNACCEPTED;
}
// get SID and TIMEOUT
if( httpmsg_find_hdr( &response.msg, HDR_SID, &sid_hdr ) == NULL ||
sid_hdr.length == 0 ||
httpmsg_find_hdr( &response.msg,
HDR_TIMEOUT, &timeout_hdr ) == NULL ||
timeout_hdr.length == 0 ) {
httpmsg_destroy( &response.msg );
return UPNP_E_BAD_RESPONSE;
}
// save timeout
if( matchstr( timeout_hdr.buf, timeout_hdr.length, "%iSecond-%d%0",
timeout ) == PARSE_OK ) {
// nothing
} else if( memptr_cmp_nocase( &timeout_hdr, "Second-infinite" ) == 0 ) {
*timeout = -1;
} else {
httpmsg_destroy( &response.msg );
return UPNP_E_BAD_RESPONSE;
}
// save SID
*sid = str_alloc( sid_hdr.buf, sid_hdr.length );
if( *sid == NULL ) {
httpmsg_destroy( &response.msg );
return UPNP_E_OUTOF_MEMORY;
}
httpmsg_destroy( &response.msg );
return UPNP_E_SUCCESS;
}
/************************************************************************
* Function : genaUnregisterClient
*
* Parameters:
* IN UpnpClient_Handle client_handle: Handle containing all the control
* point related information
*
* Description:
* This function unsubcribes all the outstanding subscriptions and cleans
* the subscription list. This function is called when control point
* unregisters.
*
* Returns: int
* return UPNP_E_SUCCESS if successful else returns appropriate error
***************************************************************************/
int
genaUnregisterClient( IN UpnpClient_Handle client_handle )
{
client_subscription sub_copy;
int return_code = UPNP_E_SUCCESS;
struct Handle_Info *handle_info = NULL;
http_parser_t response;
while( TRUE ) {
HandleLock();
if( GetHandleInfo( client_handle, &handle_info ) != HND_CLIENT ) {
HandleUnlock();
return GENA_E_BAD_HANDLE;
}
if( handle_info->ClientSubList == NULL ) {
return_code = UPNP_E_SUCCESS;
break;
}
return_code = copy_client_subscription( handle_info->ClientSubList,
&sub_copy );
if( return_code != HTTP_SUCCESS ) {
break;
}
RemoveClientSubClientSID( &handle_info->ClientSubList,
sub_copy.sid );
HandleUnlock();
return_code = gena_unsubscribe( sub_copy.EventURL,
sub_copy.ActualSID, &response );
if( return_code == 0 ) {
httpmsg_destroy( &response.msg );
}
free_client_subscription( &sub_copy );
}
freeClientSubList( handle_info->ClientSubList );
HandleUnlock();
return return_code;
}
/************************************************************************
* Function : genaUnSubscribe
*
* Parameters:
* IN UpnpClient_Handle client_handle: UPnP client handle
* IN SID in_sid: The subscription ID
*
* Description:
* This function unsubscribes a SID. It first validates the SID and
* client_handle,copies the subscription, sends UNSUBSCRIBE http request
* to service processes request and finally removes the subscription
*
* Returns: int
* return UPNP_E_SUCCESS if service response is OK else
* returns appropriate error
***************************************************************************/
#ifdef INCLUDE_CLIENT_APIS
int
genaUnSubscribe( IN UpnpClient_Handle client_handle,
IN const Upnp_SID in_sid )
{
client_subscription *sub;
int return_code = GENA_SUCCESS;
struct Handle_Info *handle_info;
client_subscription sub_copy;
http_parser_t response;
HandleLock();
// validate handle and sid
if( GetHandleInfo( client_handle, &handle_info ) != HND_CLIENT ) {
HandleUnlock();
return GENA_E_BAD_HANDLE;
}
if( ( sub =
GetClientSubClientSID( handle_info->ClientSubList, in_sid ) )
== NULL ) {
HandleUnlock();
return GENA_E_BAD_SID;
}
return_code = copy_client_subscription( sub, &sub_copy );
HandleUnlock();
return_code = gena_unsubscribe( sub_copy.EventURL, sub_copy.ActualSID,
&response );
if( return_code == 0 ) {
httpmsg_destroy( &response.msg );
}
free_client_subscription( &sub_copy );
HandleLock();
if( GetHandleInfo( client_handle, &handle_info ) != HND_CLIENT ) {
HandleUnlock();
return GENA_E_BAD_HANDLE;
}
RemoveClientSubClientSID( &handle_info->ClientSubList, in_sid );
HandleUnlock();
return return_code;
}
#endif
/************************************************************************
* Function : genaSubscribe
*
* Parameters:
* IN UpnpClient_Handle client_handle:
* IN char * PublisherURL: NULL Terminated, of the form :
* "http://134.134.156.80:4000/RedBulb/Event"
* INOUT int * TimeOut: requested Duration, if -1, then "infinite".
* in the OUT case: actual Duration granted
* by Service, -1 for infinite
* OUT Upnp_SID out_sid:sid of subscription, memory passed in by caller
*
* Description:
* This function subscribes to a PublisherURL ( also mentioned as EventURL
* some places). It sends SUBSCRIBE http request to service processes
* request. Finally adds a Subscription to
* the clients subscription list, if service responds with OK
*
* Returns: int
* return UPNP_E_SUCCESS if service response is OK else
* returns appropriate error
***************************************************************************/
#ifdef INCLUDE_CLIENT_APIS
int
genaSubscribe( IN UpnpClient_Handle client_handle,
IN char *PublisherURL,
INOUT int *TimeOut,
OUT Upnp_SID out_sid )
{
int return_code = GENA_SUCCESS;
client_subscription *newSubscription = NULL;
uuid_upnp uid;
Upnp_SID temp_sid;
char *ActualSID = NULL;
struct Handle_Info *handle_info;
char *EventURL = NULL;
UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__,
"GENA SUBSCRIBE BEGIN" );
memset( out_sid, 0, sizeof( Upnp_SID ) );
HandleReadLock();
// validate handle
if( GetHandleInfo( client_handle, &handle_info ) != HND_CLIENT ) {
HandleUnlock();
return GENA_E_BAD_HANDLE;
}
HandleUnlock();
// subscribe
SubscribeLock();
return_code =
gena_subscribe( PublisherURL, TimeOut, NULL, &ActualSID );
HandleLock();
if( return_code != UPNP_E_SUCCESS ) {
UpnpPrintf( UPNP_CRITICAL, GENA, __FILE__, __LINE__,
"SUBSCRIBE FAILED in transfer error code: %d returned\n",
return_code );
goto error_handler;
}
if( GetHandleInfo( client_handle, &handle_info ) != HND_CLIENT ) {
return_code = GENA_E_BAD_HANDLE;
goto error_handler;
}
// generate client SID
uuid_create( &uid );
uuid_unpack( &uid, temp_sid );
sprintf( out_sid, "uuid:%s", temp_sid );
// create event url
EventURL = ( char * )malloc( strlen( PublisherURL ) + 1 );
if( EventURL == NULL ) {
return_code = UPNP_E_OUTOF_MEMORY;
goto error_handler;
}
strcpy( EventURL, PublisherURL );
// fill subscription
newSubscription =
( client_subscription * ) malloc( sizeof( client_subscription ) );
if( newSubscription == NULL ) {
return_code = UPNP_E_OUTOF_MEMORY;
goto error_handler;
}
newSubscription->EventURL = EventURL;
newSubscription->ActualSID = ActualSID;
strcpy( newSubscription->sid, out_sid );
newSubscription->RenewEventId = -1;
newSubscription->next = handle_info->ClientSubList;
handle_info->ClientSubList = newSubscription;
// schedule expiration event
return_code = ScheduleGenaAutoRenew( client_handle, *TimeOut,
newSubscription );
error_handler:
if( return_code != UPNP_E_SUCCESS ) {
free( ActualSID );
free( EventURL );
free( newSubscription );
}
HandleUnlock();
SubscribeUnlock();
return return_code;
}
#endif
/************************************************************************
* Function : genaRenewSubscription
*
* Parameters:
* IN UpnpClient_Handle client_handle: Client handle
* IN const Upnp_SID in_sid: subscription ID
* INOUT int * TimeOut: requested Duration, if -1, then "infinite".
* in the OUT case: actual Duration granted
* by Service, -1 for infinite
*
* Description:
* This function renews a SID. It first validates the SID and
* client_handle and copies the subscription. It sends RENEW
* (modified SUBSCRIBE) http request to service and processes
* the response.
*
* Returns: int
* return UPNP_E_SUCCESS if service response is OK else
* returns appropriate error
***************************************************************************/
int
genaRenewSubscription( IN UpnpClient_Handle client_handle,
IN const Upnp_SID in_sid,
INOUT int *TimeOut )
{
int return_code = GENA_SUCCESS;
client_subscription *sub;
client_subscription sub_copy;
struct Handle_Info *handle_info;
char *ActualSID;
ThreadPoolJob tempJob;
HandleLock();
// validate handle and sid
if( GetHandleInfo( client_handle, &handle_info ) != HND_CLIENT ) {
HandleUnlock();
return GENA_E_BAD_HANDLE;
}
if( ( sub = GetClientSubClientSID( handle_info->ClientSubList,
in_sid ) ) == NULL ) {
HandleUnlock();
return GENA_E_BAD_SID;
}
// remove old events
if( TimerThreadRemove( &gTimerThread, sub->RenewEventId, &tempJob ) ==
0 ) {
free_upnp_timeout( ( upnp_timeout * ) tempJob.arg );
}
UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__,
"REMOVED AUTO RENEW EVENT" );
sub->RenewEventId = -1;
return_code = copy_client_subscription( sub, &sub_copy );
HandleUnlock();
if( return_code != HTTP_SUCCESS ) {
return return_code;
}
return_code = gena_subscribe( sub_copy.EventURL, TimeOut,
sub_copy.ActualSID, &ActualSID );
HandleLock();
if( GetHandleInfo( client_handle, &handle_info ) != HND_CLIENT ) {
HandleUnlock();
if( return_code == UPNP_E_SUCCESS ) {
free( ActualSID );
}
return GENA_E_BAD_HANDLE;
}
// we just called GetHandleInfo, so we don't check for return value
//GetHandleInfo(client_handle, &handle_info);
if( return_code != UPNP_E_SUCCESS ) {
// network failure (remove client sub)
RemoveClientSubClientSID( &handle_info->ClientSubList, in_sid );
free_client_subscription( &sub_copy );
HandleUnlock();
return return_code;
}
// get subscription
if( ( sub = GetClientSubClientSID( handle_info->ClientSubList,
in_sid ) ) == NULL ) {
free( ActualSID );
free_client_subscription( &sub_copy );
HandleUnlock();
return GENA_E_BAD_SID;
}
// store actual sid
free( sub->ActualSID );
sub->ActualSID = ActualSID;
// start renew subscription timer
return_code = ScheduleGenaAutoRenew( client_handle, *TimeOut, sub );
if( return_code != GENA_SUCCESS ) {
RemoveClientSubClientSID( &handle_info->ClientSubList, sub->sid );
}
free_client_subscription( &sub_copy );
HandleUnlock();
return return_code;
}
/************************************************************************
* Function : gena_process_notification_event
*
* Parameters:
* IN SOCKINFO *info: Socket structure containing the device socket
* information
* IN http_message_t* event: The http message contains the GENA
* notification
*
* Description:
* This function processes NOTIFY events that are sent by devices.
* called by genacallback()
*
* Returns: void
*
* Note : called by genacallback()
****************************************************************************/
void
gena_process_notification_event( IN SOCKINFO * info,
IN http_message_t * event )
{
struct Upnp_Event event_struct;
int eventKey;
token sid;
client_subscription *subscription;
IXML_Document *ChangedVars;
struct Handle_Info *handle_info;
void *cookie;
Upnp_FunPtr callback;
UpnpClient_Handle client_handle;
memptr sid_hdr;
memptr nt_hdr,
nts_hdr;
memptr seq_hdr;
// get SID
if( httpmsg_find_hdr( event, HDR_SID, &sid_hdr ) == NULL ) {
error_respond( info, HTTP_PRECONDITION_FAILED, event );
return;
}
sid.buff = sid_hdr.buf;
sid.size = sid_hdr.length;
// get event key
if( httpmsg_find_hdr( event, HDR_SEQ, &seq_hdr ) == NULL ||
matchstr( seq_hdr.buf, seq_hdr.length, "%d%0", &eventKey )
!= PARSE_OK ) {
error_respond( info, HTTP_BAD_REQUEST, event );
return;
}
// get NT and NTS headers
if( httpmsg_find_hdr( event, HDR_NT, &nt_hdr ) == NULL ||
httpmsg_find_hdr( event, HDR_NTS, &nts_hdr ) == NULL ) {
error_respond( info, HTTP_BAD_REQUEST, event );
return;
}
// verify NT and NTS headers
if( memptr_cmp( &nt_hdr, "upnp:event" ) != 0 ||
memptr_cmp( &nts_hdr, "upnp:propchange" ) != 0 ) {
error_respond( info, HTTP_PRECONDITION_FAILED, event );
return;
}
// parse the content (should be XML)
if( !has_xml_content_type( event ) ||
event->msg.length == 0 ||
( ixmlParseBufferEx( event->entity.buf, &ChangedVars ) ) !=
IXML_SUCCESS ) {
error_respond( info, HTTP_BAD_REQUEST, event );
return;
}
HandleLock();
// get client info
if( GetClientHandleInfo( &client_handle, &handle_info ) != HND_CLIENT ) {
error_respond( info, HTTP_PRECONDITION_FAILED, event );
HandleUnlock();
ixmlDocument_free( ChangedVars );
return;
}
// get subscription based on SID
if( ( subscription = GetClientSubActualSID( handle_info->ClientSubList,
&sid ) ) == NULL ) {
if( eventKey == 0 ) {
// wait until we've finished processing a subscription
// (if we are in the middle)
// this is to avoid mistakenly rejecting the first event if we
// receive it before the subscription response
HandleUnlock();
// try and get Subscription Lock
// (in case we are in the process of subscribing)
SubscribeLock();
// get HandleLock again
HandleLock();
if( GetClientHandleInfo( &client_handle, &handle_info )
!= HND_CLIENT ) {
error_respond( info, HTTP_PRECONDITION_FAILED, event );
SubscribeUnlock();
HandleUnlock();
ixmlDocument_free( ChangedVars );
return;
}
if( ( subscription =
GetClientSubActualSID( handle_info->ClientSubList,
&sid ) ) == NULL ) {
error_respond( info, HTTP_PRECONDITION_FAILED, event );
SubscribeUnlock();
HandleUnlock();
ixmlDocument_free( ChangedVars );
return;
}
SubscribeUnlock();
} else {
error_respond( info, HTTP_PRECONDITION_FAILED, event );
HandleUnlock();
ixmlDocument_free( ChangedVars );
return;
}
}
error_respond( info, HTTP_OK, event ); // success
// fill event struct
strcpy( event_struct.Sid, subscription->sid );
event_struct.EventKey = eventKey;
event_struct.ChangedVariables = ChangedVars;
// copy callback
callback = handle_info->Callback;
cookie = handle_info->Cookie;
HandleUnlock();
// make callback with event struct
// In future, should find a way of mainting
// that the handle is not unregistered in the middle of a
// callback
callback( UPNP_EVENT_RECEIVED, &event_struct, cookie );
ixmlDocument_free( ChangedVars );
}
#endif // INCLUDE_CLIENT_APIS
#endif // EXCLUDE_GENA