24 #include <officecfg/Inet.hxx>
26 #include <com/sun/star/beans/NamedValue.hpp>
27 #include <com/sun/star/io/Pipe.hpp>
28 #include <com/sun/star/io/SequenceInputStream.hpp>
29 #include <com/sun/star/io/SequenceOutputStream.hpp>
30 #include <com/sun/star/xml/sax/Writer.hpp>
34 #include <rtl/uri.hxx>
35 #include <rtl/strbuf.hxx>
36 #include <rtl/ustrbuf.hxx>
37 #include <config_version.h>
56 if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK)
58 assert(!
"curl_global_init failed");
65 struct ResponseHeaders
67 ::std::vector<::std::pair<::std::vector<OString>, ::std::optional<long>>> HeaderFields;
69 ResponseHeaders(CURL*
const i_pCurl)
75 auto GetResponseCode(ResponseHeaders
const& rHeaders) -> ::std::optional<long>
77 return (rHeaders.HeaderFields.empty()) ? ::std::optional<long>{}
78 : rHeaders.HeaderFields.back().second;
83 uno::Reference<io::XOutputStream> xOutStream;
84 ResponseHeaders
const& rHeaders;
85 DownloadTarget(uno::Reference<io::XOutputStream>
const& i_xOutStream,
86 ResponseHeaders
const& i_rHeaders)
87 : xOutStream(i_xOutStream)
88 , rHeaders(i_rHeaders)
95 uno::Sequence<sal_Int8>
const& rInData;
97 UploadSource(uno::Sequence<sal_Int8>
const& i_rInData)
104 auto GetErrorString(CURLcode
const rc,
char const*
const pErrorBuffer =
nullptr) -> OString
106 char const*
const pMessage(
107 (pErrorBuffer && pErrorBuffer[0] !=
'\0') ? pErrorBuffer : curl_easy_strerror(rc));
108 return OString::Concat(
"(") + OString::number(sal_Int32(rc)) +
") " + pMessage;
111 auto GetErrorStringMulti(CURLMcode
const mc) -> OString
113 return OString::Concat(
"(") + OString::number(sal_Int32(mc)) +
") " + curl_multi_strerror(mc);
128 void const*
const pValue;
132 char const*
const pExceptionString;
134 CurlOption(CURLoption
const i_Option,
void const*
const i_Value,
135 char const*
const i_pExceptionString)
139 , pExceptionString(i_pExceptionString)
144 CurlOption(CURLoption
const i_Option, curl_off_t
const i_Value,
145 char const*
const i_pExceptionString,
Type const type = Type::Long)
148 , pExceptionString(i_pExceptionString)
150 static_assert(
sizeof(
long) <=
sizeof(curl_off_t));
167 const CurlOption g_NoBody{ CURLOPT_NOBODY,
168 sal_detail_log_report(SAL_DETAIL_LOG_LEVEL_INFO,
"ucb.ucp.webdav.curl")
169 == SAL_DETAIL_LOG_ACTION_IGNORE
180 ::std::unique_lock<::std::mutex> m_Lock;
181 ::std::vector<CurlOption>
const m_Options;
186 explicit Guard(::std::mutex& rMutex, ::std::vector<CurlOption>
const& rOptions,
188 : m_Lock(rMutex, ::
std::defer_lock)
189 , m_Options(rOptions)
197 if (m_Lock.owns_lock())
205 assert(!m_Lock.owns_lock());
207 for (
auto const& it : m_Options)
209 CURLcode rc(CURL_LAST);
210 if (it.Tag == CurlOption::Type::Pointer)
212 rc = curl_easy_setopt(m_pCurl, it.Option, it.pValue);
214 else if (it.Tag == CurlOption::Type::Long)
216 rc = curl_easy_setopt(m_pCurl, it.Option, it.lValue);
218 else if (it.Tag == CurlOption::Type::CurlOffT)
220 rc = curl_easy_setopt(m_pCurl, it.Option, it.cValue);
226 if (it.pExceptionString !=
nullptr)
231 "set " << it.pExceptionString <<
" failed: " << GetErrorString(rc));
232 throw ::http_dav_ucp::DAVException(
240 assert(rc == CURLE_OK);
246 assert(m_Lock.owns_lock());
247 for (
auto const& it : m_Options)
249 CURLcode rc(CURL_LAST);
250 if (it.Tag == CurlOption::Type::Pointer)
252 rc = curl_easy_setopt(m_pCurl, it.Option,
nullptr);
254 else if (it.Tag == CurlOption::Type::Long)
256 rc = curl_easy_setopt(m_pCurl, it.Option, 0L);
258 else if (it.Tag == CurlOption::Type::CurlOffT)
260 rc = curl_easy_setopt(m_pCurl, it.Option, curl_off_t(-1));
266 assert(rc == CURLE_OK);
279 static int debug_callback(CURL* handle, curl_infotype type,
char* data,
size_t size,
282 char const* pType(
nullptr);
286 SAL_INFO(
"ucb.ucp.webdav.curl",
"debug log: " << handle <<
": " << data);
288 case CURLINFO_HEADER_IN:
290 "CURLINFO_HEADER_IN: " << handle <<
": " << OString(data, size));
292 case CURLINFO_HEADER_OUT:
295 OString tmp(data, size);
296 sal_Int32
const start(tmp.indexOf(
"Authorization: "));
299 sal_Int32
const end(tmp.indexOf(
"\r\n",
start));
304 OStringConcatenation(OString::number(
end -
start - len) +
" bytes redacted"));
306 SAL_INFO(
"ucb.ucp.webdav.curl",
"CURLINFO_HEADER_OUT: " << handle <<
": " << tmp);
309 case CURLINFO_DATA_IN:
310 pType =
"CURLINFO_DATA_IN";
312 case CURLINFO_DATA_OUT:
313 pType =
"CURLINFO_DATA_OUT";
315 case CURLINFO_SSL_DATA_IN:
316 pType =
"CURLINFO_SSL_DATA_IN";
318 case CURLINFO_SSL_DATA_OUT:
319 pType =
"CURLINFO_SSL_DATA_OUT";
322 SAL_WARN(
"ucb.ucp.webdav.curl",
"unexpected debug log type");
325 SAL_INFO(
"ucb.ucp.webdav.curl",
"debug log: " << handle <<
": " << pType <<
" " << size);
329 static size_t write_callback(
char*
const ptr,
size_t const size,
size_t const nmemb,
330 void*
const userdata)
332 auto*
const pTarget(static_cast<DownloadTarget*>(userdata));
339 assert(pTarget->xOutStream.is());
340 auto const oResponseCode(GetResponseCode(pTarget->rHeaders));
350 uno::Sequence<sal_Int8>
const data(reinterpret_cast<sal_Int8*>(ptr), nmemb);
351 pTarget->xOutStream->writeBytes(data);
355 SAL_WARN(
"ucb.ucp.webdav.curl",
"exception in write_callback");
363 static size_t read_callback(
char*
const buffer,
size_t const size,
size_t const nitems,
364 void*
const userdata)
366 auto*
const pSource(static_cast<UploadSource*>(userdata));
368 size_t const nBytes(size * nitems);
373 nRet = ::std::min<size_t>(pSource->rInData.getLength() - pSource->nPosition, nBytes);
374 ::std::memcpy(buffer, pSource->rInData.getConstArray() + pSource->nPosition, nRet);
375 pSource->nPosition += nRet;
379 SAL_WARN(
"ucb.ucp.webdav.curl",
"exception in read_callback");
380 return CURL_READFUNC_ABORT;
385 static size_t header_callback(
char*
const buffer,
size_t const size,
size_t const nitems,
386 void*
const userdata)
388 auto*
const pHeaders(static_cast<ResponseHeaders*>(userdata));
403 if (pHeaders->HeaderFields.empty())
405 SAL_WARN(
"ucb.ucp.webdav.curl",
"header_callback: empty header?");
411 auto rc = curl_easy_getinfo(pHeaders->pCurl, CURLINFO_RESPONSE_CODE, &statusCode);
412 assert(rc == CURLE_OK);
415 pHeaders->HeaderFields.back().second.emplace(statusCode);
417 else if (buffer[0] ==
' ' || buffer[0] ==
'\t')
423 }
while (i ==
' ' || i ==
'\t');
424 if (pHeaders->HeaderFields.empty() || pHeaders->HeaderFields.back().second
425 || pHeaders->HeaderFields.back().first.empty())
428 "header_callback: folded header field without start");
431 pHeaders->HeaderFields.back().first.back()
432 += OString::Concat(
" ") + ::std::string_view(&buffer[i], nitems - i);
436 if (pHeaders->HeaderFields.empty() || pHeaders->HeaderFields.back().second)
438 pHeaders->HeaderFields.emplace_back();
440 pHeaders->HeaderFields.back().first.emplace_back(OString(buffer, nitems));
445 SAL_WARN(
"ucb.ucp.webdav.curl",
"exception in header_callback");
451 static auto ProcessHeaders(::std::vector<OString>
const& rHeaders) -> ::std::map<OUString, OUString>
453 ::std::map<OUString, OUString> ret;
454 for (OString
const& rLine : rHeaders)
457 if (!rLine.endsWith(
"\r\n", &line))
459 SAL_WARN(
"ucb.ucp.webdav.curl",
"invalid header field (no CRLF)");
462 if (line.startsWith(
"HTTP/")
467 auto const nColon(line.indexOf(
':'));
471 SAL_WARN(
"ucb.ucp.webdav.curl",
"invalid header field (no :)");
477 SAL_WARN(
"ucb.ucp.webdav.curl",
"invalid header field (empty name)");
481 auto const name(::rtl::OStringToOUString(line.copy(0, nColon).toAsciiLowerCase(),
482 RTL_TEXTENCODING_ASCII_US));
483 sal_Int32 nStart(nColon + 1);
484 while (nStart < line.getLength() && (line[nStart] ==
' ' || line[nStart] ==
'\t'))
488 sal_Int32 nEnd(line.getLength());
489 while (nStart < nEnd && (line[nEnd - 1] ==
' ' || line[nEnd - 1] ==
'\t'))
494 auto const value(::rtl::OStringToOUString(line.subView(nStart, nEnd - nStart),
495 RTL_TEXTENCODING_ASCII_US));
496 auto const it(ret.find(
name));
499 it->second = it->second +
"," +
value;
510 ResponseHeaders
const& rHeaders,
511 ::std::pair<::std::vector<OUString>
const&,
DAVResource&>
const*
const pRequestedHeaders)
514 ::std::map<OUString, OUString>
const headerMap(
516 if (pRequestedHeaders)
518 for (OUString
const& rHeader : pRequestedHeaders->first)
520 auto const it(headerMap.find(rHeader.toAsciiLowerCase()));
521 if (it != headerMap.end())
525 value.
Name = it->first;
526 value.
Value <<= it->second;
527 pRequestedHeaders->second.properties.push_back(value);
534 static auto ExtractRealm(ResponseHeaders
const& rHeaders,
char const*
const pAuthHeaderName)
535 -> ::std::optional<OUString>
537 ::std::map<OUString, OUString>
const headerMap(
539 auto const it(headerMap.find(OUString::createFromAscii(pAuthHeaderName).toAsciiLowerCase()));
540 if (it == headerMap.end())
542 SAL_WARN(
"ucb.ucp.webdav.curl",
"cannot find auth header");
549 auto i(it->second.toAsciiLowerCase().indexOf(
"realm="));
553 SAL_INFO(
"ucb.ucp.webdav.curl",
"auth header has no realm");
557 i += ::std::strlen(
"realm=");
558 if (it->second.getLength() <
i + 2 || it->second[
i] !=
'\"')
560 SAL_WARN(
"ucb.ucp.webdav.curl",
"no realm value");
565 while (i < it->second.getLength() && it->second[
i] !=
'\"')
567 if (it->second[
i] ==
'\\')
570 if (it->second.getLength() <=
i)
572 SAL_WARN(
"ucb.ucp.webdav.curl",
"unterminated quoted-pair");
576 buf.append(it->second[
i]);
579 if (it->second.getLength() <=
i)
581 SAL_WARN(
"ucb.ucp.webdav.curl",
"unterminated realm");
584 return buf.makeStringAndClear();
589 uno::Sequence<beans::NamedValue>
const& rFlags,
595 , m_Proxy(rProxyDecider.getProxy(m_URI.GetScheme(), m_URI.GetHost(), m_URI.GetPort()))
601 SAL_WARN(
"ucb.ucp.webdav.curl",
"curl_multi_init failed");
605 m_pCurl.reset(curl_easy_init());
608 SAL_WARN(
"ucb.ucp.webdav.curl",
"curl_easy_init failed");
612 curl_version_info_data
const*
const pVersion(curl_version_info(CURLVERSION_NOW));
615 "curl version: " << pVersion->version <<
" " << pVersion->host
616 <<
" features: " << ::std::hex << pVersion->features <<
" ssl: "
617 << pVersion->ssl_version <<
" libz: " << pVersion->libz_version);
622 OString
const useragent(
623 OString::Concat(
"LibreOffice " LIBO_VERSION_DOTTED
" denylistedbackend/")
624 + ::std::string_view(pVersion->version, strlen(pVersion->version)) +
" "
625 + pVersion->ssl_version);
629 auto rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_USERAGENT, useragent.getStr());
632 SAL_WARN(
"ucb.ucp.webdav.curl",
"CURLOPT_USERAGENT failed: " << GetErrorString(rc));
638 rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_ERRORBUFFER,
m_ErrorBuffer);
639 assert(rc == CURLE_OK);
642 rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_DEBUGFUNCTION,
debug_callback);
643 assert(rc == CURLE_OK);
645 rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_VERBOSE, 1L);
646 assert(rc == CURLE_OK);
648 rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_ACCEPT_ENCODING,
"");
651 SAL_WARN(
"ucb.ucp.webdav.curl",
"CURLOPT_ACCEPT_ENCODING failed: " << GetErrorString(rc));
655 auto const connectTimeout(officecfg::Inet::Settings::ConnectTimeout::get());
657 rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_CONNECTTIMEOUT,
658 ::std::max<long>(2L, ::std::min<long>(connectTimeout, 180L)));
661 SAL_WARN(
"ucb.ucp.webdav.curl",
"CURLOPT_CONNECTTIMEOUT failed: " << GetErrorString(rc));
665 auto const readTimeout(officecfg::Inet::Settings::ReadTimeout::get());
666 m_nReadTimeout = ::std::max<int>(20, ::std::min<long>(readTimeout, 180)) * 1000;
668 rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_TIMEOUT, 300L);
671 SAL_WARN(
"ucb.ucp.webdav.curl",
"CURLOPT_TIMEOUT failed: " << GetErrorString(rc));
675 rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_WRITEFUNCTION, &
write_callback);
676 assert(rc == CURLE_OK);
677 rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_READFUNCTION, &
read_callback);
678 assert(rc == CURLE_OK);
679 rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_HEADERFUNCTION, &
header_callback);
681 assert(rc == CURLE_OK);
682 rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_HTTPAUTH, CURLAUTH_ANY);
683 assert(rc == CURLE_OK);
686 rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_PROXY, utf8Proxy.getStr());
689 SAL_WARN(
"ucb.ucp.webdav.curl",
"CURLOPT_PROXY failed: " << GetErrorString(rc));
695 rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_PROXYPORT,
static_cast<long>(
m_Proxy.
nPort));
696 assert(rc == CURLE_OK);
698 rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_PROXYAUTH, CURLAUTH_ANY);
699 assert(rc == CURLE_OK);
702 [](
auto const& rFlag) {
return rFlag.Name ==
"KeepAlive"; }));
703 if (it !=
m_Flags.end() && it->Value.get<
bool>())
707 rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_FORBID_REUSE, 1L);
708 assert(rc == CURLE_OK);
721 return m_URI.GetScheme() == uri.
GetScheme() && m_URI.GetHost() == uri.
GetHost()
722 && m_URI.GetPort() == uri.
GetPort() && m_Flags == rFlags;
732 assert(m_URI.GetScheme() ==
"http" || m_URI.GetScheme() ==
"https");
733 return !m_Proxy.aName.isEmpty();
739 bool expected(
false);
741 if (m_AbortFlag.compare_exchange_strong(expected,
true))
746 curl_multi_wakeup(m_pCurlMulti.get());
757 curl_slist* pRequestHeaderList, uno::Reference<io::XOutputStream>
const* pxOutStream,
758 uno::Sequence<sal_Int8>
const* pInData,
759 ::std::pair<::std::vector<OUString>
const&,
DAVResource&>
const* pRequestedHeaders,
760 ResponseHeaders& rHeaders) ->
void;
767 uno::Reference<io::XOutputStream>
const* pxOutStream,
768 uno::Reference<io::XInputStream>
const* pxInStream,
769 ::std::pair<::std::vector<OUString>
const&,
DAVResource&>
const* pRequestedHeaders) ->
void;
773 ::std::tuple<::std::vector<OUString>
const&, ::std::vector<DAVResource>*
const,
774 ::std::vector<ucb::Lock>*
const>
const* o_pRequestedProperties,
775 ::std::vector<DAVResourceInfo>*
const o_pResourceInfos,
780 bool isOverwrite,
char const* pMethod) ->
void;
785 uno::Reference<io::XInputStream>
const* pxInStream)
786 -> ::std::vector<::std::pair<ucb::Lock, sal_Int32>>;
796 if (rSession.UsesProxy())
799 assert(rURIReference.startsWith(
"http://") || rURIReference.startsWith(
"https://"));
804 assert(rURIReference.startsWith(
"/"));
805 return rSession.m_URI.CloneWithRelativeRefPathAbsolute(rURIReference);
812 curl_slist*
const pRequestHeaderList,
813 uno::Reference<io::XOutputStream>
const*
const pxOutStream,
814 uno::Sequence<sal_Int8>
const*
const pInData,
815 ::std::pair<::std::vector<OUString>
const&,
DAVResource&>
const*
const pRequestedHeaders,
816 ResponseHeaders& rHeaders) ->
void
819 auto rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_HEADERDATA,
nullptr);
820 assert(rc == CURLE_OK);
824 rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_WRITEDATA,
nullptr);
825 assert(rc == CURLE_OK);
829 rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_READDATA,
nullptr);
830 assert(rc == CURLE_OK);
831 rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_UPLOAD, 0L);
832 assert(rc == CURLE_OK);
834 if (pRequestHeaderList)
836 rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_HTTPHEADER,
nullptr);
837 assert(rc == CURLE_OK);
841 if (pRequestHeaderList)
843 auto rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_HTTPHEADER, pRequestHeaderList);
844 assert(rc == CURLE_OK);
848 auto rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_CURLU, rURI.GetCURLU());
849 assert(rc == CURLE_OK);
851 rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_HEADERDATA, &rHeaders);
852 assert(rc == CURLE_OK);
853 ::std::optional<DownloadTarget> oDownloadTarget;
856 oDownloadTarget.emplace(*pxOutStream, rHeaders);
857 rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_WRITEDATA, &*oDownloadTarget);
858 assert(rc == CURLE_OK);
860 ::std::optional<UploadSource> oUploadSource;
863 oUploadSource.emplace(*pInData);
864 rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_READDATA, &*oUploadSource);
865 assert(rc == CURLE_OK);
867 rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_UPLOAD, 1L);
868 assert(rc == CURLE_OK);
870 rSession.m_ErrorBuffer[0] =
'\0';
874 auto mc = curl_multi_add_handle(rSession.m_pCurlMulti.get(), rSession.m_pCurl.get());
878 "curl_multi_add_handle failed: " << GetErrorStringMulti(mc));
884 mc = curl_multi_remove_handle(rSession.m_pCurlMulti.get(), rSession.m_pCurl.get());
888 "curl_multi_remove_handle failed: " << GetErrorStringMulti(mc));
897 mc = curl_multi_perform(rSession.m_pCurlMulti.get(), &nRunning);
901 "curl_multi_perform failed: " << GetErrorStringMulti(mc));
911 mc = curl_multi_poll(rSession.m_pCurlMulti.get(),
nullptr, 0, rSession.m_nReadTimeout,
915 SAL_WARN(
"ucb.ucp.webdav.curl",
"curl_multi_poll failed: " << GetErrorStringMulti(mc));
920 if (rSession.m_AbortFlag.load())
924 }
while (nRunning != 0);
929 CURLMsg
const*
const pMsg = curl_multi_info_read(rSession.m_pCurlMulti.get(), &nRunning);
930 if (pMsg && pMsg->msg == CURLMSG_DONE)
932 assert(pMsg->easy_handle == rSession.m_pCurl.get());
933 rc = pMsg->data.result;
937 SAL_WARN(
"ucb.ucp.webdav.curl",
"curl_multi_info_read unexpected result");
939 }
while (nRunning != 0);
946 "curl_easy_perform failed: " << GetErrorString(rc, rSession.m_ErrorBuffer));
949 case CURLE_COULDNT_RESOLVE_PROXY:
953 case CURLE_COULDNT_RESOLVE_HOST:
957 case CURLE_COULDNT_CONNECT:
958 case CURLE_SSL_CONNECT_ERROR:
959 case CURLE_SSL_CERTPROBLEM:
960 case CURLE_SSL_CIPHER:
961 case CURLE_PEER_FAILED_VERIFICATION:
962 case CURLE_SSL_ISSUER_ERROR:
963 case CURLE_SSL_PINNEDPUBKEYNOTMATCH:
964 case CURLE_SSL_INVALIDCERTSTATUS:
965 case CURLE_FAILED_INIT:
966 #if CURL_AT_LEAST_VERSION(7, 69, 0)
967 case CURLE_QUIC_CONNECT_ERROR:
972 case CURLE_REMOTE_ACCESS_DENIED:
973 case CURLE_LOGIN_DENIED:
974 case CURLE_AUTH_ERROR:
978 case CURLE_WRITE_ERROR:
979 case CURLE_READ_ERROR:
980 case CURLE_OUT_OF_MEMORY:
981 case CURLE_ABORTED_BY_CALLBACK:
982 case CURLE_BAD_FUNCTION_ARGUMENT:
983 case CURLE_SEND_ERROR:
984 case CURLE_RECV_ERROR:
985 case CURLE_SSL_CACERT_BADFILE:
986 case CURLE_SSL_CRL_BADFILE:
987 case CURLE_RECURSIVE_API_CALL:
991 case CURLE_OPERATION_TIMEDOUT:
1001 rc = curl_easy_getinfo(rSession.m_pCurl.get(), CURLINFO_RESPONSE_CODE, &statusCode);
1002 assert(rc == CURLE_OK);
1003 assert(statusCode !=
SC_NONE);
1004 SAL_INFO(
"ucb.ucp.webdav.curl",
"HTTP status code: " << statusCode);
1005 if (statusCode < 300)
1013 OUString statusLine(
"\n" + rMethod +
"\n=>\n");
1014 if (!rHeaders.HeaderFields.empty() && !rHeaders.HeaderFields.back().first.empty()
1015 && rHeaders.HeaderFields.back().first.front().startsWith(
"HTTP"))
1017 statusLine += ::rtl::OStringToOUString(
1018 ::
o3tl::trim(rHeaders.HeaderFields.back().first.front()),
1019 RTL_TEXTENCODING_ASCII_US);
1037 char* pRedirectURL(
nullptr);
1038 rc = curl_easy_getinfo(rSession.m_pCurl.get(), CURLINFO_REDIRECT_URL,
1040 assert(rc == CURLE_OK);
1044 OUString
const redirectURL(::rtl::Uri::encode(
1046 ? OUString(pRedirectURL, strlen(pRedirectURL), RTL_TEXTENCODING_UTF8)
1048 rtl_UriCharClassUric, rtl_UriEncodeKeepEscapes, RTL_TEXTENCODING_UTF8));
1061 (*pxOutStream)->closeOutput();
1073 OUString
const*
const pToken(g_Init.LockStore.getLockTokenForURI(rURI.GetURI(),
nullptr));
1082 ::std::vector<ucb::Lock> locks;
1083 ::std::tuple<::std::vector<OUString>
const&, ::std::vector<DAVResource>*
const,
1084 ::std::vector<ucb::Lock>*
const>
const args(propertyNames,
nullptr, &locks);
1091 if (::std::any_of(locks.begin(), locks.end(), [pToken](ucb::Lock
const& rLock) {
1092 return ::std::any_of(
1093 rLock.LockTokens.begin(), rLock.LockTokens.end(),
1094 [pToken](OUString
const& rToken) {
return *pToken == rToken; });
1101 "lock token expired, removing: " << rURI.GetURI() <<
" " << *pToken);
1102 g_Init.LockStore.removeLock(rURI.GetURI());
1116 uno::Reference<io::XOutputStream>
const*
const pxOutStream,
1117 uno::Reference<io::XInputStream>
const*
const pxInStream,
1118 ::std::pair<::std::vector<OUString>
const&,
DAVResource&>
const*
const pRequestedHeaders)
1123 for (
auto const& rHeader : pEnv->m_aRequestHeaders)
1125 OString
const utf8Header(
1128 pRequestHeaderList.reset(
1129 curl_slist_append(pRequestHeaderList.release(), utf8Header.getStr()));
1130 if (!pRequestHeaderList)
1132 throw uno::RuntimeException(
"curl_slist_append failed");
1137 uno::Sequence<sal_Int8>
data;
1140 uno::Reference<io::XSeekable>
const xSeekable(*pxInStream, uno::UNO_QUERY);
1143 auto const len(xSeekable->getLength() - xSeekable->getPosition());
1144 if ((**pxInStream).readBytes(data, len) != len)
1146 throw uno::RuntimeException(
"short readBytes");
1151 ::std::vector<uno::Sequence<sal_Int8>> bufs;
1155 bufs.emplace_back();
1156 isDone = (**pxInStream).readSomeBytes(bufs.back(), 65536) == 0;
1159 for (
auto const& rBuf : bufs)
1163 throw std::bad_alloc();
1166 data.realloc(nSize);
1168 for (
auto const& rBuf : bufs)
1170 ::std::memcpy(data.getArray() + nCopied, rBuf.getConstArray(), rBuf.getLength());
1171 nCopied += rBuf.getLength();
1178 rSession.m_AbortFlag.store(
false);
1180 Guard guard(rSession.m_Mutex, rOptions, rURI, rSession.m_pCurl.get());
1187 decltype(CURLAUTH_ANY) AuthMask;
1188 Auth(OUString
const& rUserName, OUString
const& rPassword,
1189 decltype(CURLAUTH_ANY)
const & rAuthMask)
1190 : UserName(rUserName)
1191 , PassWord(rPassword)
1192 , AuthMask(rAuthMask)
1196 ::std::optional<Auth> oAuth;
1197 ::std::optional<Auth> oAuthProxy;
1198 if (pEnv && !rSession.m_isAuthenticatedProxy && !rSession.m_Proxy.aName.isEmpty())
1207 decltype(CURLAUTH_ANY)
const authSystem(CURLAUTH_NEGOTIATE | CURLAUTH_NTLM | CURLAUTH_NTLM_WB);
1208 if (pRequestedHeaders || (pEnv && !rSession.m_isAuthenticated))
1219 if (pEnv && !rSession.m_isAuthenticated
1220 && (!rURI.GetUser().isEmpty() || !rURI.GetPassword().isEmpty()))
1222 oAuth.emplace(rURI.GetUser(), rURI.GetPassword(), CURLAUTH_ANY);
1224 if (pRequestedHeaders)
1231 pRequestedHeaders->second.uri = rURI.GetURI();
1232 pRequestedHeaders->second.properties.clear();
1235 bool isRetry(
false);
1236 int nAuthRequests(0);
1237 int nAuthRequestsProxy(0);
1247 if (oAuth && !rSession.m_isAuthenticated)
1249 OString
const utf8UserName(
OUStringToOString(oAuth->UserName, RTL_TEXTENCODING_UTF8));
1251 = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_USERNAME, utf8UserName.getStr());
1254 SAL_WARN(
"ucb.ucp.webdav.curl",
"CURLOPT_USERNAME failed: " << GetErrorString(rc));
1257 OString
const utf8PassWord(
OUStringToOString(oAuth->PassWord, RTL_TEXTENCODING_UTF8));
1258 rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_PASSWORD, utf8PassWord.getStr());
1261 SAL_WARN(
"ucb.ucp.webdav.curl",
"CURLOPT_PASSWORD failed: " << GetErrorString(rc));
1264 rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_HTTPAUTH, oAuth->AuthMask);
1270 if (oAuthProxy && !rSession.m_isAuthenticatedProxy)
1272 OString
const utf8UserName(
1274 auto rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_PROXYUSERNAME,
1275 utf8UserName.getStr());
1279 "CURLOPT_PROXYUSERNAME failed: " << GetErrorString(rc));
1282 OString
const utf8PassWord(
1284 rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_PROXYPASSWORD,
1285 utf8PassWord.getStr());
1289 "CURLOPT_PROXYPASSWORD failed: " << GetErrorString(rc));
1292 rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_PROXYAUTH, oAuthProxy->AuthMask);
1298 ResponseHeaders headers(rSession.m_pCurl.get());
1300 uno::Reference<io::XSequenceOutputStream>
const xSeqOutStream(
1301 io::SequenceOutputStream::create(rSession.m_xContext));
1302 uno::Reference<io::XOutputStream>
const xTempOutStream(xSeqOutStream);
1303 assert(xTempOutStream.is());
1307 ProcessRequestImpl(rSession, rURI, rMethod, pRequestHeaderList.get(), &xTempOutStream,
1308 pxInStream ? &data :
nullptr, pRequestedHeaders, headers);
1311 (*pxOutStream)->writeBytes(xSeqOutStream->getWrittenBytes());
1312 (*pxOutStream)->closeOutput();
1318 auto const bytes(xSeqOutStream->getWrittenBytes());
1319 auto const len(::std::min<sal_Int32>(
bytes.getLength(), 10000));
1321 "DAVException; (first) " << len <<
" bytes of data received:");
1324 OStringBuffer buf(len);
1325 for (sal_Int32
i = 0;
i < len; ++
i)
1329 static char const hexDigit[16] = {
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
1330 '8',
'9',
'A',
'B',
'C',
'D',
'E',
'F' };
1332 buf.append(hexDigit[static_cast<sal_uInt8>(
bytes[
i]) >> 4]);
1333 buf.append(hexDigit[
bytes[i] & 0x0F]);
1337 buf.append(static_cast<char>(
bytes[
i]));
1340 SAL_INFO(
"ucb.ucp.webdav.curl", buf.makeStringAndClear());
1347 auto const statusCode(rException.
getStatus());
1353 if (g_Init.LockStore.getLockTokenForURI(rURI.GetURI(),
nullptr))
1381 : nAuthRequestsProxy);
1382 if (rnAuthRequests == 10)
1384 SAL_INFO(
"ucb.ucp.webdav.curl",
"aborting authentication after "
1385 << rnAuthRequests <<
" attempts");
1387 else if (pEnv && pEnv->m_xAuthListener)
1391 :
"Proxy-Authenticate"));
1393 ::std::optional<Auth>& roAuth(
1395 OUString userName(roAuth ? roAuth->UserName : OUString());
1396 OUString passWord(roAuth ? roAuth->PassWord : OUString());
1398 auto const rc = curl_easy_getinfo(rSession.m_pCurl.get(),
1400 ? CURLINFO_HTTPAUTH_AVAIL
1401 : CURLINFO_PROXYAUTH_AVAIL,
1403 assert(rc == CURLE_OK);
1410 bool const isSystemCredSupported((authAvail & authSystem) != 0
1411 && rnAuthRequests == 0);
1421 auto const ret = pEnv->m_xAuthListener->authenticate(
1422 oRealm ? *oRealm :
"",
1424 : rSession.m_Proxy.aName,
1425 userName, passWord, isSystemCredSupported);
1440 roAuth.emplace(userName, passWord,
1441 ((userName.isEmpty() && passWord.isEmpty())
1442 ? (authAvail & authSystem)
1451 SAL_INFO(
"ucb.ucp.webdav.curl",
"no auth credentials provided");
1454 rSession.m_URI.GetPort()));
1469 rSession.m_isAuthenticated =
true;
1474 rSession.m_isAuthenticatedProxy =
true;
1482 SAL_INFO(
"ucb.ucp.webdav.curl",
"OPTIONS: " << rURIReference);
1488 ::std::vector<OUString>
const headerNames{
"allow",
"dav" };
1490 ::std::pair<::std::vector<OUString>
const&,
DAVResource&>
const headers(headerNames, result);
1492 ::std::vector<CurlOption>
const options{
1493 g_NoBody, { CURLOPT_CUSTOMREQUEST,
"OPTIONS",
"CURLOPT_CUSTOMREQUEST" }
1503 SAL_INFO(
"ucb.ucp.webdav.curl",
"OPTIONS: header: " << it.Name <<
": " << value);
1504 if (it.Name.equalsIgnoreAsciiCase(
"allow"))
1506 rOptions.setAllowedMethods(value);
1508 else if (it.Name.equalsIgnoreAsciiCase(
"dav"))
1516 for (OUString
const&
v :
list)
1520 rOptions.setClass1();
1524 rOptions.setClass2();
1528 rOptions.setClass3();
1533 if (rOptions.isClass2() || rOptions.isClass3())
1535 if (g_Init.LockStore.getLockTokenForURI(uri.
GetURI(),
nullptr))
1537 rOptions.setLocked();
1544 ::std::tuple<::std::vector<OUString>
const&, ::std::vector<DAVResource>*
const,
1545 ::std::vector<ucb::Lock>*
const>
const*
const o_pRequestedProperties,
1549 assert((o_pRequestedProperties !=
nullptr) != (o_pResourceInfos !=
nullptr));
1550 assert((o_pRequestedProperties ==
nullptr)
1551 || (::std::get<1>(*o_pRequestedProperties) !=
nullptr)
1552 != (::std::get<2>(*o_pRequestedProperties) !=
nullptr));
1554 ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist, curl_slist_free_all>> pList;
1555 pList.reset(curl_slist_append(pList.release(),
"Content-Type: application/xml"));
1558 throw uno::RuntimeException(
"curl_slist_append failed");
1570 depth =
"Depth: infinity";
1575 pList.reset(curl_slist_append(pList.release(), depth.getStr()));
1578 throw uno::RuntimeException(
"curl_slist_append failed");
1581 uno::Reference<io::XSequenceOutputStream>
const xSeqOutStream(
1582 io::SequenceOutputStream::create(rSession.m_xContext));
1583 uno::Reference<io::XOutputStream>
const xRequestOutStream(xSeqOutStream);
1584 assert(xRequestOutStream.is());
1586 uno::Reference<xml::sax::XWriter>
const xWriter(xml::sax::Writer::create(rSession.m_xContext));
1587 xWriter->setOutputStream(xRequestOutStream);
1588 xWriter->startDocument();
1590 pAttrList->AddAttribute(
"xmlns",
"CDATA",
"DAV:");
1591 xWriter->startElement(
"propfind", pAttrList);
1592 if (o_pResourceInfos)
1594 xWriter->startElement(
"propname",
nullptr);
1595 xWriter->endElement(
"propname");
1599 if (::std::get<0>(*o_pRequestedProperties).empty())
1601 xWriter->startElement(
"allprop",
nullptr);
1602 xWriter->endElement(
"allprop");
1606 xWriter->startElement(
"prop",
nullptr);
1607 for (OUString
const& rName : ::std::get<0>(*o_pRequestedProperties))
1612 pAttrList->AddAttribute(
"xmlns",
"CDATA", OUString::createFromAscii(name.
nspace));
1613 xWriter->startElement(OUString::createFromAscii(name.
name), pAttrList);
1614 xWriter->endElement(OUString::createFromAscii(name.
name));
1616 xWriter->endElement(
"prop");
1619 xWriter->endElement(
"propfind");
1620 xWriter->endDocument();
1622 uno::Reference<io::XInputStream>
const xRequestInStream(
1623 io::SequenceInputStream::createStreamFromSequence(rSession.m_xContext,
1624 xSeqOutStream->getWrittenBytes()));
1625 assert(xRequestInStream.is());
1627 curl_off_t
const len(xSeqOutStream->getWrittenBytes().getLength());
1629 ::std::vector<CurlOption>
const options{
1630 { CURLOPT_CUSTOMREQUEST,
"PROPFIND",
"CURLOPT_CUSTOMREQUEST" },
1632 { CURLOPT_INFILESIZE_LARGE, len,
nullptr, CurlOption::Type::CurlOffT }
1636 uno::Reference<io::XInputStream>
const xResponseInStream(io::Pipe::create(rSession.m_xContext));
1637 uno::Reference<io::XOutputStream>
const xResponseOutStream(xResponseInStream, uno::UNO_QUERY);
1638 assert(xResponseInStream.is());
1639 assert(xResponseOutStream.is());
1642 &xResponseOutStream, &xRequestInStream,
nullptr);
1644 if (o_pResourceInfos)
1650 if (::std::get<1>(*o_pRequestedProperties) !=
nullptr)
1652 *::std::get<1>(*o_pRequestedProperties)
1654 for (
DAVResource& it : *::std::get<1>(*o_pRequestedProperties))
1657 if (it.uri.startsWith(
"/"))
1661 it.uri = rSession.m_URI.CloneWithRelativeRefPathAbsolute(it.uri).GetURI();
1666 "PROPFIND: exception parsing uri " << it.uri);
1680 ::std::vector<OUString>
const& rPropertyNames,
1681 ::std::vector<DAVResource>& o_rResources,
1684 SAL_INFO(
"ucb.ucp.webdav.curl",
"PROPFIND: " << rURIReference <<
" " << depth);
1688 ::std::tuple<::std::vector<OUString>
const&, ::std::vector<DAVResource>*
const,
1689 ::std::vector<ucb::Lock>*
const>
const args(rPropertyNames, &o_rResources,
1695 ::std::vector<DAVResourceInfo>& o_rResourceInfos,
1698 SAL_INFO(
"ucb.ucp.webdav.curl",
"PROPFIND: " << rURIReference <<
" " << depth);
1706 ::std::vector<ProppatchValue>
const& rValues,
1709 SAL_INFO(
"ucb.ucp.webdav.curl",
"PROPPATCH: " << rURIReference);
1714 ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist, curl_slist_free_all>> pList;
1715 pList.reset(curl_slist_append(pList.release(),
"Content-Type: application/xml"));
1718 throw uno::RuntimeException(
"curl_slist_append failed");
1722 uno::Reference<io::XSequenceOutputStream>
const xSeqOutStream(
1723 io::SequenceOutputStream::create(
m_xContext));
1724 uno::Reference<io::XOutputStream>
const xRequestOutStream(xSeqOutStream);
1725 assert(xRequestOutStream.is());
1726 uno::Reference<xml::sax::XWriter>
const xWriter(xml::sax::Writer::create(
m_xContext));
1727 xWriter->setOutputStream(xRequestOutStream);
1728 xWriter->startDocument();
1730 pAttrList->AddAttribute(
"xmlns",
"CDATA",
"DAV:");
1731 xWriter->startElement(
"propertyupdate", pAttrList);
1735 OUString
const operation((rPropValue.operation ==
PROPSET) ? OUString(
"set")
1736 : OUString(
"remove"));
1737 xWriter->startElement(operation,
nullptr);
1738 xWriter->startElement(
"prop",
nullptr);
1742 pAttrList->AddAttribute(
"xmlns",
"CDATA", OUString::createFromAscii(name.
nspace));
1743 xWriter->startElement(OUString::createFromAscii(name.
name), pAttrList);
1744 if (rPropValue.operation ==
PROPSET)
1748 ::std::optional<::std::pair<OUString, OUString>>
const oProp(
1752 xWriter->startElement(
"ucbprop",
nullptr);
1753 xWriter->startElement(
"type",
nullptr);
1754 xWriter->characters(oProp->first);
1755 xWriter->endElement(
"type");
1756 xWriter->startElement(
"value",
nullptr);
1757 xWriter->characters(oProp->second);
1758 xWriter->endElement(
"value");
1759 xWriter->endElement(
"ucbprop");
1765 rPropValue.value >>= value;
1766 xWriter->characters(value);
1769 xWriter->endElement(OUString::createFromAscii(name.
name));
1770 xWriter->endElement(
"prop");
1771 xWriter->endElement(operation);
1773 xWriter->endElement(
"propertyupdate");
1774 xWriter->endDocument();
1776 uno::Reference<io::XInputStream>
const xRequestInStream(
1777 io::SequenceInputStream::createStreamFromSequence(
m_xContext,
1778 xSeqOutStream->getWrittenBytes()));
1779 assert(xRequestInStream.is());
1781 curl_off_t
const len(xSeqOutStream->getWrittenBytes().getLength());
1783 ::std::vector<CurlOption>
const options{
1784 { CURLOPT_CUSTOMREQUEST,
"PROPPATCH",
"CURLOPT_CUSTOMREQUEST" },
1786 { CURLOPT_INFILESIZE_LARGE, len,
nullptr, CurlOption::Type::CurlOffT }
1790 nullptr, &xRequestInStream,
nullptr);
1796 SAL_INFO(
"ucb.ucp.webdav.curl",
"HEAD: " << rURIReference);
1800 ::std::vector<CurlOption>
const options{ g_NoBody };
1802 ::std::pair<::std::vector<OUString>
const&,
DAVResource&>
const headers(rHeaderNames,
1810 -> uno::Reference<io::XInputStream>
1812 SAL_INFO(
"ucb.ucp.webdav.curl",
"GET: " << rURIReference);
1821 uno::Reference<io::XSequenceOutputStream>
const xSeqOutStream(
1822 io::SequenceOutputStream::create(
m_xContext));
1823 uno::Reference<io::XOutputStream>
const xResponseOutStream(xSeqOutStream);
1824 assert(xResponseOutStream.is());
1826 ::std::vector<CurlOption>
const options{ { CURLOPT_HTTPGET, 1L,
nullptr } };
1831 uno::Reference<io::XInputStream>
const xResponseInStream(
1832 io::SequenceInputStream::createStreamFromSequence(
m_xContext,
1833 xSeqOutStream->getWrittenBytes()));
1834 assert(xResponseInStream.is());
1836 return xResponseInStream;
1839 auto CurlSession::GET(OUString
const& rURIReference, uno::Reference<io::XOutputStream>& rxOutStream,
1842 SAL_INFO(
"ucb.ucp.webdav.curl",
"GET: " << rURIReference);
1846 ::std::vector<CurlOption>
const options{ { CURLOPT_HTTPGET, 1L,
nullptr } };
1852 auto CurlSession::GET(OUString
const& rURIReference, ::std::vector<OUString>
const& rHeaderNames,
1854 -> uno::Reference<io::XInputStream>
1856 SAL_INFO(
"ucb.ucp.webdav.curl",
"GET: " << rURIReference);
1860 ::std::vector<CurlOption>
const options{ { CURLOPT_HTTPGET, 1L,
nullptr } };
1862 uno::Reference<io::XSequenceOutputStream>
const xSeqOutStream(
1863 io::SequenceOutputStream::create(
m_xContext));
1864 uno::Reference<io::XOutputStream>
const xResponseOutStream(xSeqOutStream);
1865 assert(xResponseOutStream.is());
1867 ::std::pair<::std::vector<OUString>
const&,
DAVResource&>
const headers(rHeaderNames,
1873 uno::Reference<io::XInputStream>
const xResponseInStream(
1874 io::SequenceInputStream::createStreamFromSequence(
m_xContext,
1875 xSeqOutStream->getWrittenBytes()));
1876 assert(xResponseInStream.is());
1878 return xResponseInStream;
1881 auto CurlSession::GET(OUString
const& rURIReference, uno::Reference<io::XOutputStream>& rxOutStream,
1882 ::std::vector<OUString>
const& rHeaderNames,
DAVResource& io_rResource,
1885 SAL_INFO(
"ucb.ucp.webdav.curl",
"GET: " << rURIReference);
1889 ::std::vector<CurlOption>
const options{ { CURLOPT_HTTPGET, 1L,
nullptr } };
1891 ::std::pair<::std::vector<OUString>
const&, DAVResource&>
const headers(rHeaderNames,
1899 uno::Reference<io::XInputStream>
const& rxInStream,
1902 SAL_INFO(
"ucb.ucp.webdav.curl",
"PUT: " << rURIReference);
1907 uno::Reference<io::XSeekable>
const xSeekable(rxInStream, uno::UNO_QUERY);
1908 if (!xSeekable.is())
1910 throw uno::RuntimeException(
"TODO: not seekable");
1912 curl_off_t
const len(xSeekable->getLength() - xSeekable->getPosition());
1914 ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist, curl_slist_free_all>> pList;
1915 OUString
const*
const pToken(g_Init.LockStore.getLockTokenForURI(uri.
GetURI(),
nullptr));
1918 OString
const utf8If(
"If: "
1927 pList.reset(curl_slist_append(pList.release(), utf8If.getStr()));
1930 throw uno::RuntimeException(
"curl_slist_append failed");
1937 ::std::vector<CurlOption>
const options{ { CURLOPT_INFILESIZE_LARGE, len,
nullptr,
1938 CurlOption::Type::CurlOffT } };
1941 &rxInStream,
nullptr);
1944 auto CurlSession::POST(OUString
const& rURIReference, OUString
const& rContentType,
1945 OUString
const& rReferer, uno::Reference<io::XInputStream>
const& rxInStream,
1948 SAL_INFO(
"ucb.ucp.webdav.curl",
"POST: " << rURIReference);
1953 ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist, curl_slist_free_all>> pList(
1954 curl_slist_append(
nullptr,
"Transfer-Encoding: chunked"));
1957 throw uno::RuntimeException(
"curl_slist_append failed");
1959 OString
const utf8ContentType(
"Content-Type: "
1961 pList.reset(curl_slist_append(pList.release(), utf8ContentType.getStr()));
1964 throw uno::RuntimeException(
"curl_slist_append failed");
1966 OString
const utf8Referer(
"Referer: " +
OUStringToOString(rReferer, RTL_TEXTENCODING_ASCII_US));
1967 pList.reset(curl_slist_append(pList.release(), utf8Referer.getStr()));
1970 throw uno::RuntimeException(
"curl_slist_append failed");
1973 ::std::vector<CurlOption>
const options{ { CURLOPT_POST, 1L,
nullptr } };
1975 uno::Reference<io::XSequenceOutputStream>
const xSeqOutStream(
1976 io::SequenceOutputStream::create(m_xContext));
1977 uno::Reference<io::XOutputStream>
const xResponseOutStream(xSeqOutStream);
1978 assert(xResponseOutStream.is());
1981 &xResponseOutStream, &rxInStream,
nullptr);
1983 uno::Reference<io::XInputStream>
const xResponseInStream(
1984 io::SequenceInputStream::createStreamFromSequence(m_xContext,
1985 xSeqOutStream->getWrittenBytes()));
1986 assert(xResponseInStream.is());
1988 return xResponseInStream;
1991 auto CurlSession::POST(OUString
const& rURIReference, OUString
const& rContentType,
1992 OUString
const& rReferer, uno::Reference<io::XInputStream>
const& rxInStream,
1993 uno::Reference<io::XOutputStream>& rxOutStream,
1994 DAVRequestEnvironment
const& rEnv) ->
void
1996 SAL_INFO(
"ucb.ucp.webdav.curl",
"POST: " << rURIReference);
2001 ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist, curl_slist_free_all>> pList(
2002 curl_slist_append(
nullptr,
"Transfer-Encoding: chunked"));
2005 throw uno::RuntimeException(
"curl_slist_append failed");
2007 OString
const utf8ContentType(
"Content-Type: "
2009 pList.reset(curl_slist_append(pList.release(), utf8ContentType.getStr()));
2012 throw uno::RuntimeException(
"curl_slist_append failed");
2014 OString
const utf8Referer(
"Referer: " +
OUStringToOString(rReferer, RTL_TEXTENCODING_ASCII_US));
2015 pList.reset(curl_slist_append(pList.release(), utf8Referer.getStr()));
2018 throw uno::RuntimeException(
"curl_slist_append failed");
2021 ::std::vector<CurlOption>
const options{ { CURLOPT_POST, 1L,
nullptr } };
2024 &rxOutStream, &rxInStream,
nullptr);
2029 SAL_INFO(
"ucb.ucp.webdav.curl",
"MKCOL: " << rURIReference);
2033 ::std::vector<CurlOption>
const options{
2034 g_NoBody, { CURLOPT_CUSTOMREQUEST,
"MKCOL",
"CURLOPT_CUSTOMREQUEST" }
2042 ::std::u16string_view
const rDestinationURI,
2044 char const*
const pMethod) ->
void
2048 OString
const utf8Destination(
"Destination: "
2050 ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist, curl_slist_free_all>> pList(
2051 curl_slist_append(
nullptr, utf8Destination.getStr()));
2054 throw uno::RuntimeException(
"curl_slist_append failed");
2056 OString
const utf8Overwrite(OString::Concat(
"Overwrite: ") + (isOverwrite ?
"T" :
"F"));
2057 pList.reset(curl_slist_append(pList.release(), utf8Overwrite.getStr()));
2060 throw uno::RuntimeException(
"curl_slist_append failed");
2063 ::std::vector<CurlOption>
const options{
2064 g_NoBody, { CURLOPT_CUSTOMREQUEST, pMethod,
"CURLOPT_CUSTOMREQUEST" }
2068 &rEnv, ::std::move(pList),
nullptr,
nullptr,
nullptr);
2074 SAL_INFO(
"ucb.ucp.webdav.curl",
"COPY: " << rSourceURIReference);
2083 SAL_INFO(
"ucb.ucp.webdav.curl",
"MOVE: " << rSourceURIReference);
2091 SAL_INFO(
"ucb.ucp.webdav.curl",
"DESTROY: " << rURIReference);
2095 ::std::vector<CurlOption>
const options{
2096 g_NoBody, { CURLOPT_CUSTOMREQUEST,
"DELETE",
"CURLOPT_CUSTOMREQUEST" }
2107 uno::Reference<io::XInputStream>
const*
const pxRequestInStream)
2108 -> ::std::vector<::std::pair<ucb::Lock, sal_Int32>>
2111 if (pxRequestInStream)
2113 uno::Reference<io::XSeekable>
const xSeekable(*pxRequestInStream, uno::UNO_QUERY);
2114 assert(xSeekable.is());
2115 len = xSeekable->getLength();
2118 ::std::vector<CurlOption>
const options{
2119 { CURLOPT_CUSTOMREQUEST,
"LOCK",
"CURLOPT_CUSTOMREQUEST" },
2121 { CURLOPT_INFILESIZE_LARGE, len,
nullptr, CurlOption::Type::CurlOffT }
2125 uno::Reference<io::XInputStream>
const xResponseInStream(io::Pipe::create(rSession.m_xContext));
2126 uno::Reference<io::XOutputStream>
const xResponseOutStream(xResponseInStream, uno::UNO_QUERY);
2127 assert(xResponseInStream.is());
2128 assert(xResponseOutStream.is());
2130 TimeValue startTime;
2131 osl_getSystemTime(&startTime);
2134 ::std::move(pRequestHeaderList), &xResponseOutStream,
2135 pxRequestInStream,
nullptr);
2138 SAL_WARN_IF(acquiredLocks.empty(),
"ucb.ucp.webdav.curl",
2139 "could not get LOCK for " << rURI.GetURI());
2142 osl_getSystemTime(&endTime);
2143 auto const elapsedSeconds(endTime.Seconds - startTime.Seconds);
2146 ::std::vector<::std::pair<ucb::Lock, sal_Int32>> ret;
2147 ret.reserve(acquiredLocks.size());
2148 for (
auto const& rLock : acquiredLocks)
2150 sal_Int32 lockExpirationTimeSeconds;
2151 if (rLock.Timeout == -1)
2153 lockExpirationTimeSeconds = -1;
2155 else if (rLock.Timeout <= elapsedSeconds)
2158 "LOCK timeout already expired when receiving LOCK response for "
2160 lockExpirationTimeSeconds = 0;
2164 lockExpirationTimeSeconds = startTime.Seconds + rLock.Timeout;
2166 ret.emplace_back(rLock, lockExpirationTimeSeconds);
2175 SAL_INFO(
"ucb.ucp.webdav.curl",
"LOCK: " << rURIReference);
2179 if (g_Init.LockStore.getLockTokenForURI(uri.
GetURI(), &rLock))
2189 uno::Reference<io::XSequenceOutputStream>
const xSeqOutStream(
2190 io::SequenceOutputStream::create(
m_xContext));
2191 uno::Reference<io::XOutputStream>
const xRequestOutStream(xSeqOutStream);
2192 assert(xRequestOutStream.is());
2193 uno::Reference<xml::sax::XWriter>
const xWriter(xml::sax::Writer::create(
m_xContext));
2194 xWriter->setOutputStream(xRequestOutStream);
2195 xWriter->startDocument();
2197 pAttrList->AddAttribute(
"xmlns",
"CDATA",
"DAV:");
2198 xWriter->startElement(
"lockinfo", pAttrList);
2199 xWriter->startElement(
"lockscope",
nullptr);
2200 switch (rLock.Scope)
2202 case ucb::LockScope_EXCLUSIVE:
2203 xWriter->startElement(
"exclusive",
nullptr);
2204 xWriter->endElement(
"exclusive");
2206 case ucb::LockScope_SHARED:
2207 xWriter->startElement(
"shared",
nullptr);
2208 xWriter->endElement(
"shared");
2213 xWriter->endElement(
"lockscope");
2214 xWriter->startElement(
"locktype",
nullptr);
2215 xWriter->startElement(
"write",
nullptr);
2216 xWriter->endElement(
"write");
2217 xWriter->endElement(
"locktype");
2219 if ((rLock.Owner >>= owner) && !owner.isEmpty())
2221 xWriter->startElement(
"owner",
nullptr);
2222 xWriter->characters(owner);
2223 xWriter->endElement(
"owner");
2225 xWriter->endElement(
"lockinfo");
2226 xWriter->endDocument();
2228 uno::Reference<io::XInputStream>
const xRequestInStream(
2229 io::SequenceInputStream::createStreamFromSequence(
m_xContext,
2230 xSeqOutStream->getWrittenBytes()));
2231 assert(xRequestInStream.is());
2233 ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist, curl_slist_free_all>> pList;
2234 pList.reset(curl_slist_append(pList.release(),
"Content-Type: application/xml"));
2237 throw uno::RuntimeException(
"curl_slist_append failed");
2240 switch (rLock.Depth)
2242 case ucb::LockDepth_ZERO:
2245 case ucb::LockDepth_ONE:
2248 case ucb::LockDepth_INFINITY:
2249 depth =
"Depth: infinity";
2254 pList.reset(curl_slist_append(pList.release(), depth.getStr()));
2257 throw uno::RuntimeException(
"curl_slist_append failed");
2260 switch (rLock.Timeout)
2263 timeout =
"Timeout: Infinite";
2266 timeout =
"Timeout: Second-180";
2269 timeout =
"Timeout: Second-" + OString::number(rLock.Timeout);
2270 assert(0 < rLock.Timeout);
2273 pList.reset(curl_slist_append(pList.release(), timeout.getStr()));
2276 throw uno::RuntimeException(
"curl_slist_append failed");
2279 auto const acquiredLocks
2282 for (
auto const& rAcquiredLock : acquiredLocks)
2284 g_Init.LockStore.addLock(uri.
GetURI(), rAcquiredLock.first,
2285 rAcquiredLock.first.LockTokens[0],
this, rAcquiredLock.second);
2286 SAL_INFO(
"ucb.ucp.webdav.curl",
"created LOCK for " << rURIReference);
2293 OUString
const*
const pToken(g_Init.LockStore.getLockTokenForURI(rURI.GetURI(),
nullptr));
2296 SAL_WARN(
"ucb.ucp.webdav.curl",
"attempt to unlock but not locked");
2299 OString
const utf8LockToken(
"Lock-Token: <"
2301 ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist, curl_slist_free_all>> pList(
2302 curl_slist_append(
nullptr, utf8LockToken.getStr()));
2305 throw uno::RuntimeException(
"curl_slist_append failed");
2308 ::std::vector<CurlOption>
const options{ { CURLOPT_CUSTOMREQUEST,
"UNLOCK",
2309 "CURLOPT_CUSTOMREQUEST" } };
2312 nullptr,
nullptr,
nullptr);
2317 SAL_INFO(
"ucb.ucp.webdav.curl",
"UNLOCK: " << rURIReference);
2325 g_Init.LockStore.removeLock(uri.
GetURI());
2329 sal_Int32& o_rLastChanceToSendRefreshRequest,
2330 bool& o_rIsAuthFailed) ->
bool
2332 SAL_INFO(
"ucb.ucp.webdav.curl",
"NonInteractive_LOCK: " << rURI);
2339 ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist, curl_slist_free_all>> pList(
2340 curl_slist_append(
nullptr,
"Timeout: Second-180"));
2342 assert(!rLockToken.empty());
2343 OString
const utf8If(
"If: (<" +
OUStringToOString(rLockToken, RTL_TEXTENCODING_ASCII_US)
2345 pList.reset(curl_slist_append(pList.release(), utf8If.getStr()));
2348 throw uno::RuntimeException(
"curl_slist_append failed");
2351 auto const acquiredLocks
2354 SAL_WARN_IF(1 < acquiredLocks.size(),
"ucb.ucp.webdav.curl",
2355 "multiple locks acquired on refresh for " << rURI);
2356 if (!acquiredLocks.empty())
2358 o_rLastChanceToSendRefreshRequest = acquiredLocks.begin()->second;
2360 SAL_INFO(
"ucb.ucp.webdav.curl",
"NonInteractive_LOCK succeeded on " << rURI);
2365 SAL_INFO(
"ucb.ucp.webdav.curl",
"NonInteractive_LOCK failed on " << rURI);
2370 o_rIsAuthFailed =
true;
2379 SAL_INFO(
"ucb.ucp.webdav.curl",
"NonInteractive_LOCK failed on " << rURI);
2386 SAL_INFO(
"ucb.ucp.webdav.curl",
"NonInteractive_UNLOCK: " << rURI);
2397 SAL_INFO(
"ucb.ucp.webdav.curl",
"NonInteractive_UNLOCK succeeded on " << rURI);
2401 SAL_INFO(
"ucb.ucp.webdav.curl",
"NonInteractive_UNLOCK failed on " << rURI);
static auto ExtractRealm(ResponseHeaders const &rHeaders, char const *const pAuthHeaderName) ->::std::optional< OUString >
static auto Unlock(CurlSession &rSession, CurlUri const &rURI, DAVRequestEnvironment const *pEnv) -> void
virtual auto LOCK(OUString const &rURIReference, css::ucb::Lock &rLock, DAVRequestEnvironment const &rEnv) -> void override
static void createSerfPropName(::std::u16string_view rFullName, SerfPropName &rName)
OUString const & GetPassword() const
const ExceptionCode & getError() const
static auto ProcessHeaders(::std::vector< OString > const &rHeaders) ->::std::map< OUString, OUString >
char m_ErrorBuffer[CURL_ERROR_SIZE]
buffer for libcurl detailed error messages
std::vector< ucb::Lock > parseWebDAVLockResponse(const uno::Reference< io::XInputStream > &xInputStream)
const sal_uInt16 SC_MOVED_TEMPORARILY
static auto URIReferenceToURI(CurlSession &rSession, OUString const &rURIReference) -> CurlUri
CurlSession(css::uno::Reference< css::uno::XComponentContext > const &xContext,::rtl::Reference< DAVSessionFactory > const &rpFactory, OUString const &rURI, css::uno::Sequence< css::beans::NamedValue > const &rFlags,::ucbhelper::InternetProxyDecider const &rProxyDecider)
static auto ExtractRequestedHeaders(ResponseHeaders const &rHeaders,::std::pair<::std::vector< OUString > const &, DAVResource & > const *const pRequestedHeaders) -> void
auto NonInteractive_LOCK(OUString const &rURI,::std::u16string_view rLockToken, sal_Int32 &o_rLastChanceToSendRefreshRequest, bool &o_rIsAuthFailed) -> bool
std::vector< sal_uInt8 > bytes
virtual auto PROPPATCH(OUString const &rURIReference,::std::vector< ProppatchValue > const &rValues, DAVRequestEnvironment const &rEnv) -> void override
virtual auto GET(OUString const &rURIReference, DAVRequestEnvironment const &rEnv) -> css::uno::Reference< css::io::XInputStream > override
virtual auto PROPFIND(OUString const &rURIReference, Depth depth,::std::vector< OUString > const &rPropertyNames,::std::vector< DAVResource > &o_rResources, DAVRequestEnvironment const &rEnv) -> void override
implementation of libcurl HTTP/DAV back-end
static size_t write_callback(char *const ptr, size_t const size, size_t const nmemb, void *const userdata)
auto NonInteractive_UNLOCK(OUString const &rURI) -> void
virtual auto UsesProxy() -> bool override
virtual ~CurlSession() override
virtual auto abort() -> void override
static size_t read_callback(char *const buffer, size_t const size, size_t const nitems, void *const userdata)
std::vector< DAVResource > parseWebDAVPropFindResponse(const uno::Reference< io::XInputStream > &xInputStream)
virtual auto MOVE(OUString const &rSourceURIReference, OUString const &rDestinationURI, DAVRequestEnvironment const &rEnv, bool isOverwrite=false) -> void override
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
int m_nReadTimeout
read timeout in milliseconds (connection timeout is stored in m_pCurl)
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
virtual auto POST(OUString const &rURIReference, OUString const &rContentType, OUString const &rReferer, css::uno::Reference< css::io::XInputStream > const &rxInStream, DAVRequestEnvironment const &rEnv) -> css::uno::Reference< css::io::XInputStream > override
static bool isUCBDeadProperty(const SerfPropName &rName)
virtual auto COPY(OUString const &rSourceURIReference, OUString const &rDestinationURI, DAVRequestEnvironment const &rEnv, bool isOverwrite=false) -> void override
#define SAL_N_ELEMENTS(arr)
virtual auto OPTIONS(OUString const &rURIReference, DAVOptions &rOptions, DAVRequestEnvironment const &rEnv) -> void override
virtual auto HEAD(OUString const &rURIReference,::std::vector< OUString > const &rHeaderNames, DAVResource &io_rResource, DAVRequestEnvironment const &rEnv) -> void override
::std::unique_ptr< CURLM, deleter_from_fn< CURLM, curl_multi_cleanup > > m_pCurlMulti
libcurl multi handle
static size_t header_callback(char *const buffer, size_t const size, size_t const nitems, void *const userdata)
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)
const sal_uInt16 SC_TEMPORARY_REDIRECT
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
::ucbhelper::InternetProxyServer const m_Proxy
proxy is used if aName is non-empty
OUString const & GetURI() const
virtual auto PUT(OUString const &rURIReference, css::uno::Reference< css::io::XInputStream > const &rxInStream, DAVRequestEnvironment const &rEnv) -> void override
virtual auto UNLOCK(OUString const &rURIReference, DAVRequestEnvironment const &rEnv) -> void override
enumrange< T >::Iterator end(enumrange< T >)
const sal_uInt16 SC_REQUEST_TIMEOUT
css::uno::Sequence< css::beans::NamedValue > const m_Flags
flags may be passed to constructor, e.g. "KeepAlive"
SQLCHAR SQLSMALLINT SQLCHAR * UserName
static constexpr OUStringLiteral LOCKDISCOVERY
exports com.sun.star.chart2. data
virtual auto CanUse(OUString const &rURI, css::uno::Sequence< css::beans::NamedValue > const &rFlags) -> bool override
this is just a bunch of static member functions called from CurlSession
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
#define SAL_WARN_IF(condition, area, stream)
static::std::optional<::std::pair< OUString, OUString > > toXML(const css::uno::Any &rInData)
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 >>
OUString const & GetUser() const
virtual auto MKCOL(OUString const &rURIReference, DAVRequestEnvironment const &rEnv) -> void override
#define SAL_INFO(area, stream)
const sal_uInt16 SC_UNAUTHORIZED
std::u16string_view trim(std::u16string_view str)
sal_uInt16 GetPort() const
const sal_uInt16 SC_LOCKED
std::vector< DAVPropertyValue > properties
static int debug_callback(CURL *handle, curl_infotype type, char *data, size_t size, void *)
static auto MoveOrCopy(CurlSession &rSession, OUString const &rSourceURIReference,::std::u16string_view rDestinationURI, DAVRequestEnvironment const &rEnv, bool isOverwrite, char const *pMethod) -> void
std::vector< DAVResourceInfo > parseWebDAVPropNameResponse(const uno::Reference< io::XInputStream > &xInputStream)
static auto TryRemoveExpiredLockToken(CurlSession &rSession, CurlUri const &rURI, DAVRequestEnvironment const *const pEnv) -> bool
OUString const & GetScheme() const
#define SAL_WARN(area, stream)
sal_uInt16 getStatus() const
Reference< XComponentContext > m_xContext
const sal_uInt16 SC_SEE_OTHER
const sal_uInt16 SC_PROXY_AUTHENTICATION_REQUIRED
const sal_uInt16 SC_PRECONDITION_FAILED
const sal_uInt16 SC_MOVED_PERMANENTLY
OUString convertCommaSeparated(uno::Sequence< OUString > const &i_rSeq)
virtual auto DESTROY(OUString const &rURIReference, DAVRequestEnvironment const &rEnv) -> void override
OUString const & GetHost() const
OUString ConnectionEndPointString(std::u16string_view rHostName, sal_uInt16 const nPort)
const sal_uInt16 SC_BAD_REQUEST
exports com.sun.star. uri