summaryrefslogtreecommitdiff
path: root/ucb
diff options
context:
space:
mode:
authorMichael Stahl <michael.stahl@allotropia.de>2021-10-29 21:53:31 +0200
committerMichael Stahl <michael.stahl@allotropia.de>2021-11-01 19:02:16 +0100
commitd4acfea2fed8f999347e49ed76c3f5115952df5c (patch)
tree92186a0808a045e6377721b7f6f28af27abe1a9f /ucb
parent4b7f8f7cf3146317ae2770a766efe48f7a295f72 (diff)
ucb: webdav-curl: rewrite authentication loop for reentrancy
Surprisingly calling authenticate() can lead to a reentrant call via event loop, which fortunately deadlocks with non-recusive mutex, or we'd have a much more difficult to debug problem: 5 std::unique_lock<std::mutex>::unique_lock() 7 http_dav_ucp::CurlProcessor::PropFind() 8 http_dav_ucp::CurlSession::PROPFIND() (this=0x391fe80, rURIReference="/testL.odt", depth=http_dav_ucp::DAVZERO, o_rResourceInfos=std::__debug::vector of length 0, capacity 0, rEnv=...) 9 http_dav_ucp::DAVResourceAccess::PROPFIND() (this=0xab3bb00, nDepth=http_dav_ucp::DAVZERO, rResInfo=std::__debug::vector of length 0, capacity 0, xEnv=uno::Reference to (class ucbhelper::CommandEnvironment *) 0x390b578) 10 http_dav_ucp::Content::getProperties() (this=0xa8ba9a0, xEnv=uno::Reference to (class ucbhelper::CommandEnvironment *) 0x390b578) 15 SfxBaseModel::isVersionable() (this=0x7159c90) 19 SfxBindings::Update_Impl(SfxStateCache&) 24 Timer::Invoke() (this=0x7394708) 30 g_main_loop_run () 35 (anonymous namespace)::executeMasterPasswordDialog() 40 (anonymous namespace)::UUIInteractionHandler::handle() 41 PasswordContainer::RequestPasswordFromUser() 42 PasswordContainer::GetMasterPassword() 47 PasswordContainer::find() 48 uui::PasswordContainerHelper::handleAuthenticationRequest() 57 ucbhelper::InterceptedInteraction::handle() 58 http_dav_ucp::DAVAuthListener_Impl::authenticate() 59 http_dav_ucp::CurlProcessor::ProcessRequestImpl() 60 http_dav_ucp::CurlProcessor::ProcessRequest() 61 http_dav_ucp::CurlProcessor::PropFind() 62 http_dav_ucp::CurlSession::PROPFIND() (this=0x391fe80, rURIReference="/testL.odt", depth=http_dav_ucp::DAVZERO, o_rResourceInfos=std::__debug::vector of length 0, capacity 0, rEnv=...) 63 http_dav_ucp::DAVResourceAccess::PROPFIND() (this=0xab8c060, nDepth=http_dav_ucp::DAVZERO, rResInfo=std::__debug::vector of length 0, capacity 0, xEnv=uno::Reference to (class ucbhelper::CommandEnvironment *) 0x390b578) 64 http_dav_ucp::Content::getProperties() (this=0xa8ba9a0, xEnv=uno::Reference to (class ucbhelper::CommandEnvironment *) 0x390b578) 67 ucbhelper::PropertySetInfo::hasPropertyByName() (this=0xa8d7a20, Name="TitleOnServer") 68 SfxBaseModel::getTitle() 74 framework::TitleHelper::impl_updateTitle() 75 framework::TitleHelper::documentEventOccured() 78 SfxBaseModel::postEvent_Impl() (this=0x7159c90, aName="OnModeChanged", xController=empty uno::Reference) 79 SfxBaseModel::Notify() 80 SfxBroadcaster::Broadcast() 81 SfxObjectShell::SetModalMode_Impl() 82 SfxViewFrame::SetModalMode(bool) 83 SfxFrameWindow_Impl::ModalHierarchyHdl() 86 SalFrame::NotifyModalHierarchy() 88 SalGtkFilePicker::execute() 92 ModelData_Impl::OutputFileDialog() 93 SfxStoringHelper::GUIStoreModel() Need to move authentication loop to the outer function, so that all the option guards in ProcessRequestImpl() are reset/set again, and also delay setting the option guards from the various callers so they can be reset in ProcessRequest() before authenticate(). Change-Id: Id697b1459f94f80fc58cee7768c2bd388369b29c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/124452 Tested-by: Jenkins Reviewed-by: Michael Stahl <michael.stahl@allotropia.de>
Diffstat (limited to 'ucb')
-rw-r--r--ucb/source/ucp/webdav-curl/CurlSession.cxx1197
1 files changed, 616 insertions, 581 deletions
diff --git a/ucb/source/ucp/webdav-curl/CurlSession.cxx b/ucb/source/ucp/webdav-curl/CurlSession.cxx
index 6fb54998c531..a3426f7ddd89 100644
--- a/ucb/source/ucp/webdav-curl/CurlSession.cxx
+++ b/ucb/source/ucp/webdav-curl/CurlSession.cxx
@@ -99,6 +99,72 @@ struct UploadSource
}
};
+auto GetErrorString(CURLcode const rc, char const* const pErrorBuffer = nullptr) -> OString
+{
+ char const* const pMessage( // static fallback
+ (pErrorBuffer && pErrorBuffer[0] != '\0') ? pErrorBuffer : curl_easy_strerror(rc));
+ return OString::Concat("(") + OString::number(sal_Int32(rc)) + ") " + pMessage;
+}
+
+auto GetErrorStringMulti(CURLMcode const mc) -> OString
+{
+ return OString::Concat("(") + OString::number(sal_Int32(mc)) + ") " + curl_multi_strerror(mc);
+}
+
+/// represent an option to be passed to curl_easy_setopt()
+struct CurlOption
+{
+ CURLoption const Option;
+ enum class Type
+ {
+ Pointer,
+ Long,
+ CurlOffT
+ };
+ Type const Tag;
+ union {
+ void const* const pValue;
+ decltype(0L) const lValue;
+ curl_off_t const cValue;
+ };
+#if 0
+ ::std::variant<void const*, long
+#if SAL_TYPES_SIZEOFLONG == 4
+ ,
+ curl_off_t
+#endif
+ > const Value;
+#endif
+ char const* const pExceptionString;
+
+ CurlOption(CURLoption const i_Option, void const* const i_Value,
+ char const* const i_pExceptionString)
+ : Option(i_Option)
+ , Tag(Type::Pointer)
+ , pValue(i_Value)
+ , pExceptionString(i_pExceptionString)
+ {
+ }
+ CurlOption(CURLoption const i_Option, decltype(0L) const i_Value,
+ char const* const i_pExceptionString)
+ : Option(i_Option)
+ , Tag(Type::Long)
+ , lValue(i_Value)
+ , pExceptionString(i_pExceptionString)
+ {
+ }
+#if SAL_TYPES_SIZEOFLONG == 4
+ CurlOption(CURLoption const i_Option, curl_off_t const i_Value,
+ char const* const i_pExceptionString)
+ : Option(i_Option)
+ , Tag(Type::CurlOffT)
+ , cValue(i_Value)
+ , pExceptionString(i_pExceptionString)
+ {
+ }
+#endif
+};
+
/// combined guard class to ensure things are released in correct order,
/// particularly in ProcessRequest() error handling
class Guard
@@ -106,22 +172,130 @@ class Guard
private:
/// mutex *first* because m_oGuard requires it
::std::unique_lock<::std::mutex> m_Lock;
- ::std::optional<::comphelper::ScopeGuard<::std::function<void()>>> m_oGuard;
+ ::std::vector<CurlOption> const m_Options;
+ ::http_dav_ucp::CurlUri const& m_rURI;
+ CURL* const m_pCurl;
public:
- explicit Guard(::std::mutex& rMutex)
- : m_Lock(rMutex)
+ explicit Guard(::std::mutex& rMutex, ::std::vector<CurlOption> const& rOptions,
+ ::http_dav_ucp::CurlUri const& rURI, CURL* const pCurl)
+ : m_Lock(rMutex, ::std::defer_lock)
+ , m_Options(rOptions)
+ , m_rURI(rURI)
+ , m_pCurl(pCurl)
{
+ Acquire();
}
- template <class Func>
- explicit Guard(::std::mutex& rMutex, Func&& rFunc)
- : m_Lock(rMutex)
- , m_oGuard(::std::move(rFunc))
+ ~Guard()
{
+ if (m_Lock.owns_lock())
+ {
+ Release();
+ }
}
- void unlock()
+
+ void Acquire()
{
- m_oGuard.reset();
+ assert(!m_Lock.owns_lock());
+ m_Lock.lock();
+ for (auto const& it : m_Options)
+ {
+ CURLcode rc(CURL_LAST); // warning C4701
+#if 0
+ if (void const* const* const pp = ::std::get_if<void const*>(&it.Value))
+ {
+ rc = curl_easy_setopt(m_pCurl, it.Option, *pp);
+ }
+ else if (decltype(0L) const * const pLong = ::std::get_if<long>(&it.Value))
+ {
+ rc = curl_easy_setopt(m_pCurl, it.Option, *pLong);
+ }
+#if SAL_TYPES_SIZEOFLONG == 4
+ else if (curl_off_t const* const pOfft = ::std::get_if<curl_off_t>(&it.Value))
+ {
+ rc = curl_easy_setopt(m_pCurl, it.Option, *pOfft);
+ }
+#endif
+#endif
+ if (it.Tag == CurlOption::Type::Pointer)
+ {
+ rc = curl_easy_setopt(m_pCurl, it.Option, it.pValue);
+ }
+ else if (it.Tag == CurlOption::Type::Long)
+ {
+ rc = curl_easy_setopt(m_pCurl, it.Option, it.lValue);
+ }
+#if SAL_TYPES_SIZEOFLONG == 4
+ else if (it.Tag == CurlOption::Type::CurlOffT)
+ {
+ rc = curl_easy_setopt(m_pCurl, it.Option, it.cValue);
+ }
+#endif
+ else
+ {
+ assert(false);
+ }
+ if (it.pExceptionString != nullptr)
+ {
+ if (rc != CURLE_OK)
+ {
+ SAL_WARN("ucb.ucp.webdav.curl",
+ "set " << it.pExceptionString << " failed: " << GetErrorString(rc));
+ throw ::http_dav_ucp::DAVException(
+ ::http_dav_ucp::DAVException::DAV_SESSION_CREATE,
+ ::http_dav_ucp::ConnectionEndPointString(m_rURI.GetHost(),
+ m_rURI.GetPort()));
+ }
+ }
+ else // many of the options cannot fail
+ {
+ assert(rc == CURLE_OK);
+ }
+ }
+ }
+ void Release()
+ {
+ assert(m_Lock.owns_lock());
+ for (auto const& it : m_Options)
+ {
+ CURLcode rc(CURL_LAST); // warning C4701
+#if 0
+ if (void const* const* const pp = ::std::get_if<void const*>(&it.Value))
+ {
+ rc = curl_easy_setopt(m_pCurl, it.Option, nullptr);
+ }
+ else if (decltype(0L) const * const pLong = ::std::get_if<long>(&it.Value))
+ {
+ rc = curl_easy_setopt(m_pCurl, it.Option, 0L);
+ }
+#if SAL_TYPES_SIZEOFLONG == 4
+ else if (curl_off_t const* const pOfft = ::std::get_if<curl_off_t>(&it.Value))
+ {
+ rc = curl_easy_setopt(m_pCurl, it.Option, curl_off_t(0));
+ }
+#endif
+#endif
+ if (it.Tag == CurlOption::Type::Pointer)
+ {
+ rc = curl_easy_setopt(m_pCurl, it.Option, nullptr);
+ }
+ else if (it.Tag == CurlOption::Type::Long)
+ {
+ rc = curl_easy_setopt(m_pCurl, it.Option, 0L);
+ }
+#if SAL_TYPES_SIZEOFLONG == 4
+ else if (it.Tag == CurlOption::Type::CurlOffT)
+ {
+ rc = curl_easy_setopt(m_pCurl, it.Option, curl_off_t(0));
+ }
+#endif
+ else
+ {
+ assert(false);
+ }
+ assert(rc == CURLE_OK);
+ (void)rc;
+ }
m_Lock.unlock();
}
};
@@ -130,19 +304,7 @@ public:
namespace http_dav_ucp
{
-static auto GetErrorString(CURLcode const rc, char const* const pErrorBuffer = nullptr) -> OString
-{
- char const* const pMessage( // static fallback
- (pErrorBuffer && pErrorBuffer[0] != '\0') ? pErrorBuffer : curl_easy_strerror(rc));
- return OString::Concat("(") + OString::number(sal_Int32(rc)) + ") " + pMessage;
-}
-
-static auto GetErrorStringMulti(CURLMcode const mc) -> OString
-{
- return OString::Concat("(") + OString::number(sal_Int32(mc)) + ") " + curl_multi_strerror(mc);
-}
-
- // libcurl callbacks:
+// libcurl callbacks:
#if OSL_DEBUG_LEVEL > 0
static int debug_callback(CURL* handle, curl_infotype type, char* data, size_t size,
@@ -607,14 +769,14 @@ struct CurlProcessor
static auto URIReferenceToURI(CurlSession& rSession, OUString const& rURIReference) -> CurlUri;
static auto ProcessRequestImpl(
- CurlSession& rSession, CurlUri const& rURI, DAVRequestEnvironment const* pEnv,
- ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist_free_all>> pRequestHeaderList,
+ CurlSession& rSession, CurlUri const& rURI, curl_slist* pRequestHeaderList,
uno::Reference<io::XOutputStream> const* pxOutStream,
uno::Reference<io::XInputStream> const* pxInStream,
- ::std::pair<::std::vector<OUString> const&, DAVResource&> const* pRequestedHeaders) -> void;
+ ::std::pair<::std::vector<OUString> const&, DAVResource&> const* pRequestedHeaders,
+ ResponseHeaders& rHeaders) -> void;
static auto ProcessRequest(
- Guard& rGuard, CurlSession& rSession, CurlUri const& rURI,
+ CurlSession& rSession, CurlUri const& rURI, ::std::vector<CurlOption> const& rOptions,
DAVRequestEnvironment const* pEnv,
::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist_free_all>> pRequestHeaderList,
uno::Reference<io::XOutputStream> const* pxOutStream,
@@ -661,32 +823,12 @@ auto CurlProcessor::URIReferenceToURI(CurlSession& rSession, OUString const& rUR
/// main function to initiate libcurl requests
auto CurlProcessor::ProcessRequestImpl(
- CurlSession& rSession, CurlUri const& rURI, DAVRequestEnvironment const* const pEnv,
- ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist_free_all>> pRequestHeaderList,
+ CurlSession& rSession, CurlUri const& rURI, curl_slist* const pRequestHeaderList,
uno::Reference<io::XOutputStream> const* const pxOutStream,
uno::Reference<io::XInputStream> const* const pxInStream,
- ::std::pair<::std::vector<OUString> const&, DAVResource&> const* const pRequestedHeaders)
- -> void
+ ::std::pair<::std::vector<OUString> const&, DAVResource&> const* const pRequestedHeaders,
+ ResponseHeaders& rHeaders) -> void
{
- // Clear flag before transfer starts; only a transfer started before
- // calling abort() will be aborted, not one started later.
- rSession.m_AbortFlag.store(false);
-
- if (pEnv)
- { // add custom request headers passed by caller
- for (auto const& rHeader : pEnv->m_aRequestHeaders)
- {
- OString const utf8Header(
- OUStringToOString(rHeader.first, RTL_TEXTENCODING_ASCII_US) + ": "
- + OUStringToOString(rHeader.second, RTL_TEXTENCODING_ASCII_US));
- pRequestHeaderList.reset(
- curl_slist_append(pRequestHeaderList.release(), utf8Header.getStr()));
- if (!pRequestHeaderList)
- {
- throw uno::RuntimeException("curl_slist_append failed");
- }
- }
- }
::comphelper::ScopeGuard const g([&]() {
auto rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_HEADERDATA, nullptr);
assert(rc == CURLE_OK);
@@ -712,8 +854,7 @@ auto CurlProcessor::ProcessRequestImpl(
if (pRequestHeaderList)
{
- auto rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_HTTPHEADER,
- pRequestHeaderList.get());
+ auto rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_HTTPHEADER, pRequestHeaderList);
assert(rc == CURLE_OK);
(void)rc;
}
@@ -721,8 +862,285 @@ auto CurlProcessor::ProcessRequestImpl(
auto rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_CURLU, rURI.GetCURLU());
assert(rc == CURLE_OK); // can't fail since 7.63.0
+ rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_HEADERDATA, &rHeaders);
+ assert(rc == CURLE_OK);
+ ::std::optional<DownloadTarget> oDownloadTarget;
+ if (pxOutStream)
+ {
+ oDownloadTarget.emplace(*pxOutStream, rHeaders);
+ rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_WRITEDATA, &*oDownloadTarget);
+ assert(rc == CURLE_OK);
+ }
+ ::std::optional<UploadSource> oUploadSource;
+ if (pxInStream)
+ {
+ oUploadSource.emplace(*pxInStream, rHeaders);
+ rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_READDATA, &*oUploadSource);
+ assert(rc == CURLE_OK);
+ // libcurl won't upload without setting this
+ rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_UPLOAD, 1L);
+ assert(rc == CURLE_OK);
+ }
+ rSession.m_ErrorBuffer[0] = '\0';
+
+ // note: easy handle must be added for *every* transfer!
+ // otherwise it gets stuck in MSTATE_MSGSENT forever after 1st transfer
+ auto mc = curl_multi_add_handle(rSession.m_pCurlMulti.get(), rSession.m_pCurl.get());
+ if (mc != CURLM_OK)
+ {
+ SAL_WARN("ucb.ucp.webdav.curl",
+ "curl_multi_add_handle failed: " << GetErrorStringMulti(mc));
+ throw DAVException(
+ DAVException::DAV_SESSION_CREATE,
+ ConnectionEndPointString(rSession.m_URI.GetHost(), rSession.m_URI.GetPort()));
+ }
+ ::comphelper::ScopeGuard const gg([&]() {
+ mc = curl_multi_remove_handle(rSession.m_pCurlMulti.get(), rSession.m_pCurl.get());
+ if (mc != CURLM_OK)
+ {
+ SAL_WARN("ucb.ucp.webdav.curl",
+ "curl_multi_remove_handle failed: " << GetErrorStringMulti(mc));
+ }
+ });
+
+ // this is where libcurl actually does something
+ rc = CURL_LAST; // clear current value
+ int nRunning;
+ do
+ {
+ mc = curl_multi_perform(rSession.m_pCurlMulti.get(), &nRunning);
+ if (mc != CURLM_OK)
+ {
+ SAL_WARN("ucb.ucp.webdav.curl",
+ "curl_multi_perform failed: " << GetErrorStringMulti(mc));
+ throw DAVException(
+ DAVException::DAV_HTTP_CONNECT,
+ ConnectionEndPointString(rSession.m_URI.GetHost(), rSession.m_URI.GetPort()));
+ }
+ if (nRunning == 0)
+ { // short request like HEAD on loopback could be done in first call
+ break;
+ }
+ int nFDs;
+ mc = curl_multi_poll(rSession.m_pCurlMulti.get(), nullptr, 0, rSession.m_nReadTimeout,
+ &nFDs);
+ if (mc != CURLM_OK)
+ {
+ SAL_WARN("ucb.ucp.webdav.curl", "curl_multi_poll failed: " << GetErrorStringMulti(mc));
+ throw DAVException(
+ DAVException::DAV_HTTP_CONNECT,
+ ConnectionEndPointString(rSession.m_URI.GetHost(), rSession.m_URI.GetPort()));
+ }
+ if (rSession.m_AbortFlag.load())
+ { // flag was set by abort() -> not sure what exception to throw?
+ throw DAVException(DAVException::DAV_HTTP_ERROR, "abort() was called", 0);
+ }
+ } while (nRunning != 0);
+ // there should be exactly 1 CURLMsg now, but the interface is
+ // extensible so future libcurl versions could yield additional things
+ do
+ {
+ CURLMsg const* const pMsg = curl_multi_info_read(rSession.m_pCurlMulti.get(), &nRunning);
+ if (pMsg && pMsg->msg == CURLMSG_DONE)
+ {
+ assert(pMsg->easy_handle == rSession.m_pCurl.get());
+ rc = pMsg->data.result;
+ }
+ else
+ {
+ SAL_WARN("ucb.ucp.webdav.curl", "curl_multi_info_read unexpected result");
+ }
+ } while (nRunning != 0);
+
+ // error handling part 1: libcurl errors
+ if (rc != CURLE_OK)
+ {
+ // TODO: is there any value in extracting CURLINFO_OS_ERRNO
+ SAL_WARN("ucb.ucp.webdav.curl",
+ "curl_easy_perform failed: " << GetErrorString(rc, rSession.m_ErrorBuffer));
+ switch (rc)
+ {
+ case CURLE_COULDNT_RESOLVE_PROXY:
+ throw DAVException(
+ DAVException::DAV_HTTP_LOOKUP,
+ ConnectionEndPointString(rSession.m_Proxy.aName, rSession.m_Proxy.nPort));
+ case CURLE_COULDNT_RESOLVE_HOST:
+ throw DAVException(
+ DAVException::DAV_HTTP_LOOKUP,
+ ConnectionEndPointString(rSession.m_URI.GetHost(), rSession.m_URI.GetPort()));
+ case CURLE_COULDNT_CONNECT:
+ case CURLE_SSL_CONNECT_ERROR:
+ case CURLE_SSL_CERTPROBLEM:
+ case CURLE_SSL_CIPHER:
+ case CURLE_PEER_FAILED_VERIFICATION:
+ case CURLE_SSL_ISSUER_ERROR:
+ case CURLE_SSL_PINNEDPUBKEYNOTMATCH:
+ case CURLE_SSL_INVALIDCERTSTATUS:
+ case CURLE_QUIC_CONNECT_ERROR:
+ throw DAVException(
+ DAVException::DAV_HTTP_CONNECT,
+ ConnectionEndPointString(rSession.m_URI.GetHost(), rSession.m_URI.GetPort()));
+ case CURLE_REMOTE_ACCESS_DENIED:
+ case CURLE_LOGIN_DENIED:
+ case CURLE_AUTH_ERROR:
+ throw DAVException(
+ DAVException::DAV_HTTP_AUTH, // probably?
+ ConnectionEndPointString(rSession.m_URI.GetHost(), rSession.m_URI.GetPort()));
+ case CURLE_WRITE_ERROR:
+ case CURLE_READ_ERROR: // error returned from our callbacks
+ case CURLE_OUT_OF_MEMORY:
+ case CURLE_ABORTED_BY_CALLBACK:
+ case CURLE_BAD_FUNCTION_ARGUMENT:
+ case CURLE_SEND_ERROR:
+ case CURLE_RECV_ERROR:
+ case CURLE_SSL_CACERT_BADFILE:
+ case CURLE_SSL_CRL_BADFILE:
+ case CURLE_RECURSIVE_API_CALL:
+ throw DAVException(
+ DAVException::DAV_HTTP_FAILED,
+ ConnectionEndPointString(rSession.m_URI.GetHost(), rSession.m_URI.GetPort()));
+ case CURLE_OPERATION_TIMEDOUT:
+ throw DAVException(
+ DAVException::DAV_HTTP_TIMEOUT,
+ ConnectionEndPointString(rSession.m_URI.GetHost(), rSession.m_URI.GetPort()));
+ default: // lots of generic errors
+ throw DAVException(DAVException::DAV_HTTP_ERROR, "", 0);
+ }
+ }
+ // error handling part 2: HTTP status codes
+ long statusCode(SC_NONE);
+ rc = curl_easy_getinfo(rSession.m_pCurl.get(), CURLINFO_RESPONSE_CODE, &statusCode);
+ assert(rc == CURLE_OK);
+ assert(statusCode != SC_NONE); // ??? should be error returned from perform?
+ SAL_INFO("ucb.ucp.webdav.curl", "HTTP status code: " << statusCode);
+ if (statusCode < 300)
+ {
+ // neon did this regardless of status or even error, which seems odd
+ ExtractRequestedHeaders(rHeaders, pRequestedHeaders);
+ }
+ else
+ {
+ switch (statusCode)
+ {
+ case SC_NONE:
+ assert(false); // ??? should be error returned from perform?
+ break;
+ case SC_REQUEST_TIMEOUT:
+ {
+ throw DAVException(
+ DAVException::DAV_HTTP_TIMEOUT,
+ ConnectionEndPointString(rSession.m_URI.GetHost(), rSession.m_URI.GetPort()));
+ break;
+ }
+ case SC_MOVED_PERMANENTLY:
+ case SC_MOVED_TEMPORARILY:
+ case SC_SEE_OTHER:
+ case SC_TEMPORARY_REDIRECT:
+ {
+ // could also use CURLOPT_FOLLOWLOCATION but apparently the
+ // upper layer wants to know about redirects?
+ char* pRedirectURL(nullptr);
+ rc = curl_easy_getinfo(rSession.m_pCurl.get(), CURLINFO_REDIRECT_URL,
+ &pRedirectURL);
+ assert(rc == CURLE_OK);
+ if (pRedirectURL)
+ {
+ throw DAVException(DAVException::DAV_HTTP_REDIRECT,
+ pRedirectURL ? OUString(pRedirectURL, strlen(pRedirectURL),
+ RTL_TEXTENCODING_UTF8)
+ : OUString());
+ }
+ [[fallthrough]];
+ }
+ default:
+ throw DAVException(DAVException::DAV_HTTP_ERROR, "", statusCode);
+ }
+ }
+
+ if (pxOutStream)
+ {
+ (*pxOutStream)->closeOutput(); // signal EOF
+ }
+}
+
+static auto TryRemoveExpiredLockToken(CurlSession& rSession, CurlUri const& rURI,
+ DAVRequestEnvironment const* const pEnv) -> bool
+{
+ if (!pEnv)
+ {
+ // caller was a NonInteractive_*LOCK function anyway, its caller is LockStore
+ return false;
+ }
+ OUString const* const pToken(g_Init.LockStore.getLockTokenForURI(rURI.GetURI(), nullptr));
+ if (!pToken)
+ {
+ return false;
+ }
+ try
+ {
+ // determine validity of existing lock via lockdiscovery request
+ ::std::vector<OUString> const propertyNames{ DAVProperties::LOCKDISCOVERY };
+ ::std::vector<ucb::Lock> locks;
+ ::std::tuple<::std::vector<OUString> const&, ::std::vector<DAVResource>* const,
+ ::std::vector<ucb::Lock>* const> const args(propertyNames, nullptr, &locks);
+
+ CurlProcessor::PropFind(rSession, rURI, DAVZERO, &args, nullptr, *pEnv);
+
+ // https://datatracker.ietf.org/doc/html/rfc4918#section-15.8
+ // The response MAY not contain tokens, but hopefully it
+ // will if client is properly authenticated.
+ if (::std::any_of(locks.begin(), locks.end(), [pToken](ucb::Lock const& rLock) {
+ return ::std::any_of(
+ rLock.LockTokens.begin(), rLock.LockTokens.end(),
+ [pToken](OUString const& rToken) { return *pToken == rToken; });
+ }))
+ {
+ return false; // still have the lock
+ }
+
+ SAL_INFO("ucb.ucp.webdav.curl",
+ "lock token expired, removing: " << rURI.GetURI() << " " << *pToken);
+ g_Init.LockStore.removeLock(rURI.GetURI());
+ return true;
+ }
+ catch (DAVException const&)
+ {
+ return false; // ignore, the caller already has a better exception
+ }
+}
+
+auto CurlProcessor::ProcessRequest(
+ CurlSession& rSession, CurlUri const& rURI, ::std::vector<CurlOption> const& rOptions,
+ DAVRequestEnvironment const* const pEnv,
+ ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist_free_all>> pRequestHeaderList,
+ uno::Reference<io::XOutputStream> const* const pxOutStream,
+ uno::Reference<io::XInputStream> const* const pxInStream,
+ ::std::pair<::std::vector<OUString> const&, DAVResource&> const* const pRequestedHeaders)
+ -> void
+{
+ if (pEnv)
+ { // add custom request headers passed by caller
+ for (auto const& rHeader : pEnv->m_aRequestHeaders)
+ {
+ OString const utf8Header(
+ OUStringToOString(rHeader.first, RTL_TEXTENCODING_ASCII_US) + ": "
+ + OUStringToOString(rHeader.second, RTL_TEXTENCODING_ASCII_US));
+ pRequestHeaderList.reset(
+ curl_slist_append(pRequestHeaderList.release(), utf8Header.getStr()));
+ if (!pRequestHeaderList)
+ {
+ throw uno::RuntimeException("curl_slist_append failed");
+ }
+ }
+ }
+
+ // Clear flag before transfer starts; only a transfer started before
+ // calling abort() will be aborted, not one started later.
+ rSession.m_AbortFlag.store(false);
+
+ Guard guard(rSession.m_Mutex, rOptions, rURI, rSession.m_pCurl.get());
+
// authentication data may be in the URI, or requested via XInteractionHandler
- // also
struct Auth
{
OUString UserName;
@@ -783,11 +1201,13 @@ auto CurlProcessor::ProcessRequestImpl(
{
isRetry = false;
- if (oAuth)
+ // re-check m_isAuthenticated flags every time, could have been set
+ // by re-entrant call
+ if (oAuth && !rSession.m_isAuthenticated)
{
- assert(!rSession.m_isAuthenticated);
OString const utf8UserName(OUStringToOString(oAuth->UserName, RTL_TEXTENCODING_UTF8));
- rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_USERNAME, utf8UserName.getStr());
+ auto rc
+ = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_USERNAME, utf8UserName.getStr());
if (rc != CURLE_OK)
{
SAL_WARN("ucb.ucp.webdav.curl", "CURLOPT_USERNAME failed: " << GetErrorString(rc));
@@ -806,13 +1226,12 @@ auto CurlProcessor::ProcessRequestImpl(
== CURLE_OK); // it shouldn't be possible to reduce auth to 0 via the authSystem masks
}
- if (oAuthProxy)
+ if (oAuthProxy && !rSession.m_isAuthenticatedProxy)
{
- assert(!rSession.m_isAuthenticatedProxy);
OString const utf8UserName(
OUStringToOString(oAuthProxy->UserName, RTL_TEXTENCODING_UTF8));
- rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_PROXYUSERNAME,
- utf8UserName.getStr());
+ auto rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_PROXYUSERNAME,
+ utf8UserName.getStr());
if (rc != CURLE_OK)
{
SAL_WARN("ucb.ucp.webdav.curl",
@@ -836,249 +1255,109 @@ auto CurlProcessor::ProcessRequestImpl(
}
ResponseHeaders headers(rSession.m_pCurl.get());
- rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_HEADERDATA, &headers);
- assert(rc == CURLE_OK);
- ::std::optional<DownloadTarget> oDownloadTarget;
- if (pxOutStream)
- {
- oDownloadTarget.emplace(*pxOutStream, headers);
- rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_WRITEDATA, &*oDownloadTarget);
- assert(rc == CURLE_OK);
- }
- ::std::optional<UploadSource> oUploadSource;
- if (pxInStream)
- {
- oUploadSource.emplace(*pxInStream, headers);
- rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_READDATA, &*oUploadSource);
- assert(rc == CURLE_OK);
- // libcurl won't upload without setting this
- rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_UPLOAD, 1L);
- assert(rc == CURLE_OK);
- }
- rSession.m_ErrorBuffer[0] = '\0';
- // note: easy handle must be added for *every* transfer!
- // otherwise it gets stuck in MSTATE_MSGSENT forever after 1st transfer
- auto mc = curl_multi_add_handle(rSession.m_pCurlMulti.get(), rSession.m_pCurl.get());
- if (mc != CURLM_OK)
- {
- SAL_WARN("ucb.ucp.webdav.curl",
- "curl_multi_add_handle failed: " << GetErrorStringMulti(mc));
- throw DAVException(
- DAVException::DAV_SESSION_CREATE,
- ConnectionEndPointString(rSession.m_URI.GetHost(), rSession.m_URI.GetPort()));
- }
- ::comphelper::ScopeGuard const gg([&]() {
- mc = curl_multi_remove_handle(rSession.m_pCurlMulti.get(), rSession.m_pCurl.get());
- if (mc != CURLM_OK)
- {
- SAL_WARN("ucb.ucp.webdav.curl",
- "curl_multi_remove_handle failed: " << GetErrorStringMulti(mc));
- }
- });
-
- // this is where libcurl actually does something
- rc = CURL_LAST; // clear current value
- int nRunning;
- do
- {
- mc = curl_multi_perform(rSession.m_pCurlMulti.get(), &nRunning);
- if (mc != CURLM_OK)
- {
- SAL_WARN("ucb.ucp.webdav.curl",
- "curl_multi_perform failed: " << GetErrorStringMulti(mc));
- throw DAVException(
- DAVException::DAV_HTTP_CONNECT,
- ConnectionEndPointString(rSession.m_URI.GetHost(), rSession.m_URI.GetPort()));
- }
- if (nRunning == 0)
- { // short request like HEAD on loopback could be done in first call
- break;
- }
- int nFDs;
- mc = curl_multi_poll(rSession.m_pCurlMulti.get(), nullptr, 0, rSession.m_nReadTimeout,
- &nFDs);
- if (mc != CURLM_OK)
- {
- SAL_WARN("ucb.ucp.webdav.curl",
- "curl_multi_poll failed: " << GetErrorStringMulti(mc));
- throw DAVException(
- DAVException::DAV_HTTP_CONNECT,
- ConnectionEndPointString(rSession.m_URI.GetHost(), rSession.m_URI.GetPort()));
- }
- if (rSession.m_AbortFlag.load())
- { // flag was set by abort() -> not sure what exception to throw?
- throw DAVException(DAVException::DAV_HTTP_ERROR, "abort() was called", 0);
- }
- } while (nRunning != 0);
- // there should be exactly 1 CURLMsg now, but the interface is
- // extensible so future libcurl versions could yield additional things
- do
- {
- CURLMsg const* const pMsg
- = curl_multi_info_read(rSession.m_pCurlMulti.get(), &nRunning);
- if (pMsg && pMsg->msg == CURLMSG_DONE)
- {
- assert(pMsg->easy_handle == rSession.m_pCurl.get());
- rc = pMsg->data.result;
- }
- else
- {
- SAL_WARN("ucb.ucp.webdav.curl", "curl_multi_info_read unexpected result");
- }
- } while (nRunning != 0);
-
- // error handling part 1: libcurl errors
- if (rc != CURLE_OK)
- {
- // TODO: is there any value in extracting CURLINFO_OS_ERRNO
- SAL_WARN("ucb.ucp.webdav.curl",
- "curl_easy_perform failed: " << GetErrorString(rc, rSession.m_ErrorBuffer));
- switch (rc)
- {
- case CURLE_COULDNT_RESOLVE_PROXY:
- throw DAVException(
- DAVException::DAV_HTTP_LOOKUP,
- ConnectionEndPointString(rSession.m_Proxy.aName, rSession.m_Proxy.nPort));
- case CURLE_COULDNT_RESOLVE_HOST:
- throw DAVException(DAVException::DAV_HTTP_LOOKUP,
- ConnectionEndPointString(rSession.m_URI.GetHost(),
- rSession.m_URI.GetPort()));
- case CURLE_COULDNT_CONNECT:
- case CURLE_SSL_CONNECT_ERROR:
- case CURLE_SSL_CERTPROBLEM:
- case CURLE_SSL_CIPHER:
- case CURLE_PEER_FAILED_VERIFICATION:
- case CURLE_SSL_ISSUER_ERROR:
- case CURLE_SSL_PINNEDPUBKEYNOTMATCH:
- case CURLE_SSL_INVALIDCERTSTATUS:
- case CURLE_QUIC_CONNECT_ERROR:
- throw DAVException(DAVException::DAV_HTTP_CONNECT,
- ConnectionEndPointString(rSession.m_URI.GetHost(),
- rSession.m_URI.GetPort()));
- case CURLE_REMOTE_ACCESS_DENIED:
- case CURLE_LOGIN_DENIED:
- case CURLE_AUTH_ERROR:
- throw DAVException(DAVException::DAV_HTTP_AUTH, // probably?
- ConnectionEndPointString(rSession.m_URI.GetHost(),
- rSession.m_URI.GetPort()));
- case CURLE_WRITE_ERROR:
- case CURLE_READ_ERROR: // error returned from our callbacks
- case CURLE_OUT_OF_MEMORY:
- case CURLE_ABORTED_BY_CALLBACK:
- case CURLE_BAD_FUNCTION_ARGUMENT:
- case CURLE_SEND_ERROR:
- case CURLE_RECV_ERROR:
- case CURLE_SSL_CACERT_BADFILE:
- case CURLE_SSL_CRL_BADFILE:
- case CURLE_RECURSIVE_API_CALL:
- throw DAVException(DAVException::DAV_HTTP_FAILED,
- ConnectionEndPointString(rSession.m_URI.GetHost(),
- rSession.m_URI.GetPort()));
- case CURLE_OPERATION_TIMEDOUT:
- throw DAVException(DAVException::DAV_HTTP_TIMEOUT,
- ConnectionEndPointString(rSession.m_URI.GetHost(),
- rSession.m_URI.GetPort()));
- default: // lots of generic errors
- throw DAVException(DAVException::DAV_HTTP_ERROR, "", 0);
- }
- }
- // error handling part 2: HTTP status codes
- long statusCode(SC_NONE);
- rc = curl_easy_getinfo(rSession.m_pCurl.get(), CURLINFO_RESPONSE_CODE, &statusCode);
- assert(rc == CURLE_OK);
- assert(statusCode != SC_NONE); // ??? should be error returned from perform?
- SAL_INFO("ucb.ucp.webdav.curl", "HTTP status code: " << statusCode);
- if (statusCode < 300)
+ try
{
- // neon did this regardless of status or even error, which seems odd
- ExtractRequestedHeaders(headers, pRequestedHeaders);
+ ProcessRequestImpl(rSession, rURI, pRequestHeaderList.get(), pxOutStream, pxInStream,
+ pRequestedHeaders, headers);
}
- else
+ catch (DAVException const& rException)
{
- switch (statusCode)
+ // error handling part 3: special HTTP status codes
+ // that require unlocking m_Mutex to handle
+ if (rException.getError() == DAVException::DAV_HTTP_ERROR)
{
- case SC_NONE:
- assert(false); // ??? should be error returned from perform?
- break;
- case SC_UNAUTHORIZED:
- case SC_PROXY_AUTHENTICATION_REQUIRED:
+ auto const statusCode(rException.getStatus());
+ switch (statusCode)
{
- if (pEnv && pEnv->m_xAuthListener)
+ case SC_LOCKED:
{
- ::std::optional<OUString> const oRealm(ExtractRealm(
- headers, statusCode == SC_UNAUTHORIZED ? "WWW-Authenticate"
- : "Proxy-Authenticate"));
-
- ::std::optional<Auth>& roAuth(statusCode == SC_UNAUTHORIZED ? oAuth
- : oAuthProxy);
- OUString userName(roAuth ? roAuth->UserName : OUString());
- OUString passWord(roAuth ? roAuth->PassWord : OUString());
- long authAvail(0);
- rc = curl_easy_getinfo(rSession.m_pCurl.get(),
- statusCode == SC_UNAUTHORIZED
- ? CURLINFO_HTTPAUTH_AVAIL
- : CURLINFO_PROXYAUTH_AVAIL,
- &authAvail);
- assert(rc == CURLE_OK);
- bool const isSystemCredSupported((authAvail & authSystem) != 0);
-
- // ask user via XInteractionHandler
- auto ret = pEnv->m_xAuthListener->authenticate(
- oRealm ? *oRealm : "",
- //statusCode == SC_UNAUTHORIZED ? uri.GetHost() : rSession.m_Proxy.aName,
- statusCode == SC_UNAUTHORIZED ? rSession.m_URI.GetHost()
- : rSession.m_Proxy.aName,
- userName, passWord, isSystemCredSupported);
-
- if (ret == 0)
+ guard.Release(); // release m_Mutex before accessing LockStore
+ if (g_Init.LockStore.getLockTokenForURI(rURI.GetURI(), nullptr))
+ {
+ throw DAVException(DAVException::DAV_LOCKED_SELF);
+ }
+ else // locked by third party
{
- roAuth.emplace(userName, passWord,
- authAvail
- & ((userName.isEmpty() && passWord.isEmpty())
- ? authSystem
- : ~authSystem));
- isRetry = true;
- break; // break out of switch
+ throw DAVException(DAVException::DAV_LOCKED);
}
- // else: throw
+ break;
}
- SAL_INFO("ucb.ucp.webdav.curl", "no auth credentials provided");
- throw DAVException(DAVException::DAV_HTTP_NOAUTH,
- ConnectionEndPointString(rSession.m_URI.GetHost(),
- rSession.m_URI.GetPort()));
- break;
- }
- case SC_REQUEST_TIMEOUT:
- {
- throw DAVException(DAVException::DAV_HTTP_TIMEOUT,
- ConnectionEndPointString(rSession.m_URI.GetHost(),
- rSession.m_URI.GetPort()));
- break;
- }
- case SC_MOVED_PERMANENTLY:
- case SC_MOVED_TEMPORARILY:
- case SC_SEE_OTHER:
- case SC_TEMPORARY_REDIRECT:
- {
- // could also use CURLOPT_FOLLOWLOCATION but apparently the
- // upper layer wants to know about redirects?
- char* pRedirectURL(nullptr);
- rc = curl_easy_getinfo(rSession.m_pCurl.get(), CURLINFO_REDIRECT_URL,
- &pRedirectURL);
- assert(rc == CURLE_OK);
- if (pRedirectURL)
+ case SC_PRECONDITION_FAILED:
+ case SC_BAD_REQUEST:
{
- throw DAVException(DAVException::DAV_HTTP_REDIRECT,
- pRedirectURL
- ? OUString(pRedirectURL, strlen(pRedirectURL),
- RTL_TEXTENCODING_UTF8)
- : OUString());
+ guard.Release(); // release m_Mutex before accessing LockStore
+ // Not obvious but apparently these codes may indicate
+ // the expiration of a lock.
+ // Initiate a new request *outside* ProcessRequestImpl
+ // *after* rGuard.unlock() to avoid messing up m_pCurl state.
+ if (TryRemoveExpiredLockToken(rSession, rURI, pEnv))
+ {
+ throw DAVException(DAVException::DAV_LOCK_EXPIRED);
+ }
+ break;
+ }
+ case SC_UNAUTHORIZED:
+ case SC_PROXY_AUTHENTICATION_REQUIRED:
+ {
+ if (pEnv && pEnv->m_xAuthListener)
+ {
+ ::std::optional<OUString> const oRealm(ExtractRealm(
+ headers, statusCode == SC_UNAUTHORIZED ? "WWW-Authenticate"
+ : "Proxy-Authenticate"));
+
+ ::std::optional<Auth>& roAuth(
+ statusCode == SC_UNAUTHORIZED ? oAuth : oAuthProxy);
+ OUString userName(roAuth ? roAuth->UserName : OUString());
+ OUString passWord(roAuth ? roAuth->PassWord : OUString());
+ long authAvail(0);
+ auto const rc = curl_easy_getinfo(rSession.m_pCurl.get(),
+ statusCode == SC_UNAUTHORIZED
+ ? CURLINFO_HTTPAUTH_AVAIL
+ : CURLINFO_PROXYAUTH_AVAIL,
+ &authAvail);
+ assert(rc == CURLE_OK);
+ (void)rc;
+ bool const isSystemCredSupported((authAvail & authSystem) != 0);
+
+ // Ask user via XInteractionHandler.
+ // Warning: This likely runs an event loop which may
+ // end up calling back into this instance, so all
+ // changes to m_pCurl must be undone now and
+ // restored after return.
+ guard.Release();
+
+ auto const ret = pEnv->m_xAuthListener->authenticate(
+ oRealm ? *oRealm : "",
+ statusCode == SC_UNAUTHORIZED ? rSession.m_URI.GetHost()
+ : rSession.m_Proxy.aName,
+ userName, passWord, isSystemCredSupported);
+
+ if (ret == 0)
+ {
+ roAuth.emplace(userName, passWord,
+ authAvail
+ & ((userName.isEmpty() && passWord.isEmpty())
+ ? authSystem
+ : ~authSystem));
+ isRetry = true;
+ // Acquire is only necessary in case of success.
+ guard.Acquire();
+ break; // break out of switch
+ }
+ // else: throw
+ }
+ SAL_INFO("ucb.ucp.webdav.curl", "no auth credentials provided");
+ throw DAVException(DAVException::DAV_HTTP_NOAUTH,
+ ConnectionEndPointString(rSession.m_URI.GetHost(),
+ rSession.m_URI.GetPort()));
+ break;
}
- [[fallthrough]];
}
- default:
- throw DAVException(DAVException::DAV_HTTP_ERROR, "", statusCode);
+ }
+ if (!isRetry)
+ {
+ throw; // everything else: re-throw
}
}
} while (isRetry);
@@ -1093,111 +1372,6 @@ auto CurlProcessor::ProcessRequestImpl(
// assume this worked, leave auth data as stored in m_pCurl
rSession.m_isAuthenticatedProxy = true;
}
-
- if (pxOutStream)
- {
- (*pxOutStream)->closeOutput(); // signal EOF
- }
-}
-
-static auto TryRemoveExpiredLockToken(CurlSession& rSession, CurlUri const& rURI,
- DAVRequestEnvironment const* const pEnv) -> bool
-{
- if (!pEnv)
- {
- // caller was a NonInteractive_*LOCK function anyway, its caller is LockStore
- return false;
- }
- OUString const* const pToken(g_Init.LockStore.getLockTokenForURI(rURI.GetURI(), nullptr));
- if (!pToken)
- {
- return false;
- }
- try
- {
- // determine validity of existing lock via lockdiscovery request
- ::std::vector<OUString> const propertyNames{ DAVProperties::LOCKDISCOVERY };
- ::std::vector<ucb::Lock> locks;
- ::std::tuple<::std::vector<OUString> const&, ::std::vector<DAVResource>* const,
- ::std::vector<ucb::Lock>* const> const args(propertyNames, nullptr, &locks);
-
- CurlProcessor::PropFind(rSession, rURI, DAVZERO, &args, nullptr, *pEnv);
-
- // https://datatracker.ietf.org/doc/html/rfc4918#section-15.8
- // The response MAY not contain tokens, but hopefully it
- // will if client is properly authenticated.
- if (::std::any_of(locks.begin(), locks.end(), [pToken](ucb::Lock const& rLock) {
- return ::std::any_of(
- rLock.LockTokens.begin(), rLock.LockTokens.end(),
- [pToken](OUString const& rToken) { return *pToken == rToken; });
- }))
- {
- return false; // still have the lock
- }
-
- SAL_INFO("ucb.ucp.webdav.curl",
- "lock token expired, removing: " << rURI.GetURI() << " " << *pToken);
- g_Init.LockStore.removeLock(rURI.GetURI());
- return true;
- }
- catch (DAVException const&)
- {
- return false; // ignore, the caller already has a better exception
- }
-}
-
-auto CurlProcessor::ProcessRequest(
- Guard& rGuard, CurlSession& rSession, CurlUri const& rURI,
- DAVRequestEnvironment const* const pEnv,
- ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist_free_all>> pRequestHeaderList,
- uno::Reference<io::XOutputStream> const* const pxOutStream,
- uno::Reference<io::XInputStream> const* const pxInStream,
- ::std::pair<::std::vector<OUString> const&, DAVResource&> const* const pRequestedHeaders)
- -> void
-{
- try
- {
- ProcessRequestImpl(rSession, rURI, pEnv, ::std::move(pRequestHeaderList), pxOutStream,
- pxInStream, pRequestedHeaders);
- }
- catch (DAVException const& rException)
- {
- // error handling part 3: special HTTP status codes
- if (rException.getError() == DAVException::DAV_HTTP_ERROR)
- {
- switch (rException.getStatus())
- {
- case SC_LOCKED:
- {
- rGuard.unlock(); // release m_Mutex before accessing LockStore
- if (g_Init.LockStore.getLockTokenForURI(rURI.GetURI(), nullptr))
- {
- throw DAVException(DAVException::DAV_LOCKED_SELF);
- }
- else // locked by third party
- {
- throw DAVException(DAVException::DAV_LOCKED);
- }
- break;
- }
- case SC_PRECONDITION_FAILED:
- case SC_BAD_REQUEST:
- {
- rGuard.unlock(); // release m_Mutex before accessing LockStore
- // Not obvious but apparently these codes may indicate
- // the expiration of a lock.
- // Initiate a new request *outside* ProcessRequestImpl
- // *after* rGuard.unlock() to avoid messing up m_pCurl state.
- if (TryRemoveExpiredLockToken(rSession, rURI, pEnv))
- {
- throw DAVException(DAVException::DAV_LOCK_EXPIRED);
- }
- break;
- }
- }
- }
- throw; // everything else: re-throw
- }
}
auto CurlSession::OPTIONS(OUString const& rURIReference,
@@ -1214,29 +1388,11 @@ auto CurlSession::OPTIONS(OUString const& rURIReference,
DAVResource result;
::std::pair<::std::vector<OUString> const&, DAVResource&> const headers(headerNames, result);
- {
- // scope to release m_Mutex before accessing LockStore
- Guard g(m_Mutex, [&]() {
- auto rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_NOBODY, 0L);
- assert(rc == CURLE_OK);
- (void)rc;
- rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_CUSTOMREQUEST, nullptr);
- assert(rc == CURLE_OK);
- });
+ ::std::vector<CurlOption> const options{ { CURLOPT_NOBODY, 1L, nullptr },
+ { CURLOPT_CUSTOMREQUEST, "OPTIONS",
+ "CURLOPT_CUSTOMREQUEST" } };
- // need this to prevent calling write_callback
- auto rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_NOBODY, 1L);
- assert(rc == CURLE_OK);
- rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_CUSTOMREQUEST, "OPTIONS");
- if (rc != CURLE_OK)
- {
- SAL_WARN("ucb.ucp.webdav.curl", "CURLOPT_CUSTOMREQUEST failed: " << GetErrorString(rc));
- throw DAVException(DAVException::DAV_SESSION_CREATE,
- ConnectionEndPointString(m_URI.GetHost(), m_URI.GetPort()));
- }
-
- CurlProcessor::ProcessRequest(g, *this, uri, &rEnv, nullptr, nullptr, nullptr, &headers);
- }
+ CurlProcessor::ProcessRequest(*this, uri, options, &rEnv, nullptr, nullptr, nullptr, &headers);
for (auto const& it : result.properties)
{
@@ -1326,20 +1482,8 @@ auto CurlProcessor::PropFind(
throw uno::RuntimeException("curl_slist_append failed");
}
- Guard g(rSession.m_Mutex, [&]() {
- auto rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_CUSTOMREQUEST, nullptr);
- assert(rc == CURLE_OK);
- (void)rc;
- });
-
- auto rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_CUSTOMREQUEST, "PROPFIND");
- if (rc != CURLE_OK)
- {
- SAL_WARN("ucb.ucp.webdav.curl", "CURLOPT_CUSTOMREQUEST failed: " << GetErrorString(rc));
- throw DAVException(
- DAVException::DAV_SESSION_CREATE,
- ConnectionEndPointString(rSession.m_URI.GetHost(), rSession.m_URI.GetPort()));
- }
+ ::std::vector<CurlOption> const options{ { CURLOPT_CUSTOMREQUEST, "PROPFIND",
+ "CURLOPT_CUSTOMREQUEST" } };
uno::Reference<io::XInputStream> const xRequestInStream(io::Pipe::create(rSession.m_xContext));
uno::Reference<io::XOutputStream> const xRequestOutStream(xRequestInStream, uno::UNO_QUERY);
@@ -1388,8 +1532,8 @@ auto CurlProcessor::PropFind(
assert(xResponseInStream.is());
assert(xResponseOutStream.is());
- CurlProcessor::ProcessRequest(g, rSession, rURI, &rEnv, ::std::move(pList), &xResponseOutStream,
- &xRequestInStream, nullptr);
+ CurlProcessor::ProcessRequest(rSession, rURI, options, &rEnv, ::std::move(pList),
+ &xResponseOutStream, &xRequestInStream, nullptr);
if (o_pResourceInfos)
{
@@ -1473,19 +1617,8 @@ auto CurlSession::PROPPATCH(OUString const& rURIReference,
throw uno::RuntimeException("curl_slist_append failed");
}
- Guard g(m_Mutex, [&]() {
- auto rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_CUSTOMREQUEST, nullptr);
- assert(rc == CURLE_OK);
- (void)rc;
- });
-
- auto rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_CUSTOMREQUEST, "PROPPATCH");
- if (rc != CURLE_OK)
- {
- SAL_WARN("ucb.ucp.webdav.curl", "CURLOPT_CUSTOMREQUEST failed: " << GetErrorString(rc));
- throw DAVException(DAVException::DAV_SESSION_CREATE,
- ConnectionEndPointString(m_URI.GetHost(), m_URI.GetPort()));
- }
+ ::std::vector<CurlOption> const options{ { CURLOPT_CUSTOMREQUEST, "PROPPATCH",
+ "CURLOPT_CUSTOMREQUEST" } };
// generate XML document for PROPPATCH
uno::Reference<io::XInputStream> const xRequestInStream(io::Pipe::create(m_xContext));
@@ -1543,7 +1676,7 @@ auto CurlSession::PROPPATCH(OUString const& rURIReference,
xWriter->endDocument();
xRequestOutStream->closeOutput();
- CurlProcessor::ProcessRequest(g, *this, uri, &rEnv, ::std::move(pList), nullptr,
+ CurlProcessor::ProcessRequest(*this, uri, options, &rEnv, ::std::move(pList), nullptr,
&xRequestInStream, nullptr);
}
@@ -1554,19 +1687,12 @@ auto CurlSession::HEAD(OUString const& rURIReference, ::std::vector<OUString> co
CurlUri const uri(CurlProcessor::URIReferenceToURI(*this, rURIReference));
- Guard g(m_Mutex, [&]() {
- auto rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_NOBODY, 0L);
- assert(rc == CURLE_OK);
- (void)rc;
- });
- auto rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_NOBODY, 1L);
- assert(rc == CURLE_OK);
- (void)rc;
+ ::std::vector<CurlOption> const options{ { CURLOPT_NOBODY, 1L, nullptr } };
::std::pair<::std::vector<OUString> const&, DAVResource&> const headers(rHeaderNames,
io_rResource);
- CurlProcessor::ProcessRequest(g, *this, uri, &rEnv, nullptr, nullptr, nullptr, &headers);
+ CurlProcessor::ProcessRequest(*this, uri, options, &rEnv, nullptr, nullptr, nullptr, &headers);
}
auto CurlSession::GET(OUString const& rURIReference, DAVRequestEnvironment const& rEnv)
@@ -1586,16 +1712,9 @@ auto CurlSession::GET(OUString const& rURIReference, DAVRequestEnvironment const
uno::Reference<io::XOutputStream> const xResponseOutStream(xSeqOutStream);
assert(xResponseOutStream.is());
- Guard g(m_Mutex, [&]() {
- auto rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_HTTPGET, 0L);
- assert(rc == CURLE_OK);
- (void)rc;
- });
- auto rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_HTTPGET, 1L);
- assert(rc == CURLE_OK);
- (void)rc;
+ ::std::vector<CurlOption> const options{ { CURLOPT_HTTPGET, 1L, nullptr } };
- CurlProcessor::ProcessRequest(g, *this, uri, &rEnv, nullptr, &xResponseOutStream, nullptr,
+ CurlProcessor::ProcessRequest(*this, uri, options, &rEnv, nullptr, &xResponseOutStream, nullptr,
nullptr);
uno::Reference<io::XInputStream> const xResponseInStream(
@@ -1613,16 +1732,10 @@ auto CurlSession::GET(OUString const& rURIReference, uno::Reference<io::XOutputS
CurlUri const uri(CurlProcessor::URIReferenceToURI(*this, rURIReference));
- Guard g(m_Mutex, [&]() {
- auto rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_HTTPGET, 0L);
- assert(rc == CURLE_OK);
- (void)rc;
- });
- auto rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_HTTPGET, 1L);
- assert(rc == CURLE_OK);
- (void)rc;
+ ::std::vector<CurlOption> const options{ { CURLOPT_HTTPGET, 1L, nullptr } };
- CurlProcessor::ProcessRequest(g, *this, uri, &rEnv, nullptr, &rxOutStream, nullptr, nullptr);
+ CurlProcessor::ProcessRequest(*this, uri, options, &rEnv, nullptr, &rxOutStream, nullptr,
+ nullptr);
}
auto CurlSession::GET(OUString const& rURIReference, ::std::vector<OUString> const& rHeaderNames,
@@ -1633,14 +1746,7 @@ auto CurlSession::GET(OUString const& rURIReference, ::std::vector<OUString> con
CurlUri const uri(CurlProcessor::URIReferenceToURI(*this, rURIReference));
- Guard g(m_Mutex, [&]() {
- auto rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_HTTPGET, 0L);
- assert(rc == CURLE_OK);
- (void)rc;
- });
- auto rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_HTTPGET, 1L);
- assert(rc == CURLE_OK);
- (void)rc;
+ ::std::vector<CurlOption> const options{ { CURLOPT_HTTPGET, 1L, nullptr } };
uno::Reference<io::XSequenceOutputStream> const xSeqOutStream(
io::SequenceOutputStream::create(m_xContext));
@@ -1650,7 +1756,7 @@ auto CurlSession::GET(OUString const& rURIReference, ::std::vector<OUString> con
::std::pair<::std::vector<OUString> const&, DAVResource&> const headers(rHeaderNames,
io_rResource);
- CurlProcessor::ProcessRequest(g, *this, uri, &rEnv, nullptr, &xResponseOutStream, nullptr,
+ CurlProcessor::ProcessRequest(*this, uri, options, &rEnv, nullptr, &xResponseOutStream, nullptr,
&headers);
uno::Reference<io::XInputStream> const xResponseInStream(
@@ -1669,19 +1775,13 @@ auto CurlSession::GET(OUString const& rURIReference, uno::Reference<io::XOutputS
CurlUri const uri(CurlProcessor::URIReferenceToURI(*this, rURIReference));
- Guard g(m_Mutex, [&]() {
- auto rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_HTTPGET, 0L);
- assert(rc == CURLE_OK);
- (void)rc;
- });
- auto rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_HTTPGET, 1L);
- assert(rc == CURLE_OK);
- (void)rc;
+ ::std::vector<CurlOption> const options{ { CURLOPT_HTTPGET, 1L, nullptr } };
::std::pair<::std::vector<OUString> const&, DAVResource&> const headers(rHeaderNames,
io_rResource);
- CurlProcessor::ProcessRequest(g, *this, uri, &rEnv, nullptr, &rxOutStream, nullptr, &headers);
+ CurlProcessor::ProcessRequest(*this, uri, options, &rEnv, nullptr, &rxOutStream, nullptr,
+ &headers);
}
auto CurlSession::PUT(OUString const& rURIReference,
@@ -1719,10 +1819,11 @@ auto CurlSession::PUT(OUString const& rURIReference,
}
// lock m_Mutex after accessing global LockStore to avoid deadlock
- Guard g(m_Mutex);
- CurlProcessor::ProcessRequest(g, *this, uri, &rEnv, ::std::move(pList), nullptr, &rxInStream,
- nullptr);
+ ::std::vector<CurlOption> const options{};
+
+ CurlProcessor::ProcessRequest(*this, uri, options, &rEnv, ::std::move(pList), nullptr,
+ &rxInStream, nullptr);
}
auto CurlSession::POST(OUString const& rURIReference, OUString const& rContentType,
@@ -1754,23 +1855,15 @@ auto CurlSession::POST(OUString const& rURIReference, OUString const& rContentTy
throw uno::RuntimeException("curl_slist_append failed");
}
- Guard g(m_Mutex, [&]() {
- auto rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_POST, 0L);
- assert(rc == CURLE_OK);
- (void)rc;
- });
-
- auto rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_POST, 1L);
- assert(rc == CURLE_OK);
- (void)rc;
+ ::std::vector<CurlOption> const options{ { CURLOPT_POST, 1L, nullptr } };
uno::Reference<io::XSequenceOutputStream> const xSeqOutStream(
io::SequenceOutputStream::create(m_xContext));
uno::Reference<io::XOutputStream> const xResponseOutStream(xSeqOutStream);
assert(xResponseOutStream.is());
- CurlProcessor::ProcessRequest(g, *this, uri, &rEnv, ::std::move(pList), &xResponseOutStream,
- &rxInStream, nullptr);
+ CurlProcessor::ProcessRequest(*this, uri, options, &rEnv, ::std::move(pList),
+ &xResponseOutStream, &rxInStream, nullptr);
uno::Reference<io::XInputStream> const xResponseInStream(
io::SequenceInputStream::createStreamFromSequence(m_xContext,
@@ -1810,17 +1903,9 @@ auto CurlSession::POST(OUString const& rURIReference, OUString const& rContentTy
throw uno::RuntimeException("curl_slist_append failed");
}
- Guard g(m_Mutex, [&]() {
- auto rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_POST, 0L);
- assert(rc == CURLE_OK);
- (void)rc;
- });
-
- auto rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_POST, 1L);
- assert(rc == CURLE_OK);
- (void)rc;
+ ::std::vector<CurlOption> const options{ { CURLOPT_POST, 1L, nullptr } };
- CurlProcessor::ProcessRequest(g, *this, uri, &rEnv, ::std::move(pList), &rxOutStream,
+ CurlProcessor::ProcessRequest(*this, uri, options, &rEnv, ::std::move(pList), &rxOutStream,
&rxInStream, nullptr);
}
@@ -1830,20 +1915,10 @@ auto CurlSession::MKCOL(OUString const& rURIReference, DAVRequestEnvironment con
CurlUri const uri(CurlProcessor::URIReferenceToURI(*this, rURIReference));
- Guard g(m_Mutex, [&]() {
- auto rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_CUSTOMREQUEST, nullptr);
- assert(rc == CURLE_OK);
- (void)rc;
- });
- auto rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_CUSTOMREQUEST, "MKCOL");
- if (rc != CURLE_OK)
- {
- SAL_WARN("ucb.ucp.webdav.curl", "CURLOPT_CUSTOMREQUEST failed: " << GetErrorString(rc));
- throw DAVException(DAVException::DAV_SESSION_CREATE,
- ConnectionEndPointString(m_URI.GetHost(), m_URI.GetPort()));
- }
+ ::std::vector<CurlOption> const options{ { CURLOPT_CUSTOMREQUEST, "MKCOL",
+ "CURLOPT_CUSTOMREQUEST" } };
- CurlProcessor::ProcessRequest(g, *this, uri, &rEnv, nullptr, nullptr, nullptr, nullptr);
+ CurlProcessor::ProcessRequest(*this, uri, options, &rEnv, nullptr, nullptr, nullptr, nullptr);
}
auto CurlProcessor::MoveOrCopy(CurlSession& rSession, OUString const& rSourceURIReference,
@@ -1868,17 +1943,10 @@ auto CurlProcessor::MoveOrCopy(CurlSession& rSession, OUString const& rSourceURI
throw uno::RuntimeException("curl_slist_append failed");
}
- Guard g(rSession.m_Mutex, [&]() {
- auto rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_CUSTOMREQUEST, nullptr);
- assert(rc == CURLE_OK);
- (void)rc;
- });
+ ::std::vector<CurlOption> const options{ { CURLOPT_CUSTOMREQUEST, pMethod,
+ "CURLOPT_CUSTOMREQUEST" } };
- auto rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_CUSTOMREQUEST, pMethod);
- assert(rc == CURLE_OK);
- (void)rc;
-
- CurlProcessor::ProcessRequest(g, rSession, uriSource, &rEnv, ::std::move(pList), nullptr,
+ CurlProcessor::ProcessRequest(rSession, uriSource, options, &rEnv, ::std::move(pList), nullptr,
nullptr, nullptr);
}
@@ -1906,20 +1974,10 @@ auto CurlSession::DESTROY(OUString const& rURIReference, DAVRequestEnvironment c
CurlUri const uri(CurlProcessor::URIReferenceToURI(*this, rURIReference));
- Guard g(m_Mutex, [&]() {
- auto rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_CUSTOMREQUEST, nullptr);
- assert(rc == CURLE_OK);
- (void)rc;
- });
- auto rc = curl_easy_setopt(m_pCurl.get(), CURLOPT_CUSTOMREQUEST, "DELETE");
- if (rc != CURLE_OK)
- {
- SAL_WARN("ucb.ucp.webdav.curl", "CURLOPT_CUSTOMREQUEST failed: " << GetErrorString(rc));
- throw DAVException(DAVException::DAV_SESSION_CREATE,
- ConnectionEndPointString(m_URI.GetHost(), m_URI.GetPort()));
- }
+ ::std::vector<CurlOption> const options{ { CURLOPT_CUSTOMREQUEST, "DELETE",
+ "CURLOPT_CUSTOMREQUEST" } };
- CurlProcessor::ProcessRequest(g, *this, uri, &rEnv, nullptr, nullptr, nullptr, nullptr);
+ CurlProcessor::ProcessRequest(*this, uri, options, &rEnv, nullptr, nullptr, nullptr, nullptr);
}
auto CurlProcessor::Lock(
@@ -1928,19 +1986,8 @@ auto CurlProcessor::Lock(
uno::Reference<io::XInputStream> const* const pxRequestInStream)
-> ::std::vector<::std::pair<ucb::Lock, sal_Int32>>
{
- Guard g(rSession.m_Mutex, [&]() {
- auto rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_CUSTOMREQUEST, nullptr);
- assert(rc == CURLE_OK);
- (void)rc;
- });
- auto rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_CUSTOMREQUEST, "LOCK");
- if (rc != CURLE_OK)
- {
- SAL_WARN("ucb.ucp.webdav.curl", "CURLOPT_CUSTOMREQUEST failed: " << GetErrorString(rc));
- throw DAVException(
- DAVException::DAV_SESSION_CREATE,
- ConnectionEndPointString(rSession.m_URI.GetHost(), rSession.m_URI.GetPort()));
- }
+ ::std::vector<CurlOption> const options{ { CURLOPT_CUSTOMREQUEST, "LOCK",
+ "CURLOPT_CUSTOMREQUEST" } };
// stream for response
uno::Reference<io::XInputStream> const xResponseInStream(io::Pipe::create(rSession.m_xContext));
@@ -1951,7 +1998,7 @@ auto CurlProcessor::Lock(
TimeValue startTime;
osl_getSystemTime(&startTime);
- CurlProcessor::ProcessRequest(g, rSession, rURI, pEnv, ::std::move(pRequestHeaderList),
+ CurlProcessor::ProcessRequest(rSession, rURI, options, pEnv, ::std::move(pRequestHeaderList),
&xResponseOutStream, pxRequestInStream, nullptr);
::std::vector<ucb::Lock> const acquiredLocks(parseWebDAVLockResponse(xResponseInStream));
@@ -2127,23 +2174,11 @@ auto CurlProcessor::Unlock(CurlSession& rSession, CurlUri const& rURI,
throw uno::RuntimeException("curl_slist_append failed");
}
- // lock m_Mutex after accessing global LockStore to avoid deadlock
- Guard g(rSession.m_Mutex, [&]() {
- auto rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_CUSTOMREQUEST, nullptr);
- assert(rc == CURLE_OK);
- (void)rc;
- });
- auto rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_CUSTOMREQUEST, "UNLOCK");
- if (rc != CURLE_OK)
- {
- SAL_WARN("ucb.ucp.webdav.curl", "CURLOPT_CUSTOMREQUEST failed: " << GetErrorString(rc));
- throw DAVException(
- DAVException::DAV_SESSION_CREATE,
- ConnectionEndPointString(rSession.m_URI.GetHost(), rSession.m_URI.GetPort()));
- }
+ ::std::vector<CurlOption> const options{ { CURLOPT_CUSTOMREQUEST, "UNLOCK",
+ "CURLOPT_CUSTOMREQUEST" } };
- CurlProcessor::ProcessRequest(g, rSession, rURI, pEnv, ::std::move(pList), nullptr, nullptr,
- nullptr);
+ CurlProcessor::ProcessRequest(rSession, rURI, options, pEnv, ::std::move(pList), nullptr,
+ nullptr, nullptr);
}
auto CurlSession::UNLOCK(OUString const& rURIReference, DAVRequestEnvironment const& rEnv) -> void