--- a/libupnpp/cdirectory.cxx
+++ b/libupnpp/cdirectory.cxx
@@ -23,11 +23,9 @@
#include <iostream>
#include <set>
#include <vector>
-using std::string;
-using std::cerr;
-using std::endl;
-using std::vector;
-using std::set;
+using namespace std;
+#include <functional>
+using namespace std::placeholders;
#include "upnpp_p.hxx"
@@ -38,310 +36,367 @@
#include "ixmlwrap.hxx"
#include "cdirectory.hxx"
#include "cdircontent.hxx"
+#include "discovery.hxx"
+
+namespace UPnPClient {
+
+// The service type string for Content Directories:
+const string ContentDirectoryService::SType("urn:schemas-upnp-org:service:ContentDirectory:1");
+
+// We don't include a version in comparisons, as we are satisfied with
+// version 1
+bool ContentDirectoryService::isCDService(const string& st)
+{
+ const string::size_type sz(SType.size()-2);
+ return !SType.compare(0, sz, st, 0, sz);
+}
+
+
+static bool DSAccum(vector<ContentDirectoryService>* out,
+ const UPnPDeviceDesc& device,
+ const UPnPServiceDesc& service)
+{
+ if (ContentDirectoryService::isCDService(service.serviceType)) {
+ out->push_back(ContentDirectoryService(device, service));
+ }
+ return true;
+}
+
+bool ContentDirectoryService::getServices(vector<ContentDirectoryService>& vds)
+{
+ //LOGDEB("UPnPDeviceDirectory::getDirServices" << endl);
+ UPnPDeviceDirectory::Visitor visitor = bind(DSAccum, &vds, _1, _2);
+ UPnPDeviceDirectory::getTheDir()->traverse(visitor);
+ return !vds.empty();
+}
+
+static bool DSFriendlySelect(const string& friendlyName,
+ bool *found,
+ ContentDirectoryService *out,
+ const UPnPDeviceDesc& device,
+ const UPnPServiceDesc& service)
+{
+ if (ContentDirectoryService::isCDService(service.serviceType)) {
+ if (!friendlyName.compare(device.friendlyName)) {
+ *out = ContentDirectoryService(device, service);
+ *found = true;
+ return false;
+ }
+ }
+ return true;
+}
+
+// Get server by friendly name.
+bool ContentDirectoryService::getServerByName(const string& friendlyName,
+ ContentDirectoryService& server)
+{
+ bool found = false;
+ UPnPDeviceDirectory::Visitor visitor =
+ bind(DSFriendlySelect, friendlyName, &found, &server, _1, _2);
+ UPnPDeviceDirectory::getTheDir()->traverse(visitor);
+ return found;
+}
+
class DirBResFree {
public:
- IXML_Document **rqpp, **rspp;
- DirBResFree(IXML_Document** _rqpp, IXML_Document **_rspp)
- :rqpp(_rqpp), rspp(_rspp)
- {}
- ~DirBResFree()
- {
- if (*rqpp)
- ixmlDocument_free(*rqpp);
- if (*rspp)
- ixmlDocument_free(*rspp);
- }
+ IXML_Document **rqpp, **rspp;
+ DirBResFree(IXML_Document** _rqpp, IXML_Document **_rspp)
+ :rqpp(_rqpp), rspp(_rspp)
+ {}
+ ~DirBResFree()
+ {
+ if (*rqpp)
+ ixmlDocument_free(*rqpp);
+ if (*rspp)
+ ixmlDocument_free(*rspp);
+ }
};
int ContentDirectoryService::readDirSlice(const string& objectId, int offset,
- int count, UPnPDirContent& dirbuf,
- int *didreadp, int *totalp)
-{
- PLOGDEB("CDService::readDirSlice: objId [%s] offset %d count %d\n",
- objectId.c_str(), offset, count);
- LibUPnP* lib = LibUPnP::getLibUPnP();
- if (lib == 0) {
- PLOGINF("CDService::readDir: no lib\n");
- return UPNP_E_OUTOF_MEMORY;
- }
- UpnpClient_Handle hdl = lib->getclh();
-
- IXML_Document *request(0);
- IXML_Document *response(0);
- DirBResFree cleaner(&request, &response);
-
- // Create request
- char ofbuf[100], cntbuf[100];
- sprintf(ofbuf, "%d", offset);
- sprintf(cntbuf, "%d", count);
- int argcnt = 6;
- // Some devices require an empty SortCriteria, else bad params
- request = UpnpMakeAction("Browse", m_serviceType.c_str(), argcnt,
- "ObjectID", objectId.c_str(),
- "BrowseFlag", "BrowseDirectChildren",
- "Filter", "*",
- "SortCriteria", "",
- "StartingIndex", ofbuf,
- "RequestedCount", cntbuf,
- NULL, NULL);
- if (request == 0) {
- PLOGINF("CDService::readDir: UpnpMakeAction failed\n");
- return UPNP_E_OUTOF_MEMORY;
- }
-
- //cerr << "Action xml: [" << ixmlPrintDocument(request) << "]" << endl;
-
- int ret = UpnpSendAction(hdl, m_actionURL.c_str(), m_serviceType.c_str(),
- 0 /*devUDN*/, request, &response);
-
- if (ret != UPNP_E_SUCCESS) {
- PLOGINF("CDService::readDir: UpnpSendAction failed: %s\n",
- UpnpGetErrorMessage(ret));
- return ret;
- }
-
- int didread = -1;
- string tbuf = ixmlwrap::getFirstElementValue(response, "NumberReturned");
- if (!tbuf.empty())
- didread = atoi(tbuf.c_str());
-
- if (count == -1 || count == 0) {
- PLOGINF("CDService::readDir: got -1 or 0 entries\n");
- return UPNP_E_BAD_RESPONSE;
- }
-
- tbuf = ixmlwrap::getFirstElementValue(response, "TotalMatches");
- if (!tbuf.empty())
- *totalp = atoi(tbuf.c_str());
-
- tbuf = ixmlwrap::getFirstElementValue(response, "Result");
+ int count, UPnPDirContent& dirbuf,
+ int *didreadp, int *totalp)
+{
+ PLOGDEB("CDService::readDirSlice: objId [%s] offset %d count %d\n",
+ objectId.c_str(), offset, count);
+ LibUPnP* lib = LibUPnP::getLibUPnP();
+ if (lib == 0) {
+ PLOGINF("CDService::readDir: no lib\n");
+ return UPNP_E_OUTOF_MEMORY;
+ }
+ UpnpClient_Handle hdl = lib->getclh();
+
+ IXML_Document *request(0);
+ IXML_Document *response(0);
+ DirBResFree cleaner(&request, &response);
+
+ // Create request
+ char ofbuf[100], cntbuf[100];
+ sprintf(ofbuf, "%d", offset);
+ sprintf(cntbuf, "%d", count);
+ int argcnt = 6;
+ // Some devices require an empty SortCriteria, else bad params
+ request = UpnpMakeAction("Browse", m_serviceType.c_str(), argcnt,
+ "ObjectID", objectId.c_str(),
+ "BrowseFlag", "BrowseDirectChildren",
+ "Filter", "*",
+ "SortCriteria", "",
+ "StartingIndex", ofbuf,
+ "RequestedCount", cntbuf,
+ NULL, NULL);
+ if (request == 0) {
+ PLOGINF("CDService::readDir: UpnpMakeAction failed\n");
+ return UPNP_E_OUTOF_MEMORY;
+ }
+
+ //cerr << "Action xml: [" << ixmlPrintDocument(request) << "]" << endl;
+
+ int ret = UpnpSendAction(hdl, m_actionURL.c_str(), m_serviceType.c_str(),
+ 0 /*devUDN*/, request, &response);
+
+ if (ret != UPNP_E_SUCCESS) {
+ PLOGINF("CDService::readDir: UpnpSendAction failed: %s\n",
+ UpnpGetErrorMessage(ret));
+ return ret;
+ }
+
+ int didread = -1;
+ string tbuf = ixmlwrap::getFirstElementValue(response, "NumberReturned");
+ if (!tbuf.empty())
+ didread = atoi(tbuf.c_str());
+
+ if (count == -1 || count == 0) {
+ PLOGINF("CDService::readDir: got -1 or 0 entries\n");
+ return UPNP_E_BAD_RESPONSE;
+ }
+
+ tbuf = ixmlwrap::getFirstElementValue(response, "TotalMatches");
+ if (!tbuf.empty())
+ *totalp = atoi(tbuf.c_str());
+
+ tbuf = ixmlwrap::getFirstElementValue(response, "Result");
#if 0
- cerr << "CDService::readDirSlice: count " << count <<
- " offset " << offset <<
- " total " << *totalp << endl;
- cerr << " result " << tbuf << endl;
+ cerr << "CDService::readDirSlice: count " << count <<
+ " offset " << offset <<
+ " total " << *totalp << endl;
+ cerr << " result " << tbuf << endl;
#endif
- dirbuf.parse(tbuf);
- *didreadp = didread;
- return UPNP_E_SUCCESS;
+ dirbuf.parse(tbuf);
+ *didreadp = didread;
+ return UPNP_E_SUCCESS;
}
int ContentDirectoryService::readDir(const string& objectId,
- UPnPDirContent& dirbuf)
-{
- PLOGDEB("CDService::readDir: url [%s] type [%s] udn [%s] objId [%s]\n",
- m_actionURL.c_str(), m_serviceType.c_str(), m_deviceId.c_str(),
- objectId.c_str());
-
- int offset = 0;
- int total = 1000;// Updated on first read.
-
- while (offset < total) {
- int count;
- int error = readDirSlice(objectId, offset, m_rdreqcnt, dirbuf,
- &count, &total);
- if (error != UPNP_E_SUCCESS)
- return error;
-
- offset += count;
- }
-
- return UPNP_E_SUCCESS;
+ UPnPDirContent& dirbuf)
+{
+ PLOGDEB("CDService::readDir: url [%s] type [%s] udn [%s] objId [%s]\n",
+ m_actionURL.c_str(), m_serviceType.c_str(), m_deviceId.c_str(),
+ objectId.c_str());
+
+ int offset = 0;
+ int total = 1000;// Updated on first read.
+
+ while (offset < total) {
+ int count;
+ int error = readDirSlice(objectId, offset, m_rdreqcnt, dirbuf,
+ &count, &total);
+ if (error != UPNP_E_SUCCESS)
+ return error;
+
+ offset += count;
+ }
+
+ return UPNP_E_SUCCESS;
}
int ContentDirectoryService::search(const string& objectId,
- const string& ss,
- UPnPDirContent& dirbuf)
-{
- PLOGDEB("CDService::search: url [%s] type [%s] udn [%s] objid [%s] "
- "search [%s]\n",
- m_actionURL.c_str(), m_serviceType.c_str(), m_deviceId.c_str(),
- objectId.c_str(), ss.c_str());
-
- LibUPnP* lib = LibUPnP::getLibUPnP();
- if (lib == 0) {
- PLOGINF("CDService::search: no lib\n");
- return UPNP_E_OUTOF_MEMORY;
- }
- UpnpClient_Handle hdl = lib->getclh();
-
- int ret = UPNP_E_SUCCESS;
- IXML_Document *request(0);
- IXML_Document *response(0);
-
- int offset = 0;
- int total = 1000;// Updated on first read.
-
- while (offset < total) {
- DirBResFree cleaner(&request, &response);
- char ofbuf[100];
- sprintf(ofbuf, "%d", offset);
- // Create request
- int argcnt = 6;
- request = UpnpMakeAction(
- "Search", m_serviceType.c_str(), argcnt,
- "ContainerID", objectId.c_str(),
- "SearchCriteria", ss.c_str(),
- "Filter", "*",
- "SortCriteria", "",
- "StartingIndex", ofbuf,
- "RequestedCount", "0", // Setting a value here gets twonky into fits
- NULL, NULL);
- if (request == 0) {
- PLOGINF("CDService::search: UpnpMakeAction failed\n");
- return UPNP_E_OUTOF_MEMORY;
- }
-
- // cerr << "Action xml: [" << ixmlPrintDocument(request) << "]" << endl;
-
- ret = UpnpSendAction(hdl, m_actionURL.c_str(), m_serviceType.c_str(),
- 0 /*devUDN*/, request, &response);
-
- if (ret != UPNP_E_SUCCESS) {
- PLOGINF("CDService::search: UpnpSendAction failed: %s\n",
- UpnpGetErrorMessage(ret));
- return ret;
- }
-
- int count = -1;
- string tbuf =
- ixmlwrap::getFirstElementValue(response, "NumberReturned");
- if (!tbuf.empty())
- count = atoi(tbuf.c_str());
-
- if (count == -1 || count == 0) {
- PLOGINF("CDService::search: got -1 or 0 entries\n");
- return count == -1 ? UPNP_E_BAD_RESPONSE : UPNP_E_SUCCESS;
- }
- offset += count;
-
- tbuf = ixmlwrap::getFirstElementValue(response, "TotalMatches");
- if (!tbuf.empty())
- total = atoi(tbuf.c_str());
-
- tbuf = ixmlwrap::getFirstElementValue(response, "Result");
+ const string& ss,
+ UPnPDirContent& dirbuf)
+{
+ PLOGDEB("CDService::search: url [%s] type [%s] udn [%s] objid [%s] "
+ "search [%s]\n",
+ m_actionURL.c_str(), m_serviceType.c_str(), m_deviceId.c_str(),
+ objectId.c_str(), ss.c_str());
+
+ LibUPnP* lib = LibUPnP::getLibUPnP();
+ if (lib == 0) {
+ PLOGINF("CDService::search: no lib\n");
+ return UPNP_E_OUTOF_MEMORY;
+ }
+ UpnpClient_Handle hdl = lib->getclh();
+
+ int ret = UPNP_E_SUCCESS;
+ IXML_Document *request(0);
+ IXML_Document *response(0);
+
+ int offset = 0;
+ int total = 1000;// Updated on first read.
+
+ while (offset < total) {
+ DirBResFree cleaner(&request, &response);
+ char ofbuf[100];
+ sprintf(ofbuf, "%d", offset);
+ // Create request
+ int argcnt = 6;
+ request = UpnpMakeAction(
+ "Search", m_serviceType.c_str(), argcnt,
+ "ContainerID", objectId.c_str(),
+ "SearchCriteria", ss.c_str(),
+ "Filter", "*",
+ "SortCriteria", "",
+ "StartingIndex", ofbuf,
+ "RequestedCount", "0", // Setting a value here gets twonky into fits
+ NULL, NULL);
+ if (request == 0) {
+ PLOGINF("CDService::search: UpnpMakeAction failed\n");
+ return UPNP_E_OUTOF_MEMORY;
+ }
+
+ // cerr << "Action xml: [" << ixmlPrintDocument(request) << "]" << endl;
+
+ ret = UpnpSendAction(hdl, m_actionURL.c_str(), m_serviceType.c_str(),
+ 0 /*devUDN*/, request, &response);
+
+ if (ret != UPNP_E_SUCCESS) {
+ PLOGINF("CDService::search: UpnpSendAction failed: %s\n",
+ UpnpGetErrorMessage(ret));
+ return ret;
+ }
+
+ int count = -1;
+ string tbuf =
+ ixmlwrap::getFirstElementValue(response, "NumberReturned");
+ if (!tbuf.empty())
+ count = atoi(tbuf.c_str());
+
+ if (count == -1 || count == 0) {
+ PLOGINF("CDService::search: got -1 or 0 entries\n");
+ return count == -1 ? UPNP_E_BAD_RESPONSE : UPNP_E_SUCCESS;
+ }
+ offset += count;
+
+ tbuf = ixmlwrap::getFirstElementValue(response, "TotalMatches");
+ if (!tbuf.empty())
+ total = atoi(tbuf.c_str());
+
+ tbuf = ixmlwrap::getFirstElementValue(response, "Result");
#if 0
- cerr << "CDService::search: count " << count <<
- " offset " << offset <<
- " total " << total << endl;
- cerr << " result " << tbuf << endl;
+ cerr << "CDService::search: count " << count <<
+ " offset " << offset <<
+ " total " << total << endl;
+ cerr << " result " << tbuf << endl;
#endif
- dirbuf.parse(tbuf);
- }
-
- return UPNP_E_SUCCESS;
+ dirbuf.parse(tbuf);
+ }
+
+ return UPNP_E_SUCCESS;
}
int ContentDirectoryService::getSearchCapabilities(set<string>& result)
{
- PLOGDEB("CDService::getSearchCapabilities:\n");
- LibUPnP* lib = LibUPnP::getLibUPnP();
- if (lib == 0) {
- PLOGINF("CDService::getSearchCapabilities: no lib\n");
- return UPNP_E_OUTOF_MEMORY;
- }
- UpnpClient_Handle hdl = lib->getclh();
-
- int ret = UPNP_E_SUCCESS;
- IXML_Document *request(0);
- IXML_Document *response(0);
-
- request = UpnpMakeAction("GetSearchCapabilities", m_serviceType.c_str(),
- 0,
- NULL, NULL);
-
- if (request == 0) {
- PLOGINF("CDService::getSearchCapa: UpnpMakeAction failed\n");
- return UPNP_E_OUTOF_MEMORY;
- }
-
- //cerr << "Action xml: [" << ixmlPrintDocument(request) << "]" << endl;
-
- ret = UpnpSendAction(hdl, m_actionURL.c_str(), m_serviceType.c_str(),
- 0 /*devUDN*/, request, &response);
-
- if (ret != UPNP_E_SUCCESS) {
- PLOGINF("CDService::getSearchCapa: UpnpSendAction failed: %s\n",
- UpnpGetErrorMessage(ret));
- return ret;
- }
- //cerr << "getSearchCapa: response xml: [" << ixmlPrintDocument(response)
- // << "]" << endl;
-
- string tbuf = ixmlwrap::getFirstElementValue(response, "SearchCaps");
- // cerr << "getSearchCapa: capa: [" << tbuf << "]" << endl;
-
- result.clear();
- if (!tbuf.compare("*")) {
- result.insert(result.end(), "*");
- } else if (!tbuf.empty()) {
- if (!csvToStrings(tbuf, result)) {
- return UPNP_E_BAD_RESPONSE;
- }
- }
-
- return UPNP_E_SUCCESS;
+ PLOGDEB("CDService::getSearchCapabilities:\n");
+ LibUPnP* lib = LibUPnP::getLibUPnP();
+ if (lib == 0) {
+ PLOGINF("CDService::getSearchCapabilities: no lib\n");
+ return UPNP_E_OUTOF_MEMORY;
+ }
+ UpnpClient_Handle hdl = lib->getclh();
+
+ int ret = UPNP_E_SUCCESS;
+ IXML_Document *request(0);
+ IXML_Document *response(0);
+
+ request = UpnpMakeAction("GetSearchCapabilities", m_serviceType.c_str(),
+ 0,
+ NULL, NULL);
+
+ if (request == 0) {
+ PLOGINF("CDService::getSearchCapa: UpnpMakeAction failed\n");
+ return UPNP_E_OUTOF_MEMORY;
+ }
+
+ //cerr << "Action xml: [" << ixmlPrintDocument(request) << "]" << endl;
+
+ ret = UpnpSendAction(hdl, m_actionURL.c_str(), m_serviceType.c_str(),
+ 0 /*devUDN*/, request, &response);
+
+ if (ret != UPNP_E_SUCCESS) {
+ PLOGINF("CDService::getSearchCapa: UpnpSendAction failed: %s\n",
+ UpnpGetErrorMessage(ret));
+ return ret;
+ }
+ //cerr << "getSearchCapa: response xml: [" << ixmlPrintDocument(response)
+ // << "]" << endl;
+
+ string tbuf = ixmlwrap::getFirstElementValue(response, "SearchCaps");
+ // cerr << "getSearchCapa: capa: [" << tbuf << "]" << endl;
+
+ result.clear();
+ if (!tbuf.compare("*")) {
+ result.insert(result.end(), "*");
+ } else if (!tbuf.empty()) {
+ if (!csvToStrings(tbuf, result)) {
+ return UPNP_E_BAD_RESPONSE;
+ }
+ }
+
+ return UPNP_E_SUCCESS;
}
int ContentDirectoryService::getMetadata(const string& objectId,
- UPnPDirContent& dirbuf)
-{
- PLOGDEB("CDService::getMetadata: url [%s] type [%s] udn [%s] objId [%s]\n",
- m_actionURL.c_str(), m_serviceType.c_str(), m_deviceId.c_str(),
- objectId.c_str());
-
- LibUPnP* lib = LibUPnP::getLibUPnP();
- if (lib == 0) {
- PLOGINF("CDService::getMetadata: no lib\n");
- return UPNP_E_OUTOF_MEMORY;
- }
- UpnpClient_Handle hdl = lib->getclh();
-
- int ret = UPNP_E_SUCCESS;
- IXML_Document *request(0);
- IXML_Document *response(0);
-
- DirBResFree cleaner(&request, &response);
- // Create request
- int argcnt = 6;
- request = UpnpMakeAction("Browse", m_serviceType.c_str(), argcnt,
- "ObjectID", objectId.c_str(),
- "BrowseFlag", "BrowseMetadata",
- "Filter", "*",
- "SortCriteria", "",
- "StartingIndex", "0",
- "RequestedCount", "1",
- NULL, NULL);
- if (request == 0) {
- PLOGINF("CDService::getmetadata: UpnpMakeAction failed\n");
- return UPNP_E_OUTOF_MEMORY;
- }
-
- //cerr << "Action xml: [" << ixmlPrintDocument(request) << "]" << endl;
-
- ret = UpnpSendAction(hdl, m_actionURL.c_str(), m_serviceType.c_str(),
- 0 /*devUDN*/, request, &response);
-
- if (ret != UPNP_E_SUCCESS) {
- PLOGINF("CDService::getmetadata: UpnpSendAction failed: %s\n",
- UpnpGetErrorMessage(ret));
- return ret;
- }
- string tbuf = ixmlwrap::getFirstElementValue(response, "Result");
- if (dirbuf.parse(tbuf))
- return UPNP_E_SUCCESS;
- else
- return UPNP_E_BAD_RESPONSE;
-}
-/* Local Variables: */
-/* mode: c++ */
-/* c-basic-offset: 4 */
-/* tab-width: 4 */
-/* indent-tabs-mode: t */
-/* End: */
+ UPnPDirContent& dirbuf)
+{
+ PLOGDEB("CDService::getMetadata: url [%s] type [%s] udn [%s] objId [%s]\n",
+ m_actionURL.c_str(), m_serviceType.c_str(), m_deviceId.c_str(),
+ objectId.c_str());
+
+ LibUPnP* lib = LibUPnP::getLibUPnP();
+ if (lib == 0) {
+ PLOGINF("CDService::getMetadata: no lib\n");
+ return UPNP_E_OUTOF_MEMORY;
+ }
+ UpnpClient_Handle hdl = lib->getclh();
+
+ int ret = UPNP_E_SUCCESS;
+ IXML_Document *request(0);
+ IXML_Document *response(0);
+
+ DirBResFree cleaner(&request, &response);
+ // Create request
+ int argcnt = 6;
+ request = UpnpMakeAction("Browse", m_serviceType.c_str(), argcnt,
+ "ObjectID", objectId.c_str(),
+ "BrowseFlag", "BrowseMetadata",
+ "Filter", "*",
+ "SortCriteria", "",
+ "StartingIndex", "0",
+ "RequestedCount", "1",
+ NULL, NULL);
+ if (request == 0) {
+ PLOGINF("CDService::getmetadata: UpnpMakeAction failed\n");
+ return UPNP_E_OUTOF_MEMORY;
+ }
+
+ //cerr << "Action xml: [" << ixmlPrintDocument(request) << "]" << endl;
+
+ ret = UpnpSendAction(hdl, m_actionURL.c_str(), m_serviceType.c_str(),
+ 0 /*devUDN*/, request, &response);
+
+ if (ret != UPNP_E_SUCCESS) {
+ PLOGINF("CDService::getmetadata: UpnpSendAction failed: %s\n",
+ UpnpGetErrorMessage(ret));
+ return ret;
+ }
+ string tbuf = ixmlwrap::getFirstElementValue(response, "Result");
+ if (dirbuf.parse(tbuf))
+ return UPNP_E_SUCCESS;
+ else
+ return UPNP_E_BAD_RESPONSE;
+}
+
+} // namespace UPnPClient