25#include <officecfg/Inet.hxx>
27#include <com/sun/star/beans/NamedValue.hpp>
28#include <com/sun/star/io/Pipe.hpp>
29#include <com/sun/star/io/SequenceInputStream.hpp>
30#include <com/sun/star/io/SequenceOutputStream.hpp>
31#include <com/sun/star/xml/sax/Writer.hpp>
36#include <rtl/strbuf.hxx>
37#include <rtl/ustrbuf.hxx>
38#include <config_version.h>
58 if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK)
60 assert(!
"curl_global_init failed");
69 ::std::vector<::std::pair<::std::vector<OString>, ::std::optional<long>>> HeaderFields;
71 ResponseHeaders(CURL*
const i_pCurl)
77auto GetResponseCode(ResponseHeaders
const& rHeaders) -> ::std::optional<long>
79 return (rHeaders.HeaderFields.empty()) ? ::std::optional<long>{}
80 : rHeaders.HeaderFields.back().second;
85 uno::Reference<io::XOutputStream> xOutStream;
86 ResponseHeaders
const& rHeaders;
87 DownloadTarget(uno::Reference<io::XOutputStream> i_xOutStream,
88 ResponseHeaders
const& i_rHeaders)
89 : xOutStream(
std::move(i_xOutStream))
90 , rHeaders(i_rHeaders)
97 uno::Sequence<sal_Int8>
const& rInData;
99 UploadSource(uno::Sequence<sal_Int8>
const& i_rInData)
106auto GetErrorString(CURLcode
const rc,
char const*
const pErrorBuffer =
nullptr) -> OString
108 char const*
const pMessage(
109 (pErrorBuffer && pErrorBuffer[0] !=
'\0') ? pErrorBuffer : curl_easy_strerror(rc));
110 return OString::Concat(
"(") + OString::number(sal_Int32(rc)) +
") " + pMessage;
113auto GetErrorStringMulti(CURLMcode
const mc) -> OString
115 return OString::Concat(
"(") + OString::number(sal_Int32(mc)) +
") " + curl_multi_strerror(mc);
121 CURLoption
const Option;
130 void const*
const pValue;
134 char const*
const pExceptionString;
136 CurlOption(CURLoption
const i_Option,
void const*
const i_Value,
137 char const*
const i_pExceptionString)
141 , pExceptionString(i_pExceptionString)
146 CurlOption(CURLoption
const i_Option, curl_off_t
const i_Value,
147 char const*
const i_pExceptionString,
Type const type = Type::Long)
150 , pExceptionString(i_pExceptionString)
152 static_assert(
sizeof(long) <=
sizeof(curl_off_t));
169const CurlOption g_NoBody{ CURLOPT_NOBODY,
170 sal_detail_log_report(SAL_DETAIL_LOG_LEVEL_INFO,
"ucb.ucp.webdav.curl")
171 == SAL_DETAIL_LOG_ACTION_IGNORE
182 ::std::unique_lock<::std::mutex> m_Lock;
183 ::std::vector<CurlOption>
const m_Options;
188 explicit Guard(::std::mutex& rMutex, ::std::vector<CurlOption> aOptions,
190 : m_Lock(rMutex, ::
std::defer_lock)
191 , m_Options(
std::move(aOptions))
199 if (m_Lock.owns_lock())
207 assert(!m_Lock.owns_lock());
209 for (
auto const& it : m_Options)
211 CURLcode rc(CURL_LAST);
212 if (it.Tag == CurlOption::Type::Pointer)
214 rc = curl_easy_setopt(m_pCurl, it.Option, it.pValue);
216 else if (it.Tag == CurlOption::Type::Long)
218 rc = curl_easy_setopt(m_pCurl, it.Option, it.lValue);
220 else if (it.Tag == CurlOption::Type::CurlOffT)
222 rc = curl_easy_setopt(m_pCurl, it.Option, it.cValue);
228 if (it.pExceptionString !=
nullptr)
233 "set " << it.pExceptionString <<
" failed: " << GetErrorString(rc));
234 throw ::http_dav_ucp::DAVException(
242 assert(rc == CURLE_OK);
248 assert(m_Lock.owns_lock());
249 for (
auto const& it : m_Options)
251 CURLcode rc(CURL_LAST);
252 if (it.Tag == CurlOption::Type::Pointer)
254 rc = curl_easy_setopt(m_pCurl, it.Option,
nullptr);
256 else if (it.Tag == CurlOption::Type::Long)
258 rc = curl_easy_setopt(m_pCurl, it.Option, 0L);
260 else if (it.Tag == CurlOption::Type::CurlOffT)
262 rc = curl_easy_setopt(m_pCurl, it.Option, curl_off_t(-1));
268 assert(rc == CURLE_OK);
281static int debug_callback(CURL* handle, curl_infotype type,
char* data,
size_t size,
284 char const* pType(
nullptr);
288 SAL_INFO(
"ucb.ucp.webdav.curl",
"debug log: " << handle <<
": " << data);
290 case CURLINFO_HEADER_IN:
292 "CURLINFO_HEADER_IN: " << handle <<
": " << OString(data,
size));
294 case CURLINFO_HEADER_OUT:
297 OString tmp(data,
size);
298 sal_Int32
const start(tmp.indexOf(
"Authorization: "));
301 sal_Int32
const end(tmp.indexOf(
"\r\n",
start));
306 Concat2View(OString::number(
end -
start - len) +
" bytes redacted"));
308 SAL_INFO(
"ucb.ucp.webdav.curl",
"CURLINFO_HEADER_OUT: " << handle <<
": " << tmp);
311 case CURLINFO_DATA_IN:
312 pType =
"CURLINFO_DATA_IN";
314 case CURLINFO_DATA_OUT:
315 pType =
"CURLINFO_DATA_OUT";
317 case CURLINFO_SSL_DATA_IN:
318 pType =
"CURLINFO_SSL_DATA_IN";
320 case CURLINFO_SSL_DATA_OUT:
321 pType =
"CURLINFO_SSL_DATA_OUT";
324 SAL_WARN(
"ucb.ucp.webdav.curl",
"unexpected debug log type");
327 SAL_INFO(
"ucb.ucp.webdav.curl",
"debug log: " << handle <<
": " << pType <<
" " <<
size);
331static size_t write_callback(
char*
const ptr,
size_t const size,
size_t const nmemb,
332 void*
const userdata)
334 auto*
const pTarget(
static_cast<DownloadTarget*
>(userdata));
341 assert(pTarget->xOutStream.is());
342 auto const oResponseCode(GetResponseCode(pTarget->rHeaders));
352 uno::Sequence<sal_Int8>
const data(
reinterpret_cast<sal_Int8*
>(ptr), nmemb);
353 pTarget->xOutStream->writeBytes(data);
357 SAL_WARN(
"ucb.ucp.webdav.curl",
"exception in write_callback");
365static size_t read_callback(
char*
const buffer,
size_t const size,
size_t const nitems,
366 void*
const userdata)
368 auto*
const pSource(
static_cast<UploadSource*
>(userdata));
370 size_t const nBytes(
size * nitems);
375 nRet = ::std::min<size_t>(pSource->rInData.getLength() - pSource->nPosition, nBytes);
376 ::std::memcpy(buffer, pSource->rInData.getConstArray() + pSource->nPosition, nRet);
377 pSource->nPosition += nRet;
381 SAL_WARN(
"ucb.ucp.webdav.curl",
"exception in read_callback");
382 return CURL_READFUNC_ABORT;
387static size_t header_callback(
char*
const buffer,
size_t const size,
size_t const nitems,
388 void*
const userdata)
390 auto*
const pHeaders(
static_cast<ResponseHeaders*
>(userdata));
405 if (pHeaders->HeaderFields.empty())
407 SAL_WARN(
"ucb.ucp.webdav.curl",
"header_callback: empty header?");
413 auto rc = curl_easy_getinfo(pHeaders->pCurl, CURLINFO_RESPONSE_CODE, &statusCode);
414 assert(rc == CURLE_OK);
417 pHeaders->HeaderFields.back().second.emplace(statusCode);
419 else if (buffer[0] ==
' ' || buffer[0] ==
'\t')
425 }
while (
i ==
' ' ||
i ==
'\t');
426 if (pHeaders->HeaderFields.empty() || pHeaders->HeaderFields.back().second
427 || pHeaders->HeaderFields.back().first.empty())
430 "header_callback: folded header field without start");
433 pHeaders->HeaderFields.back().first.back()
434 += OString::Concat(
" ") + ::std::string_view(&buffer[
i], nitems -
i);
438 if (pHeaders->HeaderFields.empty() || pHeaders->HeaderFields.back().second)
440 pHeaders->HeaderFields.emplace_back();
442 pHeaders->HeaderFields.back().first.emplace_back(OString(buffer, nitems));
447 SAL_WARN(
"ucb.ucp.webdav.curl",
"exception in header_callback");
453static auto ProcessHeaders(::std::vector<OString>
const& rHeaders) -> ::std::map<OUString, OUString>
455 ::std::map<OUString, OUString> ret;
456 for (OString
const& rLine : rHeaders)
459 if (!rLine.endsWith(
"\r\n", &
line))
461 SAL_WARN(
"ucb.ucp.webdav.curl",
"invalid header field (no CRLF)");
464 if (
line.startsWith(
"HTTP/")
469 auto const nColon(
line.indexOf(
':'));
473 SAL_WARN(
"ucb.ucp.webdav.curl",
"invalid header field (no :)");
479 SAL_WARN(
"ucb.ucp.webdav.curl",
"invalid header field (empty name)");
483 auto const name(::rtl::OStringToOUString(
line.copy(0, nColon).toAsciiLowerCase(),
484 RTL_TEXTENCODING_ASCII_US));
485 sal_Int32 nStart(nColon + 1);
486 while (nStart <
line.getLength() && (
line[nStart] ==
' ' ||
line[nStart] ==
'\t'))
490 sal_Int32 nEnd(
line.getLength());
491 while (nStart < nEnd && (
line[nEnd - 1] ==
' ' ||
line[nEnd - 1] ==
'\t'))
496 auto const value(::rtl::OStringToOUString(
line.subView(nStart, nEnd - nStart),
497 RTL_TEXTENCODING_ASCII_US));
498 auto const it(ret.find(
name));
501 it->second = it->second +
"," +
value;
512 ResponseHeaders
const& rHeaders,
513 ::std::pair<::std::vector<OUString>
const&,
DAVResource&>
const*
const pRequestedHeaders)
516 ::std::map<OUString, OUString>
const headerMap(
518 if (pRequestedHeaders)
520 for (OUString
const& rHeader : pRequestedHeaders->first)
522 auto const it(headerMap.find(rHeader.toAsciiLowerCase()));
523 if (it != headerMap.end())
526 value.IsCaseSensitive =
false;
527 value.Name = it->first;
528 value.Value <<= it->second;
529 pRequestedHeaders->second.properties.push_back(
value);
536static auto ExtractRealm(ResponseHeaders
const& rHeaders,
char const*
const pAuthHeaderName)
537 -> ::std::optional<OUString>
539 ::std::map<OUString, OUString>
const headerMap(
541 auto const it(headerMap.find(OUString::createFromAscii(pAuthHeaderName).toAsciiLowerCase()));
542 if (it == headerMap.end())
544 SAL_WARN(
"ucb.ucp.webdav.curl",
"cannot find auth header");
551 auto i(it->second.toAsciiLowerCase().indexOf(
"realm="));
555 SAL_INFO(
"ucb.ucp.webdav.curl",
"auth header has no realm");
559 i += ::std::strlen(
"realm=");
560 if (it->second.getLength() <
i + 2 || it->second[
i] !=
'\"')
562 SAL_WARN(
"ucb.ucp.webdav.curl",
"no realm value");
567 while (i < it->second.getLength() && it->second[
i] !=
'\"')
569 if (it->second[
i] ==
'\\')
572 if (it->second.getLength() <=
i)
574 SAL_WARN(
"ucb.ucp.webdav.curl",
"unterminated quoted-pair");
578 buf.append(it->second[
i]);
581 if (it->second.getLength() <=
i)
583 SAL_WARN(
"ucb.ucp.webdav.curl",
"unterminated realm");
586 return buf.makeStringAndClear();
591 uno::Sequence<beans::NamedValue>
const& rFlags,
597 , m_Proxy(rProxyDecider.getProxy(m_URI.GetScheme(), m_URI.GetHost(), m_URI.GetPort()))
603 SAL_WARN(
"ucb.ucp.webdav.curl",
"curl_multi_init failed");
607 m_pCurl.reset(curl_easy_init());
610 SAL_WARN(
"ucb.ucp.webdav.curl",
"curl_easy_init failed");
614 curl_version_info_data
const*
const pVersion(curl_version_info(CURLVERSION_NOW));
617 "curl version: " << pVersion->version <<
" " << pVersion->host
618 <<
" features: " << ::std::hex << pVersion->features <<
" ssl: "
619 << pVersion->ssl_version <<
" libz: " << pVersion->libz_version);
624 OString
const useragent(
625 OString::Concat(
"LibreOffice " LIBO_VERSION_DOTTED
" denylistedbackend/")
626 + ::std::string_view(pVersion->version, strlen(pVersion->version)) +
" "
627 + pVersion->ssl_version);
631 auto rc = curl_easy_setopt(
m_pCurl.get(), CURLOPT_USERAGENT, useragent.getStr());
634 SAL_WARN(
"ucb.ucp.webdav.curl",
"CURLOPT_USERAGENT failed: " << GetErrorString(rc));
641 assert(rc == CURLE_OK);
645 assert(rc == CURLE_OK);
647 rc = curl_easy_setopt(
m_pCurl.get(), CURLOPT_VERBOSE, 1L);
648 assert(rc == CURLE_OK);
650 rc = curl_easy_setopt(
m_pCurl.get(), CURLOPT_ACCEPT_ENCODING,
"");
653 SAL_WARN(
"ucb.ucp.webdav.curl",
"CURLOPT_ACCEPT_ENCODING failed: " << GetErrorString(rc));
657 auto const connectTimeout(officecfg::Inet::Settings::ConnectTimeout::get());
659 rc = curl_easy_setopt(
m_pCurl.get(), CURLOPT_CONNECTTIMEOUT,
660 ::std::max<long>(2L, ::std::min<long>(connectTimeout, 180L)));
663 SAL_WARN(
"ucb.ucp.webdav.curl",
"CURLOPT_CONNECTTIMEOUT failed: " << GetErrorString(rc));
667 auto const readTimeout(officecfg::Inet::Settings::ReadTimeout::get());
668 m_nReadTimeout = ::std::max<int>(20, ::std::min<long>(readTimeout, 180)) * 1000;
670 rc = curl_easy_setopt(
m_pCurl.get(), CURLOPT_TIMEOUT, 300L);
673 SAL_WARN(
"ucb.ucp.webdav.curl",
"CURLOPT_TIMEOUT failed: " << GetErrorString(rc));
678 assert(rc == CURLE_OK);
680 assert(rc == CURLE_OK);
682 assert(rc == CURLE_OK);
686#if CURL_AT_LEAST_VERSION(7, 70, 0)
687 rc = curl_easy_setopt(
m_pCurl.get(), CURLOPT_SSL_OPTIONS, CURLSSLOPT_REVOKE_BEST_EFFORT);
688 assert(rc == CURLE_OK);
689 rc = curl_easy_setopt(
m_pCurl.get(), CURLOPT_PROXY_SSL_OPTIONS, CURLSSLOPT_REVOKE_BEST_EFFORT);
690 assert(rc == CURLE_OK);
693 rc = curl_easy_setopt(
m_pCurl.get(), CURLOPT_HTTPAUTH, CURLAUTH_ANY);
694 assert(rc == CURLE_OK);
697 rc = curl_easy_setopt(
m_pCurl.get(), CURLOPT_PROXY, utf8Proxy.getStr());
700 SAL_WARN(
"ucb.ucp.webdav.curl",
"CURLOPT_PROXY failed: " << GetErrorString(rc));
707 assert(rc == CURLE_OK);
709 rc = curl_easy_setopt(
m_pCurl.get(), CURLOPT_PROXYAUTH, CURLAUTH_ANY);
710 assert(rc == CURLE_OK);
713 [](
auto const& rFlag) { return rFlag.Name ==
"KeepAlive"; }));
714 if (it !=
m_Flags.end() && it->Value.get<
bool>())
718 rc = curl_easy_setopt(
m_pCurl.get(), CURLOPT_FORBID_REUSE, 1L);
719 assert(rc == CURLE_OK);
726 rc = curl_easy_setopt(
m_pCurl.get(), CURLOPT_SSL_VERIFYPEER, 0L);
727 assert(rc == CURLE_OK);
740 return m_URI.GetScheme() == uri.
GetScheme() && m_URI.GetHost() == uri.
GetHost()
741 && m_URI.GetPort() == uri.
GetPort() && m_Flags == rFlags;
751 assert(m_URI.GetScheme() ==
"http" || m_URI.GetScheme() ==
"https");
752 return !m_Proxy.aName.isEmpty();
758 bool expected(
false);
760 if (m_AbortFlag.compare_exchange_strong(expected,
true))
765 curl_multi_wakeup(m_pCurlMulti.get());
777 curl_slist* pRequestHeaderList, uno::Reference<io::XOutputStream>
const* pxOutStream,
778 uno::Sequence<sal_Int8>
const* pInData,
779 ::std::pair<::std::vector<OUString>
const&,
DAVResource&>
const* pRequestedHeaders,
780 ResponseHeaders& rHeaders) -> void;
787 uno::Reference<io::XOutputStream>
const* pxOutStream,
788 uno::Reference<io::XInputStream>
const* pxInStream,
789 ::std::pair<::std::vector<OUString>
const&,
DAVResource&>
const* pRequestedHeaders) -> void;
793 ::std::tuple<::std::vector<OUString>
const&, ::std::vector<DAVResource>*
const,
794 ::std::vector<ucb::Lock>*
const>
const* o_pRequestedProperties,
795 ::std::vector<DAVResourceInfo>*
const o_pResourceInfos,
800 bool isOverwrite,
char const* pMethod) -> void;
805 uno::Reference<io::XInputStream>
const* pxInStream)
806 -> ::std::vector<::std::pair<ucb::Lock, sal_Int32>>;
816 if (rSession.UsesProxy())
826 return rSession.m_URI.CloneWithRelativeRefPathAbsolute(rURIReference);
833 curl_slist*
const pRequestHeaderList,
834 uno::Reference<io::XOutputStream>
const*
const pxOutStream,
835 uno::Sequence<sal_Int8>
const*
const pInData,
836 ::std::pair<::std::vector<OUString>
const&,
DAVResource&>
const*
const pRequestedHeaders,
837 ResponseHeaders& rHeaders) ->
void
840 auto rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_HEADERDATA,
nullptr);
841 assert(rc == CURLE_OK);
845 rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_WRITEDATA,
nullptr);
846 assert(rc == CURLE_OK);
850 rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_READDATA,
nullptr);
851 assert(rc == CURLE_OK);
852 rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_UPLOAD, 0L);
853 assert(rc == CURLE_OK);
855 if (pRequestHeaderList)
857 rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_HTTPHEADER,
nullptr);
858 assert(rc == CURLE_OK);
862 if (pRequestHeaderList)
864 auto rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_HTTPHEADER, pRequestHeaderList);
865 assert(rc == CURLE_OK);
869 auto rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_CURLU, rURI.GetCURLU());
870 assert(rc == CURLE_OK);
872 rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_HEADERDATA, &rHeaders);
873 assert(rc == CURLE_OK);
874 ::std::optional<DownloadTarget> oDownloadTarget;
877 oDownloadTarget.emplace(*pxOutStream, rHeaders);
878 rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_WRITEDATA, &*oDownloadTarget);
879 assert(rc == CURLE_OK);
881 ::std::optional<UploadSource> oUploadSource;
884 oUploadSource.emplace(*pInData);
885 rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_READDATA, &*oUploadSource);
886 assert(rc == CURLE_OK);
888 rSession.m_ErrorBuffer[0] =
'\0';
892 auto mc = curl_multi_add_handle(rSession.m_pCurlMulti.get(), rSession.m_pCurl.get());
896 "curl_multi_add_handle failed: " << GetErrorStringMulti(mc));
902 mc = curl_multi_remove_handle(rSession.m_pCurlMulti.get(), rSession.m_pCurl.get());
906 "curl_multi_remove_handle failed: " << GetErrorStringMulti(mc));
915 mc = curl_multi_perform(rSession.m_pCurlMulti.get(), &nRunning);
919 "curl_multi_perform failed: " << GetErrorStringMulti(mc));
929 mc = curl_multi_poll(rSession.m_pCurlMulti.get(),
nullptr, 0, rSession.m_nReadTimeout,
933 SAL_WARN(
"ucb.ucp.webdav.curl",
"curl_multi_poll failed: " << GetErrorStringMulti(mc));
938 if (rSession.m_AbortFlag.load())
942 }
while (nRunning != 0);
947 CURLMsg
const*
const pMsg = curl_multi_info_read(rSession.m_pCurlMulti.get(), &nRunning);
948 if (pMsg && pMsg->msg == CURLMSG_DONE)
950 assert(pMsg->easy_handle == rSession.m_pCurl.get());
951 rc = pMsg->data.result;
955 SAL_WARN(
"ucb.ucp.webdav.curl",
"curl_multi_info_read unexpected result");
957 }
while (nRunning != 0);
964 "curl_easy_perform failed: " << GetErrorString(rc, rSession.m_ErrorBuffer));
967 case CURLE_UNSUPPORTED_PROTOCOL:
969 case CURLE_COULDNT_RESOLVE_PROXY:
973 case CURLE_COULDNT_RESOLVE_HOST:
977 case CURLE_COULDNT_CONNECT:
978 case CURLE_SSL_CONNECT_ERROR:
979 case CURLE_SSL_CERTPROBLEM:
980 case CURLE_SSL_CIPHER:
981 case CURLE_PEER_FAILED_VERIFICATION:
982 case CURLE_SSL_ISSUER_ERROR:
983 case CURLE_SSL_PINNEDPUBKEYNOTMATCH:
984 case CURLE_SSL_INVALIDCERTSTATUS:
985 case CURLE_FAILED_INIT:
986#if CURL_AT_LEAST_VERSION(7, 69, 0)
987 case CURLE_QUIC_CONNECT_ERROR:
992 case CURLE_REMOTE_ACCESS_DENIED:
993 case CURLE_LOGIN_DENIED:
994 case CURLE_AUTH_ERROR:
998 case CURLE_WRITE_ERROR:
999 case CURLE_READ_ERROR:
1000 case CURLE_OUT_OF_MEMORY:
1001 case CURLE_ABORTED_BY_CALLBACK:
1002 case CURLE_BAD_FUNCTION_ARGUMENT:
1003 case CURLE_SEND_ERROR:
1004 case CURLE_RECV_ERROR:
1005 case CURLE_SSL_CACERT_BADFILE:
1006 case CURLE_SSL_CRL_BADFILE:
1007 case CURLE_RECURSIVE_API_CALL:
1011 case CURLE_OPERATION_TIMEDOUT:
1021 rc = curl_easy_getinfo(rSession.m_pCurl.get(), CURLINFO_RESPONSE_CODE, &statusCode);
1022 assert(rc == CURLE_OK);
1023 assert(statusCode !=
SC_NONE);
1024 SAL_INFO(
"ucb.ucp.webdav.curl",
"HTTP status code: " << statusCode);
1025 if (statusCode < 300)
1033 OUString statusLine(
"\n" + rMethod +
"\n=>\n");
1034 if (!rHeaders.HeaderFields.empty() && !rHeaders.HeaderFields.back().first.empty()
1035 && rHeaders.HeaderFields.back().first.front().startsWith(
"HTTP"))
1037 statusLine += ::rtl::OStringToOUString(
1038 ::o3tl::trim(rHeaders.HeaderFields.back().first.front()),
1039 RTL_TEXTENCODING_ASCII_US);
1057 char* pRedirectURL(
nullptr);
1058 rc = curl_easy_getinfo(rSession.m_pCurl.get(), CURLINFO_REDIRECT_URL,
1060 assert(rc == CURLE_OK);
1064 OUString
const redirectURL(::rtl::Uri::encode(
1066 ? OUString(pRedirectURL, strlen(pRedirectURL), RTL_TEXTENCODING_UTF8)
1068 rtl_UriCharClassUric, rtl_UriEncodeKeepEscapes, RTL_TEXTENCODING_UTF8));
1081 (*pxOutStream)->closeOutput();
1093 OUString
const*
const pToken(g_Init.LockStore.getLockTokenForURI(rURI.GetURI(),
nullptr));
1102 ::std::vector<ucb::Lock> locks;
1103 ::std::tuple<::std::vector<OUString>
const&, ::std::vector<DAVResource>*
const,
1104 ::std::vector<ucb::Lock>*
const>
const args(propertyNames,
nullptr, &locks);
1111 if (::std::any_of(locks.begin(), locks.end(), [pToken](ucb::Lock
const& rLock) {
1112 return ::std::any_of(
1113 rLock.LockTokens.begin(), rLock.LockTokens.end(),
1114 [pToken](OUString const& rToken) { return *pToken == rToken; });
1121 "lock token expired, removing: " << rURI.GetURI() <<
" " << *pToken);
1122 g_Init.LockStore.removeLock(rURI.GetURI());
1125 catch (DAVException
const&)
1131auto CurlProcessor::ProcessRequest(
1136 uno::Reference<io::XOutputStream>
const*
const pxOutStream,
1137 uno::Reference<io::XInputStream>
const*
const pxInStream,
1138 ::std::pair<::std::vector<OUString>
const&,
DAVResource&>
const*
const pRequestedHeaders)
1143 for (
auto const& rHeader : pEnv->m_aRequestHeaders)
1145 OString
const utf8Header(
1148 pRequestHeaderList.reset(
1149 curl_slist_append(pRequestHeaderList.release(), utf8Header.getStr()));
1150 if (!pRequestHeaderList)
1152 throw uno::RuntimeException(
"curl_slist_append failed");
1157 uno::Sequence<sal_Int8> data;
1160 uno::Reference<io::XSeekable>
const xSeekable(*pxInStream, uno::UNO_QUERY);
1163 auto const len(xSeekable->getLength() - xSeekable->getPosition());
1164 if ((**pxInStream).readBytes(data, len) != len)
1166 throw uno::RuntimeException(
"short readBytes");
1171 ::std::vector<uno::Sequence<sal_Int8>> bufs;
1175 bufs.emplace_back();
1176 isDone = (**pxInStream).readSomeBytes(bufs.back(), 65536) == 0;
1179 for (
auto const& rBuf : bufs)
1183 throw std::bad_alloc();
1186 data.realloc(nSize);
1188 for (
auto const& rBuf : bufs)
1190 ::std::memcpy(data.getArray() + nCopied, rBuf.getConstArray(), rBuf.getLength());
1191 nCopied += rBuf.getLength();
1198 rSession.m_AbortFlag.store(
false);
1200 Guard guard(rSession.m_Mutex, rOptions, rURI, rSession.m_pCurl.get());
1207 decltype(CURLAUTH_ANY) AuthMask;
1208 Auth(OUString aUserName, OUString aPassword,
decltype(CURLAUTH_ANY)
const & rAuthMask)
1209 : UserName(std::move(aUserName))
1210 , PassWord(std::move(aPassword))
1211 , AuthMask(rAuthMask)
1215 ::std::optional<Auth> oAuth;
1216 ::std::optional<Auth> oAuthProxy;
1217 if (pEnv && !rSession.m_isAuthenticatedProxy && !rSession.m_Proxy.aName.isEmpty())
1220 CurlUri const uri(rSession.m_Proxy.aName);
1226 decltype(CURLAUTH_ANY)
const authSystem(CURLAUTH_NEGOTIATE | CURLAUTH_NTLM | CURLAUTH_NTLM_WB);
1227 if (pRequestedHeaders || (pEnv && !rSession.m_isAuthenticated))
1235 CurlUri const uri(pEnv->m_aRequestURI);
1238 if (pEnv && !rSession.m_isAuthenticated
1239 && (!rURI.GetUser().isEmpty() || !rURI.GetPassword().isEmpty()))
1241 oAuth.emplace(rURI.GetUser(), rURI.GetPassword(), CURLAUTH_ANY);
1243 if (pRequestedHeaders)
1250 pRequestedHeaders->second.uri = rURI.GetURI();
1251 pRequestedHeaders->second.properties.clear();
1254 bool isRetry(
false);
1255 bool isFallbackHTTP10(
false);
1256 int nAuthRequests(0);
1257 int nAuthRequestsProxy(0);
1267 if (oAuth && !rSession.m_isAuthenticated)
1269 OString
const utf8UserName(
OUStringToOString(oAuth->UserName, RTL_TEXTENCODING_UTF8));
1271 = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_USERNAME, utf8UserName.getStr());
1274 SAL_WARN(
"ucb.ucp.webdav.curl",
"CURLOPT_USERNAME failed: " << GetErrorString(rc));
1277 OString
const utf8PassWord(
OUStringToOString(oAuth->PassWord, RTL_TEXTENCODING_UTF8));
1278 rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_PASSWORD, utf8PassWord.getStr());
1281 SAL_WARN(
"ucb.ucp.webdav.curl",
"CURLOPT_PASSWORD failed: " << GetErrorString(rc));
1284 rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_HTTPAUTH, oAuth->AuthMask);
1290 if (oAuthProxy && !rSession.m_isAuthenticatedProxy)
1292 OString
const utf8UserName(
1294 auto rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_PROXYUSERNAME,
1295 utf8UserName.getStr());
1299 "CURLOPT_PROXYUSERNAME failed: " << GetErrorString(rc));
1302 OString
const utf8PassWord(
1304 rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_PROXYPASSWORD,
1305 utf8PassWord.getStr());
1309 "CURLOPT_PROXYPASSWORD failed: " << GetErrorString(rc));
1312 rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_PROXYAUTH, oAuthProxy->AuthMask);
1318 ResponseHeaders headers(rSession.m_pCurl.get());
1320 uno::Reference<io::XSequenceOutputStream>
const xSeqOutStream(
1321 io::SequenceOutputStream::create(rSession.m_xContext));
1322 uno::Reference<io::XOutputStream>
const xTempOutStream(xSeqOutStream);
1323 assert(xTempOutStream.is());
1327 ProcessRequestImpl(rSession, rURI, rMethod, pRequestHeaderList.get(), &xTempOutStream,
1328 pxInStream ? &data :
nullptr, pRequestedHeaders, headers);
1331 (*pxOutStream)->writeBytes(xSeqOutStream->getWrittenBytes());
1332 (*pxOutStream)->closeOutput();
1338 auto const bytes(xSeqOutStream->getWrittenBytes());
1339 auto const len(::std::min<sal_Int32>(
bytes.getLength(), 10000));
1341 "DAVException; (first) " << len <<
" bytes of data received:");
1344 OStringBuffer buf(len);
1345 for (sal_Int32
i = 0;
i < len; ++
i)
1349 static char const hexDigit[16] = {
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
1350 '8',
'9',
'A',
'B',
'C',
'D',
'E',
'F' };
1351 buf.append(OString::Concat(
"\\x")
1353 + OStringChar(hexDigit[
bytes[
i] & 0x0F]));
1357 buf.append(
static_cast<char>(
bytes[
i]));
1360 SAL_INFO(
"ucb.ucp.webdav.curl", buf.makeStringAndClear());
1365 if (rException.
getError() == DAVException::DAV_HTTP_ERROR)
1367 auto const statusCode(rException.
getStatus());
1373 if (g_Init.LockStore.getLockTokenForURI(rURI.GetURI(),
nullptr))
1401 ? rSession.m_isAuthenticated
1402 : rSession.m_isAuthenticatedProxy)
1405 : nAuthRequestsProxy);
1406 if (rnAuthRequests == 10)
1408 SAL_INFO(
"ucb.ucp.webdav.curl",
"aborting authentication after "
1409 << rnAuthRequests <<
" attempts");
1411 else if (pEnv && pEnv->m_xAuthListener)
1415 :
"Proxy-Authenticate"));
1417 ::std::optional<Auth>& roAuth(
1419 OUString userName(roAuth ? roAuth->UserName : OUString());
1420 OUString passWord(roAuth ? roAuth->PassWord : OUString());
1422 auto const rc = curl_easy_getinfo(rSession.m_pCurl.get(),
1424 ? CURLINFO_HTTPAUTH_AVAIL
1425 : CURLINFO_PROXYAUTH_AVAIL,
1427 assert(rc == CURLE_OK);
1434 bool const isSystemCredSupported((authAvail & authSystem) != 0
1435 && rnAuthRequests == 0);
1445 auto const ret = pEnv->m_xAuthListener->authenticate(
1446 oRealm ? *oRealm :
"",
1448 : rSession.m_Proxy.aName,
1449 userName, passWord, isSystemCredSupported);
1464 roAuth.emplace(userName, passWord,
1465 ((userName.isEmpty() && passWord.isEmpty())
1466 ? (authAvail & authSystem)
1475 SAL_INFO(
"ucb.ucp.webdav.curl",
"no auth credentials provided");
1478 rSession.m_URI.GetPort()));
1483 else if (rException.
getError() == DAVException::DAV_UNSUPPORTED)
1489 if (isFallbackHTTP10)
1493 isFallbackHTTP10 =
true;
1495 auto rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_HTTP_VERSION,
1496 CURL_HTTP_VERSION_1_0);
1501 SAL_INFO(
"ucb.ucp.webdav.curl",
"attempting fallback to HTTP/1.0");
1514 rSession.m_isAuthenticated =
true;
1519 rSession.m_isAuthenticatedProxy =
true;
1523auto CurlSession::OPTIONS(OUString
const& rURIReference,
1527 SAL_INFO(
"ucb.ucp.webdav.curl",
"OPTIONS: " << rURIReference);
1531 CurlUri const uri(CurlProcessor::URIReferenceToURI(*
this, rURIReference));
1533 ::std::vector<OUString>
const headerNames{
"allow",
"dav" };
1535 ::std::pair<::std::vector<OUString>
const&,
DAVResource&>
const headers(headerNames,
result);
1537 ::std::vector<CurlOption>
const options{
1538 g_NoBody, { CURLOPT_CUSTOMREQUEST,
"OPTIONS",
"CURLOPT_CUSTOMREQUEST" }
1541 CurlProcessor::ProcessRequest(*
this, uri,
"OPTIONS", options, &rEnv,
nullptr,
nullptr,
nullptr,
1544 for (
auto const& it :
result.properties)
1548 SAL_INFO(
"ucb.ucp.webdav.curl",
"OPTIONS: header: " << it.Name <<
": " <<
value);
1549 if (it.Name.equalsIgnoreAsciiCase(
"allow"))
1551 rOptions.setAllowedMethods(
value);
1553 else if (it.Name.equalsIgnoreAsciiCase(
"dav"))
1560 auto const list(::comphelper::string::convertCommaSeparated(
value));
1561 for (OUString
const&
v : list)
1565 rOptions.setClass1();
1569 rOptions.setClass2();
1573 rOptions.setClass3();
1578 if (rOptions.isClass2() || rOptions.isClass3())
1580 if (g_Init.LockStore.getLockTokenForURI(uri.
GetURI(),
nullptr))
1582 rOptions.setLocked();
1587auto CurlProcessor::PropFind(
1589 ::std::tuple<::std::vector<OUString>
const&, ::std::vector<DAVResource>*
const,
1590 ::std::vector<ucb::Lock>*
const>
const*
const o_pRequestedProperties,
1594 assert((o_pRequestedProperties !=
nullptr) != (o_pResourceInfos !=
nullptr));
1595 assert((o_pRequestedProperties ==
nullptr)
1596 || (::std::get<1>(*o_pRequestedProperties) !=
nullptr)
1597 != (::std::get<2>(*o_pRequestedProperties) !=
nullptr));
1599 ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist, curl_slist_free_all>> pList;
1600 pList.reset(curl_slist_append(pList.release(),
"Content-Type: application/xml"));
1603 throw uno::RuntimeException(
"curl_slist_append failed");
1615 depth =
"Depth: infinity";
1620 pList.reset(curl_slist_append(pList.release(), depth.getStr()));
1623 throw uno::RuntimeException(
"curl_slist_append failed");
1626 uno::Reference<io::XSequenceOutputStream>
const xSeqOutStream(
1627 io::SequenceOutputStream::create(rSession.m_xContext));
1628 uno::Reference<io::XOutputStream>
const xRequestOutStream(xSeqOutStream);
1629 assert(xRequestOutStream.is());
1631 uno::Reference<xml::sax::XWriter>
const xWriter(xml::sax::Writer::create(rSession.m_xContext));
1632 xWriter->setOutputStream(xRequestOutStream);
1633 xWriter->startDocument();
1635 pAttrList->AddAttribute(
"xmlns",
"DAV:");
1636 xWriter->startElement(
"propfind", pAttrList);
1637 if (o_pResourceInfos)
1639 xWriter->startElement(
"propname",
nullptr);
1640 xWriter->endElement(
"propname");
1644 if (::std::get<0>(*o_pRequestedProperties).empty())
1646 xWriter->startElement(
"allprop",
nullptr);
1647 xWriter->endElement(
"allprop");
1651 xWriter->startElement(
"prop",
nullptr);
1652 for (OUString
const& rName : ::std::get<0>(*o_pRequestedProperties))
1655 DAVProperties::createSerfPropName(rName,
name);
1657 pAttrList->AddAttribute(
"xmlns", OUString::createFromAscii(
name.nspace));
1658 xWriter->startElement(OUString::createFromAscii(
name.name), pAttrList);
1659 xWriter->endElement(OUString::createFromAscii(
name.name));
1661 xWriter->endElement(
"prop");
1664 xWriter->endElement(
"propfind");
1665 xWriter->endDocument();
1667 uno::Reference<io::XInputStream>
const xRequestInStream(
1668 io::SequenceInputStream::createStreamFromSequence(rSession.m_xContext,
1669 xSeqOutStream->getWrittenBytes()));
1670 assert(xRequestInStream.is());
1672 curl_off_t
const len(xSeqOutStream->getWrittenBytes().getLength());
1674 ::std::vector<CurlOption>
const options{
1675 { CURLOPT_UPLOAD, 1L,
nullptr },
1676 { CURLOPT_CUSTOMREQUEST,
"PROPFIND",
"CURLOPT_CUSTOMREQUEST" },
1678 { CURLOPT_INFILESIZE_LARGE, len,
nullptr, CurlOption::Type::CurlOffT }
1682 uno::Reference<io::XInputStream>
const xResponseInStream(io::Pipe::create(rSession.m_xContext));
1683 uno::Reference<io::XOutputStream>
const xResponseOutStream(xResponseInStream, uno::UNO_QUERY);
1684 assert(xResponseInStream.is());
1685 assert(xResponseOutStream.is());
1687 CurlProcessor::ProcessRequest(rSession, rURI,
"PROPFIND", options, &rEnv, ::std::move(pList),
1688 &xResponseOutStream, &xRequestInStream,
nullptr);
1690 if (o_pResourceInfos)
1696 if (::std::get<1>(*o_pRequestedProperties) !=
nullptr)
1698 *::std::get<1>(*o_pRequestedProperties)
1700 for (
DAVResource& it : *::std::get<1>(*o_pRequestedProperties))
1703 if (it.uri.startsWith(
"/"))
1707 it.uri = rSession.m_URI.CloneWithRelativeRefPathAbsolute(it.uri).GetURI();
1712 "PROPFIND: exception parsing uri " << it.uri);
1725auto CurlSession::PROPFIND(OUString
const& rURIReference,
Depth const depth,
1726 ::std::vector<OUString>
const& rPropertyNames,
1727 ::std::vector<DAVResource>& o_rResources,
1730 SAL_INFO(
"ucb.ucp.webdav.curl",
"PROPFIND: " << rURIReference <<
" " << depth);
1732 CurlUri const uri(CurlProcessor::URIReferenceToURI(*
this, rURIReference));
1734 ::std::tuple<::std::vector<OUString>
const&, ::std::vector<DAVResource>*
const,
1735 ::std::vector<ucb::Lock>*
const>
const args(rPropertyNames, &o_rResources,
1737 return CurlProcessor::PropFind(*
this, uri, depth, &
args,
nullptr, rEnv);
1740auto CurlSession::PROPFIND(OUString
const& rURIReference,
Depth const depth,
1741 ::std::vector<DAVResourceInfo>& o_rResourceInfos,
1744 SAL_INFO(
"ucb.ucp.webdav.curl",
"PROPFIND: " << rURIReference <<
" " << depth);
1746 CurlUri const uri(CurlProcessor::URIReferenceToURI(*
this, rURIReference));
1748 return CurlProcessor::PropFind(*
this, uri, depth,
nullptr, &o_rResourceInfos, rEnv);
1751auto CurlSession::PROPPATCH(OUString
const& rURIReference,
1752 ::std::vector<ProppatchValue>
const& rValues,
1755 SAL_INFO(
"ucb.ucp.webdav.curl",
"PROPPATCH: " << rURIReference);
1757 CurlUri const uri(CurlProcessor::URIReferenceToURI(*
this, rURIReference));
1760 ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist, curl_slist_free_all>> pList;
1761 pList.reset(curl_slist_append(pList.release(),
"Content-Type: application/xml"));
1764 throw uno::RuntimeException(
"curl_slist_append failed");
1768 uno::Reference<io::XSequenceOutputStream>
const xSeqOutStream(
1769 io::SequenceOutputStream::create(
m_xContext));
1770 uno::Reference<io::XOutputStream>
const xRequestOutStream(xSeqOutStream);
1771 assert(xRequestOutStream.is());
1772 uno::Reference<xml::sax::XWriter>
const xWriter(xml::sax::Writer::create(
m_xContext));
1773 xWriter->setOutputStream(xRequestOutStream);
1774 xWriter->startDocument();
1776 pAttrList->AddAttribute(
"xmlns",
"DAV:");
1777 xWriter->startElement(
"propertyupdate", pAttrList);
1781 OUString
const operation((rPropValue.operation ==
PROPSET) ? OUString(
"set")
1782 : OUString(
"remove"));
1783 xWriter->startElement(operation,
nullptr);
1784 xWriter->startElement(
"prop",
nullptr);
1786 DAVProperties::createSerfPropName(rPropValue.name,
name);
1788 pAttrList->AddAttribute(
"xmlns", OUString::createFromAscii(
name.nspace));
1789 xWriter->startElement(OUString::createFromAscii(
name.name), pAttrList);
1790 if (rPropValue.operation ==
PROPSET)
1792 if (DAVProperties::isUCBDeadProperty(
name))
1794 ::std::optional<::std::pair<OUString, OUString>>
const oProp(
1795 UCBDeadPropertyValue::toXML(rPropValue.value));
1798 xWriter->startElement(
"ucbprop",
nullptr);
1799 xWriter->startElement(
"type",
nullptr);
1800 xWriter->characters(oProp->first);
1801 xWriter->endElement(
"type");
1802 xWriter->startElement(
"value",
nullptr);
1803 xWriter->characters(oProp->second);
1804 xWriter->endElement(
"value");
1805 xWriter->endElement(
"ucbprop");
1811 rPropValue.value >>=
value;
1812 xWriter->characters(
value);
1815 xWriter->endElement(OUString::createFromAscii(
name.name));
1816 xWriter->endElement(
"prop");
1817 xWriter->endElement(operation);
1819 xWriter->endElement(
"propertyupdate");
1820 xWriter->endDocument();
1822 uno::Reference<io::XInputStream>
const xRequestInStream(
1823 io::SequenceInputStream::createStreamFromSequence(
m_xContext,
1824 xSeqOutStream->getWrittenBytes()));
1825 assert(xRequestInStream.is());
1827 curl_off_t
const len(xSeqOutStream->getWrittenBytes().getLength());
1829 ::std::vector<CurlOption>
const options{
1830 { CURLOPT_UPLOAD, 1L,
nullptr },
1831 { CURLOPT_CUSTOMREQUEST,
"PROPPATCH",
"CURLOPT_CUSTOMREQUEST" },
1833 { CURLOPT_INFILESIZE_LARGE, len,
nullptr, CurlOption::Type::CurlOffT }
1836 CurlProcessor::ProcessRequest(*
this, uri,
"PROPPATCH", options, &rEnv, ::std::move(pList),
1837 nullptr, &xRequestInStream,
nullptr);
1840auto CurlSession::HEAD(OUString
const& rURIReference, ::std::vector<OUString>
const& rHeaderNames,
1843 SAL_INFO(
"ucb.ucp.webdav.curl",
"HEAD: " << rURIReference);
1845 CurlUri const uri(CurlProcessor::URIReferenceToURI(*
this, rURIReference));
1847 ::std::vector<CurlOption>
const options{ g_NoBody };
1849 ::std::pair<::std::vector<OUString>
const&,
DAVResource&>
const headers(rHeaderNames,
1852 CurlProcessor::ProcessRequest(*
this, uri,
"HEAD", options, &rEnv,
nullptr,
nullptr,
nullptr,
1857 -> uno::Reference<io::XInputStream>
1859 SAL_INFO(
"ucb.ucp.webdav.curl",
"GET: " << rURIReference);
1861 CurlUri const uri(CurlProcessor::URIReferenceToURI(*
this, rURIReference));
1868 uno::Reference<io::XSequenceOutputStream>
const xSeqOutStream(
1869 io::SequenceOutputStream::create(
m_xContext));
1870 uno::Reference<io::XOutputStream>
const xResponseOutStream(xSeqOutStream);
1871 assert(xResponseOutStream.is());
1873 ::std::vector<CurlOption>
const options{ { CURLOPT_HTTPGET, 1L,
nullptr } };
1875 CurlProcessor::ProcessRequest(*
this, uri,
"GET", options, &rEnv,
nullptr, &xResponseOutStream,
1878 uno::Reference<io::XInputStream>
const xResponseInStream(
1879 io::SequenceInputStream::createStreamFromSequence(
m_xContext,
1880 xSeqOutStream->getWrittenBytes()));
1881 assert(xResponseInStream.is());
1883 return xResponseInStream;
1886auto CurlSession::GET(OUString
const& rURIReference, uno::Reference<io::XOutputStream>& rxOutStream,
1889 SAL_INFO(
"ucb.ucp.webdav.curl",
"GET: " << rURIReference);
1891 CurlUri const uri(CurlProcessor::URIReferenceToURI(*
this, rURIReference));
1893 ::std::vector<CurlOption>
const options{ { CURLOPT_HTTPGET, 1L,
nullptr } };
1895 CurlProcessor::ProcessRequest(*
this, uri,
"GET", options, &rEnv,
nullptr, &rxOutStream,
nullptr,
1899auto CurlSession::GET(OUString
const& rURIReference, ::std::vector<OUString>
const& rHeaderNames,
1901 -> uno::Reference<io::XInputStream>
1903 SAL_INFO(
"ucb.ucp.webdav.curl",
"GET: " << rURIReference);
1905 CurlUri const uri(CurlProcessor::URIReferenceToURI(*
this, rURIReference));
1907 ::std::vector<CurlOption>
const options{ { CURLOPT_HTTPGET, 1L,
nullptr } };
1909 uno::Reference<io::XSequenceOutputStream>
const xSeqOutStream(
1910 io::SequenceOutputStream::create(
m_xContext));
1911 uno::Reference<io::XOutputStream>
const xResponseOutStream(xSeqOutStream);
1912 assert(xResponseOutStream.is());
1914 ::std::pair<::std::vector<OUString>
const&,
DAVResource&>
const headers(rHeaderNames,
1917 CurlProcessor::ProcessRequest(*
this, uri,
"GET", options, &rEnv,
nullptr, &xResponseOutStream,
1920 uno::Reference<io::XInputStream>
const xResponseInStream(
1921 io::SequenceInputStream::createStreamFromSequence(
m_xContext,
1922 xSeqOutStream->getWrittenBytes()));
1923 assert(xResponseInStream.is());
1925 return xResponseInStream;
1928auto CurlSession::GET(OUString
const& rURIReference, uno::Reference<io::XOutputStream>& rxOutStream,
1929 ::std::vector<OUString>
const& rHeaderNames,
DAVResource& io_rResource,
1932 SAL_INFO(
"ucb.ucp.webdav.curl",
"GET: " << rURIReference);
1934 CurlUri const uri(CurlProcessor::URIReferenceToURI(*
this, rURIReference));
1936 ::std::vector<CurlOption>
const options{ { CURLOPT_HTTPGET, 1L,
nullptr } };
1938 ::std::pair<::std::vector<OUString>
const&, DAVResource&>
const headers(rHeaderNames,
1941 CurlProcessor::ProcessRequest(*
this, uri,
"GET", options, &rEnv,
nullptr, &rxOutStream,
nullptr,
1945auto CurlSession::PUT(OUString
const& rURIReference,
1946 uno::Reference<io::XInputStream>
const& rxInStream,
1949 SAL_INFO(
"ucb.ucp.webdav.curl",
"PUT: " << rURIReference);
1951 CurlUri const uri(CurlProcessor::URIReferenceToURI(*
this, rURIReference));
1954 uno::Reference<io::XSeekable>
const xSeekable(rxInStream, uno::UNO_QUERY);
1955 if (!xSeekable.is())
1957 throw uno::RuntimeException(
"TODO: not seekable");
1959 curl_off_t
const len(xSeekable->getLength() - xSeekable->getPosition());
1961 ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist, curl_slist_free_all>> pList;
1962 OUString
const*
const pToken(g_Init.LockStore.getLockTokenForURI(uri.
GetURI(),
nullptr));
1965 OString
const utf8If(
"If: "
1974 pList.reset(curl_slist_append(pList.release(), utf8If.getStr()));
1977 throw uno::RuntimeException(
"curl_slist_append failed");
1984 ::std::vector<CurlOption>
const options{
1985 { CURLOPT_UPLOAD, 1L,
nullptr },
1986 { CURLOPT_INFILESIZE_LARGE, len,
nullptr, CurlOption::Type::CurlOffT }
1989 CurlProcessor::ProcessRequest(*
this, uri,
"PUT", options, &rEnv, ::std::move(pList),
nullptr,
1990 &rxInStream,
nullptr);
1993auto CurlSession::POST(OUString
const& rURIReference, OUString
const& rContentType,
1994 OUString
const& rReferer, uno::Reference<io::XInputStream>
const& rxInStream,
1997 SAL_INFO(
"ucb.ucp.webdav.curl",
"POST: " << rURIReference);
1999 CurlUri const uri(CurlProcessor::URIReferenceToURI(*
this, rURIReference));
2002 ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist, curl_slist_free_all>> pList(
2003 curl_slist_append(
nullptr,
"Transfer-Encoding: chunked"));
2006 throw uno::RuntimeException(
"curl_slist_append failed");
2008 OString
const utf8ContentType(
"Content-Type: "
2010 pList.reset(curl_slist_append(pList.release(), utf8ContentType.getStr()));
2013 throw uno::RuntimeException(
"curl_slist_append failed");
2015 OString
const utf8Referer(
"Referer: " +
OUStringToOString(rReferer, RTL_TEXTENCODING_ASCII_US));
2016 pList.reset(curl_slist_append(pList.release(), utf8Referer.getStr()));
2019 throw uno::RuntimeException(
"curl_slist_append failed");
2022 ::std::vector<CurlOption>
const options{ { CURLOPT_POST, 1L,
nullptr } };
2024 uno::Reference<io::XSequenceOutputStream>
const xSeqOutStream(
2025 io::SequenceOutputStream::create(m_xContext));
2026 uno::Reference<io::XOutputStream>
const xResponseOutStream(xSeqOutStream);
2027 assert(xResponseOutStream.is());
2029 CurlProcessor::ProcessRequest(*
this, uri,
"POST", options, &rEnv, ::std::move(pList),
2030 &xResponseOutStream, &rxInStream,
nullptr);
2032 uno::Reference<io::XInputStream>
const xResponseInStream(
2033 io::SequenceInputStream::createStreamFromSequence(m_xContext,
2034 xSeqOutStream->getWrittenBytes()));
2035 assert(xResponseInStream.is());
2037 return xResponseInStream;
2040auto CurlSession::POST(OUString
const& rURIReference, OUString
const& rContentType,
2041 OUString
const& rReferer, uno::Reference<io::XInputStream>
const& rxInStream,
2042 uno::Reference<io::XOutputStream>& rxOutStream,
2043 DAVRequestEnvironment
const& rEnv) ->
void
2045 SAL_INFO(
"ucb.ucp.webdav.curl",
"POST: " << rURIReference);
2047 CurlUri
const uri(CurlProcessor::URIReferenceToURI(*
this, rURIReference));
2050 ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist, curl_slist_free_all>> pList(
2051 curl_slist_append(
nullptr,
"Transfer-Encoding: chunked"));
2054 throw uno::RuntimeException(
"curl_slist_append failed");
2056 OString
const utf8ContentType(
"Content-Type: "
2058 pList.reset(curl_slist_append(pList.release(), utf8ContentType.getStr()));
2061 throw uno::RuntimeException(
"curl_slist_append failed");
2063 OString
const utf8Referer(
"Referer: " +
OUStringToOString(rReferer, RTL_TEXTENCODING_ASCII_US));
2064 pList.reset(curl_slist_append(pList.release(), utf8Referer.getStr()));
2067 throw uno::RuntimeException(
"curl_slist_append failed");
2070 ::std::vector<CurlOption>
const options{ { CURLOPT_POST, 1L,
nullptr } };
2072 CurlProcessor::ProcessRequest(*
this, uri,
"POST", options, &rEnv, ::std::move(pList),
2073 &rxOutStream, &rxInStream,
nullptr);
2078 SAL_INFO(
"ucb.ucp.webdav.curl",
"MKCOL: " << rURIReference);
2080 CurlUri const uri(CurlProcessor::URIReferenceToURI(*
this, rURIReference));
2082 ::std::vector<CurlOption>
const options{
2083 g_NoBody, { CURLOPT_CUSTOMREQUEST,
"MKCOL",
"CURLOPT_CUSTOMREQUEST" }
2086 CurlProcessor::ProcessRequest(*
this, uri,
"MKCOL", options, &rEnv,
nullptr,
nullptr,
nullptr,
2090auto CurlProcessor::MoveOrCopy(
CurlSession& rSession, std::u16string_view rSourceURIReference,
2091 ::std::u16string_view
const rDestinationURI,
2093 char const*
const pMethod) ->
void
2095 CurlUri const uriSource(CurlProcessor::URIReferenceToURI(rSession, rSourceURIReference));
2097 OString
const utf8Destination(
"Destination: "
2099 ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist, curl_slist_free_all>> pList(
2100 curl_slist_append(
nullptr, utf8Destination.getStr()));
2103 throw uno::RuntimeException(
"curl_slist_append failed");
2105 OString
const utf8Overwrite(OString::Concat(
"Overwrite: ") + (isOverwrite ?
"T" :
"F"));
2106 pList.reset(curl_slist_append(pList.release(), utf8Overwrite.getStr()));
2109 throw uno::RuntimeException(
"curl_slist_append failed");
2112 ::std::vector<CurlOption>
const options{
2113 g_NoBody, { CURLOPT_CUSTOMREQUEST, pMethod,
"CURLOPT_CUSTOMREQUEST" }
2116 CurlProcessor::ProcessRequest(rSession, uriSource, OUString::createFromAscii(pMethod), options,
2117 &rEnv, ::std::move(pList),
nullptr,
nullptr,
nullptr);
2120auto CurlSession::COPY(OUString
const& rSourceURIReference, OUString
const& rDestinationURI,
2123 SAL_INFO(
"ucb.ucp.webdav.curl",
"COPY: " << rSourceURIReference);
2125 return CurlProcessor::MoveOrCopy(*
this, rSourceURIReference, rDestinationURI, rEnv, isOverwrite,
2129auto CurlSession::MOVE(OUString
const& rSourceURIReference, OUString
const& rDestinationURI,
2132 SAL_INFO(
"ucb.ucp.webdav.curl",
"MOVE: " << rSourceURIReference);
2134 return CurlProcessor::MoveOrCopy(*
this, rSourceURIReference, rDestinationURI, rEnv, isOverwrite,
2140 SAL_INFO(
"ucb.ucp.webdav.curl",
"DESTROY: " << rURIReference);
2142 CurlUri const uri(CurlProcessor::URIReferenceToURI(*
this, rURIReference));
2144 ::std::vector<CurlOption>
const options{
2145 g_NoBody, { CURLOPT_CUSTOMREQUEST,
"DELETE",
"CURLOPT_CUSTOMREQUEST" }
2148 CurlProcessor::ProcessRequest(*
this, uri,
"DESTROY", options, &rEnv,
nullptr,
nullptr,
nullptr,
2152auto CurlProcessor::Lock(
2156 uno::Reference<io::XInputStream>
const*
const pxRequestInStream)
2157 -> ::std::vector<::std::pair<ucb::Lock, sal_Int32>>
2160 if (pxRequestInStream)
2162 uno::Reference<io::XSeekable>
const xSeekable(*pxRequestInStream, uno::UNO_QUERY);
2163 assert(xSeekable.is());
2164 len = xSeekable->getLength();
2167 ::std::vector<CurlOption>
const options{
2168 { CURLOPT_UPLOAD, 1L,
nullptr },
2169 { CURLOPT_CUSTOMREQUEST,
"LOCK",
"CURLOPT_CUSTOMREQUEST" },
2171 { CURLOPT_INFILESIZE_LARGE, len,
nullptr, CurlOption::Type::CurlOffT }
2175 uno::Reference<io::XInputStream>
const xResponseInStream(io::Pipe::create(rSession.m_xContext));
2176 uno::Reference<io::XOutputStream>
const xResponseOutStream(xResponseInStream, uno::UNO_QUERY);
2177 assert(xResponseInStream.is());
2178 assert(xResponseOutStream.is());
2180 TimeValue startTime;
2181 osl_getSystemTime(&startTime);
2183 CurlProcessor::ProcessRequest(rSession, rURI,
"LOCK", options, pEnv,
2184 ::std::move(pRequestHeaderList), &xResponseOutStream,
2185 pxRequestInStream,
nullptr);
2188 SAL_WARN_IF(acquiredLocks.empty(),
"ucb.ucp.webdav.curl",
2189 "could not get LOCK for " << rURI.GetURI());
2192 osl_getSystemTime(&endTime);
2193 auto const elapsedSeconds(endTime.Seconds - startTime.Seconds);
2196 ::std::vector<::std::pair<ucb::Lock, sal_Int32>> ret;
2197 ret.reserve(acquiredLocks.size());
2198 for (
auto const& rLock : acquiredLocks)
2200 sal_Int32 lockExpirationTimeSeconds;
2201 if (rLock.Timeout == -1)
2203 lockExpirationTimeSeconds = -1;
2205 else if (rLock.Timeout <= elapsedSeconds)
2208 "LOCK timeout already expired when receiving LOCK response for "
2210 lockExpirationTimeSeconds = 0;
2214 lockExpirationTimeSeconds = startTime.Seconds + rLock.Timeout;
2216 ret.emplace_back(rLock, lockExpirationTimeSeconds);
2222auto CurlSession::LOCK(OUString
const& rURIReference, ucb::Lock & rLock,
2225 SAL_INFO(
"ucb.ucp.webdav.curl",
"LOCK: " << rURIReference);
2227 CurlUri const uri(CurlProcessor::URIReferenceToURI(*
this, rURIReference));
2229 if (g_Init.LockStore.getLockTokenForURI(uri.
GetURI(), &rLock))
2239 uno::Reference<io::XSequenceOutputStream>
const xSeqOutStream(
2240 io::SequenceOutputStream::create(
m_xContext));
2241 uno::Reference<io::XOutputStream>
const xRequestOutStream(xSeqOutStream);
2242 assert(xRequestOutStream.is());
2243 uno::Reference<xml::sax::XWriter>
const xWriter(xml::sax::Writer::create(
m_xContext));
2244 xWriter->setOutputStream(xRequestOutStream);
2245 xWriter->startDocument();
2247 pAttrList->AddAttribute(
"xmlns",
"DAV:");
2248 xWriter->startElement(
"lockinfo", pAttrList);
2249 xWriter->startElement(
"lockscope",
nullptr);
2250 switch (rLock.Scope)
2252 case ucb::LockScope_EXCLUSIVE:
2253 xWriter->startElement(
"exclusive",
nullptr);
2254 xWriter->endElement(
"exclusive");
2256 case ucb::LockScope_SHARED:
2257 xWriter->startElement(
"shared",
nullptr);
2258 xWriter->endElement(
"shared");
2263 xWriter->endElement(
"lockscope");
2264 xWriter->startElement(
"locktype",
nullptr);
2265 xWriter->startElement(
"write",
nullptr);
2266 xWriter->endElement(
"write");
2267 xWriter->endElement(
"locktype");
2269 if ((rLock.Owner >>= owner) && !owner.isEmpty())
2271 xWriter->startElement(
"owner",
nullptr);
2272 xWriter->characters(owner);
2273 xWriter->endElement(
"owner");
2275 xWriter->endElement(
"lockinfo");
2276 xWriter->endDocument();
2278 uno::Reference<io::XInputStream>
const xRequestInStream(
2279 io::SequenceInputStream::createStreamFromSequence(
m_xContext,
2280 xSeqOutStream->getWrittenBytes()));
2281 assert(xRequestInStream.is());
2283 ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist, curl_slist_free_all>> pList;
2284 pList.reset(curl_slist_append(pList.release(),
"Content-Type: application/xml"));
2287 throw uno::RuntimeException(
"curl_slist_append failed");
2290 switch (rLock.Depth)
2292 case ucb::LockDepth_ZERO:
2295 case ucb::LockDepth_ONE:
2298 case ucb::LockDepth_INFINITY:
2299 depth =
"Depth: infinity";
2304 pList.reset(curl_slist_append(pList.release(), depth.getStr()));
2307 throw uno::RuntimeException(
"curl_slist_append failed");
2310 switch (rLock.Timeout)
2313 timeout =
"Timeout: Infinite";
2316 timeout =
"Timeout: Second-180";
2319 timeout =
"Timeout: Second-" + OString::number(rLock.Timeout);
2320 assert(0 < rLock.Timeout);
2323 pList.reset(curl_slist_append(pList.release(), timeout.getStr()));
2326 throw uno::RuntimeException(
"curl_slist_append failed");
2329 auto const acquiredLocks
2330 = CurlProcessor::Lock(*
this, uri, &rEnv, ::std::move(pList), &xRequestInStream);
2332 for (
auto const& rAcquiredLock : acquiredLocks)
2334 g_Init.LockStore.addLock(uri.
GetURI(), rAcquiredLock.first,
2335 rAcquiredLock.first.LockTokens[0],
this, rAcquiredLock.second);
2336 SAL_INFO(
"ucb.ucp.webdav.curl",
"created LOCK for " << rURIReference);
2343 OUString
const*
const pToken(g_Init.LockStore.getLockTokenForURI(rURI.GetURI(),
nullptr));
2346 SAL_WARN(
"ucb.ucp.webdav.curl",
"attempt to unlock but not locked");
2349 OString
const utf8LockToken(
"Lock-Token: <"
2351 ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist, curl_slist_free_all>> pList(
2352 curl_slist_append(
nullptr, utf8LockToken.getStr()));
2355 throw uno::RuntimeException(
"curl_slist_append failed");
2358 ::std::vector<CurlOption>
const options{ { CURLOPT_CUSTOMREQUEST,
"UNLOCK",
2359 "CURLOPT_CUSTOMREQUEST" } };
2361 CurlProcessor::ProcessRequest(rSession, rURI,
"UNLOCK", options, pEnv, ::std::move(pList),
2362 nullptr,
nullptr,
nullptr);
2367 SAL_INFO(
"ucb.ucp.webdav.curl",
"UNLOCK: " << rURIReference);
2371 CurlUri const uri(CurlProcessor::URIReferenceToURI(*
this, rURIReference));
2373 CurlProcessor::Unlock(*
this, uri, &rEnv);
2375 g_Init.LockStore.removeLock(uri.
GetURI());
2378auto CurlSession::NonInteractive_LOCK(OUString
const& rURI, ::std::u16string_view
const rLockToken,
2379 sal_Int32& o_rLastChanceToSendRefreshRequest,
2380 bool& o_rIsAuthFailed) ->
bool
2382 SAL_INFO(
"ucb.ucp.webdav.curl",
"NonInteractive_LOCK: " << rURI);
2389 ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist, curl_slist_free_all>> pList(
2390 curl_slist_append(
nullptr,
"Timeout: Second-180"));
2392 assert(!rLockToken.empty());
2393 OString
const utf8If(
"If: (<" +
OUStringToOString(rLockToken, RTL_TEXTENCODING_ASCII_US)
2395 pList.reset(curl_slist_append(pList.release(), utf8If.getStr()));
2398 throw uno::RuntimeException(
"curl_slist_append failed");
2401 auto const acquiredLocks
2402 = CurlProcessor::Lock(*
this, uri,
nullptr, ::std::move(pList),
nullptr);
2404 SAL_WARN_IF(1 < acquiredLocks.size(),
"ucb.ucp.webdav.curl",
2405 "multiple locks acquired on refresh for " << rURI);
2406 if (!acquiredLocks.empty())
2408 o_rLastChanceToSendRefreshRequest = acquiredLocks.begin()->second;
2410 SAL_INFO(
"ucb.ucp.webdav.curl",
"NonInteractive_LOCK succeeded on " << rURI);
2415 SAL_INFO(
"ucb.ucp.webdav.curl",
"NonInteractive_LOCK failed on " << rURI);
2418 case DAVException::DAV_HTTP_AUTH:
2419 case DAVException::DAV_HTTP_NOAUTH:
2420 o_rIsAuthFailed =
true;
2429 SAL_INFO(
"ucb.ucp.webdav.curl",
"NonInteractive_LOCK failed on " << rURI);
2434auto CurlSession::NonInteractive_UNLOCK(OUString
const& rURI) ->
void
2436 SAL_INFO(
"ucb.ucp.webdav.curl",
"NonInteractive_UNLOCK: " << rURI);
2444 CurlProcessor::Unlock(*
this, uri,
nullptr);
2447 SAL_INFO(
"ucb.ucp.webdav.curl",
"NonInteractive_UNLOCK succeeded on " << rURI);
2451 SAL_INFO(
"ucb.ucp.webdav.curl",
"NonInteractive_UNLOCK failed on " << rURI);
Reference< XComponentContext > m_xContext
implementation of libcurl HTTP/DAV back-end
int m_nReadTimeout
read timeout in milliseconds (connection timeout is stored in m_pCurl)
virtual auto abort() -> void override
::std::unique_ptr< CURLM, deleter_from_fn< CURLM, curl_multi_cleanup > > m_pCurlMulti
libcurl multi handle
virtual ~CurlSession() override
css::uno::Sequence< css::beans::NamedValue > const m_Flags
flags may be passed to constructor, e.g. "KeepAlive"
char m_ErrorBuffer[CURL_ERROR_SIZE]
buffer for libcurl detailed error messages
virtual auto UsesProxy() -> bool override
CurlSession(css::uno::Reference< css::uno::XComponentContext > xContext, ::rtl::Reference< DAVSessionFactory > const &rpFactory, OUString const &rURI, css::uno::Sequence< css::beans::NamedValue > const &rFlags, ::ucbhelper::InternetProxyDecider const &rProxyDecider)
::std::unique_ptr< CURL, deleter_from_fn< CURL, curl_easy_cleanup > > m_pCurl
libcurl easy handle
virtual auto CanUse(OUString const &rURI, css::uno::Sequence< css::beans::NamedValue > const &rFlags) -> bool override
::ucbhelper::InternetProxyServer const m_Proxy
proxy is used if aName is non-empty
sal_uInt16 GetPort() const
OUString const & GetHost() const
OUString const & GetPassword() const
OUString const & GetUser() const
OUString const & GetURI() const
OUString const & GetScheme() const
const ExceptionCode & getError() const
sal_uInt16 getStatus() const
#define SAL_WARN_IF(condition, area, stream)
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
#define SAL_N_ELEMENTS(arr)
static auto ExtractRequestedHeaders(ResponseHeaders const &rHeaders, ::std::pair<::std::vector< OUString > const &, DAVResource & > const *const pRequestedHeaders) -> void
const sal_uInt16 SC_TEMPORARY_REDIRECT
const sal_uInt16 SC_SEE_OTHER
const sal_uInt16 SC_LOCKED
static size_t write_callback(char *const ptr, size_t const size, size_t const nmemb, void *const userdata)
static auto TryRemoveExpiredLockToken(CurlSession &rSession, CurlUri const &rURI, DAVRequestEnvironment const *const pEnv) -> bool
const sal_uInt16 SC_MOVED_TEMPORARILY
std::vector< ucb::Lock > parseWebDAVLockResponse(const uno::Reference< io::XInputStream > &xInputStream)
std::vector< DAVResourceInfo > parseWebDAVPropNameResponse(const uno::Reference< io::XInputStream > &xInputStream)
static size_t read_callback(char *const buffer, size_t const size, size_t const nitems, void *const userdata)
const sal_uInt16 SC_PRECONDITION_FAILED
static auto ProcessHeaders(::std::vector< OString > const &rHeaders) -> ::std::map< OUString, OUString >
const sal_uInt16 SC_PROXY_AUTHENTICATION_REQUIRED
const sal_uInt16 SC_MOVED_PERMANENTLY
std::vector< DAVResource > parseWebDAVPropFindResponse(const uno::Reference< io::XInputStream > &xInputStream)
OUString ConnectionEndPointString(std::u16string_view rHostName, sal_uInt16 const nPort)
const sal_uInt16 SC_UNAUTHORIZED
const sal_uInt16 SC_BAD_REQUEST
static int debug_callback(CURL *handle, curl_infotype type, char *data, size_t size, void *)
static size_t header_callback(char *const buffer, size_t const size, size_t const nitems, void *const userdata)
static auto ExtractRealm(ResponseHeaders const &rHeaders, char const *const pAuthHeaderName) -> ::std::optional< OUString >
const sal_uInt16 SC_REQUEST_TIMEOUT
std::enable_if< std::is_signed< T >::value, bool >::type checked_add(T a, T b, T &result)
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
constexpr bool starts_with(std::basic_string_view< charT, traits > sv, std::basic_string_view< charT, traits > x) noexcept
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
std::vector< sal_uInt8 > bytes
this is just a bunch of static member functions called from CurlSession
static auto URIReferenceToURI(CurlSession &rSession, std::u16string_view rURIReference) -> CurlUri
static auto Lock(CurlSession &rSession, CurlUri const &rURI, DAVRequestEnvironment const *pEnv, ::std::unique_ptr< curl_slist, deleter_from_fn< curl_slist, curl_slist_free_all > > pRequestHeaderList, uno::Reference< io::XInputStream > const *pxInStream) -> ::std::vector<::std::pair< ucb::Lock, sal_Int32 > >
static auto ProcessRequestImpl(CurlSession &rSession, CurlUri const &rURI, OUString const &rMethod, curl_slist *pRequestHeaderList, uno::Reference< io::XOutputStream > const *pxOutStream, uno::Sequence< sal_Int8 > const *pInData, ::std::pair<::std::vector< OUString > const &, DAVResource & > const *pRequestedHeaders, ResponseHeaders &rHeaders) -> void
main function to initiate libcurl requests
static auto Unlock(CurlSession &rSession, CurlUri const &rURI, DAVRequestEnvironment const *pEnv) -> void
static auto PropFind(CurlSession &rSession, CurlUri const &rURI, Depth depth, ::std::tuple<::std::vector< OUString > const &, ::std::vector< DAVResource > *const, ::std::vector< ucb::Lock > *const > const *o_pRequestedProperties, ::std::vector< DAVResourceInfo > *const o_pResourceInfos, DAVRequestEnvironment const &rEnv) -> void
static auto MoveOrCopy(CurlSession &rSession, std::u16string_view rSourceURIReference, ::std::u16string_view rDestinationURI, DAVRequestEnvironment const &rEnv, bool isOverwrite, char const *pMethod) -> void
static auto ProcessRequest(CurlSession &rSession, CurlUri const &rURI, OUString const &rMethod, ::std::vector< CurlOption > const &rOptions, DAVRequestEnvironment const *pEnv, ::std::unique_ptr< curl_slist, deleter_from_fn< curl_slist, curl_slist_free_all > > pRequestHeaderList, uno::Reference< io::XOutputStream > const *pxOutStream, uno::Reference< io::XInputStream > const *pxInStream, ::std::pair<::std::vector< OUString > const &, DAVResource & > const *pRequestedHeaders) -> void
static constexpr OUStringLiteral LOCKDISCOVERY