LibreOffice Module ucb (master) 1
CurlUri.cxx
Go to the documentation of this file.
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.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include "CurlUri.hxx"
21
22#include <sal/log.hxx>
23#include <rtl/uri.hxx>
24#include <rtl/ustrbuf.hxx>
25
26#include <optional>
27
28namespace http_dav_ucp
29{
30const auto DEFAULT_HTTP_PORT = 80;
31const auto DEFAULT_HTTPS_PORT = 443;
32
33static ::std::optional<OUString> GetURLComponent(CURLU& rURI, CURLUPart const what,
34 CURLUcode const expected,
35 unsigned int const flags = 0)
36{
37 char* pPart(nullptr);
38 auto uc = curl_url_get(&rURI, what, &pPart, flags);
39 if (expected != CURLUE_OK && uc == expected)
40 {
41 return ::std::optional<OUString>();
42 }
43 if (uc != CURLUE_OK)
44 {
45 SAL_WARN("ucb.ucp.webdav.curl", "curl_url_get failed: " << what << " " << uc);
47 }
48 assert(pPart);
49 CurlUniquePtr<char> pPart2(pPart);
50 return ::rtl::OStringToOUString(pPart, RTL_TEXTENCODING_UTF8);
51}
52
54{
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;
59
60 auto const oScheme(GetURLComponent(*m_pUrl, CURLUPART_SCHEME, CURLUE_NO_SCHEME));
61 if (oScheme)
62 {
63 m_Scheme = *oScheme;
64 }
65 auto const oUser(GetURLComponent(*m_pUrl, CURLUPART_USER, CURLUE_NO_USER));
66 if (oUser)
67 {
68 m_User = *oUser;
69 }
70 auto const oPassWord(GetURLComponent(*m_pUrl, CURLUPART_PASSWORD, CURLUE_NO_PASSWORD));
71 if (oPassWord)
72 {
73 m_Password = *oPassWord;
74 }
75 auto const oHost(GetURLComponent(*m_pUrl, CURLUPART_HOST, CURLUE_NO_HOST));
76 if (oHost)
77 {
78 m_Host = *oHost;
79 }
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)
84 {
85 m_nPort = oPort->toInt32();
86 }
87
88 auto const oPath(GetURLComponent(*m_pUrl, CURLUPART_PATH, CURLUE_OK));
89 assert(oPath);
90 m_Path = *oPath;
91
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)
95 {
96 m_QueryAndFragment += "?" + *oQuery;
97 }
98 auto const oFragment(GetURLComponent(*m_pUrl, CURLUPART_FRAGMENT, CURLUE_NO_FRAGMENT));
99 if (oFragment)
100 {
101 m_QueryAndFragment += "#" + *oFragment;
102 }
103}
104
105CurlUri::CurlUri(::std::u16string_view const rURI)
106{
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
110
111 m_pUrl.reset(curl_url());
112 if (!m_pUrl)
113 {
114 throw ::std::bad_alloc();
115 }
116
117 // use curl to parse the URI, to get a consistent interpretation
118 OString const utf8URI(OUStringToOString(rURI, RTL_TEXTENCODING_UTF8));
119 auto uc = curl_url_set(m_pUrl.get(), CURLUPART_URL, utf8URI.getStr(), 0);
120 if (uc != CURLUE_OK)
121 {
122 SAL_WARN("ucb.ucp.webdav.curl", "curl_url_set failed: " << uc);
124 }
125
126 Init();
127}
128
129CurlUri::CurlUri(CURLU /*const*/& rUrl)
130 : m_pUrl(curl_url_dup(&rUrl))
131{
132 if (!m_pUrl)
133 {
134 throw ::std::bad_alloc();
135 }
136
137 Init();
138}
139
141 : m_pUrl(curl_url_dup(rOther.m_pUrl.get()))
142 , m_URI(rOther.m_URI)
143 , m_Scheme(rOther.m_Scheme)
144 , m_User(rOther.m_User)
145 , m_Password(rOther.m_Password)
146 , m_Host(rOther.m_Host)
147 , m_nPort(rOther.m_nPort)
148 , m_Path(rOther.m_Path)
149 , m_QueryAndFragment(rOther.m_QueryAndFragment)
150{
151 assert(rOther.m_pUrl);
152 if (!m_pUrl)
153 {
154 throw ::std::bad_alloc();
155 }
156}
157
158void CurlUri::operator=(CurlUri const& rOther)
159{
160 assert(rOther.m_pUrl);
161 m_pUrl.reset(curl_url_dup(rOther.m_pUrl.get()));
162 if (!m_pUrl)
163 {
164 throw ::std::bad_alloc();
165 }
166 m_URI = rOther.m_URI;
167 m_Scheme = rOther.m_Scheme;
168 m_User = rOther.m_User;
169 m_Password = rOther.m_Password;
170 m_Host = rOther.m_Host;
171 m_nPort = rOther.m_nPort;
172 m_Path = rOther.m_Path;
174}
175
176bool CurlUri::operator==(CurlUri const& rOther) const { return m_URI == rOther.m_URI; }
177
179{
180 sal_Int32 nPos = m_Path.lastIndexOf('/');
181 sal_Int32 nTrail = 0;
182 if (nPos == m_Path.getLength() - 1)
183 {
184 // Trailing slash found. Skip.
185 nTrail = 1;
186 nPos = m_Path.lastIndexOf('/', nPos);
187 }
188 if (nPos == -1)
189 {
190 return "/";
191 }
192 return m_Path.copy(nPos + 1, m_Path.getLength() - nPos - 1 - nTrail);
193}
194
196
197void CurlUri::SetScheme(::std::u16string_view const rScheme)
198{
199 OString const utf8URI(OUStringToOString(rScheme, RTL_TEXTENCODING_UTF8));
200 auto uc = curl_url_set(m_pUrl.get(), CURLUPART_SCHEME, utf8URI.getStr(), 0);
201 if (uc != CURLUE_OK)
202 {
203 SAL_WARN("ucb.ucp.webdav.curl", "curl_url_set failed: " << uc);
205 }
206 auto const oURI(GetURLComponent(*m_pUrl, CURLUPART_URL, CURLUE_OK, CURLU_NO_DEFAULT_PORT));
207 assert(oURI);
208 m_URI = *oURI;
209 auto const oScheme(GetURLComponent(*m_pUrl, CURLUPART_SCHEME, CURLUE_NO_SCHEME));
210 if (oScheme)
211 {
212 m_Scheme = *oScheme;
213 }
214}
215
216void CurlUri::AppendPath(::std::u16string_view const rPath)
217{
218 OUStringBuffer path(m_Path);
219 if (path.lastIndexOf('/') != path.getLength() - 1)
220 {
221 path.append("/");
222 }
223 path.append(rPath);
224 OString const utf8Path(OUStringToOString(path, RTL_TEXTENCODING_UTF8));
225 auto uc = curl_url_set(m_pUrl.get(), CURLUPART_PATH, utf8Path.getStr(), 0);
226 if (uc != CURLUE_OK)
227 {
228 SAL_WARN("ucb.ucp.webdav.curl", "curl_url_set failed: " << uc);
230 }
231 auto const oURI(GetURLComponent(*m_pUrl, CURLUPART_URL, CURLUE_OK, CURLU_NO_DEFAULT_PORT));
232 assert(oURI);
233 m_URI = *oURI;
234 auto const oPath(GetURLComponent(*m_pUrl, CURLUPART_PATH, CURLUE_OK));
235 assert(oPath);
236 m_Path = *oPath;
237}
238
239CurlUri CurlUri::CloneWithRelativeRefPathAbsolute(std::u16string_view rRelativeRef) const
240{
241 ::std::unique_ptr<CURLU, deleter_from_fn<CURLU, curl_url_cleanup>> pUrl(
242 curl_url_dup(m_pUrl.get()));
243 size_t indexEnd(rRelativeRef.size());
244 auto const indexQuery(rRelativeRef.find('?'));
245 auto const indexFragment(rRelativeRef.find('#'));
246 CURLUcode uc;
247 if (indexFragment != std::u16string_view::npos)
248 {
249 std::u16string_view const fragment(rRelativeRef.substr(indexFragment + 1));
250 indexEnd = indexFragment;
251 OString const utf8Fragment(OUStringToOString(fragment, RTL_TEXTENCODING_UTF8));
252 uc = curl_url_set(pUrl.get(), CURLUPART_FRAGMENT, utf8Fragment.getStr(), 0);
253 }
254 else
255 {
256 uc = curl_url_set(pUrl.get(), CURLUPART_FRAGMENT, nullptr, 0);
257 }
258 if (uc != CURLUE_OK)
259 {
260 SAL_WARN("ucb.ucp.webdav.curl", "curl_url_set failed: " << uc);
262 }
263 if (indexQuery != std::u16string_view::npos
264 && (indexFragment == std::u16string_view::npos || indexQuery < indexFragment))
265 {
266 std::u16string_view const query(
267 rRelativeRef.substr(indexQuery + 1, indexEnd - indexQuery - 1));
268 indexEnd = indexQuery;
269 OString const utf8Query(OUStringToOString(query, RTL_TEXTENCODING_UTF8));
270 uc = curl_url_set(pUrl.get(), CURLUPART_QUERY, utf8Query.getStr(), 0);
271 }
272 else
273 {
274 uc = curl_url_set(pUrl.get(), CURLUPART_QUERY, nullptr, 0);
275 }
276 if (uc != CURLUE_OK)
277 {
278 SAL_WARN("ucb.ucp.webdav.curl", "curl_url_set failed: " << uc);
280 }
281 std::u16string_view const path(rRelativeRef.substr(0, indexEnd));
282 OString const utf8Path(OUStringToOString(path, RTL_TEXTENCODING_UTF8));
283 uc = curl_url_set(pUrl.get(), CURLUPART_PATH, utf8Path.getStr(), 0);
284 if (uc != CURLUE_OK)
285 {
286 SAL_WARN("ucb.ucp.webdav.curl", "curl_url_set failed: " << uc);
288 }
289 return CurlUri(*pUrl.release());
290}
291
292OUString EncodeSegment(OUString const& rSegment)
293{
294 return rtl::Uri::encode(rSegment, rtl_UriCharClassPchar, rtl_UriEncodeIgnoreEscapes,
295 RTL_TEXTENCODING_UTF8);
296}
297
298OUString DecodeURI(OUString const& rURI)
299{
300 return rtl::Uri::decode(rURI, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8);
301}
302
303OUString ConnectionEndPointString(std::u16string_view rHostName, sal_uInt16 const nPort)
304{
305 OUStringBuffer aBuf;
306
307 // Is host a numeric IPv6 address?
308 if ((rHostName.find(':') != std::u16string_view::npos) && (rHostName[0] != '['))
309 {
310 aBuf.append(OUString::Concat("[") + rHostName + "]");
311 }
312 else
313 {
314 aBuf.append(rHostName);
315 }
316
317 if ((nPort != DEFAULT_HTTP_PORT) && (nPort != DEFAULT_HTTPS_PORT))
318 {
319 aBuf.append(":" + OUString::number(sal_Int32(nPort)));
320 }
321 return aBuf.makeStringAndClear();
322}
323
324} // namespace http_dav_ucp
325
326/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
CurlUri CloneWithRelativeRefPathAbsolute(std::u16string_view rRelativeRef) const
Definition: CurlUri.cxx:239
OUString GetPathBaseName() const
Definition: CurlUri.cxx:178
void SetScheme(::std::u16string_view rScheme)
Definition: CurlUri.cxx:197
CurlUri(CurlUri const &rUri)
Definition: CurlUri.cxx:140
::std::unique_ptr< CURLU, deleter_from_fn< CURLU, curl_url_cleanup > > m_pUrl
native curl representation of parsed URI
Definition: CurlUri.hxx:42
OUString m_QueryAndFragment
Definition: CurlUri.hxx:51
OUString m_URI
duplicate state for quick access to some components
Definition: CurlUri.hxx:44
bool operator==(CurlUri const &rOther) const
Definition: CurlUri.cxx:176
void operator=(CurlUri const &rOther)
Definition: CurlUri.cxx:158
OUString GetPathBaseNameUnescaped() const
Definition: CurlUri.cxx:195
sal_uInt16 m_nPort
Definition: CurlUri.hxx:49
void AppendPath(::std::u16string_view rPath)
Definition: CurlUri.cxx:216
sal_uInt16 nPos
#define SAL_WARN(area, stream)
aBuf
OUString DecodeURI(OUString const &rURI)
Definition: CurlUri.cxx:298
const auto DEFAULT_HTTP_PORT
Definition: CurlUri.cxx:30
static ::std::optional< OUString > GetURLComponent(CURLU &rURI, CURLUPart const what, CURLUcode const expected, unsigned int const flags=0)
Definition: CurlUri.cxx:33
::std::unique_ptr< T, deleter_from_fn< T, curl_free > > CurlUniquePtr
Definition: CurlUri.hxx:36
OUString EncodeSegment(OUString const &rSegment)
Definition: CurlUri.cxx:292
OUString ConnectionEndPointString(std::u16string_view rHostName, sal_uInt16 const nPort)
Definition: CurlUri.cxx:303
const auto DEFAULT_HTTPS_PORT
Definition: CurlUri.cxx:31
int fragment
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
store_handle_type *SAL_CALL query(OStoreObject *pHandle, store_handle_type *)
css::uno::Reference< css::linguistic2::XProofreadingIterator > get(css::uno::Reference< css::uno::XComponentContext > const &context)