diff options
Diffstat (limited to 'ucb/source/ucp/webdav-curl/CurlUri.cxx')
-rw-r--r-- | ucb/source/ucp/webdav-curl/CurlUri.cxx | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/ucb/source/ucp/webdav-curl/CurlUri.cxx b/ucb/source/ucp/webdav-curl/CurlUri.cxx new file mode 100644 index 000000000..4dba6ac22 --- /dev/null +++ b/ucb/source/ucp/webdav-curl/CurlUri.cxx @@ -0,0 +1,328 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "CurlUri.hxx" + +#include <sal/log.hxx> +#include <rtl/uri.hxx> +#include <rtl/ustrbuf.hxx> + +#include <optional> + +namespace http_dav_ucp +{ +const auto DEFAULT_HTTP_PORT = 80; +const auto DEFAULT_HTTPS_PORT = 443; + +static ::std::optional<OUString> GetURLComponent(CURLU& rURI, CURLUPart const what, + CURLUcode const expected, + unsigned int const flags = 0) +{ + char* pPart(nullptr); + auto uc = curl_url_get(&rURI, what, &pPart, flags); + if (expected != CURLUE_OK && uc == expected) + { + return ::std::optional<OUString>(); + } + if (uc != CURLUE_OK) + { + SAL_WARN("ucb.ucp.webdav.curl", "curl_url_get failed: " << what << " " << uc); + throw DAVException(DAVException::DAV_INVALID_ARG); + } + assert(pPart); + CurlUniquePtr<char> pPart2(pPart); + return ::rtl::OStringToOUString(pPart, RTL_TEXTENCODING_UTF8); +} + +void CurlUri::Init() +{ + // looks like the result should be the same as the old calculateURI() + auto const oURI(GetURLComponent(*m_pUrl, CURLUPART_URL, CURLUE_OK, CURLU_NO_DEFAULT_PORT)); + assert(oURI); + m_URI = *oURI; + + auto const oScheme(GetURLComponent(*m_pUrl, CURLUPART_SCHEME, CURLUE_NO_SCHEME)); + if (oScheme) + { + m_Scheme = *oScheme; + } + auto const oUser(GetURLComponent(*m_pUrl, CURLUPART_USER, CURLUE_NO_USER)); + if (oUser) + { + m_User = *oUser; + } + auto const oPassWord(GetURLComponent(*m_pUrl, CURLUPART_PASSWORD, CURLUE_NO_PASSWORD)); + if (oPassWord) + { + m_Password = *oPassWord; + } + auto const oHost(GetURLComponent(*m_pUrl, CURLUPART_HOST, CURLUE_NO_HOST)); + if (oHost) + { + m_Host = *oHost; + } + // DAV schemes always have port but Content::transfer() is called with + // arbitrary URLs so use CURLUE_NO_PORT + auto const oPort(GetURLComponent(*m_pUrl, CURLUPART_PORT, CURLUE_NO_PORT, CURLU_DEFAULT_PORT)); + if (oPort) + { + m_nPort = oPort->toInt32(); + } + + auto const oPath(GetURLComponent(*m_pUrl, CURLUPART_PATH, CURLUE_OK)); + assert(oPath); + m_Path = *oPath; + + // note: this used to be added to m_Path because before 2007, ne_uri path contained query/fragment as well :-/ + auto const oQuery(GetURLComponent(*m_pUrl, CURLUPART_QUERY, CURLUE_NO_QUERY)); + if (oQuery) + { + m_QueryAndFragment += "?" + *oQuery; + } + auto const oFragment(GetURLComponent(*m_pUrl, CURLUPART_FRAGMENT, CURLUE_NO_FRAGMENT)); + if (oFragment) + { + m_QueryAndFragment += "#" + *oFragment; + } +} + +CurlUri::CurlUri(::std::u16string_view const rURI) +{ + // note: in the old implementation, the rURI would be URI-encoded again + // here, apparently because it could actually be an IRI (RFC 3987) and + // neon didn't support that - not clear if this is a good idea + + m_pUrl.reset(curl_url()); + if (!m_pUrl) + { + throw ::std::bad_alloc(); + } + + // use curl to parse the URI, to get a consistent interpretation + OString const utf8URI(OUStringToOString(rURI, RTL_TEXTENCODING_UTF8)); + auto uc = curl_url_set(m_pUrl.get(), CURLUPART_URL, utf8URI.getStr(), 0); + if (uc != CURLUE_OK) + { + SAL_WARN("ucb.ucp.webdav.curl", "curl_url_set failed: " << uc); + throw DAVException(DAVException::DAV_INVALID_ARG); + } + + Init(); +} + +CurlUri::CurlUri(CURLU /*const*/& rUrl) + : m_pUrl(curl_url_dup(&rUrl)) +{ + if (!m_pUrl) + { + throw ::std::bad_alloc(); + } + + Init(); +} + +CurlUri::CurlUri(CurlUri const& rOther) + : m_pUrl(curl_url_dup(rOther.m_pUrl.get())) + , m_URI(rOther.m_URI) + , m_Scheme(rOther.m_Scheme) + , m_User(rOther.m_User) + , m_Password(rOther.m_Password) + , m_Host(rOther.m_Host) + , m_nPort(rOther.m_nPort) + , m_Path(rOther.m_Path) + , m_QueryAndFragment(rOther.m_QueryAndFragment) +{ + assert(rOther.m_pUrl); + if (!m_pUrl) + { + throw ::std::bad_alloc(); + } +} + +void CurlUri::operator=(CurlUri const& rOther) +{ + assert(rOther.m_pUrl); + m_pUrl.reset(curl_url_dup(rOther.m_pUrl.get())); + if (!m_pUrl) + { + throw ::std::bad_alloc(); + } + m_URI = rOther.m_URI; + m_Scheme = rOther.m_Scheme; + m_User = rOther.m_User; + m_Password = rOther.m_Password; + m_Host = rOther.m_Host; + m_nPort = rOther.m_nPort; + m_Path = rOther.m_Path; + m_QueryAndFragment = rOther.m_QueryAndFragment; +} + +bool CurlUri::operator==(CurlUri const& rOther) const { return m_URI == rOther.m_URI; } + +OUString CurlUri::GetPathBaseName() const +{ + sal_Int32 nPos = m_Path.lastIndexOf('/'); + sal_Int32 nTrail = 0; + if (nPos == m_Path.getLength() - 1) + { + // Trailing slash found. Skip. + nTrail = 1; + nPos = m_Path.lastIndexOf('/', nPos); + } + if (nPos == -1) + { + return "/"; + } + return m_Path.copy(nPos + 1, m_Path.getLength() - nPos - 1 - nTrail); +} + +OUString CurlUri::GetPathBaseNameUnescaped() const { return DecodeURI(GetPathBaseName()); } + +void CurlUri::SetScheme(::std::u16string_view const rScheme) +{ + OString const utf8URI(OUStringToOString(rScheme, RTL_TEXTENCODING_UTF8)); + auto uc = curl_url_set(m_pUrl.get(), CURLUPART_SCHEME, utf8URI.getStr(), 0); + if (uc != CURLUE_OK) + { + SAL_WARN("ucb.ucp.webdav.curl", "curl_url_set failed: " << uc); + throw DAVException(DAVException::DAV_INVALID_ARG); + } + auto const oURI(GetURLComponent(*m_pUrl, CURLUPART_URL, CURLUE_OK, CURLU_NO_DEFAULT_PORT)); + assert(oURI); + m_URI = *oURI; + auto const oScheme(GetURLComponent(*m_pUrl, CURLUPART_SCHEME, CURLUE_NO_SCHEME)); + if (oScheme) + { + m_Scheme = *oScheme; + } +} + +void CurlUri::AppendPath(::std::u16string_view const rPath) +{ + OUStringBuffer path(m_Path); + if (path.lastIndexOf('/') != path.getLength() - 1) + { + path.append("/"); + } + path.append(rPath); + OString const utf8Path(OUStringToOString(path.makeStringAndClear(), RTL_TEXTENCODING_UTF8)); + auto uc = curl_url_set(m_pUrl.get(), CURLUPART_PATH, utf8Path.getStr(), 0); + if (uc != CURLUE_OK) + { + SAL_WARN("ucb.ucp.webdav.curl", "curl_url_set failed: " << uc); + throw DAVException(DAVException::DAV_INVALID_ARG); + } + auto const oURI(GetURLComponent(*m_pUrl, CURLUPART_URL, CURLUE_OK, CURLU_NO_DEFAULT_PORT)); + assert(oURI); + m_URI = *oURI; + auto const oPath(GetURLComponent(*m_pUrl, CURLUPART_PATH, CURLUE_OK)); + assert(oPath); + m_Path = *oPath; +} + +CurlUri CurlUri::CloneWithRelativeRefPathAbsolute(OUString const& rRelativeRef) const +{ + ::std::unique_ptr<CURLU, deleter_from_fn<CURLU, curl_url_cleanup>> pUrl( + curl_url_dup(m_pUrl.get())); + sal_Int32 indexEnd(rRelativeRef.getLength()); + auto const indexQuery(rRelativeRef.indexOf('?')); + auto const indexFragment(rRelativeRef.indexOf('#')); + CURLUcode uc; + if (indexFragment != -1) + { + std::u16string_view const fragment(rRelativeRef.subView(indexFragment + 1)); + indexEnd = indexFragment; + OString const utf8Fragment(OUStringToOString(fragment, RTL_TEXTENCODING_UTF8)); + uc = curl_url_set(pUrl.get(), CURLUPART_FRAGMENT, utf8Fragment.getStr(), 0); + } + else + { + uc = curl_url_set(pUrl.get(), CURLUPART_FRAGMENT, nullptr, 0); + } + if (uc != CURLUE_OK) + { + SAL_WARN("ucb.ucp.webdav.curl", "curl_url_set failed: " << uc); + throw DAVException(DAVException::DAV_INVALID_ARG); + } + if (indexQuery != -1 && (indexFragment == -1 || indexQuery < indexFragment)) + { + std::u16string_view const query( + rRelativeRef.subView(indexQuery + 1, indexEnd - indexQuery - 1)); + indexEnd = indexQuery; + OString const utf8Query(OUStringToOString(query, RTL_TEXTENCODING_UTF8)); + uc = curl_url_set(pUrl.get(), CURLUPART_QUERY, utf8Query.getStr(), 0); + } + else + { + uc = curl_url_set(pUrl.get(), CURLUPART_QUERY, nullptr, 0); + } + if (uc != CURLUE_OK) + { + SAL_WARN("ucb.ucp.webdav.curl", "curl_url_set failed: " << uc); + throw DAVException(DAVException::DAV_INVALID_ARG); + } + std::u16string_view const path(rRelativeRef.subView(0, indexEnd)); + OString const utf8Path(OUStringToOString(path, RTL_TEXTENCODING_UTF8)); + uc = curl_url_set(pUrl.get(), CURLUPART_PATH, utf8Path.getStr(), 0); + if (uc != CURLUE_OK) + { + SAL_WARN("ucb.ucp.webdav.curl", "curl_url_set failed: " << uc); + throw DAVException(DAVException::DAV_INVALID_ARG); + } + return CurlUri(*pUrl.release()); +} + +OUString EncodeSegment(OUString const& rSegment) +{ + return rtl::Uri::encode(rSegment, rtl_UriCharClassPchar, rtl_UriEncodeIgnoreEscapes, + RTL_TEXTENCODING_UTF8); +} + +OUString DecodeURI(OUString const& rURI) +{ + return rtl::Uri::decode(rURI, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8); +} + +OUString ConnectionEndPointString(std::u16string_view rHostName, sal_uInt16 const nPort) +{ + OUStringBuffer aBuf; + + // Is host a numeric IPv6 address? + if ((rHostName.find(':') != std::u16string_view::npos) && (rHostName[0] != '[')) + { + aBuf.append("["); + aBuf.append(rHostName); + aBuf.append("]"); + } + else + { + aBuf.append(rHostName); + } + + if ((nPort != DEFAULT_HTTP_PORT) && (nPort != DEFAULT_HTTPS_PORT)) + { + aBuf.append(":"); + aBuf.append(sal_Int32(nPort)); + } + return aBuf.makeStringAndClear(); +} + +} // namespace http_dav_ucp + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ |