wsd/ClientSession.cpp | 244 +++++++++++++++++++++++++++++++++++++++++++++++- wsd/ClientSession.hpp | 22 +++- wsd/PrisonerSession.cpp | 7 - 3 files changed, 257 insertions(+), 16 deletions(-)
New commits: commit e584a514434e733fac1edc16a5d64842d5a61683 Author: Ashod Nakashian <[email protected]> Date: Sat Jan 21 21:38:11 2017 -0500 wsd: merge PrisonerSession into ClientSession Change-Id: I8ae924a7afae61b9c6e25e15ace187918d1e006b Reviewed-on: https://gerrit.libreoffice.org/33433 Reviewed-by: Ashod Nakashian <[email protected]> Tested-by: Ashod Nakashian <[email protected]> diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp index 63ca129..d36705c 100644 --- a/wsd/ClientSession.cpp +++ b/wsd/ClientSession.cpp @@ -25,6 +25,7 @@ using namespace LOOLProtocol; +using Poco::Path; using Poco::StringTokenizer; ClientSession::ClientSession(const std::string& id, @@ -43,7 +44,6 @@ ClientSession::ClientSession(const std::string& id, { LOG_INF("ClientSession ctor [" << getName() << "]."); - _peer = std::make_shared<PrisonerSession>(*this, docBroker); _senderThread = std::thread([this]{ senderThread(); }); } @@ -60,7 +60,7 @@ ClientSession::~ClientSession() bool ClientSession::isLoaded() const { - return _isLoadRequested && _peer && _peer->gotStatus(); + return _isLoadRequested && gotStatus(); } bool ClientSession::_handleInput(const char *buffer, int length) @@ -481,9 +481,245 @@ void ClientSession::senderThread() LOG_DBG(getName() << " SenderThread finished"); } -bool ClientSession::handleKitToClientMessage(const char* data, const int size) +bool ClientSession::handleKitToClientMessage(const char* buffer, const int length) { - return _peer->handleInput(data, size); + LOG_TRC(getName() + ": handling [" << getAbbreviatedMessage(buffer, length) << "]."); + const std::string firstLine = getFirstLine(buffer, length); + StringTokenizer tokens(firstLine, " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM); + + const auto docBroker = _docBroker.lock(); + if (!docBroker) + { + LOG_ERR("No DocBroker to handle kit-to-client message: " << firstLine); + return false; + } + + LOOLWSD::dumpOutgoingTrace(docBroker->getJailId(), getId(), firstLine); + + if (tokens[0] == "unocommandresult:") + { + const std::string stringMsg(buffer, length); + LOG_INF(getName() << ": Command: " << stringMsg); + const auto index = stringMsg.find_first_of('{'); + if (index != std::string::npos) + { + const std::string stringJSON = stringMsg.substr(index); + Poco::JSON::Parser parser; + const auto parsedJSON = parser.parse(stringJSON); + const auto& object = parsedJSON.extract<Poco::JSON::Object::Ptr>(); + if (object->get("commandName").toString() == ".uno:Save") + { + bool success = object->get("success").toString() == "true"; + std::string result; + if (object->has("result")) + { + const auto parsedResultJSON = object->get("result"); + const auto& resultObj = parsedResultJSON.extract<Poco::JSON::Object::Ptr>(); + if (resultObj->get("type").toString() == "string") + result = resultObj->get("value").toString(); + } + + // Save to Storage and log result. + docBroker->save(getId(), success, result); + return true; + } + } + else + { + LOG_WRN("Expected json unocommandresult. Ignoring: " << stringMsg); + } + } + else if (tokens[0] == "error:") + { + std::string errorCommand; + std::string errorKind; + if (getTokenString(tokens[1], "cmd", errorCommand) && + getTokenString(tokens[2], "kind", errorKind) ) + { + if (errorCommand == "load") + { + if (errorKind == "passwordrequired:to-view" || + errorKind == "passwordrequired:to-modify" || + errorKind == "wrongpassword") + { + const auto payload = std::make_shared<Message>(buffer, length, + Message::Dir::Out, + Message::Type::Text); + forwardToClient(payload); + LOG_WRN("Document load failed: " << errorKind); + return false; + } + } + } + } + else if (tokens[0] == "curpart:" && tokens.count() == 2) + { + //TODO: Should forward to client? + int curPart; + return getTokenInteger(tokens[1], "part", curPart); + } + else if (tokens.count() == 2 && tokens[0] == "saveas:") + { + std::string url; + if (!getTokenString(tokens[1], "url", url)) + { + LOG_ERR("Bad syntax for: " << firstLine); + return false; + } + + // Save-as completed, inform the ClientSession. + const std::string filePrefix("file:///"); + if (url.find(filePrefix) == 0) + { + // Rewrite file:// URLs, as they are visible to the outside world. + const Path path(docBroker->getJailRoot(), url.substr(filePrefix.length())); + if (Poco::File(path).exists()) + { + url = filePrefix + path.toString().substr(1); + } + else + { + // Blank for failure. + LOG_DBG("SaveAs produced no output, producing blank url."); + url.clear(); + } + } + + setSaveAsUrl(url); + return true; + } + else if (tokens.count() == 2 && tokens[0] == "statechanged:") + { + if (docBroker) + { + StringTokenizer stateTokens(tokens[1], "=", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM); + if (stateTokens.count() == 2 && stateTokens[0] == ".uno:ModifiedStatus") + { + docBroker->setModified(stateTokens[1] == "true"); + } + } + } + + if (!_isDocPasswordProtected) + { + if (tokens[0] == "tile:") + { + assert(false && "Tile traffic should go through the DocumentBroker-LoKit WS."); + } + else if (tokens[0] == "status:") + { + _gotStatus = true; + docBroker->setLoaded(); + + // Forward the status response to the client. + const auto payload = std::make_shared<Message>(buffer, length, + Message::Dir::Out, + Message::Type::Text); + return forwardToClient(payload); + } + else if (tokens[0] == "commandvalues:") + { + const std::string stringMsg(buffer, length); + const auto index = stringMsg.find_first_of('{'); + if (index != std::string::npos) + { + const std::string stringJSON = stringMsg.substr(index); + Poco::JSON::Parser parser; + const auto result = parser.parse(stringJSON); + const auto& object = result.extract<Poco::JSON::Object::Ptr>(); + const std::string commandName = object->has("commandName") ? object->get("commandName").toString() : ""; + if (commandName == ".uno:CharFontName" || + commandName == ".uno:StyleApply") + { + // other commands should not be cached + docBroker->tileCache().saveTextFile(stringMsg, "cmdValues" + commandName + ".txt"); + } + } + } + else if (tokens[0] == "partpagerectangles:") + { + if (tokens.count() > 1 && !tokens[1].empty()) + { + docBroker->tileCache().saveTextFile(std::string(buffer, length), "partpagerectangles.txt"); + } + } + else if (tokens[0] == "invalidatetiles:") + { + assert(firstLine.size() == static_cast<std::string::size_type>(length)); + docBroker->invalidateTiles(firstLine); + } + else if (tokens[0] == "invalidatecursor:") + { + assert(firstLine.size() == static_cast<std::string::size_type>(length)); + StringTokenizer firstLineTokens(firstLine, " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM); + int x = 0, y = 0, w = 0, h = 0; + if (firstLineTokens.count() > 2 && + stringToInteger(firstLineTokens[1], x) && + stringToInteger(firstLineTokens[2], y)) + { + if (firstLineTokens.count() > 3) + { + stringToInteger(firstLineTokens[3], w); + stringToInteger(firstLineTokens[4], h); + } + docBroker->invalidateCursor(x, y, w, h); + } + else + { + LOG_ERR("Unable to parse " << firstLine); + } + } + else if (tokens[0] == "renderfont:") + { + std::string font, text; + if (tokens.count() < 3 || + !getTokenString(tokens[1], "font", font)) + { + LOG_ERR("Bad syntax for: " << firstLine); + return false; + } + + getTokenString(tokens[2], "char", text); + assert(firstLine.size() < static_cast<std::string::size_type>(length)); + docBroker->tileCache().saveRendering(font+text, "font", buffer + firstLine.size() + 1, length - firstLine.size() - 1); + const auto payload = std::make_shared<Message>(buffer, length, + Message::Dir::Out, + Message::Type::Binary); + return forwardToClient(payload); + } + } + else + { + LOG_INF("Ignoring notification on password protected document: " << firstLine); + } + + // Detect json messages, since we must send those as text even though they are multiline. + // If not, the UI will read the first line of a binary payload, assuming that's the only + // text part and the rest is binary. + const bool isBinary = buffer[length - 1] != '}' && firstLine.find('{') == std::string::npos; + + // Forward everything else. + const auto payload = std::make_shared<Message>(buffer, length, + Message::Dir::Out, + isBinary ? Message::Type::Binary + : Message::Type::Text); + return forwardToClient(payload); +} + +bool ClientSession::forwardToClient(const std::shared_ptr<Message>& payload) +{ + const auto& message = payload->abbr(); + + if (isCloseFrame()) + { + LOG_TRC(getName() << ": peer began the closing handshake. Dropping forward message [" << message << "]."); + return true; + } + + LOG_TRC(getName() << " enqueue to client: " << message); + enqueueSendMessage(payload); + + return true; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp index f2656e8..e53a414 100644 --- a/wsd/ClientSession.hpp +++ b/wsd/ClientSession.hpp @@ -35,6 +35,9 @@ public: void setReadOnly(); bool isReadOnly() const { return _isReadOnly; } + /// Returns true if we've got status message. + bool gotStatus() const { return _gotStatus; } + bool isLoaded() const; const std::string getUserId() const { return _userId; } @@ -140,6 +143,8 @@ private: bool forwardToChild(const std::string& message, const std::shared_ptr<DocumentBroker>& docBroker); + bool forwardToClient(const std::shared_ptr<Message>& payload); + /// Returns true if given message from the client should be allowed or not /// Eg. in readonly mode only few messages should be allowed bool filterMessage(const std::string& msg) const; @@ -158,15 +163,13 @@ private: /// Whether this session is the owner of currently opened document bool _isDocumentOwner; - /// Our peer that connects us to the child. - std::shared_ptr<PrisonerSession> _peer; - /// Store URLs of completed 'save as' documents. MessageQueue _saveAsQueue; int _loadPart; bool _isLoadRequested; + bool _gotStatus; /// Wopi FileInfo object std::unique_ptr<WopiStorage::WOPIFileInfo> _wopiFileInfo; commit c7e2a4cc8892d9593018b862eba0bdb0a083f021 Author: Ashod Nakashian <[email protected]> Date: Sat Jan 21 20:59:45 2017 -0500 wsd: move headless check in enqueue function Change-Id: I2a09d2c5e5c1705e5caeb8563babcd939384a583 Reviewed-on: https://gerrit.libreoffice.org/33432 Reviewed-by: Ashod Nakashian <[email protected]> Tested-by: Ashod Nakashian <[email protected]> diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp index b6211a7..f2656e8 100644 --- a/wsd/ClientSession.hpp +++ b/wsd/ClientSession.hpp @@ -68,8 +68,17 @@ public: void enqueueSendMessage(const std::shared_ptr<Message>& data) { - LOG_TRC(getName() << " enqueueing client message: " << data->abbr()); - _senderQueue.enqueue(data); + if (isHeadless()) + { + // Fail silently and return as there is no actual websocket + // connection in this case. + LOG_INF(getName() << ": Headless peer, not forwarding message [" << data->abbr() << "]."); + } + else + { + LOG_TRC(getName() << " enqueueing client message: " << data->abbr()); + _senderQueue.enqueue(data); + } } bool stopping() const { return _stop || _senderQueue.stopping(); } diff --git a/wsd/PrisonerSession.cpp b/wsd/PrisonerSession.cpp index 9a2b0cf..c4188d9 100644 --- a/wsd/PrisonerSession.cpp +++ b/wsd/PrisonerSession.cpp @@ -275,13 +275,6 @@ bool PrisonerSession::forwardToPeer(const std::shared_ptr<Message>& payload) LOG_TRC(getName() << ": peer began the closing handshake. Dropping forward message [" << message << "]."); return true; } - else if (_peer.isHeadless()) - { - // Fail silently and return as there is no actual websocket - // connection in this case. - LOG_INF(getName() << ": Headless peer, not forwarding message [" << message << "]."); - return true; - } LOG_TRC(getName() << " -> " << _peer.getName() << ": " << message); _peer.enqueueSendMessage(payload); _______________________________________________ Libreoffice-commits mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits
