1///////////////////////////////////////////////////////////////////////////
2//
3// Copyright (c) 2000-2003 Intel Corporation
4// All rights reserved.
5//
6// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions are met:
8//
9// * Redistributions of source code must retain the above copyright notice,
10// this list of conditions and the following disclaimer.
11// * Redistributions in binary form must reproduce the above copyright notice,
12// this list of conditions and the following disclaimer in the documentation
13// and/or other materials provided with the distribution.
14// * Neither name of Intel Corporation nor the names of its contributors
15// may be used to endorse or promote products derived from this software
16// without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
22// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
26// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29//
30///////////////////////////////////////////////////////////////////////////
31
32#include "config.h"
33#if EXCLUDE_GENA == 0
34#ifdef INCLUDE_CLIENT_APIS
35
36#include "gena.h"
37#include "sysdep.h"
38#include "uuid.h"
39#include "upnpapi.h"
40#include "parsetools.h"
41#include "statcodes.h"
42#include "httpparser.h"
43#include "httpreadwrite.h"
44
45extern ithread_mutex_t GlobalClientSubscribeMutex;
46
47/************************************************************************
48* Function : GenaAutoRenewSubscription
49*
50* Parameters:
51*	IN void *input: Thread data(upnp_timeout *) needed to send the renewal
52*
53* Description:
54*	This is a thread function to send the renewal just before the
55*	subscription times out.
56*
57* Returns: VOID
58*
59***************************************************************************/
60static void
61GenaAutoRenewSubscription( IN void *input )
62{
63    upnp_timeout *event = ( upnp_timeout * ) input;
64    void *cookie;
65    Upnp_FunPtr callback_fun;
66    struct Handle_Info *handle_info;
67    struct Upnp_Event_Subscribe *sub_struct =
68        ( struct Upnp_Event_Subscribe * )
69        event->Event;
70
71    int send_callback = 0;
72    int eventType = 0;
73
74    if( AUTO_RENEW_TIME == 0 ) {
75        DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__,
76                             "GENA SUB EXPIRED" ) );
77        sub_struct->ErrCode = UPNP_E_SUCCESS;
78        send_callback = 1;
79        eventType = UPNP_EVENT_SUBSCRIPTION_EXPIRED;
80    } else {
81        DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__,
82                             "GENA AUTO RENEW" ) );
83        if( ( ( sub_struct->ErrCode = genaRenewSubscription( event->handle,
84                                                             sub_struct->
85                                                             Sid,
86                                                             &sub_struct->
87                                                             TimeOut ) ) !=
88              UPNP_E_SUCCESS )
89            && ( sub_struct->ErrCode != GENA_E_BAD_SID )
90            && ( sub_struct->ErrCode != GENA_E_BAD_HANDLE ) ) {
91            send_callback = 1;
92            eventType = UPNP_EVENT_AUTORENEWAL_FAILED;
93        }
94    }
95    if( send_callback ) {
96        HandleLock(  );
97        if( GetHandleInfo( event->handle, &handle_info ) != HND_CLIENT ) {
98            HandleUnlock(  );
99            free_upnp_timeout( event );
100            return;
101        }
102        DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__,
103                             "HANDLE IS VALID" ) );
104        callback_fun = handle_info->Callback;
105        cookie = handle_info->Cookie;
106        HandleUnlock(  );
107        //make callback
108
109        callback_fun( eventType, event->Event, cookie );
110    }
111
112    free_upnp_timeout( event );
113}
114
115/************************************************************************
116* Function : ScheduleGenaAutoRenew
117*
118* Parameters:
119*	IN int client_handle: Handle that also contains the subscription list
120*	IN int TimeOut: The time out value of the subscription
121*	IN client_subscription * sub: Subscription being renewed
122*
123* Description:
124*	This function schedules a job to renew the subscription just before
125*	time out.
126*
127* Returns: int
128*	return GENA_E_SUCCESS if successful else returns appropriate error
129***************************************************************************/
130static int
131ScheduleGenaAutoRenew( IN int client_handle,
132                       IN int TimeOut,
133                       IN client_subscription * sub )
134{
135    struct Upnp_Event_Subscribe *RenewEventStruct = NULL;
136    upnp_timeout *RenewEvent = NULL;
137    int return_code = GENA_SUCCESS;
138    ThreadPoolJob job;
139
140    if( TimeOut == UPNP_INFINITE ) {
141        return GENA_SUCCESS;
142    }
143
144    RenewEventStruct = ( struct Upnp_Event_Subscribe * )malloc( sizeof
145                                                                ( struct
146                                                                  Upnp_Event_Subscribe ) );
147
148    if( RenewEventStruct == NULL ) {
149        return UPNP_E_OUTOF_MEMORY;
150    }
151
152    RenewEvent = ( upnp_timeout * ) malloc( sizeof( upnp_timeout ) );
153
154    if( RenewEvent == NULL ) {
155        free( RenewEventStruct );
156        return UPNP_E_OUTOF_MEMORY;
157    }
158    //schedule expire event
159    strcpy( RenewEventStruct->Sid, sub->sid );
160    RenewEventStruct->ErrCode = UPNP_E_SUCCESS;
161    strncpy( RenewEventStruct->PublisherUrl, sub->EventURL,
162             NAME_SIZE - 1 );
163    RenewEventStruct->TimeOut = TimeOut;
164
165    //RenewEvent->EventType=UPNP_EVENT_SUBSCRIPTION_EXPIRE;
166    RenewEvent->handle = client_handle;
167    RenewEvent->Event = RenewEventStruct;
168
169    TPJobInit( &job, ( start_routine ) GenaAutoRenewSubscription,
170               RenewEvent );
171    TPJobSetFreeFunction( &job, ( free_routine ) free_upnp_timeout );
172    TPJobSetPriority( &job, MED_PRIORITY );
173
174    //Schedule the job
175    if( ( return_code = TimerThreadSchedule( &gTimerThread,
176                                             TimeOut - AUTO_RENEW_TIME,
177                                             REL_SEC, &job, SHORT_TERM,
178                                             &( RenewEvent->
179                                                eventId ) ) ) !=
180        UPNP_E_SUCCESS ) {
181        free( RenewEvent );
182        free( RenewEventStruct );
183        return return_code;
184    }
185
186    sub->RenewEventId = RenewEvent->eventId;
187    return GENA_SUCCESS;
188}
189
190/************************************************************************
191* Function : gena_unsubscribe
192*
193* Parameters:
194*	IN char *url: Event URL of the service
195*	IN char *sid: The subcription ID.
196*	OUT http_parser_t* response: The UNSUBCRIBE response from the device
197*
198* Description:
199*	This function sends the UNSUBCRIBE gena request and recieves the
200*	response from the device and returns it as a parameter
201*
202* Returns: int
203*	return 0 if successful else returns appropriate error
204***************************************************************************/
205static int
206gena_unsubscribe( IN char *url,
207                  IN char *sid,
208                  OUT http_parser_t * response )
209{
210    int return_code;
211    uri_type dest_url;
212    membuffer request;
213
214    // parse url
215    return_code = http_FixStrUrl( url, strlen( url ), &dest_url );
216    if( return_code != 0 ) {
217        return return_code;
218    }
219    // make request msg
220    membuffer_init( &request );
221    request.size_inc = 30;
222    return_code = http_MakeMessage( &request, 1, 1,
223                                    "q" "ssc" "U" "c",
224                                    HTTPMETHOD_UNSUBSCRIBE, &dest_url,
225                                    "SID: ", sid );
226
227    //Not able to make the message so destroy the existing buffer
228    if( return_code != 0 ) {
229        membuffer_destroy( &request );
230        return return_code;
231    }
232    // send request and get reply
233    return_code = http_RequestAndResponse( &dest_url, request.buf,
234                                           request.length,
235                                           HTTPMETHOD_UNSUBSCRIBE,
236                                           HTTP_DEFAULT_TIMEOUT,
237                                           response );
238
239    membuffer_destroy( &request );
240
241    if( return_code != 0 )
242        httpmsg_destroy( &response->msg );
243
244    if( return_code == 0 && response->msg.status_code != HTTP_OK ) {
245        return_code = UPNP_E_UNSUBSCRIBE_UNACCEPTED;
246        httpmsg_destroy( &response->msg );
247    }
248
249    return return_code;
250}
251
252/************************************************************************
253* Function : gena_subscribe
254*
255* Parameters:
256*	IN char *url: url of service to subscribe
257*	INOUT int* timeout:subscription time desired (in secs)
258*	IN char* renewal_sid:for renewal, this contains a currently h
259*						 held subscription SID. For first time
260*						 subscription, this must be NULL
261*	OUT char** sid: SID returned by the subscription or renew msg
262*
263* Description:
264*	This function subscribes or renew subscription
265*
266* Returns: int
267*	return 0 if successful else returns appropriate error
268***************************************************************************/
269static int
270gena_subscribe( IN char *url,
271                INOUT int *timeout,
272                IN char *renewal_sid,
273                OUT char **sid )
274{
275    int return_code;
276    memptr sid_hdr,
277      timeout_hdr;
278    char timeout_str[25];
279    membuffer request;
280    uri_type dest_url;
281    http_parser_t response;
282
283    *sid = NULL;                // init
284
285    // request timeout to string
286    if( ( timeout == NULL ) ||
287        ( ( *timeout > 0 )
288          && ( *timeout < CP_MINIMUM_SUBSCRIPTION_TIME ) ) ) {
289        sprintf( timeout_str, "%d", CP_MINIMUM_SUBSCRIPTION_TIME );
290    } else if( *timeout >= 0 ) {
291        sprintf( timeout_str, "%d", *timeout );
292    } else {
293        strcpy( timeout_str, "infinite" );
294    }
295
296    // parse url
297    return_code = http_FixStrUrl( url, strlen( url ), &dest_url );
298    if( return_code != 0 ) {
299        return return_code;
300    }
301    // make request msg
302    membuffer_init( &request );
303    request.size_inc = 30;
304    if( renewal_sid ) {
305        // renew subscription
306        return_code = http_MakeMessage( &request, 1, 1,
307                                        "q" "ssc" "ssc" "c",
308                                        HTTPMETHOD_SUBSCRIBE, &dest_url,
309                                        "SID: ", renewal_sid,
310                                        "TIMEOUT: Second-", timeout_str );
311    } else {
312        // subscribe
313        return_code = http_MakeMessage( &request, 1, 1,
314                                        "q" "sssdsscc",
315                                        HTTPMETHOD_SUBSCRIBE, &dest_url,
316                                        "CALLBACK: <http://", LOCAL_HOST,
317                                        ":", LOCAL_PORT,
318                                        "/>\r\n" "NT: upnp:event\r\n"
319                                        "TIMEOUT: Second-", timeout_str );
320    }
321    if( return_code != 0 ) {
322        return return_code;
323    }
324    // send request and get reply
325    return_code = http_RequestAndResponse( &dest_url, request.buf,
326                                           request.length,
327                                           HTTPMETHOD_SUBSCRIBE,
328                                           HTTP_DEFAULT_TIMEOUT,
329                                           &response );
330
331    membuffer_destroy( &request );
332
333    if( return_code != 0 ) {
334        httpmsg_destroy( &response.msg );
335        return return_code;
336    }
337    if( response.msg.status_code != HTTP_OK ) {
338        httpmsg_destroy( &response.msg );
339        return UPNP_E_SUBSCRIBE_UNACCEPTED;
340    }
341    // get SID and TIMEOUT
342    if( httpmsg_find_hdr( &response.msg, HDR_SID, &sid_hdr ) == NULL ||
343        sid_hdr.length == 0 ||
344        httpmsg_find_hdr( &response.msg,
345                          HDR_TIMEOUT, &timeout_hdr ) == NULL ||
346        timeout_hdr.length == 0 ) {
347        httpmsg_destroy( &response.msg );
348        return UPNP_E_BAD_RESPONSE;
349    }
350    // save timeout
351    if( matchstr( timeout_hdr.buf, timeout_hdr.length, "%iSecond-%d%0",
352                  timeout ) == PARSE_OK ) {
353        // nothing
354    } else if( memptr_cmp_nocase( &timeout_hdr, "Second-infinite" ) == 0 ) {
355        *timeout = -1;
356    } else {
357        httpmsg_destroy( &response.msg );
358        return UPNP_E_BAD_RESPONSE;
359    }
360
361    // save SID
362    *sid = str_alloc( sid_hdr.buf, sid_hdr.length );
363    if( *sid == NULL ) {
364        httpmsg_destroy( &response.msg );
365        return UPNP_E_OUTOF_MEMORY;
366    }
367
368    httpmsg_destroy( &response.msg );
369    return UPNP_E_SUCCESS;
370}
371
372/************************************************************************
373* Function : genaUnregisterClient
374*
375* Parameters:
376*	IN UpnpClient_Handle client_handle: Handle containing all the control
377*			point related information
378*
379* Description:
380*	This function unsubcribes all the outstanding subscriptions and cleans
381*	the subscription list. This function is called when control point
382*	unregisters.
383*
384* Returns: int
385*	return UPNP_E_SUCCESS if successful else returns appropriate error
386***************************************************************************/
387int
388genaUnregisterClient( IN UpnpClient_Handle client_handle )
389{
390    client_subscription sub_copy;
391    int return_code = UPNP_E_SUCCESS;
392    struct Handle_Info *handle_info = NULL;
393    http_parser_t response;
394
395    while( TRUE ) {
396        HandleLock(  );
397        if( GetHandleInfo( client_handle, &handle_info ) != HND_CLIENT ) {
398            HandleUnlock(  );
399            return GENA_E_BAD_HANDLE;
400        }
401
402        if( handle_info->ClientSubList == NULL ) {
403            return_code = UPNP_E_SUCCESS;
404            break;
405        }
406
407        return_code = copy_client_subscription( handle_info->ClientSubList,
408                                                &sub_copy );
409        if( return_code != HTTP_SUCCESS ) {
410            break;
411        }
412
413        RemoveClientSubClientSID( &handle_info->ClientSubList,
414                                  sub_copy.sid );
415
416        HandleUnlock(  );
417
418        return_code = gena_unsubscribe( sub_copy.EventURL,
419                                        sub_copy.ActualSID, &response );
420        if( return_code == 0 ) {
421            httpmsg_destroy( &response.msg );
422        }
423
424        free_client_subscription( &sub_copy );
425    }
426
427    freeClientSubList( handle_info->ClientSubList );
428    HandleUnlock(  );
429    return return_code;
430}
431
432/************************************************************************
433* Function : genaUnSubscribe
434*
435* Parameters:
436*	IN UpnpClient_Handle client_handle: UPnP client handle
437*	IN SID in_sid: The subscription ID
438*
439* Description:
440*	This function unsubscribes a SID. It first validates the SID and
441*	client_handle,copies the subscription, sends UNSUBSCRIBE http request
442*	to service processes request and finally removes the subscription
443*
444* Returns: int
445*	return UPNP_E_SUCCESS if service response is OK else
446*	returns appropriate error
447***************************************************************************/
448int
449genaUnSubscribe( IN UpnpClient_Handle client_handle,
450                 IN const Upnp_SID in_sid )
451{
452    client_subscription *sub;
453    int return_code = GENA_SUCCESS;
454    struct Handle_Info *handle_info;
455    client_subscription sub_copy;
456    http_parser_t response;
457
458    HandleLock(  );
459
460    // validate handle and sid
461
462    if( GetHandleInfo( client_handle, &handle_info ) != HND_CLIENT ) {
463        HandleUnlock(  );
464        return GENA_E_BAD_HANDLE;
465    }
466
467    if( ( sub =
468          GetClientSubClientSID( handle_info->ClientSubList, in_sid ) )
469        == NULL ) {
470        HandleUnlock(  );
471        return GENA_E_BAD_SID;
472    }
473
474    return_code = copy_client_subscription( sub, &sub_copy );
475
476    HandleUnlock(  );
477
478    return_code = gena_unsubscribe( sub_copy.EventURL, sub_copy.ActualSID,
479                                    &response );
480
481    if( return_code == 0 ) {
482        httpmsg_destroy( &response.msg );
483    }
484
485    free_client_subscription( &sub_copy );
486
487    HandleLock(  );
488
489    if( GetHandleInfo( client_handle, &handle_info ) != HND_CLIENT ) {
490        HandleUnlock(  );
491        return GENA_E_BAD_HANDLE;
492    }
493
494    RemoveClientSubClientSID( &handle_info->ClientSubList, in_sid );
495
496    HandleUnlock(  );
497
498    return return_code;
499}
500
501/************************************************************************
502* Function : genaSubscribe
503*
504* Parameters:
505*	IN UpnpClient_Handle client_handle:
506*	IN char * PublisherURL: NULL Terminated, of the form :
507*						"http://134.134.156.80:4000/RedBulb/Event"
508*	INOUT int * TimeOut: requested Duration, if -1, then "infinite".
509*						in the OUT case: actual Duration granted
510*						by Service, -1 for infinite
511*	OUT Upnp_SID out_sid:sid of subscription, memory passed in by caller
512*
513* Description:
514*	This function subscribes to a PublisherURL ( also mentioned as EventURL
515*	some places). It sends SUBSCRIBE http request to service processes
516*	request. Finally adds a Subscription to
517*	the clients subscription list, if service responds with OK
518*
519* Returns: int
520*	return UPNP_E_SUCCESS if service response is OK else
521*	returns appropriate error
522***************************************************************************/
523int
524genaSubscribe( IN UpnpClient_Handle client_handle,
525               IN char *PublisherURL,
526               INOUT int *TimeOut,
527               OUT Upnp_SID out_sid )
528{
529    int return_code = GENA_SUCCESS;
530    client_subscription *newSubscription = NULL;
531    uuid_upnp uid;
532    Upnp_SID temp_sid;
533    char *ActualSID = NULL;
534    struct Handle_Info *handle_info;
535    char *EventURL = NULL;
536
537    DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__,
538                         "GENA SUBSCRIBE BEGIN" ) );
539    HandleLock(  );
540
541    memset( out_sid, 0, sizeof( Upnp_SID ) );
542
543    // validate handle
544    if( GetHandleInfo( client_handle, &handle_info ) != HND_CLIENT ) {
545        HandleUnlock(  );
546        return GENA_E_BAD_HANDLE;
547    }
548    HandleUnlock(  );
549
550    // subscribe
551    SubscribeLock(  );
552    return_code =
553        gena_subscribe( PublisherURL, TimeOut, NULL, &ActualSID );
554    HandleLock(  );
555    if( return_code != UPNP_E_SUCCESS ) {
556        DBGONLY( UpnpPrintf( UPNP_CRITICAL, GENA, __FILE__, __LINE__,
557                             "SUBSCRIBE FAILED in transfer error code: %d returned\n",
558                             return_code ) );
559        goto error_handler;
560    }
561
562    if( GetHandleInfo( client_handle, &handle_info ) != HND_CLIENT ) {
563        return_code = GENA_E_BAD_HANDLE;
564        goto error_handler;
565    }
566    // generate client SID
567    uuid_create( &uid );
568    uuid_unpack( &uid, temp_sid );
569    sprintf( out_sid, "uuid:%s", temp_sid );
570
571    // create event url
572    EventURL = ( char * )malloc( strlen( PublisherURL ) + 1 );
573    if( EventURL == NULL ) {
574        return_code = UPNP_E_OUTOF_MEMORY;
575        goto error_handler;
576    }
577
578    strcpy( EventURL, PublisherURL );
579
580    // fill subscription
581    newSubscription =
582        ( client_subscription * ) malloc( sizeof( client_subscription ) );
583    if( newSubscription == NULL ) {
584        return_code = UPNP_E_OUTOF_MEMORY;
585        goto error_handler;
586    }
587    newSubscription->EventURL = EventURL;
588    newSubscription->ActualSID = ActualSID;
589    strcpy( newSubscription->sid, out_sid );
590    newSubscription->RenewEventId = -1;
591    newSubscription->next = handle_info->ClientSubList;
592    handle_info->ClientSubList = newSubscription;
593
594    // schedule expiration event
595    return_code = ScheduleGenaAutoRenew( client_handle, *TimeOut,
596                                         newSubscription );
597
598  error_handler:
599    if( return_code != UPNP_E_SUCCESS ) {
600        free( ActualSID );
601        free( EventURL );
602        free( newSubscription );
603    }
604    HandleUnlock(  );
605    SubscribeUnlock(  );
606    return return_code;
607}
608
609/************************************************************************
610* Function : genaRenewSubscription
611*
612* Parameters:
613*	IN UpnpClient_Handle client_handle: Client handle
614*	IN const Upnp_SID in_sid: subscription ID
615*	INOUT int * TimeOut: requested Duration, if -1, then "infinite".
616*						in the OUT case: actual Duration granted
617*						by Service, -1 for infinite
618*
619* Description:
620*	This function renews a SID. It first validates the SID and
621*	client_handle and copies the subscription. It sends RENEW
622*	(modified SUBSCRIBE) http request to service and processes
623*	the response.
624*
625* Returns: int
626*	return UPNP_E_SUCCESS if service response is OK else
627*	returns appropriate error
628***************************************************************************/
629int
630genaRenewSubscription( IN UpnpClient_Handle client_handle,
631                       IN const Upnp_SID in_sid,
632                       INOUT int *TimeOut )
633{
634    int return_code = GENA_SUCCESS;
635    client_subscription *sub;
636    client_subscription sub_copy;
637    struct Handle_Info *handle_info;
638
639    char *ActualSID;
640    ThreadPoolJob tempJob;
641
642    HandleLock(  );
643
644    // validate handle and sid
645    if( GetHandleInfo( client_handle, &handle_info ) != HND_CLIENT ) {
646        HandleUnlock(  );
647        return GENA_E_BAD_HANDLE;
648    }
649
650    if( ( sub = GetClientSubClientSID( handle_info->ClientSubList,
651                                       in_sid ) ) == NULL ) {
652        HandleUnlock(  );
653        return GENA_E_BAD_SID;
654    }
655    // remove old events
656    if( TimerThreadRemove( &gTimerThread, sub->RenewEventId, &tempJob ) ==
657        0 ) {
658
659        free_upnp_timeout( ( upnp_timeout * ) tempJob.arg );
660    }
661
662    DBGONLY( UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__,
663                         "REMOVED AUTO RENEW  EVENT" ) );
664
665    sub->RenewEventId = -1;
666    return_code = copy_client_subscription( sub, &sub_copy );
667
668    HandleUnlock(  );
669
670    if( return_code != HTTP_SUCCESS ) {
671        return return_code;
672    }
673
674    return_code = gena_subscribe( sub_copy.EventURL, TimeOut,
675                                  sub_copy.ActualSID, &ActualSID );
676    HandleLock(  );
677
678    if( GetHandleInfo( client_handle, &handle_info ) != HND_CLIENT ) {
679        HandleUnlock(  );
680        if( return_code == UPNP_E_SUCCESS ) {
681        	if(ActualSID)
682            free( ActualSID );
683        }
684        return GENA_E_BAD_HANDLE;
685    }
686    // we just called GetHandleInfo, so we don't check for return value
687    //GetHandleInfo(client_handle, &handle_info);
688
689    if( return_code != UPNP_E_SUCCESS ) {
690        // network failure (remove client sub)
691        RemoveClientSubClientSID( &handle_info->ClientSubList, in_sid );
692        free_client_subscription( &sub_copy );
693        HandleUnlock(  );
694        return return_code;
695    }
696    // get subscription
697    if( ( sub = GetClientSubClientSID( handle_info->ClientSubList,
698                                       in_sid ) ) == NULL ) {
699		if(ActualSID)
700        free( ActualSID );
701        free_client_subscription( &sub_copy );
702        HandleUnlock(  );
703        return GENA_E_BAD_SID;
704    }
705    // store actual sid
706    if(sub->ActualSID)
707    free( sub->ActualSID );
708    sub->ActualSID = ActualSID;
709
710    // start renew subscription timer
711    return_code = ScheduleGenaAutoRenew( client_handle, *TimeOut, sub );
712    if( return_code != GENA_SUCCESS ) {
713        RemoveClientSubClientSID( &handle_info->ClientSubList, sub->sid );
714    }
715    free_client_subscription( &sub_copy );
716    HandleUnlock(  );
717    return return_code;
718}
719
720/************************************************************************
721* Function : gena_process_notification_event
722*
723* Parameters:
724*	IN SOCKINFO *info: Socket structure containing the device socket
725*					information
726*	IN http_message_t* event: The http message contains the GENA
727*								notification
728*
729* Description:
730*	This function processes NOTIFY events that are sent by devices.
731*	called by genacallback()
732*
733* Returns: void
734*
735* Note : called by genacallback()
736****************************************************************************/
737void
738gena_process_notification_event( IN SOCKINFO * info,
739                                 IN http_message_t * event )
740{
741    struct Upnp_Event event_struct;
742    int eventKey;
743    token sid;
744    client_subscription *subscription_1;
745    IXML_Document *ChangedVars;
746    struct Handle_Info *handle_info;
747    void *cookie;
748    Upnp_FunPtr callback;
749    UpnpClient_Handle client_handle;
750
751    memptr sid_hdr;
752    memptr nt_hdr,
753      nts_hdr;
754    memptr seq_hdr;
755
756    // get SID
757    if( httpmsg_find_hdr( event, HDR_SID, &sid_hdr ) == NULL ) {
758        error_respond( info, HTTP_PRECONDITION_FAILED, event );
759
760        return;
761    }
762    sid.buff = sid_hdr.buf;
763    sid.size = sid_hdr.length;
764
765    // get event key
766    if( httpmsg_find_hdr( event, HDR_SEQ, &seq_hdr ) == NULL ||
767        matchstr( seq_hdr.buf, seq_hdr.length, "%d%0", &eventKey )
768        != PARSE_OK ) {
769        error_respond( info, HTTP_BAD_REQUEST, event );
770
771        return;
772    }
773    // get NT and NTS headers
774    if( httpmsg_find_hdr( event, HDR_NT, &nt_hdr ) == NULL ||
775        httpmsg_find_hdr( event, HDR_NTS, &nts_hdr ) == NULL ) {
776        error_respond( info, HTTP_BAD_REQUEST, event );
777
778        return;
779    }
780    // verify NT and NTS headers
781    if( memptr_cmp( &nt_hdr, "upnp:event" ) != 0 ||
782        memptr_cmp( &nts_hdr, "upnp:propchange" ) != 0 ) {
783        error_respond( info, HTTP_PRECONDITION_FAILED, event );
784
785        return;
786    }
787    // parse the content (should be XML)
788    if( !has_xml_content_type( event ) ||
789        event->msg.length == 0 ||
790        ( ixmlParseBufferEx( event->entity.buf, &ChangedVars ) ) !=
791        IXML_SUCCESS ) {
792        error_respond( info, HTTP_BAD_REQUEST, event );
793
794        return;
795    }
796
797    HandleLock(  );
798
799    // get client info
800    if( GetClientHandleInfo( &client_handle, &handle_info ) != HND_CLIENT ) {
801        error_respond( info, HTTP_PRECONDITION_FAILED, event );
802        HandleUnlock(  );
803        ixmlDocument_free( ChangedVars );
804
805        return;
806    }
807    // get subscription based on SID
808    if( ( subscription_1 = GetClientSubActualSID( handle_info->ClientSubList,
809                                                &sid ) ) == NULL ) {
810        if( eventKey == 0 ) {
811            // wait until we've finished processing a subscription
812            //   (if we are in the middle)
813            // this is to avoid mistakenly rejecting the first event if we
814            //   receive it before the subscription response
815            HandleUnlock(  );
816
817            // try and get Subscription Lock
818            //   (in case we are in the process of subscribing)
819            SubscribeLock(  );
820
821            // get HandleLock again
822            HandleLock(  );
823
824            if( GetClientHandleInfo( &client_handle, &handle_info )
825                != HND_CLIENT ) {
826                error_respond( info, HTTP_PRECONDITION_FAILED, event );
827                SubscribeUnlock(  );
828                HandleUnlock(  );
829                ixmlDocument_free( ChangedVars );
830
831                return;
832            }
833
834            if( ( subscription_1 =
835                  GetClientSubActualSID( handle_info->ClientSubList,
836                                         &sid ) ) == NULL ) {
837                error_respond( info, HTTP_PRECONDITION_FAILED, event );
838                SubscribeUnlock(  );
839                HandleUnlock(  );
840                ixmlDocument_free( ChangedVars );
841
842                return;
843            }
844
845            SubscribeUnlock(  );
846        } else {
847            error_respond( info, HTTP_PRECONDITION_FAILED, event );
848            HandleUnlock(  );
849            ixmlDocument_free( ChangedVars );
850
851            return;
852        }
853    }
854
855    error_respond( info, HTTP_OK, event );  // success
856
857    // fill event struct
858    strcpy( event_struct.Sid, subscription_1->sid );
859    event_struct.EventKey = eventKey;
860    event_struct.ChangedVariables = ChangedVars;
861
862    // copy callback
863    callback = handle_info->Callback;
864    cookie = handle_info->Cookie;
865
866    HandleUnlock(  );
867
868    // make callback with event struct
869    // In future, should find a way of mainting
870    // that the handle is not unregistered in the middle of a
871    // callback
872    callback( UPNP_EVENT_RECEIVED, &event_struct, cookie );
873
874    ixmlDocument_free( ChangedVars );
875}
876
877#endif // INCLUDE_CLIENT_APIS
878#endif // EXCLUDE_GENA
879