diff --git a/src/Instance.cxx b/src/Instance.cxx index e0a40f6e2..4933ebd13 100644 --- a/src/Instance.cxx +++ b/src/Instance.cxx @@ -27,7 +27,7 @@ #ifdef ENABLE_CURL #include "RemoteTagCache.hxx" -#include "util/UriUtil.hxx" +#include "util/UriExtract.hxx" #endif #ifdef ENABLE_DATABASE diff --git a/src/LocateUri.cxx b/src/LocateUri.cxx index dcb52bfde..2b8a14e9c 100644 --- a/src/LocateUri.cxx +++ b/src/LocateUri.cxx @@ -22,8 +22,8 @@ #include "client/Client.hxx" #include "fs/AllocatedPath.hxx" #include "ls.hxx" -#include "util/UriUtil.hxx" #include "util/ASCII.hxx" +#include "util/UriExtract.hxx" #ifdef ENABLE_DATABASE #include "storage/StorageInterface.hxx" diff --git a/src/PlaylistFile.cxx b/src/PlaylistFile.cxx index 51955b3cc..3edee3aad 100644 --- a/src/PlaylistFile.cxx +++ b/src/PlaylistFile.cxx @@ -40,7 +40,7 @@ #include "fs/FileInfo.hxx" #include "fs/DirectoryReader.hxx" #include "util/StringCompare.hxx" -#include "util/UriUtil.hxx" +#include "util/UriExtract.hxx" #include #include diff --git a/src/PlaylistSave.cxx b/src/PlaylistSave.cxx index cca47e0ba..9c0ec3706 100644 --- a/src/PlaylistSave.cxx +++ b/src/PlaylistSave.cxx @@ -30,7 +30,7 @@ #include "fs/FileSystem.hxx" #include "fs/io/FileOutputStream.hxx" #include "fs/io/BufferedOutputStream.hxx" -#include "util/UriUtil.hxx" +#include "util/UriExtract.hxx" static void playlist_print_path(BufferedOutputStream &os, const Path path) diff --git a/src/SongUpdate.cxx b/src/SongUpdate.cxx index d843f5ed2..aa9fe9c23 100644 --- a/src/SongUpdate.cxx +++ b/src/SongUpdate.cxx @@ -23,12 +23,12 @@ #include "db/plugins/simple/Directory.hxx" #include "storage/StorageInterface.hxx" #include "storage/FileInfo.hxx" -#include "util/UriUtil.hxx" #include "fs/AllocatedPath.hxx" #include "fs/FileInfo.hxx" #include "tag/Builder.hxx" #include "TagFile.hxx" #include "TagStream.hxx" +#include "util/UriExtract.hxx" #ifdef ENABLE_ARCHIVE #include "TagArchive.hxx" diff --git a/src/TagStream.cxx b/src/TagStream.cxx index 9e23d9e8b..e1f8071eb 100644 --- a/src/TagStream.cxx +++ b/src/TagStream.cxx @@ -22,11 +22,11 @@ #include "tag/Handler.hxx" #include "tag/Builder.hxx" #include "util/MimeType.hxx" -#include "util/UriUtil.hxx" #include "decoder/DecoderList.hxx" #include "decoder/DecoderPlugin.hxx" #include "input/InputStream.hxx" #include "thread/Mutex.hxx" +#include "util/UriExtract.hxx" #include diff --git a/src/command/FileCommands.cxx b/src/command/FileCommands.cxx index e19adb648..97b5d89fe 100644 --- a/src/command/FileCommands.cxx +++ b/src/command/FileCommands.cxx @@ -27,7 +27,7 @@ #include "client/Response.hxx" #include "util/CharUtil.hxx" #include "util/StringView.hxx" -#include "util/UriUtil.hxx" +#include "util/UriExtract.hxx" #include "tag/Handler.hxx" #include "tag/Generic.hxx" #include "TagStream.hxx" diff --git a/src/command/FingerprintCommands.cxx b/src/command/FingerprintCommands.cxx index 460275afe..9b687df71 100644 --- a/src/command/FingerprintCommands.cxx +++ b/src/command/FingerprintCommands.cxx @@ -35,7 +35,7 @@ #include "thread/Cond.hxx" #include "system/Error.hxx" #include "util/MimeType.hxx" -#include "util/UriUtil.hxx" +#include "util/UriExtract.hxx" class GetChromaprintCommand final : public ThreadBackgroundCommand, ChromaprintDecoderClient, InputStreamHandler diff --git a/src/command/PlaylistCommands.cxx b/src/command/PlaylistCommands.cxx index 1aa630085..cf079c600 100644 --- a/src/command/PlaylistCommands.cxx +++ b/src/command/PlaylistCommands.cxx @@ -38,8 +38,8 @@ #include "Mapper.hxx" #include "fs/AllocatedPath.hxx" #include "time/ChronoUtil.hxx" -#include "util/UriUtil.hxx" #include "util/ConstBuffer.hxx" +#include "util/UriExtract.hxx" #include "LocateUri.hxx" bool diff --git a/src/db/update/Walk.cxx b/src/db/update/Walk.cxx index 23db623e5..2c7e2a3e4 100644 --- a/src/db/update/Walk.cxx +++ b/src/db/update/Walk.cxx @@ -37,7 +37,7 @@ #include "input/Error.hxx" #include "util/Alloc.hxx" #include "util/StringCompare.hxx" -#include "util/UriUtil.hxx" +#include "util/UriExtract.hxx" #include "Log.hxx" #include diff --git a/src/decoder/Thread.cxx b/src/decoder/Thread.cxx index a37090ffe..747f2438a 100644 --- a/src/decoder/Thread.cxx +++ b/src/decoder/Thread.cxx @@ -31,6 +31,7 @@ #include "DecoderList.hxx" #include "system/Error.hxx" #include "util/MimeType.hxx" +#include "util/UriExtract.hxx" #include "util/UriUtil.hxx" #include "util/RuntimeError.hxx" #include "util/Domain.hxx" diff --git a/src/decoder/plugins/GmeDecoderPlugin.cxx b/src/decoder/plugins/GmeDecoderPlugin.cxx index b265fbee6..b72132d66 100644 --- a/src/decoder/plugins/GmeDecoderPlugin.cxx +++ b/src/decoder/plugins/GmeDecoderPlugin.cxx @@ -30,7 +30,7 @@ #include "util/ScopeExit.hxx" #include "util/StringFormat.hxx" #include "util/StringView.hxx" -#include "util/UriUtil.hxx" +#include "util/UriExtract.hxx" #include "util/Domain.hxx" #include "Log.hxx" diff --git a/src/lib/upnp/ContentDirectoryService.cxx b/src/lib/upnp/ContentDirectoryService.cxx index 9ea43ec03..e74f09405 100644 --- a/src/lib/upnp/ContentDirectoryService.cxx +++ b/src/lib/upnp/ContentDirectoryService.cxx @@ -21,7 +21,7 @@ #include "UniqueIxml.hxx" #include "Device.hxx" #include "ixmlwrap.hxx" -#include "util/UriUtil.hxx" +#include "util/UriRelative.hxx" #include "util/RuntimeError.hxx" #include "util/SplitString.hxx" diff --git a/src/ls.cxx b/src/ls.cxx index cbbe175dc..3826b2412 100644 --- a/src/ls.cxx +++ b/src/ls.cxx @@ -22,7 +22,7 @@ #include "input/Registry.hxx" #include "input/InputPlugin.hxx" #include "client/Response.hxx" -#include "util/UriUtil.hxx" +#include "util/UriExtract.hxx" #include @@ -38,7 +38,7 @@ void print_supported_uri_schemes_to_fp(FILE *fp) plugin->ForeachSupportedUri([&](const char* uri) { protocols.emplace(uri); }); - + for (auto protocol : protocols) { fprintf(fp, " %s", protocol.c_str()); } diff --git a/src/playlist/PlaylistRegistry.cxx b/src/playlist/PlaylistRegistry.cxx index f24b28201..0571cd904 100644 --- a/src/playlist/PlaylistRegistry.cxx +++ b/src/playlist/PlaylistRegistry.cxx @@ -33,9 +33,9 @@ #include "plugins/EmbeddedCuePlaylistPlugin.hxx" #include "input/InputStream.hxx" #include "util/MimeType.hxx" -#include "util/UriUtil.hxx" #include "util/StringUtil.hxx" #include "util/StringView.hxx" +#include "util/UriExtract.hxx" #include "config/Data.hxx" #include "config/Block.hxx" diff --git a/src/playlist/PlaylistSong.cxx b/src/playlist/PlaylistSong.cxx index f23cfee2b..9d759a934 100644 --- a/src/playlist/PlaylistSong.cxx +++ b/src/playlist/PlaylistSong.cxx @@ -22,8 +22,8 @@ #include "tag/Tag.hxx" #include "tag/Builder.hxx" #include "fs/Traits.hxx" -#include "util/UriUtil.hxx" #include "song/DetachedSong.hxx" +#include "util/UriExtract.hxx" #include #include diff --git a/src/playlist/PlaylistStream.cxx b/src/playlist/PlaylistStream.cxx index eb9c3e04d..cad5741a7 100644 --- a/src/playlist/PlaylistStream.cxx +++ b/src/playlist/PlaylistStream.cxx @@ -20,10 +20,10 @@ #include "PlaylistStream.hxx" #include "PlaylistRegistry.hxx" #include "SongEnumerator.hxx" -#include "util/UriUtil.hxx" #include "input/InputStream.hxx" #include "input/LocalOpen.hxx" #include "fs/Path.hxx" +#include "util/UriExtract.hxx" #include "Log.hxx" #include diff --git a/src/song/BaseSongFilter.cxx b/src/song/BaseSongFilter.cxx index ab474a94d..ad055504b 100644 --- a/src/song/BaseSongFilter.cxx +++ b/src/song/BaseSongFilter.cxx @@ -20,7 +20,7 @@ #include "BaseSongFilter.hxx" #include "Escape.hxx" #include "LightSong.hxx" -#include "util/UriUtil.hxx" +#include "util/UriRelative.hxx" std::string BaseSongFilter::ToExpression() const noexcept diff --git a/src/song/DetachedSong.cxx b/src/song/DetachedSong.cxx index 2e3b6dd12..c93580006 100644 --- a/src/song/DetachedSong.cxx +++ b/src/song/DetachedSong.cxx @@ -19,7 +19,7 @@ #include "song/DetachedSong.hxx" #include "song/LightSong.hxx" -#include "util/UriUtil.hxx" +#include "util/UriExtract.hxx" #include "fs/Traits.hxx" DetachedSong::DetachedSong(const LightSong &other) diff --git a/src/storage/Configured.cxx b/src/storage/Configured.cxx index e89a492dd..9052fea85 100644 --- a/src/storage/Configured.cxx +++ b/src/storage/Configured.cxx @@ -24,8 +24,8 @@ #include "config/Data.hxx" #include "fs/StandardDirectory.hxx" #include "fs/CheckFile.hxx" -#include "util/UriUtil.hxx" #include "util/RuntimeError.hxx" +#include "util/UriExtract.hxx" static std::unique_ptr CreateConfiguredStorageUri(EventLoop &event_loop, const char *uri) diff --git a/src/storage/plugins/CurlStorage.cxx b/src/storage/plugins/CurlStorage.cxx index 4d67b526e..664abb591 100644 --- a/src/storage/plugins/CurlStorage.cxx +++ b/src/storage/plugins/CurlStorage.cxx @@ -1,5 +1,5 @@ /* - * Copyright 2003-2016 The Music Player Daemon Project + * Copyright 2003-2019 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -38,7 +38,7 @@ #include "util/RuntimeError.hxx" #include "util/StringCompare.hxx" #include "util/StringFormat.hxx" -#include "util/UriUtil.hxx" +#include "util/UriExtract.hxx" #include #include diff --git a/src/util/UriExtract.cxx b/src/util/UriExtract.cxx new file mode 100644 index 000000000..ac9eba628 --- /dev/null +++ b/src/util/UriExtract.cxx @@ -0,0 +1,140 @@ +/* + * Copyright 2008-2019 Max Kellermann + * + * 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 "UriExtract.hxx" +#include "CharUtil.hxx" +#include "StringView.hxx" + +#include + +static constexpr bool +IsValidSchemeStart(char ch) +{ + return IsLowerAlphaASCII(ch); +} + +static constexpr bool +IsValidSchemeChar(char ch) +{ + return IsLowerAlphaASCII(ch) || IsDigitASCII(ch) || + ch == '+' || ch == '.' || ch == '-'; +} + +gcc_pure +static bool +IsValidScheme(StringView p) noexcept +{ + if (p.empty() || !IsValidSchemeStart(p.front())) + return false; + + for (size_t i = 1; i < p.size; ++i) + if (!IsValidSchemeChar(p[i])) + return false; + + return true; +} + +/** + * Return the URI part after the scheme specification (and after the + * double slash). + */ +gcc_pure +static const char * +uri_after_scheme(const char *uri) noexcept +{ + if (uri[0] == '/' && uri[1] == '/' && uri[2] != '/') + return uri + 2; + + const char *colon = strchr(uri, ':'); + return colon != nullptr && + IsValidScheme({uri, colon}) && + colon[1] == '/' && colon[2] == '/' + ? colon + 3 + : nullptr; +} + +bool +uri_has_scheme(const char *uri) noexcept +{ + return strstr(uri, "://") != nullptr; +} + +std::string +uri_get_scheme(const char *uri) noexcept +{ + const char *end = strstr(uri, "://"); + if (end == nullptr) + end = uri; + + return std::string(uri, end); +} + +const char * +uri_get_path(const char *uri) noexcept +{ + const char *ap = uri_after_scheme(uri); + if (ap != nullptr) + return strchr(ap, '/'); + + return uri; +} + +/* suffixes should be ascii only characters */ +const char * +uri_get_suffix(const char *uri) noexcept +{ + const char *suffix = strrchr(uri, '.'); + if (suffix == nullptr || suffix == uri || + suffix[-1] == '/' || suffix[-1] == '\\') + return nullptr; + + ++suffix; + + if (strpbrk(suffix, "/\\") != nullptr) + return nullptr; + + return suffix; +} + +const char * +uri_get_suffix(const char *uri, UriSuffixBuffer &buffer) noexcept +{ + const char *suffix = uri_get_suffix(uri); + if (suffix == nullptr) + return nullptr; + + const char *q = strchr(suffix, '?'); + if (q != nullptr && size_t(q - suffix) < sizeof(buffer.data)) { + memcpy(buffer.data, suffix, q - suffix); + buffer.data[q - suffix] = 0; + suffix = buffer.data; + } + + return suffix; +} diff --git a/src/util/UriExtract.hxx b/src/util/UriExtract.hxx new file mode 100644 index 000000000..46af8ae1e --- /dev/null +++ b/src/util/UriExtract.hxx @@ -0,0 +1,75 @@ +/* + * Copyright 2008-2019 Max Kellermann + * + * 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 URI_EXTRACT_HXX +#define URI_EXTRACT_HXX + +#include "Compiler.h" + +#include + +/** + * Checks whether the specified URI has a scheme in the form + * "scheme://". + */ +gcc_pure +bool +uri_has_scheme(const char *uri) noexcept; + +/** + * Returns the scheme name of the specified URI, or an empty string. + */ +gcc_pure +std::string +uri_get_scheme(const char *uri) noexcept; + +/** + * Returns the URI path (including the query string) or nullptr if the + * given URI has no path. + */ +gcc_pure gcc_nonnull_all +const char * +uri_get_path(const char *uri) noexcept; + +gcc_pure +const char * +uri_get_suffix(const char *uri) noexcept; + +struct UriSuffixBuffer { + char data[8]; +}; + +/** + * Returns the file name suffix, ignoring the query string. + */ +gcc_pure +const char * +uri_get_suffix(const char *uri, UriSuffixBuffer &buffer) noexcept; + +#endif diff --git a/src/util/UriRelative.cxx b/src/util/UriRelative.cxx new file mode 100644 index 000000000..7424e052d --- /dev/null +++ b/src/util/UriRelative.cxx @@ -0,0 +1,82 @@ +/* + * Copyright 2008-2019 Max Kellermann + * + * 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 "UriRelative.hxx" + +#include +#include + +bool +uri_is_child(const char *parent, const char *child) noexcept +{ +#if !CLANG_CHECK_VERSION(3,6) + /* disabled on clang due to -Wtautological-pointer-compare */ + assert(parent != nullptr); + assert(child != nullptr); +#endif + + const size_t parent_length = strlen(parent); + return memcmp(parent, child, parent_length) == 0 && + child[parent_length] == '/'; +} + + +bool +uri_is_child_or_same(const char *parent, const char *child) noexcept +{ + return strcmp(parent, child) == 0 || uri_is_child(parent, child); +} + +std::string +uri_apply_base(const std::string &uri, const std::string &base) noexcept +{ + if (uri.front() == '/') { + /* absolute path: replace the whole URI path in base */ + + auto i = base.find("://"); + if (i == base.npos) + /* no scheme: override base completely */ + return uri; + + /* find the first slash after the host part */ + i = base.find('/', i + 3); + if (i == base.npos) + /* there's no URI path - simply append uri */ + i = base.length(); + + return base.substr(0, i) + uri; + } + + std::string out(base); + if (out.back() != '/') + out.push_back('/'); + + out += uri; + return out; +} diff --git a/src/util/UriRelative.hxx b/src/util/UriRelative.hxx new file mode 100644 index 000000000..ccfa28e9d --- /dev/null +++ b/src/util/UriRelative.hxx @@ -0,0 +1,58 @@ +/* + * Copyright 2008-2019 Max Kellermann + * + * 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 URI_RELATIVE_HXX +#define URI_RELATIVE_HXX + +#include "Compiler.h" + +#include + +/** + * Check whether #child specifies a resource "inside" the directory + * specified by #parent. If the strings are equal, the function + * returns false. + */ +gcc_pure gcc_nonnull_all +bool +uri_is_child(const char *parent, const char *child) noexcept; + +gcc_pure gcc_nonnull_all +bool +uri_is_child_or_same(const char *parent, const char *child) noexcept; + +/** + * Translate the given URI in the context of #base. For example, + * uri_apply_base("foo", "http://bar/a/")=="http://bar/a/foo". + */ +gcc_pure +std::string +uri_apply_base(const std::string &uri, const std::string &base) noexcept; + +#endif diff --git a/src/util/UriUtil.cxx b/src/util/UriUtil.cxx index f8ce72fd6..09ebafbd7 100644 --- a/src/util/UriUtil.cxx +++ b/src/util/UriUtil.cxx @@ -29,117 +29,10 @@ #include "UriUtil.hxx" #include "ASCII.hxx" -#include "CharUtil.hxx" #include #include -static constexpr bool -IsValidSchemeStart(char ch) -{ - return IsLowerAlphaASCII(ch); -} - -static constexpr bool -IsValidSchemeChar(char ch) -{ - return IsLowerAlphaASCII(ch) || IsDigitASCII(ch) || - ch == '+' || ch == '.' || ch == '-'; -} - -gcc_pure -static bool -IsValidScheme(StringView p) noexcept -{ - if (p.empty() || !IsValidSchemeStart(p.front())) - return false; - - for (size_t i = 1; i < p.size; ++i) - if (!IsValidSchemeChar(p[i])) - return false; - - return true; -} - -/** - * Return the URI part after the scheme specification (and after the - * double slash). - */ -gcc_pure -static const char * -uri_after_scheme(const char *uri) noexcept -{ - if (uri[0] == '/' && uri[1] == '/' && uri[2] != '/') - return uri + 2; - - const char *colon = strchr(uri, ':'); - return colon != nullptr && - IsValidScheme({uri, colon}) && - colon[1] == '/' && colon[2] == '/' - ? colon + 3 - : nullptr; -} - -bool -uri_has_scheme(const char *uri) noexcept -{ - return strstr(uri, "://") != nullptr; -} - -std::string -uri_get_scheme(const char *uri) noexcept -{ - const char *end = strstr(uri, "://"); - if (end == nullptr) - end = uri; - - return std::string(uri, end); -} - -const char * -uri_get_path(const char *uri) noexcept -{ - const char *ap = uri_after_scheme(uri); - if (ap != nullptr) - return strchr(ap, '/'); - - return uri; -} - -/* suffixes should be ascii only characters */ -const char * -uri_get_suffix(const char *uri) noexcept -{ - const char *suffix = strrchr(uri, '.'); - if (suffix == nullptr || suffix == uri || - suffix[-1] == '/' || suffix[-1] == '\\') - return nullptr; - - ++suffix; - - if (strpbrk(suffix, "/\\") != nullptr) - return nullptr; - - return suffix; -} - -const char * -uri_get_suffix(const char *uri, UriSuffixBuffer &buffer) noexcept -{ - const char *suffix = uri_get_suffix(uri); - if (suffix == nullptr) - return nullptr; - - const char *q = strchr(suffix, '?'); - if (q != nullptr && size_t(q - suffix) < sizeof(buffer.data)) { - memcpy(buffer.data, suffix, q - suffix); - buffer.data[q - suffix] = 0; - suffix = buffer.data; - } - - return suffix; -} - static const char * verify_uri_segment(const char *p) noexcept { @@ -210,52 +103,3 @@ uri_remove_auth(const char *uri) noexcept result.erase(auth - uri, at + 1 - auth); return result; } - -bool -uri_is_child(const char *parent, const char *child) noexcept -{ -#if !CLANG_CHECK_VERSION(3,6) - /* disabled on clang due to -Wtautological-pointer-compare */ - assert(parent != nullptr); - assert(child != nullptr); -#endif - - const size_t parent_length = strlen(parent); - return memcmp(parent, child, parent_length) == 0 && - child[parent_length] == '/'; -} - - -bool -uri_is_child_or_same(const char *parent, const char *child) noexcept -{ - return strcmp(parent, child) == 0 || uri_is_child(parent, child); -} - -std::string -uri_apply_base(const std::string &uri, const std::string &base) noexcept -{ - if (uri.front() == '/') { - /* absolute path: replace the whole URI path in base */ - - auto i = base.find("://"); - if (i == base.npos) - /* no scheme: override base completely */ - return uri; - - /* find the first slash after the host part */ - i = base.find('/', i + 3); - if (i == base.npos) - /* there's no URI path - simply append uri */ - i = base.length(); - - return base.substr(0, i) + uri; - } - - std::string out(base); - if (out.back() != '/') - out.push_back('/'); - - out += uri; - return out; -} diff --git a/src/util/UriUtil.hxx b/src/util/UriUtil.hxx index 835fe4685..528e7e872 100644 --- a/src/util/UriUtil.hxx +++ b/src/util/UriUtil.hxx @@ -34,44 +34,6 @@ #include -/** - * Checks whether the specified URI has a scheme in the form - * "scheme://". - */ -gcc_pure -bool -uri_has_scheme(const char *uri) noexcept; - -/** - * Returns the scheme name of the specified URI, or an empty string. - */ -gcc_pure -std::string -uri_get_scheme(const char *uri) noexcept; - -/** - * Returns the URI path (including the query string) or nullptr if the - * given URI has no path. - */ -gcc_pure gcc_nonnull_all -const char * -uri_get_path(const char *uri) noexcept; - -gcc_pure -const char * -uri_get_suffix(const char *uri) noexcept; - -struct UriSuffixBuffer { - char data[8]; -}; - -/** - * Returns the file name suffix, ignoring the query string. - */ -gcc_pure -const char * -uri_get_suffix(const char *uri, UriSuffixBuffer &buffer) noexcept; - /** * Returns true if this is a safe "local" URI: * @@ -94,25 +56,4 @@ gcc_pure std::string uri_remove_auth(const char *uri) noexcept; -/** - * Check whether #child specifies a resource "inside" the directory - * specified by #parent. If the strings are equal, the function - * returns false. - */ -gcc_pure gcc_nonnull_all -bool -uri_is_child(const char *parent, const char *child) noexcept; - -gcc_pure gcc_nonnull_all -bool -uri_is_child_or_same(const char *parent, const char *child) noexcept; - -/** - * Translate the given URI in the context of #base. For example, - * uri_apply_base("foo", "http://bar/a/")=="http://bar/a/foo". - */ -gcc_pure -std::string -uri_apply_base(const std::string &uri, const std::string &base) noexcept; - #endif diff --git a/src/util/meson.build b/src/util/meson.build index 1729d8588..da42d4362 100644 --- a/src/util/meson.build +++ b/src/util/meson.build @@ -17,6 +17,8 @@ util = static_library( 'SplitString.cxx', 'FormatString.cxx', 'Tokenizer.cxx', + 'UriExtract.cxx', + 'UriRelative.cxx', 'UriUtil.cxx', 'LazyRandomEngine.cxx', 'HugeAllocator.cxx', diff --git a/test/ContainerScan.cxx b/test/ContainerScan.cxx index 1c40806d6..952de4fb2 100644 --- a/test/ContainerScan.cxx +++ b/test/ContainerScan.cxx @@ -25,8 +25,8 @@ #include "fs/Path.hxx" #include "fs/io/StdioOutputStream.hxx" #include "fs/io/BufferedOutputStream.hxx" -#include "util/UriUtil.hxx" #include "util/PrintException.hxx" +#include "util/UriExtract.hxx" #include diff --git a/test/TestUriExtract.cxx b/test/TestUriExtract.cxx new file mode 100644 index 000000000..b5e4c8656 --- /dev/null +++ b/test/TestUriExtract.cxx @@ -0,0 +1,33 @@ +/* + * Unit tests for src/util/ + */ + +#include "util/UriExtract.hxx" + +#include + +TEST(UriExtract, Suffix) +{ + EXPECT_EQ((const char *)nullptr, uri_get_suffix("/foo/bar")); + EXPECT_EQ((const char *)nullptr, uri_get_suffix("/foo.jpg/bar")); + EXPECT_STREQ(uri_get_suffix("/foo/bar.jpg"), "jpg"); + EXPECT_STREQ(uri_get_suffix("/foo.png/bar.jpg"), "jpg"); + EXPECT_EQ((const char *)nullptr, uri_get_suffix(".jpg")); + EXPECT_EQ((const char *)nullptr, uri_get_suffix("/foo/.jpg")); + + /* the first overload does not eliminate the query + string */ + EXPECT_STREQ(uri_get_suffix("/foo/bar.jpg?query_string"), + "jpg?query_string"); + + /* ... but the second one does */ + UriSuffixBuffer buffer; + EXPECT_STREQ(uri_get_suffix("/foo/bar.jpg?query_string", buffer), + "jpg"); + + /* repeat some of the above tests with the second overload */ + EXPECT_EQ((const char *)nullptr, uri_get_suffix("/foo/bar", buffer)); + EXPECT_EQ((const char *)nullptr, + uri_get_suffix("/foo.jpg/bar", buffer)); + EXPECT_STREQ(uri_get_suffix("/foo/bar.jpg", buffer), "jpg"); +} diff --git a/test/TestUriUtil.cxx b/test/TestUriUtil.cxx index 21cbd2cdf..17a6189e9 100644 --- a/test/TestUriUtil.cxx +++ b/test/TestUriUtil.cxx @@ -6,32 +6,6 @@ #include -TEST(UriUtil, Suffix) -{ - EXPECT_EQ((const char *)nullptr, uri_get_suffix("/foo/bar")); - EXPECT_EQ((const char *)nullptr, uri_get_suffix("/foo.jpg/bar")); - EXPECT_STREQ(uri_get_suffix("/foo/bar.jpg"), "jpg"); - EXPECT_STREQ(uri_get_suffix("/foo.png/bar.jpg"), "jpg"); - EXPECT_EQ((const char *)nullptr, uri_get_suffix(".jpg")); - EXPECT_EQ((const char *)nullptr, uri_get_suffix("/foo/.jpg")); - - /* the first overload does not eliminate the query - string */ - EXPECT_STREQ(uri_get_suffix("/foo/bar.jpg?query_string"), - "jpg?query_string"); - - /* ... but the second one does */ - UriSuffixBuffer buffer; - EXPECT_STREQ(uri_get_suffix("/foo/bar.jpg?query_string", buffer), - "jpg"); - - /* repeat some of the above tests with the second overload */ - EXPECT_EQ((const char *)nullptr, uri_get_suffix("/foo/bar", buffer)); - EXPECT_EQ((const char *)nullptr, - uri_get_suffix("/foo.jpg/bar", buffer)); - EXPECT_STREQ(uri_get_suffix("/foo/bar.jpg", buffer), "jpg"); -} - TEST(UriUtil, RemoveAuth) { EXPECT_EQ(std::string(), diff --git a/test/meson.build b/test/meson.build index d426731ac..4a146dcf9 100644 --- a/test/meson.build +++ b/test/meson.build @@ -37,6 +37,7 @@ test('TestUtil', executable( 'TestDivideString.cxx', 'TestMimeType.cxx', 'TestSplitString.cxx', + 'TestUriExtract.cxx', 'TestUriUtil.cxx', 'test_byte_reverse.cxx', include_directories: inc,