summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Kellermann <max@musicpd.org>2021-01-05 13:08:39 +0100
committerMax Kellermann <max@musicpd.org>2021-01-05 13:09:15 +0100
commite99f6b5b388a0480686c0659f8dfe07f5f9b61d0 (patch)
treebafce25950a8db72b9910ee7146486785c31d123
parent5348f8c9c88b930d06042e5103ffb617d2c09d75 (diff)
parent74b2fc7fdca9be13cbbe4cb52b2fab573b3cf82c (diff)
Merge branch 'bugfix/1039/fix-webdav' of git://github.com/PVince81/MPD into v0.22.x
Closes https://github.com/MusicPlayerDaemon/MPD/issues/1039
-rw-r--r--NEWS2
-rw-r--r--src/storage/plugins/CurlStorage.cxx61
-rw-r--r--src/util/UriExtract.cxx2
-rw-r--r--src/util/UriExtract.hxx2
4 files changed, 49 insertions, 18 deletions
diff --git a/NEWS b/NEWS
index 00248d505..05f56ef39 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,6 @@
ver 0.22.4 (not yet released)
+* storage
+ - curl: fix several WebDAV protocol bugs
* decoder
- dsdiff: apply padding to odd-sized chunks
* filter
diff --git a/src/storage/plugins/CurlStorage.cxx b/src/storage/plugins/CurlStorage.cxx
index ccfb0bf12..d717a824c 100644
--- a/src/storage/plugins/CurlStorage.cxx
+++ b/src/storage/plugins/CurlStorage.cxx
@@ -243,6 +243,7 @@ class PropfindOperation : BlockingHttpRequest, CommonExpatParser {
enum class State {
ROOT,
RESPONSE,
+ PROPSTAT,
HREF,
STATUS,
TYPE,
@@ -262,18 +263,19 @@ public:
request.SetOption(CURLOPT_MAXREDIRS, 1L);
request_headers.Append(StringFormat<40>("depth: %u", depth));
+ request_headers.Append("content-type: text/xml");
request.SetOption(CURLOPT_HTTPHEADER, request_headers.Get());
request.SetOption(CURLOPT_POSTFIELDS,
"<?xml version=\"1.0\"?>\n"
"<a:propfind xmlns:a=\"DAV:\">"
- "<a:prop><a:resourcetype/></a:prop>"
- "<a:prop><a:getcontenttype/></a:prop>"
- "<a:prop><a:getcontentlength/></a:prop>"
+ "<a:prop>"
+ "<a:resourcetype/>"
+ "<a:getcontenttype/>"
+ "<a:getcontentlength/>"
+ "</a:prop>"
"</a:propfind>");
-
- // TODO: send request body
}
using BlockingHttpRequest::GetEasy;
@@ -321,9 +323,13 @@ private:
break;
case State::RESPONSE:
- if (strcmp(name, "DAV:|href") == 0)
+ if (strcmp(name, "DAV:|propstat") == 0)
+ state = State::PROPSTAT;
+ else if (strcmp(name, "DAV:|href") == 0)
state = State::HREF;
- else if (strcmp(name, "DAV:|status") == 0)
+ break;
+ case State::PROPSTAT:
+ if (strcmp(name, "DAV:|status") == 0)
state = State::STATUS;
else if (strcmp(name, "DAV:|resourcetype") == 0)
state = State::TYPE;
@@ -353,9 +359,15 @@ private:
case State::RESPONSE:
if (strcmp(name, "DAV:|response") == 0) {
- FinishResponse();
state = State::ROOT;
}
+ break;
+
+ case State::PROPSTAT:
+ if (strcmp(name, "DAV:|propstat") == 0) {
+ FinishResponse();
+ state = State::RESPONSE;
+ }
break;
@@ -366,22 +378,22 @@ private:
case State::STATUS:
if (strcmp(name, "DAV:|status") == 0)
- state = State::RESPONSE;
+ state = State::PROPSTAT;
break;
case State::TYPE:
if (strcmp(name, "DAV:|resourcetype") == 0)
- state = State::RESPONSE;
+ state = State::PROPSTAT;
break;
case State::MTIME:
if (strcmp(name, "DAV:|getlastmodified") == 0)
- state = State::RESPONSE;
+ state = State::PROPSTAT;
break;
case State::LENGTH:
if (strcmp(name, "DAV:|getcontentlength") == 0)
- state = State::RESPONSE;
+ state = State::PROPSTAT;
break;
}
}
@@ -389,6 +401,7 @@ private:
void CharacterData(const XML_Char *s, int len) final {
switch (state) {
case State::ROOT:
+ case State::PROPSTAT:
case State::RESPONSE:
case State::TYPE:
break;
@@ -455,11 +468,19 @@ CurlStorage::GetInfo(std::string_view uri_utf8, [[maybe_unused]] bool follow)
gcc_pure
static std::string_view
-UriPathOrSlash(const char *uri) noexcept
+UriPathOrSlash(const char *uri, bool relative) noexcept
{
auto path = uri_get_path(uri);
if (path.data() == nullptr)
path = "/";
+ else if (relative) {
+ // search after first slash
+ path = path.substr(1);
+ auto slash = path.find('/');
+ if (slash != std::string_view::npos)
+ path = path.substr(slash);
+ }
+
return path;
}
@@ -468,13 +489,15 @@ UriPathOrSlash(const char *uri) noexcept
*/
class HttpListDirectoryOperation final : public PropfindOperation {
const std::string base_path;
+ const std::string base_path_relative;
MemoryStorageDirectoryReader::List entries;
public:
HttpListDirectoryOperation(CurlGlobal &curl, const char *uri)
:PropfindOperation(curl, uri, 1),
- base_path(CurlUnescape(GetEasy(), UriPathOrSlash(uri))) {}
+ base_path(CurlUnescape(GetEasy(), UriPathOrSlash(uri, false))),
+ base_path_relative(CurlUnescape(GetEasy(), UriPathOrSlash(uri, true))) {}
std::unique_ptr<StorageDirectoryReader> Perform() {
DeferStart();
@@ -500,9 +523,15 @@ private:
/* kludge: ignoring case in this comparison to avoid
false negatives if the web server uses a different
case */
- path = StringAfterPrefixIgnoreCase(path, base_path.c_str());
- if (path == nullptr || path.empty())
+ if (uri_has_scheme(path)) {
+ path = StringAfterPrefixIgnoreCase(path, base_path.c_str());
+ } else {
+ path = StringAfterPrefixIgnoreCase(path, base_path_relative.c_str());
+ }
+
+ if (path == nullptr || path.empty()) {
return nullptr;
+ }
const char *slash = path.Find('/');
if (slash == nullptr)
diff --git a/src/util/UriExtract.cxx b/src/util/UriExtract.cxx
index 053c20cc7..ab8a7244a 100644
--- a/src/util/UriExtract.cxx
+++ b/src/util/UriExtract.cxx
@@ -85,7 +85,7 @@ uri_after_scheme(std::string_view uri) noexcept
}
bool
-uri_has_scheme(const char *uri) noexcept
+uri_has_scheme(std::string_view uri) noexcept
{
return !uri_get_scheme(uri).empty();
}
diff --git a/src/util/UriExtract.hxx b/src/util/UriExtract.hxx
index 05e288b30..5f17910ea 100644
--- a/src/util/UriExtract.hxx
+++ b/src/util/UriExtract.hxx
@@ -40,7 +40,7 @@
*/
gcc_pure
bool
-uri_has_scheme(const char *uri) noexcept;
+uri_has_scheme(std::string_view uri) noexcept;
/**
* Returns the scheme name of the specified URI, or an empty string.