
git-svn-id: https://pupnp.svn.sourceforge.net/svnroot/pupnp/branches/branch-1.6.x@251 119443c7-1b9e-41f8-b6fc-b9c35fce742c
885 lines
28 KiB
C
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
|
|
|