summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Kellermann <max@musicpd.org>2019-10-16 12:03:12 +0200
committerMax Kellermann <max@musicpd.org>2019-10-16 12:03:12 +0200
commite1867a99e99de1ee6997eaf9068179207eb1bef3 (patch)
tree8f5dcc922941d5ee82939ac11997cf498efe2ae6
parentb2c4a5db149ebedd0f130ce02156d86b565eb4b8 (diff)
parent9b95e65bd9ba9d49e1d29f42c64abede7a9b2a92 (diff)
Merge tag 'v0.21.16'
release v0.21.16
-rw-r--r--NEWS11
-rw-r--r--android/AndroidManifest.xml4
-rw-r--r--src/db/update/Walk.cxx6
-rw-r--r--src/lib/curl/Easy.hxx5
-rw-r--r--src/lib/curl/Escape.cxx71
-rw-r--r--src/lib/curl/Escape.hxx51
-rw-r--r--src/lib/curl/Form.cxx9
-rw-r--r--src/lib/curl/String.hxx77
-rw-r--r--src/lib/curl/meson.build1
-rw-r--r--src/lib/icu/meson.build2
-rw-r--r--src/queue/PlaylistEdit.cxx2
-rw-r--r--src/storage/plugins/CurlStorage.cxx48
12 files changed, 249 insertions, 38 deletions
diff --git a/NEWS b/NEWS
index d60469c65..59c899c6e 100644
--- a/NEWS
+++ b/NEWS
@@ -32,6 +32,17 @@ ver 0.22 (not yet released)
* switch to C++17
- GCC 7 or clang 4 (or newer) recommended
+ver 0.21.16 (2019/10/16)
+* queue
+ - fix relative destination offset when moving a range
+* storage
+ - curl: request the "resourcetype" property to fix database update
+ - curl: URL-encode more paths
+ - curl: follow redirects for collections without trailing slash
+* update
+ - fix crash when music_directory is not a directory
+* fix build with iconv() instead of ICU
+
ver 0.21.15 (2019/09/25)
* decoder
- dsdiff, dsf: fix displayed bit rate
diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml
index 282707c61..e6bf686a9 100644
--- a/android/AndroidManifest.xml
+++ b/android/AndroidManifest.xml
@@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.musicpd"
android:installLocation="auto"
- android:versionCode="38"
- android:versionName="0.21.15">
+ android:versionCode="39"
+ android:versionName="0.21.16">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="26"/>
diff --git a/src/db/update/Walk.cxx b/src/db/update/Walk.cxx
index fbdb2957e..b540114eb 100644
--- a/src/db/update/Walk.cxx
+++ b/src/db/update/Walk.cxx
@@ -475,6 +475,12 @@ UpdateWalk::Walk(Directory &root, const char *path, bool discard) noexcept
if (!GetInfo(storage, "", info))
return false;
+ if (!info.IsDirectory()) {
+ FormatError(update_domain, "Not a directory: %s",
+ storage.MapUTF8("").c_str());
+ return false;
+ }
+
ExcludeList exclude_list;
UpdateDirectory(root, exclude_list, info);
diff --git a/src/lib/curl/Easy.hxx b/src/lib/curl/Easy.hxx
index 479f8f530..20e25a5c2 100644
--- a/src/lib/curl/Easy.hxx
+++ b/src/lib/curl/Easy.hxx
@@ -30,6 +30,7 @@
#ifndef CURL_EASY_HXX
#define CURL_EASY_HXX
+#include "String.hxx"
#include "util/Compiler.h"
#include <curl/curl.h>
@@ -208,8 +209,8 @@ public:
return ::curl_easy_pause(handle, CURLPAUSE_CONT) == CURLE_OK;
}
- char *Escape(const char *string, int length=0) const noexcept {
- return curl_easy_escape(handle, string, length);
+ CurlString Escape(const char *string, int length=0) const noexcept {
+ return CurlString(curl_easy_escape(handle, string, length));
}
};
diff --git a/src/lib/curl/Escape.cxx b/src/lib/curl/Escape.cxx
new file mode 100644
index 000000000..ab832eac9
--- /dev/null
+++ b/src/lib/curl/Escape.cxx
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 Max Kellermann <max.kellermann@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "Escape.hxx"
+#include "Easy.hxx"
+#include "String.hxx"
+#include "util/IterableSplitString.hxx"
+
+std::string
+CurlEscapeUriPath(CURL *curl, StringView src) noexcept
+{
+ std::string dest;
+
+ for (const auto i : IterableSplitString(src, '/')) {
+ CurlString escaped(curl_easy_escape(curl, i.data, i.size));
+ if (!dest.empty())
+ dest.push_back('/');
+ dest += escaped.c_str();
+ }
+
+ return dest;
+}
+
+std::string
+CurlEscapeUriPath(StringView src) noexcept
+{
+ CurlEasy easy;
+ return CurlEscapeUriPath(easy.Get(), src);
+}
+
+std::string
+CurlUnescape(CURL *curl, StringView src) noexcept
+{
+ int outlength;
+ CurlString tmp(curl_easy_unescape(curl, src.data, src.size,
+ &outlength));
+ return std::string(tmp.c_str(), outlength);
+}
+
+std::string
+CurlUnescape(StringView src) noexcept
+{
+ CurlEasy easy;
+ return CurlUnescape(easy.Get(), src);
+}
diff --git a/src/lib/curl/Escape.hxx b/src/lib/curl/Escape.hxx
new file mode 100644
index 000000000..1a7c9ab70
--- /dev/null
+++ b/src/lib/curl/Escape.hxx
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 Max Kellermann <max.kellermann@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CURL_ESCAPE_HXX
+#define CURL_ESCAPE_HXX
+
+#include <curl/curl.h>
+
+#include <string>
+
+struct StringView;
+
+std::string
+CurlEscapeUriPath(CURL *curl, StringView src) noexcept;
+
+std::string
+CurlEscapeUriPath(StringView src) noexcept;
+
+std::string
+CurlUnescape(CURL *curl, StringView src) noexcept;
+
+std::string
+CurlUnescape(StringView src) noexcept;
+
+#endif
diff --git a/src/lib/curl/Form.cxx b/src/lib/curl/Form.cxx
index 7e9d84753..65430641d 100644
--- a/src/lib/curl/Form.cxx
+++ b/src/lib/curl/Form.cxx
@@ -28,6 +28,7 @@
*/
#include "Form.hxx"
+#include "String.hxx"
std::string
EncodeForm(CURL *curl,
@@ -43,12 +44,10 @@ EncodeForm(CURL *curl,
result.push_back('=');
if (!i.second.empty()) {
- char *value = curl_easy_escape(curl, i.second.data(),
- i.second.length());
- if (value != nullptr) {
+ CurlString value(curl_easy_escape(curl, i.second.data(),
+ i.second.length()));
+ if (value)
result.append(value);
- curl_free(value);
- }
}
}
diff --git a/src/lib/curl/String.hxx b/src/lib/curl/String.hxx
new file mode 100644
index 000000000..f39c28992
--- /dev/null
+++ b/src/lib/curl/String.hxx
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2019 Max Kellermann <max.kellermann@gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CURL_STRING_HXX
+#define CURL_STRING_HXX
+
+#include <curl/curl.h>
+
+#include <utility>
+
+/**
+ * An OO wrapper for an allocated string to be freed with curl_free().
+ */
+class CurlString {
+ char *p = nullptr;
+
+public:
+ CurlString() noexcept = default;
+ CurlString(std::nullptr_t) noexcept {}
+
+ explicit CurlString(char *_p) noexcept
+ :p(_p) {}
+
+ CurlString(CurlString &&src) noexcept
+ :p(std::exchange(src.p, nullptr)) {}
+
+ ~CurlString() noexcept {
+ if (p != nullptr)
+ curl_free(p);
+ }
+
+ CurlString &operator=(CurlString &&src) noexcept {
+ using std::swap;
+ swap(p, src.p);
+ return *this;
+ }
+
+ operator bool() const noexcept {
+ return p != nullptr;
+ }
+
+ operator const char *() const noexcept {
+ return p;
+ }
+
+ const char *c_str() const noexcept {
+ return p;
+ }
+};
+
+#endif
diff --git a/src/lib/curl/meson.build b/src/lib/curl/meson.build
index 3116b6512..2b38957b4 100644
--- a/src/lib/curl/meson.build
+++ b/src/lib/curl/meson.build
@@ -11,6 +11,7 @@ curl = static_library(
'Init.cxx',
'Global.cxx',
'Request.cxx',
+ 'Escape.cxx',
'Form.cxx',
include_directories: inc,
dependencies: [
diff --git a/src/lib/icu/meson.build b/src/lib/icu/meson.build
index 3232266ec..5386a62b4 100644
--- a/src/lib/icu/meson.build
+++ b/src/lib/icu/meson.build
@@ -20,7 +20,7 @@ if icu_dep.found()
elif not get_option('iconv').disabled()
have_iconv = compiler.has_function('iconv')
conf.set('HAVE_ICONV', have_iconv)
- if get_option('iconv').enabled()
+ if not have_iconv and get_option('iconv').enabled()
error('iconv() not available')
endif
endif
diff --git a/src/queue/PlaylistEdit.cxx b/src/queue/PlaylistEdit.cxx
index d6ab55d15..d53286a25 100644
--- a/src/queue/PlaylistEdit.cxx
+++ b/src/queue/PlaylistEdit.cxx
@@ -352,7 +352,7 @@ playlist::MoveRange(PlayerControl &pc,
return;
to = (currentSong + abs(to)) % GetLength();
if (start < (unsigned)to)
- to--;
+ to -= end - start;
}
queue.MoveRange(start, end, to);
diff --git a/src/storage/plugins/CurlStorage.cxx b/src/storage/plugins/CurlStorage.cxx
index 664abb591..72ad439f4 100644
--- a/src/storage/plugins/CurlStorage.cxx
+++ b/src/storage/plugins/CurlStorage.cxx
@@ -25,8 +25,10 @@
#include "lib/curl/Init.hxx"
#include "lib/curl/Global.hxx"
#include "lib/curl/Slist.hxx"
+#include "lib/curl/String.hxx"
#include "lib/curl/Request.hxx"
#include "lib/curl/Handler.hxx"
+#include "lib/curl/Escape.hxx"
#include "lib/expat/ExpatParser.hxx"
#include "fs/Traits.hxx"
#include "event/DeferEvent.hxx"
@@ -34,7 +36,6 @@
#include "thread/Cond.hxx"
#include "time/Parser.hxx"
#include "util/ASCII.hxx"
-#include "util/IterableSplitString.hxx"
#include "util/RuntimeError.hxx"
#include "util/StringCompare.hxx"
#include "util/StringFormat.hxx"
@@ -74,26 +75,15 @@ CurlStorage::MapUTF8(const char *uri_utf8) const noexcept
if (StringIsEmpty(uri_utf8))
return base;
- CurlEasy easy;
- std::string path_esc;
-
- for (auto elt: IterableSplitString(uri_utf8, '/')) {
- char *elt_esc = easy.Escape(elt.data, elt.size);
- if (!path_esc.empty())
- path_esc.push_back('/');
- path_esc += elt_esc;
- curl_free(elt_esc);
- }
-
+ std::string path_esc = CurlEscapeUriPath(uri_utf8);
return PathTraitsUTF8::Build(base.c_str(), path_esc.c_str());
}
const char *
CurlStorage::MapToRelativeUTF8(const char *uri_utf8) const noexcept
{
- // TODO: escape/unescape?
-
- return PathTraitsUTF8::Relative(base.c_str(), uri_utf8);
+ return PathTraitsUTF8::Relative(base.c_str(),
+ CurlUnescape(uri_utf8).c_str());
}
class BlockingHttpRequest : protected CurlResponseHandler {
@@ -128,6 +118,10 @@ public:
std::rethrow_exception(postponed_error);
}
+ CURL *GetEasy() noexcept {
+ return request.Get();
+ }
+
protected:
void SetDone() {
assert(!done);
@@ -265,6 +259,8 @@ public:
CommonExpatParser(ExpatNamespaceSeparator{'|'})
{
request.SetOption(CURLOPT_CUSTOMREQUEST, "PROPFIND");
+ request.SetOption(CURLOPT_FOLLOWLOCATION, 1l);
+ request.SetOption(CURLOPT_MAXREDIRS, 1l);
request_headers.Append(StringFormat<40>("depth: %u", depth));
@@ -273,6 +269,7 @@ public:
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:propfind>");
@@ -280,6 +277,7 @@ public:
// TODO: send request body
}
+ using BlockingHttpRequest::GetEasy;
using BlockingHttpRequest::Wait;
protected:
@@ -450,9 +448,7 @@ CurlStorage::GetInfo(const char *uri_utf8, gcc_unused bool follow)
{
// TODO: escape the given URI
- std::string uri = base;
- uri += uri_utf8;
-
+ const auto uri = MapUTF8(uri_utf8);
return HttpGetInfoOperation(*curl, uri.c_str()).Perform();
}
@@ -499,7 +495,11 @@ private:
if (path == nullptr)
return nullptr;
- path = StringAfterPrefix(path, base_path.c_str());
+ /* kludge: ignoring case in this comparison to avoid
+ false negatives if the web server uses a different
+ case in hex digits in escaped characters; TODO:
+ implement properly */
+ path = StringAfterPrefixIgnoreCase(path, base_path.c_str());
if (path == nullptr || *path == 0)
return nullptr;
@@ -525,10 +525,7 @@ protected:
if (escaped_name.IsNull())
return;
- // TODO: unescape
- const auto name = escaped_name;
-
- entries.emplace_front(std::string(name.data, name.size));
+ entries.emplace_front(CurlUnescape(GetEasy(), escaped_name));
auto &info = entries.front().info;
info = StorageFileInfo(r.collection
@@ -542,10 +539,7 @@ protected:
std::unique_ptr<StorageDirectoryReader>
CurlStorage::OpenDirectory(const char *uri_utf8)
{
- // TODO: escape the given URI
-
- std::string uri = base;
- uri += uri_utf8;
+ std::string uri = MapUTF8(uri_utf8);
/* collection URIs must end with a slash */
if (uri.back() != '/')