ContentDirectoryService.cxx 5.36 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright (C) 2003-2015 The Music Player Daemon Project
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 * 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
#include "lib/upnp/ContentDirectoryService.hxx"
#include "lib/upnp/Domain.hxx"
#include "lib/upnp/ixmlwrap.hxx"
24
#include "lib/upnp/UniqueIxml.hxx"
25
#include "lib/upnp/Action.hxx"
26
#include "Directory.hxx"
27
#include "util/NumberParser.hxx"
28
#include "util/UriUtil.hxx"
29
#include "util/RuntimeError.hxx"
30
#include "util/Error.hxx"
31
#include "util/ScopeExit.hxx"
32 33 34

#include <stdio.h>

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

42
	dirbuf.Parse(p);
43 44
}

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

68 69
	AtScopeExit(request) { ixmlDocument_free(request); };

70
	IXML_Document *response;
71 72
	int code = UpnpSendAction(hdl, m_actionURL.c_str(), m_serviceType.c_str(),
				  0 /*devUDN*/, request, &response);
73 74 75
	if (code != UPNP_E_SUCCESS)
		throw FormatRuntimeError("UpnpSendAction() failed: %s",
					 UpnpGetErrorMessage(code));
76

77 78
	AtScopeExit(response) { ixmlDocument_free(response); };

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

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

88
	ReadResultTag(dirbuf, response);
89 90
}

91
UPnPDirContent
92
ContentDirectoryService::readDir(UpnpClient_Handle handle,
93
				 const char *objectId) const
94
{
95
	UPnPDirContent dirbuf;
96
	unsigned offset = 0, total = -1, count;
97

98
	do {
99 100
		readDirSlice(handle, objectId, offset, m_rdreqcnt, dirbuf,
			     count, total);
101 102

		offset += count;
103
	} while (count > 0 && offset < total);
104

105
	return dirbuf;
106 107
}

108
UPnPDirContent
109 110
ContentDirectoryService::search(UpnpClient_Handle hdl,
				const char *objectId,
111
				const char *ss) const
112
{
113
	UPnPDirContent dirbuf;
114
	unsigned offset = 0, total = -1, count;
115

116
	do {
117 118
		char ofbuf[100];
		sprintf(ofbuf, "%d", offset);
119

120 121 122 123 124 125 126 127
		UniqueIxmlDocument request(MakeActionHelper("Search", m_serviceType.c_str(),
							    "ContainerID", objectId,
							    "SearchCriteria", ss,
							    "Filter", "*",
							    "SortCriteria", "",
							    "StartingIndex", ofbuf,
							    "RequestedCount", "0")); // Setting a value here gets twonky into fits
		if (!request)
128
			throw std::runtime_error("UpnpMakeAction() failed");
129

130
		IXML_Document *_response;
131 132
		auto code = UpnpSendAction(hdl, m_actionURL.c_str(),
					   m_serviceType.c_str(),
133 134
					   0 /*devUDN*/,
					   request.get(), &_response);
135 136 137
		if (code != UPNP_E_SUCCESS)
			throw FormatRuntimeError("UpnpSendAction() failed: %s",
						 UpnpGetErrorMessage(code));
138

139 140
		UniqueIxmlDocument response(_response);

141
		const char *value =
142 143
			ixmlwrap::getFirstElementValue(response.get(),
						       "NumberReturned");
144
		count = value != nullptr
145 146
			? ParseUnsigned(value)
			: 0;
147 148 149

		offset += count;

150 151
		value = ixmlwrap::getFirstElementValue(response.get(),
						       "TotalMatches");
152
		if (value != nullptr)
153
			total = ParseUnsigned(value);
154

155
		ReadResultTag(dirbuf, response.get());
156
	} while (count > 0 && offset < total);
157

158
	return dirbuf;
159 160
}

161
UPnPDirContent
162
ContentDirectoryService::getMetadata(UpnpClient_Handle hdl,
163
				     const char *objectId) const
164 165
{
	// Create request
166 167 168 169 170 171 172
	UniqueIxmlDocument request(MakeActionHelper("Browse", m_serviceType.c_str(),
						    "ObjectID", objectId,
						    "BrowseFlag", "BrowseMetadata",
						    "Filter", "*",
						    "SortCriteria", "",
						    "StartingIndex", "0",
						    "RequestedCount", "1"));
173 174
	if (request == nullptr)
		throw std::runtime_error("UpnpMakeAction() failed");
175

176
	IXML_Document *_response;
177 178
	auto code = UpnpSendAction(hdl, m_actionURL.c_str(),
				   m_serviceType.c_str(),
179
				   0 /*devUDN*/, request.get(), &_response);
180 181 182
	if (code != UPNP_E_SUCCESS)
		throw FormatRuntimeError("UpnpSendAction() failed: %s",
					 UpnpGetErrorMessage(code));
183

184
	UniqueIxmlDocument response(_response);
185 186 187
	UPnPDirContent dirbuf;
	ReadResultTag(dirbuf, response.get());
	return dirbuf;
188
}