loolwsd/ChildProcessSession.cpp | 820 ++++++++++++++++++++++++++++++++++++++++ loolwsd/ChildProcessSession.hpp | 64 +++ loolwsd/LOKitHelper.hpp | 2 loolwsd/LOOLSession.cpp | 778 ------------------------------------- loolwsd/LOOLSession.hpp | 48 -- loolwsd/LOOLWSD.cpp | 1 loolwsd/Makefile.am | 4 7 files changed, 889 insertions(+), 828 deletions(-)
New commits: commit 1f43e99ac7465342d2e0fbff7a313844d45a4807 Author: Henry Castro <hcas...@collabora.com> Date: Sat Dec 12 14:23:44 2015 -0500 loolwsd: Refactored ChildProcessSession ChildProcessSession class now moved to own files. Change-Id: Ic67c8563ada51f23c83e06631ad913af610d395c Reviewed-on: https://gerrit.libreoffice.org/20895 Reviewed-by: Ashod Nakashian <ashnak...@gmail.com> Tested-by: Henry Castro <hcas...@collabora.com> diff --git a/loolwsd/ChildProcessSession.cpp b/loolwsd/ChildProcessSession.cpp new file mode 100644 index 0000000..ab92478 --- /dev/null +++ b/loolwsd/ChildProcessSession.cpp @@ -0,0 +1,820 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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 <iostream> + +#include <Poco/File.h> +#include <Poco/JSON/Object.h> +#include <Poco/JSON/Parser.h> +#include <Poco/Net/WebSocket.h> +#include <Poco/Path.h> +#include <Poco/Process.h> +#include <Poco/String.h> +#include <Poco/StringTokenizer.h> +#include <Poco/URI.h> +#include <Poco/Util/Application.h> + +#include "ChildProcessSession.hpp" +#include "LOKitHelper.hpp" +#include "LOOLProtocol.hpp" +#include "LOOLWSD.hpp" +#include "Util.hpp" + +using namespace LOOLProtocol; + +using Poco::File; +using Poco::IOException; +using Poco::JSON::Object; +using Poco::JSON::Parser; +using Poco::Net::WebSocket; +using Poco::Path; +using Poco::Process; +using Poco::ProcessHandle; +using Poco::StringTokenizer; +using Poco::URI; +using Poco::Util::Application; + +ChildProcessSession::ChildProcessSession(std::shared_ptr<WebSocket> ws, LibreOfficeKit *loKit, std::string childId) : + LOOLSession(ws, Kind::ToMaster), + _loKitDocument(NULL), + _loKit(loKit), + _childId(childId), + _clientPart(0) +{ + std::cout << Util::logPrefix() << "ChildProcessSession ctor this=" << this << " ws=" << _ws.get() << std::endl; +} + +ChildProcessSession::~ChildProcessSession() +{ + std::cout << Util::logPrefix() << "ChildProcessSession dtor this=" << this << std::endl; + if (LIBREOFFICEKIT_HAS(_loKit, registerCallback)) + _loKit->pClass->registerCallback(_loKit, 0, 0); +} + +bool ChildProcessSession::handleInput(const char *buffer, int length) +{ + std::string firstLine = getFirstLine(buffer, length); + StringTokenizer tokens(firstLine, " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM); + + Application::instance().logger().information(Util::logPrefix() + _kindString + ",Input," + getAbbreviatedMessage(buffer, length)); + + if (tokens[0] == "canceltiles") + { + // this command makes sense only on the command queue level, nothing + // to do here + return true; + } + else if (tokens[0] == "commandvalues") + { + return getCommandValues(buffer, length, tokens); + } + else if (tokens[0] == "partpagerectangles") + { + return getPartPageRectangles(buffer, length); + } + else if (tokens[0] == "load") + { + if (_docURL != "") + { + sendTextFrame("error: cmd=load kind=docalreadyloaded"); + return false; + } + return loadDocument(buffer, length, tokens); + } + else if (_docURL == "") + { + sendTextFrame("error: cmd=" + tokens[0] + " kind=nodocloaded"); + return false; + } + else if (tokens[0] == "renderfont") + { + sendFontRendering(buffer, length, tokens); + } + else if (tokens[0] == "setclientpart") + { + return setClientPart(buffer, length, tokens); + } + else if (tokens[0] == "setpage") + { + return setPage(buffer, length, tokens); + } + else if (tokens[0] == "status") + { + return getStatus(buffer, length); + } + else if (tokens[0] == "tile") + { + sendTile(buffer, length, tokens); + } + else + { + // All other commands are such that they always require a LibreOfficeKitDocument session, + // i.e. need to be handled in a child process. + + assert(tokens[0] == "clientzoom" || + tokens[0] == "downloadas" || + tokens[0] == "getchildid" || + tokens[0] == "gettextselection" || + tokens[0] == "paste" || + tokens[0] == "insertfile" || + tokens[0] == "key" || + tokens[0] == "mouse" || + tokens[0] == "uno" || + tokens[0] == "selecttext" || + tokens[0] == "selectgraphic" || + tokens[0] == "resetselection" || + tokens[0] == "saveas"); + + if (_docType != "text" && _loKitDocument->pClass->getPart(_loKitDocument) != _clientPart) + { + _loKitDocument->pClass->setPart(_loKitDocument, _clientPart); + } + if (tokens[0] == "clientzoom") + { + return clientZoom(buffer, length, tokens); + } + else if (tokens[0] == "downloadas") + { + return downloadAs(buffer, length, tokens); + } + else if (tokens[0] == "getchildid") + { + return getChildId(); + } + else if (tokens[0] == "gettextselection") + { + return getTextSelection(buffer, length, tokens); + } + else if (tokens[0] == "paste") + { + return paste(buffer, length, tokens); + } + else if (tokens[0] == "insertfile") + { + return insertFile(buffer, length, tokens); + } + else if (tokens[0] == "key") + { + return keyEvent(buffer, length, tokens); + } + else if (tokens[0] == "mouse") + { + return mouseEvent(buffer, length, tokens); + } + else if (tokens[0] == "uno") + { + return unoCommand(buffer, length, tokens); + } + else if (tokens[0] == "selecttext") + { + return selectText(buffer, length, tokens); + } + else if (tokens[0] == "selectgraphic") + { + return selectGraphic(buffer, length, tokens); + } + else if (tokens[0] == "resetselection") + { + return resetSelection(buffer, length, tokens); + } + else if (tokens[0] == "saveas") + { + return saveAs(buffer, length, tokens); + } + else + { + assert(false); + } + } + return true; +} + +extern "C" +{ + static void myCallback(int nType, const char* pPayload, void* pData) + { + ChildProcessSession *srv = reinterpret_cast<ChildProcessSession *>(pData); + + switch ((LibreOfficeKitCallbackType) nType) + { + case LOK_CALLBACK_INVALIDATE_TILES: + { + int curPart = srv->_loKitDocument->pClass->getPart(srv->_loKitDocument); + srv->sendTextFrame("curpart: part=" + std::to_string(curPart)); + if (srv->_docType == "text") + { + curPart = 0; + } + StringTokenizer tokens(std::string(pPayload), " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM); + if (tokens.count() == 4) + { + int x, y, width, height; + + try { + x = std::stoi(tokens[0]); + y = std::stoi(tokens[1]); + width = std::stoi(tokens[2]); + height = std::stoi(tokens[3]); + } + catch (std::out_of_range&) + { + // something went wrong, invalidate everything + Application::instance().logger().information(Util::logPrefix() + "Ignoring integer values out of range: " + pPayload); + x = 0; + y = 0; + width = INT_MAX; + height = INT_MAX; + } + + srv->sendTextFrame("invalidatetiles:" + " part=" + std::to_string(curPart) + + " x=" + std::to_string(x) + + " y=" + std::to_string(y) + + " width=" + std::to_string(width) + + " height=" + std::to_string(height)); + } + else + { + srv->sendTextFrame("invalidatetiles: " + std::string(pPayload)); + } + } + break; + case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR: + srv->sendTextFrame("invalidatecursor: " + std::string(pPayload)); + break; + case LOK_CALLBACK_TEXT_SELECTION: + srv->sendTextFrame("textselection: " + std::string(pPayload)); + break; + case LOK_CALLBACK_TEXT_SELECTION_START: + srv->sendTextFrame("textselectionstart: " + std::string(pPayload)); + break; + case LOK_CALLBACK_TEXT_SELECTION_END: + srv->sendTextFrame("textselectionend: " + std::string(pPayload)); + break; + case LOK_CALLBACK_CURSOR_VISIBLE: + srv->sendTextFrame("cursorvisible: " + std::string(pPayload)); + break; + case LOK_CALLBACK_GRAPHIC_SELECTION: + srv->sendTextFrame("graphicselection: " + std::string(pPayload)); + break; + case LOK_CALLBACK_CELL_CURSOR: + srv->sendTextFrame("cellcursor: " + std::string(pPayload)); + break; + case LOK_CALLBACK_CELL_FORMULA: + srv->sendTextFrame("cellformula: " + std::string(pPayload)); + break; + case LOK_CALLBACK_MOUSE_POINTER: + srv->sendTextFrame("mousepointer: " + std::string(pPayload)); + break; + case LOK_CALLBACK_HYPERLINK_CLICKED: + srv->sendTextFrame("hyperlinkclicked: " + std::string(pPayload)); + break; + case LOK_CALLBACK_STATE_CHANGED: + srv->sendTextFrame("statechanged: " + std::string(pPayload)); + break; + case LOK_CALLBACK_STATUS_INDICATOR_START: + srv->sendTextFrame("statusindicatorstart:"); + break; + case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE: + srv->sendTextFrame("statusindicatorsetvalue: " + std::string(pPayload)); + break; + case LOK_CALLBACK_STATUS_INDICATOR_FINISH: + srv->sendTextFrame("statusindicatorfinish:"); + break; + case LOK_CALLBACK_SEARCH_NOT_FOUND: + srv->sendTextFrame("searchnotfound: " + std::string(pPayload)); + break; + case LOK_CALLBACK_SEARCH_RESULT_SELECTION: + srv->sendTextFrame("searchresultselection: " + std::string(pPayload)); + break; + case LOK_CALLBACK_DOCUMENT_SIZE_CHANGED: + srv->getStatus("", 0); + srv->getPartPageRectangles("", 0); + break; + case LOK_CALLBACK_SET_PART: + srv->sendTextFrame("setpart: " + std::string(pPayload)); + break; + case LOK_CALLBACK_UNO_COMMAND_RESULT: + srv->sendTextFrame("unocommandresult: " + std::string(pPayload)); + break; + } + } +} + +bool ChildProcessSession::loadDocument(const char *buffer, int length, StringTokenizer& tokens) +{ + int part = -1; + if (tokens.count() < 2) + { + sendTextFrame("error: cmd=load kind=syntax"); + return false; + } + + std::string timestamp; + parseDocOptions(tokens, part, timestamp); + + URI aUri; + try + { + aUri = URI(_docURL); + } + catch(Poco::SyntaxException&) + { + sendTextFrame("error: cmd=load kind=uriinvalid"); + return false; + } + + if (aUri.empty()) + { + sendTextFrame("error: cmd=load kind=uriempty"); + return false; + } + + // The URL in the request is the original one, not visible in the chroot jail. + // The child process uses the fixed name jailDocumentURL. + + if (LIBREOFFICEKIT_HAS(_loKit, registerCallback)) + _loKit->pClass->registerCallback(_loKit, myCallback, this); + + if (aUri.isRelative() || aUri.getScheme() == "file") + aUri = URI( URI("file://"), Path(jailDocumentURL + Path::separator() + std::to_string(Process::id()), + Path(aUri.getPath()).getFileName()).toString() ); + + if ((_loKitDocument = _loKit->pClass->documentLoad(_loKit, aUri.toString().c_str())) == NULL) + { + sendTextFrame("error: cmd=load kind=failed"); + Application::instance().logger().information(Util::logPrefix() + "Failed to load: " + aUri.toString() + ", error is: " + _loKit->pClass->getError(_loKit)); + return false; + } + + std::string renderingOptions; + if (!_docOptions.empty()) + { + Poco::JSON::Parser parser; + Poco::Dynamic::Var var = parser.parse(_docOptions); + Poco::JSON::Object::Ptr object = var.extract<Poco::JSON::Object::Ptr>(); + renderingOptions = object->get("rendering").toString(); + } + + _loKitDocument->pClass->initializeForRendering(_loKitDocument, (renderingOptions.empty() ? nullptr : renderingOptions.c_str())); + + if ( _docType != "text" && part != -1) + { + _clientPart = part; + _loKitDocument->pClass->setPart(_loKitDocument, part); + } + + if (!getStatus(buffer, length)) + return false; + + _loKitDocument->pClass->registerCallback(_loKitDocument, myCallback, this); + + return true; +} + +void ChildProcessSession::sendFontRendering(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) +{ + std::string font, decodedFont; + int width, height; + unsigned char *pixmap; + + if (tokens.count() < 2 || + !getTokenString(tokens[1], "font", font)) + { + sendTextFrame("error: cmd=renderfont kind=syntax"); + return; + } + + URI::decode(font, decodedFont); + std::string response = "renderfont: " + Poco::cat(std::string(" "), tokens.begin() + 1, tokens.end()) + "\n"; + + std::vector<char> output; + output.resize(response.size()); + std::memcpy(output.data(), response.data(), response.size()); + + Poco::Timestamp timestamp; + pixmap = _loKitDocument->pClass->renderFont(_loKitDocument, decodedFont.c_str(), &width, &height); + std::cout << Util::logPrefix() << "renderFont called, font[" << font << "] rendered in " << double(timestamp.elapsed())/1000 << "ms" << std::endl; + + if (pixmap != nullptr) { + if (!Util::encodePNGAndAppendToBuffer(pixmap, width, height, output, LOK_TILEMODE_RGBA)) + { + sendTextFrame("error: cmd=renderfont kind=failure"); + delete[] pixmap; + return; + } + delete[] pixmap; + } + + sendBinaryFrame(output.data(), output.size()); +} + +bool ChildProcessSession::getStatus(const char* /*buffer*/, int /*length*/) +{ + std::string status = "status: " + LOKitHelper::documentStatus(_loKitDocument); + StringTokenizer tokens(status, " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM); + if (!getTokenString(tokens[1], "type", _docType)) + { + Application::instance().logger().information(Util::logPrefix() + "failed to get document type from" + status); + } + sendTextFrame(status); + + return true; +} + +bool ChildProcessSession::getCommandValues(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) +{ + std::string command; + if (tokens.count() != 2 || !getTokenString(tokens[1], "command", command)) + { + sendTextFrame("error: cmd=commandvalues kind=syntax"); + return false; + } + sendTextFrame("commandvalues: " + std::string(_loKitDocument->pClass->getCommandValues(_loKitDocument, command.c_str()))); + return true; +} + +bool ChildProcessSession::getPartPageRectangles(const char* /*buffer*/, int /*length*/) +{ + sendTextFrame("partpagerectangles: " + std::string(_loKitDocument->pClass->getPartPageRectangles(_loKitDocument))); + return true; +} + +void ChildProcessSession::sendTile(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) +{ + int part, width, height, tilePosX, tilePosY, tileWidth, tileHeight; + + if (tokens.count() < 8 || + !getTokenInteger(tokens[1], "part", part) || + !getTokenInteger(tokens[2], "width", width) || + !getTokenInteger(tokens[3], "height", height) || + !getTokenInteger(tokens[4], "tileposx", tilePosX) || + !getTokenInteger(tokens[5], "tileposy", tilePosY) || + !getTokenInteger(tokens[6], "tilewidth", tileWidth) || + !getTokenInteger(tokens[7], "tileheight", tileHeight)) + { + sendTextFrame("error: cmd=tile kind=syntax"); + return; + } + + if (part < 0 || + width <= 0 || + height <= 0 || + tilePosX < 0 || + tilePosY < 0 || + tileWidth <= 0 || + tileHeight <= 0) + { + sendTextFrame("error: cmd=tile kind=invalid"); + return; + } + + std::string response = "tile: " + Poco::cat(std::string(" "), tokens.begin() + 1, tokens.end()) + "\n"; + + std::vector<char> output; + output.reserve(4 * width * height); + output.resize(response.size()); + std::memcpy(output.data(), response.data(), response.size()); + + unsigned char *pixmap = new unsigned char[4 * width * height]; + memset(pixmap, 0, 4 * width * height); + + if (_docType != "text" && part != _loKitDocument->pClass->getPart(_loKitDocument)) + { + _loKitDocument->pClass->setPart(_loKitDocument, part); + } + + Poco::Timestamp timestamp; + _loKitDocument->pClass->paintTile(_loKitDocument, pixmap, width, height, tilePosX, tilePosY, tileWidth, tileHeight); + std::cout << Util::logPrefix() << "paintTile called, tile at [" << tilePosX << ", " << tilePosY << "] rendered in " << double(timestamp.elapsed())/1000 << "ms" << std::endl; + + LibreOfficeKitTileMode mode = static_cast<LibreOfficeKitTileMode>(_loKitDocument->pClass->getTileMode(_loKitDocument)); + if (!Util::encodePNGAndAppendToBuffer(pixmap, width, height, output, mode)) + { + sendTextFrame("error: cmd=tile kind=failure"); + return; + } + + delete[] pixmap; + + sendBinaryFrame(output.data(), output.size()); +} + +bool ChildProcessSession::clientZoom(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) +{ + int tilePixelWidth, tilePixelHeight, tileTwipWidth, tileTwipHeight; + + if (tokens.count() != 5 || + !getTokenInteger(tokens[1], "tilepixelwidth", tilePixelWidth) || + !getTokenInteger(tokens[2], "tilepixelheight", tilePixelHeight) || + !getTokenInteger(tokens[3], "tiletwipwidth", tileTwipWidth) || + !getTokenInteger(tokens[4], "tiletwipheight", tileTwipHeight)) + { + sendTextFrame("error: cmd=clientzoom kind=syntax"); + return false; + } + + _loKitDocument->pClass->setClientZoom(_loKitDocument, tilePixelWidth, tilePixelHeight, tileTwipWidth, tileTwipHeight); + return true; +} +bool ChildProcessSession::downloadAs(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) +{ + std::string name, id, format, filterOptions; + + if (tokens.count() < 5 || + !getTokenString(tokens[1], "name", name) || + !getTokenString(tokens[2], "id", id)) + { + sendTextFrame("error: cmd=downloadas kind=syntax"); + return false; + } + + getTokenString(tokens[3], "format", format); + + if (getTokenString(tokens[4], "options", filterOptions)) + { + if (tokens.count() > 5) + { + filterOptions += Poco::cat(std::string(" "), tokens.begin() + 5, tokens.end()); + } + } + + std::string tmpDir, url; + File *file = NULL; + do + { + if (file != NULL) + { + delete file; + } + tmpDir = std::to_string((((Poco::UInt64)LOOLWSD::_rng.next()) << 32) | LOOLWSD::_rng.next() | 1); + url = jailDocumentURL + "/" + tmpDir + "/" + name; + file = new File(url); + } while (file->exists()); + delete file; + + _loKitDocument->pClass->saveAs(_loKitDocument, url.c_str(), + format.size() == 0 ? NULL :format.c_str(), + filterOptions.size() == 0 ? NULL : filterOptions.c_str()); + + sendTextFrame("downloadas: jail=" + _childId + " dir=" + tmpDir + " name=" + name + + " port=" + std::to_string(LOOLWSD::portNumber) + " id=" + id); + return true; +} + +bool ChildProcessSession::getChildId() +{ + sendTextFrame("getchildid: id=" + _childId); + return true; +} + +bool ChildProcessSession::getTextSelection(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) +{ + std::string mimeType; + + if (tokens.count() != 2 || + !getTokenString(tokens[1], "mimetype", mimeType)) + { + sendTextFrame("error: cmd=gettextselection kind=syntax"); + return false; + } + + char *textSelection = _loKitDocument->pClass->getTextSelection(_loKitDocument, mimeType.c_str(), NULL); + + sendTextFrame("textselectioncontent: " + std::string(textSelection)); + return true; +} + +bool ChildProcessSession::paste(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) +{ + std::string mimeType; + std::string data; + + if (tokens.count() < 3 || !getTokenString(tokens[1], "mimetype", mimeType) || !getTokenString(tokens[2], "data", data)) + { + sendTextFrame("error: cmd=paste kind=syntax"); + return false; + } + + data = Poco::cat(std::string(" "), tokens.begin() + 2, tokens.end()).substr(strlen("data=")); + + _loKitDocument->pClass->paste(_loKitDocument, mimeType.c_str(), data.c_str(), std::strlen(data.c_str())); + + return true; +} + +bool ChildProcessSession::insertFile(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) +{ + std::string name, type; + + if (tokens.count() != 3 || + !getTokenString(tokens[1], "name", name) || + !getTokenString(tokens[2], "type", type)) + { + sendTextFrame("error: cmd=insertfile kind=syntax"); + return false; + } + + if (type == "graphic") + { + std::string fileName = "file://" + jailDocumentURL + "/insertfile/" + name; + std::string command = ".uno:InsertGraphic"; + std::string arguments = "{" + "\"FileName\":{" + "\"type\":\"string\"," + "\"value\":\"" + fileName + "\"" + "}}"; + _loKitDocument->pClass->postUnoCommand(_loKitDocument, command.c_str(), arguments.c_str(), false); + } + + return true; +} + +bool ChildProcessSession::keyEvent(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) +{ + int type, charcode, keycode; + + if (tokens.count() != 4 || + !getTokenKeyword(tokens[1], "type", + {{"input", LOK_KEYEVENT_KEYINPUT}, {"up", LOK_KEYEVENT_KEYUP}}, + type) || + !getTokenInteger(tokens[2], "char", charcode) || + !getTokenInteger(tokens[3], "key", keycode)) + { + sendTextFrame("error: cmd=key kind=syntax"); + return false; + } + + _loKitDocument->pClass->postKeyEvent(_loKitDocument, type, charcode, keycode); + + return true; +} + +bool ChildProcessSession::mouseEvent(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) +{ + int type, x, y, count, buttons, modifier; + + if (tokens.count() != 7 || + !getTokenKeyword(tokens[1], "type", + {{"buttondown", LOK_MOUSEEVENT_MOUSEBUTTONDOWN}, + {"buttonup", LOK_MOUSEEVENT_MOUSEBUTTONUP}, + {"move", LOK_MOUSEEVENT_MOUSEMOVE}}, + type) || + !getTokenInteger(tokens[2], "x", x) || + !getTokenInteger(tokens[3], "y", y) || + !getTokenInteger(tokens[4], "count", count) || + !getTokenInteger(tokens[5], "buttons", buttons) || + !getTokenInteger(tokens[6], "modifier", modifier)) + { + sendTextFrame("error: cmd=mouse kind=syntax"); + return false; + } + + _loKitDocument->pClass->postMouseEvent(_loKitDocument, type, x, y, count, buttons, modifier); + + return true; +} + +bool ChildProcessSession::unoCommand(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) +{ + if (tokens.count() == 1) + { + sendTextFrame("error: cmd=uno kind=syntax"); + return false; + } + + // we need to get LOK_CALLBACK_UNO_COMMAND_RESULT callback when saving + bool bNotify = (tokens[1] == ".uno:Save"); + + if (tokens.count() == 2) + { + _loKitDocument->pClass->postUnoCommand(_loKitDocument, tokens[1].c_str(), 0, bNotify); + } + else + { + _loKitDocument->pClass->postUnoCommand(_loKitDocument, tokens[1].c_str(), Poco::cat(std::string(" "), tokens.begin() + 2, tokens.end()).c_str(), bNotify); + } + + return true; +} + +bool ChildProcessSession::selectText(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) +{ + int type, x, y; + + if (tokens.count() != 4 || + !getTokenKeyword(tokens[1], "type", + {{"start", LOK_SETTEXTSELECTION_START}, + {"end", LOK_SETTEXTSELECTION_END}, + {"reset", LOK_SETTEXTSELECTION_RESET}}, + type) || + !getTokenInteger(tokens[2], "x", x) || + !getTokenInteger(tokens[3], "y", y)) + { + sendTextFrame("error: cmd=selecttext kind=syntax"); + return false; + } + + _loKitDocument->pClass->setTextSelection(_loKitDocument, type, x, y); + + return true; +} + +bool ChildProcessSession::selectGraphic(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) +{ + int type, x, y; + + if (tokens.count() != 4 || + !getTokenKeyword(tokens[1], "type", + {{"start", LOK_SETGRAPHICSELECTION_START}, + {"end", LOK_SETGRAPHICSELECTION_END}}, + type) || + !getTokenInteger(tokens[2], "x", x) || + !getTokenInteger(tokens[3], "y", y)) + { + sendTextFrame("error: cmd=selectgraphic kind=syntax"); + return false; + } + + _loKitDocument->pClass->setGraphicSelection(_loKitDocument, type, x, y); + + return true; +} + +bool ChildProcessSession::resetSelection(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) +{ + if (tokens.count() != 1) + { + sendTextFrame("error: cmd=resetselection kind=syntax"); + return false; + } + + _loKitDocument->pClass->resetSelection(_loKitDocument); + + return true; +} + +bool ChildProcessSession::saveAs(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) +{ + std::string url, format, filterOptions; + + if (tokens.count() < 4 || + !getTokenString(tokens[1], "url", url)) + { + sendTextFrame("error: cmd=saveas kind=syntax"); + return false; + } + + getTokenString(tokens[2], "format", format); + + if (getTokenString(tokens[3], "options", filterOptions)) + { + if (tokens.count() > 4) + { + filterOptions += Poco::cat(std::string(" "), tokens.begin() + 4, tokens.end()); + } + } + + bool success = _loKitDocument->pClass->saveAs(_loKitDocument, url.c_str(), + format.size() == 0 ? NULL :format.c_str(), + filterOptions.size() == 0 ? NULL : filterOptions.c_str()); + + sendTextFrame("saveas: url=" + url); + std::string successStr = success ? "true" : "false"; + sendTextFrame("unocommandresult: {" + "\"commandName\":\"saveas\"," + "\"success\":\"" + successStr + "\"}"); + + return true; +} + +bool ChildProcessSession::setClientPart(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) +{ + if (tokens.count() < 2 || + !getTokenInteger(tokens[1], "part", _clientPart)) + { + return false; + } + return true; +} + +bool ChildProcessSession::setPage(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) +{ + int page; + if (tokens.count() < 2 || + !getTokenInteger(tokens[1], "page", page)) + { + sendTextFrame("error: cmd=setpage kind=invalid"); + return false; + } + _loKitDocument->pClass->setPart(_loKitDocument, page); + return true; +} + + diff --git a/loolwsd/ChildProcessSession.hpp b/loolwsd/ChildProcessSession.hpp new file mode 100644 index 0000000..d2665d0 --- /dev/null +++ b/loolwsd/ChildProcessSession.hpp @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * 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/. + */ + +#ifndef INCLUDED_CHILDPROCESSSESSION_HPP +#define INCLUDED_CHILDPROCESSSESSION_HPP + +#include "LOOLSession.hpp" + +class ChildProcessSession final : public LOOLSession +{ +public: + ChildProcessSession(std::shared_ptr<Poco::Net::WebSocket> ws, LibreOfficeKit *loKit, std::string _childId); + virtual ~ChildProcessSession(); + + virtual bool handleInput(const char *buffer, int length) override; + + virtual bool getStatus(const char *buffer, int length); + + virtual bool getCommandValues(const char *buffer, int length, Poco::StringTokenizer& tokens); + + virtual bool getPartPageRectangles(const char *buffer, int length) override; + + LibreOfficeKitDocument *_loKitDocument; + std::string _docType; + + protected: + virtual bool loadDocument(const char *buffer, int length, Poco::StringTokenizer& tokens) override; + + virtual void sendTile(const char *buffer, int length, Poco::StringTokenizer& tokens); + + virtual void sendFontRendering(const char *buffer, int length, Poco::StringTokenizer& tokens); + + bool clientZoom(const char *buffer, int length, Poco::StringTokenizer& tokens); + bool downloadAs(const char *buffer, int length, Poco::StringTokenizer& tokens); + bool getChildId(); + bool getTextSelection(const char *buffer, int length, Poco::StringTokenizer& tokens); + bool paste(const char *buffer, int length, Poco::StringTokenizer& tokens); + bool insertFile(const char *buffer, int length, Poco::StringTokenizer& tokens); + bool keyEvent(const char *buffer, int length, Poco::StringTokenizer& tokens); + bool mouseEvent(const char *buffer, int length, Poco::StringTokenizer& tokens); + bool unoCommand(const char *buffer, int length, Poco::StringTokenizer& tokens); + bool selectText(const char *buffer, int length, Poco::StringTokenizer& tokens); + bool selectGraphic(const char *buffer, int length, Poco::StringTokenizer& tokens); + bool resetSelection(const char *buffer, int length, Poco::StringTokenizer& tokens); + bool saveAs(const char *buffer, int length, Poco::StringTokenizer& tokens); + bool setClientPart(const char *buffer, int length, Poco::StringTokenizer& tokens); + bool setPage(const char *buffer, int length, Poco::StringTokenizer& tokens); + + std::string _loSubPath; + LibreOfficeKit *_loKit; + std::string _childId; + + private: + int _clientPart; +}; + +#endif + diff --git a/loolwsd/LOKitHelper.hpp b/loolwsd/LOKitHelper.hpp index 33adeb8..fb0cdbc 100644 --- a/loolwsd/LOKitHelper.hpp +++ b/loolwsd/LOKitHelper.hpp @@ -18,6 +18,7 @@ namespace LOKitHelper { + inline std::string documentTypeToString(LibreOfficeKitDocumentType type) { switch (type) @@ -35,6 +36,7 @@ namespace LOKitHelper } } + inline std::string documentStatus(LibreOfficeKitDocument *loKitDocument) { std::string typeString(documentTypeToString(static_cast<LibreOfficeKitDocumentType>(loKitDocument->pClass->getDocumentType(loKitDocument)))); diff --git a/loolwsd/LOOLSession.cpp b/loolwsd/LOOLSession.cpp index 7c208bb..db59e76 100644 --- a/loolwsd/LOOLSession.cpp +++ b/loolwsd/LOOLSession.cpp @@ -51,7 +51,6 @@ #include <Poco/Net/SocketAddress.h> #include <Poco/FileStream.h> -#include "LOKitHelper.hpp" #include "LOOLProtocol.hpp" #include "LOOLSession.hpp" #include "LOOLWSD.hpp" @@ -169,781 +168,4 @@ void LOOLSession::parseDocOptions(const StringTokenizer& tokens, int& part, std: } } -ChildProcessSession::ChildProcessSession(std::shared_ptr<WebSocket> ws, LibreOfficeKit *loKit, std::string childId) : - LOOLSession(ws, Kind::ToMaster), - _loKitDocument(NULL), - _loKit(loKit), - _childId(childId), - _clientPart(0) -{ - std::cout << Util::logPrefix() << "ChildProcessSession ctor this=" << this << " ws=" << _ws.get() << std::endl; -} - -ChildProcessSession::~ChildProcessSession() -{ - std::cout << Util::logPrefix() << "ChildProcessSession dtor this=" << this << std::endl; - if (LIBREOFFICEKIT_HAS(_loKit, registerCallback)) - _loKit->pClass->registerCallback(_loKit, 0, 0); -} - -bool ChildProcessSession::handleInput(const char *buffer, int length) -{ - std::string firstLine = getFirstLine(buffer, length); - StringTokenizer tokens(firstLine, " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM); - - Application::instance().logger().information(Util::logPrefix() + _kindString + ",Input," + getAbbreviatedMessage(buffer, length)); - - if (tokens[0] == "canceltiles") - { - // this command makes sense only on the command queue level, nothing - // to do here - return true; - } - else if (tokens[0] == "commandvalues") - { - return getCommandValues(buffer, length, tokens); - } - else if (tokens[0] == "partpagerectangles") - { - return getPartPageRectangles(buffer, length); - } - else if (tokens[0] == "load") - { - if (_docURL != "") - { - sendTextFrame("error: cmd=load kind=docalreadyloaded"); - return false; - } - return loadDocument(buffer, length, tokens); - } - else if (_docURL == "") - { - sendTextFrame("error: cmd=" + tokens[0] + " kind=nodocloaded"); - return false; - } - else if (tokens[0] == "renderfont") - { - sendFontRendering(buffer, length, tokens); - } - else if (tokens[0] == "setclientpart") - { - return setClientPart(buffer, length, tokens); - } - else if (tokens[0] == "setpage") - { - return setPage(buffer, length, tokens); - } - else if (tokens[0] == "status") - { - return getStatus(buffer, length); - } - else if (tokens[0] == "tile") - { - sendTile(buffer, length, tokens); - } - else - { - // All other commands are such that they always require a LibreOfficeKitDocument session, - // i.e. need to be handled in a child process. - - assert(tokens[0] == "clientzoom" || - tokens[0] == "downloadas" || - tokens[0] == "getchildid" || - tokens[0] == "gettextselection" || - tokens[0] == "paste" || - tokens[0] == "insertfile" || - tokens[0] == "key" || - tokens[0] == "mouse" || - tokens[0] == "uno" || - tokens[0] == "selecttext" || - tokens[0] == "selectgraphic" || - tokens[0] == "resetselection" || - tokens[0] == "saveas"); - - if (_docType != "text" && _loKitDocument->pClass->getPart(_loKitDocument) != _clientPart) - { - _loKitDocument->pClass->setPart(_loKitDocument, _clientPart); - } - if (tokens[0] == "clientzoom") - { - return clientZoom(buffer, length, tokens); - } - else if (tokens[0] == "downloadas") - { - return downloadAs(buffer, length, tokens); - } - else if (tokens[0] == "getchildid") - { - return getChildId(); - } - else if (tokens[0] == "gettextselection") - { - return getTextSelection(buffer, length, tokens); - } - else if (tokens[0] == "paste") - { - return paste(buffer, length, tokens); - } - else if (tokens[0] == "insertfile") - { - return insertFile(buffer, length, tokens); - } - else if (tokens[0] == "key") - { - return keyEvent(buffer, length, tokens); - } - else if (tokens[0] == "mouse") - { - return mouseEvent(buffer, length, tokens); - } - else if (tokens[0] == "uno") - { - return unoCommand(buffer, length, tokens); - } - else if (tokens[0] == "selecttext") - { - return selectText(buffer, length, tokens); - } - else if (tokens[0] == "selectgraphic") - { - return selectGraphic(buffer, length, tokens); - } - else if (tokens[0] == "resetselection") - { - return resetSelection(buffer, length, tokens); - } - else if (tokens[0] == "saveas") - { - return saveAs(buffer, length, tokens); - } - else - { - assert(false); - } - } - return true; -} - -extern "C" -{ - static void myCallback(int nType, const char* pPayload, void* pData) - { - ChildProcessSession *srv = reinterpret_cast<ChildProcessSession *>(pData); - - switch ((LibreOfficeKitCallbackType) nType) - { - case LOK_CALLBACK_INVALIDATE_TILES: - { - int curPart = srv->_loKitDocument->pClass->getPart(srv->_loKitDocument); - srv->sendTextFrame("curpart: part=" + std::to_string(curPart)); - if (srv->_docType == "text") - { - curPart = 0; - } - StringTokenizer tokens(std::string(pPayload), " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM); - if (tokens.count() == 4) - { - int x, y, width, height; - - try { - x = std::stoi(tokens[0]); - y = std::stoi(tokens[1]); - width = std::stoi(tokens[2]); - height = std::stoi(tokens[3]); - } - catch (std::out_of_range&) - { - // something went wrong, invalidate everything - Application::instance().logger().information(Util::logPrefix() + "Ignoring integer values out of range: " + pPayload); - x = 0; - y = 0; - width = INT_MAX; - height = INT_MAX; - } - - srv->sendTextFrame("invalidatetiles:" - " part=" + std::to_string(curPart) + - " x=" + std::to_string(x) + - " y=" + std::to_string(y) + - " width=" + std::to_string(width) + - " height=" + std::to_string(height)); - } - else - { - srv->sendTextFrame("invalidatetiles: " + std::string(pPayload)); - } - } - break; - case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR: - srv->sendTextFrame("invalidatecursor: " + std::string(pPayload)); - break; - case LOK_CALLBACK_TEXT_SELECTION: - srv->sendTextFrame("textselection: " + std::string(pPayload)); - break; - case LOK_CALLBACK_TEXT_SELECTION_START: - srv->sendTextFrame("textselectionstart: " + std::string(pPayload)); - break; - case LOK_CALLBACK_TEXT_SELECTION_END: - srv->sendTextFrame("textselectionend: " + std::string(pPayload)); - break; - case LOK_CALLBACK_CURSOR_VISIBLE: - srv->sendTextFrame("cursorvisible: " + std::string(pPayload)); - break; - case LOK_CALLBACK_GRAPHIC_SELECTION: - srv->sendTextFrame("graphicselection: " + std::string(pPayload)); - break; - case LOK_CALLBACK_CELL_CURSOR: - srv->sendTextFrame("cellcursor: " + std::string(pPayload)); - break; - case LOK_CALLBACK_CELL_FORMULA: - srv->sendTextFrame("cellformula: " + std::string(pPayload)); - break; - case LOK_CALLBACK_MOUSE_POINTER: - srv->sendTextFrame("mousepointer: " + std::string(pPayload)); - break; - case LOK_CALLBACK_HYPERLINK_CLICKED: - srv->sendTextFrame("hyperlinkclicked: " + std::string(pPayload)); - break; - case LOK_CALLBACK_STATE_CHANGED: - srv->sendTextFrame("statechanged: " + std::string(pPayload)); - break; - case LOK_CALLBACK_STATUS_INDICATOR_START: - srv->sendTextFrame("statusindicatorstart:"); - break; - case LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE: - srv->sendTextFrame("statusindicatorsetvalue: " + std::string(pPayload)); - break; - case LOK_CALLBACK_STATUS_INDICATOR_FINISH: - srv->sendTextFrame("statusindicatorfinish:"); - break; - case LOK_CALLBACK_SEARCH_NOT_FOUND: - srv->sendTextFrame("searchnotfound: " + std::string(pPayload)); - break; - case LOK_CALLBACK_SEARCH_RESULT_SELECTION: - srv->sendTextFrame("searchresultselection: " + std::string(pPayload)); - break; - case LOK_CALLBACK_DOCUMENT_SIZE_CHANGED: - srv->getStatus("", 0); - srv->getPartPageRectangles("", 0); - break; - case LOK_CALLBACK_SET_PART: - srv->sendTextFrame("setpart: " + std::string(pPayload)); - break; - case LOK_CALLBACK_UNO_COMMAND_RESULT: - srv->sendTextFrame("unocommandresult: " + std::string(pPayload)); - break; - } - } -} - -bool ChildProcessSession::loadDocument(const char *buffer, int length, StringTokenizer& tokens) -{ - int part = -1; - if (tokens.count() < 2) - { - sendTextFrame("error: cmd=load kind=syntax"); - return false; - } - - std::string timestamp; - parseDocOptions(tokens, part, timestamp); - - URI aUri; - try - { - aUri = URI(_docURL); - } - catch(Poco::SyntaxException&) - { - sendTextFrame("error: cmd=load kind=uriinvalid"); - return false; - } - - if (aUri.empty()) - { - sendTextFrame("error: cmd=load kind=uriempty"); - return false; - } - - // The URL in the request is the original one, not visible in the chroot jail. - // The child process uses the fixed name jailDocumentURL. - - if (LIBREOFFICEKIT_HAS(_loKit, registerCallback)) - _loKit->pClass->registerCallback(_loKit, myCallback, this); - - if (aUri.isRelative() || aUri.getScheme() == "file") - aUri = URI( URI("file://"), Path(jailDocumentURL + Path::separator() + std::to_string(Process::id()), - Path(aUri.getPath()).getFileName()).toString() ); - - if ((_loKitDocument = _loKit->pClass->documentLoad(_loKit, aUri.toString().c_str())) == NULL) - { - sendTextFrame("error: cmd=load kind=failed"); - Application::instance().logger().information(Util::logPrefix() + "Failed to load: " + aUri.toString() + ", error is: " + _loKit->pClass->getError(_loKit)); - return false; - } - - std::string renderingOptions; - if (!_docOptions.empty()) - { - Poco::JSON::Parser parser; - Poco::Dynamic::Var var = parser.parse(_docOptions); - Poco::JSON::Object::Ptr object = var.extract<Poco::JSON::Object::Ptr>(); - renderingOptions = object->get("rendering").toString(); - } - - _loKitDocument->pClass->initializeForRendering(_loKitDocument, (renderingOptions.empty() ? nullptr : renderingOptions.c_str())); - - if ( _docType != "text" && part != -1) - { - _clientPart = part; - _loKitDocument->pClass->setPart(_loKitDocument, part); - } - - if (!getStatus(buffer, length)) - return false; - - _loKitDocument->pClass->registerCallback(_loKitDocument, myCallback, this); - - return true; -} - -void ChildProcessSession::sendFontRendering(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) -{ - std::string font, decodedFont; - int width, height; - unsigned char *pixmap; - - if (tokens.count() < 2 || - !getTokenString(tokens[1], "font", font)) - { - sendTextFrame("error: cmd=renderfont kind=syntax"); - return; - } - - URI::decode(font, decodedFont); - std::string response = "renderfont: " + Poco::cat(std::string(" "), tokens.begin() + 1, tokens.end()) + "\n"; - - std::vector<char> output; - output.resize(response.size()); - std::memcpy(output.data(), response.data(), response.size()); - - Poco::Timestamp timestamp; - pixmap = _loKitDocument->pClass->renderFont(_loKitDocument, decodedFont.c_str(), &width, &height); - std::cout << Util::logPrefix() << "renderFont called, font[" << font << "] rendered in " << double(timestamp.elapsed())/1000 << "ms" << std::endl; - - if (pixmap != nullptr) { - if (!Util::encodePNGAndAppendToBuffer(pixmap, width, height, output, LOK_TILEMODE_RGBA)) - { - sendTextFrame("error: cmd=renderfont kind=failure"); - delete[] pixmap; - return; - } - delete[] pixmap; - } - - sendBinaryFrame(output.data(), output.size()); -} - -bool ChildProcessSession::getStatus(const char* /*buffer*/, int /*length*/) -{ - std::string status = "status: " + LOKitHelper::documentStatus(_loKitDocument); - StringTokenizer tokens(status, " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM); - if (!getTokenString(tokens[1], "type", _docType)) - { - Application::instance().logger().information(Util::logPrefix() + "failed to get document type from" + status); - } - sendTextFrame(status); - - return true; -} - -bool ChildProcessSession::getCommandValues(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) -{ - std::string command; - if (tokens.count() != 2 || !getTokenString(tokens[1], "command", command)) - { - sendTextFrame("error: cmd=commandvalues kind=syntax"); - return false; - } - sendTextFrame("commandvalues: " + std::string(_loKitDocument->pClass->getCommandValues(_loKitDocument, command.c_str()))); - return true; -} - -bool ChildProcessSession::getPartPageRectangles(const char* /*buffer*/, int /*length*/) -{ - sendTextFrame("partpagerectangles: " + std::string(_loKitDocument->pClass->getPartPageRectangles(_loKitDocument))); - return true; -} - -void ChildProcessSession::sendTile(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) -{ - int part, width, height, tilePosX, tilePosY, tileWidth, tileHeight; - - if (tokens.count() < 8 || - !getTokenInteger(tokens[1], "part", part) || - !getTokenInteger(tokens[2], "width", width) || - !getTokenInteger(tokens[3], "height", height) || - !getTokenInteger(tokens[4], "tileposx", tilePosX) || - !getTokenInteger(tokens[5], "tileposy", tilePosY) || - !getTokenInteger(tokens[6], "tilewidth", tileWidth) || - !getTokenInteger(tokens[7], "tileheight", tileHeight)) - { - sendTextFrame("error: cmd=tile kind=syntax"); - return; - } - - if (part < 0 || - width <= 0 || - height <= 0 || - tilePosX < 0 || - tilePosY < 0 || - tileWidth <= 0 || - tileHeight <= 0) - { - sendTextFrame("error: cmd=tile kind=invalid"); - return; - } - - std::string response = "tile: " + Poco::cat(std::string(" "), tokens.begin() + 1, tokens.end()) + "\n"; - - std::vector<char> output; - output.reserve(4 * width * height); - output.resize(response.size()); - std::memcpy(output.data(), response.data(), response.size()); - - unsigned char *pixmap = new unsigned char[4 * width * height]; - memset(pixmap, 0, 4 * width * height); - - if (_docType != "text" && part != _loKitDocument->pClass->getPart(_loKitDocument)) - { - _loKitDocument->pClass->setPart(_loKitDocument, part); - } - - Poco::Timestamp timestamp; - _loKitDocument->pClass->paintTile(_loKitDocument, pixmap, width, height, tilePosX, tilePosY, tileWidth, tileHeight); - std::cout << Util::logPrefix() << "paintTile called, tile at [" << tilePosX << ", " << tilePosY << "] rendered in " << double(timestamp.elapsed())/1000 << "ms" << std::endl; - - LibreOfficeKitTileMode mode = static_cast<LibreOfficeKitTileMode>(_loKitDocument->pClass->getTileMode(_loKitDocument)); - if (!Util::encodePNGAndAppendToBuffer(pixmap, width, height, output, mode)) - { - sendTextFrame("error: cmd=tile kind=failure"); - return; - } - - delete[] pixmap; - - sendBinaryFrame(output.data(), output.size()); -} - -bool ChildProcessSession::clientZoom(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) -{ - int tilePixelWidth, tilePixelHeight, tileTwipWidth, tileTwipHeight; - - if (tokens.count() != 5 || - !getTokenInteger(tokens[1], "tilepixelwidth", tilePixelWidth) || - !getTokenInteger(tokens[2], "tilepixelheight", tilePixelHeight) || - !getTokenInteger(tokens[3], "tiletwipwidth", tileTwipWidth) || - !getTokenInteger(tokens[4], "tiletwipheight", tileTwipHeight)) - { - sendTextFrame("error: cmd=clientzoom kind=syntax"); - return false; - } - - _loKitDocument->pClass->setClientZoom(_loKitDocument, tilePixelWidth, tilePixelHeight, tileTwipWidth, tileTwipHeight); - return true; -} -bool ChildProcessSession::downloadAs(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) -{ - std::string name, id, format, filterOptions; - - if (tokens.count() < 5 || - !getTokenString(tokens[1], "name", name) || - !getTokenString(tokens[2], "id", id)) - { - sendTextFrame("error: cmd=downloadas kind=syntax"); - return false; - } - - getTokenString(tokens[3], "format", format); - - if (getTokenString(tokens[4], "options", filterOptions)) - { - if (tokens.count() > 5) - { - filterOptions += Poco::cat(std::string(" "), tokens.begin() + 5, tokens.end()); - } - } - - std::string tmpDir, url; - File *file = NULL; - do - { - if (file != NULL) - { - delete file; - } - tmpDir = std::to_string((((Poco::UInt64)LOOLWSD::_rng.next()) << 32) | LOOLWSD::_rng.next() | 1); - url = jailDocumentURL + "/" + tmpDir + "/" + name; - file = new File(url); - } while (file->exists()); - delete file; - - _loKitDocument->pClass->saveAs(_loKitDocument, url.c_str(), - format.size() == 0 ? NULL :format.c_str(), - filterOptions.size() == 0 ? NULL : filterOptions.c_str()); - - sendTextFrame("downloadas: jail=" + _childId + " dir=" + tmpDir + " name=" + name + - " port=" + std::to_string(LOOLWSD::portNumber) + " id=" + id); - return true; -} - -bool ChildProcessSession::getChildId() -{ - sendTextFrame("getchildid: id=" + _childId); - return true; -} - -bool ChildProcessSession::getTextSelection(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) -{ - std::string mimeType; - - if (tokens.count() != 2 || - !getTokenString(tokens[1], "mimetype", mimeType)) - { - sendTextFrame("error: cmd=gettextselection kind=syntax"); - return false; - } - - char *textSelection = _loKitDocument->pClass->getTextSelection(_loKitDocument, mimeType.c_str(), NULL); - - sendTextFrame("textselectioncontent: " + std::string(textSelection)); - return true; -} - -bool ChildProcessSession::paste(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) -{ - std::string mimeType; - std::string data; - - if (tokens.count() < 3 || !getTokenString(tokens[1], "mimetype", mimeType) || !getTokenString(tokens[2], "data", data)) - { - sendTextFrame("error: cmd=paste kind=syntax"); - return false; - } - - data = Poco::cat(std::string(" "), tokens.begin() + 2, tokens.end()).substr(strlen("data=")); - - _loKitDocument->pClass->paste(_loKitDocument, mimeType.c_str(), data.c_str(), std::strlen(data.c_str())); - - return true; -} - -bool ChildProcessSession::insertFile(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) -{ - std::string name, type; - - if (tokens.count() != 3 || - !getTokenString(tokens[1], "name", name) || - !getTokenString(tokens[2], "type", type)) - { - sendTextFrame("error: cmd=insertfile kind=syntax"); - return false; - } - - if (type == "graphic") - { - std::string fileName = "file://" + jailDocumentURL + "/insertfile/" + name; - std::string command = ".uno:InsertGraphic"; - std::string arguments = "{" - "\"FileName\":{" - "\"type\":\"string\"," - "\"value\":\"" + fileName + "\"" - "}}"; - _loKitDocument->pClass->postUnoCommand(_loKitDocument, command.c_str(), arguments.c_str(), false); - } - - return true; -} - -bool ChildProcessSession::keyEvent(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) -{ - int type, charcode, keycode; - - if (tokens.count() != 4 || - !getTokenKeyword(tokens[1], "type", - {{"input", LOK_KEYEVENT_KEYINPUT}, {"up", LOK_KEYEVENT_KEYUP}}, - type) || - !getTokenInteger(tokens[2], "char", charcode) || - !getTokenInteger(tokens[3], "key", keycode)) - { - sendTextFrame("error: cmd=key kind=syntax"); - return false; - } - - _loKitDocument->pClass->postKeyEvent(_loKitDocument, type, charcode, keycode); - - return true; -} - -bool ChildProcessSession::mouseEvent(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) -{ - int type, x, y, count, buttons, modifier; - - if (tokens.count() != 7 || - !getTokenKeyword(tokens[1], "type", - {{"buttondown", LOK_MOUSEEVENT_MOUSEBUTTONDOWN}, - {"buttonup", LOK_MOUSEEVENT_MOUSEBUTTONUP}, - {"move", LOK_MOUSEEVENT_MOUSEMOVE}}, - type) || - !getTokenInteger(tokens[2], "x", x) || - !getTokenInteger(tokens[3], "y", y) || - !getTokenInteger(tokens[4], "count", count) || - !getTokenInteger(tokens[5], "buttons", buttons) || - !getTokenInteger(tokens[6], "modifier", modifier)) - { - sendTextFrame("error: cmd=mouse kind=syntax"); - return false; - } - - _loKitDocument->pClass->postMouseEvent(_loKitDocument, type, x, y, count, buttons, modifier); - - return true; -} - -bool ChildProcessSession::unoCommand(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) -{ - if (tokens.count() == 1) - { - sendTextFrame("error: cmd=uno kind=syntax"); - return false; - } - - // we need to get LOK_CALLBACK_UNO_COMMAND_RESULT callback when saving - bool bNotify = (tokens[1] == ".uno:Save"); - - if (tokens.count() == 2) - { - _loKitDocument->pClass->postUnoCommand(_loKitDocument, tokens[1].c_str(), 0, bNotify); - } - else - { - _loKitDocument->pClass->postUnoCommand(_loKitDocument, tokens[1].c_str(), Poco::cat(std::string(" "), tokens.begin() + 2, tokens.end()).c_str(), bNotify); - } - - return true; -} - -bool ChildProcessSession::selectText(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) -{ - int type, x, y; - - if (tokens.count() != 4 || - !getTokenKeyword(tokens[1], "type", - {{"start", LOK_SETTEXTSELECTION_START}, - {"end", LOK_SETTEXTSELECTION_END}, - {"reset", LOK_SETTEXTSELECTION_RESET}}, - type) || - !getTokenInteger(tokens[2], "x", x) || - !getTokenInteger(tokens[3], "y", y)) - { - sendTextFrame("error: cmd=selecttext kind=syntax"); - return false; - } - - _loKitDocument->pClass->setTextSelection(_loKitDocument, type, x, y); - - return true; -} - -bool ChildProcessSession::selectGraphic(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) -{ - int type, x, y; - - if (tokens.count() != 4 || - !getTokenKeyword(tokens[1], "type", - {{"start", LOK_SETGRAPHICSELECTION_START}, - {"end", LOK_SETGRAPHICSELECTION_END}}, - type) || - !getTokenInteger(tokens[2], "x", x) || - !getTokenInteger(tokens[3], "y", y)) - { - sendTextFrame("error: cmd=selectgraphic kind=syntax"); - return false; - } - - _loKitDocument->pClass->setGraphicSelection(_loKitDocument, type, x, y); - - return true; -} - -bool ChildProcessSession::resetSelection(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) -{ - if (tokens.count() != 1) - { - sendTextFrame("error: cmd=resetselection kind=syntax"); - return false; - } - - _loKitDocument->pClass->resetSelection(_loKitDocument); - - return true; -} - -bool ChildProcessSession::saveAs(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) -{ - std::string url, format, filterOptions; - - if (tokens.count() < 4 || - !getTokenString(tokens[1], "url", url)) - { - sendTextFrame("error: cmd=saveas kind=syntax"); - return false; - } - - getTokenString(tokens[2], "format", format); - - if (getTokenString(tokens[3], "options", filterOptions)) - { - if (tokens.count() > 4) - { - filterOptions += Poco::cat(std::string(" "), tokens.begin() + 4, tokens.end()); - } - } - - bool success = _loKitDocument->pClass->saveAs(_loKitDocument, url.c_str(), - format.size() == 0 ? NULL :format.c_str(), - filterOptions.size() == 0 ? NULL : filterOptions.c_str()); - - sendTextFrame("saveas: url=" + url); - std::string successStr = success ? "true" : "false"; - sendTextFrame("unocommandresult: {" - "\"commandName\":\"saveas\"," - "\"success\":\"" + successStr + "\"}"); - - return true; -} - -bool ChildProcessSession::setClientPart(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) -{ - if (tokens.count() < 2 || - !getTokenInteger(tokens[1], "part", _clientPart)) - { - return false; - } - return true; -} - -bool ChildProcessSession::setPage(const char* /*buffer*/, int /*length*/, StringTokenizer& tokens) -{ - int page; - if (tokens.count() < 2 || - !getTokenInteger(tokens[1], "page", page)) - { - sendTextFrame("error: cmd=setpage kind=invalid"); - return false; - } - _loKitDocument->pClass->setPart(_loKitDocument, page); - return true; -} - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/loolwsd/LOOLSession.hpp b/loolwsd/LOOLSession.hpp index 8b7175c..22a372f 100644 --- a/loolwsd/LOOLSession.hpp +++ b/loolwsd/LOOLSession.hpp @@ -106,54 +106,6 @@ inline std::basic_ostream<charT, traits> & operator <<(std::basic_ostream<charT, } } -class ChildProcessSession final : public LOOLSession -{ -public: - ChildProcessSession(std::shared_ptr<Poco::Net::WebSocket> ws, LibreOfficeKit *loKit, std::string _childId); - virtual ~ChildProcessSession(); - - virtual bool handleInput(const char *buffer, int length) override; - - virtual bool getStatus(const char *buffer, int length); - - virtual bool getCommandValues(const char *buffer, int length, Poco::StringTokenizer& tokens); - - virtual bool getPartPageRectangles(const char *buffer, int length) override; - - LibreOfficeKitDocument *_loKitDocument; - std::string _docType; - - protected: - virtual bool loadDocument(const char *buffer, int length, Poco::StringTokenizer& tokens) override; - - virtual void sendTile(const char *buffer, int length, Poco::StringTokenizer& tokens); - - virtual void sendFontRendering(const char *buffer, int length, Poco::StringTokenizer& tokens); - - bool clientZoom(const char *buffer, int length, Poco::StringTokenizer& tokens); - bool downloadAs(const char *buffer, int length, Poco::StringTokenizer& tokens); - bool getChildId(); - bool getTextSelection(const char *buffer, int length, Poco::StringTokenizer& tokens); - bool paste(const char *buffer, int length, Poco::StringTokenizer& tokens); - bool insertFile(const char *buffer, int length, Poco::StringTokenizer& tokens); - bool keyEvent(const char *buffer, int length, Poco::StringTokenizer& tokens); - bool mouseEvent(const char *buffer, int length, Poco::StringTokenizer& tokens); - bool unoCommand(const char *buffer, int length, Poco::StringTokenizer& tokens); - bool selectText(const char *buffer, int length, Poco::StringTokenizer& tokens); - bool selectGraphic(const char *buffer, int length, Poco::StringTokenizer& tokens); - bool resetSelection(const char *buffer, int length, Poco::StringTokenizer& tokens); - bool saveAs(const char *buffer, int length, Poco::StringTokenizer& tokens); - bool setClientPart(const char *buffer, int length, Poco::StringTokenizer& tokens); - bool setPage(const char *buffer, int length, Poco::StringTokenizer& tokens); - - std::string _loSubPath; - LibreOfficeKit *_loKit; - std::string _childId; - - private: - int _clientPart; -}; - #endif /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/loolwsd/LOOLWSD.cpp b/loolwsd/LOOLWSD.cpp index 42aa0b4..b89fb9c 100644 --- a/loolwsd/LOOLWSD.cpp +++ b/loolwsd/LOOLWSD.cpp @@ -106,6 +106,7 @@ DEALINGS IN THE SOFTWARE. #include "LOOLProtocol.hpp" #include "LOOLSession.hpp" #include "MasterProcessSession.hpp" +#include "ChildProcessSession.hpp" #include "LOOLWSD.hpp" #include "MessageQueue.hpp" #include "Util.hpp" diff --git a/loolwsd/Makefile.am b/loolwsd/Makefile.am index 3a81c34..e34292f 100644 --- a/loolwsd/Makefile.am +++ b/loolwsd/Makefile.am @@ -7,7 +7,7 @@ dist_bin_SCRIPTS = loolwsd-systemplate-setup AM_CPPFLAGS = -pthread AM_LDFLAGS = -pthread -loolwsd_SOURCES = LOOLWSD.cpp LOOLSession.cpp MasterProcessSession.cpp MessageQueue.cpp TileCache.cpp Util.cpp LOOLProtocol.cpp +loolwsd_SOURCES = LOOLWSD.cpp LOOLSession.cpp MasterProcessSession.cpp ChildProcessSession.cpp MessageQueue.cpp TileCache.cpp Util.cpp LOOLProtocol.cpp noinst_PROGRAMS = loadtest connect lokitclient @@ -17,7 +17,7 @@ connect_SOURCES = Connect.cpp Util.cpp LOOLProtocol.cpp lokitclient_SOURCES = LOKitClient.cpp Util.cpp -noinst_HEADERS = LOKitHelper.hpp LOOLProtocol.hpp LOOLSession.hpp MasterProcessSession.hpp LOOLWSD.hpp LoadTest.hpp MessageQueue.hpp TileCache.hpp Util.hpp Png.hpp \ +noinst_HEADERS = LOKitHelper.hpp LOOLProtocol.hpp LOOLSession.hpp MasterProcessSession.hpp ChildProcessSession.hpp LOOLWSD.hpp LoadTest.hpp MessageQueue.hpp TileCache.hpp Util.hpp Png.hpp \ bundled/include/LibreOfficeKit/LibreOfficeKit.h bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h \ bundled/include/LibreOfficeKit/LibreOfficeKitInit.h bundled/include/LibreOfficeKit/LibreOfficeKitTypes.h _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/libreoffice-commits