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.

414 lines
12 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. --- a/meson_options.txt
  20. +++ b/meson_options.txt
  21. @@ -54,7 +54,10 @@ option('dsd', type: 'boolean', value: tr
  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. --- a/src/db/plugins/upnp/ContentDirectoryService.cxx
  32. +++ b/src/db/plugins/upnp/ContentDirectoryService.cxx
  33. @@ -18,7 +18,10 @@
  34. */
  35. #include "lib/upnp/ContentDirectoryService.hxx"
  36. +#include "config.h"
  37. +#ifdef USING_PUPNP
  38. #include "lib/upnp/ixmlwrap.hxx"
  39. +#endif
  40. #include "lib/upnp/UniqueIxml.hxx"
  41. #include "lib/upnp/Action.hxx"
  42. #include "Directory.hxx"
  43. @@ -28,8 +31,11 @@
  44. #include "util/ScopeExit.hxx"
  45. #include "util/StringFormat.hxx"
  46. +#include <algorithm>
  47. +
  48. #include <stdio.h>
  49. +#ifdef USING_PUPNP
  50. static void
  51. ReadResultTag(UPnPDirContent &dirbuf, IXML_Document *response)
  52. {
  53. @@ -39,6 +45,7 @@ ReadResultTag(UPnPDirContent &dirbuf, IX
  54. dirbuf.Parse(p);
  55. }
  56. +#endif
  57. inline void
  58. ContentDirectoryService::readDirSlice(UpnpClient_Handle hdl,
  59. @@ -47,6 +54,7 @@ ContentDirectoryService::readDirSlice(Up
  60. unsigned &didreadp,
  61. unsigned &totalp) const
  62. {
  63. +#ifdef USING_PUPNP
  64. // Some devices require an empty SortCriteria, else bad params
  65. IXML_Document *request =
  66. MakeActionHelper("Browse", m_serviceType.c_str(),
  67. @@ -82,6 +90,37 @@ ContentDirectoryService::readDirSlice(Up
  68. totalp = ParseUnsigned(value);
  69. ReadResultTag(dirbuf, response);
  70. +#else
  71. + std::vector<std::pair<std::string, std::string> > actionParams{
  72. + { "ObjectID", objectId },
  73. + { "BrowseFlag", "BrowseDirectChildren" },
  74. + { "Filter", "*" },
  75. + { "SortCriteria", "" },
  76. + { "StartingIndex", StringFormat<32>("%u", offset).c_str() },
  77. + { "RequestedCount", StringFormat<32>("%u", count).c_str() }
  78. + };
  79. + std::vector<std::pair<std::string, std::string> > responseData;
  80. + int errcode;
  81. + std::string errdesc;
  82. + int code =
  83. + 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. @@ -110,6 +149,7 @@ ContentDirectoryService::search(UpnpClie
  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. @@ -147,6 +187,39 @@ ContentDirectoryService::search(UpnpClie
  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",
  120. + StringFormat<32>("%u", offset).c_str() },
  121. + { "RequestedCount", "0" }
  122. + };
  123. + std::vector<std::pair<std::string, std::string> > responseData;
  124. + int errcode;
  125. + std::string errdesc;
  126. + int code = UpnpSendAction(hdl, "", m_actionURL, m_serviceType,
  127. + "Search", actionParams, responseData,
  128. + &errcode, errdesc);
  129. + if (code != UPNP_E_SUCCESS)
  130. + throw FormatRuntimeError("UpnpSendAction() failed: %s",
  131. + UpnpGetErrorMessage(code));
  132. + const char *p = "";
  133. + count = 0;
  134. + for (const auto &entry : responseData) {
  135. + if (entry.first == "Result") {
  136. + p = entry.second.c_str();
  137. + } else if (entry.first == "TotalMatches") {
  138. + total = ParseUnsigned(entry.second.c_str());
  139. + } else if (entry.first == "NumberReturned") {
  140. + count = ParseUnsigned(entry.second.c_str());
  141. + offset += count;
  142. + }
  143. + }
  144. + dirbuf.Parse(p);
  145. +#endif
  146. } while (count > 0 && offset < total);
  147. return dirbuf;
  148. @@ -156,6 +229,7 @@ UPnPDirContent
  149. ContentDirectoryService::getMetadata(UpnpClient_Handle hdl,
  150. const char *objectId) const
  151. {
  152. +#ifdef USING_PUPNP
  153. // Create request
  154. UniqueIxmlDocument request(MakeActionHelper("Browse", m_serviceType.c_str(),
  155. "ObjectID", objectId,
  156. @@ -179,4 +253,31 @@ ContentDirectoryService::getMetadata(Upn
  157. UPnPDirContent dirbuf;
  158. ReadResultTag(dirbuf, response.get());
  159. return dirbuf;
  160. +#else
  161. + std::vector<std::pair<std::string, std::string> > actionParams{
  162. + { "ObjectID", objectId }, { "BrowseFlag", "BrowseMetadata" },
  163. + { "Filter", "*" }, { "SortCriteria", "" },
  164. + { "StartingIndex", "0" }, { "RequestedCount", "1" }
  165. + };
  166. + std::vector<std::pair<std::string, std::string> > responseData;
  167. + int errcode;
  168. + std::string errdesc;
  169. + int code =
  170. + UpnpSendAction(hdl, "", m_actionURL, m_serviceType, "Browse",
  171. + actionParams, responseData, &errcode, errdesc);
  172. + if (code != UPNP_E_SUCCESS)
  173. + throw FormatRuntimeError("UpnpSendAction() failed: %s",
  174. + UpnpGetErrorMessage(code));
  175. + const char *p = "";
  176. + for (const auto &entry : responseData) {
  177. + if (entry.first == "Result") {
  178. + p = entry.second.c_str();
  179. + break;
  180. + }
  181. + }
  182. +
  183. + UPnPDirContent dirbuf;
  184. + dirbuf.Parse(p);
  185. + return dirbuf;
  186. +#endif
  187. }
  188. --- a/src/lib/upnp/Action.hxx
  189. +++ b/src/lib/upnp/Action.hxx
  190. @@ -38,6 +38,7 @@ CountNameValuePairs(gcc_unused const cha
  191. return 1 + CountNameValuePairs(args...);
  192. }
  193. +#ifdef USING_PUPNP
  194. /**
  195. * A wrapper for UpnpMakeAction() that counts the number of name/value
  196. * pairs and adds the nullptr sentinel.
  197. @@ -52,5 +53,6 @@ MakeActionHelper(const char *action_name
  198. args...,
  199. nullptr, nullptr);
  200. }
  201. +#endif
  202. #endif
  203. --- a/src/lib/upnp/ClientInit.cxx
  204. +++ b/src/lib/upnp/ClientInit.cxx
  205. @@ -31,14 +31,12 @@ static Mutex upnp_client_init_mutex;
  206. static unsigned upnp_client_ref;
  207. static UpnpClient_Handle upnp_client_handle;
  208. -static int
  209. -UpnpClientCallback(Upnp_EventType et,
  210. -#if UPNP_VERSION >= 10800
  211. - const
  212. +static int UpnpClientCallback(Upnp_EventType et,
  213. +#if 1
  214. + const
  215. #endif
  216. - void *evp,
  217. - void *cookie) noexcept
  218. -{
  219. + void *evp,
  220. + void *cookie) noexcept {
  221. if (cookie == nullptr)
  222. /* this is the cookie passed to UpnpRegisterClient();
  223. but can this ever happen? Will libupnp ever invoke
  224. --- a/src/lib/upnp/Compat.hxx
  225. +++ b/src/lib/upnp/Compat.hxx
  226. @@ -22,14 +22,14 @@
  227. #include <upnp.h>
  228. -#if UPNP_VERSION < 10800
  229. +#if 0
  230. /* emulate the libupnp 1.8 API with older versions */
  231. using UpnpDiscovery = Upnp_Discovery;
  232. #endif
  233. -#if UPNP_VERSION < 10624
  234. +#if 0
  235. #include "util/Compiler.h"
  236. gcc_pure
  237. --- a/src/lib/upnp/ContentDirectoryService.cxx
  238. +++ b/src/lib/upnp/ContentDirectoryService.cxx
  239. @@ -17,15 +17,21 @@
  240. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  241. */
  242. +#include "config.h"
  243. +
  244. #include "ContentDirectoryService.hxx"
  245. #include "UniqueIxml.hxx"
  246. #include "Device.hxx"
  247. +#ifdef USING_PUPNP
  248. #include "ixmlwrap.hxx"
  249. +#endif
  250. #include "Action.hxx"
  251. #include "util/UriUtil.hxx"
  252. #include "util/RuntimeError.hxx"
  253. #include "util/SplitString.hxx"
  254. +#include <algorithm>
  255. +
  256. ContentDirectoryService::ContentDirectoryService(const UPnPDevice &device,
  257. const UPnPService &service) noexcept
  258. :m_actionURL(uri_apply_base(service.controlURL, device.URLBase)),
  259. @@ -51,6 +57,7 @@ ContentDirectoryService::~ContentDirecto
  260. std::forward_list<std::string>
  261. ContentDirectoryService::getSearchCapabilities(UpnpClient_Handle hdl) const
  262. {
  263. +#ifdef USING_PUPNP
  264. UniqueIxmlDocument request(UpnpMakeAction("GetSearchCapabilities", m_serviceType.c_str(),
  265. 0,
  266. nullptr, nullptr));
  267. @@ -69,6 +76,24 @@ ContentDirectoryService::getSearchCapabi
  268. const char *s = ixmlwrap::getFirstElementValue(response.get(),
  269. "SearchCaps");
  270. +#else
  271. + std::vector<std::pair<std::string, std::string> > responseData;
  272. + int errcode;
  273. + std::string errdesc;
  274. + auto code = UpnpSendAction(hdl, "", m_actionURL, m_serviceType,
  275. + "GetSearchCapabilities", {}, responseData,
  276. + &errcode, errdesc);
  277. + if (code != UPNP_E_SUCCESS)
  278. + throw FormatRuntimeError("UpnpSendAction() failed: %s",
  279. + UpnpGetErrorMessage(code));
  280. + const char *s{ nullptr };
  281. + for (auto &entry : responseData) {
  282. + if (entry.first == "SearchCaps") {
  283. + s = entry.second.c_str();
  284. + break;
  285. + }
  286. + }
  287. +#endif
  288. if (s == nullptr || *s == 0)
  289. /* we could just "return {}" here, but GCC 5 doesn't
  290. understand that */
  291. --- a/src/lib/upnp/Init.cxx
  292. +++ b/src/lib/upnp/Init.cxx
  293. @@ -23,7 +23,9 @@
  294. #include <upnp.h>
  295. #include <upnptools.h>
  296. +#ifdef USING_PUPNP
  297. #include <ixml.h>
  298. +#endif
  299. #include <assert.h>
  300. @@ -44,8 +46,10 @@ DoInit()
  301. UpnpSetMaxContentLength(2000*1024);
  302. +#ifdef USING_PUPNP
  303. // Servers sometimes make error (e.g.: minidlna returns bad utf-8)
  304. ixmlRelaxParser(1);
  305. +#endif
  306. }
  307. void
  308. --- a/src/lib/upnp/UniqueIxml.hxx
  309. +++ b/src/lib/upnp/UniqueIxml.hxx
  310. @@ -20,6 +20,7 @@
  311. #ifndef MPD_UPNP_UNIQUE_XML_HXX
  312. #define MPD_UPNP_UNIQUE_XML_HXX
  313. +#ifdef USING_PUPNP
  314. #include <ixml.h>
  315. #include <memory>
  316. @@ -37,4 +38,5 @@ struct UpnpIxmlDeleter {
  317. typedef std::unique_ptr<IXML_Document, UpnpIxmlDeleter> UniqueIxmlDocument;
  318. typedef std::unique_ptr<IXML_NodeList, UpnpIxmlDeleter> UniqueIxmlNodeList;
  319. +#endif /* USING_PUPNP */
  320. #endif
  321. --- a/src/lib/upnp/ixmlwrap.cxx
  322. +++ b/src/lib/upnp/ixmlwrap.cxx
  323. @@ -15,6 +15,9 @@
  324. * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  325. */
  326. +#include "config.h"
  327. +
  328. +#ifdef USING_PUPNP
  329. #include "ixmlwrap.hxx"
  330. #include "UniqueIxml.hxx"
  331. @@ -39,3 +42,4 @@ getFirstElementValue(IXML_Document *doc,
  332. }
  333. }
  334. +#endif
  335. --- a/src/lib/upnp/ixmlwrap.hxx
  336. +++ b/src/lib/upnp/ixmlwrap.hxx
  337. @@ -17,6 +17,7 @@
  338. #ifndef _IXMLWRAP_H_INCLUDED_
  339. #define _IXMLWRAP_H_INCLUDED_
  340. +#ifdef USING_PUPNP
  341. #include <ixml.h>
  342. #include <string>
  343. @@ -32,4 +33,5 @@ namespace ixmlwrap {
  344. }
  345. +#endif /* USING_PUPNP */
  346. #endif /* _IXMLWRAP_H_INCLUDED_ */
  347. --- a/src/lib/upnp/meson.build
  348. +++ b/src/lib/upnp/meson.build
  349. @@ -1,4 +1,22 @@
  350. -upnp_dep = dependency('libupnp', required: get_option('upnp'))
  351. +upnp_option = get_option('upnp')
  352. +
  353. +if upnp_option == 'auto'
  354. + upnp_dep = dependency('libupnp', version: '>= 1.8', required: false)
  355. + conf.set('USING_PUPNP', upnp_dep.found())
  356. + if not upnp_dep.found()
  357. + upnp_dep = dependency('libnpupnp', version: '>= 1.8', required: false)
  358. + endif
  359. +elif upnp_option == 'pupnp'
  360. + upnp_dep = dependency('libupnp', version: '>= 1.8', required: true)
  361. + conf.set('USING_PUPNP', true)
  362. +elif upnp_option == 'npupnp'
  363. + upnp_dep = dependency('libnpupnp', required: true)
  364. + conf.set('USING_PUPNP', false)
  365. +elif upnp_option == 'disabled'
  366. + upnp_dep = dependency('', required: false)
  367. + subdir_done()
  368. +endif
  369. +
  370. conf.set('ENABLE_UPNP', upnp_dep.found())
  371. if not upnp_dep.found()
  372. subdir_done()