Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / ucb / source / ucp / webdav-curl / CurlUri.cxx
blob3ee218d5aca3aeff77e1e834c876998f095b5928
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "CurlUri.hxx"
22 #include <sal/log.hxx>
23 #include <rtl/uri.hxx>
24 #include <rtl/ustrbuf.hxx>
26 #include <optional>
28 namespace http_dav_ucp
30 const auto DEFAULT_HTTP_PORT = 80;
31 const auto DEFAULT_HTTPS_PORT = 443;
33 static ::std::optional<OUString> GetURLComponent(CURLU& rURI, CURLUPart const what,
34 CURLUcode const expected,
35 unsigned int const flags = 0)
37 char* pPart(nullptr);
38 auto uc = curl_url_get(&rURI, what, &pPart, flags);
39 if (expected != CURLUE_OK && uc == expected)
41 return ::std::optional<OUString>();
43 if (uc != CURLUE_OK)
45 SAL_WARN("ucb.ucp.webdav.curl", "curl_url_get failed: " << what << " " << uc);
46 throw DAVException(DAVException::DAV_INVALID_ARG);
48 assert(pPart);
49 CurlUniquePtr<char> pPart2(pPart);
50 return ::rtl::OStringToOUString(pPart, RTL_TEXTENCODING_UTF8);
53 void CurlUri::Init()
55 // looks like the result should be the same as the old calculateURI()
56 auto const oURI(GetURLComponent(*m_pUrl, CURLUPART_URL, CURLUE_OK, CURLU_NO_DEFAULT_PORT));
57 assert(oURI);
58 m_URI = *oURI;
60 auto const oScheme(GetURLComponent(*m_pUrl, CURLUPART_SCHEME, CURLUE_NO_SCHEME));
61 if (oScheme)
63 m_Scheme = *oScheme;
65 auto const oUser(GetURLComponent(*m_pUrl, CURLUPART_USER, CURLUE_NO_USER));
66 if (oUser)
68 m_User = *oUser;
70 auto const oPassWord(GetURLComponent(*m_pUrl, CURLUPART_PASSWORD, CURLUE_NO_PASSWORD));
71 if (oPassWord)
73 m_Password = *oPassWord;
75 auto const oHost(GetURLComponent(*m_pUrl, CURLUPART_HOST, CURLUE_NO_HOST));
76 if (oHost)
78 m_Host = *oHost;
80 // DAV schemes always have port but Content::transfer() is called with
81 // arbitrary URLs so use CURLUE_NO_PORT
82 auto const oPort(GetURLComponent(*m_pUrl, CURLUPART_PORT, CURLUE_NO_PORT, CURLU_DEFAULT_PORT));
83 if (oPort)
85 m_nPort = oPort->toInt32();
88 auto const oPath(GetURLComponent(*m_pUrl, CURLUPART_PATH, CURLUE_OK));
89 assert(oPath);
90 m_Path = *oPath;
92 // note: this used to be added to m_Path because before 2007, ne_uri path contained query/fragment as well :-/
93 auto const oQuery(GetURLComponent(*m_pUrl, CURLUPART_QUERY, CURLUE_NO_QUERY));
94 if (oQuery)
96 m_QueryAndFragment += "?" + *oQuery;
98 auto const oFragment(GetURLComponent(*m_pUrl, CURLUPART_FRAGMENT, CURLUE_NO_FRAGMENT));
99 if (oFragment)
101 m_QueryAndFragment += "#" + *oFragment;
105 CurlUri::CurlUri(::std::u16string_view const rURI)
107 // note: in the old implementation, the rURI would be URI-encoded again
108 // here, apparently because it could actually be an IRI (RFC 3987) and
109 // neon didn't support that - not clear if this is a good idea
111 m_pUrl.reset(curl_url());
112 if (!m_pUrl)
114 throw ::std::bad_alloc();
117 // use curl to parse the URI, to get a consistent interpretation
118 if (rURI.find(u'\0') != std::u16string_view::npos)
120 throw DAVException(DAVException::DAV_INVALID_ARG);
122 OString const utf8URI(OUStringToOString(rURI, RTL_TEXTENCODING_UTF8));
123 auto uc = curl_url_set(m_pUrl.get(), CURLUPART_URL, utf8URI.getStr(), 0);
124 if (uc != CURLUE_OK)
126 SAL_WARN("ucb.ucp.webdav.curl", "curl_url_set failed: " << uc);
127 throw DAVException(DAVException::DAV_INVALID_ARG);
130 Init();
133 CurlUri::CurlUri(CURLU /*const*/& rUrl)
134 : m_pUrl(curl_url_dup(&rUrl))
136 if (!m_pUrl)
138 throw ::std::bad_alloc();
141 Init();
144 CurlUri::CurlUri(CurlUri const& rOther)
145 : m_pUrl(curl_url_dup(rOther.m_pUrl.get()))
146 , m_URI(rOther.m_URI)
147 , m_Scheme(rOther.m_Scheme)
148 , m_User(rOther.m_User)
149 , m_Password(rOther.m_Password)
150 , m_Host(rOther.m_Host)
151 , m_nPort(rOther.m_nPort)
152 , m_Path(rOther.m_Path)
153 , m_QueryAndFragment(rOther.m_QueryAndFragment)
155 assert(rOther.m_pUrl);
156 if (!m_pUrl)
158 throw ::std::bad_alloc();
162 void CurlUri::operator=(CurlUri const& rOther)
164 assert(rOther.m_pUrl);
165 m_pUrl.reset(curl_url_dup(rOther.m_pUrl.get()));
166 if (!m_pUrl)
168 throw ::std::bad_alloc();
170 m_URI = rOther.m_URI;
171 m_Scheme = rOther.m_Scheme;
172 m_User = rOther.m_User;
173 m_Password = rOther.m_Password;
174 m_Host = rOther.m_Host;
175 m_nPort = rOther.m_nPort;
176 m_Path = rOther.m_Path;
177 m_QueryAndFragment = rOther.m_QueryAndFragment;
180 bool CurlUri::operator==(CurlUri const& rOther) const { return m_URI == rOther.m_URI; }
182 OUString CurlUri::GetPathBaseName() const
184 sal_Int32 nPos = m_Path.lastIndexOf('/');
185 sal_Int32 nTrail = 0;
186 if (nPos == m_Path.getLength() - 1)
188 // Trailing slash found. Skip.
189 nTrail = 1;
190 nPos = m_Path.lastIndexOf('/', nPos);
192 if (nPos == -1)
194 return "/";
196 return m_Path.copy(nPos + 1, m_Path.getLength() - nPos - 1 - nTrail);
199 OUString CurlUri::GetPathBaseNameUnescaped() const { return DecodeURI(GetPathBaseName()); }
201 void CurlUri::SetScheme(::std::u16string_view const rScheme)
203 OString const utf8URI(OUStringToOString(rScheme, RTL_TEXTENCODING_UTF8));
204 auto uc = curl_url_set(m_pUrl.get(), CURLUPART_SCHEME, utf8URI.getStr(), 0);
205 if (uc != CURLUE_OK)
207 SAL_WARN("ucb.ucp.webdav.curl", "curl_url_set failed: " << uc);
208 throw DAVException(DAVException::DAV_INVALID_ARG);
210 auto const oURI(GetURLComponent(*m_pUrl, CURLUPART_URL, CURLUE_OK, CURLU_NO_DEFAULT_PORT));
211 assert(oURI);
212 m_URI = *oURI;
213 auto const oScheme(GetURLComponent(*m_pUrl, CURLUPART_SCHEME, CURLUE_NO_SCHEME));
214 if (oScheme)
216 m_Scheme = *oScheme;
220 void CurlUri::AppendPath(::std::u16string_view const rPath)
222 OUStringBuffer path(m_Path);
223 if (path.lastIndexOf('/') != path.getLength() - 1)
225 path.append("/");
227 path.append(rPath);
228 OString const utf8Path(OUStringToOString(path, RTL_TEXTENCODING_UTF8));
229 auto uc = curl_url_set(m_pUrl.get(), CURLUPART_PATH, utf8Path.getStr(), 0);
230 if (uc != CURLUE_OK)
232 SAL_WARN("ucb.ucp.webdav.curl", "curl_url_set failed: " << uc);
233 throw DAVException(DAVException::DAV_INVALID_ARG);
235 auto const oURI(GetURLComponent(*m_pUrl, CURLUPART_URL, CURLUE_OK, CURLU_NO_DEFAULT_PORT));
236 assert(oURI);
237 m_URI = *oURI;
238 auto const oPath(GetURLComponent(*m_pUrl, CURLUPART_PATH, CURLUE_OK));
239 assert(oPath);
240 m_Path = *oPath;
243 CurlUri CurlUri::CloneWithRelativeRefPathAbsolute(std::u16string_view rRelativeRef) const
245 ::std::unique_ptr<CURLU, deleter_from_fn<CURLU, curl_url_cleanup>> pUrl(
246 curl_url_dup(m_pUrl.get()));
247 size_t indexEnd(rRelativeRef.size());
248 auto const indexQuery(rRelativeRef.find('?'));
249 auto const indexFragment(rRelativeRef.find('#'));
250 CURLUcode uc;
251 if (indexFragment != std::u16string_view::npos)
253 std::u16string_view const fragment(rRelativeRef.substr(indexFragment + 1));
254 indexEnd = indexFragment;
255 OString const utf8Fragment(OUStringToOString(fragment, RTL_TEXTENCODING_UTF8));
256 uc = curl_url_set(pUrl.get(), CURLUPART_FRAGMENT, utf8Fragment.getStr(), 0);
258 else
260 uc = curl_url_set(pUrl.get(), CURLUPART_FRAGMENT, nullptr, 0);
262 if (uc != CURLUE_OK)
264 SAL_WARN("ucb.ucp.webdav.curl", "curl_url_set failed: " << uc);
265 throw DAVException(DAVException::DAV_INVALID_ARG);
267 if (indexQuery != std::u16string_view::npos
268 && (indexFragment == std::u16string_view::npos || indexQuery < indexFragment))
270 std::u16string_view const query(
271 rRelativeRef.substr(indexQuery + 1, indexEnd - indexQuery - 1));
272 indexEnd = indexQuery;
273 OString const utf8Query(OUStringToOString(query, RTL_TEXTENCODING_UTF8));
274 uc = curl_url_set(pUrl.get(), CURLUPART_QUERY, utf8Query.getStr(), 0);
276 else
278 uc = curl_url_set(pUrl.get(), CURLUPART_QUERY, nullptr, 0);
280 if (uc != CURLUE_OK)
282 SAL_WARN("ucb.ucp.webdav.curl", "curl_url_set failed: " << uc);
283 throw DAVException(DAVException::DAV_INVALID_ARG);
285 std::u16string_view const path(rRelativeRef.substr(0, indexEnd));
286 OString const utf8Path(OUStringToOString(path, RTL_TEXTENCODING_UTF8));
287 uc = curl_url_set(pUrl.get(), CURLUPART_PATH, utf8Path.getStr(), 0);
288 if (uc != CURLUE_OK)
290 SAL_WARN("ucb.ucp.webdav.curl", "curl_url_set failed: " << uc);
291 throw DAVException(DAVException::DAV_INVALID_ARG);
293 return CurlUri(*pUrl.release());
296 OUString EncodeSegment(OUString const& rSegment)
298 return rtl::Uri::encode(rSegment, rtl_UriCharClassPchar, rtl_UriEncodeIgnoreEscapes,
299 RTL_TEXTENCODING_UTF8);
302 OUString DecodeURI(OUString const& rURI)
304 return rtl::Uri::decode(rURI, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8);
307 OUString ConnectionEndPointString(std::u16string_view rHostName, sal_uInt16 const nPort)
309 OUStringBuffer aBuf;
311 // Is host a numeric IPv6 address?
312 if ((rHostName.find(':') != std::u16string_view::npos) && (rHostName[0] != '['))
314 aBuf.append(OUString::Concat("[") + rHostName + "]");
316 else
318 aBuf.append(rHostName);
321 if ((nPort != DEFAULT_HTTP_PORT) && (nPort != DEFAULT_HTTPS_PORT))
323 aBuf.append(":" + OUString::number(sal_Int32(nPort)));
325 return aBuf.makeStringAndClear();
328 } // namespace http_dav_ucp
330 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */