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 
28 namespace http_dav_ucp
29 {
30 const auto DEFAULT_HTTP_PORT = 80;
31 const auto DEFAULT_HTTPS_PORT = 443;
32 
33 static ::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 
105 CurlUri::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 
129 CurlUri::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 
140 CurlUri::CurlUri(CurlUri const& rOther)
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 
158 void 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 
176 bool CurlUri::operator==(CurlUri const& rOther) const { return m_URI == rOther.m_URI; }
177 
178 OUString CurlUri::GetPathBaseName() const
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 
197 void 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 
216 void 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.makeStringAndClear(), 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 
239 CurlUri CurlUri::CloneWithRelativeRefPathAbsolute(OUString const& rRelativeRef) const
240 {
241  ::std::unique_ptr<CURLU, deleter_from_fn<CURLU, curl_url_cleanup>> pUrl(
242  curl_url_dup(m_pUrl.get()));
243  sal_Int32 indexEnd(rRelativeRef.getLength());
244  auto const indexQuery(rRelativeRef.indexOf('?'));
245  auto const indexFragment(rRelativeRef.indexOf('#'));
246  CURLUcode uc;
247  if (indexFragment != -1)
248  {
249  std::u16string_view const fragment(rRelativeRef.subView(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 != -1 && (indexFragment == -1 || indexQuery < indexFragment))
264  {
265  std::u16string_view const query(
266  rRelativeRef.subView(indexQuery + 1, indexEnd - indexQuery - 1));
267  indexEnd = indexQuery;
268  OString const utf8Query(OUStringToOString(query, RTL_TEXTENCODING_UTF8));
269  uc = curl_url_set(pUrl.get(), CURLUPART_QUERY, utf8Query.getStr(), 0);
270  }
271  else
272  {
273  uc = curl_url_set(pUrl.get(), CURLUPART_QUERY, nullptr, 0);
274  }
275  if (uc != CURLUE_OK)
276  {
277  SAL_WARN("ucb.ucp.webdav.curl", "curl_url_set failed: " << uc);
279  }
280  std::u16string_view const path(rRelativeRef.subView(0, indexEnd));
281  OString const utf8Path(OUStringToOString(path, RTL_TEXTENCODING_UTF8));
282  uc = curl_url_set(pUrl.get(), CURLUPART_PATH, utf8Path.getStr(), 0);
283  if (uc != CURLUE_OK)
284  {
285  SAL_WARN("ucb.ucp.webdav.curl", "curl_url_set failed: " << uc);
287  }
288  return CurlUri(*pUrl.release());
289 }
290 
291 OUString EncodeSegment(OUString const& rSegment)
292 {
293  return rtl::Uri::encode(rSegment, rtl_UriCharClassPchar, rtl_UriEncodeIgnoreEscapes,
294  RTL_TEXTENCODING_UTF8);
295 }
296 
297 OUString DecodeURI(OUString const& rURI)
298 {
299  return rtl::Uri::decode(rURI, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8);
300 }
301 
302 OUString ConnectionEndPointString(std::u16string_view rHostName, sal_uInt16 const nPort)
303 {
304  OUStringBuffer aBuf;
305 
306  // Is host a numeric IPv6 address?
307  if ((rHostName.find(':') != std::u16string_view::npos) && (rHostName[0] != '['))
308  {
309  aBuf.append("[");
310  aBuf.append(rHostName);
311  aBuf.append("]");
312  }
313  else
314  {
315  aBuf.append(rHostName);
316  }
317 
318  if ((nPort != DEFAULT_HTTP_PORT) && (nPort != DEFAULT_HTTPS_PORT))
319  {
320  aBuf.append(":");
321  aBuf.append(sal_Int32(nPort));
322  }
323  return aBuf.makeStringAndClear();
324 }
325 
326 } // namespace http_dav_ucp
327 
328 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
css::uno::Reference< css::linguistic2::XProofreadingIterator > get(css::uno::Reference< css::uno::XComponentContext > const &context)
const auto DEFAULT_HTTP_PORT
Definition: CurlUri.cxx:30
OUString GetPathBaseName() const
Definition: CurlUri.cxx:178
::std::unique_ptr< CURLU, deleter_from_fn< CURLU, curl_url_cleanup > > m_pUrl
native curl representation of parsed URI
Definition: CurlUri.hxx:42
aBuf
OUString m_URI
duplicate state for quick access to some components
Definition: CurlUri.hxx:44
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
OUString m_QueryAndFragment
Definition: CurlUri.hxx:51
OUString GetPathBaseNameUnescaped() const
Definition: CurlUri.cxx:195
void operator=(CurlUri const &rOther)
Definition: CurlUri.cxx:158
OUString EncodeSegment(OUString const &rSegment)
Definition: CurlUri.cxx:291
CurlUri CloneWithRelativeRefPathAbsolute(OUString const &rRelativeRef) const
Definition: CurlUri.cxx:239
const auto DEFAULT_HTTPS_PORT
Definition: CurlUri.cxx:31
static::std::optional< OUString > GetURLComponent(CURLU &rURI, CURLUPart const what, CURLUcode const expected, unsigned int const flags=0)
Definition: CurlUri.cxx:33
sal_uInt16 m_nPort
Definition: CurlUri.hxx:49
bool operator==(CurlUri const &rOther) const
Definition: CurlUri.cxx:176
OUString DecodeURI(OUString const &rURI)
Definition: CurlUri.cxx:297
store_handle_type *SAL_CALL query(OStoreObject *pHandle, store_handle_type *)
#define SAL_WARN(area, stream)
void SetScheme(::std::u16string_view rScheme)
Definition: CurlUri.cxx:197
::std::unique_ptr< T, deleter_from_fn< T, curl_free >> CurlUniquePtr
Definition: CurlUri.hxx:36
void AppendPath(::std::u16string_view rPath)
Definition: CurlUri.cxx:216
int fragment
CurlUri(CurlUri const &rUri)
Definition: CurlUri.cxx:140
sal_uInt16 nPos
OUString ConnectionEndPointString(std::u16string_view rHostName, sal_uInt16 const nPort)
Definition: CurlUri.cxx:302