diff options
author | Michael Stahl <michael.stahl@allotropia.de> | 2021-10-22 17:09:30 +0200 |
---|---|---|
committer | Michael Stahl <michael.stahl@allotropia.de> | 2021-11-01 18:56:07 +0100 |
commit | cd3daf7f6e7842e96f60a85cb791b18bfcc0d7b3 (patch) | |
tree | 6645c0e2ac3d30548156435a03f0425035a52c67 /ucb/source | |
parent | 3f2c3acfac2c699820f1a7e66a485ad98d5d92ab (diff) |
ucb: webdav-curl: convert URI reference to URI as early as possible
Ensure that LockStore uses the full URL as key, not just the path.
Change-Id: I84caf0d22e8c0ba176c19d004ee8ffcd2fdd05c8
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/124077
Tested-by: Jenkins
Reviewed-by: Michael Stahl <michael.stahl@allotropia.de>
Diffstat (limited to 'ucb/source')
-rw-r--r-- | ucb/source/ucp/webdav-curl/CurlSession.cxx | 248 | ||||
-rw-r--r-- | ucb/source/ucp/webdav-curl/CurlSession.hxx | 6 | ||||
-rw-r--r-- | ucb/source/ucp/webdav-curl/CurlUri.cxx | 41 | ||||
-rw-r--r-- | ucb/source/ucp/webdav-curl/CurlUri.hxx | 5 | ||||
-rw-r--r-- | ucb/source/ucp/webdav-curl/SerfLockStore.cxx | 22 | ||||
-rw-r--r-- | ucb/source/ucp/webdav-curl/SerfLockStore.hxx | 6 |
6 files changed, 207 insertions, 121 deletions
diff --git a/ucb/source/ucp/webdav-curl/CurlSession.cxx b/ucb/source/ucp/webdav-curl/CurlSession.cxx index b0c1c4c7e2ac..7c5bd5fe5973 100644 --- a/ucb/source/ucp/webdav-curl/CurlSession.cxx +++ b/ucb/source/ucp/webdav-curl/CurlSession.cxx @@ -588,16 +588,17 @@ auto CurlSession::abort() -> void /// this is just a bunch of static member functions called from CurlSession struct CurlProcessor { + static auto URIReferenceToURI(CurlSession& rSession, OUString const& rURIReference) -> CurlUri; + static auto ProcessRequestImpl( - CurlSession& rSession, ::std::u16string_view rURIReference, - DAVRequestEnvironment const* pEnv, + CurlSession& rSession, CurlUri const& rURI, DAVRequestEnvironment const* pEnv, ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist_free_all>> pRequestHeaderList, uno::Reference<io::XOutputStream> const* pxOutStream, uno::Reference<io::XInputStream> const* pxInStream, ::std::pair<::std::vector<OUString> const&, DAVResource&> const* pRequestedHeaders) -> void; static auto ProcessRequest( - Guard& rGuard, CurlSession& rSession, OUString const& rURIReference, + Guard& rGuard, CurlSession& rSession, CurlUri const& rURI, DAVRequestEnvironment const* pEnv, ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist_free_all>> pRequestHeaderList, uno::Reference<io::XOutputStream> const* pxOutStream, @@ -605,7 +606,7 @@ struct CurlProcessor ::std::pair<::std::vector<OUString> const&, DAVResource&> const* pRequestedHeaders) -> void; static auto - PropFind(CurlSession& rSession, OUString const& rURIReference, Depth depth, + 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, @@ -616,19 +617,35 @@ struct CurlProcessor bool isOverwrite, char const* pMethod) -> void; static auto - Lock(CurlSession& rSession, OUString const& rURIReference, DAVRequestEnvironment const* pEnv, + Lock(CurlSession& rSession, CurlUri const& rURI, DAVRequestEnvironment const* pEnv, ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist_free_all>> pRequestHeaderList, uno::Reference<io::XInputStream> const* pxInStream) -> ::std::vector<::std::pair<ucb::Lock, sal_Int32>>; - static auto Unlock(CurlSession& rSession, OUString const& rURIReference, + static auto Unlock(CurlSession& rSession, CurlUri const& rURI, DAVRequestEnvironment const* pEnv) -> void; }; +auto CurlProcessor::URIReferenceToURI(CurlSession& rSession, OUString const& rURIReference) + -> CurlUri +{ + // No need to acquire rSession.m_Mutex because accessed members are const. + if (rSession.UsesProxy()) + // very odd, but see DAVResourceAccess::getRequestURI() :-/ + { + assert(rURIReference.startsWith("http://") || rURIReference.startsWith("https://")); + return CurlUri(rURIReference); + } + else + { + assert(rURIReference.startsWith("/") && !rURIReference.startsWith("//")); + return rSession.m_URI.CloneWithRelativeRefPathAbsolute(rURIReference); + } +} + /// main function to initiate libcurl requests auto CurlProcessor::ProcessRequestImpl( - CurlSession& rSession, ::std::u16string_view const rURIReference, - DAVRequestEnvironment const* const pEnv, + 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, @@ -681,20 +698,7 @@ auto CurlProcessor::ProcessRequestImpl( (void)rc; } - ::std::unique_ptr<CURLU, deleter_from_fn<curl_url_cleanup>> const pUrl( - rSession.m_URI.CloneCURLU()); - OString const utf8URIRef(OUStringToOString(rURIReference, RTL_TEXTENCODING_UTF8)); - auto uc = curl_url_set(pUrl.get(), - // very odd, but see DAVResourceAccess::getRequestURI() :-/ - rSession.UsesProxy() ? CURLUPART_URL : CURLUPART_PATH, - utf8URIRef.getStr(), 0); - if (uc != CURLUE_OK) - { - SAL_WARN("ucb.ucp.webdav.curl", "curl_url_set failed: " << uc); - throw DAVException(DAVException::DAV_INVALID_ARG); - } - - auto rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_CURLU, pUrl.get()); + auto rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_CURLU, rURI.GetCURLU()); assert(rc == CURLE_OK); // can't fail since 7.63.0 // authentication data may be in the URI, or requested via XInteractionHandler @@ -730,16 +734,15 @@ auto CurlProcessor::ProcessRequestImpl( // TODO: why is there this m_aRequestURI and also rURIReference argument? // ... only caller is DAVResourceAccess - always identical except MOVE/COPY // which doesn't work if it's just a URI reference so let's just use - // rURIReference via pUrl instead + // rURIReference via rURI instead #if 0 CurlUri const uri(pEnv->m_aRequestURI); #endif - CurlUri const uri(*pUrl); // note: due to parsing bug pwd didn't work in previous webdav ucps if (pEnv && !rSession.m_isAuthenticated - && (!uri.GetUser().isEmpty() || !uri.GetPassword().isEmpty())) + && (!rURI.GetUser().isEmpty() || !rURI.GetPassword().isEmpty())) { - oAuth.emplace(uri.GetUser(), uri.GetPassword(), CURLAUTH_ANY); + oAuth.emplace(rURI.GetUser(), rURI.GetPassword(), CURLAUTH_ANY); } if (pRequestedHeaders) { @@ -748,7 +751,7 @@ auto CurlProcessor::ProcessRequestImpl( // But it looks like all consumers of this .uri are interested // only in the path, so it shouldn't make a difference to give // the entire URI when the caller extracts the path anyway. - pRequestedHeaders->second.uri = uri.GetURI(); + pRequestedHeaders->second.uri = rURI.GetURI(); pRequestedHeaders->second.properties.clear(); } } @@ -1007,7 +1010,7 @@ auto CurlProcessor::ProcessRequestImpl( } } -static auto TryRemoveExpiredLockToken(CurlSession& rSession, OUString const& rURIReference, +static auto TryRemoveExpiredLockToken(CurlSession& rSession, CurlUri const& rURI, DAVRequestEnvironment const* const pEnv) -> bool { if (!pEnv) @@ -1015,7 +1018,7 @@ static auto TryRemoveExpiredLockToken(CurlSession& rSession, OUString const& rUR // caller was a NonInteractive_*LOCK function anyway, its caller is LockStore return false; } - OUString const* const pToken(g_Init.LockStore.getLockTokenForURI(rURIReference, nullptr)); + OUString const* const pToken(g_Init.LockStore.getLockTokenForURI(rURI.GetURI(), nullptr)); if (!pToken) { return false; @@ -1028,7 +1031,7 @@ static auto TryRemoveExpiredLockToken(CurlSession& rSession, OUString const& rUR ::std::tuple<::std::vector<OUString> const&, ::std::vector<DAVResource>* const, ::std::vector<ucb::Lock>* const> const args(propertyNames, nullptr, &locks); - CurlProcessor::PropFind(rSession, rURIReference, DAVZERO, &args, nullptr, *pEnv); + 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 @@ -1043,8 +1046,8 @@ static auto TryRemoveExpiredLockToken(CurlSession& rSession, OUString const& rUR } SAL_INFO("ucb.ucp.webdav.curl", - "lock token expired, removing: " << rURIReference << " " << *pToken); - g_Init.LockStore.removeLock(rURIReference); + "lock token expired, removing: " << rURI.GetURI() << " " << *pToken); + g_Init.LockStore.removeLock(rURI.GetURI()); return true; } catch (DAVException const&) @@ -1054,7 +1057,7 @@ static auto TryRemoveExpiredLockToken(CurlSession& rSession, OUString const& rUR } auto CurlProcessor::ProcessRequest( - Guard& rGuard, CurlSession& rSession, OUString const& rURIReference, + 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, @@ -1064,8 +1067,8 @@ auto CurlProcessor::ProcessRequest( { try { - ProcessRequestImpl(rSession, rURIReference, pEnv, ::std::move(pRequestHeaderList), - pxOutStream, pxInStream, pRequestedHeaders); + ProcessRequestImpl(rSession, rURI, pEnv, ::std::move(pRequestHeaderList), pxOutStream, + pxInStream, pRequestedHeaders); } catch (DAVException const& rException) { @@ -1077,7 +1080,7 @@ auto CurlProcessor::ProcessRequest( case SC_LOCKED: { rGuard.unlock(); // release m_Mutex before accessing LockStore - if (g_Init.LockStore.getLockTokenForURI(rURIReference, nullptr)) + if (g_Init.LockStore.getLockTokenForURI(rURI.GetURI(), nullptr)) { throw DAVException(DAVException::DAV_LOCKED_SELF); } @@ -1095,7 +1098,7 @@ auto CurlProcessor::ProcessRequest( // the expiration of a lock. // Initiate a new request *outside* ProcessRequestImpl // *after* rGuard.unlock() to avoid messing up m_pCurl state. - if (TryRemoveExpiredLockToken(rSession, rURIReference, pEnv)) + if (TryRemoveExpiredLockToken(rSession, rURI, pEnv)) { throw DAVException(DAVException::DAV_LOCK_EXPIRED); } @@ -1115,6 +1118,8 @@ auto CurlSession::OPTIONS(OUString const& rURIReference, rOptions.init(); + CurlUri const uri(CurlProcessor::URIReferenceToURI(*this, rURIReference)); + ::std::vector<OUString> const headerNames{ "allow", "dav" }; DAVResource result; ::std::pair<::std::vector<OUString> const&, DAVResource&> const headers(headerNames, result); @@ -1140,8 +1145,7 @@ auto CurlSession::OPTIONS(OUString const& rURIReference, ConnectionEndPointString(m_URI.GetHost(), m_URI.GetPort())); } - CurlProcessor::ProcessRequest(g, *this, rURIReference, &rEnv, nullptr, nullptr, nullptr, - &headers); + CurlProcessor::ProcessRequest(g, *this, uri, &rEnv, nullptr, nullptr, nullptr, &headers); } for (auto const& it : result.properties) @@ -1180,7 +1184,7 @@ auto CurlSession::OPTIONS(OUString const& rURIReference, } if (rOptions.isClass2() || rOptions.isClass3()) { - if (g_Init.LockStore.getLockTokenForURI(rURIReference, nullptr)) + if (g_Init.LockStore.getLockTokenForURI(uri.GetURI(), nullptr)) { rOptions.setLocked(); } @@ -1188,7 +1192,7 @@ auto CurlSession::OPTIONS(OUString const& rURIReference, } auto CurlProcessor::PropFind( - CurlSession& rSession, OUString const& rURIReference, Depth const nDepth, + CurlSession& rSession, CurlUri const& rURI, Depth const nDepth, ::std::tuple<::std::vector<OUString> const&, ::std::vector<DAVResource>* const, ::std::vector<ucb::Lock>* const> const* const o_pRequestedProperties, ::std::vector<DAVResourceInfo>* const o_pResourceInfos, DAVRequestEnvironment const& rEnv) @@ -1294,8 +1298,8 @@ auto CurlProcessor::PropFind( assert(xResponseInStream.is()); assert(xResponseOutStream.is()); - CurlProcessor::ProcessRequest(g, rSession, rURIReference, &rEnv, ::std::move(pList), - &xResponseOutStream, &xRequestInStream, nullptr); + CurlProcessor::ProcessRequest(g, rSession, rURI, &rEnv, ::std::move(pList), &xResponseOutStream, + &xRequestInStream, nullptr); if (o_pResourceInfos) { @@ -1322,10 +1326,13 @@ auto CurlSession::PROPFIND(OUString const& rURIReference, Depth const depth, DAVRequestEnvironment const& rEnv) -> void { SAL_INFO("ucb.ucp.webdav.curl", "PROPFIND: " << rURIReference << " " << depth); + + CurlUri const uri(CurlProcessor::URIReferenceToURI(*this, rURIReference)); + ::std::tuple<::std::vector<OUString> const&, ::std::vector<DAVResource>* const, ::std::vector<ucb::Lock>* const> const args(rPropertyNames, &o_rResources, nullptr); - return CurlProcessor::PropFind(*this, rURIReference, depth, &args, nullptr, rEnv); + return CurlProcessor::PropFind(*this, uri, depth, &args, nullptr, rEnv); } auto CurlSession::PROPFIND(OUString const& rURIReference, Depth const depth, @@ -1333,7 +1340,10 @@ auto CurlSession::PROPFIND(OUString const& rURIReference, Depth const depth, DAVRequestEnvironment const& rEnv) -> void { SAL_INFO("ucb.ucp.webdav.curl", "PROPFIND: " << rURIReference << " " << depth); - return CurlProcessor::PropFind(*this, rURIReference, depth, nullptr, &o_rResourceInfos, rEnv); + + CurlUri const uri(CurlProcessor::URIReferenceToURI(*this, rURIReference)); + + return CurlProcessor::PropFind(*this, uri, depth, nullptr, &o_rResourceInfos, rEnv); } auto CurlSession::PROPPATCH(OUString const& rURIReference, @@ -1342,6 +1352,8 @@ auto CurlSession::PROPPATCH(OUString const& rURIReference, { SAL_INFO("ucb.ucp.webdav.curl", "PROPPATCH: " << rURIReference); + CurlUri const uri(CurlProcessor::URIReferenceToURI(*this, rURIReference)); + //FIXME why does toXML encode stuff which parser ignores //isUCBDeadProperty case not handled @@ -1416,7 +1428,7 @@ auto CurlSession::PROPPATCH(OUString const& rURIReference, xWriter->endDocument(); xRequestOutStream->closeOutput(); - CurlProcessor::ProcessRequest(g, *this, rURIReference, &rEnv, ::std::move(pList), nullptr, + CurlProcessor::ProcessRequest(g, *this, uri, &rEnv, ::std::move(pList), nullptr, &xRequestInStream, nullptr); } @@ -1425,6 +1437,8 @@ auto CurlSession::HEAD(OUString const& rURIReference, ::std::vector<OUString> co { SAL_INFO("ucb.ucp.webdav.curl", "HEAD: " << rURIReference); + 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); @@ -1437,8 +1451,7 @@ auto CurlSession::HEAD(OUString const& rURIReference, ::std::vector<OUString> co ::std::pair<::std::vector<OUString> const&, DAVResource&> const headers(rHeaderNames, io_rResource); - CurlProcessor::ProcessRequest(g, *this, rURIReference, &rEnv, nullptr, nullptr, nullptr, - &headers); + CurlProcessor::ProcessRequest(g, *this, uri, &rEnv, nullptr, nullptr, nullptr, &headers); } auto CurlSession::GET(OUString const& rURIReference, DAVRequestEnvironment const& rEnv) @@ -1446,6 +1459,8 @@ auto CurlSession::GET(OUString const& rURIReference, DAVRequestEnvironment const { SAL_INFO("ucb.ucp.webdav.curl", "GET: " << rURIReference); + CurlUri const uri(CurlProcessor::URIReferenceToURI(*this, rURIReference)); + // could use either com.sun.star.io.Pipe or com.sun.star.io.SequenceInputStream? // Pipe can just write into its XOuputStream, which is simpler. // Both resize exponentially, so performance should be fine. @@ -1465,8 +1480,8 @@ auto CurlSession::GET(OUString const& rURIReference, DAVRequestEnvironment const assert(rc == CURLE_OK); (void)rc; - CurlProcessor::ProcessRequest(g, *this, rURIReference, &rEnv, nullptr, &xResponseOutStream, - nullptr, nullptr); + CurlProcessor::ProcessRequest(g, *this, uri, &rEnv, nullptr, &xResponseOutStream, nullptr, + nullptr); uno::Reference<io::XInputStream> const xResponseInStream( io::SequenceInputStream::createStreamFromSequence(m_xContext, @@ -1481,6 +1496,8 @@ auto CurlSession::GET(OUString const& rURIReference, uno::Reference<io::XOutputS { SAL_INFO("ucb.ucp.webdav.curl", "GET: " << rURIReference); + 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); @@ -1490,8 +1507,7 @@ auto CurlSession::GET(OUString const& rURIReference, uno::Reference<io::XOutputS assert(rc == CURLE_OK); (void)rc; - CurlProcessor::ProcessRequest(g, *this, rURIReference, &rEnv, nullptr, &rxOutStream, nullptr, - nullptr); + CurlProcessor::ProcessRequest(g, *this, uri, &rEnv, nullptr, &rxOutStream, nullptr, nullptr); } auto CurlSession::GET(OUString const& rURIReference, ::std::vector<OUString> const& rHeaderNames, @@ -1500,6 +1516,8 @@ auto CurlSession::GET(OUString const& rURIReference, ::std::vector<OUString> con { SAL_INFO("ucb.ucp.webdav.curl", "GET: " << rURIReference); + 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); @@ -1517,8 +1535,8 @@ 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, rURIReference, &rEnv, nullptr, &xResponseOutStream, - nullptr, &headers); + CurlProcessor::ProcessRequest(g, *this, uri, &rEnv, nullptr, &xResponseOutStream, nullptr, + &headers); uno::Reference<io::XInputStream> const xResponseInStream( io::SequenceInputStream::createStreamFromSequence(m_xContext, @@ -1534,6 +1552,8 @@ auto CurlSession::GET(OUString const& rURIReference, uno::Reference<io::XOutputS { SAL_INFO("ucb.ucp.webdav.curl", "GET: " << rURIReference); + 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); @@ -1546,8 +1566,7 @@ auto CurlSession::GET(OUString const& rURIReference, uno::Reference<io::XOutputS ::std::pair<::std::vector<OUString> const&, DAVResource&> const headers(rHeaderNames, io_rResource); - CurlProcessor::ProcessRequest(g, *this, rURIReference, &rEnv, nullptr, &rxOutStream, nullptr, - &headers); + CurlProcessor::ProcessRequest(g, *this, uri, &rEnv, nullptr, &rxOutStream, nullptr, &headers); } auto CurlSession::PUT(OUString const& rURIReference, @@ -1556,6 +1575,8 @@ auto CurlSession::PUT(OUString const& rURIReference, { SAL_INFO("ucb.ucp.webdav.curl", "PUT: " << rURIReference); + CurlUri const uri(CurlProcessor::URIReferenceToURI(*this, rURIReference)); + // TODO: either set CURLOPT_INFILESIZE_LARGE or chunked? ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist_free_all>> pList( curl_slist_append(nullptr, "Transfer-Encoding: chunked")); @@ -1563,8 +1584,7 @@ auto CurlSession::PUT(OUString const& rURIReference, { throw uno::RuntimeException("curl_slist_append failed"); } - // TODO: why is a *global* LockStore keyed by *path*? - OUString const token(g_Init.LockStore.getLockToken(rURIReference)); + OUString const token(g_Init.LockStore.getLockToken(uri.GetURI())); if (!token.isEmpty()) { OString const utf8If("If: <" + OUStringToOString(rURIReference, RTL_TEXTENCODING_ASCII_US) @@ -1579,8 +1599,8 @@ auto CurlSession::PUT(OUString const& rURIReference, // lock m_Mutex after accessing global LockStore to avoid deadlock Guard g(m_Mutex); - CurlProcessor::ProcessRequest(g, *this, rURIReference, &rEnv, ::std::move(pList), nullptr, - &rxInStream, nullptr); + CurlProcessor::ProcessRequest(g, *this, uri, &rEnv, ::std::move(pList), nullptr, &rxInStream, + nullptr); } auto CurlSession::POST(OUString const& rURIReference, OUString const& rContentType, @@ -1589,6 +1609,8 @@ auto CurlSession::POST(OUString const& rURIReference, OUString const& rContentTy { SAL_INFO("ucb.ucp.webdav.curl", "POST: " << rURIReference); + CurlUri const uri(CurlProcessor::URIReferenceToURI(*this, rURIReference)); + // TODO: either set CURLOPT_POSTFIELDSIZE_LARGE or chunked? ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist_free_all>> pList( curl_slist_append(nullptr, "Transfer-Encoding: chunked")); @@ -1625,8 +1647,8 @@ auto CurlSession::POST(OUString const& rURIReference, OUString const& rContentTy uno::Reference<io::XOutputStream> const xResponseOutStream(xSeqOutStream); assert(xResponseOutStream.is()); - CurlProcessor::ProcessRequest(g, *this, rURIReference, &rEnv, ::std::move(pList), - &xResponseOutStream, &rxInStream, nullptr); + CurlProcessor::ProcessRequest(g, *this, uri, &rEnv, ::std::move(pList), &xResponseOutStream, + &rxInStream, nullptr); uno::Reference<io::XInputStream> const xResponseInStream( io::SequenceInputStream::createStreamFromSequence(m_xContext, @@ -1643,6 +1665,8 @@ auto CurlSession::POST(OUString const& rURIReference, OUString const& rContentTy { SAL_INFO("ucb.ucp.webdav.curl", "POST: " << rURIReference); + CurlUri const uri(CurlProcessor::URIReferenceToURI(*this, rURIReference)); + // TODO: either set CURLOPT_POSTFIELDSIZE_LARGE or chunked? ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist_free_all>> pList( curl_slist_append(nullptr, "Transfer-Encoding: chunked")); @@ -1674,7 +1698,7 @@ auto CurlSession::POST(OUString const& rURIReference, OUString const& rContentTy assert(rc == CURLE_OK); (void)rc; - CurlProcessor::ProcessRequest(g, *this, rURIReference, &rEnv, ::std::move(pList), &rxOutStream, + CurlProcessor::ProcessRequest(g, *this, uri, &rEnv, ::std::move(pList), &rxOutStream, &rxInStream, nullptr); } @@ -1682,6 +1706,8 @@ auto CurlSession::MKCOL(OUString const& rURIReference, DAVRequestEnvironment con { SAL_INFO("ucb.ucp.webdav.curl", "MKCOL: " << rURIReference); + 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); @@ -1695,8 +1721,7 @@ auto CurlSession::MKCOL(OUString const& rURIReference, DAVRequestEnvironment con ConnectionEndPointString(m_URI.GetHost(), m_URI.GetPort())); } - CurlProcessor::ProcessRequest(g, *this, rURIReference, &rEnv, nullptr, nullptr, nullptr, - nullptr); + CurlProcessor::ProcessRequest(g, *this, uri, &rEnv, nullptr, nullptr, nullptr, nullptr); } auto CurlProcessor::MoveOrCopy(CurlSession& rSession, OUString const& rSourceURIReference, @@ -1704,6 +1729,8 @@ auto CurlProcessor::MoveOrCopy(CurlSession& rSession, OUString const& rSourceURI DAVRequestEnvironment const& rEnv, bool const isOverwrite, char const* const pMethod) -> void { + CurlUri const uriSource(CurlProcessor::URIReferenceToURI(rSession, rSourceURIReference)); + OString const utf8Destination("Destination: " + OUStringToOString(rDestinationURI, RTL_TEXTENCODING_ASCII_US)); ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist_free_all>> pList( @@ -1729,8 +1756,8 @@ auto CurlProcessor::MoveOrCopy(CurlSession& rSession, OUString const& rSourceURI assert(rc == CURLE_OK); (void)rc; - CurlProcessor::ProcessRequest(g, rSession, rSourceURIReference, &rEnv, ::std::move(pList), - nullptr, nullptr, nullptr); + CurlProcessor::ProcessRequest(g, rSession, uriSource, &rEnv, ::std::move(pList), nullptr, + nullptr, nullptr); } auto CurlSession::COPY(OUString const& rSourceURIReference, OUString const& rDestinationURI, @@ -1755,6 +1782,8 @@ auto CurlSession::DESTROY(OUString const& rURIReference, DAVRequestEnvironment c { SAL_INFO("ucb.ucp.webdav.curl", "DESTROY: " << rURIReference); + 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); @@ -1768,12 +1797,11 @@ auto CurlSession::DESTROY(OUString const& rURIReference, DAVRequestEnvironment c ConnectionEndPointString(m_URI.GetHost(), m_URI.GetPort())); } - CurlProcessor::ProcessRequest(g, *this, rURIReference, &rEnv, nullptr, nullptr, nullptr, - nullptr); + CurlProcessor::ProcessRequest(g, *this, uri, &rEnv, nullptr, nullptr, nullptr, nullptr); } auto CurlProcessor::Lock( - CurlSession& rSession, OUString const& rURIReference, DAVRequestEnvironment const* const pEnv, + 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::XInputStream> const* const pxRequestInStream) -> ::std::vector<::std::pair<ucb::Lock, sal_Int32>> @@ -1801,12 +1829,12 @@ auto CurlProcessor::Lock( TimeValue startTime; osl_getSystemTime(&startTime); - CurlProcessor::ProcessRequest(g, rSession, rURIReference, pEnv, ::std::move(pRequestHeaderList), + CurlProcessor::ProcessRequest(g, rSession, rURI, pEnv, ::std::move(pRequestHeaderList), &xResponseOutStream, pxRequestInStream, nullptr); ::std::vector<ucb::Lock> const acquiredLocks(parseWebDAVLockResponse(xResponseInStream)); SAL_WARN_IF(acquiredLocks.empty(), "ucb.ucp.webdav.curl", - "could not get LOCK for " << rURIReference); + "could not get LOCK for " << rURI.GetURI()); TimeValue endTime; osl_getSystemTime(&endTime); @@ -1826,7 +1854,7 @@ auto CurlProcessor::Lock( { SAL_WARN("ucb.ucp.webdav.curl", "LOCK timeout already expired when receiving LOCK response for " - << rURIReference); + << rURI.GetURI()); lockExpirationTimeSeconds = 0; } else @@ -1844,8 +1872,9 @@ auto CurlSession::LOCK(OUString const& rURIReference, ucb::Lock /*const*/& rLock { SAL_INFO("ucb.ucp.webdav.curl", "LOCK: " << rURIReference); - // FIXME: why is a *global* LockStore keyed by *path*? - if (g_Init.LockStore.getLockTokenForURI(rURIReference, &rLock)) + CurlUri const uri(CurlProcessor::URIReferenceToURI(*this, rURIReference)); + + if (g_Init.LockStore.getLockTokenForURI(uri.GetURI(), &rLock)) { // already have a lock that covers the requirement // TODO: maybe use DAV:lockdiscovery to ensure it's valid @@ -1948,21 +1977,20 @@ auto CurlSession::LOCK(OUString const& rURIReference, ucb::Lock /*const*/& rLock } auto const acquiredLocks - = CurlProcessor::Lock(*this, rURIReference, &rEnv, ::std::move(pList), &xRequestInStream); + = CurlProcessor::Lock(*this, uri, &rEnv, ::std::move(pList), &xRequestInStream); for (auto const& rAcquiredLock : acquiredLocks) { - g_Init.LockStore.addLock(rURIReference, rAcquiredLock.first, + g_Init.LockStore.addLock(uri.GetURI(), rAcquiredLock.first, rAcquiredLock.first.LockTokens[0], this, rAcquiredLock.second); SAL_INFO("ucb.ucp.webdav.curl", "created LOCK for " << rURIReference); } } -auto CurlProcessor::Unlock(CurlSession& rSession, OUString const& rURIReference, +auto CurlProcessor::Unlock(CurlSession& rSession, CurlUri const& rURI, DAVRequestEnvironment const* const pEnv) -> void { - // TODO: why is a *global* LockStore keyed by *path*? - OUString const token(g_Init.LockStore.getLockToken(rURIReference)); + OUString const token(g_Init.LockStore.getLockToken(rURI.GetURI())); if (token.isEmpty()) { SAL_WARN("ucb.ucp.webdav.curl", "attempt to unlock but not locked"); @@ -1992,8 +2020,8 @@ auto CurlProcessor::Unlock(CurlSession& rSession, OUString const& rURIReference, ConnectionEndPointString(rSession.m_URI.GetHost(), rSession.m_URI.GetPort())); } - CurlProcessor::ProcessRequest(g, rSession, rURIReference, pEnv, ::std::move(pList), nullptr, - nullptr, nullptr); + CurlProcessor::ProcessRequest(g, rSession, rURI, pEnv, ::std::move(pList), nullptr, nullptr, + nullptr); } auto CurlSession::UNLOCK(OUString const& rURIReference, DAVRequestEnvironment const& rEnv) -> void @@ -2002,68 +2030,72 @@ auto CurlSession::UNLOCK(OUString const& rURIReference, DAVRequestEnvironment co // note: no m_Mutex lock needed here, only in CurlProcessor::Unlock() - CurlProcessor::Unlock(*this, rURIReference, &rEnv); + CurlUri const uri(CurlProcessor::URIReferenceToURI(*this, rURIReference)); - g_Init.LockStore.removeLock(rURIReference); + CurlProcessor::Unlock(*this, uri, &rEnv); + + g_Init.LockStore.removeLock(uri.GetURI()); } -auto CurlSession::NonInteractive_LOCK(OUString const& rURIReference, +auto CurlSession::NonInteractive_LOCK(OUString const& rURI, sal_Int32& o_rLastChanceToSendRefreshRequest) -> bool { - SAL_INFO("ucb.ucp.webdav.curl", "NonInteractive_LOCK: " << rURIReference); + SAL_INFO("ucb.ucp.webdav.curl", "NonInteractive_LOCK: " << rURI); // note: no m_Mutex lock needed here, only in CurlProcessor::Lock() - ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist_free_all>> pList( - curl_slist_append(nullptr, "Timeout: Second-180")); - - // TODO: why is a *global* LockStore keyed by *path*? - OUString const token(g_Init.LockStore.getLockToken(rURIReference)); - assert(!token.isEmpty()); // LockStore is the caller - OString const utf8If("If: (<" + OUStringToOString(token, RTL_TEXTENCODING_ASCII_US) + ">)"); - pList.reset(curl_slist_append(pList.release(), utf8If.getStr())); - if (!pList) - { - throw uno::RuntimeException("curl_slist_append failed"); - } - try { + CurlUri const uri(rURI); + ::std::unique_ptr<curl_slist, deleter_from_fn<curl_slist_free_all>> pList( + curl_slist_append(nullptr, "Timeout: Second-180")); + + OUString const token(g_Init.LockStore.getLockToken(rURI)); + assert(!token.isEmpty()); // LockStore is the caller + OString const utf8If("If: (<" + OUStringToOString(token, RTL_TEXTENCODING_ASCII_US) + ">)"); + pList.reset(curl_slist_append(pList.release(), utf8If.getStr())); + if (!pList) + { + throw uno::RuntimeException("curl_slist_append failed"); + } + auto const acquiredLocks - = CurlProcessor::Lock(*this, rURIReference, nullptr, ::std::move(pList), nullptr); + = CurlProcessor::Lock(*this, uri, nullptr, ::std::move(pList), nullptr); SAL_WARN_IF(1 < acquiredLocks.size(), "ucb.ucp.webdav.curl", - "multiple locks acquired on refresh for " << rURIReference); + "multiple locks acquired on refresh for " << rURI); if (!acquiredLocks.empty()) { o_rLastChanceToSendRefreshRequest = acquiredLocks.begin()->second; } - SAL_INFO("ucb.ucp.webdav.curl", "NonInteractive_LOCK succeeded on " << rURIReference); + SAL_INFO("ucb.ucp.webdav.curl", "NonInteractive_LOCK succeeded on " << rURI); return true; } catch (...) { - SAL_INFO("ucb.ucp.webdav.curl", "NonInteractive_LOCK failed on " << rURIReference); + SAL_INFO("ucb.ucp.webdav.curl", "NonInteractive_LOCK failed on " << rURI); return false; } } -auto CurlSession::NonInteractive_UNLOCK(OUString const& rURIReference) -> void +auto CurlSession::NonInteractive_UNLOCK(OUString const& rURI) -> void { - SAL_INFO("ucb.ucp.webdav.curl", "NonInteractive_UNLOCK: " << rURIReference); + SAL_INFO("ucb.ucp.webdav.curl", "NonInteractive_UNLOCK: " << rURI); // note: no m_Mutex lock needed here, only in CurlProcessor::Unlock() try { - CurlProcessor::Unlock(*this, rURIReference, nullptr); + CurlUri const uri(rURI); + + CurlProcessor::Unlock(*this, uri, nullptr); // the only caller is the dtor of the LockStore, don't call remove! - SAL_INFO("ucb.ucp.webdav.curl", "NonInteractive_UNLOCK succeeded on " << rURIReference); + SAL_INFO("ucb.ucp.webdav.curl", "NonInteractive_UNLOCK succeeded on " << rURI); } catch (...) { - SAL_INFO("ucb.ucp.webdav.curl", "NonInteractive_UNLOCK failed on " << rURIReference); + SAL_INFO("ucb.ucp.webdav.curl", "NonInteractive_UNLOCK failed on " << rURI); } } diff --git a/ucb/source/ucp/webdav-curl/CurlSession.hxx b/ucb/source/ucp/webdav-curl/CurlSession.hxx index 58a828b29f14..88496308a947 100644 --- a/ucb/source/ucp/webdav-curl/CurlSession.hxx +++ b/ucb/source/ucp/webdav-curl/CurlSession.hxx @@ -127,9 +127,9 @@ public: virtual auto abort() -> void override; - auto NonInteractive_LOCK(OUString const& rURIReference, - sal_Int32& o_rLastChanceToSendRefreshRequest) -> bool; - auto NonInteractive_UNLOCK(OUString const& rURIReference) -> void; + auto NonInteractive_LOCK(OUString const& rURI, sal_Int32& o_rLastChanceToSendRefreshRequest) + -> bool; + auto NonInteractive_UNLOCK(OUString const& rURI) -> void; }; } // namespace http_dav_ucp diff --git a/ucb/source/ucp/webdav-curl/CurlUri.cxx b/ucb/source/ucp/webdav-curl/CurlUri.cxx index 5ab41db8d756..1c596dfcd299 100644 --- a/ucb/source/ucp/webdav-curl/CurlUri.cxx +++ b/ucb/source/ucp/webdav-curl/CurlUri.cxx @@ -245,6 +245,47 @@ void CurlUri::AppendPath(::std::u16string_view const rPath) m_Path = *oPath; } +CurlUri CurlUri::CloneWithRelativeRefPathAbsolute(OUString const& rRelativeRef) const +{ + ::std::unique_ptr<CURLU, deleter_from_fn<curl_url_cleanup>> pUrl(curl_url_dup(m_pUrl.get())); + sal_Int32 indexEnd(rRelativeRef.getLength()); + auto const indexQuery(rRelativeRef.indexOf('?')); + auto const indexFragment(rRelativeRef.indexOf('#')); + if (indexFragment != -1) + { + OUString const fragment(rRelativeRef.copy(indexFragment)); + indexEnd = indexFragment; + OString const utf8Fragment(OUStringToOString(fragment, RTL_TEXTENCODING_UTF8)); + auto const uc = curl_url_set(pUrl.get(), CURLUPART_QUERY, utf8Fragment.getStr(), 0); + if (uc != CURLUE_OK) + { + SAL_WARN("ucb.ucp.webdav.curl", "curl_url_set failed: " << uc); + throw DAVException(DAVException::DAV_INVALID_ARG); + } + } + if (indexQuery != -1 && (indexFragment == -1 || indexQuery < indexFragment)) + { + OUString const query(rRelativeRef.copy(indexQuery, indexEnd - indexQuery)); + indexEnd = indexQuery; + OString const utf8Query(OUStringToOString(query, RTL_TEXTENCODING_UTF8)); + auto const uc = curl_url_set(pUrl.get(), CURLUPART_QUERY, utf8Query.getStr(), 0); + if (uc != CURLUE_OK) + { + SAL_WARN("ucb.ucp.webdav.curl", "curl_url_set failed: " << uc); + throw DAVException(DAVException::DAV_INVALID_ARG); + } + } + OUString const path(rRelativeRef.copy(0, indexEnd)); + OString const utf8Path(OUStringToOString(path, RTL_TEXTENCODING_UTF8)); + auto const uc = curl_url_set(pUrl.get(), CURLUPART_PATH, utf8Path.getStr(), 0); + if (uc != CURLUE_OK) + { + SAL_WARN("ucb.ucp.webdav.curl", "curl_url_set failed: " << uc); + throw DAVException(DAVException::DAV_INVALID_ARG); + } + return CurlUri(*pUrl.release()); +} + OUString EncodeSegment(OUString const& rSegment) { return rtl::Uri::encode(rSegment, rtl_UriCharClassPchar, rtl_UriEncodeIgnoreEscapes, diff --git a/ucb/source/ucp/webdav-curl/CurlUri.hxx b/ucb/source/ucp/webdav-curl/CurlUri.hxx index 13859666bc79..4071991c904e 100644 --- a/ucb/source/ucp/webdav-curl/CurlUri.hxx +++ b/ucb/source/ucp/webdav-curl/CurlUri.hxx @@ -59,7 +59,7 @@ public: bool operator==(CurlUri const& rOther) const; - CURLU* CloneCURLU() const { return curl_url_dup(m_pUrl.get()); } + CURLU const* GetCURLU() const { return m_pUrl.get(); } OUString const& GetURI() const { return m_URI; } OUString const& GetScheme() const { return m_Scheme; } OUString const& GetUser() const { return m_User; } @@ -76,6 +76,9 @@ public: void SetScheme(::std::u16string_view rScheme); /// @throws DAVException void AppendPath(::std::u16string_view rPath); + /// @param matches: relative-ref = path-absolute [ "?" query ] [ "#" fragment ] + /// @throws DAVException + CurlUri CloneWithRelativeRefPathAbsolute(OUString const& rRelativeRef) const; }; OUString EncodeSegment(OUString const& rSegment); diff --git a/ucb/source/ucp/webdav-curl/SerfLockStore.cxx b/ucb/source/ucp/webdav-curl/SerfLockStore.cxx index 62526bd873fc..93cca865eb1b 100644 --- a/ucb/source/ucp/webdav-curl/SerfLockStore.cxx +++ b/ucb/source/ucp/webdav-curl/SerfLockStore.cxx @@ -139,11 +139,13 @@ void SerfLockStore::stopTicker(osl::ClearableMutexGuard & rGuard) pTickerThread->join(); // without m_aMutex locked (to prevent deadlock) } -OUString SerfLockStore::getLockToken( const OUString& rLock ) +OUString SerfLockStore::getLockToken(const OUString& rURI) { + assert(rURI.startsWith("http://") || rURI.startsWith("https://")); + osl::MutexGuard aGuard( m_aMutex ); - LockInfoMap::const_iterator it( m_aLockInfoMap.find( rLock ) ); + LockInfoMap::const_iterator const it( m_aLockInfoMap.find(rURI) ); if ( it != m_aLockInfoMap.end() ) return (*it).second.m_sToken; @@ -154,6 +156,8 @@ OUString SerfLockStore::getLockToken( const OUString& rLock ) OUString const* SerfLockStore::getLockTokenForURI(OUString const& rURI, css::ucb::Lock const*const pLock) { + assert(rURI.startsWith("http://") || rURI.startsWith("https://")); + osl::MutexGuard aGuard( m_aMutex ); auto const it(m_aLockInfoMap.find(rURI)); @@ -187,6 +191,8 @@ void SerfLockStore::addLock( const OUString& rURI, rtl::Reference<CurlSession> const & xSession, sal_Int32 nLastChanceToSendRefreshRequest ) { + assert(rURI.startsWith("http://") || rURI.startsWith("https://")); + osl::MutexGuard aGuard( m_aMutex ); m_aLockInfoMap[ rURI ] @@ -196,12 +202,14 @@ void SerfLockStore::addLock( const OUString& rURI, } -void SerfLockStore::updateLock( const OUString& rLock, +void SerfLockStore::updateLock( const OUString& rURI, sal_Int32 nLastChanceToSendRefreshRequest ) { + assert(rURI.startsWith("http://") || rURI.startsWith("https://")); + osl::MutexGuard aGuard( m_aMutex ); - LockInfoMap::iterator it( m_aLockInfoMap.find( rLock ) ); + LockInfoMap::iterator const it(m_aLockInfoMap.find(rURI)); SAL_WARN_IF( it == m_aLockInfoMap.end(), "ucb.ucp.webdav", "SerfLockStore::updateLock: lock not found!" ); @@ -213,11 +221,13 @@ void SerfLockStore::updateLock( const OUString& rLock, } -void SerfLockStore::removeLock( const OUString& rLock ) +void SerfLockStore::removeLock(const OUString& rURI) { + assert(rURI.startsWith("http://") || rURI.startsWith("https://")); + osl::ClearableMutexGuard aGuard( m_aMutex ); - m_aLockInfoMap.erase( rLock ); + m_aLockInfoMap.erase(rURI); if ( m_aLockInfoMap.empty() ) stopTicker(aGuard); diff --git a/ucb/source/ucp/webdav-curl/SerfLockStore.hxx b/ucb/source/ucp/webdav-curl/SerfLockStore.hxx index 4aeb3a020dbc..7a420187813c 100644 --- a/ucb/source/ucp/webdav-curl/SerfLockStore.hxx +++ b/ucb/source/ucp/webdav-curl/SerfLockStore.hxx @@ -68,7 +68,7 @@ public: ~SerfLockStore(); bool finishing() const; - OUString getLockToken( const OUString& rLock ); + OUString getLockToken(const OUString& rURI); OUString const* getLockTokenForURI(OUString const& rURI, css::ucb::Lock const* pLock); @@ -80,10 +80,10 @@ public: // -1: infinite lock, no refresh sal_Int32 nLastChanceToSendRefreshRequest ); - void updateLock( const OUString& rLock, + void updateLock( const OUString& rURI, sal_Int32 nLastChanceToSendRefreshRequest ); - void removeLock( const OUString& rLock ); + void removeLock(const OUString& rURI); void refreshLocks(); |