diff --git a/wsd/ClientRequestDispatcher.cpp b/wsd/ClientRequestDispatcher.cpp index 28fea06db96b8..9e150dc7a5f01 100644 --- a/wsd/ClientRequestDispatcher.cpp +++ b/wsd/ClientRequestDispatcher.cpp @@ -9,10 +9,15 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include +#include +#include #include #include #include +#include +#include #if ENABLE_FEATURE_LOCK #include "CommandControl.hpp" @@ -49,9 +54,13 @@ #include #include #include +#include #include +#include +#include #include #include +#include #include #include @@ -797,6 +806,8 @@ void ClientRequestDispatcher::handleIncomingMessage(SocketDisposition& dispositi handleWopiDiscoveryRequest(requestDetails, socket); else if (requestDetails.equals(1, "capabilities")) handleCapabilitiesRequest(request, socket); + else if (requestDetails.equals(1, "wopiAccessCheck")) + handleWopiAccessCheckRequest(request, message, socket); } else if (requestDetails.isGet("/robots.txt")) handleRobotsTxtRequest(request, socket); @@ -990,6 +1001,156 @@ void ClientRequestDispatcher::handleWopiDiscoveryRequest( LOG_INF("Sent discovery.xml successfully."); } +void ClientRequestDispatcher::handleWopiAccessCheckRequest(const Poco::Net::HTTPRequest& request, + Poco::MemoryInputStream& message, + const std::shared_ptr& socket) +{ + assert(socket && "Must have a valid socket"); + + LOG_DBG("Wopi Access Check request: " << request.getURI()); + + // Poco::JSON + Poco::JSON::Parser jsonParser; + Poco::Dynamic::Var parsingResult; + Poco::JSON::Object::Ptr object; + Poco::URI uri; + + std::string text(std::istreambuf_iterator(message), {}); + try + { + parsingResult = jsonParser.parse(text); + object = parsingResult.extract(); + + auto callbackUrlStr = object->getValue("callbackUrl"); + + uri = Poco::URI(callbackUrlStr); + } + catch (const std::exception& exception) + { + LOG_ERR_S("Wopi Access Check request error, json object expected got [" + << text << "] on request to URL: " << request.getURI() << exception.what()); + + HttpHelper::sendErrorAndShutdown(http::StatusCode::BadRequest, socket); + return; + } + + enum class CheckStatus + { + Ok = 0, + NotHttpSucess, + HostNotFound, + HostUnReachable, + UnspecifiedError, + ConnectionAborted, + ConnectionRefused, + InvalidCertificate, + CertificateValidation, + NotHttps, + NoScheme, + Timeout, + }; + map colorToString = { + { CheckStatus::Ok, "Ok" }, + { CheckStatus::NotHttpSucess, "NOT_HTTP_SUCESS" }, + { CheckStatus::HostNotFound, "HOST_NOT_FOUND" }, + { CheckStatus::HostUnReachable, "HOST_UNREACHABLE" }, + { CheckStatus::UnspecifiedError, "UNSPECIFIED_ERROR" }, + { CheckStatus::ConnectionAborted, "CONNECTION_ABORTED" }, + { CheckStatus::ConnectionRefused, "CONNECTION_REFUSED" }, + { CheckStatus::InvalidCertificate, "INVALID_CERTIFICATE" }, + { CheckStatus::NotHttps, "NOT_HTTPS" }, + { CheckStatus::NoScheme, "NO_SCHEME" }, + { CheckStatus::Timeout, "TIMEOUT" } + }; + + CheckStatus result = CheckStatus::Ok; + + if (uri.getScheme().empty()) + { + result = CheckStatus::NoScheme; + } + else if (uri.getScheme() != "https") + { + result = CheckStatus::NotHttps; + } + else + { + // request the url + try + { + const auto hostAddress(Poco::Net::DNS::resolve(uri.getHost())); + + Poco::Net::HTTPSClientSession httpSession(uri.getHost(), 443); + httpSession.setConnectTimeout(Poco::Timespan(0, 300)); + Poco::Net::HTTPRequest httpRequest(Poco::Net::HTTPRequest::HTTP_GET, + uri.getPathAndQuery()); + httpSession.sendRequest(httpRequest); + Poco::Net::HTTPResponse response; + std::istream* responseStream = &httpSession.receiveResponse(response); + + std::cout << responseStream->rdbuf(); + if (response.getStatus() != 200) + { + result = CheckStatus::NotHttpSucess; + } + } + catch (Poco::Net::HostNotFoundException& hostNotfound) + { + result = CheckStatus::HostNotFound; + } + catch (Poco::Net::NoAddressFoundException& noAddressFound) + { + result = CheckStatus::HostUnReachable; + } + catch (Poco::Net::ConnectionAbortedException& connectionAborted) + { + result = CheckStatus::ConnectionAborted; + } + catch (Poco::Net::ConnectionRefusedException& connectionRefused) + { + result = CheckStatus::ConnectionRefused; + } + catch (Poco::Net::InvalidCertificateException& invalidCertification) + { + result = CheckStatus::InvalidCertificate; + } + catch (Poco::Net::CertificateValidationException& certificateValidation) + { + result = CheckStatus::CertificateValidation; + } + catch (Poco::TimeoutException& timeout) + { + result = CheckStatus::Timeout; + } + catch (Poco::Exception& exception) + { + LOG_ERR_S("Wopi Access Check request error, query to callback [" + << uri.toString() << "] failed:" << exception.what() << exception.className() + << exception.name() << exception.message()); + + result = CheckStatus::UnspecifiedError; + } + } + + // construct the result + + Poco::JSON::Object::Ptr status = new Poco::JSON::Object; + status->set("status", (int)result); + status->set("details", map[result]); + + std::ostringstream ostrJSON; + status->stringify(ostrJSON); + const auto output = ostrJSON.str(); + + http::Response httpResponse(http::StatusCode::OK); + FileServerRequestHandler::hstsHeaders(httpResponse); + httpResponse.set("Last-Modified", Util::getHttpTimeNow()); + httpResponse.setBody(output, "application/json"); + httpResponse.set("X-Content-Type-Options", "nosniff"); + socket->sendAndShutdown(httpResponse); + LOG_INF("Sent capabilities.json successfully."); +} + void ClientRequestDispatcher::handleClipboardRequest(const Poco::Net::HTTPRequest& request, Poco::MemoryInputStream& message, SocketDisposition& disposition, diff --git a/wsd/ClientRequestDispatcher.hpp b/wsd/ClientRequestDispatcher.hpp index 148b036536ca1..79dbfb24204fd 100644 --- a/wsd/ClientRequestDispatcher.hpp +++ b/wsd/ClientRequestDispatcher.hpp @@ -67,6 +67,10 @@ class ClientRequestDispatcher final : public SimpleSocketHandler void handleCapabilitiesRequest(const Poco::Net::HTTPRequest& request, const std::shared_ptr& socket); + void handleWopiAccessCheckRequest(const Poco::Net::HTTPRequest& request, + Poco::MemoryInputStream& message, + const std::shared_ptr& socket); + static void handleClipboardRequest(const Poco::Net::HTTPRequest& request, Poco::MemoryInputStream& message, SocketDisposition& disposition,