You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

495 lines
18 KiB

  1. From 993e4e157e4a8c4898b45982045cf63e3b57a6a1 Mon Sep 17 00:00:00 2001
  2. From: Jean-Francois Dockes <jf@dockes.org>
  3. Date: Fri, 13 Mar 2020 09:19:04 +0100
  4. Subject: [PATCH] Quick changes for working with NPUPNP
  5. (Rebased and made default)
  6. Signed-off-by: Rosen Penev <rosenp@gmail.com>
  7. ---
  8. CMakeLists.txt | 35 +++++++++++++++---------
  9. src/action_request.cc | 11 +++++++-
  10. src/device_description_handler.cc | 4 +++
  11. src/file_request_handler.cc | 4 +++
  12. src/iohandler/file_io_handler.cc | 1 -
  13. src/iohandler/io_handler.cc | 1 -
  14. src/iohandler/mem_io_handler.cc | 1 -
  15. src/serve_request_handler.cc | 8 ++++++
  16. src/server.cc | 8 ++++++
  17. src/transcoding/transcode_ext_handler.cc | 1 -
  18. src/upnp_cds.cc | 11 ++++++++
  19. src/upnp_cm.cc | 11 ++++++++
  20. src/upnp_mrreg.cc | 7 ++++-
  21. src/url_request_handler.cc | 5 +++-
  22. src/util/upnp_clients.cc | 12 ++++++++
  23. src/util/upnp_headers.cc | 14 ++++++++++
  24. src/web/web_request_handler.cc | 4 +++
  25. 17 files changed, 118 insertions(+), 20 deletions(-)
  26. diff --git a/CMakeLists.txt b/CMakeLists.txt
  27. index 81f7818e..56472b97 100644
  28. --- a/CMakeLists.txt
  29. +++ b/CMakeLists.txt
  30. @@ -35,6 +35,7 @@ if (CONAN_EXPORTED)
  31. endif()
  32. set(CMAKE_VERBOSE_MAKEFILE off CACHE BOOL "Show verbose build commands")
  33. +set(WITH_NPUPNP 0 CACHE BOOL "Use npupnp instead of pupnp")
  34. set(WITH_MAGIC 1 CACHE BOOL "Use libmagic to identify file mime types")
  35. set(WITH_MYSQL 0 CACHE BOOL "Store media information in MySQL DB")
  36. set(WITH_CURL 1 CACHE BOOL "CURL required for online services")
  37. @@ -303,23 +304,31 @@ add_definitions(${LFS_DEFINITIONS})
  38. add_compile_options(${LFS_COMPILE_OPTIONS})
  39. target_link_libraries(libgerbera ${LFS_LIBRARIES})
  40. -find_package (pupnp "1.12.1" REQUIRED)
  41. +if (WITH_NPUPNP)
  42. + pkg_check_modules (NPUPNP REQUIRED libnpupnp)
  43. + include_directories (${NPUPNP_INCLUDE_DIRS})
  44. + set(CMAKE_REQUIRED_LIBRARIES npupnp)
  45. + add_definitions(-DUSING_NPUPNP)
  46. + target_link_libraries (libgerbera ${NPUPNP_LIBRARIES})
  47. +else()
  48. + find_package (pupnp "1.12.1" REQUIRED)
  49. -set(CMAKE_REQUIRED_LIBRARIES pupnp::pupnp)
  50. + set(CMAKE_REQUIRED_LIBRARIES pupnp::pupnp)
  51. -check_cxx_symbol_exists(UPNP_ENABLE_IPV6 "upnpconfig.h" UPNP_HAS_IPV6)
  52. -if (NOT UPNP_HAS_IPV6)
  53. - message(FATAL_ERROR "Gerbera requires libupnp with IPv6 support.")
  54. -endif()
  55. + check_cxx_symbol_exists(UPNP_ENABLE_IPV6 "upnpconfig.h" UPNP_HAS_IPV6)
  56. + if (NOT UPNP_HAS_IPV6)
  57. + message(FATAL_ERROR "Gerbera requires libupnp with IPv6 support.")
  58. + endif()
  59. -check_cxx_symbol_exists(UPNP_MINISERVER_REUSEADDR "upnpconfig.h" UPNP_HAS_REUSEADDR)
  60. -if (NOT UPNP_HAS_REUSEADDR)
  61. - message(WARNING [=[
  62. -!! It is strongly recommended to build libupnp with --enable-reuseaddr !!
  63. -Without this option Gerbera will be unable to restart with the same port number.]=])
  64. -endif()
  65. + check_cxx_symbol_exists(UPNP_MINISERVER_REUSEADDR "upnpconfig.h" UPNP_HAS_REUSEADDR)
  66. + if (NOT UPNP_HAS_REUSEADDR)
  67. + message(WARNING [=[
  68. + !! It is strongly recommended to build libupnp with --enable-reuseaddr !!
  69. + Without this option Gerbera will be unable to restart with the same port number.]=])
  70. + endif()
  71. -target_link_libraries (libgerbera pupnp::pupnp)
  72. + target_link_libraries (libgerbera pupnp::pupnp)
  73. +endif()
  74. find_package(fmt REQUIRED)
  75. target_link_libraries(libgerbera fmt::fmt)
  76. diff --git a/src/action_request.cc b/src/action_request.cc
  77. index fab0e910..5677e61e 100644
  78. --- a/src/action_request.cc
  79. +++ b/src/action_request.cc
  80. @@ -65,10 +65,14 @@ std::string ActionRequest::getServiceID() const
  81. std::unique_ptr<pugi::xml_document> ActionRequest::getRequest() const
  82. {
  83. - DOMString cxml = ixmlPrintDocument(UpnpActionRequest_get_ActionRequest(upnp_request));
  84. auto request = std::make_unique<pugi::xml_document>();
  85. +#if defined(USING_NPUPNP)
  86. + auto ret = request->load_string(upnp_request->xmlAction.c_str());
  87. +#else
  88. + DOMString cxml = ixmlPrintDocument(UpnpActionRequest_get_ActionRequest(upnp_request));
  89. auto ret = request->load_string(cxml);
  90. ixmlFreeDOMString(cxml);
  91. +#endif
  92. if (ret.status != pugi::xml_parse_status::status_ok)
  93. throw_std_runtime_error("Unable to parse ixml");
  94. @@ -94,6 +98,10 @@ void ActionRequest::update()
  95. std::string xml = buf.str();
  96. log_debug("ActionRequest::update(): {}", xml.c_str());
  97. +#if defined(USING_NPUPNP)
  98. + UpnpActionRequest_set_xmlResponse(upnp_request, xml);
  99. + UpnpActionRequest_set_ErrCode(upnp_request, errCode);
  100. +#else
  101. IXML_Document* result = nullptr;
  102. int err = ixmlParseBufferEx(xml.c_str(), &result);
  103. @@ -105,6 +113,7 @@ void ActionRequest::update()
  104. UpnpActionRequest_set_ActionResult(upnp_request, result);
  105. UpnpActionRequest_set_ErrCode(upnp_request, errCode);
  106. }
  107. +#endif
  108. } else {
  109. // ok, here there can be two cases
  110. // either the function below already did set an error code,
  111. diff --git a/src/device_description_handler.cc b/src/device_description_handler.cc
  112. index 6aca745e..cf2e8015 100644
  113. --- a/src/device_description_handler.cc
  114. +++ b/src/device_description_handler.cc
  115. @@ -45,7 +45,11 @@ void DeviceDescriptionHandler::getInfo(const char* filename, UpnpFileInfo* info)
  116. {
  117. // We should be able to do the generation here, but libupnp doesnt support the request cookies yet
  118. UpnpFileInfo_set_FileLength(info, -1);
  119. +#if defined(USING_NPUPNP)
  120. UpnpFileInfo_set_ContentType(info, "application/xml");
  121. +#else
  122. + UpnpFileInfo_set_ContentType(info, ixmlCloneDOMString("application/xml"));
  123. +#endif
  124. UpnpFileInfo_set_IsReadable(info, 1);
  125. UpnpFileInfo_set_IsDirectory(info, 0);
  126. }
  127. diff --git a/src/file_request_handler.cc b/src/file_request_handler.cc
  128. index cfa3eaed..915e411b 100644
  129. --- a/src/file_request_handler.cc
  130. +++ b/src/file_request_handler.cc
  131. @@ -238,7 +238,11 @@ void FileRequestHandler::getInfo(const char* filename, UpnpFileInfo* info)
  132. UpnpFileInfo_set_LastModified(info, statbuf.st_mtime);
  133. UpnpFileInfo_set_IsDirectory(info, S_ISDIR(statbuf.st_mode));
  134. +#if defined(USING_NPUPNP)
  135. + UpnpFileInfo_set_ContentType(info, mimeType);
  136. +#else
  137. UpnpFileInfo_set_ContentType(info, ixmlCloneDOMString(mimeType.c_str()));
  138. +#endif
  139. headers->writeHeaders(info);
  140. diff --git a/src/iohandler/file_io_handler.cc b/src/iohandler/file_io_handler.cc
  141. index 7e239250..b023e85b 100644
  142. --- a/src/iohandler/file_io_handler.cc
  143. +++ b/src/iohandler/file_io_handler.cc
  144. @@ -32,7 +32,6 @@
  145. #include "file_io_handler.h" // API
  146. #include <cstdio>
  147. -#include <ixml.h>
  148. #include <utility>
  149. #include "cds_objects.h"
  150. diff --git a/src/iohandler/io_handler.cc b/src/iohandler/io_handler.cc
  151. index f9789425..1153ce6b 100644
  152. --- a/src/iohandler/io_handler.cc
  153. +++ b/src/iohandler/io_handler.cc
  154. @@ -31,7 +31,6 @@
  155. #include "io_handler.h" // API
  156. -#include <ixml.h>
  157. #include <unistd.h>
  158. #include "server.h"
  159. diff --git a/src/iohandler/mem_io_handler.cc b/src/iohandler/mem_io_handler.cc
  160. index 5574a16d..223746ef 100644
  161. --- a/src/iohandler/mem_io_handler.cc
  162. +++ b/src/iohandler/mem_io_handler.cc
  163. @@ -35,7 +35,6 @@
  164. #include <cstdlib>
  165. #include <cstring>
  166. #include <ctime>
  167. -#include <ixml.h>
  168. #include <sys/stat.h>
  169. #include <sys/types.h>
  170. #include <unistd.h>
  171. diff --git a/src/serve_request_handler.cc b/src/serve_request_handler.cc
  172. index 210140a3..01dde69b 100644
  173. --- a/src/serve_request_handler.cc
  174. +++ b/src/serve_request_handler.cc
  175. @@ -94,7 +94,11 @@ void ServeRequestHandler::getInfo(const char* filename, UpnpFileInfo* info)
  176. UpnpFileInfo_set_IsReadable(info, 0);
  177. }
  178. +#if defined(USING_NPUPNP)
  179. + UpnpFileInfo_set_ContentType(info, mimetype);
  180. +#else
  181. UpnpFileInfo_set_ContentType(info, ixmlCloneDOMString(mimetype.c_str()));
  182. +#endif
  183. } else {
  184. throw_std_runtime_error("Not a regular file: " + path);
  185. }
  186. @@ -158,7 +162,11 @@ std::unique_ptr<IOHandler> ServeRequestHandler::open(const char* filename,
  187. }
  188. +#if defined(USING_NPUPNP)
  189. + info->content_type = mimetype;
  190. +#else
  191. info->content_type = ixmlCloneDOMString(mimetype.c_str());
  192. +#endif
  193. */
  194. } else {
  195. throw_std_runtime_error("Not a regular file: " + path);
  196. diff --git a/src/server.cc b/src/server.cc
  197. index a83c28cd..d4ce3e51 100644
  198. --- a/src/server.cc
  199. +++ b/src/server.cc
  200. @@ -393,9 +393,17 @@ int Server::handleUpnpClientEvent(Upnp_EventType eventType, const void* event)
  201. case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
  202. case UPNP_DISCOVERY_SEARCH_RESULT: {
  203. auto d_event = reinterpret_cast<const UpnpDiscovery*>(event);
  204. +#if defined(USING_NPUPNP)
  205. + const char* userAgent = UpnpDiscovery_get_Os_cstr(d_event);
  206. +#else
  207. const char* userAgent = UpnpString_get_String(UpnpDiscovery_get_Os(d_event));
  208. +#endif
  209. const struct sockaddr_storage* destAddr = UpnpDiscovery_get_DestAddr(d_event);
  210. +#if defined(USING_NPUPNP)
  211. + const char* location = UpnpDiscovery_get_Location_cstr(d_event);
  212. +#else
  213. const char* location = UpnpString_get_String(UpnpDiscovery_get_Location(d_event));
  214. +#endif
  215. Clients::addClientByDiscovery(destAddr, userAgent, location);
  216. break;
  217. diff --git a/src/transcoding/transcode_ext_handler.cc b/src/transcoding/transcode_ext_handler.cc
  218. index 67ee79d9..1da59ea2 100644
  219. --- a/src/transcoding/transcode_ext_handler.cc
  220. +++ b/src/transcoding/transcode_ext_handler.cc
  221. @@ -37,7 +37,6 @@
  222. #include <cstring>
  223. #include <fcntl.h>
  224. #include <filesystem>
  225. -#include <ixml.h>
  226. #include <sys/stat.h>
  227. #include <sys/types.h>
  228. #include <unistd.h>
  229. diff --git a/src/upnp_cds.cc b/src/upnp_cds.cc
  230. index 12ffeea2..5c2e1043 100644
  231. --- a/src/upnp_cds.cc
  232. +++ b/src/upnp_cds.cc
  233. @@ -284,6 +284,11 @@ void ContentDirectoryService::processSubscriptionRequest(const std::unique_ptr<S
  234. propset->print(buf, "", 0);
  235. std::string xml = buf.str();
  236. +#if defined(USING_NPUPNP)
  237. + UpnpAcceptSubscriptionXML(
  238. + deviceHandle, config->getOption(CFG_SERVER_UDN).c_str(),
  239. + DESC_CDS_SERVICE_ID, xml, request->getSubscriptionID().c_str());
  240. +#else
  241. IXML_Document* event = nullptr;
  242. int err = ixmlParseBufferEx(xml.c_str(), &event);
  243. if (err != IXML_SUCCESS) {
  244. @@ -295,6 +300,7 @@ void ContentDirectoryService::processSubscriptionRequest(const std::unique_ptr<S
  245. DESC_CDS_SERVICE_ID, event, request->getSubscriptionID().c_str());
  246. ixmlDocument_free(event);
  247. +#endif
  248. log_debug("end");
  249. }
  250. @@ -313,6 +319,10 @@ void ContentDirectoryService::sendSubscriptionUpdate(const std::string& containe
  251. propset->print(buf, "", 0);
  252. std::string xml = buf.str();
  253. +#if defined(USING_NPUPNP)
  254. + UpnpNotifyXML(deviceHandle, config->getOption(CFG_SERVER_UDN).c_str(),
  255. + DESC_CDS_SERVICE_ID, xml);
  256. +#else
  257. IXML_Document* event = nullptr;
  258. int err = ixmlParseBufferEx(xml.c_str(), &event);
  259. if (err != IXML_SUCCESS) {
  260. @@ -325,6 +335,7 @@ void ContentDirectoryService::sendSubscriptionUpdate(const std::string& containe
  261. DESC_CDS_SERVICE_ID, event);
  262. ixmlDocument_free(event);
  263. +#endif
  264. log_debug("end");
  265. }
  266. diff --git a/src/upnp_cm.cc b/src/upnp_cm.cc
  267. index aa608480..d7ab40cf 100644
  268. --- a/src/upnp_cm.cc
  269. +++ b/src/upnp_cm.cc
  270. @@ -127,6 +127,11 @@ void ConnectionManagerService::processSubscriptionRequest(const std::unique_ptr<
  271. propset->print(buf, "", 0);
  272. std::string xml = buf.str();
  273. +#if defined(USING_NPUPNP)
  274. + UpnpAcceptSubscriptionXML(
  275. + deviceHandle, config->getOption(CFG_SERVER_UDN).c_str(),
  276. + DESC_CM_SERVICE_ID, xml, request->getSubscriptionID().c_str());
  277. +#else
  278. IXML_Document* event = nullptr;
  279. int err = ixmlParseBufferEx(xml.c_str(), &event);
  280. if (err != IXML_SUCCESS) {
  281. @@ -138,6 +143,7 @@ void ConnectionManagerService::processSubscriptionRequest(const std::unique_ptr<
  282. DESC_CM_SERVICE_ID, event, request->getSubscriptionID().c_str());
  283. ixmlDocument_free(event);
  284. +#endif
  285. }
  286. void ConnectionManagerService::sendSubscriptionUpdate(const std::string& sourceProtocol_CSV)
  287. @@ -150,6 +156,10 @@ void ConnectionManagerService::sendSubscriptionUpdate(const std::string& sourceP
  288. propset->print(buf, "", 0);
  289. std::string xml = buf.str();
  290. +#if defined(USING_NPUPNP)
  291. + UpnpNotifyXML(deviceHandle, config->getOption(CFG_SERVER_UDN).c_str(),
  292. + DESC_CM_SERVICE_ID, xml);
  293. +#else
  294. IXML_Document* event = nullptr;
  295. int err = ixmlParseBufferEx(xml.c_str(), &event);
  296. if (err != IXML_SUCCESS) {
  297. @@ -162,4 +172,5 @@ void ConnectionManagerService::sendSubscriptionUpdate(const std::string& sourceP
  298. DESC_CM_SERVICE_ID, event);
  299. ixmlDocument_free(event);
  300. +#endif
  301. }
  302. diff --git a/src/upnp_mrreg.cc b/src/upnp_mrreg.cc
  303. index 16eefaed..ecb49025 100644
  304. --- a/src/upnp_mrreg.cc
  305. +++ b/src/upnp_mrreg.cc
  306. @@ -34,7 +34,6 @@
  307. #include <utility>
  308. #include "config/config_manager.h"
  309. -#include "ixml.h"
  310. #include "server.h"
  311. #include "storage/storage.h"
  312. #include "upnp_xml.h"
  313. @@ -120,6 +119,11 @@ void MRRegistrarService::processSubscriptionRequest(const std::unique_ptr<Subscr
  314. propset->print(buf, "", 0);
  315. std::string xml = buf.str();
  316. +#if defined(USING_NPUPNP)
  317. + UpnpAcceptSubscriptionXML(
  318. + deviceHandle, config->getOption(CFG_SERVER_UDN).c_str(),
  319. + DESC_MRREG_SERVICE_ID, xml, request->getSubscriptionID().c_str());
  320. +#else
  321. IXML_Document* event = nullptr;
  322. int err = ixmlParseBufferEx(xml.c_str(), &event);
  323. if (err != IXML_SUCCESS) {
  324. @@ -131,6 +135,7 @@ void MRRegistrarService::processSubscriptionRequest(const std::unique_ptr<Subscr
  325. DESC_MRREG_SERVICE_ID, event, request->getSubscriptionID().c_str());
  326. ixmlDocument_free(event);
  327. +#endif
  328. }
  329. // TODO: FIXME
  330. diff --git a/src/url_request_handler.cc b/src/url_request_handler.cc
  331. index f2a99c94..7de2227d 100644
  332. --- a/src/url_request_handler.cc
  333. +++ b/src/url_request_handler.cc
  334. @@ -32,7 +32,6 @@
  335. #ifdef HAVE_CURL
  336. #include "url_request_handler.h" // API
  337. -#include <ixml.h>
  338. #include <utility>
  339. #include "config/config_manager.h"
  340. @@ -138,7 +137,11 @@ void URLRequestHandler::getInfo(const char* filename, UpnpFileInfo* info)
  341. // ixmlCloneDOMString(header.c_str()));
  342. // }
  343. +#if defined(USING_NPUPNP)
  344. + UpnpFileInfo_set_ContentType(info, mimeType);
  345. +#else
  346. UpnpFileInfo_set_ContentType(info, ixmlCloneDOMString(mimeType.c_str()));
  347. +#endif
  348. log_debug("web_get_info(): end");
  349. /// \todo transcoding for get_info
  350. diff --git a/src/util/upnp_clients.cc b/src/util/upnp_clients.cc
  351. index 2033cf31..1dd7964d 100644
  352. --- a/src/util/upnp_clients.cc
  353. +++ b/src/util/upnp_clients.cc
  354. @@ -268,6 +268,17 @@ bool Clients::getInfoByType(const std::string& match, ClientMatchType type, cons
  355. bool Clients::downloadDescription(const std::string& location, std::unique_ptr<pugi::xml_document>& xml)
  356. {
  357. +#if defined(USING_NPUPNP)
  358. + std::string description, ct;
  359. + int errCode = UpnpDownloadUrlItem(location, description, ct);
  360. + if (errCode != UPNP_E_SUCCESS) {
  361. + log_debug("Error obtaining client description from {} -- error = {}", location, errCode);
  362. + return false;
  363. + }
  364. + const char* cxml = description.c_str();
  365. + xml = std::make_unique<pugi::xml_document>();
  366. + auto ret = xml->load_string(cxml);
  367. +#else
  368. IXML_Document* descDoc = nullptr;
  369. int errCode = UpnpDownloadXmlDoc(location.c_str(), &descDoc);
  370. if (errCode != UPNP_E_SUCCESS) {
  371. @@ -281,6 +292,7 @@ bool Clients::downloadDescription(const std::string& location, std::unique_ptr<p
  372. ixmlFreeDOMString(cxml);
  373. ixmlDocument_free(descDoc);
  374. +#endif
  375. if (ret.status != pugi::xml_parse_status::status_ok) {
  376. log_debug("Unable to parse xml client description from {}", location);
  377. diff --git a/src/util/upnp_headers.cc b/src/util/upnp_headers.cc
  378. index ef85106b..aec13850 100644
  379. --- a/src/util/upnp_headers.cc
  380. +++ b/src/util/upnp_headers.cc
  381. @@ -25,11 +25,13 @@
  382. #include "upnp_headers.h" // API
  383. +#if !defined(USING_NPUPNP)
  384. #if (UPNP_VERSION > 11201)
  385. #include <UpnpExtraHeaders.h>
  386. #else
  387. #include <ExtraHeaders.h>
  388. #endif
  389. +#endif
  390. #include <string>
  391. #include "common.h"
  392. @@ -101,18 +103,29 @@ void Headers::writeHeaders(UpnpFileInfo* fileInfo) const
  393. if (headers == nullptr)
  394. return;
  395. +#if defined(USING_NPUPNP)
  396. + for (const auto& iter : *headers) {
  397. + fileInfo->response_headers.push_back(iter);
  398. + }
  399. +#else
  400. auto head = const_cast<UpnpListHead*>(UpnpFileInfo_get_ExtraHeadersList(fileInfo));
  401. for (const auto& iter : *headers) {
  402. UpnpExtraHeaders* h = UpnpExtraHeaders_new();
  403. UpnpExtraHeaders_set_resp(h, formatHeader(iter, false).c_str());
  404. UpnpListInsert(head, UpnpListEnd(head), const_cast<UpnpListHead*>(UpnpExtraHeaders_get_node(h)));
  405. }
  406. +#endif
  407. }
  408. std::unique_ptr<std::map<std::string, std::string>> Headers::readHeaders(UpnpFileInfo* fileInfo)
  409. {
  410. auto ret = std::make_unique<std::map<std::string, std::string>>();
  411. +#if defined(USING_NPUPNP)
  412. + for (const auto& entry : fileInfo->request_headers) {
  413. + ret->insert(entry);
  414. + }
  415. +#else
  416. auto head = const_cast<UpnpListHead*>(UpnpFileInfo_get_ExtraHeadersList(fileInfo));
  417. UpnpListIter pos;
  418. for (pos = UpnpListBegin(head); pos != UpnpListEnd(head); pos = UpnpListNext(head, pos)) {
  419. @@ -121,6 +134,7 @@ std::unique_ptr<std::map<std::string, std::string>> Headers::readHeaders(UpnpFil
  420. auto add = parseHeader(header);
  421. ret->insert(add);
  422. }
  423. +#endif
  424. return ret;
  425. }
  426. diff --git a/src/web/web_request_handler.cc b/src/web/web_request_handler.cc
  427. index 60e2d028..117dcbfa 100644
  428. --- a/src/web/web_request_handler.cc
  429. +++ b/src/web/web_request_handler.cc
  430. @@ -112,7 +112,11 @@ void WebRequestHandler::getInfo(const char* filename, UpnpFileInfo* info)
  431. std::string mimetype = (returnType == "xml") ? MIMETYPE_XML : MIMETYPE_JSON;
  432. std::string contentType = mimetype + "; charset=" + DEFAULT_INTERNAL_CHARSET;
  433. +#if defined(USING_NPUPNP)
  434. + UpnpFileInfo_set_ContentType(info, contentType);
  435. +#else
  436. UpnpFileInfo_set_ContentType(info, ixmlCloneDOMString(contentType.c_str()));
  437. +#endif
  438. Headers headers;
  439. headers.addHeader(std::string { "Cache-Control" }, std::string { "no-cache, must-revalidate" });
  440. headers.writeHeaders(info);