1 /*******************************************************************************
2 *
3 * Copyright (c) 2000-2003 Intel Corporation
4 * All rights reserved.
5 * Copyright (c) 2012 France Telecom All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * - Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 * - Neither name of Intel Corporation nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
27 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 ******************************************************************************/
32
33
34 #include "config.h"
35
36 #if EXCLUDE_GENA == 0
37 #ifdef INCLUDE_CLIENT_APIS
38
39 #include <curl/curl.h>
40
41 #include <string>
42 #include <map>
43 #include <sstream>
44
45 #include "gena.h"
46 #include "httputils.h"
47 #include "statcodes.h"
48 #include "upnpapi.h"
49 #include "upnp_timeout.h"
50 #include "genut.h"
51 #include "TimerThread.h"
52 #include "gena_sids.h"
53 #include "netif.h"
54
55 #ifdef USE_EXPAT
56 #include "expatmm.hxx"
57 #define XMLPARSERTP inputRefXMLParser
58 #else
59 #include "picoxml.h"
60 #define XMLPARSERTP PicoXMLParser
61 #endif
62
63 extern TimerThread *gTimerThread;
64
clientCancelRenew(ClientSubscription * sub)65 static void clientCancelRenew(ClientSubscription *sub)
66 {
67 if (nullptr == sub) {
68 return;
69 }
70 int renewEventId = sub->renewEventId;
71 sub->renewEventId = -1;
72 sub->actualSID.clear();
73 sub->eventURL.clear();
74 if (renewEventId != -1) {
75 gTimerThread->remove(renewEventId);
76 }
77 }
78
79 /*!
80 * \brief This is a thread function to send the renewal just before the
81 * subscription times out.
82 */
thread_autorenewsubscription(void * input)83 static void *thread_autorenewsubscription(
84 /*! [in] Thread data(upnp_timeout *) needed to send the renewal. */
85 void *input)
86 {
87 auto event = static_cast<upnp_timeout *>(input);
88 auto sub_struct = static_cast<struct Upnp_Event_Subscribe *>(event->Event);
89 int send_callback = 0;
90 Upnp_EventType eventType = UPNP_EVENT_AUTORENEWAL_FAILED;
91
92 if (AUTO_RENEW_TIME == 0) {
93 // We are compile-time configured for no auto-renewal.
94 UpnpPrintf(UPNP_INFO, GENA, __FILE__, __LINE__, "GENA SUB EXPIRED\n");
95 sub_struct->ErrCode = UPNP_E_SUCCESS;
96 send_callback = 1;
97 eventType = UPNP_EVENT_SUBSCRIPTION_EXPIRED;
98 } else {
99 UpnpPrintf(UPNP_DEBUG, GENA, __FILE__, __LINE__, "GENA AUTO RENEW\n");
100 int timeout = sub_struct->TimeOut;
101 std::string tmpSID = sub_struct->Sid;
102 int errCode = genaRenewSubscription(event->handle, tmpSID, &timeout);
103 sub_struct->ErrCode = errCode;
104 sub_struct->TimeOut = timeout;
105 if (errCode != UPNP_E_SUCCESS &&
106 errCode != GENA_E_BAD_SID &&
107 errCode != GENA_E_BAD_HANDLE) {
108 send_callback = 1;
109 }
110 }
111
112 if (send_callback) {
113 HandleReadLock();
114 struct Handle_Info *handle_info;
115 if (GetHandleInfo(event->handle, &handle_info) != HND_CLIENT) {
116 HandleUnlock();
117 free_upnp_timeout(event);
118 goto end_function;
119 }
120
121 /* make callback */
122 Upnp_FunPtr callback_fun = handle_info->Callback;
123 HandleUnlock();
124 callback_fun(eventType, event->Event, handle_info->Cookie);
125 }
126
127 end_function:
128 return nullptr;
129 }
130
131
132 /*!
133 * \brief Schedules a job to renew the subscription just before time out.
134 *
135 * \return GENA_E_SUCCESS if successful, otherwise returns the appropriate
136 * error code.
137 */
ScheduleGenaAutoRenew(int client_handle,int TimeOut,ClientSubscription * sub)138 static int ScheduleGenaAutoRenew(
139 /*! [in] Handle that also contains the subscription list. */
140 int client_handle,
141 /*! [in] The time out value of the subscription. */
142 int TimeOut,
143 /*! [in] Subscription being renewed. */
144 ClientSubscription *sub)
145 {
146 struct Upnp_Event_Subscribe *RenewEventStruct = nullptr;
147 upnp_timeout *RenewEvent = nullptr;
148 int return_code = GENA_SUCCESS;
149 const std::string& tmpSID = sub->SID;
150 const std::string& tmpEventURL = sub->eventURL;
151
152 if (TimeOut == UPNP_INFINITE) {
153 return_code = GENA_SUCCESS;
154 goto end_function;
155 }
156
157 RenewEventStruct = static_cast<struct Upnp_Event_Subscribe *>(malloc(
158 sizeof(struct Upnp_Event_Subscribe)));
159 if (RenewEventStruct == nullptr) {
160 return_code = UPNP_E_OUTOF_MEMORY;
161 goto end_function;
162 }
163
164 RenewEvent = new upnp_timeout;
165 if (nullptr == RenewEvent) {
166 free(RenewEventStruct);
167 return_code = UPNP_E_OUTOF_MEMORY;
168 goto end_function;
169 }
170
171 /* schedule expire event */
172 *RenewEventStruct = {};
173 RenewEventStruct->ErrCode = UPNP_E_SUCCESS;
174 RenewEventStruct->TimeOut = TimeOut;
175 upnp_strlcpy(RenewEventStruct->Sid, tmpSID, sizeof(RenewEventStruct->Sid));
176 upnp_strlcpy(RenewEventStruct->PublisherUrl, tmpEventURL, NAME_SIZE);
177
178 RenewEvent->handle = client_handle;
179 RenewEvent->Event = RenewEventStruct;
180
181 /* Schedule the job */
182 return_code = gTimerThread->schedule(
183 TimerThread::SHORT_TERM, TimerThread::REL_SEC, TimeOut - AUTO_RENEW_TIME,
184 &(RenewEvent->eventId), thread_autorenewsubscription, RenewEvent,
185 reinterpret_cast<ThreadPool::free_routine>(free_upnp_timeout));
186
187 if (return_code != UPNP_E_SUCCESS) {
188 free_upnp_timeout(RenewEvent);
189 goto end_function;
190 }
191
192 sub->renewEventId = RenewEvent->eventId;
193
194 return_code = GENA_SUCCESS;
195
196 end_function:
197 return return_code;
198 }
199
200
201 /*!
202 * \brief Sends the UNSUBCRIBE gena request and recieves the response from the
203 * device and returns it as a parameter.
204 *
205 * \returns 0 if successful, otherwise returns the appropriate error code.
206 */
gena_unsubscribe(const std::string & url,const std::string & sid)207 static int gena_unsubscribe(
208 /*! [in] Event URL of the service. */
209 const std::string& url,
210 /*! [in] The subcription ID. */
211 const std::string& sid)
212 {
213 int return_code;
214 uri_type dest_url;
215
216 /* parse url */
217 return_code = http_FixStrUrl(url, &dest_url);
218 if (return_code != 0) {
219 return return_code;
220 }
221
222 CURL *easy = curl_easy_init();
223 char curlerrormessage[CURL_ERROR_SIZE];
224 curl_easy_setopt(easy, CURLOPT_ERRORBUFFER, curlerrormessage);
225 curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, write_callback_null_curl);
226 curl_easy_setopt(easy, CURLOPT_CUSTOMREQUEST, "UNSUBSCRIBE");
227 std::string surl = uri_asurlstr(dest_url);
228 curl_easy_setopt(easy, CURLOPT_URL, surl.c_str());
229 curl_easy_setopt(easy, CURLOPT_TIMEOUT, HTTP_DEFAULT_TIMEOUT);
230
231 struct curl_slist *list = nullptr;
232 list = curl_slist_append(list, (std::string("SID: ") + sid).c_str());
233 list = curl_slist_append(list, (std::string("USER-AGENT: ") + get_sdk_client_info()).c_str());
234 curl_easy_setopt(easy, CURLOPT_HTTPHEADER, list);
235
236 CURLcode code = curl_easy_perform(easy);
237
238 if (code != CURLE_OK) {
239 curl_easy_cleanup(easy);
240 curl_slist_free_all(list);
241 /* We may want to detail things here, depending on the curl error */
242 UpnpPrintf(UPNP_ERROR,GENA,__FILE__,__LINE__, "CURL ERROR MESSAGE %s\n", curlerrormessage);
243 return UPNP_E_SOCKET_CONNECT;
244 }
245
246 long http_status;
247 curl_easy_getinfo (easy, CURLINFO_RESPONSE_CODE, &http_status);
248
249 curl_easy_cleanup(easy);
250 curl_slist_free_all(list);
251
252 if (http_status != HTTP_OK) {
253 return_code = UPNP_E_UNSUBSCRIBE_UNACCEPTED;
254 }
255
256 return return_code;
257 }
258
259 // localaddr is already in inet_ntop-provided dot or ipv6 format
myCallbackUrl(NetIF::IPAddr & netaddr)260 static std::string myCallbackUrl(NetIF::IPAddr& netaddr)
261 {
262 std::ostringstream oss;
263 oss << "http://";
264 if (netaddr.family() == NetIF::IPAddr::Family::IPV6) {
265 oss << "[";
266 }
267 oss << netaddr.straddr();
268 if (netaddr.family() == NetIF::IPAddr::Family::IPV6) {
269 oss << "]";
270 }
271 oss << ":" << (netaddr.family() == NetIF::IPAddr::Family::IPV6 ? LOCAL_PORT_V6 : LOCAL_PORT_V4);
272 return oss.str();
273 }
274
275 struct CurlGuard {
276 CURL *htalk{nullptr};
277 struct curl_slist *hlist{nullptr};
~CurlGuardCurlGuard278 ~CurlGuard() {
279 if (htalk) {
280 curl_easy_cleanup(htalk);
281 }
282 if (hlist) {
283 curl_slist_free_all(hlist);
284 }
285 }
286 };
287
288 /*!
289 * \brief Subscribes or renew subscription.
290 *
291 * \return 0 if successful, otherwise returns the appropriate error code.
292 */
gena_subscribe(const std::string & url,int * timeout,const std::string & renewal_sid,std::string * sid)293 static int gena_subscribe(
294 /*! [in] URL of service to subscribe. */
295 const std::string& url,
296 /*! [in,out] Subscription time desired (in secs). */
297 int *timeout,
298 /*! [in] for renewal, this contains a currently held subscription SID.
299 * For first time subscription, this must be empty. */
300 const std::string& renewal_sid,
301 /*! [out] SID returned by the subscription or renew msg. */
302 std::string *sid)
303 {
304 int local_timeout = CP_MINIMUM_SUBSCRIPTION_TIME;
305
306 sid->clear();
307
308 /* request timeout to string */
309 if (timeout == nullptr) {
310 timeout = &local_timeout;
311 }
312 std::ostringstream timostr;
313 if (*timeout < 0) {
314 timostr << "infinite";
315 } else if (*timeout < CP_MINIMUM_SUBSCRIPTION_TIME) {
316 timostr << CP_MINIMUM_SUBSCRIPTION_TIME;
317 } else {
318 timostr << *timeout;
319 }
320
321 /* parse url */
322 uri_type dest_url;
323 int return_code = http_FixStrUrl(url, &dest_url);
324 if (return_code != 0) {
325 return return_code;
326 }
327 std::string urlforcurl = uri_asurlstr(dest_url);
328 NetIF::IPAddr destaddr(
329 reinterpret_cast<struct sockaddr*>(&dest_url.hostport.IPaddress));
330 NetIF::IPAddr myaddr;
331 const NetIF::Interface *ifp =
332 NetIF::Interfaces::theInterfaces()->interfaceForAddress(destaddr,myaddr);
333 if (nullptr == ifp) {
334 UpnpPrintf(UPNP_ERROR, GENA, __FILE__, __LINE__,
335 "Could not find the interface for the destination address\n");
336 return UPNP_E_SOCKET_CONNECT;
337 }
338
339 CurlGuard hdls;
340 std::map<std::string, std::string> http_headers;
341 char curlerrormessage[CURL_ERROR_SIZE];
342
343 hdls.htalk = curl_easy_init();
344 curl_easy_setopt(hdls.htalk, CURLOPT_ERRORBUFFER, curlerrormessage);
345 curl_easy_setopt(hdls.htalk, CURLOPT_WRITEFUNCTION,write_callback_null_curl);
346 curl_easy_setopt(hdls.htalk, CURLOPT_CUSTOMREQUEST, "SUBSCRIBE");
347 curl_easy_setopt(hdls.htalk, CURLOPT_URL, urlforcurl.c_str());
348 curl_easy_setopt(hdls.htalk, CURLOPT_TIMEOUT, HTTP_DEFAULT_TIMEOUT);
349 curl_easy_setopt(hdls.htalk, CURLOPT_HEADERFUNCTION, header_callback_curl);
350 curl_easy_setopt(hdls.htalk, CURLOPT_HEADERDATA, &http_headers);
351 if (renewal_sid.empty()) {
352 std::string cbheader{"CALLBACK: <"};
353 cbheader += myCallbackUrl(myaddr) + "/>";
354 hdls.hlist = curl_slist_append(hdls.hlist, cbheader.c_str());
355 UpnpPrintf(UPNP_DEBUG, GENA, __FILE__, __LINE__,
356 "gena_subscribe: callback: %s\n", cbheader.c_str());
357 hdls.hlist = curl_slist_append(hdls.hlist, "NT: upnp:event");
358 } else {
359 hdls.hlist = curl_slist_append(
360 hdls.hlist, (std::string("SID: ") + renewal_sid).c_str());
361 }
362 hdls.hlist = curl_slist_append(
363 hdls.hlist, (std::string("TIMEOUT: Second-") + timostr.str()).c_str());
364 hdls.hlist = curl_slist_append(
365 hdls.hlist, (std::string("USER-AGENT: ")+get_sdk_client_info()).c_str());
366 curl_easy_setopt(hdls.htalk, CURLOPT_HTTPHEADER, hdls.hlist);
367
368 CURLcode curlcode = curl_easy_perform(hdls.htalk);
369 if (curlcode != CURLE_OK) {
370 /* We may want to detail things here, depending on the curl error */
371 UpnpPrintf(UPNP_ERROR,GENA,__FILE__,__LINE__, "CURL ERROR MESSAGE %s\n", curlerrormessage);
372 return UPNP_E_SOCKET_CONNECT;
373 }
374
375 long http_status;
376 curl_easy_getinfo (hdls.htalk, CURLINFO_RESPONSE_CODE, &http_status);
377 if (http_status != HTTP_OK) {
378 return UPNP_E_SUBSCRIBE_UNACCEPTED;
379 }
380
381 /* get SID and TIMEOUT. the header callback lowercases the header names */
382 const auto itsid = http_headers.find("sid");
383 const auto ittimeout = http_headers.find("timeout");
384 if (itsid == http_headers.end() || ittimeout == http_headers.end()) {
385 return UPNP_E_BAD_RESPONSE;
386 }
387
388 /* save timeout */
389 if (!timeout_header_value(http_headers, timeout)) {
390 return UPNP_E_BAD_RESPONSE;
391 }
392
393 /* save SID */
394 *sid = itsid->second;
395
396 return UPNP_E_SUCCESS;
397 }
398
399
genaUnregisterClient(UpnpClient_Handle client_handle)400 int genaUnregisterClient(UpnpClient_Handle client_handle)
401 {
402 int return_code = UPNP_E_SUCCESS;
403 struct Handle_Info *handle_info = nullptr;
404
405 while (true) {
406 HandleLock();
407
408 if (GetHandleInfo(client_handle, &handle_info) != HND_CLIENT) {
409 HandleUnlock();
410 return GENA_E_BAD_HANDLE;
411 }
412 if (handle_info->ClientSubList.empty()) {
413 return_code = UPNP_E_SUCCESS;
414 break;
415 }
416 ClientSubscription sub_copy = handle_info->ClientSubList.front();
417 RemoveClientSubClientSID(handle_info->ClientSubList, sub_copy.SID);
418
419 HandleUnlock();
420
421 gena_unsubscribe(sub_copy.eventURL, sub_copy.actualSID);
422 clientCancelRenew(&sub_copy);
423 }
424
425 handle_info->ClientSubList.clear();
426 HandleUnlock();
427
428 return return_code;
429 }
430
431
genaUnSubscribe(UpnpClient_Handle client_handle,const std::string & in_sid)432 int genaUnSubscribe(
433 UpnpClient_Handle client_handle,
434 const std::string& in_sid)
435 {
436 ClientSubscription *sub = nullptr;
437 int return_code = GENA_SUCCESS;
438 struct Handle_Info *handle_info;
439 ClientSubscription sub_copy;
440
441 /* validate handle and sid */
442 HandleLock();
443 if (GetHandleInfo(client_handle, &handle_info) != HND_CLIENT) {
444 HandleUnlock();
445 return_code = GENA_E_BAD_HANDLE;
446 goto exit_function;
447 }
448 sub = GetClientSubClientSID(handle_info->ClientSubList, in_sid);
449 if (nullptr == sub) {
450 HandleUnlock();
451 return_code = GENA_E_BAD_SID;
452 goto exit_function;
453 }
454 sub_copy = *sub;
455 HandleUnlock();
456
457 return_code = gena_unsubscribe(sub_copy.eventURL, sub_copy.actualSID);
458 clientCancelRenew(&sub_copy);
459
460 HandleLock();
461 if (GetHandleInfo(client_handle, &handle_info) != HND_CLIENT) {
462 HandleUnlock();
463 return_code = GENA_E_BAD_HANDLE;
464 goto exit_function;
465 }
466 RemoveClientSubClientSID(handle_info->ClientSubList, in_sid);
467 HandleUnlock();
468
469 exit_function:
470 return return_code;
471 }
472
473
genaSubscribe(UpnpClient_Handle client_handle,const std::string & PublisherURL,int * TimeOut,std::string * out_sid)474 int genaSubscribe(
475 UpnpClient_Handle client_handle,
476 const std::string& PublisherURL,
477 int *TimeOut,
478 std::string *out_sid)
479 {
480 int return_code = GENA_SUCCESS;
481 ClientSubscription newSubscription;
482 std::string ActualSID;
483 std::string EventURL;
484 struct Handle_Info *handle_info;
485
486 UpnpPrintf(UPNP_DEBUG, GENA, __FILE__, __LINE__, "genaSubscribe\n");
487
488 out_sid->clear();
489
490 HandleReadLock();
491 /* validate handle */
492 if (GetHandleInfo(client_handle, &handle_info) != HND_CLIENT) {
493 return_code = GENA_E_BAD_HANDLE;
494 SubscribeLock();
495 goto error_handler;
496 }
497 HandleUnlock();
498
499 /* subscribe */
500 SubscribeLock();
501 return_code = gena_subscribe(PublisherURL,TimeOut,std::string(), &ActualSID);
502 HandleLock();
503 if (return_code != UPNP_E_SUCCESS) {
504 UpnpPrintf(UPNP_ERROR, GENA, __FILE__, __LINE__,
505 "genSubscribe: subscribe error, return %d\n", return_code);
506 goto error_handler;
507 }
508
509 if(GetHandleInfo(client_handle, &handle_info) != HND_CLIENT) {
510 return_code = GENA_E_BAD_HANDLE;
511 goto error_handler;
512 }
513
514 /* generate client SID */
515 out_sid->assign(std::string("uuid:") + gena_sid_uuid());
516
517 /* create event url */
518 EventURL = PublisherURL;
519
520 newSubscription.renewEventId = -1;
521 newSubscription.SID = *out_sid;
522 newSubscription.actualSID = ActualSID;
523 newSubscription.eventURL = EventURL;
524 handle_info->ClientSubList.push_front(newSubscription);
525
526 /* schedule expiration event */
527 return_code = ScheduleGenaAutoRenew(client_handle, *TimeOut,
528 &handle_info->ClientSubList.front());
529
530 error_handler:
531 HandleUnlock();
532 SubscribeUnlock();
533
534 return return_code;
535 }
536
537
genaRenewSubscription(UpnpClient_Handle client_handle,const std::string & in_sid,int * TimeOut)538 int genaRenewSubscription(
539 UpnpClient_Handle client_handle,
540 const std::string& in_sid,
541 int *TimeOut)
542 {
543 int return_code = GENA_SUCCESS;
544 ClientSubscription *sub = nullptr;
545 ClientSubscription sub_copy;
546 struct Handle_Info *handle_info;
547 std::string ActualSID;
548
549 HandleLock();
550
551 /* validate handle and sid */
552 if (GetHandleInfo(client_handle, &handle_info) != HND_CLIENT) {
553 HandleUnlock();
554 return_code = GENA_E_BAD_HANDLE;
555 goto exit_function;
556 }
557
558 sub = GetClientSubClientSID(handle_info->ClientSubList, in_sid);
559 if (sub == nullptr) {
560 HandleUnlock();
561 return_code = GENA_E_BAD_SID;
562 goto exit_function;
563 }
564 /* remove old events */
565 gTimerThread->remove(sub->renewEventId);
566
567 sub->renewEventId = -1;
568 sub_copy = *sub;
569
570 HandleUnlock();
571
572 return_code = gena_subscribe(sub_copy.eventURL, TimeOut, sub_copy.actualSID, &ActualSID);
573
574 HandleLock();
575
576 if (GetHandleInfo(client_handle, &handle_info) != HND_CLIENT) {
577 HandleUnlock();
578 return_code = GENA_E_BAD_HANDLE;
579 goto exit_function;
580 }
581
582 if (return_code != UPNP_E_SUCCESS) {
583 /* network failure (remove client sub) */
584 RemoveClientSubClientSID(handle_info->ClientSubList, in_sid);
585 clientCancelRenew(&sub_copy);
586 HandleUnlock();
587 goto exit_function;
588 }
589
590 /* get subscription */
591 sub = GetClientSubClientSID(handle_info->ClientSubList, in_sid);
592 if (sub == nullptr) {
593 clientCancelRenew(&sub_copy);
594 HandleUnlock();
595 return_code = GENA_E_BAD_SID;
596 goto exit_function;
597 }
598
599 /* store actual sid */
600 sub->actualSID = ActualSID;
601
602 /* start renew subscription timer */
603 return_code = ScheduleGenaAutoRenew(client_handle, *TimeOut, sub);
604 if (return_code != GENA_SUCCESS) {
605 RemoveClientSubClientSID(handle_info->ClientSubList, sub->SID);
606 }
607 clientCancelRenew(&sub_copy);
608 HandleUnlock();
609
610 exit_function:
611 return return_code;
612 }
613
614 class UPnPPropertysetParser : public XMLPARSERTP {
615 public:
UPnPPropertysetParser(const std::string & input,std::unordered_map<std::string,std::string> & propd)616 UPnPPropertysetParser(
617 // XML to be parsed
618 const std::string& input,
619 // Output data
620 std::unordered_map<std::string, std::string>& propd)
621 : XMLPARSERTP(input), propdata(propd) {
622 }
623
624 protected:
EndElement(const XML_Char * name)625 void EndElement(const XML_Char *name) override {
626 const std::string& parentname = (m_path.size() == 1) ?
627 "root" : m_path[m_path.size()-2].name;
628 trimstring(m_chardata, " \t\n\r");
629
630 if (!dom_cmp_name(parentname, "property")) {
631 propdata[name] = m_chardata;
632 }
633 m_chardata.clear();
634 }
635
CharacterData(const XML_Char * s,int len)636 void CharacterData(const XML_Char *s, int len) override {
637 if (s == nullptr || *s == 0)
638 return;
639 m_chardata.append(s, len);
640 }
641
642 private:
643 std::string m_chardata;
644 std::unordered_map<std::string, std::string>& propdata;
645 };
646
gena_process_notification_event(MHDTransaction * mhdt)647 void gena_process_notification_event(MHDTransaction *mhdt)
648 {
649 struct Upnp_Event event_struct;
650 int eventKey;
651 ClientSubscription *subscription = nullptr;
652 struct Handle_Info *handle_info;
653 void *cookie;
654 Upnp_FunPtr callback;
655 UpnpClient_Handle client_handle;
656 std::string tmpSID;
657
658 UpnpPrintf(UPNP_DEBUG, GENA, __FILE__, __LINE__,
659 "gena_process_notification_event\n");
660
661 auto itsid = mhdt->headers.find("sid");
662 /* get SID */
663 if (itsid == mhdt->headers.end()) {
664 http_SendStatusResponse(mhdt, HTTP_PRECONDITION_FAILED);
665 UpnpPrintf(UPNP_DEBUG,GENA,__FILE__,__LINE__, "gena_process_notification_event: no SID\n");
666 return;
667 }
668 const std::string& sid = itsid->second;
669
670 auto itseq = mhdt->headers.find("seq");
671 /* get event key */
672 if (itseq == mhdt->headers.end()) {
673 http_SendStatusResponse(mhdt, HTTP_BAD_REQUEST);
674 UpnpPrintf(UPNP_DEBUG,GENA,__FILE__,__LINE__, "gena_process_notification_event: no SEQ\n");
675 return;
676 }
677 char cb[2];
678 if (sscanf(itseq->second.c_str(), "%d%1c", &eventKey, cb) != 1) {
679 http_SendStatusResponse(mhdt, HTTP_BAD_REQUEST);
680 UpnpPrintf(UPNP_DEBUG,GENA,__FILE__,__LINE__, "gena_process_notification_event: bad seq\n");
681 return;
682 }
683
684 auto itnt = mhdt->headers.find("nt");
685 auto itnts = mhdt->headers.find("nts");
686 /* get NT and NTS headers */
687 if (itnt == mhdt->headers.end() || itnts == mhdt->headers.end()) {
688 http_SendStatusResponse(mhdt, HTTP_BAD_REQUEST);
689 UpnpPrintf(UPNP_DEBUG,GENA,__FILE__,__LINE__, "gena_process_notification_event: no NTS\n");
690 return;
691 }
692
693 /* verify NT and NTS headers */
694 if (itnt->second != "upnp:event" || itnts->second != "upnp:propchange") {
695 http_SendStatusResponse(mhdt, HTTP_PRECONDITION_FAILED);
696 UpnpPrintf(UPNP_DEBUG, GENA, __FILE__, __LINE__,
697 "gena_process_notification_event: bad nt or nts\n");
698 return;
699 }
700
701 /* parse the content (should be XML) */
702 if (!has_xml_content_type(mhdt) || mhdt->postdata.empty()) {
703 http_SendStatusResponse(mhdt, HTTP_BAD_REQUEST);
704 UpnpPrintf(UPNP_DEBUG, GENA, __FILE__, __LINE__,
705 "gena_process_notification_event: empty or not xml\n");
706 return;
707 }
708 std::unordered_map<std::string, std::string> propset;
709 UPnPPropertysetParser parser(mhdt->postdata, propset);
710 if (!parser.Parse()) {
711 http_SendStatusResponse(mhdt, HTTP_BAD_REQUEST);
712 UpnpPrintf(UPNP_DEBUG, GENA, __FILE__, __LINE__,
713 "gena_process_notification_event: xml parse failed: [%s]\n",
714 mhdt->postdata.c_str());
715 return;
716 }
717 HandleLock();
718
719 /* get client info */
720 if (GetClientHandleInfo(&client_handle, &handle_info) != HND_CLIENT) {
721 http_SendStatusResponse(mhdt, HTTP_PRECONDITION_FAILED);
722 HandleUnlock();
723 return;
724 }
725
726 /* get subscription based on SID */
727 subscription = GetClientSubActualSID(handle_info->ClientSubList, sid);
728 if (subscription == nullptr) {
729 if (eventKey == 0) {
730 /* wait until we've finished processing a subscription */
731 /* (if we are in the middle) */
732 /* this is to avoid mistakenly rejecting the first event if we */
733 /* receive it before the subscription response */
734 HandleUnlock();
735
736 /* try and get Subscription Lock */
737 /* (in case we are in the process of subscribing) */
738 SubscribeLock();
739
740 /* get HandleLock again */
741 HandleLock();
742
743 if (GetClientHandleInfo(&client_handle,&handle_info) != HND_CLIENT) {
744 http_SendStatusResponse(mhdt, HTTP_PRECONDITION_FAILED);
745 SubscribeUnlock();
746 HandleUnlock();
747 return;
748 }
749
750 subscription = GetClientSubActualSID(handle_info->ClientSubList,sid);
751 if (subscription == nullptr) {
752 http_SendStatusResponse(mhdt, HTTP_PRECONDITION_FAILED);
753 SubscribeUnlock();
754 HandleUnlock();
755 return;
756 }
757
758 SubscribeUnlock();
759 } else {
760 http_SendStatusResponse(mhdt, HTTP_PRECONDITION_FAILED);
761 HandleUnlock();
762 return;
763 }
764 }
765
766 /* success */
767 http_SendStatusResponse(mhdt, HTTP_OK);
768
769 /* fill event struct */
770 tmpSID = subscription->SID;
771 memset(event_struct.Sid, 0, sizeof(event_struct.Sid));
772 upnp_strlcpy(event_struct.Sid, tmpSID, sizeof(event_struct.Sid));
773 event_struct.EventKey = eventKey;
774 event_struct.ChangedVariables = propset;
775
776 /* copy callback */
777 callback = handle_info->Callback;
778 cookie = handle_info->Cookie;
779
780 HandleUnlock();
781
782 /* make callback with event struct */
783 /* In future, should find a way of mainting */
784 /* that the handle is not unregistered in the middle of a */
785 /* callback */
786 callback(UPNP_EVENT_RECEIVED, &event_struct, cookie);
787 }
788
789
790 #endif /* INCLUDE_CLIENT_APIS */
791 #endif /* EXCLUDE_GENA */
792