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.

397 lines
12 KiB

  1. From ae107c22b946b60167d0d1da9630805cbb1af578 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 | 96 ++++++++++++++++++-
  9. src/lib/upnp/Action.hxx | 2 +
  10. src/lib/upnp/ContentDirectoryService.cxx | 35 ++++++-
  11. src/lib/upnp/Init.cxx | 6 +-
  12. src/lib/upnp/UniqueIxml.hxx | 6 +-
  13. src/lib/upnp/ixmlwrap.cxx | 8 +-
  14. src/lib/upnp/ixmlwrap.hxx | 4 +-
  15. src/lib/upnp/meson.build | 20 +++-
  16. 9 files changed, 169 insertions(+), 13 deletions(-)
  17. diff --git a/meson_options.txt b/meson_options.txt
  18. index 0a70ff3a3..d9fe0147d 100644
  19. --- a/meson_options.txt
  20. +++ b/meson_options.txt
  21. @@ -57,7 +57,10 @@ option('dsd', type: 'boolean', value: true, description: 'Support the DSD audio
  22. #
  23. option('database', type: 'boolean', value: true, description: 'enable support for the music database')
  24. -option('upnp', type: 'feature', description: 'UPnP client support')
  25. +option('upnp', type: 'combo',
  26. + choices: ['auto', 'pupnp', 'npupnp', 'disabled'],
  27. + value: 'auto',
  28. + description: 'UPnP client support')
  29. option('libmpdclient', type: 'feature', description: 'libmpdclient support (for the proxy database plugin)')
  30. #
  31. diff --git a/src/db/plugins/upnp/ContentDirectoryService.cxx b/src/db/plugins/upnp/ContentDirectoryService.cxx
  32. index 0cb94c42c..50405b9a0 100644
  33. --- a/src/db/plugins/upnp/ContentDirectoryService.cxx
  34. +++ b/src/db/plugins/upnp/ContentDirectoryService.cxx
  35. @@ -18,7 +18,10 @@
  36. */
  37. #include "lib/upnp/ContentDirectoryService.hxx"
  38. -#include "lib/upnp/ixmlwrap.hxx"
  39. +#include "config.h"
  40. +#ifdef USING_PUPNP
  41. +# include "lib/upnp/ixmlwrap.hxx"
  42. +#endif
  43. #include "lib/upnp/UniqueIxml.hxx"
  44. #include "lib/upnp/Action.hxx"
  45. #include "Directory.hxx"
  46. @@ -27,6 +30,9 @@
  47. #include "util/ScopeExit.hxx"
  48. #include "util/StringFormat.hxx"
  49. +#include <algorithm>
  50. +
  51. +#ifdef USING_PUPNP
  52. static void
  53. ReadResultTag(UPnPDirContent &dirbuf, IXML_Document *response)
  54. {
  55. @@ -36,6 +42,7 @@ ReadResultTag(UPnPDirContent &dirbuf, IXML_Document *response)
  56. dirbuf.Parse(p);
  57. }
  58. +#endif
  59. inline void
  60. ContentDirectoryService::readDirSlice(UpnpClient_Handle hdl,
  61. @@ -44,6 +51,7 @@ ContentDirectoryService::readDirSlice(UpnpClient_Handle hdl,
  62. unsigned &didreadp,
  63. unsigned &totalp) const
  64. {
  65. +#ifdef USING_PUPNP
  66. // Some devices require an empty SortCriteria, else bad params
  67. IXML_Document *request =
  68. MakeActionHelper("Browse", m_serviceType.c_str(),
  69. @@ -79,6 +87,35 @@ ContentDirectoryService::readDirSlice(UpnpClient_Handle hdl,
  70. totalp = ParseUnsigned(value);
  71. ReadResultTag(dirbuf, response);
  72. +#else
  73. + std::vector<std::pair<std::string, std::string>> actionParams{
  74. + {"ObjectID", objectId},
  75. + {"BrowseFlag", "BrowseDirectChildren"},
  76. + {"Filter", "*"},
  77. + {"SortCriteria", ""},
  78. + {"StartingIndex", StringFormat<32>("%u", offset).c_str()},
  79. + {"RequestedCount", StringFormat<32>("%u", count).c_str()}};
  80. + std::vector<std::pair<std::string, std::string>> responseData;
  81. + int errcode;
  82. + std::string errdesc;
  83. + int code = UpnpSendAction(hdl, "", m_actionURL, m_serviceType, "Browse",
  84. + actionParams, responseData, &errcode, errdesc);
  85. + if (code != UPNP_E_SUCCESS)
  86. + throw FormatRuntimeError("UpnpSendAction() failed: %s",
  87. + UpnpGetErrorMessage(code));
  88. + const char *p = "";
  89. + didreadp = 0;
  90. + for (const auto &entry : responseData) {
  91. + if (entry.first == "Result") {
  92. + p = entry.second.c_str();
  93. + } else if (entry.first == "TotalMatches") {
  94. + totalp = ParseUnsigned(entry.second.c_str());
  95. + } else if (entry.first == "NumberReturned") {
  96. + didreadp = ParseUnsigned(entry.second.c_str());
  97. + }
  98. + }
  99. + dirbuf.Parse(p);
  100. +#endif
  101. }
  102. UPnPDirContent
  103. @@ -107,6 +144,7 @@ ContentDirectoryService::search(UpnpClient_Handle hdl,
  104. unsigned offset = 0, total = -1, count;
  105. do {
  106. +#ifdef USING_PUPNP
  107. UniqueIxmlDocument request(MakeActionHelper("Search", m_serviceType.c_str(),
  108. "ContainerID", objectId,
  109. "SearchCriteria", ss,
  110. @@ -144,6 +182,36 @@ ContentDirectoryService::search(UpnpClient_Handle hdl,
  111. total = ParseUnsigned(value);
  112. ReadResultTag(dirbuf, response.get());
  113. +#else
  114. + std::vector<std::pair<std::string, std::string>> actionParams{
  115. + {"ContainerID", objectId},
  116. + {"SearchCriteria", ss},
  117. + {"Filter", "*"},
  118. + {"SortCriteria", ""},
  119. + {"StartingIndex", StringFormat<32>("%u", offset).c_str()},
  120. + {"RequestedCount", "0"}};
  121. + std::vector<std::pair<std::string, std::string>> responseData;
  122. + int errcode;
  123. + std::string errdesc;
  124. + int code = UpnpSendAction(hdl, "", m_actionURL, m_serviceType, "Search",
  125. + actionParams, responseData, &errcode, errdesc);
  126. + if (code != UPNP_E_SUCCESS)
  127. + throw FormatRuntimeError("UpnpSendAction() failed: %s",
  128. + UpnpGetErrorMessage(code));
  129. + const char *p = "";
  130. + count = 0;
  131. + for (const auto &entry : responseData) {
  132. + if (entry.first == "Result") {
  133. + p = entry.second.c_str();
  134. + } else if (entry.first == "TotalMatches") {
  135. + total = ParseUnsigned(entry.second.c_str());
  136. + } else if (entry.first == "NumberReturned") {
  137. + count = ParseUnsigned(entry.second.c_str());
  138. + offset += count;
  139. + }
  140. + }
  141. + dirbuf.Parse(p);
  142. +#endif
  143. } while (count > 0 && offset < total);
  144. return dirbuf;
  145. @@ -153,6 +221,7 @@ UPnPDirContent
  146. ContentDirectoryService::getMetadata(UpnpClient_Handle hdl,
  147. const char *objectId) const
  148. {
  149. +#ifdef USING_PUPNP
  150. // Create request
  151. UniqueIxmlDocument request(MakeActionHelper("Browse", m_serviceType.c_str(),
  152. "ObjectID", objectId,
  153. @@ -176,4 +245,29 @@ ContentDirectoryService::getMetadata(UpnpClient_Handle hdl,
  154. UPnPDirContent dirbuf;
  155. ReadResultTag(dirbuf, response.get());
  156. return dirbuf;
  157. +#else
  158. + std::vector<std::pair<std::string, std::string>> actionParams{
  159. + {"ObjectID", objectId}, {"BrowseFlag", "BrowseMetadata"},
  160. + {"Filter", "*"}, {"SortCriteria", ""},
  161. + {"StartingIndex", "0"}, {"RequestedCount", "1"}};
  162. + std::vector<std::pair<std::string, std::string>> responseData;
  163. + int errcode;
  164. + std::string errdesc;
  165. + int code = UpnpSendAction(hdl, "", m_actionURL, m_serviceType, "Browse",
  166. + actionParams, responseData, &errcode, errdesc);
  167. + if (code != UPNP_E_SUCCESS)
  168. + throw FormatRuntimeError("UpnpSendAction() failed: %s",
  169. + UpnpGetErrorMessage(code));
  170. + const char *p = "";
  171. + for (const auto &entry : responseData) {
  172. + if (entry.first == "Result") {
  173. + p = entry.second.c_str();
  174. + break;
  175. + }
  176. + }
  177. +
  178. + UPnPDirContent dirbuf;
  179. + dirbuf.Parse(p);
  180. + return dirbuf;
  181. +#endif
  182. }
  183. diff --git a/src/lib/upnp/Action.hxx b/src/lib/upnp/Action.hxx
  184. index 6de060560..19c423534 100644
  185. --- a/src/lib/upnp/Action.hxx
  186. +++ b/src/lib/upnp/Action.hxx
  187. @@ -38,6 +38,7 @@ CountNameValuePairs([[maybe_unused]] const char *name, [[maybe_unused]] const ch
  188. return 1 + CountNameValuePairs(args...);
  189. }
  190. +#ifdef USING_PUPNP
  191. /**
  192. * A wrapper for UpnpMakeAction() that counts the number of name/value
  193. * pairs and adds the nullptr sentinel.
  194. @@ -52,5 +53,6 @@ MakeActionHelper(const char *action_name, const char *service_type,
  195. args...,
  196. nullptr, nullptr);
  197. }
  198. +#endif
  199. #endif
  200. diff --git a/src/lib/upnp/ContentDirectoryService.cxx b/src/lib/upnp/ContentDirectoryService.cxx
  201. index 56d1cf3aa..786bac6b1 100644
  202. --- a/src/lib/upnp/ContentDirectoryService.cxx
  203. +++ b/src/lib/upnp/ContentDirectoryService.cxx
  204. @@ -17,13 +17,21 @@
  205. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  206. */
  207. +#include "config.h"
  208. +
  209. #include "ContentDirectoryService.hxx"
  210. -#include "UniqueIxml.hxx"
  211. #include "Device.hxx"
  212. -#include "ixmlwrap.hxx"
  213. -#include "util/UriRelative.hxx"
  214. -#include "util/RuntimeError.hxx"
  215. +#include "UniqueIxml.hxx"
  216. +#ifdef USING_PUPNP
  217. +# include "ixmlwrap.hxx"
  218. +#endif
  219. +#include "Action.hxx"
  220. #include "util/IterableSplitString.hxx"
  221. +#include "util/RuntimeError.hxx"
  222. +#include "util/UriRelative.hxx"
  223. +#include "util/UriUtil.hxx"
  224. +
  225. +#include <algorithm>
  226. #include <upnptools.h>
  227. @@ -50,6 +58,7 @@ ContentDirectoryService::~ContentDirectoryService() noexcept = default;
  228. std::forward_list<std::string>
  229. ContentDirectoryService::getSearchCapabilities(UpnpClient_Handle hdl) const
  230. {
  231. +#ifdef USING_PUPNP
  232. UniqueIxmlDocument request(UpnpMakeAction("GetSearchCapabilities", m_serviceType.c_str(),
  233. 0,
  234. nullptr, nullptr));
  235. @@ -68,6 +77,24 @@ ContentDirectoryService::getSearchCapabilities(UpnpClient_Handle hdl) const
  236. const char *s = ixmlwrap::getFirstElementValue(response.get(),
  237. "SearchCaps");
  238. +#else
  239. + std::vector<std::pair<std::string, std::string>> responseData;
  240. + int errcode;
  241. + std::string errdesc;
  242. + auto code = UpnpSendAction(hdl, "", m_actionURL, m_serviceType,
  243. + "GetSearchCapabilities", {}, responseData, &errcode,
  244. + errdesc);
  245. + if (code != UPNP_E_SUCCESS)
  246. + throw FormatRuntimeError("UpnpSendAction() failed: %s",
  247. + UpnpGetErrorMessage(code));
  248. + const char *s{nullptr};
  249. + for (auto &entry : responseData) {
  250. + if (entry.first == "SearchCaps") {
  251. + s = entry.second.c_str();
  252. + break;
  253. + }
  254. + }
  255. +#endif
  256. if (s == nullptr || *s == 0)
  257. return {};
  258. diff --git a/src/lib/upnp/Init.cxx b/src/lib/upnp/Init.cxx
  259. index def851f2c..d85b9a523 100644
  260. --- a/src/lib/upnp/Init.cxx
  261. +++ b/src/lib/upnp/Init.cxx
  262. @@ -23,7 +23,9 @@
  263. #include "util/RuntimeError.hxx"
  264. #include <upnptools.h>
  265. -#include <ixml.h>
  266. +#ifdef USING_PUPNP
  267. +# include <ixml.h>
  268. +#endif
  269. #include <cassert>
  270. @@ -44,8 +46,10 @@ DoInit()
  271. UpnpSetMaxContentLength(2000*1024);
  272. +#ifdef USING_PUPNP
  273. // Servers sometimes make error (e.g.: minidlna returns bad utf-8)
  274. ixmlRelaxParser(1);
  275. +#endif
  276. }
  277. void
  278. diff --git a/src/lib/upnp/UniqueIxml.hxx b/src/lib/upnp/UniqueIxml.hxx
  279. index 037e161b7..2ebc2c1c1 100644
  280. --- a/src/lib/upnp/UniqueIxml.hxx
  281. +++ b/src/lib/upnp/UniqueIxml.hxx
  282. @@ -20,9 +20,10 @@
  283. #ifndef MPD_UPNP_UNIQUE_XML_HXX
  284. #define MPD_UPNP_UNIQUE_XML_HXX
  285. -#include <ixml.h>
  286. +#ifdef USING_PUPNP
  287. +# include <ixml.h>
  288. -#include <memory>
  289. +# include <memory>
  290. struct UpnpIxmlDeleter {
  291. void operator()(IXML_Document *doc) noexcept {
  292. @@ -37,4 +38,5 @@ struct UpnpIxmlDeleter {
  293. typedef std::unique_ptr<IXML_Document, UpnpIxmlDeleter> UniqueIxmlDocument;
  294. typedef std::unique_ptr<IXML_NodeList, UpnpIxmlDeleter> UniqueIxmlNodeList;
  295. +#endif /* USING_PUPNP */
  296. #endif
  297. diff --git a/src/lib/upnp/ixmlwrap.cxx b/src/lib/upnp/ixmlwrap.cxx
  298. index f811b07a6..90e8d3155 100644
  299. --- a/src/lib/upnp/ixmlwrap.cxx
  300. +++ b/src/lib/upnp/ixmlwrap.cxx
  301. @@ -15,8 +15,11 @@
  302. * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  303. */
  304. -#include "ixmlwrap.hxx"
  305. -#include "UniqueIxml.hxx"
  306. +#include "config.h"
  307. +
  308. +#ifdef USING_PUPNP
  309. +# include "ixmlwrap.hxx"
  310. +# include "UniqueIxml.hxx"
  311. namespace ixmlwrap {
  312. @@ -39,3 +42,4 @@ getFirstElementValue(IXML_Document *doc, const char *name) noexcept
  313. }
  314. } // namespace ixmlwrap
  315. +#endif
  316. diff --git a/src/lib/upnp/ixmlwrap.hxx b/src/lib/upnp/ixmlwrap.hxx
  317. index 2ef142ac7..25ac8695d 100644
  318. --- a/src/lib/upnp/ixmlwrap.hxx
  319. +++ b/src/lib/upnp/ixmlwrap.hxx
  320. @@ -17,7 +17,8 @@
  321. #ifndef _IXMLWRAP_H_INCLUDED_
  322. #define _IXMLWRAP_H_INCLUDED_
  323. -#include <ixml.h>
  324. +#ifdef USING_PUPNP
  325. +# include <ixml.h>
  326. namespace ixmlwrap {
  327. /**
  328. @@ -30,4 +31,5 @@ namespace ixmlwrap {
  329. }
  330. +#endif /* USING_PUPNP */
  331. #endif /* _IXMLWRAP_H_INCLUDED_ */
  332. diff --git a/src/lib/upnp/meson.build b/src/lib/upnp/meson.build
  333. index f7570eb1c..bdc248e6c 100644
  334. --- a/src/lib/upnp/meson.build
  335. +++ b/src/lib/upnp/meson.build
  336. @@ -1,4 +1,22 @@
  337. -upnp_dep = dependency('libupnp', version: '>= 1.8', required: get_option('upnp'))
  338. +upnp_option = get_option('upnp')
  339. +
  340. +if upnp_option == 'auto'
  341. + upnp_dep = dependency('libupnp', version: '>= 1.8', required: false)
  342. + conf.set('USING_PUPNP', upnp_dep.found())
  343. + if not upnp_dep.found()
  344. + upnp_dep = dependency('libnpupnp', version: '>= 1.8', required: false)
  345. + endif
  346. +elif upnp_option == 'pupnp'
  347. + upnp_dep = dependency('libupnp', version: '>= 1.8', required: true)
  348. + conf.set('USING_PUPNP', true)
  349. +elif upnp_option == 'npupnp'
  350. + upnp_dep = dependency('libnpupnp', required: true)
  351. + conf.set('USING_PUPNP', false)
  352. +elif upnp_option == 'disabled'
  353. + upnp_dep = dependency('', required: false)
  354. + subdir_done()
  355. +endif
  356. +
  357. conf.set('ENABLE_UPNP', upnp_dep.found())
  358. if not upnp_dep.found()
  359. subdir_done()