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_device.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 /*!
34  * \file
35  */
36
37
38 #include "config.h"
39
40
41 #if EXCLUDE_GENA == 0
42 #ifdef INCLUDE_DEVICE_APIS
43
44
45 #include "gena.h"
46 #include "httpparser.h"
47 #include "httpreadwrite.h"
48 #include "parsetools.h"
49 #include "ssdplib.h"
50 #include "statcodes.h"
51 #include "sysdep.h"
52 #include "unixutil.h"
53 #include "upnpapi.h"
54 #include "uuid.h"
55
56
57 /*!
58  * \brief Unregisters a device.
59  *
60  * \return UPNP_E_SUCCESS on success, GENA_E_BAD_HANDLE on failure.
61  */
62 int genaUnregisterDevice(
63         /*! [in] Device handle. */
64         UpnpDevice_Handle device_handle)
65 {
66         int ret = 0;
67         struct Handle_Info *handle_info;
68
69         HandleLock();
70         if (GetHandleInfo(device_handle, &handle_info) != HND_DEVICE) {
71                 UpnpPrintf(UPNP_CRITICAL, GENA, __FILE__, __LINE__,
72                         "genaUnregisterDevice: BAD Handle: %d\n",
73                         device_handle);
74                 ret = GENA_E_BAD_HANDLE;
75         } else {
76                 freeServiceTable(&handle_info->ServiceTable);
77                 ret = UPNP_E_SUCCESS;
78         }
79         HandleUnlock();
80
81         return ret;
82 }
83
84
85 /*!
86  * \brief Generates XML property set for notifications.
87  *
88  * \return UPNP_E_SUCCESS if successful else returns GENA_E_BAD_HANDLE.
89  *
90  * \note The XML_VERSION comment is NOT sent due to interoperability issues
91  *      with other UPnP vendors.
92  */
93 static int GeneratePropertySet(
94         /*! [in] Array of variable names (go in the event notify). */
95         char **names,
96         /*! [in] Array of variable values (go in the event notify). */
97         char **values,
98         /*! [in] number of variables. */
99         int count,
100         /*! [out] PropertySet node in the string format. */
101         DOMString *out)
102 {
103         char *buffer;
104         int counter = 0;
105         int size = 0;
106         int temp_counter = 0;
107
108         /*size += strlen(XML_VERSION);*/
109         size += strlen(XML_PROPERTYSET_HEADER);
110         size += strlen("</e:propertyset>\n\n");
111         for (temp_counter = 0, counter = 0; counter < count; counter++) {
112                 size += strlen( "<e:property>\n</e:property>\n" );
113                 size += 2 * strlen(names[counter]) +
114                         strlen(values[counter]) +
115                         strlen("<></>\n");
116         }
117
118         buffer = (char *)malloc(size + 1);
119         if (buffer == NULL) {
120                 return UPNP_E_OUTOF_MEMORY;
121         }
122         memset(buffer, 0, size + 1);
123         /*
124         strcpy(buffer,XML_VERSION);
125         strcat(buffer, XML_PROPERTYSET_HEADER);
126         */
127         strcpy(buffer, XML_PROPERTYSET_HEADER);
128         for (counter = 0; counter < count; counter++) {
129                 strcat(buffer, "<e:property>\n");
130                 sprintf(&buffer[strlen(buffer)],
131                         "<%s>%s</%s>\n</e:property>\n",
132                         names[counter],
133                         values[counter],
134                         names[counter]);
135         }
136         strcat(buffer, "</e:propertyset>\n\n");
137         *out = ixmlCloneDOMString(buffer);
138         free(buffer);
139
140         return XML_SUCCESS;
141 }
142
143
144 /*!
145  * \brief Frees memory used in notify_threads if the reference count is 0,
146  * otherwise decrements the refrence count.
147  */
148 static void free_notify_struct(
149         /*! [in] Notify structure. */
150         notify_thread_struct *input)
151 {
152         (*input->reference_count)--;
153         if (*input->reference_count == 0) {
154                 free(input->headers);
155                 ixmlFreeDOMString(input->propertySet);
156                 free(input->servId);
157                 free(input->UDN);
158                 free(input->reference_count);
159         }
160         free(input);
161 }
162
163
164 /*!
165  * \brief Sends the notify message and returns a reply.
166  *
167  * \return on success returns UPNP_E_SUCCESS, otherwise returns a UPNP error.
168  *
169  * \note called by genaNotify
170  */
171 static UPNP_INLINE int notify_send_and_recv(
172         /*! [in] subscription callback URL (URL of the control point). */
173         uri_type *destination_url,
174         /*! [in] Common HTTP headers. */
175         membuffer *mid_msg,
176         /*! [in] The evented XML. */
177         char *propertySet,
178         /*! [out] The response from the control point. */
179         http_parser_t *response)
180 {
181     uri_type url;
182     int conn_fd;
183     membuffer start_msg;
184     int ret_code;
185     int err_code;
186     int timeout;
187     SOCKINFO info;
188
189     // connect
190     UpnpPrintf( UPNP_ALL, GENA, __FILE__, __LINE__,
191         "gena notify to: %.*s\n",
192         (int)destination_url->hostport.text.size,
193         destination_url->hostport.text.buff );
194
195     conn_fd = http_Connect( destination_url, &url );
196     if( conn_fd < 0 ) {
197         return conn_fd;         // return UPNP error
198     }
199
200     if( ( ret_code = sock_init( &info, conn_fd ) ) != 0 ) {
201         sock_destroy( &info, SD_BOTH );
202         return ret_code;
203     }
204     // make start line and HOST header
205     membuffer_init( &start_msg );
206     if (http_MakeMessage(
207         &start_msg, 1, 1,
208         "q" "s",
209         HTTPMETHOD_NOTIFY, &url,
210         mid_msg->buf ) != 0 ) {
211         membuffer_destroy( &start_msg );
212         sock_destroy( &info, SD_BOTH );
213         return UPNP_E_OUTOF_MEMORY;
214     }
215
216     timeout = HTTP_DEFAULT_TIMEOUT;
217
218     // send msg (note +1 for propertyset; null-terminator is also sent)
219     if( ( ret_code = http_SendMessage( &info, &timeout,
220                                        "bb",
221                                        start_msg.buf, start_msg.length,
222                                        propertySet,
223                                        strlen( propertySet ) + 1 ) ) !=
224         0 ) {
225         membuffer_destroy( &start_msg );
226         sock_destroy( &info, SD_BOTH );
227         return ret_code;
228     }
229
230     if( ( ret_code = http_RecvMessage( &info, response,
231                                        HTTPMETHOD_NOTIFY, &timeout,
232                                        &err_code ) ) != 0 ) {
233         membuffer_destroy( &start_msg );
234         sock_destroy( &info, SD_BOTH );
235         httpmsg_destroy( &response->msg );
236         return ret_code;
237     }
238
239     sock_destroy( &info, SD_BOTH ); //should shutdown completely
240     //when closing socket
241     //  sock_destroy( &info,SD_RECEIVE);
242     membuffer_destroy( &start_msg );
243
244     return UPNP_E_SUCCESS;
245 }
246
247
248 /*!
249  * \brief Function to Notify a particular subscription of a particular event.
250  *
251  * In general the service should NOT be blocked around this call (this may
252  * cause deadlock with a client).
253  *
254  * NOTIFY http request is sent and the reply is processed.
255  *
256  * \return GENA_SUCCESS if the event was delivered, otherwise returns the
257  *      appropriate error code.
258  */
259 static int genaNotify(
260         /*! [in] Null terminated, includes all headers (including \\r\\n) except SID and SEQ. */
261         char *headers,
262         /*! [in] The evented XML. */
263         char *propertySet,
264         /*! [in] subscription to be Notified, assumes this is valid for life of function. */
265         subscription *sub)
266 {
267     int i;
268     membuffer mid_msg;
269     membuffer endmsg;
270     uri_type *url;
271     http_parser_t response;
272     int return_code = -1;
273
274     membuffer_init( &mid_msg );
275
276     // make 'end' msg (the part that won't vary with the destination)
277     endmsg.size_inc = 30;
278     if( http_MakeMessage(
279         &mid_msg, 1, 1,
280         "s" "ssc" "sdcc",
281         headers,
282         "SID: ", sub->sid,
283         "SEQ: ", sub->ToSendEventKey ) != 0 ) {
284         membuffer_destroy( &mid_msg );
285         return UPNP_E_OUTOF_MEMORY;
286     }
287     // send a notify to each url until one goes thru
288     for( i = 0; i < sub->DeliveryURLs.size; i++ ) {
289         url = &sub->DeliveryURLs.parsedURLs[i];
290
291         if( ( return_code = notify_send_and_recv( url,
292                                                   &mid_msg, propertySet,
293                                                   &response ) ) ==
294             UPNP_E_SUCCESS ) {
295             break;
296         }
297     }
298
299     membuffer_destroy( &mid_msg );
300
301     if( return_code == UPNP_E_SUCCESS ) {
302         if( response.msg.status_code == HTTP_OK ) {
303             return_code = GENA_SUCCESS;
304         } else {
305             if( response.msg.status_code == HTTP_PRECONDITION_FAILED ) {
306                 //Invalid SID gets removed
307                 return_code = GENA_E_NOTIFY_UNACCEPTED_REMOVE_SUB;
308             } else {
309                 return_code = GENA_E_NOTIFY_UNACCEPTED;
310             }
311         }
312         httpmsg_destroy( &response.msg );
313     }
314
315     return return_code;
316 }
317
318
319 /*!
320  * \brief Thread job to Notify a control point.
321  *
322  * It validates the subscription and copies the subscription. Also make sure
323  * that events are sent in order.
324  *
325  * \note calls the genaNotify to do the actual work.
326  */
327 static void genaNotifyThread(
328         /*! [in] notify thread structure containing all the headers and property set info. */
329         void *input)
330 {
331     subscription *sub;
332     service_info *service;
333     subscription sub_copy;
334     notify_thread_struct *in = ( notify_thread_struct * ) input;
335     int return_code;
336     struct Handle_Info *handle_info;
337     ThreadPoolJob job;
338
339     HandleReadLock();
340     //validate context
341
342     if( GetHandleInfo( in->device_handle, &handle_info ) != HND_DEVICE ) {
343         free_notify_struct( in );
344         HandleUnlock();
345         return;
346     }
347
348     if( ( ( service = FindServiceId( &handle_info->ServiceTable,
349                                      in->servId, in->UDN ) ) == NULL )
350         || ( !service->active )
351         || ( ( sub = GetSubscriptionSID( in->sid, service ) ) == NULL )
352         || ( ( copy_subscription( sub, &sub_copy ) != HTTP_SUCCESS ) ) ) {
353         free_notify_struct( in );
354         HandleUnlock();
355         return;
356     }
357     //If the event is out of order push it back to the job queue
358     if( in->eventKey != sub->ToSendEventKey ) {
359
360         TPJobInit( &job, ( start_routine ) genaNotifyThread, input );
361         TPJobSetFreeFunction( &job, ( free_function ) free_notify_struct );
362         TPJobSetPriority( &job, MED_PRIORITY );
363         ThreadPoolAdd( &gSendThreadPool, &job, NULL );
364
365         freeSubscription( &sub_copy );
366         HandleUnlock();
367         return;
368     }
369
370     HandleUnlock();
371
372     //send the notify
373     return_code = genaNotify( in->headers, in->propertySet, &sub_copy );
374
375     freeSubscription( &sub_copy );
376
377     HandleLock();
378
379     if( GetHandleInfo( in->device_handle, &handle_info ) != HND_DEVICE ) {
380         free_notify_struct( in );
381         HandleUnlock();
382         return;
383     }
384     //validate context
385     if( ( ( service = FindServiceId( &handle_info->ServiceTable,
386                                      in->servId, in->UDN ) ) == NULL )
387         || ( !service->active )
388         || ( ( sub = GetSubscriptionSID( in->sid, service ) ) == NULL ) ) {
389         free_notify_struct( in );
390         HandleUnlock();
391         return;
392     }
393
394     sub->ToSendEventKey++;
395
396     if( sub->ToSendEventKey < 0 )   //wrap to 1 for overflow
397         sub->ToSendEventKey = 1;
398
399     if( return_code == GENA_E_NOTIFY_UNACCEPTED_REMOVE_SUB ) {
400         RemoveSubscriptionSID( in->sid, service );
401     }
402
403     free_notify_struct( in );
404     HandleUnlock();
405 }
406
407
408 /*!
409  * \brief Allocates the GENA header.
410  *
411  * \note The header must be destroyed after with a call to free(), otherwise
412  * there will be a memory leak.
413  *
414  * \return The constructed header.
415  */
416 static char *AllocGenaHeaders(
417         /*! [in] The property set string. */
418         const DOMString propertySet)
419 {
420         static const char *HEADER_LINE_1 =
421                 "CONTENT-TYPE: text/xml; charset=\"utf-8\"\r\n";
422         static const char *HEADER_LINE_2A =
423                 "CONTENT-LENGTH: ";
424         static const char *HEADER_LINE_2B =
425                 "\r\n";
426         static const char *HEADER_LINE_3 =
427                 "NT: upnp:event\r\n";
428         static const char *HEADER_LINE_4 =
429                 "NTS: upnp:propchange\r\n";
430         char *headers = NULL;
431         int headers_size = 0;
432         int line = 0;
433
434         headers_size =
435                 strlen(HEADER_LINE_1 ) +
436                 strlen(HEADER_LINE_2A) + MAX_CONTENT_LENGTH +
437                 strlen(HEADER_LINE_2B) +
438                 strlen(HEADER_LINE_3 ) +
439                 strlen(HEADER_LINE_4 ) + 1;
440         headers = (char *)malloc(headers_size);
441         if (headers == NULL) {
442                 line = __LINE__;
443                 goto ExitFunction;
444         }
445         sprintf(headers, "%s%s%"PRIzu"%s%s%s",
446                 HEADER_LINE_1,
447                 HEADER_LINE_2A,
448                 strlen(propertySet) + 1,
449                 HEADER_LINE_2B,
450                 HEADER_LINE_3,
451                 HEADER_LINE_4);
452
453 ExitFunction:
454         if (headers == NULL) {
455                 UpnpPrintf(UPNP_ALL, GENA, __FILE__, line,
456                         "AllocGenaHeaders(): Error UPNP_E_OUTOF_MEMORY\n");
457         }
458         return headers;
459 }
460
461
462 int genaInitNotify(
463         UpnpDevice_Handle device_handle,
464         char *UDN,
465         char *servId,
466         char **VarNames,
467         char **VarValues,
468         int var_count,
469         const Upnp_SID sid)
470 {
471         int ret = GENA_SUCCESS;
472         int line = 0;
473
474         int *reference_count = NULL;
475         char *UDN_copy = NULL;
476         char *servId_copy = NULL;
477         DOMString propertySet = NULL;
478         char *headers = NULL;
479         notify_thread_struct *thread_struct = NULL;
480
481         subscription *sub = NULL;
482         service_info *service = NULL;
483         struct Handle_Info *handle_info;
484         ThreadPoolJob job;
485
486         UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__,
487                 "GENA BEGIN INITIAL NOTIFY");
488
489         reference_count = (int *)malloc(sizeof (int));
490         if (reference_count == NULL) {
491                 line = __LINE__;
492                 ret = UPNP_E_OUTOF_MEMORY;
493                 goto ExitFunction;
494         }
495         *reference_count = 0;
496         
497         UDN_copy = (char *)malloc(strlen(UDN) + 1);
498         if (UDN_copy == NULL) {
499                 line = __LINE__;
500                 ret = UPNP_E_OUTOF_MEMORY;
501                 goto ExitFunction;
502         }
503
504         servId_copy = (char *)malloc(strlen(servId) + 1);
505         if (servId_copy == NULL) {
506                 line = __LINE__;
507                 ret = UPNP_E_OUTOF_MEMORY;
508                 goto ExitFunction;
509         }
510
511         strcpy(UDN_copy, UDN);
512         strcpy(servId_copy, servId);
513
514         HandleLock();
515
516         if (GetHandleInfo(device_handle, &handle_info) != HND_DEVICE) {
517                 line = __LINE__;
518                 ret = GENA_E_BAD_HANDLE;
519                 goto ExitFunction;
520         }
521
522         service = FindServiceId(&handle_info->ServiceTable, servId, UDN);
523         if (service == NULL) {
524                 line = __LINE__;
525                 ret = GENA_E_BAD_SERVICE;
526                 goto ExitFunction;
527         }
528         UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__,
529                 "FOUND SERVICE IN INIT NOTFY: UDN %s, ServID: %s",
530                 UDN, servId);
531
532         sub = GetSubscriptionSID(sid, service);
533         if (sub == NULL || sub->active) {
534                 line = __LINE__;
535                 ret = GENA_E_BAD_SID;
536                 goto ExitFunction;
537         }
538         UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__,
539                 "FOUND SUBSCRIPTION IN INIT NOTIFY: SID %s", sid);
540         sub->active = 1;
541
542         if (var_count <= 0) {
543                 line = __LINE__;
544                 ret = GENA_SUCCESS;
545                 goto ExitFunction;
546         }
547
548         ret = GeneratePropertySet(VarNames, VarValues, var_count, &propertySet);
549         if (ret != XML_SUCCESS) {
550                 line = __LINE__;
551                 goto ExitFunction;
552         }
553         UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__,
554                 "GENERATED PROPERTY SET IN INIT NOTIFY: %s",
555                 propertySet);
556
557         headers = AllocGenaHeaders(propertySet);
558         if (headers == NULL) {
559                 line = __LINE__;
560                 ret = UPNP_E_OUTOF_MEMORY;
561                 goto ExitFunction;
562         }
563
564         /* schedule thread for initial notification */
565
566         thread_struct = (notify_thread_struct *)malloc(sizeof (notify_thread_struct));
567         if (thread_struct == NULL) {
568                 line = __LINE__;
569                 ret = UPNP_E_OUTOF_MEMORY;
570         } else {
571                 *reference_count = 1;
572                 thread_struct->servId = servId_copy;
573                 thread_struct->UDN = UDN_copy;
574                 thread_struct->headers = headers;
575                 thread_struct->propertySet = propertySet;
576                 strcpy(thread_struct->sid, sid);
577                 thread_struct->eventKey = sub->eventKey++;
578                 thread_struct->reference_count = reference_count;
579                 thread_struct->device_handle = device_handle;
580
581                 TPJobInit(&job, (start_routine)genaNotifyThread, thread_struct);
582                 TPJobSetFreeFunction(&job, (free_routine)free_notify_struct);
583                 TPJobSetPriority(&job, MED_PRIORITY);
584
585                 ret = ThreadPoolAdd(&gSendThreadPool, &job, NULL);
586                 if (ret != 0) {
587                         if (ret == EOUTOFMEM) {
588                                 line = __LINE__;
589                                 ret = UPNP_E_OUTOF_MEMORY;
590                         }
591                 } else {
592                         line = __LINE__;
593                         ret = GENA_SUCCESS;
594                 }
595         }
596
597 ExitFunction:
598         if (ret != GENA_SUCCESS || var_count <= 0) {
599                 free(thread_struct);
600                 free(headers);
601                 ixmlFreeDOMString(propertySet);
602                 free(servId_copy);
603                 free(UDN_copy);
604                 free(reference_count);
605         }
606
607         HandleUnlock();
608
609         UpnpPrintf(UPNP_INFO, GENA, __FILE__, line,
610                 "GENA END INITIAL NOTIFY, ret = %d",
611                 ret);
612
613         return ret;
614 }
615
616
617 int genaInitNotifyExt(
618         UpnpDevice_Handle device_handle,
619         char *UDN,
620         char *servId,
621         IXML_Document *PropSet,
622         const Upnp_SID sid)
623 {
624         int ret = GENA_SUCCESS;
625         int line = 0;
626
627         int *reference_count = NULL;
628         char *UDN_copy = NULL;
629         char *servId_copy = NULL;
630         DOMString propertySet = NULL;
631         char *headers = NULL;
632         notify_thread_struct *thread_struct = NULL;
633
634         subscription *sub = NULL;
635         service_info *service = NULL;
636         struct Handle_Info *handle_info;
637         ThreadPoolJob job;
638
639         UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__,
640                 "GENA BEGIN INITIAL NOTIFY EXT");
641         
642         reference_count = (int *)malloc(sizeof (int));
643         if (reference_count == NULL) {
644                 line = __LINE__;
645                 ret = UPNP_E_OUTOF_MEMORY;
646                 goto ExitFunction;
647         }
648         *reference_count = 0;
649         
650         UDN_copy = (char *)malloc(strlen(UDN) + 1);
651         if (UDN_copy == NULL) {
652                 line = __LINE__;
653                 ret = UPNP_E_OUTOF_MEMORY;
654                 goto ExitFunction;
655         }
656
657         servId_copy = (char *)malloc(strlen(servId) + 1);
658         if( servId_copy == NULL ) {
659                 line = __LINE__;
660                 ret = UPNP_E_OUTOF_MEMORY;
661                 goto ExitFunction;
662         }
663
664         strcpy(UDN_copy, UDN);
665         strcpy(servId_copy, servId);
666
667         HandleLock();
668
669         if (GetHandleInfo(device_handle, &handle_info) != HND_DEVICE) {
670                 line = __LINE__;
671                 ret = GENA_E_BAD_HANDLE;
672                 goto ExitFunction;
673         }
674
675         service = FindServiceId(&handle_info->ServiceTable, servId, UDN);
676         if (service == NULL) {
677                 line = __LINE__;
678                 ret = GENA_E_BAD_SERVICE;
679                 goto ExitFunction;
680         }
681         UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__,
682                 "FOUND SERVICE IN INIT NOTFY EXT: UDN %s, ServID: %s",
683                 UDN, servId);
684
685         sub = GetSubscriptionSID(sid, service);
686         if (sub == NULL || sub->active) {
687                 line = __LINE__;
688                 ret = GENA_E_BAD_SID;
689                 goto ExitFunction;
690         }
691         UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__,
692                 "FOUND SUBSCRIPTION IN INIT NOTIFY EXT: SID %s", sid);
693         sub->active = 1;
694
695         if (PropSet == 0) {
696                 line = __LINE__;
697                 ret = GENA_SUCCESS;
698                 goto ExitFunction;
699         }
700
701         propertySet = ixmlPrintNode((IXML_Node *)PropSet);
702         if (propertySet == NULL) {
703                 line = __LINE__;
704                 ret = UPNP_E_INVALID_PARAM;
705                 goto ExitFunction;
706         }
707         UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__,
708                 "GENERATED PROPERTY SET IN INIT EXT NOTIFY: %s",
709                 propertySet);
710
711         headers = AllocGenaHeaders(propertySet);
712         if (headers == NULL) {
713                 line = __LINE__;
714                 ret = UPNP_E_OUTOF_MEMORY;
715                 goto ExitFunction;
716         }
717
718         /* schedule thread for initial notification */
719
720         thread_struct = (notify_thread_struct *)malloc(sizeof (notify_thread_struct));
721         if (thread_struct == NULL) {
722                 line = __LINE__;
723                 ret = UPNP_E_OUTOF_MEMORY;
724         } else {
725                 *reference_count = 1;
726                 thread_struct->servId = servId_copy;
727                 thread_struct->UDN = UDN_copy;
728                 thread_struct->headers = headers;
729                 thread_struct->propertySet = propertySet;
730                 strcpy(thread_struct->sid, sid);
731                 thread_struct->eventKey = sub->eventKey++;
732                 thread_struct->reference_count = reference_count;
733                 thread_struct->device_handle = device_handle;
734
735                 TPJobInit(&job, (start_routine)genaNotifyThread, thread_struct);
736                 TPJobSetFreeFunction(&job, (free_routine)free_notify_struct);
737                 TPJobSetPriority(&job, MED_PRIORITY);
738
739                 ret = ThreadPoolAdd(&gSendThreadPool, &job, NULL);
740                 if (ret != 0) {
741                         if (ret == EOUTOFMEM) {
742                                 line = __LINE__;
743                                 ret = UPNP_E_OUTOF_MEMORY;
744                         }
745                 } else {
746                         line = __LINE__;
747                         ret = GENA_SUCCESS;
748                 }
749         }
750
751 ExitFunction:
752         if (ret != GENA_SUCCESS || PropSet == 0) {
753                 free(thread_struct);
754                 free(headers);
755                 ixmlFreeDOMString(propertySet);
756                 free(servId_copy);
757                 free(UDN_copy);
758                 free(reference_count);
759         }
760
761         HandleUnlock();
762
763         UpnpPrintf(UPNP_INFO, GENA, __FILE__, line,
764                 "GENA END INITIAL NOTIFY EXT, ret = %d",
765                 ret);
766
767         return ret;
768 }
769
770
771 int genaNotifyAllExt(
772         UpnpDevice_Handle device_handle,
773         char *UDN,
774         char *servId,
775         IXML_Document *PropSet)
776 {
777         int ret = GENA_SUCCESS;
778         int line = 0;
779
780         int *reference_count = NULL;
781         char *UDN_copy = NULL;
782         char *servId_copy = NULL;
783         DOMString propertySet = NULL;
784         char *headers = NULL;
785         notify_thread_struct *thread_struct = NULL;
786
787         subscription *finger = NULL;
788         service_info *service = NULL;
789         struct Handle_Info *handle_info;
790         ThreadPoolJob job;
791
792         UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__,
793                 "GENA BEGIN NOTIFY ALL EXT");
794
795         reference_count = (int *)malloc(sizeof (int));
796         if (reference_count == NULL) {
797                 line = __LINE__;
798                 ret = UPNP_E_OUTOF_MEMORY;
799                 goto ExitFunction;
800         }
801         *reference_count = 0;
802         
803         UDN_copy = (char *)malloc(strlen(UDN) + 1);
804         if (UDN_copy == NULL) {
805                 line = __LINE__;
806                 ret = UPNP_E_OUTOF_MEMORY;
807                 goto ExitFunction;
808         }
809
810         servId_copy = (char *)malloc(strlen(servId) + 1);
811         if( servId_copy == NULL ) {
812                 line = __LINE__;
813                 ret = UPNP_E_OUTOF_MEMORY;
814                 goto ExitFunction;
815         }
816
817         strcpy(UDN_copy, UDN);
818         strcpy(servId_copy, servId);
819
820         propertySet = ixmlPrintNode((IXML_Node *)PropSet);
821         if (propertySet == NULL) {
822                 line = __LINE__;
823                 ret = UPNP_E_INVALID_PARAM;
824                 goto ExitFunction;
825         }
826         UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__,
827                 "GENERATED PROPERTY SET IN EXT NOTIFY: %s",
828                 propertySet);
829
830         headers = AllocGenaHeaders(propertySet);
831         if (headers == NULL) {
832                 line = __LINE__;
833                 ret = UPNP_E_OUTOF_MEMORY;
834                 goto ExitFunction;
835         }
836
837         HandleLock();
838
839         if (GetHandleInfo(device_handle, &handle_info) != HND_DEVICE) {
840                 line = __LINE__;
841                 ret = GENA_E_BAD_HANDLE;
842         } else {
843                 service = FindServiceId(&handle_info->ServiceTable, servId, UDN);
844                 if (service != NULL) {
845                         finger = GetFirstSubscription(service);
846                         while (finger) {
847                                 thread_struct = (notify_thread_struct *)malloc(sizeof (notify_thread_struct));
848                                 if (thread_struct == NULL) {
849                                         line = __LINE__;
850                                         ret = UPNP_E_OUTOF_MEMORY;
851                                         break;
852                                 }
853
854                                 (*reference_count)++;
855                                 thread_struct->reference_count = reference_count;
856                                 thread_struct->UDN = UDN_copy;
857                                 thread_struct->servId = servId_copy;
858                                 thread_struct->headers = headers;
859                                 thread_struct->propertySet = propertySet;
860                                 strcpy(thread_struct->sid, finger->sid);
861                                 thread_struct->eventKey = finger->eventKey++;
862                                 thread_struct->device_handle = device_handle;
863                                 /* if overflow, wrap to 1 */
864                                 if (finger->eventKey < 0) {
865                                         finger->eventKey = 1;
866                                 }
867
868                                 TPJobInit(&job, (start_routine)genaNotifyThread, thread_struct);
869                                 TPJobSetFreeFunction(&job, (free_routine)free_notify_struct);
870                                 TPJobSetPriority(&job, MED_PRIORITY);
871                                 ret = ThreadPoolAdd(&gSendThreadPool, &job, NULL);
872                                 if (ret != 0) {
873                                         line = __LINE__;
874                                         if (ret == EOUTOFMEM) {
875                                                 line = __LINE__;
876                                                 ret = UPNP_E_OUTOF_MEMORY;
877                                         }
878                                         break;
879                                 }
880
881                                 finger = GetNextSubscription(service, finger);
882                         }
883                 } else {
884                         line = __LINE__;
885                         ret = GENA_E_BAD_SERVICE;
886                 }
887         }
888
889 ExitFunction:
890         if (ret != GENA_SUCCESS || *reference_count == 0) {
891                 free(headers);
892                 ixmlFreeDOMString(propertySet);
893                 free(servId_copy);
894                 free(UDN_copy);
895                 free(reference_count);
896         }
897
898         HandleUnlock();
899
900         UpnpPrintf(UPNP_INFO, GENA, __FILE__, line,
901                 "GENA END NOTIFY ALL EXT, ret = %d",
902                 ret);
903
904         return ret;
905 }
906
907
908 int genaNotifyAll(
909         UpnpDevice_Handle device_handle,
910         char *UDN,
911         char *servId,
912         char **VarNames,
913         char **VarValues,
914         int var_count)
915 {
916         int ret = GENA_SUCCESS;
917         int line = 0;
918
919         int *reference_count = NULL;
920         char *UDN_copy = NULL;
921         char *servId_copy = NULL;
922         DOMString propertySet = NULL;
923         char *headers = NULL;
924         notify_thread_struct *thread_struct = NULL;
925
926         subscription *finger = NULL;
927         service_info *service = NULL;
928         struct Handle_Info *handle_info;
929         ThreadPoolJob job;
930
931         UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__,
932                 "GENA BEGIN NOTIFY ALL");
933
934         reference_count = (int *)malloc(sizeof (int));
935         if (reference_count == NULL) {
936                 line = __LINE__;
937                 ret = UPNP_E_OUTOF_MEMORY;
938                 goto ExitFunction;
939         }
940         *reference_count = 0;
941         
942         UDN_copy = (char *)malloc(strlen(UDN) + 1);
943         if (UDN_copy == NULL) {
944                 line = __LINE__;
945                 ret = UPNP_E_OUTOF_MEMORY;
946                 goto ExitFunction;
947         }
948
949         servId_copy = (char *)malloc(strlen(servId) + 1);
950         if( servId_copy == NULL ) {
951                 line = __LINE__;
952                 ret = UPNP_E_OUTOF_MEMORY;
953                 goto ExitFunction;
954         }
955
956         strcpy(UDN_copy, UDN);
957         strcpy(servId_copy, servId);
958
959         ret = GeneratePropertySet(VarNames, VarValues, var_count, &propertySet);
960         if (ret != XML_SUCCESS) {
961                 line = __LINE__;
962                 goto ExitFunction;
963         }
964         UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__,
965                 "GENERATED PROPERTY SET IN EXT NOTIFY: %s",
966                 propertySet);
967
968         headers = AllocGenaHeaders(propertySet);
969         if (headers == NULL) {
970                 line = __LINE__;
971                 ret = UPNP_E_OUTOF_MEMORY;
972                 goto ExitFunction;
973         }
974
975         HandleLock();
976
977         if (GetHandleInfo(device_handle, &handle_info) != HND_DEVICE) {
978                 line = __LINE__;
979                 ret = GENA_E_BAD_HANDLE;
980         } else {
981                 service = FindServiceId(&handle_info->ServiceTable, servId, UDN);
982                 if (service != NULL) {
983                         finger = GetFirstSubscription(service);
984                         while (finger) {
985                                 thread_struct = (notify_thread_struct *)malloc(sizeof (notify_thread_struct));
986                                 if (thread_struct == NULL) {
987                                         line = __LINE__;
988                                         ret = UPNP_E_OUTOF_MEMORY;
989                                         break;
990                                 }
991
992                                 (*reference_count)++;
993                                 thread_struct->reference_count = reference_count;
994                                 thread_struct->UDN = UDN_copy;
995                                 thread_struct->servId = servId_copy;
996                                 thread_struct->headers = headers;
997                                 thread_struct->propertySet = propertySet;
998                                 strcpy(thread_struct->sid, finger->sid);
999                                 thread_struct->eventKey = finger->eventKey++;
1000                                 thread_struct->device_handle = device_handle;
1001                                 /* if overflow, wrap to 1 */
1002                                 if (finger->eventKey < 0) {
1003                                         finger->eventKey = 1;
1004                                 }
1005
1006
1007                                 TPJobInit(&job, (start_routine)genaNotifyThread, thread_struct);
1008                                 TPJobSetFreeFunction(&job, (free_routine)free_notify_struct);
1009                                 TPJobSetPriority(&job, MED_PRIORITY);
1010                                 ret = ThreadPoolAdd(&gSendThreadPool, &job, NULL);
1011                                 if (ret != 0) {
1012                                         line = __LINE__;
1013                                         if (ret == EOUTOFMEM) {
1014                                                 line = __LINE__;
1015                                                 ret = UPNP_E_OUTOF_MEMORY;
1016                                         }
1017                                         break;
1018                                 }
1019
1020                                 finger = GetNextSubscription(service, finger);
1021                         }
1022                 } else {
1023                         line = __LINE__;
1024                         ret = GENA_E_BAD_SERVICE;
1025                 }
1026         }
1027
1028 ExitFunction:
1029         if (ret != GENA_SUCCESS || *reference_count == 0) {
1030                 free(headers);
1031                 ixmlFreeDOMString(propertySet);
1032                 free(servId_copy);
1033                 free(UDN_copy);
1034                 free(reference_count);
1035         }
1036
1037         HandleUnlock();
1038
1039         UpnpPrintf(UPNP_INFO, GENA, __FILE__, line,
1040                 "GENA END NOTIFY ALL, ret = %d",
1041                 ret);
1042
1043         return ret;
1044 }
1045
1046
1047 /*!
1048  * \brief Returns OK message in the case of a subscription request.
1049  *
1050  * \return UPNP_E_SUCCESS if successful, otherwise the appropriate error code.
1051  */
1052 static int respond_ok(
1053         /*! [in] Socket connection of request. */
1054         SOCKINFO *info,
1055         /*! [in] Accepted duration. */
1056         int time_out,
1057         /*! [in] Accepted subscription. */
1058         subscription *sub,
1059         /*! [in] Http request. */
1060         http_message_t *request)
1061 {
1062     int major;
1063     int minor;
1064     membuffer response;
1065     int return_code;
1066     char timeout_str[100];
1067     int upnp_timeout = UPNP_TIMEOUT;
1068
1069     http_CalcResponseVersion( request->major_version,
1070                               request->minor_version, &major, &minor );
1071
1072     if( time_out >= 0 ) {
1073         sprintf( timeout_str, "TIMEOUT: Second-%d", time_out );
1074     } else {
1075         strcpy( timeout_str, "TIMEOUT: Second-infinite" );
1076     }
1077
1078     membuffer_init( &response );
1079     response.size_inc = 30;
1080     if( http_MakeMessage(
1081         &response, major, minor,
1082         "R" "D" "S" "N" "Xc" "ssc" "scc",
1083         HTTP_OK,
1084         (off_t)0,
1085         X_USER_AGENT,
1086         "SID: ", sub->sid,
1087         timeout_str ) != 0 ) {
1088         membuffer_destroy( &response );
1089         error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request );
1090         return UPNP_E_OUTOF_MEMORY;
1091     }
1092
1093     return_code = http_SendMessage( info, &upnp_timeout, "b",
1094                                     response.buf, response.length );
1095
1096     membuffer_destroy( &response );
1097
1098     return return_code;
1099 }
1100
1101
1102 /*!
1103  * \brief Function to parse the Callback header value in subscription requests.
1104  *
1105  * Takes in a buffer containing URLS delimited by '<' and '>'. The entire buffer
1106  * is copied into dynamic memory and stored in the URL_list. Pointers to the
1107  * individual urls within this buffer are allocated and stored in the URL_list.
1108  * Only URLs with network addresses are considered (i.e. host:port or domain name).
1109  *
1110  * \return The number of URLs parsed if successful, otherwise UPNP_E_OUTOF_MEMORY.
1111  */
1112 static int create_url_list(
1113         /*! [in] . */
1114         memptr *url_list,
1115         /*! [out] . */
1116         URL_list *out)
1117 {
1118     int URLcount = 0;
1119     int i;
1120     int return_code = 0;
1121     uri_type temp;
1122     token urls;
1123     token *URLS;
1124
1125     urls.buff = url_list->buf;
1126     urls.size = url_list->length;
1127     URLS = &urls;
1128
1129     out->size = 0;
1130     out->URLs = NULL;
1131     out->parsedURLs = NULL;
1132
1133     for( i = 0; i < URLS->size; i++ ) {
1134         if( ( URLS->buff[i] == '<' ) && ( i + 1 < URLS->size ) ) {
1135             if( ( ( return_code = parse_uri( &URLS->buff[i + 1],
1136                                              URLS->size - i + 1,
1137                                              &temp ) ) == HTTP_SUCCESS )
1138                 && ( temp.hostport.text.size != 0 ) ) {
1139                 URLcount++;
1140             } else {
1141                 if( return_code == UPNP_E_OUTOF_MEMORY ) {
1142                     return return_code;
1143                 }
1144             }
1145         }
1146     }
1147
1148     if( URLcount > 0 ) {
1149         out->URLs = ( char * )malloc( URLS->size + 1 );
1150         out->parsedURLs =
1151             ( uri_type * ) malloc( sizeof( uri_type ) * URLcount );
1152         if( ( out->URLs == NULL ) || ( out->parsedURLs == NULL ) ) {
1153             free( out->URLs );
1154             free( out->parsedURLs );
1155             out->URLs = NULL;
1156             out->parsedURLs = NULL;
1157             return UPNP_E_OUTOF_MEMORY;
1158         }
1159         memcpy( out->URLs, URLS->buff, URLS->size );
1160         out->URLs[URLS->size] = 0;
1161         URLcount = 0;
1162         for( i = 0; i < URLS->size; i++ ) {
1163             if( ( URLS->buff[i] == '<' ) && ( i + 1 < URLS->size ) ) {
1164                 if( ( ( return_code =
1165                         parse_uri( &out->URLs[i + 1], URLS->size - i + 1,
1166                                    &out->parsedURLs[URLcount] ) ) ==
1167                       HTTP_SUCCESS )
1168                     && ( out->parsedURLs[URLcount].hostport.text.size !=
1169                          0 ) ) {
1170                     URLcount++;
1171                 } else {
1172                     if( return_code == UPNP_E_OUTOF_MEMORY ) {
1173                         free( out->URLs );
1174                         free( out->parsedURLs );
1175                         out->URLs = NULL;
1176                         out->parsedURLs = NULL;
1177                         return return_code;
1178                     }
1179                 }
1180             }
1181         }
1182     }
1183     out->size = URLcount;
1184
1185     return URLcount;
1186 }
1187
1188
1189 void gena_process_subscription_request(
1190         SOCKINFO *info,
1191         http_message_t *request)
1192 {
1193         struct Upnp_Subscription_Request request_struct;
1194         Upnp_SID temp_sid;
1195         int return_code = 1;
1196         int time_out = 1801;
1197         service_info *service;
1198         subscription *sub;
1199         uuid_upnp uid;
1200         struct Handle_Info *handle_info;
1201         void *cookie;
1202         Upnp_FunPtr callback_fun;
1203         UpnpDevice_Handle device_handle;
1204         memptr nt_hdr;
1205         char *event_url_path = NULL;
1206         memptr callback_hdr;
1207         memptr timeout_hdr;
1208
1209         UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__,
1210                 "Subscription Request Received:\n");
1211
1212         if (httpmsg_find_hdr(request, HDR_NT, &nt_hdr) == NULL) {
1213                 error_respond(info, HTTP_BAD_REQUEST, request);
1214                 goto exit_function;
1215         }
1216
1217         // check NT header
1218         // Windows Millenium Interoperability:
1219         // we accept either upnp:event, or upnp:propchange for the NT header
1220         if (memptr_cmp_nocase(&nt_hdr, "upnp:event") != 0) {
1221                 error_respond(info, HTTP_PRECONDITION_FAILED, request);
1222                 goto exit_function;
1223         }
1224
1225         // if a SID is present then the we have a bad request "incompatible headers"
1226         if (httpmsg_find_hdr(request, HDR_SID, NULL) != NULL) {
1227                 error_respond(info, HTTP_BAD_REQUEST, request);
1228                 goto exit_function;
1229         }
1230         // look up service by eventURL
1231         event_url_path = str_alloc(request->uri.pathquery.buff, request->uri.pathquery.size);
1232         if (event_url_path == NULL) {
1233                 error_respond(info, HTTP_INTERNAL_SERVER_ERROR, request);
1234                 goto exit_function;
1235         }
1236
1237         UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__,
1238                 "SubscriptionRequest for event URL path: %s\n",
1239                 event_url_path);
1240
1241         HandleLock();
1242
1243         // CURRENTLY, ONLY ONE DEVICE
1244         if (GetDeviceHandleInfo(
1245             &device_handle, &handle_info) != HND_DEVICE) {
1246                 free(event_url_path);
1247                 error_respond(info, HTTP_INTERNAL_SERVER_ERROR, request);
1248                 HandleUnlock();
1249                 goto exit_function;
1250         }
1251         service = FindServiceEventURLPath(&handle_info->ServiceTable, event_url_path);
1252         free(event_url_path);
1253
1254         if (service == NULL || !service->active) {
1255                 error_respond(info, HTTP_NOT_FOUND, request);
1256                 HandleUnlock();
1257                 goto exit_function;
1258         }
1259
1260         UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__,
1261                 "Subscription Request: Number of Subscriptions already %d\n "
1262                 "Max Subscriptions allowed: %d\n",
1263                 service->TotalSubscriptions,
1264                 handle_info->MaxSubscriptions);
1265
1266         // too many subscriptions
1267         if (handle_info->MaxSubscriptions != -1 &&
1268             service->TotalSubscriptions >= handle_info->MaxSubscriptions) {
1269                 error_respond(info, HTTP_INTERNAL_SERVER_ERROR, request);
1270                 HandleUnlock();
1271                 goto exit_function;
1272         }
1273         // generate new subscription
1274         sub = (subscription *)malloc(sizeof (subscription));
1275         if (sub == NULL) {
1276                 error_respond(info, HTTP_INTERNAL_SERVER_ERROR, request);
1277                 HandleUnlock();
1278                 goto exit_function;
1279         }
1280         sub->eventKey = 0;
1281         sub->ToSendEventKey = 0;
1282         sub->active = 0;
1283         sub->next = NULL;
1284         sub->DeliveryURLs.size = 0;
1285         sub->DeliveryURLs.URLs = NULL;
1286         sub->DeliveryURLs.parsedURLs = NULL;
1287
1288         // check for valid callbacks
1289         if (httpmsg_find_hdr( request, HDR_CALLBACK, &callback_hdr) == NULL) {
1290                 error_respond(info, HTTP_PRECONDITION_FAILED, request);
1291                 freeSubscriptionList(sub);
1292                 HandleUnlock();
1293                 goto exit_function;
1294         }
1295         return_code = create_url_list(&callback_hdr, &sub->DeliveryURLs);
1296         if (return_code == 0) {
1297                 error_respond(info, HTTP_PRECONDITION_FAILED, request);
1298                 freeSubscriptionList(sub);
1299                 HandleUnlock();
1300                 goto exit_function;
1301         }
1302         if (return_code == UPNP_E_OUTOF_MEMORY) {
1303                 error_respond(info, HTTP_INTERNAL_SERVER_ERROR, request);
1304                 freeSubscriptionList(sub);
1305                 HandleUnlock();
1306                 goto exit_function;
1307         }
1308         // set the timeout
1309         if (httpmsg_find_hdr(request, HDR_TIMEOUT, &timeout_hdr) != NULL) {
1310                 if (matchstr(timeout_hdr.buf, timeout_hdr.length,
1311                     "%iSecond-%d%0", &time_out) == PARSE_OK) {
1312                         // nothing
1313                 } else if(memptr_cmp_nocase(&timeout_hdr, "Second-infinite") == 0) {
1314                         // infinite timeout
1315                         time_out = -1;
1316                 } else {
1317                         // default is > 1800 seconds
1318                         time_out = DEFAULT_TIMEOUT;
1319                 }
1320         }
1321         // replace infinite timeout with max timeout, if possible
1322         if (handle_info->MaxSubscriptionTimeOut != -1) {
1323                 if (time_out == -1 ||
1324                     time_out > handle_info->MaxSubscriptionTimeOut) {
1325                         time_out = handle_info->MaxSubscriptionTimeOut;
1326                 }
1327         }
1328         if (time_out >= 0) {
1329                 sub->expireTime = time(NULL) + time_out;
1330         } else {
1331                 // infinite time
1332                 sub->expireTime = 0;
1333         }
1334
1335         // generate SID
1336         uuid_create(&uid);
1337         uuid_unpack(&uid, temp_sid);
1338         sprintf(sub->sid, "uuid:%s", temp_sid);
1339
1340         // respond OK
1341         if (respond_ok(info, time_out, sub, request) != UPNP_E_SUCCESS) {
1342                 freeSubscriptionList(sub);
1343                 HandleUnlock();
1344                 goto exit_function;
1345         }
1346         // add to subscription list
1347         sub->next = service->subscriptionList;
1348         service->subscriptionList = sub;
1349         service->TotalSubscriptions++;
1350
1351         // finally generate callback for init table dump
1352         request_struct.ServiceId = service->serviceId;
1353         request_struct.UDN = service->UDN;
1354         strcpy((char *)request_struct.Sid, sub->sid);
1355
1356         // copy callback
1357         callback_fun = handle_info->Callback;
1358         cookie = handle_info->Cookie;
1359
1360         HandleUnlock();
1361
1362         // make call back with request struct
1363         // in the future should find a way of mainting that the handle
1364         // is not unregistered in the middle of a callback
1365         callback_fun(UPNP_EVENT_SUBSCRIPTION_REQUEST, &request_struct, cookie);
1366
1367 exit_function:
1368         return;
1369 }
1370
1371
1372 void gena_process_subscription_renewal_request(
1373         SOCKINFO *info,
1374         http_message_t *request)
1375 {
1376     Upnp_SID sid;
1377     subscription *sub;
1378     int time_out = 1801;
1379     service_info *service;
1380     struct Handle_Info *handle_info;
1381     UpnpDevice_Handle device_handle;
1382     memptr temp_hdr;
1383     membuffer event_url_path;
1384     memptr timeout_hdr;
1385
1386     // if a CALLBACK or NT header is present, then it is an error
1387     if( httpmsg_find_hdr( request, HDR_CALLBACK, NULL ) != NULL ||
1388         httpmsg_find_hdr( request, HDR_NT, NULL ) != NULL ) {
1389         error_respond( info, HTTP_BAD_REQUEST, request );
1390         return;
1391     }
1392     // get SID
1393     if( httpmsg_find_hdr( request, HDR_SID, &temp_hdr ) == NULL ||
1394         temp_hdr.length > SID_SIZE ) {
1395         error_respond( info, HTTP_PRECONDITION_FAILED, request );
1396         return;
1397     }
1398     memcpy( sid, temp_hdr.buf, temp_hdr.length );
1399     sid[temp_hdr.length] = '\0';
1400
1401     // lookup service by eventURL
1402     membuffer_init( &event_url_path );
1403     if( membuffer_append( &event_url_path, request->uri.pathquery.buff,
1404                           request->uri.pathquery.size ) != 0 ) {
1405         error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request );
1406         return;
1407     }
1408
1409     HandleLock();
1410
1411     // CURRENTLY, ONLY SUPPORT ONE DEVICE
1412     if (GetDeviceHandleInfo(
1413         &device_handle, &handle_info ) != HND_DEVICE ) {
1414         error_respond( info, HTTP_PRECONDITION_FAILED, request );
1415         membuffer_destroy( &event_url_path );
1416         HandleUnlock();
1417         return;
1418     }
1419     service = FindServiceEventURLPath( &handle_info->ServiceTable,
1420                                        event_url_path.buf );
1421     membuffer_destroy( &event_url_path );
1422
1423     // get subscription
1424     if( service == NULL ||
1425         !service->active ||
1426         ( ( sub = GetSubscriptionSID( sid, service ) ) == NULL ) ) {
1427         error_respond( info, HTTP_PRECONDITION_FAILED, request );
1428         HandleUnlock();
1429         return;
1430     }
1431
1432     UpnpPrintf( UPNP_INFO, GENA, __FILE__, __LINE__,
1433         "Renew request: Number of subscriptions already: %d\n "
1434         "Max Subscriptions allowed:%d\n",
1435         service->TotalSubscriptions,
1436         handle_info->MaxSubscriptions );
1437     // too many subscriptions
1438     if( handle_info->MaxSubscriptions != -1 &&
1439             service->TotalSubscriptions > handle_info->MaxSubscriptions ) {
1440         error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request );
1441         RemoveSubscriptionSID( sub->sid, service );
1442         HandleUnlock();
1443         return;
1444     }
1445     // set the timeout
1446     if( httpmsg_find_hdr( request, HDR_TIMEOUT, &timeout_hdr ) != NULL ) {
1447         if( matchstr( timeout_hdr.buf, timeout_hdr.length,
1448                       "%iSecond-%d%0", &time_out ) == PARSE_OK ) {
1449
1450             //nothing
1451
1452         } else if( memptr_cmp_nocase( &timeout_hdr, "Second-infinite" ) ==
1453                    0 ) {
1454
1455             time_out = -1;      // inifinite timeout
1456
1457         } else {
1458             time_out = DEFAULT_TIMEOUT; // default is > 1800 seconds
1459
1460         }
1461     }
1462
1463     // replace infinite timeout with max timeout, if possible
1464     if( handle_info->MaxSubscriptionTimeOut != -1 ) {
1465         if( time_out == -1 ||
1466             time_out > handle_info->MaxSubscriptionTimeOut ) {
1467             time_out = handle_info->MaxSubscriptionTimeOut;
1468         }
1469     }
1470
1471     if( time_out == -1 ) {
1472         sub->expireTime = 0;
1473     } else {
1474         sub->expireTime = time( NULL ) + time_out;
1475     }
1476
1477     if( respond_ok( info, time_out, sub, request ) != UPNP_E_SUCCESS ) {
1478         RemoveSubscriptionSID( sub->sid, service );
1479     }
1480
1481     HandleUnlock();
1482 }
1483
1484
1485 void gena_process_unsubscribe_request(
1486         SOCKINFO *info,
1487         http_message_t *request)
1488 {
1489     Upnp_SID sid;
1490     service_info *service;
1491     struct Handle_Info *handle_info;
1492     UpnpDevice_Handle device_handle;
1493
1494     memptr temp_hdr;
1495     membuffer event_url_path;
1496
1497     // if a CALLBACK or NT header is present, then it is an error
1498     if( httpmsg_find_hdr( request, HDR_CALLBACK, NULL ) != NULL ||
1499         httpmsg_find_hdr( request, HDR_NT, NULL ) != NULL ) {
1500         error_respond( info, HTTP_BAD_REQUEST, request );
1501         return;
1502     }
1503     // get SID
1504     if( httpmsg_find_hdr( request, HDR_SID, &temp_hdr ) == NULL ||
1505         temp_hdr.length > SID_SIZE ) {
1506         error_respond( info, HTTP_PRECONDITION_FAILED, request );
1507         return;
1508     }
1509     memcpy( sid, temp_hdr.buf, temp_hdr.length );
1510     sid[temp_hdr.length] = '\0';
1511
1512     // lookup service by eventURL
1513     membuffer_init( &event_url_path );
1514     if( membuffer_append( &event_url_path, request->uri.pathquery.buff,
1515                           request->uri.pathquery.size ) != 0 ) {
1516         error_respond( info, HTTP_INTERNAL_SERVER_ERROR, request );
1517         return;
1518     }
1519
1520     HandleLock();
1521
1522     // CURRENTLY, ONLY SUPPORT ONE DEVICE
1523     if (GetDeviceHandleInfo(
1524         &device_handle, &handle_info ) != HND_DEVICE ) {
1525         error_respond( info, HTTP_PRECONDITION_FAILED, request );
1526         membuffer_destroy( &event_url_path );
1527         HandleUnlock();
1528         return;
1529     }
1530     service = FindServiceEventURLPath( &handle_info->ServiceTable,
1531                                        event_url_path.buf );
1532     membuffer_destroy( &event_url_path );
1533
1534     // validate service
1535     if( service == NULL ||
1536         !service->active || GetSubscriptionSID( sid, service ) == NULL )
1537     {
1538         error_respond( info, HTTP_PRECONDITION_FAILED, request );
1539         HandleUnlock();
1540         return;
1541     }
1542
1543     RemoveSubscriptionSID(sid, service);
1544     error_respond(info, HTTP_OK, request);    // success
1545
1546     HandleUnlock();
1547 }
1548
1549
1550 #endif /* INCLUDE_DEVICE_APIS */
1551 #endif /* EXCLUDE_GENA */
1552