ContentDirectoryService.cxx 5.51 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
 * Copyright (C) 2003-2014 The Music Player Daemon Project
 * http://www.musicpd.org
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "config.h"
21 22 23 24
#include "lib/upnp/ContentDirectoryService.hxx"
#include "lib/upnp/Domain.hxx"
#include "lib/upnp/ixmlwrap.hxx"
#include "lib/upnp/Action.hxx"
25
#include "Directory.hxx"
26
#include "util/NumberParser.hxx"
27
#include "util/UriUtil.hxx"
28 29 30 31
#include "util/Error.hxx"

#include <stdio.h>

32 33 34 35 36 37 38 39 40 41
static bool
ReadResultTag(UPnPDirContent &dirbuf, IXML_Document *response, Error &error)
{
	const char *p = ixmlwrap::getFirstElementValue(response, "Result");
	if (p == nullptr)
		p = "";

	return dirbuf.parse(p, error);
}

42
inline bool
43
ContentDirectoryService::readDirSlice(UpnpClient_Handle hdl,
44 45 46
				      const char *objectId, unsigned offset,
				      unsigned count, UPnPDirContent &dirbuf,
				      unsigned &didreadp, unsigned &totalp,
47
				      Error &error) const
48 49 50
{
	// Create request
	char ofbuf[100], cntbuf[100];
51 52
	sprintf(ofbuf, "%u", offset);
	sprintf(cntbuf, "%u", count);
53
	// Some devices require an empty SortCriteria, else bad params
54
	IXML_Document *request =
55 56 57 58 59 60 61
		MakeActionHelper("Browse", m_serviceType.c_str(),
				 "ObjectID", objectId,
				 "BrowseFlag", "BrowseDirectChildren",
				 "Filter", "*",
				 "SortCriteria", "",
				 "StartingIndex", ofbuf,
				 "RequestedCount", cntbuf);
62 63 64 65 66
	if (request == nullptr) {
		error.Set(upnp_domain, "UpnpMakeAction() failed");
		return false;
	}

67
	IXML_Document *response;
68 69
	int code = UpnpSendAction(hdl, m_actionURL.c_str(), m_serviceType.c_str(),
				  0 /*devUDN*/, request, &response);
70
	ixmlDocument_free(request);
71 72 73 74 75 76 77
	if (code != UPNP_E_SUCCESS) {
		error.Format(upnp_domain, code,
			     "UpnpSendAction() failed: %s",
			     UpnpGetErrorMessage(code));
		return false;
	}

78
	const char *value = ixmlwrap::getFirstElementValue(response, "NumberReturned");
79 80 81
	didreadp = value != nullptr
		? ParseUnsigned(value)
		: 0;
82

83 84
	value = ixmlwrap::getFirstElementValue(response, "TotalMatches");
	if (value != nullptr)
85
		totalp = ParseUnsigned(value);
86

87 88 89
	bool success = ReadResultTag(dirbuf, response, error);
	ixmlDocument_free(response);
	return success;
90 91 92
}

bool
93 94
ContentDirectoryService::readDir(UpnpClient_Handle handle,
				 const char *objectId,
95
				 UPnPDirContent &dirbuf,
96
				 Error &error) const
97
{
98
	unsigned offset = 0, total = -1, count;
99

100
	do {
101
		if (!readDirSlice(handle, objectId, offset, m_rdreqcnt, dirbuf,
102
				  count, total, error))
103 104 105
			return false;

		offset += count;
106
	} while (count > 0 && offset < total);
107 108 109 110 111

	return true;
}

bool
112 113
ContentDirectoryService::search(UpnpClient_Handle hdl,
				const char *objectId,
114 115
				const char *ss,
				UPnPDirContent &dirbuf,
116
				Error &error) const
117
{
118
	unsigned offset = 0, total = -1, count;
119

120
	do {
121 122
		char ofbuf[100];
		sprintf(ofbuf, "%d", offset);
123 124

		IXML_Document *request =
125 126 127 128 129 130 131
			MakeActionHelper("Search", m_serviceType.c_str(),
					 "ContainerID", objectId,
					 "SearchCriteria", ss,
					 "Filter", "*",
					 "SortCriteria", "",
					 "StartingIndex", ofbuf,
					 "RequestedCount", "0"); // Setting a value here gets twonky into fits
132 133 134 135 136
		if (request == 0) {
			error.Set(upnp_domain, "UpnpMakeAction() failed");
			return false;
		}

137
		IXML_Document *response;
138 139 140
		auto code = UpnpSendAction(hdl, m_actionURL.c_str(),
					   m_serviceType.c_str(),
					   0 /*devUDN*/, request, &response);
141
		ixmlDocument_free(request);
142 143 144 145 146 147 148
		if (code != UPNP_E_SUCCESS) {
			error.Format(upnp_domain, code,
				     "UpnpSendAction() failed: %s",
				     UpnpGetErrorMessage(code));
			return false;
		}

149
		const char *value =
150
			ixmlwrap::getFirstElementValue(response, "NumberReturned");
151
		count = value != nullptr
152 153
			? ParseUnsigned(value)
			: 0;
154 155 156

		offset += count;

157 158
		value = ixmlwrap::getFirstElementValue(response, "TotalMatches");
		if (value != nullptr)
159
			total = ParseUnsigned(value);
160

161 162 163
		bool success = ReadResultTag(dirbuf, response, error);
		ixmlDocument_free(response);
		if (!success)
164
			return false;
165
	} while (count > 0 && offset < total);
166 167 168 169 170

	return true;
}

bool
171 172
ContentDirectoryService::getMetadata(UpnpClient_Handle hdl,
				     const char *objectId,
173
				     UPnPDirContent &dirbuf,
174
				     Error &error) const
175 176 177
{
	// Create request
	IXML_Document *request =
178 179 180 181 182 183 184
		MakeActionHelper("Browse", m_serviceType.c_str(),
				 "ObjectID", objectId,
				 "BrowseFlag", "BrowseMetadata",
				 "Filter", "*",
				 "SortCriteria", "",
				 "StartingIndex", "0",
				 "RequestedCount", "1");
185 186 187 188 189
	if (request == nullptr) {
		error.Set(upnp_domain, "UpnpMakeAction() failed");
		return false;
	}

190
	IXML_Document *response;
191 192 193
	auto code = UpnpSendAction(hdl, m_actionURL.c_str(),
				   m_serviceType.c_str(),
				   0 /*devUDN*/, request, &response);
194
	ixmlDocument_free(request);
195 196 197 198 199 200 201
	if (code != UPNP_E_SUCCESS) {
		error.Format(upnp_domain, code,
			     "UpnpSendAction() failed: %s",
			     UpnpGetErrorMessage(code));
		return false;
	}

202
	bool success = ReadResultTag(dirbuf, response, error);
203
	ixmlDocument_free(response);
204
	return success;
205
}