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