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.

436 lines
13 KiB

  1. From 61df54155a3cb1846e6bf15e4f007ec8d623de63 Mon Sep 17 00:00:00 2001
  2. From: Jean-Francois Dockes <jf@dockes.org>
  3. Date: Sun, 23 Aug 2020 14:22:21 +0200
  4. Subject: [PATCH] Modification to use npupnp instead of pupnp when the upnp
  5. meson option is set
  6. ---
  7. meson_options.txt | 5 +-
  8. .../plugins/upnp/ContentDirectoryService.cxx | 101 ++++++++++++++++++
  9. src/lib/upnp/Action.hxx | 2 +
  10. src/lib/upnp/ClientInit.cxx | 12 +--
  11. src/lib/upnp/Compat.hxx | 4 +-
  12. src/lib/upnp/ContentDirectoryService.cxx | 25 +++++
  13. src/lib/upnp/Init.cxx | 4 +
  14. src/lib/upnp/UniqueIxml.hxx | 2 +
  15. src/lib/upnp/ixmlwrap.cxx | 4 +
  16. src/lib/upnp/ixmlwrap.hxx | 2 +
  17. src/lib/upnp/meson.build | 20 +++-
  18. 11 files changed, 170 insertions(+), 11 deletions(-)
  19. diff --git a/meson_options.txt b/meson_options.txt
  20. index d17ac1ca8..da90ccfd8 100644
  21. --- a/meson_options.txt
  22. +++ b/meson_options.txt
  23. @@ -54,7 +54,10 @@ option('dsd', type: 'boolean', value: true, description: 'Support the DSD audio
  24. #
  25. option('database', type: 'boolean', value: true, description: 'enable support for the music database')
  26. -option('upnp', type: 'feature', description: 'UPnP client support')
  27. +option('upnp', type: 'combo',
  28. + choices: ['auto', 'pupnp', 'npupnp', 'disabled'],
  29. + value: 'auto',
  30. + description: 'UPnP client support')
  31. option('libmpdclient', type: 'feature', description: 'libmpdclient support (for the proxy database plugin)')
  32. #
  33. diff --git a/src/db/plugins/upnp/ContentDirectoryService.cxx b/src/db/plugins/upnp/ContentDirectoryService.cxx
  34. index 99893d89d..29d58ca23 100644
  35. --- a/src/db/plugins/upnp/ContentDirectoryService.cxx
  36. +++ b/src/db/plugins/upnp/ContentDirectoryService.cxx
  37. @@ -18,7 +18,10 @@
  38. */
  39. #include "lib/upnp/ContentDirectoryService.hxx"
  40. +#include "config.h"
  41. +#ifdef USING_PUPNP
  42. #include "lib/upnp/ixmlwrap.hxx"
  43. +#endif
  44. #include "lib/upnp/UniqueIxml.hxx"
  45. #include "lib/upnp/Action.hxx"
  46. #include "Directory.hxx"
  47. @@ -28,8 +31,11 @@
  48. #include "util/ScopeExit.hxx"
  49. #include "util/StringFormat.hxx"
  50. +#include <algorithm>
  51. +
  52. #include <stdio.h>
  53. +#ifdef USING_PUPNP
  54. static void
  55. ReadResultTag(UPnPDirContent &dirbuf, IXML_Document *response)
  56. {
  57. @@ -39,6 +45,7 @@ ReadResultTag(UPnPDirContent &dirbuf, IXML_Document *response)
  58. dirbuf.Parse(p);
  59. }
  60. +#endif
  61. inline void
  62. ContentDirectoryService::readDirSlice(UpnpClient_Handle hdl,
  63. @@ -47,6 +54,7 @@ ContentDirectoryService::readDirSlice(UpnpClient_Handle hdl,
  64. unsigned &didreadp,
  65. unsigned &totalp) const
  66. {
  67. +#ifdef USING_PUPNP
  68. // Some devices require an empty SortCriteria, else bad params
  69. IXML_Document *request =
  70. MakeActionHelper("Browse", m_serviceType.c_str(),
  71. @@ -82,6 +90,37 @@ ContentDirectoryService::readDirSlice(UpnpClient_Handle hdl,
  72. totalp = ParseUnsigned(value);
  73. ReadResultTag(dirbuf, response);
  74. +#else
  75. + std::vector<std::pair<std::string, std::string> > actionParams{
  76. + { "ObjectID", objectId },
  77. + { "BrowseFlag", "BrowseDirectChildren" },
  78. + { "Filter", "*" },
  79. + { "SortCriteria", "" },
  80. + { "StartingIndex", StringFormat<32>("%u", offset).c_str() },
  81. + { "RequestedCount", StringFormat<32>("%u", count).c_str() }
  82. + };
  83. + std::vector<std::pair<std::string, std::string> > responseData;
  84. + int errcode;
  85. + std::string errdesc;
  86. + int code =
  87. + UpnpSendAction(hdl, "", m_actionURL, m_serviceType, "Browse",
  88. + actionParams, responseData, &errcode, errdesc);
  89. + if (code != UPNP_E_SUCCESS)
  90. + throw FormatRuntimeError("UpnpSendAction() failed: %s",
  91. + UpnpGetErrorMessage(code));
  92. + const char *p = "";
  93. + didreadp = 0;
  94. + for (const auto &entry : responseData) {
  95. + if (entry.first == "Result") {
  96. + p = entry.second.c_str();
  97. + } else if (entry.first == "TotalMatches") {
  98. + totalp = ParseUnsigned(entry.second.c_str());
  99. + } else if (entry.first == "NumberReturned") {
  100. + didreadp = ParseUnsigned(entry.second.c_str());
  101. + }
  102. + }
  103. + dirbuf.Parse(p);
  104. +#endif
  105. }
  106. UPnPDirContent
  107. @@ -110,6 +149,7 @@ ContentDirectoryService::search(UpnpClient_Handle hdl,
  108. unsigned offset = 0, total = -1, count;
  109. do {
  110. +#ifdef USING_PUPNP
  111. UniqueIxmlDocument request(MakeActionHelper("Search", m_serviceType.c_str(),
  112. "ContainerID", objectId,
  113. "SearchCriteria", ss,
  114. @@ -147,6 +187,39 @@ ContentDirectoryService::search(UpnpClient_Handle hdl,
  115. total = ParseUnsigned(value);
  116. ReadResultTag(dirbuf, response.get());
  117. +#else
  118. + std::vector<std::pair<std::string, std::string> > actionParams{
  119. + { "ContainerID", objectId },
  120. + { "SearchCriteria", ss },
  121. + { "Filter", "*" },
  122. + { "SortCriteria", "" },
  123. + { "StartingIndex",
  124. + StringFormat<32>("%u", offset).c_str() },
  125. + { "RequestedCount", "0" }
  126. + };
  127. + std::vector<std::pair<std::string, std::string> > responseData;
  128. + int errcode;
  129. + std::string errdesc;
  130. + int code = UpnpSendAction(hdl, "", m_actionURL, m_serviceType,
  131. + "Search", actionParams, responseData,
  132. + &errcode, errdesc);
  133. + if (code != UPNP_E_SUCCESS)
  134. + throw FormatRuntimeError("UpnpSendAction() failed: %s",
  135. + UpnpGetErrorMessage(code));
  136. + const char *p = "";
  137. + count = 0;
  138. + for (const auto &entry : responseData) {
  139. + if (entry.first == "Result") {
  140. + p = entry.second.c_str();
  141. + } else if (entry.first == "TotalMatches") {
  142. + total = ParseUnsigned(entry.second.c_str());
  143. + } else if (entry.first == "NumberReturned") {
  144. + count = ParseUnsigned(entry.second.c_str());
  145. + offset += count;
  146. + }
  147. + }
  148. + dirbuf.Parse(p);
  149. +#endif
  150. } while (count > 0 && offset < total);
  151. return dirbuf;
  152. @@ -156,6 +229,7 @@ UPnPDirContent
  153. ContentDirectoryService::getMetadata(UpnpClient_Handle hdl,
  154. const char *objectId) const
  155. {
  156. +#ifdef USING_PUPNP
  157. // Create request
  158. UniqueIxmlDocument request(MakeActionHelper("Browse", m_serviceType.c_str(),
  159. "ObjectID", objectId,
  160. @@ -179,4 +253,31 @@ ContentDirectoryService::getMetadata(UpnpClient_Handle hdl,
  161. UPnPDirContent dirbuf;
  162. ReadResultTag(dirbuf, response.get());
  163. return dirbuf;
  164. +#else
  165. + std::vector<std::pair<std::string, std::string> > actionParams{
  166. + { "ObjectID", objectId }, { "BrowseFlag", "BrowseMetadata" },
  167. + { "Filter", "*" }, { "SortCriteria", "" },
  168. + { "StartingIndex", "0" }, { "RequestedCount", "1" }
  169. + };
  170. + std::vector<std::pair<std::string, std::string> > responseData;
  171. + int errcode;
  172. + std::string errdesc;
  173. + int code =
  174. + UpnpSendAction(hdl, "", m_actionURL, m_serviceType, "Browse",
  175. + actionParams, responseData, &errcode, errdesc);
  176. + if (code != UPNP_E_SUCCESS)
  177. + throw FormatRuntimeError("UpnpSendAction() failed: %s",
  178. + UpnpGetErrorMessage(code));
  179. + const char *p = "";
  180. + for (const auto &entry : responseData) {
  181. + if (entry.first == "Result") {
  182. + p = entry.second.c_str();
  183. + break;
  184. + }
  185. + }
  186. +
  187. + UPnPDirContent dirbuf;
  188. + dirbuf.Parse(p);
  189. + return dirbuf;
  190. +#endif
  191. }
  192. diff --git a/src/lib/upnp/Action.hxx b/src/lib/upnp/Action.hxx
  193. index 49ed75198..4ecf4cb06 100644
  194. --- a/src/lib/upnp/Action.hxx
  195. +++ b/src/lib/upnp/Action.hxx
  196. @@ -38,6 +38,7 @@ CountNameValuePairs(gcc_unused const char *name, gcc_unused const char *value,
  197. return 1 + CountNameValuePairs(args...);
  198. }
  199. +#ifdef USING_PUPNP
  200. /**
  201. * A wrapper for UpnpMakeAction() that counts the number of name/value
  202. * pairs and adds the nullptr sentinel.
  203. @@ -52,5 +53,6 @@ MakeActionHelper(const char *action_name, const char *service_type,
  204. args...,
  205. nullptr, nullptr);
  206. }
  207. +#endif
  208. #endif
  209. diff --git a/src/lib/upnp/ClientInit.cxx b/src/lib/upnp/ClientInit.cxx
  210. index 23ba9cade..54b677fa2 100644
  211. --- a/src/lib/upnp/ClientInit.cxx
  212. +++ b/src/lib/upnp/ClientInit.cxx
  213. @@ -31,14 +31,12 @@ static Mutex upnp_client_init_mutex;
  214. static unsigned upnp_client_ref;
  215. static UpnpClient_Handle upnp_client_handle;
  216. -static int
  217. -UpnpClientCallback(Upnp_EventType et,
  218. -#if UPNP_VERSION >= 10800
  219. - const
  220. +static int UpnpClientCallback(Upnp_EventType et,
  221. +#if 1
  222. + const
  223. #endif
  224. - void *evp,
  225. - void *cookie) noexcept
  226. -{
  227. + void *evp,
  228. + void *cookie) noexcept {
  229. if (cookie == nullptr)
  230. /* this is the cookie passed to UpnpRegisterClient();
  231. but can this ever happen? Will libupnp ever invoke
  232. diff --git a/src/lib/upnp/Compat.hxx b/src/lib/upnp/Compat.hxx
  233. index 7fba1d83b..b9a4d7cf3 100644
  234. --- a/src/lib/upnp/Compat.hxx
  235. +++ b/src/lib/upnp/Compat.hxx
  236. @@ -22,14 +22,14 @@
  237. #include <upnp.h>
  238. -#if UPNP_VERSION < 10800
  239. +#if 0
  240. /* emulate the libupnp 1.8 API with older versions */
  241. using UpnpDiscovery = Upnp_Discovery;
  242. #endif
  243. -#if UPNP_VERSION < 10624
  244. +#if 0
  245. #include "util/Compiler.h"
  246. gcc_pure
  247. diff --git a/src/lib/upnp/ContentDirectoryService.cxx b/src/lib/upnp/ContentDirectoryService.cxx
  248. index ae514c717..eed28b41a 100644
  249. --- a/src/lib/upnp/ContentDirectoryService.cxx
  250. +++ b/src/lib/upnp/ContentDirectoryService.cxx
  251. @@ -17,15 +17,21 @@
  252. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  253. */
  254. +#include "config.h"
  255. +
  256. #include "ContentDirectoryService.hxx"
  257. #include "UniqueIxml.hxx"
  258. #include "Device.hxx"
  259. +#ifdef USING_PUPNP
  260. #include "ixmlwrap.hxx"
  261. +#endif
  262. #include "Action.hxx"
  263. #include "util/UriUtil.hxx"
  264. #include "util/RuntimeError.hxx"
  265. #include "util/SplitString.hxx"
  266. +#include <algorithm>
  267. +
  268. ContentDirectoryService::ContentDirectoryService(const UPnPDevice &device,
  269. const UPnPService &service) noexcept
  270. :m_actionURL(uri_apply_base(service.controlURL, device.URLBase)),
  271. @@ -51,6 +57,7 @@ ContentDirectoryService::~ContentDirectoryService() noexcept
  272. std::forward_list<std::string>
  273. ContentDirectoryService::getSearchCapabilities(UpnpClient_Handle hdl) const
  274. {
  275. +#ifdef USING_PUPNP
  276. UniqueIxmlDocument request(UpnpMakeAction("GetSearchCapabilities", m_serviceType.c_str(),
  277. 0,
  278. nullptr, nullptr));
  279. @@ -69,6 +76,24 @@ ContentDirectoryService::getSearchCapabilities(UpnpClient_Handle hdl) const
  280. const char *s = ixmlwrap::getFirstElementValue(response.get(),
  281. "SearchCaps");
  282. +#else
  283. + std::vector<std::pair<std::string, std::string> > responseData;
  284. + int errcode;
  285. + std::string errdesc;
  286. + auto code = UpnpSendAction(hdl, "", m_actionURL, m_serviceType,
  287. + "GetSearchCapabilities", {}, responseData,
  288. + &errcode, errdesc);
  289. + if (code != UPNP_E_SUCCESS)
  290. + throw FormatRuntimeError("UpnpSendAction() failed: %s",
  291. + UpnpGetErrorMessage(code));
  292. + const char *s{ nullptr };
  293. + for (auto &entry : responseData) {
  294. + if (entry.first == "SearchCaps") {
  295. + s = entry.second.c_str();
  296. + break;
  297. + }
  298. + }
  299. +#endif
  300. if (s == nullptr || *s == 0)
  301. /* we could just "return {}" here, but GCC 5 doesn't
  302. understand that */
  303. diff --git a/src/lib/upnp/Init.cxx b/src/lib/upnp/Init.cxx
  304. index 7ad4d565a..10510402a 100644
  305. --- a/src/lib/upnp/Init.cxx
  306. +++ b/src/lib/upnp/Init.cxx
  307. @@ -23,7 +23,9 @@
  308. #include <upnp.h>
  309. #include <upnptools.h>
  310. +#ifdef USING_PUPNP
  311. #include <ixml.h>
  312. +#endif
  313. #include <assert.h>
  314. @@ -44,8 +46,10 @@ DoInit()
  315. UpnpSetMaxContentLength(2000*1024);
  316. +#ifdef USING_PUPNP
  317. // Servers sometimes make error (e.g.: minidlna returns bad utf-8)
  318. ixmlRelaxParser(1);
  319. +#endif
  320. }
  321. void
  322. diff --git a/src/lib/upnp/UniqueIxml.hxx b/src/lib/upnp/UniqueIxml.hxx
  323. index 2ff2afa62..8a0ea0a1f 100644
  324. --- a/src/lib/upnp/UniqueIxml.hxx
  325. +++ b/src/lib/upnp/UniqueIxml.hxx
  326. @@ -20,6 +20,7 @@
  327. #ifndef MPD_UPNP_UNIQUE_XML_HXX
  328. #define MPD_UPNP_UNIQUE_XML_HXX
  329. +#ifdef USING_PUPNP
  330. #include <ixml.h>
  331. #include <memory>
  332. @@ -37,4 +38,5 @@ struct UpnpIxmlDeleter {
  333. typedef std::unique_ptr<IXML_Document, UpnpIxmlDeleter> UniqueIxmlDocument;
  334. typedef std::unique_ptr<IXML_NodeList, UpnpIxmlDeleter> UniqueIxmlNodeList;
  335. +#endif /* USING_PUPNP */
  336. #endif
  337. diff --git a/src/lib/upnp/ixmlwrap.cxx b/src/lib/upnp/ixmlwrap.cxx
  338. index 4e44f35a6..c7798e557 100644
  339. --- a/src/lib/upnp/ixmlwrap.cxx
  340. +++ b/src/lib/upnp/ixmlwrap.cxx
  341. @@ -15,6 +15,9 @@
  342. * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  343. */
  344. +#include "config.h"
  345. +
  346. +#ifdef USING_PUPNP
  347. #include "ixmlwrap.hxx"
  348. #include "UniqueIxml.hxx"
  349. @@ -39,3 +42,4 @@ getFirstElementValue(IXML_Document *doc, const char *name) noexcept
  350. }
  351. }
  352. +#endif
  353. diff --git a/src/lib/upnp/ixmlwrap.hxx b/src/lib/upnp/ixmlwrap.hxx
  354. index 6713d59bd..4b01801f7 100644
  355. --- a/src/lib/upnp/ixmlwrap.hxx
  356. +++ b/src/lib/upnp/ixmlwrap.hxx
  357. @@ -17,6 +17,7 @@
  358. #ifndef _IXMLWRAP_H_INCLUDED_
  359. #define _IXMLWRAP_H_INCLUDED_
  360. +#ifdef USING_PUPNP
  361. #include <ixml.h>
  362. #include <string>
  363. @@ -32,4 +33,5 @@ namespace ixmlwrap {
  364. }
  365. +#endif /* USING_PUPNP */
  366. #endif /* _IXMLWRAP_H_INCLUDED_ */
  367. diff --git a/src/lib/upnp/meson.build b/src/lib/upnp/meson.build
  368. index 9e16f7319..bdc248e6c 100644
  369. --- a/src/lib/upnp/meson.build
  370. +++ b/src/lib/upnp/meson.build
  371. @@ -1,4 +1,22 @@
  372. -upnp_dep = dependency('libupnp', required: get_option('upnp'))
  373. +upnp_option = get_option('upnp')
  374. +
  375. +if upnp_option == 'auto'
  376. + upnp_dep = dependency('libupnp', version: '>= 1.8', required: false)
  377. + conf.set('USING_PUPNP', upnp_dep.found())
  378. + if not upnp_dep.found()
  379. + upnp_dep = dependency('libnpupnp', version: '>= 1.8', required: false)
  380. + endif
  381. +elif upnp_option == 'pupnp'
  382. + upnp_dep = dependency('libupnp', version: '>= 1.8', required: true)
  383. + conf.set('USING_PUPNP', true)
  384. +elif upnp_option == 'npupnp'
  385. + upnp_dep = dependency('libnpupnp', required: true)
  386. + conf.set('USING_PUPNP', false)
  387. +elif upnp_option == 'disabled'
  388. + upnp_dep = dependency('', required: false)
  389. + subdir_done()
  390. +endif
  391. +
  392. conf.set('ENABLE_UPNP', upnp_dep.found())
  393. if not upnp_dep.found()
  394. subdir_done()