UPnPsdk 0.1
Universal Plug and Play +, Software Development Kit
 
Loading...
Searching...
No Matches
gena_device.cpp
Go to the documentation of this file.
1/*******************************************************************************
2 *
3 * Copyright (c) 2000-2003 Intel Corporation
4 * All rights reserved.
5 * Copyright (c) 2012 France Telecom All rights reserved.
6 * Copyright (C) 2022+ GPL 3 and higher by Ingo Höft, <Ingo@Hoeft-online.de>
7 * Redistribution only with this Copyright remark. Last modified: 2025-05-29
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions are met:
11 *
12 * - Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above copyright notice,
15 * this list of conditions and the following disclaimer in the documentation
16 * and/or other materials provided with the distribution.
17 * - Neither name of Intel Corporation nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
25 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
29 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 ******************************************************************************/
39#include <assert.h>
40
41#include <gena.hpp>
42#include <httpreadwrite.hpp>
43#include <parsetools.hpp>
44#include <ssdp_common.hpp>
45#include <statcodes.hpp>
46#include <unixutil.hpp>
47#include <upnpapi.hpp>
48#include <uuid.hpp>
50#include <webserver.hpp>
51
53#define STALE_JOBID (INVALID_JOB_ID - 1)
54
56#define PRIzu "zu"
58
59namespace {
60
71 char** names,
73 char** values,
75 int count,
77 DOMString* out) {
78 char* buffer;
79 int counter = 0;
80 size_t size = 0;
81
82 /*size += strlen(XML_VERSION);*/
83 size += strlen(XML_PROPERTYSET_HEADER);
84 size += strlen("</e:propertyset>\n\n");
85 for (counter = 0; counter < count; counter++) {
86 size += strlen("<e:property>\n</e:property>\n");
87 size += 2 * strlen(names[counter]) + strlen(values[counter]) +
88 strlen("<></>\n");
89 }
90
91 size_t buffer_len{size + 1};
92 buffer = (char*)malloc(buffer_len);
93 if (buffer == NULL)
95 memset(buffer, 0, size + 1);
96 /*
97 strcpy(buffer,XML_VERSION);
98 strcat(buffer, XML_PROPERTYSET_HEADER);
99 */
100 strcpy(buffer, XML_PROPERTYSET_HEADER);
101 for (counter = 0; counter < count; counter++) {
102 strcat(buffer, "<e:property>\n");
103 snprintf(&buffer[strlen(buffer)], buffer_len,
104 "<%s>%s</%s>\n</e:property>\n", names[counter],
105 values[counter], names[counter]);
106 }
107 strcat(buffer, "</e:propertyset>\n\n");
108 *out = ixmlCloneDOMString(buffer);
109 free(buffer);
110
111 return XML_SUCCESS;
112}
113
120 notify_thread_struct* input) {
121 (*input->reference_count)--;
122 if (*input->reference_count == 0) {
123 free(input->headers);
125 free(input->servId);
126 free(input->UDN);
127 free(input->reference_count);
128 }
129 free(input);
130}
131
141 uri_type* destination_url,
143 membuffer* mid_msg,
145 char* propertySet,
147 http_parser_t* response) {
148 uri_type url;
149 SOCKET conn_fd;
150 membuffer start_msg;
151 int ret_code;
152 int err_code;
153 int timeout;
154 SOCKINFO info;
155 const char* CRLF = "\r\n";
156
157 /* connect */
158 UpnpPrintf(UPNP_ALL, GENA, __FILE__, __LINE__, "gena notify to: %.*s\n",
159 (int)destination_url->hostport.text.size,
160 destination_url->hostport.text.buff);
161
162 conn_fd = http_Connect(destination_url, &url);
163 if (conn_fd < 0)
164 /* return UPNP error */
166 ret_code = sock_init(&info, conn_fd);
167 if (ret_code) {
168 sock_destroy(&info, SD_BOTH);
169 return ret_code;
170 }
171 /* make start line and HOST header */
172 membuffer_init(&start_msg);
173 if (http_MakeMessage(&start_msg, 1, 1,
174 "q"
175 "s",
176 HTTPMETHOD_NOTIFY, &url, mid_msg->buf) != 0) {
177 membuffer_destroy(&start_msg);
178 sock_destroy(&info, SD_BOTH);
179 return UPNP_E_OUTOF_MEMORY;
180 }
182 /* send msg (note: end of notification will contain "\r\n" twice) */
183 ret_code = http_SendMessage(&info, &timeout, "bbb", start_msg.buf,
184 start_msg.length, propertySet,
185 strlen(propertySet), CRLF, strlen(CRLF));
186 if (ret_code) {
187 membuffer_destroy(&start_msg);
188 sock_destroy(&info, SD_BOTH);
189 return ret_code;
190 }
192 ret_code = http_RecvMessage(&info, response, HTTPMETHOD_NOTIFY, &timeout,
193 &err_code);
194 if (ret_code) {
195 membuffer_destroy(&start_msg);
196 sock_destroy(&info, SD_BOTH);
197 httpmsg_destroy(&response->msg);
198 return ret_code;
199 }
200 /* should shutdown completely when closing socket */
201 sock_destroy(&info, SD_BOTH);
202 membuffer_destroy(&start_msg);
203
204 return UPNP_E_SUCCESS;
205}
206
221 char* headers,
223 char* propertySet,
226 subscription* sub) {
227 size_t i;
228 membuffer mid_msg;
229 uri_type* url;
230 http_parser_t response{};
231 int return_code = -1;
232
233 membuffer_init(&mid_msg);
234 if (http_MakeMessage(&mid_msg, 1, 1,
235 "s"
236 "ssc"
237 "sdcc",
238 headers, "SID: ", sub->sid,
239 "SEQ: ", sub->ToSendEventKey) != 0) {
240 membuffer_destroy(&mid_msg);
241 return UPNP_E_OUTOF_MEMORY;
242 }
243 /* send a notify to each url until one goes thru */
244 for (i = 0; i < sub->DeliveryURLs.size; i++) {
245 url = &sub->DeliveryURLs.parsedURLs[i];
246 return_code =
247 notify_send_and_recv(url, &mid_msg, propertySet, &response);
248 if (return_code == UPNP_E_SUCCESS)
249 break;
250 }
251 membuffer_destroy(&mid_msg);
252 if (return_code == UPNP_E_SUCCESS) {
253 if (response.msg.status_code == HTTP_OK)
254 return_code = GENA_SUCCESS;
255 else {
256 if (response.msg.status_code == HTTP_PRECONDITION_FAILED)
257 /*Invalid SID gets removed */
259 else
260 return_code = GENA_E_NOTIFY_UNACCEPTED;
261 }
262 httpmsg_destroy(&response.msg);
263 }
264
265 return return_code;
266}
267
279 void* input) {
280 subscription* sub;
281 service_info* service;
282 subscription sub_copy;
284 int return_code;
285 struct Handle_Info* handle_info;
286
287 /* This should be a HandleLock and not a HandleReadLock otherwise if
288 * there is a lot of notifications, then multiple threads will acquire a
289 * read lock and the thread which sends the notification will be blocked
290 * forever on the HandleLock at the end of this function. */
291 /*HandleReadLock(); */
292 HandleLock();
293 /* validate context */
294
295 if (GetHandleInfo(in->device_handle, &handle_info) != HND_DEVICE) {
297 HandleUnlock();
298 return;
299 }
300
301 if (((service = FindServiceId(&handle_info->ServiceTable, in->servId,
302 in->UDN)) == 0) ||
303 !service->active ||
304 ((sub = GetSubscriptionSID(in->sid, service)) == 0) ||
305 copy_subscription(sub, &sub_copy) != HTTP_SUCCESS) {
307 HandleUnlock();
308 return;
309 }
310
311 HandleUnlock();
312
313 /* send the notify */
314 return_code = genaNotify(in->headers, in->propertySet, &sub_copy);
315 freeSubscription(&sub_copy);
316 HandleLock();
317 if (GetHandleInfo(in->device_handle, &handle_info) != HND_DEVICE) {
319 HandleUnlock();
320 return;
321 }
322 /* validate context */
323 if (((service = FindServiceId(&handle_info->ServiceTable, in->servId,
324 in->UDN)) == 0) ||
325 !service->active ||
326 ((sub = GetSubscriptionSID(in->sid, service)) == 0)) {
328 HandleUnlock();
329 return;
330 }
331 sub->ToSendEventKey++;
332 if (sub->ToSendEventKey < 0)
333 /* wrap to 1 for overflow */
334 sub->ToSendEventKey = 1;
335
336 /* Remove head of event queue. Possibly activate next */
337 {
338 ListNode* node = ListHead(&sub->outgoing);
339 if (node)
340 ListDelNode(&sub->outgoing, node, 1);
341 if (ListSize(&sub->outgoing) > 0) {
342 ThreadPoolJob* job;
343 ListNode* node2 = ListHead(&sub->outgoing);
344 job = (ThreadPoolJob*)node2->item;
345 /* The new head of queue should not have already been
346 added to the pool, else something is very wrong */
347 assert(job->jobId != STALE_JOBID);
348
349 ThreadPoolAdd(&gSendThreadPool, job, NULL);
350 job->jobId = STALE_JOBID;
351 }
352 }
353
354 if (return_code == GENA_E_NOTIFY_UNACCEPTED_REMOVE_SUB)
355 RemoveSubscriptionSID(in->sid, service);
357
358 HandleUnlock();
359}
360
371 const DOMString propertySet) {
372 static const char* HEADER_LINE_1 =
373 "CONTENT-TYPE: text/xml; charset=\"utf-8\"\r\n";
374 static const char* HEADER_LINE_2A = "CONTENT-LENGTH: ";
375 static const char* HEADER_LINE_2B = "\r\n";
376 static const char* HEADER_LINE_3 = "NT: upnp:event\r\n";
377 static const char* HEADER_LINE_4 = "NTS: upnp:propchange\r\n";
378 char* headers = NULL;
379 size_t headers_size = 0;
380 int line = 0;
381 int rc = 0;
382
383 headers_size = strlen(HEADER_LINE_1) + strlen(HEADER_LINE_2A) +
384 MAX_CONTENT_LENGTH + strlen(HEADER_LINE_2B) +
385 strlen(HEADER_LINE_3) + strlen(HEADER_LINE_4) + 1;
386 headers = (char*)malloc(headers_size);
387 if (headers == NULL) {
388 line = __LINE__;
389 goto ExitFunction;
390 }
391 rc = snprintf(headers, headers_size, "%s%s%" PRIzu "%s%s%s", HEADER_LINE_1,
392 HEADER_LINE_2A, strlen(propertySet) + 2, HEADER_LINE_2B,
393 HEADER_LINE_3, HEADER_LINE_4);
394
395ExitFunction:
396 if (headers == NULL || rc < 0 || (unsigned int)rc >= headers_size) {
397 UpnpPrintf(UPNP_ALL, GENA, __FILE__, line,
398 "AllocGenaHeaders(): Error UPNP_E_OUTOF_MEMORY\n");
399 }
400 return headers;
401}
402
404int genaInitNotifyCommon(UpnpDevice_Handle device_handle, char* UDN,
405 char* servId, DOMString propertySet,
406 const Upnp_SID sid) {
407 int ret = GENA_SUCCESS;
408 int line = 0;
409
410 int* reference_count = NULL;
411 char* UDN_copy = NULL;
412 char* servId_copy = NULL;
413 char* headers = NULL;
414 notify_thread_struct* thread_struct = NULL;
415
416 subscription* sub = NULL;
417 service_info* service = NULL;
418 struct Handle_Info* handle_info;
419 ThreadPoolJob* job = NULL;
420
421 UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__,
422 "GENA BEGIN INITIAL NOTIFY COMMON\n");
423
424 job = (ThreadPoolJob*)malloc(sizeof(ThreadPoolJob));
425 if (job == NULL) {
426 line = __LINE__;
428 goto ExitFunction;
429 }
430 memset(job, 0, sizeof(ThreadPoolJob));
431
432 reference_count = (int*)malloc(sizeof(int));
433 if (reference_count == NULL) {
434 line = __LINE__;
436 goto ExitFunction;
437 }
438 *reference_count = 0;
439
440 UDN_copy = strdup(UDN);
441 if (UDN_copy == NULL) {
442 line = __LINE__;
444 goto ExitFunction;
445 }
446
447 servId_copy = strdup(servId);
448 if (servId_copy == NULL) {
449 line = __LINE__;
451 goto ExitFunction;
452 }
453
454 HandleLock();
455
456 if (GetHandleInfo(device_handle, &handle_info) != HND_DEVICE) {
457 line = __LINE__;
458 ret = GENA_E_BAD_HANDLE;
459 goto ExitFunction;
460 }
461
462 service = FindServiceId(&handle_info->ServiceTable, servId, UDN);
463 if (service == NULL) {
464 line = __LINE__;
465 ret = GENA_E_BAD_SERVICE;
466 goto ExitFunction;
467 }
468 UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__,
469 "FOUND SERVICE IN INIT NOTFY: UDN %s, ServID: %s", UDN, servId);
470
471 sub = GetSubscriptionSID(sid, service);
472 if (sub == NULL || sub->active) {
473 line = __LINE__;
474 ret = GENA_E_BAD_SID;
475 goto ExitFunction;
476 }
477 UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__,
478 "FOUND SUBSCRIPTION IN INIT NOTIFY: SID %s", sid);
479 sub->active = 1;
480
481 headers = AllocGenaHeaders(propertySet);
482 if (headers == NULL) {
483 line = __LINE__;
485 goto ExitFunction;
486 }
487
488 /* schedule thread for initial notification */
489
490 thread_struct = (notify_thread_struct*)malloc(sizeof(notify_thread_struct));
491 if (thread_struct == NULL) {
492 line = __LINE__;
494 } else {
495 *reference_count = 1;
496 thread_struct->servId = servId_copy;
497 thread_struct->UDN = UDN_copy;
498 thread_struct->headers = headers;
499 thread_struct->propertySet = propertySet;
500 memset(thread_struct->sid, 0, sizeof(thread_struct->sid));
501 strncpy(thread_struct->sid, sid, sizeof(thread_struct->sid) - 1);
502 thread_struct->ctime = time(0);
503 thread_struct->reference_count = reference_count;
504 thread_struct->device_handle = device_handle;
505
506 TPJobInit(job, (UPnPsdk::start_routine)genaNotifyThread, thread_struct);
508 TPJobSetPriority(job, MED_PRIORITY);
509
510 ret = ThreadPoolAdd(&gSendThreadPool, job, NULL);
511 if (ret != 0) {
512 if (ret == EOUTOFMEM) {
513 line = __LINE__;
515 }
516 } else {
517 ListNode* node = ListAddTail(&sub->outgoing, job);
518 if (node != NULL) {
519 ((ThreadPoolJob*)node->item)->jobId = STALE_JOBID;
520 line = __LINE__;
521 ret = GENA_SUCCESS;
522 } else {
523 line = __LINE__;
525 }
526 }
527 }
528
529ExitFunction:
530 if (ret != GENA_SUCCESS) {
531 free(job);
532 free(thread_struct);
533 free(headers);
534 ixmlFreeDOMString(propertySet);
535 free(servId_copy);
536 free(UDN_copy);
537 free(reference_count);
538 }
539
540 HandleUnlock();
541
542 UpnpPrintf(UPNP_INFO, GENA, __FILE__, line,
543 "GENA END INITIAL NOTIFY COMMON, ret = %d\n", ret);
544
545 return ret;
546}
547
558 time_t now = time(0L);
560
561 while (ListSize(listp) > 1) {
562 ListNode* node = ListHead(listp);
563 /* The first candidate is the second event: first non-active */
564 if (node == 0 || (node = node->next) == 0) {
565 /* Major inconsistency, really, should abort here. */
566 fprintf(stderr, "gena_device: maybeDiscardEvents: "
567 "list is inconsistent\n");
568 break;
569 }
570
571 ntsp = (notify_thread_struct*)(((ThreadPoolJob*)node->item)->arg);
572 if (ListSize(listp) > g_UpnpSdkEQMaxLen ||
573 now - ntsp->ctime > g_UpnpSdkEQMaxAge) {
574 free_notify_struct(ntsp);
575 free(node->item);
576 ListDelNode(listp, node, 0);
577 } else {
578 /* If the list is smaller than the max and the oldest
579 * task is young enough, stop pruning */
580 break;
581 }
582 }
583}
584
586int genaNotifyAllCommon(UpnpDevice_Handle device_handle, char* UDN,
587 char* servId, DOMString propertySet) {
588 int ret = GENA_SUCCESS;
589 int line = 0;
590
591 int* reference_count = NULL;
592 char* UDN_copy = NULL;
593 char* servId_copy = NULL;
594 char* headers = NULL;
595 notify_thread_struct* thread_s = NULL;
596
597 subscription* finger = NULL;
598 service_info* service = NULL;
599 struct Handle_Info* handle_info;
600
601 UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__,
602 "GENA BEGIN NOTIFY ALL COMMON\n");
603
604 /* Keep this allocation first */
605 reference_count = (int*)malloc(sizeof(int));
606 if (reference_count == NULL) {
607 line = __LINE__;
609 goto ExitFunction;
610 }
611 *reference_count = 0;
612
613 UDN_copy = strdup(UDN);
614 if (UDN_copy == NULL) {
615 line = __LINE__;
617 goto ExitFunction;
618 }
619
620 servId_copy = strdup(servId);
621 if (servId_copy == NULL) {
622 line = __LINE__;
624 goto ExitFunction;
625 }
626
627 headers = AllocGenaHeaders(propertySet);
628 if (headers == NULL) {
629 line = __LINE__;
631 goto ExitFunction;
632 }
633
634 HandleLock();
635
636 if (GetHandleInfo(device_handle, &handle_info) != HND_DEVICE) {
637 line = __LINE__;
638 ret = GENA_E_BAD_HANDLE;
639 } else {
640 service = FindServiceId(&handle_info->ServiceTable, servId, UDN);
641 if (service != NULL) {
642 finger = GetFirstSubscription(service);
643 while (finger) {
644 ThreadPoolJob* job = NULL;
645 ListNode* node;
646
647 thread_s =
649 if (thread_s == NULL) {
650 line = __LINE__;
652 break;
653 }
654
655 (*reference_count)++;
656 thread_s->reference_count = reference_count;
657 thread_s->UDN = UDN_copy;
658 thread_s->servId = servId_copy;
659 thread_s->headers = headers;
660 thread_s->propertySet = propertySet;
661 strncpy(thread_s->sid, finger->sid, sizeof thread_s->sid);
662 thread_s->sid[sizeof thread_s->sid - 1] = 0;
663 thread_s->ctime = time(0);
664 thread_s->device_handle = device_handle;
665
667 job = (ThreadPoolJob*)malloc(sizeof(ThreadPoolJob));
668 if (!job) {
669 free(thread_s);
670 line = __LINE__;
672 break;
673 }
674 memset(job, 0, sizeof(ThreadPoolJob));
675 TPJobInit(job, (UPnPsdk::start_routine)genaNotifyThread,
676 thread_s);
678 TPJobSetPriority(job, MED_PRIORITY);
679 node = ListAddTail(&finger->outgoing, job);
680
681 /* If there is only one element on the list
682 (which we just
683 added), need to kickstart the threadpool */
684 if (ListSize(&finger->outgoing) == 1) {
685 ret = ThreadPoolAdd(&gSendThreadPool, job, NULL);
686 if (ret != 0) {
687 line = __LINE__;
688 if (ret == EOUTOFMEM) {
689 line = __LINE__;
691 }
692 break;
693 }
694 if (node) {
695 ((ThreadPoolJob*)(node->item))->jobId = STALE_JOBID;
696 }
697 }
698 finger = GetNextSubscription(service, finger);
699 }
700 } else {
701 line = __LINE__;
702 ret = GENA_E_BAD_SERVICE;
703 }
704 }
705
706ExitFunction:
707 /* The only case where we want to free memory here is if the
708 struct was never queued. Else, let the normal cleanup take place.
709 reference_count is allocated first so it's ok to do nothing if it's 0
710 */
711 if (reference_count && *reference_count == 0) {
712 free(headers);
713 ixmlFreeDOMString(propertySet);
714 free(servId_copy);
715 free(UDN_copy);
716 free(reference_count);
717 }
718
719 HandleUnlock();
720
721 UpnpPrintf(UPNP_INFO, GENA, __FILE__, line,
722 "GENA END NOTIFY ALL COMMON, ret = %d\n", ret);
723
724 return ret;
725}
726
734 SOCKINFO* info,
736 int time_out,
738 subscription* sub,
740 http_message_t* request) {
741 int major;
742 int minor;
743 membuffer response;
744 int return_code;
745 char timeout_str[100];
746 int upnp_timeout = UPNP_TIMEOUT;
747 int rc = 0;
748
750 &major, &minor);
751
752 if (time_out >= 0) {
753 rc = snprintf(timeout_str, sizeof(timeout_str), "TIMEOUT: Second-%d",
754 time_out);
755 } else {
756 memset(timeout_str, 0, sizeof(timeout_str));
757 strncpy(timeout_str, "TIMEOUT: Second-infinite",
758 sizeof(timeout_str) - 1);
759 }
760 if (rc < 0 || (unsigned int)rc >= sizeof(timeout_str)) {
761 error_respond(info, HTTP_INTERNAL_SERVER_ERROR, request);
762 return UPNP_E_OUTOF_MEMORY;
763 }
764
765 membuffer_init(&response);
766 response.size_inc = 30;
767 if (http_MakeMessage(&response, major, minor,
768 "R"
769 "D"
770 "S"
771 "N"
772 "Xc"
773 "ssc"
774 "scc",
775 HTTP_OK, (off_t)0, X_USER_AGENT, "SID: ", sub->sid,
776 timeout_str) != 0) {
777 membuffer_destroy(&response);
778 error_respond(info, HTTP_INTERNAL_SERVER_ERROR, request);
779 return UPNP_E_OUTOF_MEMORY;
780 }
781
782 return_code = http_SendMessage(info, &upnp_timeout, "b", response.buf,
783 response.length);
784
785 membuffer_destroy(&response);
786
787 return return_code;
788}
789
804 memptr* url_list,
806 URL_list* out) {
807 size_t URLcount = 0, URLcount2 = 0;
808 size_t i;
809 int return_code = 0;
810 uri_type temp;
811 token urls;
812 token* URLS;
813
814 urls.buff = url_list->buf;
815 urls.size = url_list->length;
816 URLS = &urls;
817
818 out->size = 0;
819 out->URLs = NULL;
820 out->parsedURLs = NULL;
821
822 for (i = 0; i < URLS->size; i++) {
823 if ((URLS->buff[i] == '<') && (i + 1 < URLS->size)) {
824 if (((return_code = parse_uri(&URLS->buff[i + 1],
825 URLS->size - i + 1, &temp)) ==
826 HTTP_SUCCESS) &&
827 (temp.hostport.text.size != 0)) {
828 URLcount++;
829 } else {
830 if (return_code == UPNP_E_OUTOF_MEMORY) {
831 return return_code;
832 }
833 }
834 }
835 }
836
837 if (URLcount > 0) {
838 out->URLs = (char*)malloc(URLS->size + 1);
839 out->parsedURLs = (uri_type*)malloc(sizeof(uri_type) * URLcount);
840 if (!out->URLs || !out->parsedURLs) {
841 free(out->URLs);
842 free(out->parsedURLs);
843 out->URLs = NULL;
844 out->parsedURLs = NULL;
845 return UPNP_E_OUTOF_MEMORY;
846 }
847 memcpy(out->URLs, URLS->buff, URLS->size);
848 out->URLs[URLS->size] = 0;
849 for (i = 0; i < URLS->size; i++) {
850 if ((URLS->buff[i] == '<') && (i + 1 < URLS->size)) {
851 if (((return_code = parse_uri(
852 &out->URLs[i + 1], URLS->size - i + 1,
853 &out->parsedURLs[URLcount2])) == HTTP_SUCCESS) &&
854 (out->parsedURLs[URLcount2].hostport.text.size != 0)) {
855 URLcount2++;
856 if (URLcount2 >= URLcount)
857 /*
858 * break early here in case
859 * there is a bogus URL that was
860 * skipped above. This prevents
861 * to access
862 * out->parsedURLs[URLcount]
863 * which is beyond the
864 * allocation.
865 */
866 break;
867 } else {
868 if (return_code == UPNP_E_OUTOF_MEMORY) {
869 free(out->URLs);
870 free(out->parsedURLs);
871 out->URLs = NULL;
872 out->parsedURLs = NULL;
873 return return_code;
874 }
875 }
876 }
877 }
878 }
879 out->size = URLcount;
880
881 return (int)URLcount;
882}
883
884} // namespace
885
886
888 int ret = 0;
889 struct Handle_Info* handle_info;
890
891 HandleLock();
892 if (GetHandleInfo(device_handle, &handle_info) != HND_DEVICE) {
893 UpnpPrintf(UPNP_CRITICAL, GENA, __FILE__, __LINE__,
894 "genaUnregisterDevice: BAD Handle: %d\n", device_handle);
895 ret = GENA_E_BAD_HANDLE;
896 } else {
897 freeServiceTable(&handle_info->ServiceTable);
898 ret = UPNP_E_SUCCESS;
899 }
900 HandleUnlock();
901
902 return ret;
903}
904
906 if (ListSize(&sub->outgoing) > 0) {
907 /* The first event is discarded without dealing
908 notify_thread_struct: there is a mirror ThreadPool entry for
909 this one, and it will take care of the refcount etc. Other
910 entries must be fully cleaned-up here */
911 int first = 1;
912 ListNode* node = ListHead(&sub->outgoing);
913 while (node) {
914 ThreadPoolJob* job = (ThreadPoolJob*)node->item;
915 if (first) {
916 first = 0;
917 } else {
919 }
920 free(node->item);
921 ListDelNode(&sub->outgoing, node, 0);
922 node = ListHead(&sub->outgoing);
923 }
924 }
925}
926
927
928int genaInitNotify(UpnpDevice_Handle device_handle, char* UDN, char* servId,
929 char** VarNames, char** VarValues, int var_count,
930 const Upnp_SID sid) {
931 int ret = GENA_SUCCESS;
932 int line = 0;
933 DOMString propertySet = NULL;
934
935 UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__,
936 "GENA BEGIN INITIAL NOTIFY\n");
937
938 if (var_count <= 0) {
939 line = __LINE__;
940 ret = GENA_SUCCESS;
941 goto ExitFunction;
942 }
943
944 ret = GeneratePropertySet(VarNames, VarValues, var_count, &propertySet);
945 if (ret != XML_SUCCESS) {
946 line = __LINE__;
947 goto ExitFunction;
948 }
949 UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__,
950 "GENERATED PROPERTY SET IN INIT NOTIFY: %s", propertySet);
951
952 ret = genaInitNotifyCommon(device_handle, UDN, servId, propertySet, sid);
953
954ExitFunction:
955
956 UpnpPrintf(UPNP_INFO, GENA, __FILE__, line,
957 "GENA END INITIAL NOTIFY, ret = %d\n", ret);
958
959 return ret;
960}
961
962int genaInitNotifyExt(UpnpDevice_Handle device_handle, char* UDN, char* servId,
963 IXML_Document* PropSet, const Upnp_SID sid) {
964 int ret = GENA_SUCCESS;
965 int line = 0;
966
967 DOMString propertySet = NULL;
968
969 UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__,
970 "GENA BEGIN INITIAL NOTIFY EXT\n");
971
972 if (PropSet == 0) {
973 line = __LINE__;
974 ret = GENA_SUCCESS;
975 goto ExitFunction;
976 }
977
978 propertySet = ixmlPrintNode((IXML_Node*)PropSet);
979 if (propertySet == NULL) {
980 line = __LINE__;
982 goto ExitFunction;
983 }
984 UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__,
985 "GENERATED PROPERTY SET IN INIT EXT NOTIFY: %s", propertySet);
986
987 ret = genaInitNotifyCommon(device_handle, UDN, servId, propertySet, sid);
988
989ExitFunction:
990
991 UpnpPrintf(UPNP_INFO, GENA, __FILE__, line,
992 "GENA END INITIAL NOTIFY EXT, ret = %d\n", ret);
993
994 return ret;
995}
996
997int genaNotifyAllExt(UpnpDevice_Handle device_handle, char* UDN, char* servId,
998 IXML_Document* PropSet) {
999 int ret = GENA_SUCCESS;
1000 int line = 0;
1001
1002 DOMString propertySet = NULL;
1003
1004 UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__,
1005 "GENA BEGIN NOTIFY ALL EXT\n");
1006
1007 propertySet = ixmlPrintNode((IXML_Node*)PropSet);
1008 if (propertySet == NULL) {
1009 line = __LINE__;
1011 goto ExitFunction;
1012 }
1013 UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__,
1014 "GENERATED PROPERTY SET IN EXT NOTIFY: %s", propertySet);
1015
1016 ret = genaNotifyAllCommon(device_handle, UDN, servId, propertySet);
1017
1018ExitFunction:
1019
1020 UpnpPrintf(UPNP_INFO, GENA, __FILE__, line,
1021 "GENA END NOTIFY ALL EXT, ret = %d\n", ret);
1022
1023 return ret;
1024}
1025
1026int genaNotifyAll(UpnpDevice_Handle device_handle, char* UDN, char* servId,
1027 char** VarNames, char** VarValues, int var_count) {
1028 int ret = GENA_SUCCESS;
1029 int line = 0;
1030
1031 DOMString propertySet = NULL;
1032
1033 UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__, "GENA BEGIN NOTIFY ALL\n");
1034
1035 ret = GeneratePropertySet(VarNames, VarValues, var_count, &propertySet);
1036 if (ret != XML_SUCCESS) {
1037 line = __LINE__;
1038 goto ExitFunction;
1039 }
1040 UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__,
1041 "GENERATED PROPERTY SET IN EXT NOTIFY: %s", propertySet);
1042
1043 ret = genaNotifyAllCommon(device_handle, UDN, servId, propertySet);
1044
1045ExitFunction:
1046
1047 UpnpPrintf(UPNP_INFO, GENA, __FILE__, line,
1048 "GENA END NOTIFY ALL, ret = %d\n", ret);
1049
1050 return ret;
1051}
1052
1063 SOCKINFO* info,
1065 URL_list* url_list) {
1066 size_t i = 0;
1067 struct in_addr genaAddr4;
1068 struct in_addr genaNetmask;
1069 struct sockaddr_in* deliveryAddr4 = NULL;
1070 struct in6_addr genaAddr6Lla;
1071 struct in6_addr genaAddr6UlaGua;
1072 struct in6_addr* genaAddr6 = NULL;
1073 unsigned int if_prefix;
1074 struct sockaddr_in6* deliveryAddr6 = NULL;
1075 char deliveryAddrString[INET6_ADDRSTRLEN];
1076
1077 if (info == NULL || url_list == NULL) {
1078 return 0;
1079 }
1080
1081 switch (info->foreign_sockaddr.ss_family) {
1082 case AF_INET:
1083 if (!inet_pton(AF_INET, gIF_IPV4, &genaAddr4)) {
1084 return -1;
1085 }
1086
1087 if (!inet_pton(AF_INET, gIF_IPV4_NETMASK, &genaNetmask)) {
1088 return -1;
1089 }
1090
1091 for (i = 0; i < url_list->size; i++) {
1092 deliveryAddr4 = (struct sockaddr_in*)&url_list->parsedURLs[i]
1094 if ((deliveryAddr4->sin_addr.s_addr & genaNetmask.s_addr) !=
1095 (genaAddr4.s_addr & genaNetmask.s_addr)) {
1096 inet_ntop(AF_INET, &deliveryAddr4->sin_addr, deliveryAddrString,
1097 sizeof(deliveryAddrString));
1098 UpnpPrintf(UPNP_CRITICAL, GENA, __FILE__, __LINE__,
1099 "DeliveryURL %s is invalid.\n"
1100 "It is not in the expected network "
1101 "segment (IPv4: %s, netmask: %s)\n",
1102 deliveryAddrString, gIF_IPV4, gIF_IPV4_NETMASK);
1103 return -1;
1104 }
1105 }
1106 break;
1107 case AF_INET6:
1108 if (!inet_pton(AF_INET6, gIF_IPV6, &genaAddr6Lla)) {
1109 return -1;
1110 }
1111
1112 if (!inet_pton(AF_INET6, gIF_IPV6_ULA_GUA, &genaAddr6UlaGua)) {
1113 return -1;
1114 }
1115
1116 for (i = 0; i < url_list->size; i++) {
1117 deliveryAddr6 = (struct sockaddr_in6*)&url_list->parsedURLs[i]
1119 if (IN6_IS_ADDR_LINKLOCAL(&deliveryAddr6->sin6_addr)) {
1120 genaAddr6 = &genaAddr6Lla;
1121 if_prefix = gIF_IPV6_PREFIX_LENGTH;
1122 } else {
1123 genaAddr6 = &genaAddr6UlaGua;
1125 }
1126 /* We assume that IPv6 prefix is a multiple of 8 */
1127 if (memcmp(deliveryAddr6->sin6_addr.s6_addr, genaAddr6->s6_addr,
1128 if_prefix / 8)) {
1129 inet_ntop(AF_INET6, &deliveryAddr6->sin6_addr,
1130 deliveryAddrString, sizeof(deliveryAddrString));
1131 UpnpPrintf(UPNP_CRITICAL, GENA, __FILE__, __LINE__,
1132 "DeliveryURL %s is invalid.\n"
1133 "It is not in the expected network "
1134 "segment (IPv6: %s, prefix: %d)\n",
1135 deliveryAddrString,
1136 IN6_IS_ADDR_LINKLOCAL(&deliveryAddr6->sin6_addr)
1137 ? gIF_IPV6
1139 if_prefix);
1140 return -1;
1141 }
1142 }
1143 break;
1144 }
1145 return 0;
1146}
1147
1149 http_message_t* request) {
1151 Upnp_SID temp_sid;
1152 int return_code = 1;
1153 int time_out = 1801;
1154 service_info* service;
1155 subscription* sub;
1156 uuid_upnp uid;
1157 struct Handle_Info* handle_info;
1158 void* cookie;
1159 Upnp_FunPtr callback_fun;
1160 UpnpDevice_Handle device_handle;
1161 memptr nt_hdr;
1162 char* event_url_path = NULL;
1163 memptr callback_hdr;
1164 memptr timeout_hdr;
1165 int rc = 0;
1166
1167 UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__,
1168 "Subscription Request Received:\n");
1169
1170 if (httpmsg_find_hdr(request, HDR_NT, &nt_hdr) == NULL) {
1171 error_respond(info, HTTP_BAD_REQUEST, request);
1172 goto exit_function;
1173 }
1174
1175 /* check NT header */
1176 /* Windows Millenium Interoperability: */
1177 /* we accept either upnp:event, or upnp:propchange for the NT header */
1178 if (memptr_cmp_nocase(&nt_hdr, "upnp:event") != 0) {
1179 error_respond(info, HTTP_PRECONDITION_FAILED, request);
1180 goto exit_function;
1181 }
1182
1183 /* if a SID is present then the we have a bad request "incompatible
1184 * headers" */
1185 if (httpmsg_find_hdr(request, HDR_SID, NULL) != NULL) {
1186 error_respond(info, HTTP_BAD_REQUEST, request);
1187 goto exit_function;
1188 }
1189 /* look up service by eventURL */
1190 event_url_path =
1191 str_alloc(request->uri.pathquery.buff, request->uri.pathquery.size);
1192 if (event_url_path == NULL) {
1193 error_respond(info, HTTP_INTERNAL_SERVER_ERROR, request);
1194 goto exit_function;
1195 }
1196
1197 UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__,
1198 "SubscriptionRequest for event URL path: %s\n", event_url_path);
1199
1200 HandleLock();
1201
1203 event_url_path, info->foreign_sockaddr.ss_family, &device_handle,
1204 &handle_info, &service) != HND_DEVICE) {
1205 free(event_url_path);
1206 error_respond(info, HTTP_INTERNAL_SERVER_ERROR, request);
1207 HandleUnlock();
1208 goto exit_function;
1209 }
1210 free(event_url_path);
1211
1212 if (service == NULL || !service->active) {
1213 error_respond(info, HTTP_NOT_FOUND, request);
1214 HandleUnlock();
1215 goto exit_function;
1216 }
1217
1218 UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__,
1219 "Subscription Request: Number of Subscriptions already %d\n "
1220 "Max Subscriptions allowed: %d\n",
1221 service->TotalSubscriptions, handle_info->MaxSubscriptions);
1222
1223 /* too many subscriptions */
1224 if (handle_info->MaxSubscriptions != -1 &&
1225 service->TotalSubscriptions >= handle_info->MaxSubscriptions) {
1226 error_respond(info, HTTP_INTERNAL_SERVER_ERROR, request);
1227 HandleUnlock();
1228 goto exit_function;
1229 }
1230 /* generate new subscription */
1231 sub = (subscription*)malloc(sizeof(subscription));
1232 if (sub == NULL) {
1233 error_respond(info, HTTP_INTERNAL_SERVER_ERROR, request);
1234 HandleUnlock();
1235 goto exit_function;
1236 }
1237 sub->ToSendEventKey = 0;
1238 sub->active = 0;
1239 sub->next = NULL;
1240 sub->DeliveryURLs.size = 0;
1241 sub->DeliveryURLs.URLs = NULL;
1242 sub->DeliveryURLs.parsedURLs = NULL;
1243 if (ListInit(&sub->outgoing, 0, free) != 0) {
1244 error_respond(info, HTTP_INTERNAL_SERVER_ERROR, request);
1245 HandleUnlock();
1246 goto exit_function;
1247 }
1248
1249 /* check for valid callbacks */
1250 if (httpmsg_find_hdr(request, HDR_CALLBACK, &callback_hdr) == NULL) {
1251 error_respond(info, HTTP_PRECONDITION_FAILED, request);
1253 HandleUnlock();
1254 goto exit_function;
1255 }
1256 return_code = create_url_list(&callback_hdr, &sub->DeliveryURLs);
1257 if (return_code == 0) {
1258 error_respond(info, HTTP_PRECONDITION_FAILED, request);
1260 HandleUnlock();
1261 goto exit_function;
1262 }
1263 if (return_code == UPNP_E_OUTOF_MEMORY) {
1264 error_respond(info, HTTP_INTERNAL_SERVER_ERROR, request);
1266 HandleUnlock();
1267 goto exit_function;
1268 }
1269 return_code = gena_validate_delivery_urls(info, &sub->DeliveryURLs);
1270 if (return_code != 0) {
1271 error_respond(info, HTTP_PRECONDITION_FAILED, request);
1273 HandleUnlock();
1274 goto exit_function;
1275 }
1276 /* set the timeout */
1277 if (httpmsg_find_hdr(request, HDR_TIMEOUT, &timeout_hdr) != NULL) {
1278 if (matchstr(timeout_hdr.buf, timeout_hdr.length, "%iSecond-%d%0",
1279 &time_out) == PARSE_OK) {
1280 /* nothing */
1281 } else if (memptr_cmp_nocase(&timeout_hdr, "Second-infinite") == 0) {
1282 /* infinite timeout */
1283 time_out = -1;
1284 } else {
1285 /* default is > 1800 seconds */
1286 time_out = DEFAULT_TIMEOUT;
1287 }
1288 }
1289 /* replace infinite timeout with max timeout, if possible */
1290 if (handle_info->MaxSubscriptionTimeOut != -1) {
1291 if (time_out == -1 || time_out > handle_info->MaxSubscriptionTimeOut) {
1292 time_out = handle_info->MaxSubscriptionTimeOut;
1293 }
1294 }
1295 if (time_out >= 0) {
1296 sub->expireTime = time(NULL) + time_out;
1297 } else {
1298 /* infinite time */
1299 sub->expireTime = 0;
1300 }
1301
1302 /* generate SID */
1303 uuid_create(&uid);
1304 upnp_uuid_unpack(&uid, temp_sid);
1305 rc = snprintf(sub->sid, sizeof(sub->sid), "uuid:%s", temp_sid);
1306
1307 /* respond OK */
1308 if (rc < 0 || (unsigned int)rc >= sizeof(sub->sid) ||
1309 (respond_ok(info, time_out, sub, request) != UPNP_E_SUCCESS)) {
1311 HandleUnlock();
1312 goto exit_function;
1313 }
1314 /* add to subscription list */
1315 sub->next = service->subscriptionList;
1316 service->subscriptionList = sub;
1317 service->TotalSubscriptions++;
1318
1319 /* finally generate callback for init table dump */
1321 service->serviceId);
1322 UpnpSubscriptionRequest_strcpy_UDN(request_struct, service->UDN);
1323 UpnpSubscriptionRequest_strcpy_SID(request_struct, sub->sid);
1324
1325 /* copy callback */
1326 callback_fun = handle_info->Callback;
1327 cookie = handle_info->Cookie;
1328
1329 HandleUnlock();
1330
1331 /* make call back with request struct */
1332 /* in the future should find a way of mainting that the handle */
1333 /* is not unregistered in the middle of a callback */
1334 callback_fun(UPNP_EVENT_SUBSCRIPTION_REQUEST, request_struct, cookie);
1335
1336exit_function:
1337 UpnpSubscriptionRequest_delete(request_struct);
1338}
1339
1341 http_message_t* request) {
1342 Upnp_SID sid;
1343 subscription* sub;
1344 int time_out = 1801;
1345 service_info* service;
1346 struct Handle_Info* handle_info;
1347 UpnpDevice_Handle device_handle;
1348 memptr temp_hdr;
1349 membuffer event_url_path;
1350 memptr timeout_hdr;
1351
1352 /* if a CALLBACK or NT header is present, then it is an error */
1353 if (httpmsg_find_hdr(request, HDR_CALLBACK, NULL) != NULL ||
1354 httpmsg_find_hdr(request, HDR_NT, NULL) != NULL) {
1355 error_respond(info, HTTP_BAD_REQUEST, request);
1356 return;
1357 }
1358 /* get SID */
1359 if (httpmsg_find_hdr(request, HDR_SID, &temp_hdr) == NULL ||
1360 temp_hdr.length > SID_SIZE) {
1361 error_respond(info, HTTP_PRECONDITION_FAILED, request);
1362 return;
1363 }
1364 memcpy(sid, temp_hdr.buf, temp_hdr.length);
1365 sid[temp_hdr.length] = '\0';
1366
1367 /* lookup service by eventURL */
1368 membuffer_init(&event_url_path);
1369 if (membuffer_append(&event_url_path, request->uri.pathquery.buff,
1370 request->uri.pathquery.size) != 0) {
1371 error_respond(info, HTTP_INTERNAL_SERVER_ERROR, request);
1372 return;
1373 }
1374
1375 HandleLock();
1376
1378 event_url_path.buf, info->foreign_sockaddr.ss_family,
1379 &device_handle, &handle_info, &service) != HND_DEVICE) {
1380 error_respond(info, HTTP_PRECONDITION_FAILED, request);
1381 membuffer_destroy(&event_url_path);
1382 HandleUnlock();
1383 return;
1384 }
1385 membuffer_destroy(&event_url_path);
1386
1387 /* get subscription */
1388 if (service == NULL || !service->active ||
1389 ((sub = GetSubscriptionSID(sid, service)) == NULL)) {
1390 error_respond(info, HTTP_PRECONDITION_FAILED, request);
1391 HandleUnlock();
1392 return;
1393 }
1394
1395 UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__,
1396 "Renew request: Number of subscriptions already: %d\n "
1397 "Max Subscriptions allowed:%d\n",
1398 service->TotalSubscriptions, handle_info->MaxSubscriptions);
1399 /* too many subscriptions */
1400 if (handle_info->MaxSubscriptions != -1 &&
1401 service->TotalSubscriptions > handle_info->MaxSubscriptions) {
1402 error_respond(info, HTTP_INTERNAL_SERVER_ERROR, request);
1403 RemoveSubscriptionSID(sub->sid, service);
1404 HandleUnlock();
1405 return;
1406 }
1407 /* set the timeout */
1408 if (httpmsg_find_hdr(request, HDR_TIMEOUT, &timeout_hdr) != NULL) {
1409 if (matchstr(timeout_hdr.buf, timeout_hdr.length, "%iSecond-%d%0",
1410 &time_out) == PARSE_OK) {
1411
1412 /*nothing */
1413
1414 } else if (memptr_cmp_nocase(&timeout_hdr, "Second-infinite") == 0) {
1415
1416 time_out = -1; /* inifinite timeout */
1417
1418 } else {
1419 time_out = DEFAULT_TIMEOUT; /* default is > 1800 seconds */
1420 }
1421 }
1422
1423 /* replace infinite timeout with max timeout, if possible */
1424 if (handle_info->MaxSubscriptionTimeOut != -1) {
1425 if (time_out == -1 || time_out > handle_info->MaxSubscriptionTimeOut) {
1426 time_out = handle_info->MaxSubscriptionTimeOut;
1427 }
1428 }
1429
1430 if (time_out == -1) {
1431 sub->expireTime = 0;
1432 } else {
1433 sub->expireTime = time(NULL) + time_out;
1434 }
1435
1436 if (respond_ok(info, time_out, sub, request) != UPNP_E_SUCCESS) {
1437 RemoveSubscriptionSID(sub->sid, service);
1438 }
1439
1440 HandleUnlock();
1441}
1442
1444 Upnp_SID sid;
1445 service_info* service;
1446 struct Handle_Info* handle_info;
1447 UpnpDevice_Handle device_handle;
1448
1449 memptr temp_hdr;
1450 membuffer event_url_path;
1451
1452 /* if a CALLBACK or NT header is present, then it is an error */
1453 if (httpmsg_find_hdr(request, HDR_CALLBACK, NULL) != NULL ||
1454 httpmsg_find_hdr(request, HDR_NT, NULL) != NULL) {
1455 error_respond(info, HTTP_BAD_REQUEST, request);
1456 return;
1457 }
1458 /* get SID */
1459 if (httpmsg_find_hdr(request, HDR_SID, &temp_hdr) == NULL ||
1460 temp_hdr.length > SID_SIZE) {
1461 error_respond(info, HTTP_PRECONDITION_FAILED, request);
1462 return;
1463 }
1464 memcpy(sid, temp_hdr.buf, temp_hdr.length);
1465 sid[temp_hdr.length] = '\0';
1466
1467 /* lookup service by eventURL */
1468 membuffer_init(&event_url_path);
1469 if (membuffer_append(&event_url_path, request->uri.pathquery.buff,
1470 request->uri.pathquery.size) != 0) {
1471 error_respond(info, HTTP_INTERNAL_SERVER_ERROR, request);
1472 return;
1473 }
1474
1475 HandleLock();
1476
1478 event_url_path.buf, info->foreign_sockaddr.ss_family,
1479 &device_handle, &handle_info, &service) != HND_DEVICE) {
1480 error_respond(info, HTTP_PRECONDITION_FAILED, request);
1481 membuffer_destroy(&event_url_path);
1482 HandleUnlock();
1483 return;
1484 }
1485 membuffer_destroy(&event_url_path);
1486
1487 /* validate service */
1488 if (service == NULL || !service->active ||
1489 GetSubscriptionSID(sid, service) == NULL) {
1490 error_respond(info, HTTP_PRECONDITION_FAILED, request);
1491 HandleUnlock();
1492 return;
1493 }
1494
1495 RemoveSubscriptionSID(sid, service);
1496 error_respond(info, HTTP_OK, request); /* success */
1497
1498 HandleUnlock();
1499}
@ UPNP_EVENT_SUBSCRIPTION_REQUEST
Definition Callback.hpp:83
int(* Upnp_FunPtr)(Upnp_EventType EventType, const void *Event, void *Cookie)
Definition Callback.hpp:143
char Upnp_SID[44]
Holds the subscription identifier for a subscription between a client and a device.
Definition API.hpp:434
int UpnpDevice_Handle
Returned when a device application registers with UpnpRegisterRootDevice(),UpnpRegisterRootDevice2(),...
Definition API.hpp:425
int http_SendMessage(SOCKINFO *info, int *TimeOut, const char *fmt,...)
Sends a message to the destination based on the format parameter.
SOCKET http_Connect(uri_type *destination_url, uri_type *url)
Gets destination address from URL and then connects to the remote end.
void http_CalcResponseVersion(int request_major_vers, int request_minor_vers, int *response_major_vers, int *response_minor_vers)
Calculate HTTP response versions based on the request versions.
int http_RecvMessage(SOCKINFO *info, http_parser_t *parser, http_method_t request_method, int *timeout_secs, int *http_error_code)
Get the data on the socket and take actions based on the read data to modify the parser objects buffe...
int http_MakeMessage(membuffer *buf, int http_major_version, int http_minor_version, const char *fmt,...)
Generate an HTTP message based on the format that is specified in the input parameters.
http_message_t msg
entire raw message
uri_type uri
Type of a uri, e.g. absolute, relative, etc.
int major_version
Http major version.
#define HDR_NT
Type of a HTTP header.
#define HDR_CALLBACK
Type of a HTTP header.
@ PARSE_OK
int minor_version
Http minor version.
#define HDR_TIMEOUT
Type of a HTTP header.
#define HDR_SID
Type of a HTTP header.
Structure of an HTTP parser object.
Structure of an HTTP message.
char * URLs
Dynamic memory for all urls, delimited by <>
Definition uri.hpp:110
hostport_type hostport
Member variable.
Definition uri.hpp:100
uri_type * parsedURLs
parsed URLs
Definition uri.hpp:111
size_t size
Size of the buffer.
Definition uri.hpp:78
size_t size
size
Definition uri.hpp:109
const char * buff
Buffer.
Definition uri.hpp:77
token pathquery
Member variable.
Definition uri.hpp:98
sockaddr_storage IPaddress
Network socket address.
Definition uri.hpp:86
constexpr int HTTP_SUCCESS
Yet another success code.
Definition uri.hpp:70
token text
Pointing to the full host:port string representation.
Definition uri.hpp:85
Represents a list of URLs as in the "callback" header of SUBSCRIBE message in GENA.
Definition uri.hpp:108
Represents a URI used in parse_uri and elsewhere.
Definition uri.hpp:92
Buffer used in parsinghttp messages, urls, etc. Generally this simply holds a pointer into a larger a...
Definition uri.hpp:76
#define X_USER_AGENT
Can be overwritten by configure CFLAGS argument.
Definition webserver.hpp:87
long ListSize(LinkedList *list)
Returns the size of the list.
void * ListDelNode(LinkedList *list, ListNode *dnode, int freeItem)
Removes a node from the list. The memory for the node is freed.
ListNode * ListHead(LinkedList *list)
Returns the head of the list.
ListNode * ListAddTail(LinkedList *list, void *item)
Adds a node to the tail of the list. Node gets added immediately before list.tail.
int ListInit(LinkedList *list, cmp_routine cmp_func, free_function free_func)
Initializes LinkedList. Must be called first and only once for List.
#define EOUTOFMEM
Error condition for "out of memory".
Linked list node. Stores generic item and pointers to next and prev.
Linked list (no protection).
int ThreadPoolAdd(ThreadPool *tp, ThreadPoolJob *job, int *jobId)
Adds a job to the thread pool.
int TPJobSetFreeFunction(ThreadPoolJob *job, free_routine func)
Sets the jobs free function.
int TPJobSetPriority(ThreadPoolJob *job, ThreadPriority priority)
Sets the priority of the threadpool job.
int TPJobInit(ThreadPoolJob *job, UPnPsdk::start_routine func, void *arg)
Initializes thread pool job.
void(* free_routine)(void *arg)
Internal ThreadPool Job.
Header file for UpnpSubscriptionRequest methods.
PUPNP_Api int UpnpSubscriptionRequest_strcpy_SID(UpnpSubscriptionRequest *p, const char *s)
PUPNP_Api UpnpSubscriptionRequest * UpnpSubscriptionRequest_new(void)
PUPNP_Api void UpnpSubscriptionRequest_delete(UpnpSubscriptionRequest *p)
PUPNP_Api int UpnpSubscriptionRequest_strcpy_UDN(UpnpSubscriptionRequest *p, const char *s)
PUPNP_Api int UpnpSubscriptionRequest_strcpy_ServiceId(UpnpSubscriptionRequest *p, const char *s)
#define GENA_NOTIFICATION_SENDING_TIMEOUT
The GENA_NOTIFICATION_SENDING_TIMEOUT specifies the number of seconds to wait for sending GENA notifi...
Definition config.hpp:229
#define GENA_NOTIFICATION_ANSWERING_TIMEOUT
The GENA_NOTIFICATION_ANSWERING_TIMEOUT specifies the number of seconds to wait for receiving the ans...
Definition config.hpp:246
Manage Eventing with GENA, the General Event Notification Architecture.
#define GENA_E_BAD_HANDLE
Definition gena.hpp:79
UpnpDevice_Handle device_handle
Definition gena.hpp:102
#define GENA_SUCCESS
Definition gena.hpp:83
#define XML_PROPERTYSET_HEADER
Definition gena.hpp:52
#define XML_SUCCESS
Definition gena.hpp:82
#define GENA_E_NOTIFY_UNACCEPTED
Definition gena.hpp:77
#define GENA_E_NOTIFY_UNACCEPTED_REMOVE_SUB
Definition gena.hpp:78
#define MAX_CONTENT_LENGTH
Definition gena.hpp:67
#define DEFAULT_TIMEOUT
Definition gena.hpp:86
DOMString propertySet
Definition gena.hpp:96
#define GENA_E_BAD_SID
Definition gena.hpp:75
#define GENA_E_BAD_SERVICE
Definition gena.hpp:73
void error_respond(SOCKINFO *info, int error_code, http_message_t *hmsg)
Sends an error message to the control point in the case of incorrect GENA requests.
void freeSubscriptionQueuedEvents(subscription *sub)
???
void gena_process_subscription_request(SOCKINFO *info, http_message_t *request)
Handles a subscription request from a ctrl point. The socket is not closed on return.
int genaNotifyAll(UpnpDevice_Handle device_handle, char *UDN, char *servId, char **VarNames, char **VarValues, int var_count)
Sends a notification to all the subscribed control points.
void gena_process_unsubscribe_request(SOCKINFO *info, http_message_t *request)
Handles a subscription cancellation request from a ctrl point. The connection is not destroyed on ret...
int genaUnregisterDevice(UpnpDevice_Handle device_handle)
Cleans the service table of the device.
void gena_process_subscription_renewal_request(SOCKINFO *info, http_message_t *request)
Handles a subscription renewal request from a ctrl point. The connection is not destroyed on return.
int gena_validate_delivery_urls(SOCKINFO *info, URL_list *url_list)
Validate that the URLs passed by the user are on the same network segment than the device.
int genaNotifyAllExt(UpnpDevice_Handle device_handle, char *UDN, char *servId, IXML_Document *PropSet)
Sends a notification to all the subscribed control points.
int genaInitNotifyExt(UpnpDevice_Handle device_handle, char *UDN, char *servId, IXML_Document *PropSet, const Upnp_SID sid)
Similar to the genaInitNofity. The only difference is that it takes the xml document for the state ta...
#define STALE_JOBID
Invalid job id.
int genaInitNotify(UpnpDevice_Handle device_handle, char *UDN, char *servId, char **VarNames, char **VarValues, int var_count, const Upnp_SID sid)
Sends the intial state table dump to newly subscribed control point.
Data structure common to all types of nodes.
Definition ixml.hpp:132
Data structure representing the DOM Document.
Definition ixml.hpp:155
PUPNP_Api DOMString ixmlPrintNode(IXML_Node *doc)
Renders a Node and all sub-elements into an XML text representation.
Definition ixml.cpp:338
#define DOMString
The type of DOM strings.
Definition ixml.hpp:47
PUPNP_Api void ixmlFreeDOMString(DOMString buf)
Frees a DOMString.
Definition ixml.cpp:418
PUPNP_Api DOMString ixmlCloneDOMString(const DOMString src)
Clones an existing DOMString.
Definition ixml.cpp:410
http_header_t * httpmsg_find_hdr(http_message_t *msg, int header_name_id, memptr *value)
Finds header from a list, with the given 'name_id'.
void httpmsg_destroy(http_message_t *msg)
Free memory allocated for the http message.
parse_status_t matchstr(char *str, size_t slen, const char *fmt,...)
Matches a variable parameter list with a string and takes actions based on the data type specified.
void membuffer_destroy(membuffer *m)
Free's memory allocated for membuffer* m.
char * str_alloc(const char *str, size_t str_len)
Allocate memory and copy information from the input string to the newly allocated memory.
Definition membuffer.cpp:56
void membuffer_init(membuffer *m)
Wrapper to membuffer_initialize().
int membuffer_append(membuffer *m, const void *buf, size_t buf_len)
Invokes function to appends data from a constant buffer to the buffer.
int memptr_cmp_nocase(memptr *m, const char *s)
Compares characters of 2 strings irrespective of the case for a specific count of bytes.
Definition membuffer.cpp:84
size_t size_inc
used to increase size; MUST be > 0; (read/write).
Definition membuffer.hpp:69
size_t length
length of buffer without terminating null byte (read-only).
Definition membuffer.hpp:65
char * buf
mem buffer; must not write beyond buf[length-1] (read/write).
Definition membuffer.hpp:63
size_t length
length of memory (read-only).
Definition membuffer.hpp:54
char * buf
start of memory (read/write).
Definition membuffer.hpp:52
pointer to a chunk of memory.
Definition membuffer.hpp:50
Maintains a block of dynamically allocated memory.
Definition membuffer.hpp:61
#define UPNP_E_SOCKET_CONNECT
The SDK had a problem connecting to a remote host.
Definition messages.hpp:209
#define UPNP_E_SUCCESS
The operation completed successfully.
Definition messages.hpp:27
#define UPNP_E_OUTOF_MEMORY
Not enough resources are currently available to complete the operation.
Definition messages.hpp:57
#define UPNP_E_INVALID_PARAM
One or more of the parameters passed to the function is not valid.
Definition messages.hpp:40
int respond_ok(SOCKINFO *info, int time_out, subscription *sub, http_message_t *request)
Returns OK message in the case of a subscription request.
int genaInitNotifyCommon(UpnpDevice_Handle device_handle, char *UDN, char *servId, DOMString propertySet, const Upnp_SID sid)
We take ownership of propertySet and will free it.
char * AllocGenaHeaders(const DOMString propertySet)
Allocates the GENA header.
void maybeDiscardEvents(LinkedList *listp)
This gets called before queuing a new event.
int genaNotify(char *headers, char *propertySet, subscription *sub)
Function to Notify a particular subscription of a particular event.
int GeneratePropertySet(char **names, char **values, int count, DOMString *out)
Generates XML property set for notifications.
int genaNotifyAllCommon(UpnpDevice_Handle device_handle, char *UDN, char *servId, DOMString propertySet)
We take ownership of propertySet and will free it.
int notify_send_and_recv(uri_type *destination_url, membuffer *mid_msg, char *propertySet, http_parser_t *response)
Sends the notify message and returns a reply.
void free_notify_struct(notify_thread_struct *input)
Frees memory used in notify_threads if the reference count is 0, otherwise decrements the refrence co...
void genaNotifyThread(void *input)
Thread job to Notify a control point.
int create_url_list(memptr *url_list, URL_list *out)
Function to parse the Callback header value in subscription requests.
Function to extract header information from an http message and match the data with XML data.
subscription * GetFirstSubscription(service_info *service)
Gets pointer to the first subscription node in the service table.
subscription * GetSubscriptionSID(const Upnp_SID sid, service_info *service)
Return the subscription from the service table that matches const Upnp_SID sid value.
int copy_subscription(subscription *in, subscription *out)
Makes a copy of the subscription.
void freeServiceTable(service_table *table)
Free's dynamic memory in table (does not free table, only memory within the structure).
void freeSubscription(subscription *sub)
Free's the memory allocated for storing the URL of the subscription.
void RemoveSubscriptionSID(Upnp_SID sid, service_info *service)
Remove the subscription from the service table and update it.
void freeSubscriptionList(subscription *head)
Free's memory allocated for all the subscriptions in the service table.
subscription * GetNextSubscription(service_info *service, subscription *current)
Get current and valid subscription from the service table.
service_info * FindServiceId(service_table *table, const char *serviceId, const char *UDN)
Traverses through the service table to find a service.
int TotalSubscriptions
Part of Service information.
URL_list DeliveryURLs
Part of subscription.
int active
Part of subscription.
struct subscription * next
Part of subscription.
Upnp_SID sid
Part of subscription.
subscription * subscriptionList
Part of Service information.
DOMString UDN
Part of Service information.
DOMString serviceId
Part of Service information.
LinkedList outgoing
List of queued events for this subscription.
int ToSendEventKey
Part of subscription.
#define SID_SIZE
???
time_t expireTime
Part of subscription.
int active
Part of Service information.
device subscriptions
-brief Service information
int sock_destroy(SOCKINFO *info, int ShutdownMethod)
Shutsdown the socket using the ShutdownMethod to indicate whether sends and receives on the socket wi...
Definition sock.cpp:489
int sock_init(SOCKINFO *info, SOCKET sockfd)
Assign the passed in socket descriptor to socket descriptor in the SOCKINFO structure.
Definition sock.cpp:440
sockaddr_storage foreign_sockaddr
Socket address of the remote node only filled in incoming requests.
Definition sock.hpp:69
Additional socket information for connections and ssl.
Definition sock.hpp:65
Manage "Step 1: Discovery" of the UPnP+™ specification with SSDP.
HTTP status codes.
Unix-specific network utilities.
unsigned gIF_IPV6_ULA_GUA_PREFIX_LENGTH
IPv6 ULA or GUA prefix length. (extern'ed in upnp.h)
Definition upnpapi.cpp:308
Upnp_Handle_Type GetHandleInfo(UpnpClient_Handle Hnd, Handle_Info **HndInfo)
Get handle information.
Definition upnpapi.cpp:3342
ThreadPool gSendThreadPool
Send thread pool.
Definition upnpapi.cpp:259
char gIF_IPV6_ULA_GUA[INET6_ADDRSTRLEN]
Static buffer to contain interface IPv6 unique-local or globally-unique address (ULA or GUA)....
Definition upnpapi.cpp:305
Upnp_Handle_Type GetDeviceHandleInfoForPath(const char *path, int AddressFamily, UpnpDevice_Handle *device_handle_out, struct Handle_Info **HndInfo, service_info **serv_info)
Retrieves the device handle and information of the first device of the address family specified,...
Definition upnpapi.cpp:3305
unsigned gIF_IPV6_PREFIX_LENGTH
IPv6 LLA prefix length. (extern'ed in upnp.h)
Definition upnpapi.cpp:295
int g_UpnpSdkEQMaxLen
Global variable to determines the maximum number of events.
Definition upnpapi.cpp:335
char gIF_IPV4[INET_ADDRSTRLEN]
Static buffer to contain interface IPv4 address. (extern'ed in upnp.h)
Definition upnpapi.cpp:284
char gIF_IPV4_NETMASK[INET_ADDRSTRLEN]
Static buffer to contain interface IPv4 netmask. (extern'ed in upnp.h)
Definition upnpapi.cpp:288
int g_UpnpSdkEQMaxAge
Global variable to determine the maximum number of seconds which an event can spend on a subscription...
Definition upnpapi.cpp:346
char gIF_IPV6[INET6_ADDRSTRLEN]
Static buffer to contain interface IPv6 link-local address (LLA). (extern'ed in upnp....
Definition upnpapi.cpp:292
Inititalize the compatible library before it can be used.
#define HandleLock()
HandleLock.
Definition upnpapi.hpp:138
service_table ServiceTable
Table holding subscriptions and URL information.
Definition upnpapi.hpp:108
#define UPNP_TIMEOUT
UPNP_TIMEOUT.
Definition upnpapi.hpp:71
#define HandleUnlock()
HandleUnlock.
Definition upnpapi.hpp:147
char * Cookie
???
Definition upnpapi.hpp:87
Upnp_FunPtr Callback
Callback function pointer.
Definition upnpapi.hpp:86
int MaxSubscriptionTimeOut
???
Definition upnpapi.hpp:110
int MaxSubscriptions
???
Definition upnpapi.hpp:109
Data to be stored in handle table for Handle Info.
Definition upnpapi.hpp:84
UPnPsdk_VIS void UpnpPrintf(Upnp_LogLevel DLevel, Dbg_Module Module, const char *DbgFileName, int DbgLineNo, const char *FmtStr,...)
Prints the debug statement.
int parse_uri(const char *in, size_t max, uri_type *out)
Parses a uri as defined in RFC 2396 (explaining URIs).
Definition uri.cpp:733
Manage UUIDs.
void upnp_uuid_unpack(uuid_upnp *u, char *out)
Unpack a UUID.
Definition uuid.cpp:242
int uuid_create(uuid_upnp *uid)
Generate a UUID.
Definition uuid.cpp:211
uuid UPNP
Definition uuid.hpp:42