1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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>
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)
38 auto uc
= curl_url_get(&rURI
, what
, &pPart
, flags
);
39 if (expected
!= CURLUE_OK
&& uc
== expected
)
41 return ::std::optional
<OUString
>();
45 SAL_WARN("ucb.ucp.webdav.curl", "curl_url_get failed: " << what
<< " " << uc
);
46 throw DAVException(DAVException::DAV_INVALID_ARG
);
49 CurlUniquePtr
<char> pPart2(pPart
);
50 return ::rtl::OStringToOUString(pPart
, RTL_TEXTENCODING_UTF8
);
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
));
60 auto const oScheme(GetURLComponent(*m_pUrl
, CURLUPART_SCHEME
, CURLUE_NO_SCHEME
));
65 auto const oUser(GetURLComponent(*m_pUrl
, CURLUPART_USER
, CURLUE_NO_USER
));
70 auto const oPassWord(GetURLComponent(*m_pUrl
, CURLUPART_PASSWORD
, CURLUE_NO_PASSWORD
));
73 m_Password
= *oPassWord
;
75 auto const oHost(GetURLComponent(*m_pUrl
, CURLUPART_HOST
, CURLUE_NO_HOST
));
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
));
85 m_nPort
= oPort
->toInt32();
88 auto const oPath(GetURLComponent(*m_pUrl
, CURLUPART_PATH
, CURLUE_OK
));
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
));
96 m_QueryAndFragment
+= "?" + *oQuery
;
98 auto const oFragment(GetURLComponent(*m_pUrl
, CURLUPART_FRAGMENT
, CURLUE_NO_FRAGMENT
));
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());
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);
126 SAL_WARN("ucb.ucp.webdav.curl", "curl_url_set failed: " << uc
);
127 throw DAVException(DAVException::DAV_INVALID_ARG
);
133 CurlUri::CurlUri(CURLU
/*const*/& rUrl
)
134 : m_pUrl(curl_url_dup(&rUrl
))
138 throw ::std::bad_alloc();
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
);
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()));
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.
190 nPos
= m_Path
.lastIndexOf('/', nPos
);
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);
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
));
213 auto const oScheme(GetURLComponent(*m_pUrl
, CURLUPART_SCHEME
, CURLUE_NO_SCHEME
));
220 void CurlUri::AppendPath(::std::u16string_view
const rPath
)
222 OUStringBuffer
path(m_Path
);
223 if (path
.lastIndexOf('/') != path
.getLength() - 1)
228 OString
const utf8Path(OUStringToOString(path
, RTL_TEXTENCODING_UTF8
));
229 auto uc
= curl_url_set(m_pUrl
.get(), CURLUPART_PATH
, utf8Path
.getStr(), 0);
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
));
238 auto const oPath(GetURLComponent(*m_pUrl
, CURLUPART_PATH
, CURLUE_OK
));
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('#'));
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);
260 uc
= curl_url_set(pUrl
.get(), CURLUPART_FRAGMENT
, nullptr, 0);
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);
278 uc
= curl_url_set(pUrl
.get(), CURLUPART_QUERY
, nullptr, 0);
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);
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
)
311 // Is host a numeric IPv6 address?
312 if ((rHostName
.find(':') != std::u16string_view::npos
) && (rHostName
[0] != '['))
314 aBuf
.append(OUString::Concat("[") + rHostName
+ "]");
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: */