pupnp (libupnp) snapshot from SourceForge: git clone git://pupnp.git.sourceforge...
[igd2-for-linux:pandonghui1211s-igd2-for-linux.git] / pupnp_branch-1.6.x / upnp / src / gena / gena_ctrlpt.c
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
33 #include "config.h"
34
35
36 /*!
37  * \file
38  */
39
40
41 #if EXCLUDE_GENA == 0
42 #ifdef INCLUDE_CLIENT_APIS
43
44
45 #include "gena.h"
46 #include "httpparser.h"
47 #include "httpreadwrite.h"
48 #include "parsetools.h"
49 #include "statcodes.h"
50 #include "sysdep.h"
51 #include "uuid.h"
52 #include "upnpapi.h"
53
54
55 extern ithread_mutex_t GlobalClientSubscribeMutex;
56
57
58 /*!
59  * \brief This is a thread function to send the renewal just before the
60  * subscription times out.
61  */
62 static void GenaAutoRenewSubscription(
63         /*! [in] Thread data(upnp_timeout *) needed to send the renewal. */
64         IN void *input)
65 {
66         upnp_timeout *event = (upnp_timeout *) input;
67         struct Upnp_Event_Subscribe *sub_struct = (struct Upnp_Event_Subscribe *)event->Event;
68         void *cookie;
69         Upnp_FunPtr callback_fun;
70         struct Handle_Info *handle_info;
71         int send_callback = 0;
72         int eventType = 0;
73         int timeout = 0;
74         int errCode = 0;
75
76         if (AUTO_RENEW_TIME == 0) {
77                 UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__, "GENA SUB EXPIRED");
78                 sub_struct->ErrCode = UPNP_E_SUCCESS;
79                 send_callback = 1;
80                 eventType = UPNP_EVENT_SUBSCRIPTION_EXPIRED;
81         } else {
82                 UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__, "GENA AUTO RENEW");
83                 errCode = genaRenewSubscription(
84                         event->handle,
85                         sub_struct->Sid,
86                         &timeout);
87                 sub_struct->ErrCode = errCode;
88                 sub_struct->TimeOut = timeout;
89                 if (errCode != UPNP_E_SUCCESS &&
90                     errCode != GENA_E_BAD_SID &&
91                     errCode != GENA_E_BAD_HANDLE) {
92                         send_callback = 1;
93                         eventType = UPNP_EVENT_AUTORENEWAL_FAILED;
94                 }
95         }
96
97         if (send_callback) {
98                 HandleReadLock();
99                 if( GetHandleInfo( event->handle, &handle_info ) != HND_CLIENT ) {
100                         HandleUnlock();
101                         free_upnp_timeout(event);
102
103                         return;
104                 }
105                 UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__, "HANDLE IS VALID");
106
107                 // make callback
108                 callback_fun = handle_info->Callback;
109                 cookie = handle_info->Cookie;
110                 HandleUnlock();
111                 callback_fun(eventType, event->Event, cookie);
112         }
113
114         free_upnp_timeout(event);
115 }
116
117
118 /*!
119  * \brief Schedules a job to renew the subscription just before time out.
120  *
121  * \return GENA_E_SUCCESS if successful, otherwise returns the appropriate
122  *      error code.
123  */
124 static int ScheduleGenaAutoRenew(
125         /*! [in] Handle that also contains the subscription list. */
126         IN int client_handle,
127         /*! [in] The time out value of the subscription. */
128         IN int TimeOut,
129         /*! [in] Subscription being renewed. */
130         IN client_subscription *sub)
131 {
132         struct Upnp_Event_Subscribe *RenewEventStruct = NULL;
133         upnp_timeout *RenewEvent = NULL;
134         int return_code = GENA_SUCCESS;
135         ThreadPoolJob job;
136
137         if( TimeOut == UPNP_INFINITE ) {
138                 return GENA_SUCCESS;
139         }
140
141         RenewEventStruct = (struct Upnp_Event_Subscribe *)malloc(sizeof (struct Upnp_Event_Subscribe));
142         if( RenewEventStruct == NULL ) {
143                 return UPNP_E_OUTOF_MEMORY;
144         }
145
146         RenewEvent = (upnp_timeout *) malloc(sizeof(upnp_timeout));
147         if( RenewEvent == NULL ) {
148                 free( RenewEventStruct );
149                 return UPNP_E_OUTOF_MEMORY;
150         }
151
152         // schedule expire event
153         strcpy( RenewEventStruct->Sid, sub->sid );
154         RenewEventStruct->ErrCode = UPNP_E_SUCCESS;
155         strncpy( RenewEventStruct->PublisherUrl, sub->EventURL, NAME_SIZE - 1 );
156         RenewEventStruct->TimeOut = TimeOut;
157
158         // RenewEvent->EventType=UPNP_EVENT_SUBSCRIPTION_EXPIRE;
159         RenewEvent->handle = client_handle;
160         RenewEvent->Event = RenewEventStruct;
161
162         TPJobInit(&job, (start_routine) GenaAutoRenewSubscription, RenewEvent);
163         TPJobSetFreeFunction(&job, (free_routine)free_upnp_timeout);
164         TPJobSetPriority(&job, MED_PRIORITY);
165
166         // Schedule the job
167         return_code = TimerThreadSchedule(
168                 &gTimerThread,
169                 TimeOut - AUTO_RENEW_TIME,
170                 REL_SEC,
171                 &job, SHORT_TERM,
172                 &(RenewEvent->eventId));
173         if (return_code != UPNP_E_SUCCESS) {
174                 free(RenewEvent);
175                 free(RenewEventStruct);
176
177                 return return_code;
178         }
179
180         sub->RenewEventId = RenewEvent->eventId;
181
182         return GENA_SUCCESS;
183 }
184
185
186 /*!
187  * \brief Sends the UNSUBCRIBE gena request and recieves the response from the
188  *      device and returns it as a parameter.
189  *
190  * \returns 0 if successful, otherwise returns the appropriate error code.
191  */
192 static int gena_unsubscribe(
193         /*! [in] Event URL of the service. */
194         IN const char *url,
195         /*! [in] The subcription ID. */
196         IN const char *sid,
197         /*! [out] The UNSUBCRIBE response from the device. */
198         OUT http_parser_t *response )
199 {
200         int return_code;
201         uri_type dest_url;
202         membuffer request;
203
204         // parse url
205         return_code = http_FixStrUrl(url, strlen(url), &dest_url);
206         if (return_code != 0) {
207                 return return_code;
208         }
209
210         // make request msg
211         membuffer_init(&request);
212         request.size_inc = 30;
213         return_code = http_MakeMessage(
214                 &request, 1, 1,
215                 "q" "ssc" "Uc",
216                 HTTPMETHOD_UNSUBSCRIBE, &dest_url,
217                 "SID: ", sid);
218
219         // Not able to make the message so destroy the existing buffer
220         if (return_code != 0) {
221                 membuffer_destroy(&request);
222
223                 return return_code;
224         }
225
226         // send request and get reply
227         return_code = http_RequestAndResponse(
228                 &dest_url, request.buf, request.length,
229                 HTTPMETHOD_UNSUBSCRIBE, HTTP_DEFAULT_TIMEOUT, response);
230         membuffer_destroy(&request);
231         if (return_code != 0) {
232                 httpmsg_destroy(&response->msg);
233         }
234
235         if (return_code == 0 && response->msg.status_code != HTTP_OK) {
236                 return_code = UPNP_E_UNSUBSCRIBE_UNACCEPTED;
237                 httpmsg_destroy(&response->msg);
238         }
239
240         return return_code;
241 }
242
243
244 /*!
245  * \brief Subscribes or renew subscription.
246  *
247  * \return 0 if successful, otherwise returns the appropriate error code.
248  */
249 static int gena_subscribe(
250         /*! [in] URL of service to subscribe. */
251         IN const char *url,
252         /*! [in,out] Subscription time desired (in secs). */
253         INOUT int *timeout,
254         /*! [in] for renewal, this contains a currently held subscription SID.
255          * For first time subscription, this must be NULL. */
256         IN const char *renewal_sid,
257         /*! [out] SID returned by the subscription or renew msg. */
258         OUT char **sid)
259 {
260         int return_code;
261         int parse_ret = 0;
262         int local_timeout = CP_MINIMUM_SUBSCRIPTION_TIME;
263         memptr sid_hdr;
264         memptr timeout_hdr;
265         char timeout_str[25];
266         membuffer request;
267         uri_type dest_url;
268         http_parser_t response;
269
270         *sid = NULL; // init
271
272         // request timeout to string
273         if (timeout == NULL) {
274                 timeout = &local_timeout;
275         }
276         if (*timeout < 0) {
277                 strcpy(timeout_str, "infinite");
278         } else if(*timeout < CP_MINIMUM_SUBSCRIPTION_TIME) {
279                 sprintf(timeout_str, "%d", CP_MINIMUM_SUBSCRIPTION_TIME);
280         } else {
281                 sprintf(timeout_str, "%d", *timeout);
282         }
283
284         // parse url
285         return_code = http_FixStrUrl( url, strlen( url ), &dest_url );
286         if (return_code != 0) {
287                 return return_code;
288         }
289
290         // make request msg
291         membuffer_init(&request);
292         request.size_inc = 30;
293         if (renewal_sid) {
294                 // renew subscription
295                 return_code = http_MakeMessage(
296                         &request, 1, 1,
297                         "q" "ssc" "sscc",
298                         HTTPMETHOD_SUBSCRIBE, &dest_url,
299                         "SID: ", renewal_sid,
300                         "TIMEOUT: Second-", timeout_str );
301         } else {
302                 // subscribe
303                         return_code = http_MakeMessage(
304                                 &request, 1, 1,
305                                 "q" "sssdsc" "sc" "sscc",
306                                 HTTPMETHOD_SUBSCRIBE, &dest_url,
307                         "CALLBACK: <http://", LOCAL_HOST, ":", LOCAL_PORT, "/>",
308                                 "NT: upnp:event",
309                                 "TIMEOUT: Second-", timeout_str);
310                 }
311         if (return_code != 0) {
312                 return return_code;
313         }
314
315         // send request and get reply
316         return_code = http_RequestAndResponse(&dest_url, request.buf,
317                 request.length,
318                 HTTPMETHOD_SUBSCRIBE,
319                 HTTP_DEFAULT_TIMEOUT,
320                 &response);
321         membuffer_destroy(&request);
322
323         if (return_code != 0) {
324                 httpmsg_destroy(&response.msg);
325
326                 return return_code;
327         }
328         if (response.msg.status_code != HTTP_OK) {
329                 httpmsg_destroy(&response.msg);
330
331                 return UPNP_E_SUBSCRIBE_UNACCEPTED;
332         }
333
334         // get SID and TIMEOUT
335         if (httpmsg_find_hdr(&response.msg, HDR_SID, &sid_hdr) == NULL ||
336             sid_hdr.length == 0 ||
337             httpmsg_find_hdr( &response.msg, HDR_TIMEOUT, &timeout_hdr ) == NULL ||
338             timeout_hdr.length == 0 ) {
339                 httpmsg_destroy( &response.msg );
340
341                 return UPNP_E_BAD_RESPONSE;
342         }
343
344         // save timeout
345         parse_ret = matchstr(timeout_hdr.buf, timeout_hdr.length, "%iSecond-%d%0", timeout);
346         if (parse_ret == PARSE_OK) {
347                 // nothing to do
348         } else if (memptr_cmp_nocase(&timeout_hdr, "Second-infinite") == 0) {
349                 *timeout = -1;
350         } else {
351                 httpmsg_destroy( &response.msg );
352
353                 return UPNP_E_BAD_RESPONSE;
354         }
355
356         // save SID
357         *sid = str_alloc( sid_hdr.buf, sid_hdr.length );
358         if( *sid == NULL ) {
359                 httpmsg_destroy(&response.msg);
360
361                 return UPNP_E_OUTOF_MEMORY;
362         }
363         httpmsg_destroy(&response.msg);
364
365         return UPNP_E_SUCCESS;
366 }
367
368
369 int genaUnregisterClient(UpnpClient_Handle client_handle)
370 {
371         client_subscription sub_copy;
372         int return_code = UPNP_E_SUCCESS;
373         struct Handle_Info *handle_info = NULL;
374         http_parser_t response;
375
376         while (TRUE) {
377                 HandleLock();
378
379                 if (GetHandleInfo(client_handle, &handle_info) != HND_CLIENT) {
380                         HandleUnlock();
381                         return_code = GENA_E_BAD_HANDLE;
382                         goto exit_function;
383                 }
384                 if (handle_info->ClientSubList == NULL) {
385                         return_code = UPNP_E_SUCCESS;
386                         break;
387                 }
388                 return_code = copy_client_subscription( handle_info->ClientSubList, &sub_copy);
389                 RemoveClientSubClientSID(&handle_info->ClientSubList, sub_copy.sid);
390
391                 HandleUnlock();
392
393                 return_code = gena_unsubscribe(
394                         sub_copy.EventURL,
395                         sub_copy.ActualSID,
396                         &response);
397                 if (return_code == 0) {
398                         httpmsg_destroy(&response.msg);
399                 }
400                 free_client_subscription(&sub_copy);
401         }
402
403         freeClientSubList(handle_info->ClientSubList);
404         HandleUnlock();
405
406 exit_function:
407         return return_code;
408 }
409
410
411 #ifdef INCLUDE_CLIENT_APIS
412 int genaUnSubscribe(
413         UpnpClient_Handle client_handle,
414         const Upnp_SID in_sid)
415 {
416         client_subscription *sub = NULL;
417         int return_code = GENA_SUCCESS;
418         struct Handle_Info *handle_info;
419         client_subscription sub_copy;
420         http_parser_t response;
421
422         // validate handle and sid
423         HandleLock();
424         if (GetHandleInfo(client_handle, &handle_info) != HND_CLIENT) {
425                 HandleUnlock();
426                 return_code = GENA_E_BAD_HANDLE;
427                 goto exit_function;
428         }
429         sub = GetClientSubClientSID(handle_info->ClientSubList, in_sid);
430         if (sub == NULL) {
431                 HandleUnlock();
432                 return_code = GENA_E_BAD_SID;
433                 goto exit_function;
434         }
435         return_code = copy_client_subscription( sub, &sub_copy );
436         HandleUnlock();
437
438         return_code = gena_unsubscribe(
439                 sub_copy.EventURL,
440                 sub_copy.ActualSID,
441                 &response);
442         if (return_code == 0) {
443                 httpmsg_destroy(&response.msg);
444         }
445         free_client_subscription(&sub_copy);
446
447         HandleLock();
448         if (GetHandleInfo(client_handle, &handle_info) != HND_CLIENT) {
449                 HandleUnlock();
450                 return_code = GENA_E_BAD_HANDLE;
451                 goto exit_function;
452         }
453         RemoveClientSubClientSID(&handle_info->ClientSubList, in_sid);
454         HandleUnlock();
455
456 exit_function:
457         return return_code;
458 }
459 #endif /* INCLUDE_CLIENT_APIS */
460
461
462 #ifdef INCLUDE_CLIENT_APIS
463 int genaSubscribe(
464         UpnpClient_Handle client_handle,
465         const char *PublisherURL,
466         int *TimeOut,
467         Upnp_SID out_sid)
468 {
469         int return_code = GENA_SUCCESS;
470         client_subscription *newSubscription = NULL;
471         uuid_upnp uid;
472         Upnp_SID temp_sid;
473         char *ActualSID = NULL;
474         struct Handle_Info *handle_info;
475         char *EventURL = NULL;
476
477         UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__, "GENA SUBSCRIBE BEGIN");
478
479         memset( out_sid, 0, sizeof( Upnp_SID ) );
480
481         HandleReadLock();
482         // validate handle
483         if (GetHandleInfo(client_handle, &handle_info) != HND_CLIENT) {
484                 HandleUnlock();
485
486                 return GENA_E_BAD_HANDLE;
487         }
488         HandleUnlock();
489
490         // subscribe
491         SubscribeLock();
492         return_code = gena_subscribe(PublisherURL, TimeOut, NULL, &ActualSID);
493         HandleLock();
494         if (return_code != UPNP_E_SUCCESS) {
495                 UpnpPrintf( UPNP_CRITICAL, GENA, __FILE__, __LINE__,
496                         "SUBSCRIBE FAILED in transfer error code: %d returned\n",
497                         return_code );
498                 goto error_handler;
499         }
500
501         if(GetHandleInfo(client_handle, &handle_info) != HND_CLIENT) {
502                 return_code = GENA_E_BAD_HANDLE;
503                 goto error_handler;
504         }
505
506         // generate client SID
507         uuid_create(&uid );
508         uuid_unpack(&uid, temp_sid);
509         sprintf( out_sid, "uuid:%s", temp_sid );
510
511         // create event url
512         EventURL = ( char * )malloc( strlen( PublisherURL ) + 1 );
513         if( EventURL == NULL ) {
514                 return_code = UPNP_E_OUTOF_MEMORY;
515                 goto error_handler;
516         }
517         strcpy( EventURL, PublisherURL );
518
519         // fill subscription
520         newSubscription = (client_subscription *)malloc(sizeof (client_subscription));
521         if (newSubscription == NULL) {
522                 return_code = UPNP_E_OUTOF_MEMORY;
523                 goto error_handler;
524         }
525         newSubscription->EventURL = EventURL;
526         newSubscription->ActualSID = ActualSID;
527         strcpy(newSubscription->sid, out_sid);
528         newSubscription->RenewEventId = -1;
529         newSubscription->next = handle_info->ClientSubList;
530         handle_info->ClientSubList = newSubscription;
531
532         // schedule expiration event
533         return_code = ScheduleGenaAutoRenew(client_handle, *TimeOut, newSubscription);
534
535 error_handler:
536         if (return_code != UPNP_E_SUCCESS) {
537                 free(ActualSID);
538                 free(EventURL);
539                 free(newSubscription);
540         }
541         HandleUnlock();
542         SubscribeUnlock();
543
544         return return_code;
545 }
546 #endif /* INCLUDE_CLIENT_APIS */
547
548
549 int genaRenewSubscription(
550         UpnpClient_Handle client_handle,
551         const char *in_sid,
552         int *TimeOut)
553 {
554         int return_code = GENA_SUCCESS;
555         client_subscription *sub = NULL;
556         client_subscription sub_copy;
557         struct Handle_Info *handle_info;
558         char *ActualSID;
559         ThreadPoolJob tempJob;
560
561         HandleLock();
562
563         // validate handle and sid
564         if (GetHandleInfo(client_handle, &handle_info) != HND_CLIENT) {
565                 HandleUnlock();
566
567                 return_code = GENA_E_BAD_HANDLE;
568                 goto exit_function;
569         }
570
571         sub = GetClientSubClientSID(handle_info->ClientSubList, in_sid);
572         if (sub == NULL) {
573                 HandleUnlock();
574
575                 return_code = GENA_E_BAD_SID;
576                 goto exit_function;
577         }
578
579         // remove old events
580         if (TimerThreadRemove(
581                 &gTimerThread,
582                 sub->RenewEventId,
583                 &tempJob) == 0 ) {
584                 free_upnp_timeout((upnp_timeout *)tempJob.arg);
585         }
586
587         UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__, "REMOVED AUTO RENEW  EVENT");
588
589         sub->RenewEventId = -1;
590         return_code = copy_client_subscription(sub, &sub_copy);
591
592         HandleUnlock();
593
594         if( return_code != HTTP_SUCCESS ) {
595                 return return_code;
596         }
597
598         return_code = gena_subscribe(
599                 sub_copy.EventURL,
600                 TimeOut,
601                 sub_copy.ActualSID,
602                 &ActualSID);
603
604         HandleLock();
605
606         if (GetHandleInfo(client_handle, &handle_info) != HND_CLIENT) {
607                 HandleUnlock();
608                 return_code = GENA_E_BAD_HANDLE;
609                 goto exit_function;
610         }
611
612         // we just called GetHandleInfo, so we don't check for return value
613         //GetHandleInfo(client_handle, &handle_info);
614         if (return_code != UPNP_E_SUCCESS) {
615                 // network failure (remove client sub)
616                 RemoveClientSubClientSID(&handle_info->ClientSubList, in_sid);
617                 free_client_subscription(&sub_copy);
618                 HandleUnlock();
619                 goto exit_function;
620         }
621
622         // get subscription
623         sub = GetClientSubClientSID(handle_info->ClientSubList, in_sid);
624         if (sub == NULL) {
625                 free_client_subscription(&sub_copy);
626                 HandleUnlock();
627                 return_code = GENA_E_BAD_SID;
628                 goto exit_function;
629         }
630
631         // store actual sid
632         free( sub->ActualSID );
633         sub->ActualSID = ActualSID;
634
635         // start renew subscription timer
636         return_code = ScheduleGenaAutoRenew(client_handle, *TimeOut, sub);
637         if (return_code != GENA_SUCCESS) {
638                 RemoveClientSubClientSID(
639                         &handle_info->ClientSubList,
640                         sub->sid);
641         }
642         free_client_subscription(&sub_copy);
643         HandleUnlock();
644
645 exit_function:
646         free(ActualSID);
647         
648         return return_code;
649 }
650
651
652 void gena_process_notification_event(
653         SOCKINFO *info,
654         http_message_t *event)
655 {
656         struct Upnp_Event event_struct;
657         IXML_Document *ChangedVars = NULL;
658         int eventKey;
659         token sid;
660         client_subscription *subscription = NULL;
661         struct Handle_Info *handle_info;
662         void *cookie;
663         Upnp_FunPtr callback;
664         UpnpClient_Handle client_handle;
665
666         memptr sid_hdr;
667         memptr nt_hdr,
668         nts_hdr;
669         memptr seq_hdr;
670
671         // get SID
672         if (httpmsg_find_hdr(event, HDR_SID, &sid_hdr) == NULL) {
673                 error_respond(info, HTTP_PRECONDITION_FAILED, event);
674                 goto exit_function;
675         }
676         sid.buff = sid_hdr.buf;
677         sid.size = sid_hdr.length;
678
679         // get event key
680         if (httpmsg_find_hdr(event, HDR_SEQ, &seq_hdr) == NULL ||
681             matchstr(seq_hdr.buf, seq_hdr.length, "%d%0", &eventKey) != PARSE_OK) {
682                 error_respond( info, HTTP_BAD_REQUEST, event );
683                 goto exit_function;
684         }
685
686         // get NT and NTS headers
687         if (httpmsg_find_hdr(event, HDR_NT, &nt_hdr) == NULL ||
688             httpmsg_find_hdr(event, HDR_NTS, &nts_hdr) == NULL) {
689                 error_respond( info, HTTP_BAD_REQUEST, event );
690                 goto exit_function;
691         }
692
693         // verify NT and NTS headers
694         if (memptr_cmp(&nt_hdr, "upnp:event") != 0 ||
695             memptr_cmp(&nts_hdr, "upnp:propchange") != 0) {
696                 error_respond(info, HTTP_PRECONDITION_FAILED, event);
697                 goto exit_function;
698         }
699
700         // parse the content (should be XML)
701         if (!has_xml_content_type(event) ||
702             event->msg.length == 0 ||
703             ixmlParseBufferEx(event->entity.buf, &ChangedVars) != IXML_SUCCESS) {
704                 error_respond(info, HTTP_BAD_REQUEST, event);
705                 goto exit_function;
706         }
707
708         HandleLock();
709
710         // get client info
711         if (GetClientHandleInfo(&client_handle, &handle_info) != HND_CLIENT) {
712                 error_respond(info, HTTP_PRECONDITION_FAILED, event);
713                 HandleUnlock();
714                 goto exit_function;
715         }
716
717         // get subscription based on SID
718         subscription = GetClientSubActualSID(handle_info->ClientSubList, &sid);
719         if (subscription == NULL) {
720                 if (eventKey == 0) {
721                         // wait until we've finished processing a subscription 
722                         //   (if we are in the middle)
723                         // this is to avoid mistakenly rejecting the first event if we 
724                         //   receive it before the subscription response
725                         HandleUnlock();
726
727                         // try and get Subscription Lock 
728                         //   (in case we are in the process of subscribing)
729                         SubscribeLock();
730
731                         // get HandleLock again
732                         HandleLock();
733
734                         if (GetClientHandleInfo(&client_handle, &handle_info) != HND_CLIENT) {
735                                 error_respond(info, HTTP_PRECONDITION_FAILED, event);
736                                 SubscribeUnlock();
737                                 HandleUnlock();
738                                 goto exit_function;
739                         }
740
741                         subscription = GetClientSubActualSID(handle_info->ClientSubList, &sid);
742                         if (subscription == NULL) {
743                                 error_respond( info, HTTP_PRECONDITION_FAILED, event );
744                                 SubscribeUnlock();
745                                 HandleUnlock();
746                                 goto exit_function;
747                         }
748
749                         SubscribeUnlock();
750                 } else {
751                         error_respond( info, HTTP_PRECONDITION_FAILED, event );
752                         HandleUnlock();
753                         goto exit_function;
754                 }
755         }
756
757         // success
758         error_respond(info, HTTP_OK, event);
759
760         // fill event struct
761         strcpy(event_struct.Sid, subscription->sid);
762         event_struct.EventKey = eventKey;
763         event_struct.ChangedVariables = ChangedVars;
764
765         // copy callback
766         callback = handle_info->Callback;
767         cookie = handle_info->Cookie;
768
769         HandleUnlock();
770
771         // make callback with event struct
772         // In future, should find a way of mainting
773         // that the handle is not unregistered in the middle of a
774         // callback
775         callback(UPNP_EVENT_RECEIVED, &event_struct, cookie);
776
777 exit_function:
778         ixmlDocument_free(ChangedVars);
779 }
780
781
782 #endif /* INCLUDE_CLIENT_APIS */
783 #endif /* EXCLUDE_GENA */
784