2023-03-04 12:24:01 -06:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
|
|
|
|
/*
|
|
|
|
* Copyright the Collabora Online contributors.
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: MPL-2.0
|
|
|
|
*
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include <RequestVettingStation.hpp>
|
|
|
|
|
|
|
|
#include <COOLWSD.hpp>
|
2024-01-16 05:26:21 -06:00
|
|
|
#include <RequestDetails.hpp>
|
2023-03-04 12:24:01 -06:00
|
|
|
#include <TraceEvent.hpp>
|
|
|
|
#include <Exceptions.hpp>
|
|
|
|
#include <Log.hpp>
|
|
|
|
#include <DocumentBroker.hpp>
|
|
|
|
#include <ClientSession.hpp>
|
|
|
|
#include <common/JsonUtil.hpp>
|
|
|
|
#include <Util.hpp>
|
|
|
|
|
2024-03-23 07:46:11 -05:00
|
|
|
extern std::pair<std::shared_ptr<DocumentBroker>, std::string>
|
|
|
|
findOrCreateDocBroker(DocumentBroker::ChildType type, const std::string& uri,
|
2023-03-04 12:24:01 -06:00
|
|
|
const std::string& docKey, const std::string& id, const Poco::URI& uriPublic,
|
|
|
|
unsigned mobileAppDocId = 0);
|
2024-03-23 07:46:11 -05:00
|
|
|
|
2023-03-04 12:24:01 -06:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
void sendLoadResult(const std::shared_ptr<ClientSession>& clientSession, bool success,
|
|
|
|
const std::string& errorMsg)
|
|
|
|
{
|
|
|
|
const std::string result = success ? "" : "Error while loading document";
|
|
|
|
const std::string resultstr = success ? "true" : "false";
|
|
|
|
// Some sane limit, otherwise we get problems transferring this
|
|
|
|
// to the client with large strings (can be a whole webpage)
|
|
|
|
// Replace reserved characters
|
|
|
|
std::string errorMsgFormatted = COOLProtocol::getAbbreviatedMessage(errorMsg);
|
|
|
|
errorMsgFormatted = Poco::translate(errorMsg, "\"", "'");
|
|
|
|
clientSession->sendMessage("commandresult: { \"command\": \"load\", \"success\": " + resultstr +
|
|
|
|
", \"result\": \"" + result + "\", \"errorMsg\": \"" +
|
|
|
|
errorMsgFormatted + "\"}");
|
|
|
|
}
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
2024-01-16 05:26:21 -06:00
|
|
|
void RequestVettingStation::handleRequest(const std::string& id)
|
|
|
|
{
|
|
|
|
_id = id;
|
|
|
|
|
|
|
|
const std::string url = _requestDetails.getDocumentURI();
|
|
|
|
|
|
|
|
const auto uriPublic = RequestDetails::sanitizeURI(url);
|
|
|
|
const auto docKey = RequestDetails::getDocKey(uriPublic);
|
|
|
|
const std::string fileId = Util::getFilenameFromURL(docKey);
|
|
|
|
Util::mapAnonymized(fileId, fileId); // Identity mapping, since fileId is already obfuscated
|
|
|
|
|
|
|
|
// Check if readonly session is required
|
|
|
|
bool isReadOnly = false;
|
|
|
|
for (const auto& param : uriPublic.getQueryParameters())
|
|
|
|
{
|
|
|
|
LOG_TRC("Query param: " << param.first << ", value: " << param.second);
|
|
|
|
if (param.first == "permission" && param.second == "readonly")
|
|
|
|
{
|
|
|
|
isReadOnly = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-21 18:38:58 -06:00
|
|
|
LOG_INF("URL [" << COOLWSD::anonymizeUrl(url)
|
|
|
|
<< "] will be proactively vetted. Sanitized uriPublic: ["
|
|
|
|
<< COOLWSD::anonymizeUrl(uriPublic.toString()) << "], docKey: [" << docKey
|
|
|
|
<< "], session: [" << _id << "], fileId: [" << fileId << "] "
|
|
|
|
<< (isReadOnly ? "(readonly)" : "(writable)"));
|
2024-01-16 05:26:21 -06:00
|
|
|
|
|
|
|
// Before we create DocBroker with a SocketPoll thread, a ClientSession, and a Kit process,
|
|
|
|
// we need to vet this request by invoking CheckFileInfo.
|
|
|
|
// For that, we need the storage settings to create a connection.
|
|
|
|
const StorageBase::StorageType storageType =
|
|
|
|
StorageBase::validate(uriPublic, /*takeOwnership=*/false);
|
|
|
|
switch (storageType)
|
|
|
|
{
|
|
|
|
case StorageBase::StorageType::Unsupported:
|
|
|
|
LOG_ERR("Unsupported URI [" << COOLWSD::anonymizeUrl(uriPublic.toString())
|
|
|
|
<< "] or no storage configured");
|
|
|
|
throw BadRequestException("No Storage configured or invalid URI " +
|
|
|
|
COOLWSD::anonymizeUrl(uriPublic.toString()) + ']');
|
|
|
|
|
|
|
|
break;
|
|
|
|
case StorageBase::StorageType::Unauthorized:
|
|
|
|
LOG_ERR("No authorized hosts found matching the target host [" << uriPublic.getHost()
|
|
|
|
<< "] in config");
|
|
|
|
sendErrorAndShutdown(_ws, "error: cmd=internal kind=unauthorized",
|
|
|
|
WebSocketHandler::StatusCodes::POLICY_VIOLATION);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case StorageBase::StorageType::FileSystem:
|
|
|
|
LOG_INF("URI [" << COOLWSD::anonymizeUrl(uriPublic.toString()) << "] on docKey ["
|
|
|
|
<< docKey << "] is for a FileSystem document");
|
|
|
|
break;
|
|
|
|
#if !MOBILEAPP
|
|
|
|
case StorageBase::StorageType::Wopi:
|
|
|
|
LOG_INF("URI [" << COOLWSD::anonymizeUrl(uriPublic.toString()) << "] on docKey ["
|
|
|
|
<< docKey << "] is for a WOPI document");
|
|
|
|
|
|
|
|
// CheckFileInfo asynchronously.
|
2024-03-02 06:03:16 -06:00
|
|
|
checkFileInfo(uriPublic, isReadOnly, RedirectionLimit);
|
2024-01-16 05:26:21 -06:00
|
|
|
break;
|
|
|
|
#endif //!MOBILEAPP
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-13 12:40:23 -06:00
|
|
|
void RequestVettingStation::handleRequest(const std::string& id,
|
2024-01-16 05:26:21 -06:00
|
|
|
const RequestDetails& requestDetails,
|
2024-01-13 12:40:23 -06:00
|
|
|
const std::shared_ptr<WebSocketHandler>& ws,
|
|
|
|
const std::shared_ptr<StreamSocket>& socket,
|
|
|
|
unsigned mobileAppDocId, SocketDisposition& disposition)
|
2023-03-04 12:24:01 -06:00
|
|
|
{
|
2024-01-13 12:40:23 -06:00
|
|
|
_id = id;
|
2024-01-16 05:26:21 -06:00
|
|
|
_requestDetails = requestDetails;
|
2024-01-13 12:40:23 -06:00
|
|
|
_ws = ws;
|
|
|
|
_socket = socket;
|
|
|
|
_mobileAppDocId = mobileAppDocId;
|
|
|
|
|
2023-03-04 12:24:01 -06:00
|
|
|
const std::string url = _requestDetails.getDocumentURI();
|
|
|
|
|
|
|
|
const auto uriPublic = RequestDetails::sanitizeURI(url);
|
|
|
|
const auto docKey = RequestDetails::getDocKey(uriPublic);
|
|
|
|
const std::string fileId = Util::getFilenameFromURL(docKey);
|
|
|
|
Util::mapAnonymized(fileId, fileId); // Identity mapping, since fileId is already obfuscated
|
|
|
|
|
|
|
|
// Check if readonly session is required
|
|
|
|
bool isReadOnly = false;
|
|
|
|
for (const auto& param : uriPublic.getQueryParameters())
|
|
|
|
{
|
|
|
|
LOG_TRC("Query param: " << param.first << ", value: " << param.second);
|
|
|
|
if (param.first == "permission" && param.second == "readonly")
|
|
|
|
{
|
|
|
|
isReadOnly = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-21 18:38:58 -06:00
|
|
|
LOG_INF("URL [" << COOLWSD::anonymizeUrl(url) << "] for WS Request. Sanitized uriPublic: ["
|
|
|
|
<< COOLWSD::anonymizeUrl(uriPublic.toString()) << "], docKey: [" << docKey
|
|
|
|
<< "], session: [" << _id << "], fileId: [" << fileId << "] "
|
|
|
|
<< (isReadOnly ? "(readonly)" : "(writable)"));
|
2023-03-04 12:24:01 -06:00
|
|
|
|
|
|
|
// Before we create DocBroker with a SocketPoll thread, a ClientSession, and a Kit process,
|
|
|
|
// we need to vet this request by invoking CheckFileInfo.
|
|
|
|
// For that, we need the storage settings to create a connection.
|
|
|
|
const StorageBase::StorageType storageType =
|
|
|
|
StorageBase::validate(uriPublic, /*takeOwnership=*/false);
|
|
|
|
switch (storageType)
|
|
|
|
{
|
|
|
|
case StorageBase::StorageType::Unsupported:
|
|
|
|
LOG_ERR("Unsupported URI [" << COOLWSD::anonymizeUrl(uriPublic.toString())
|
|
|
|
<< "] or no storage configured");
|
|
|
|
throw BadRequestException("No Storage configured or invalid URI " +
|
|
|
|
COOLWSD::anonymizeUrl(uriPublic.toString()) + ']');
|
|
|
|
|
|
|
|
break;
|
|
|
|
case StorageBase::StorageType::Unauthorized:
|
2023-05-22 04:43:37 -05:00
|
|
|
LOG_ERR("No authorized hosts found matching the target host [" << uriPublic.getHost()
|
|
|
|
<< "] in config");
|
2024-01-11 19:43:23 -06:00
|
|
|
sendErrorAndShutdown(_ws, "error: cmd=internal kind=unauthorized",
|
2023-09-17 16:24:16 -05:00
|
|
|
WebSocketHandler::StatusCodes::POLICY_VIOLATION);
|
2023-03-04 12:24:01 -06:00
|
|
|
break;
|
2023-05-22 04:43:37 -05:00
|
|
|
|
2023-03-04 12:24:01 -06:00
|
|
|
case StorageBase::StorageType::FileSystem:
|
2023-05-31 05:43:00 -05:00
|
|
|
LOG_INF("URI [" << COOLWSD::anonymizeUrl(uriPublic.toString()) << "] on docKey ["
|
|
|
|
<< docKey << "] is for a FileSystem document");
|
|
|
|
|
2023-05-21 21:31:29 -05:00
|
|
|
// Remove from the current poll and transfer.
|
|
|
|
disposition.setMove(
|
|
|
|
[this, docKey, url, uriPublic,
|
|
|
|
isReadOnly](const std::shared_ptr<Socket>& moveSocket)
|
|
|
|
{
|
|
|
|
LOG_TRC_S('#' << moveSocket->getFD()
|
|
|
|
<< ": Dissociating client socket from "
|
|
|
|
"ClientRequestDispatcher and creating DocBroker for ["
|
|
|
|
<< docKey << ']');
|
|
|
|
|
|
|
|
// Create the DocBroker.
|
2024-01-22 02:25:44 -06:00
|
|
|
if (createDocBroker(docKey, url, uriPublic))
|
|
|
|
{
|
|
|
|
assert(_docBroker && "Must have docBroker");
|
|
|
|
createClientSession(docKey, url, uriPublic, isReadOnly);
|
|
|
|
}
|
2023-05-21 21:31:29 -05:00
|
|
|
});
|
2023-03-04 12:24:01 -06:00
|
|
|
break;
|
2024-01-20 12:02:33 -06:00
|
|
|
#if !MOBILEAPP
|
2023-03-04 12:24:01 -06:00
|
|
|
case StorageBase::StorageType::Wopi:
|
2023-05-31 05:43:00 -05:00
|
|
|
LOG_INF("URI [" << COOLWSD::anonymizeUrl(uriPublic.toString()) << "] on docKey ["
|
|
|
|
<< docKey << "] is for a WOPI document");
|
2023-05-21 21:31:29 -05:00
|
|
|
// Remove from the current poll and transfer.
|
|
|
|
disposition.setMove(
|
2024-01-13 12:40:23 -06:00
|
|
|
[this, docKey, url, uriPublic,
|
2023-05-21 21:31:29 -05:00
|
|
|
isReadOnly](const std::shared_ptr<Socket>& moveSocket)
|
|
|
|
{
|
|
|
|
LOG_TRC_S('#' << moveSocket->getFD()
|
|
|
|
<< ": Dissociating client socket from "
|
|
|
|
"ClientRequestDispatcher and invoking CheckFileInfo for ["
|
2024-03-02 06:03:16 -06:00
|
|
|
<< docKey << "], "
|
|
|
|
<< (_checkFileInfo ? CheckFileInfo::name(_checkFileInfo->state())
|
|
|
|
: "no CheckFileInfo"));
|
2023-05-21 21:31:29 -05:00
|
|
|
|
|
|
|
// CheckFileInfo and only when it's good create DocBroker.
|
2024-03-02 06:03:16 -06:00
|
|
|
if (_checkFileInfo && _checkFileInfo->state() == CheckFileInfo::State::Active)
|
2024-01-16 05:26:21 -06:00
|
|
|
{
|
|
|
|
// Wait for CheckFileInfo result.
|
|
|
|
LOG_DBG("CheckFileInfo request is in progress. Will resume when done");
|
|
|
|
}
|
2024-03-02 06:03:16 -06:00
|
|
|
else if (_checkFileInfo &&
|
|
|
|
_checkFileInfo->state() == CheckFileInfo::State::Pass &&
|
|
|
|
_checkFileInfo->wopiInfo())
|
2024-01-16 05:26:21 -06:00
|
|
|
{
|
|
|
|
// We have a valid CheckFileInfo result; Create the DocBroker.
|
2024-01-22 02:25:44 -06:00
|
|
|
if (createDocBroker(docKey, url, uriPublic))
|
|
|
|
{
|
|
|
|
assert(_docBroker && "Must have docBroker");
|
|
|
|
createClientSession(docKey, url, uriPublic, isReadOnly);
|
|
|
|
}
|
2024-01-16 05:26:21 -06:00
|
|
|
}
|
2024-03-02 06:03:16 -06:00
|
|
|
else if (_checkFileInfo == nullptr ||
|
|
|
|
_checkFileInfo->state() == CheckFileInfo::State::None)
|
2024-01-16 05:26:21 -06:00
|
|
|
{
|
|
|
|
// We don't have CheckFileInfo
|
2024-03-02 06:03:16 -06:00
|
|
|
checkFileInfo(uriPublic, isReadOnly, RedirectionLimit);
|
2024-01-16 05:26:21 -06:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2024-03-02 06:03:16 -06:00
|
|
|
// E.g. Timeout.
|
2024-03-12 19:52:23 -05:00
|
|
|
LOG_ERR_S('#'
|
|
|
|
<< moveSocket->getFD() << ": CheckFileInfo failed for [" << docKey
|
|
|
|
<< "], "
|
|
|
|
<< (_checkFileInfo ? CheckFileInfo::name(_checkFileInfo->state())
|
|
|
|
: "no CheckFileInfo"));
|
2024-01-16 05:26:21 -06:00
|
|
|
sendErrorAndShutdown(_ws, "error: cmd=internal kind=unauthorized",
|
|
|
|
WebSocketHandler::StatusCodes::POLICY_VIOLATION);
|
|
|
|
}
|
2023-05-21 21:31:29 -05:00
|
|
|
});
|
2023-03-04 12:24:01 -06:00
|
|
|
break;
|
2024-01-20 12:02:33 -06:00
|
|
|
#endif //!MOBILEAPP
|
2023-03-04 12:24:01 -06:00
|
|
|
}
|
2023-05-21 19:58:46 -05:00
|
|
|
}
|
|
|
|
|
2024-01-20 12:02:33 -06:00
|
|
|
#if !MOBILEAPP
|
2024-03-02 06:03:16 -06:00
|
|
|
void RequestVettingStation::checkFileInfo(const Poco::URI& uri, bool isReadOnly, int redirectLimit)
|
2023-05-21 19:58:46 -05:00
|
|
|
{
|
2024-03-02 06:03:16 -06:00
|
|
|
auto cfiContinuation = [this, isReadOnly]([[maybe_unused]] CheckFileInfo& checkFileInfo)
|
2023-03-04 12:24:01 -06:00
|
|
|
{
|
2024-03-02 06:03:16 -06:00
|
|
|
assert(&checkFileInfo == _checkFileInfo.get() && "Unknown CheckFileInfo instance");
|
|
|
|
if (_checkFileInfo && _checkFileInfo->state() == CheckFileInfo::State::Pass &&
|
|
|
|
_checkFileInfo->wopiInfo())
|
2023-05-21 21:31:29 -05:00
|
|
|
{
|
2024-03-23 09:13:12 -05:00
|
|
|
// The final URL might be different due to redirection.
|
|
|
|
const std::string url = checkFileInfo.url().toString();
|
|
|
|
const auto uriPublic = RequestDetails::sanitizeURI(url);
|
|
|
|
const auto docKey = RequestDetails::getDocKey(uriPublic);
|
|
|
|
LOG_DBG("WOPI::CheckFileInfo succeeded and will create DocBroker ["
|
|
|
|
<< docKey << "] now with URL: [" << url << ']');
|
2024-03-02 06:03:16 -06:00
|
|
|
if (_ws)
|
2023-05-21 21:31:29 -05:00
|
|
|
{
|
2024-03-02 06:03:16 -06:00
|
|
|
if (createDocBroker(docKey, url, uriPublic))
|
|
|
|
{
|
|
|
|
assert(_docBroker && "Must have docBroker");
|
|
|
|
createClientSession(docKey, url, uriPublic, isReadOnly);
|
|
|
|
}
|
2023-05-21 21:31:29 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2024-03-02 06:03:16 -06:00
|
|
|
LOG_DBG("WOPI::CheckFileInfo succeeded but we don't have the client's "
|
2024-03-23 09:13:12 -05:00
|
|
|
"WebSocket yet. Creating DocBroker without connection");
|
|
|
|
auto [docBroker, errorMsg] =
|
|
|
|
findOrCreateDocBroker(DocumentBroker::ChildType::Interactive, url, docKey, _id,
|
|
|
|
uriPublic, _mobileAppDocId);
|
|
|
|
_docBroker = docBroker;
|
|
|
|
if (!_docBroker)
|
|
|
|
{
|
|
|
|
LOG_DBG("Failed to find document [" << docKey << "]: " << errorMsg);
|
|
|
|
}
|
2023-05-21 21:31:29 -05:00
|
|
|
}
|
|
|
|
}
|
2024-03-02 06:03:16 -06:00
|
|
|
else
|
2023-03-04 12:24:01 -06:00
|
|
|
{
|
2024-03-02 06:03:16 -06:00
|
|
|
if (_ws)
|
2023-05-22 04:43:37 -05:00
|
|
|
{
|
2024-03-02 06:03:16 -06:00
|
|
|
LOG_DBG("WOPI::CheckFileInfo failed, sending error and closing connection now");
|
|
|
|
sendErrorAndShutdown(_ws, "error: cmd=storage kind=unauthorized",
|
|
|
|
WebSocketHandler::StatusCodes::POLICY_VIOLATION);
|
2023-05-22 04:43:37 -05:00
|
|
|
}
|
2023-03-10 17:35:17 -06:00
|
|
|
else
|
2024-01-16 05:26:21 -06:00
|
|
|
{
|
2024-03-02 06:03:16 -06:00
|
|
|
LOG_DBG("WOPI::CheckFileInfo failed but no client WebSocket to send error to");
|
2024-01-16 05:26:21 -06:00
|
|
|
}
|
2023-03-10 17:35:17 -06:00
|
|
|
}
|
2023-03-04 12:24:01 -06:00
|
|
|
};
|
|
|
|
|
2024-03-02 06:03:16 -06:00
|
|
|
// CheckFileInfo asynchronously.
|
|
|
|
_checkFileInfo =
|
|
|
|
std::make_unique<CheckFileInfo>(_poll, uri, std::move(cfiContinuation), redirectLimit);
|
2023-03-04 12:24:01 -06:00
|
|
|
}
|
2024-01-20 12:02:33 -06:00
|
|
|
#endif //!MOBILEAPP
|
2023-03-09 18:01:44 -06:00
|
|
|
|
2024-01-22 02:25:44 -06:00
|
|
|
bool RequestVettingStation::createDocBroker(const std::string& docKey, const std::string& url,
|
|
|
|
const Poco::URI& uriPublic)
|
2023-03-09 18:01:44 -06:00
|
|
|
{
|
|
|
|
// Request a kit process for this doc.
|
2024-03-23 11:58:37 -05:00
|
|
|
const auto [docBroker, error] = findOrCreateDocBroker(
|
|
|
|
DocumentBroker::ChildType::Interactive, url, docKey, _id, uriPublic, _mobileAppDocId);
|
|
|
|
|
|
|
|
_docBroker = docBroker;
|
|
|
|
if (_docBroker)
|
|
|
|
{
|
|
|
|
// Indicate to the client that we're connecting to the docbroker.
|
|
|
|
if (_ws)
|
|
|
|
{
|
|
|
|
const std::string statusConnect = "statusindicator: connect";
|
|
|
|
LOG_TRC("Sending to Client [" << statusConnect << ']');
|
|
|
|
_ws->sendTextMessage(statusConnect.data(), statusConnect.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG_DBG("DocBroker [" << docKey << "] acquired for [" << url << ']');
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Failed.
|
|
|
|
LOG_ERR("Failed to create DocBroker [" << docKey << ']');
|
|
|
|
if (_ws)
|
2023-03-09 18:01:44 -06:00
|
|
|
{
|
2024-01-11 19:43:23 -06:00
|
|
|
sendErrorAndShutdown(_ws, "error: cmd=internal kind=load",
|
2023-09-17 16:24:16 -05:00
|
|
|
WebSocketHandler::StatusCodes::UNEXPECTED_CONDITION);
|
2023-03-09 18:01:44 -06:00
|
|
|
}
|
|
|
|
|
2024-03-23 11:58:37 -05:00
|
|
|
return false;
|
2024-01-22 02:25:44 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
void RequestVettingStation::createClientSession(const std::string& docKey, const std::string& url,
|
|
|
|
const Poco::URI& uriPublic, const bool isReadOnly)
|
|
|
|
{
|
|
|
|
assert(_docBroker && "Must have DocBroker");
|
|
|
|
|
2023-03-09 18:01:44 -06:00
|
|
|
std::shared_ptr<ClientSession> clientSession =
|
2024-01-22 02:25:44 -06:00
|
|
|
_docBroker->createNewClientSession(_ws, _id, uriPublic, isReadOnly, _requestDetails);
|
2023-03-09 18:01:44 -06:00
|
|
|
if (!clientSession)
|
|
|
|
{
|
2023-05-22 04:43:37 -05:00
|
|
|
LOG_ERR("Failed to create Client Session [" << _id << "] on docKey [" << docKey << ']');
|
2024-01-11 19:43:23 -06:00
|
|
|
sendErrorAndShutdown(_ws, "error: cmd=internal kind=load",
|
2023-09-17 16:24:16 -05:00
|
|
|
WebSocketHandler::StatusCodes::UNEXPECTED_CONDITION);
|
2023-05-22 04:43:37 -05:00
|
|
|
return;
|
2023-03-09 18:01:44 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
LOG_DBG("ClientSession [" << clientSession->getName() << "] for [" << docKey
|
|
|
|
<< "] acquired for [" << url << ']');
|
|
|
|
|
2024-03-02 06:03:16 -06:00
|
|
|
Poco::JSON::Object::Ptr wopiInfo;
|
|
|
|
#if !MOBILEAPP
|
|
|
|
assert((!_checkFileInfo || _checkFileInfo->wopiInfo()) &&
|
|
|
|
"Must have WopiInfo when CheckFileInfo exists");
|
|
|
|
wopiInfo = _checkFileInfo ? _checkFileInfo->wopiInfo() : nullptr;
|
|
|
|
#endif // !MOBILEAPP
|
|
|
|
|
2023-03-09 18:01:44 -06:00
|
|
|
// Transfer the client socket to the DocumentBroker when we get back to the poll:
|
2023-06-03 08:37:02 -05:00
|
|
|
const auto ws = _ws;
|
2024-01-22 02:25:44 -06:00
|
|
|
const auto docBroker = _docBroker;
|
|
|
|
_docBroker->setupTransfer(
|
2023-03-09 18:01:44 -06:00
|
|
|
_socket,
|
2024-02-10 14:18:01 -06:00
|
|
|
[clientSession, uriPublic, wopiInfo=std::move(wopiInfo), ws,
|
2023-06-03 08:37:02 -05:00
|
|
|
docBroker](const std::shared_ptr<Socket>& moveSocket) mutable
|
2024-01-22 02:25:44 -06:00
|
|
|
{
|
2023-03-09 18:01:44 -06:00
|
|
|
try
|
|
|
|
{
|
|
|
|
LOG_DBG_S("Transfering docBroker [" << docBroker->getDocKey() << ']');
|
|
|
|
|
|
|
|
auto streamSocket = std::static_pointer_cast<StreamSocket>(moveSocket);
|
|
|
|
|
|
|
|
// Set WebSocketHandler's socket after its construction for shared_ptr goodness.
|
2023-06-03 08:37:02 -05:00
|
|
|
streamSocket->setHandler(ws);
|
2023-03-09 18:01:44 -06:00
|
|
|
|
|
|
|
LOG_DBG_S('#' << moveSocket->getFD() << " handler is " << clientSession->getName());
|
|
|
|
|
2023-03-10 17:35:17 -06:00
|
|
|
std::unique_ptr<WopiStorage::WOPIFileInfo> wopiFileInfo;
|
2024-01-20 12:02:33 -06:00
|
|
|
#if !MOBILEAPP
|
2023-03-10 17:35:17 -06:00
|
|
|
if (wopiInfo)
|
|
|
|
{
|
|
|
|
std::size_t size = 0;
|
|
|
|
std::string filename, ownerId, lastModifiedTime;
|
|
|
|
|
|
|
|
JsonUtil::findJSONValue(wopiInfo, "Size", size);
|
|
|
|
JsonUtil::findJSONValue(wopiInfo, "OwnerId", ownerId);
|
|
|
|
JsonUtil::findJSONValue(wopiInfo, "BaseFileName", filename);
|
|
|
|
JsonUtil::findJSONValue(wopiInfo, "LastModifiedTime", lastModifiedTime);
|
|
|
|
|
|
|
|
StorageBase::FileInfo fileInfo =
|
|
|
|
StorageBase::FileInfo({ filename, ownerId, lastModifiedTime });
|
|
|
|
|
|
|
|
wopiFileInfo =
|
|
|
|
std::make_unique<WopiStorage::WOPIFileInfo>(fileInfo, wopiInfo, uriPublic);
|
|
|
|
}
|
2024-01-20 12:02:33 -06:00
|
|
|
#else // MOBILEAPP
|
|
|
|
assert(!wopiInfo && "Wopi is not used on mobile");
|
|
|
|
#endif // MOBILEAPP
|
2023-03-10 17:35:17 -06:00
|
|
|
|
2023-03-09 18:01:44 -06:00
|
|
|
// Add and load the session.
|
2023-03-10 17:35:17 -06:00
|
|
|
// Will download synchronously, but in own docBroker thread.
|
|
|
|
docBroker->addSession(clientSession, std::move(wopiFileInfo));
|
2023-03-09 18:01:44 -06:00
|
|
|
|
|
|
|
COOLWSD::checkDiskSpaceAndWarnClients(true);
|
|
|
|
// Users of development versions get just an info
|
|
|
|
// when reaching max documents or connections
|
|
|
|
COOLWSD::checkSessionLimitsAndWarnClients();
|
|
|
|
|
2024-03-23 07:29:54 -05:00
|
|
|
sendLoadResult(clientSession, /*success=*/true, /*errorMsg=*/std::string());
|
2023-03-09 18:01:44 -06:00
|
|
|
}
|
|
|
|
catch (const UnauthorizedRequestException& exc)
|
|
|
|
{
|
|
|
|
LOG_ERR_S("Unauthorized Request while starting session on "
|
|
|
|
<< docBroker->getDocKey() << " for socket #" << moveSocket->getFD()
|
|
|
|
<< ". Terminating connection. Error: " << exc.what());
|
2024-01-11 19:43:23 -06:00
|
|
|
sendErrorAndShutdown(ws, "error: cmd=internal kind=unauthorized",
|
2023-09-17 16:24:16 -05:00
|
|
|
WebSocketHandler::StatusCodes::POLICY_VIOLATION);
|
2023-03-09 18:01:44 -06:00
|
|
|
}
|
|
|
|
catch (const StorageConnectionException& exc)
|
|
|
|
{
|
|
|
|
LOG_ERR_S("Storage error while starting session on "
|
|
|
|
<< docBroker->getDocKey() << " for socket #" << moveSocket->getFD()
|
|
|
|
<< ". Terminating connection. Error: " << exc.what());
|
2024-01-11 19:43:23 -06:00
|
|
|
sendErrorAndShutdown(ws, "error: cmd=storage kind=loadfailed",
|
2023-09-17 16:24:16 -05:00
|
|
|
WebSocketHandler::StatusCodes::POLICY_VIOLATION);
|
2023-03-09 18:01:44 -06:00
|
|
|
}
|
2023-09-17 17:33:01 -05:00
|
|
|
catch (const StorageSpaceLowException& exc)
|
|
|
|
{
|
|
|
|
LOG_ERR_S("Disk-Full error while starting session on "
|
|
|
|
<< docBroker->getDocKey() << " for socket #" << moveSocket->getFD()
|
|
|
|
<< ". Terminating connection. Error: " << exc.what());
|
2024-01-11 19:43:23 -06:00
|
|
|
sendErrorAndShutdown(ws, "error: cmd=internal kind=diskfull",
|
2023-06-03 08:37:02 -05:00
|
|
|
WebSocketHandler::StatusCodes::UNEXPECTED_CONDITION);
|
2023-09-17 17:33:01 -05:00
|
|
|
}
|
2023-03-09 18:01:44 -06:00
|
|
|
catch (const std::exception& exc)
|
|
|
|
{
|
|
|
|
LOG_ERR_S("Error while starting session on "
|
|
|
|
<< docBroker->getDocKey() << " for socket #" << moveSocket->getFD()
|
|
|
|
<< ". Terminating connection. Error: " << exc.what());
|
2024-01-11 19:43:23 -06:00
|
|
|
sendErrorAndShutdown(ws, "error: cmd=storage kind=loadfailed",
|
2023-09-17 16:24:16 -05:00
|
|
|
WebSocketHandler::StatusCodes::POLICY_VIOLATION);
|
2023-03-09 18:01:44 -06:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2023-09-17 16:24:16 -05:00
|
|
|
|
|
|
|
void RequestVettingStation::sendErrorAndShutdown(const std::shared_ptr<WebSocketHandler>& ws,
|
|
|
|
const std::string& msg,
|
|
|
|
WebSocketHandler::StatusCodes statusCode)
|
|
|
|
{
|
2024-03-12 19:52:23 -05:00
|
|
|
if (ws)
|
|
|
|
{
|
|
|
|
ws->sendMessage(msg);
|
|
|
|
ws->shutdown(statusCode, msg); // And ignore input (done in shutdown()).
|
|
|
|
}
|
2023-06-03 08:37:02 -05:00
|
|
|
}
|