/* -*- 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_CLIENTSSESSION_HPP #define INCLUDED_CLIENTSSESSION_HPP #include "Session.hpp" #include "Storage.hpp" #include "MessageQueue.hpp" #include "SenderQueue.hpp" #include "DocumentBroker.hpp" #include #include #include #include #include #include class DocumentBroker; /// Represents a session to a LOOL client, in the WSD process. class ClientSession final : public Session { public: ClientSession(const std::shared_ptr& ws, const std::string& id, const std::shared_ptr& docBroker, const Poco::URI& uriPublic, const bool isReadOnly, const std::string& hostNoTrust); void construct(); virtual ~ClientSession(); /// Lookup any session by id. static std::shared_ptr getById(const std::string &id); void setReadOnly() override; enum SessionState { DETACHED, // initial LOADING, // attached to a DocBroker & waiting for load LIVE, // Document is loaded & editable or viewable. WAIT_DISCONNECT // closed and waiting for Kit's disconnected message }; /// Returns true if this session has loaded a view (i.e. we got status message). bool isViewLoaded() const { return _state == SessionState::LIVE; } /// returns true if we're waiting for the kit to acknowledge disconnect. bool inWaitDisconnected() const { return _state == SessionState::WAIT_DISCONNECT; } /// transition to a new state void setState(SessionState newState); void setDocumentOwner(const bool documentOwner) { _isDocumentOwner = documentOwner; } bool isDocumentOwner() const { return _isDocumentOwner; } /// Handle kit-to-client message. bool handleKitToClientMessage(const char* data, const int size); /// Integer id of the view in the kit process, or -1 if unknown int getKitViewId() const { return _kitViewId; } /// Disconnect the session and do final cleanup, @returns true if we should not wait. bool disconnectFromKit(); // sendTextFrame that takes std::string and string literal. using Session::sendTextFrame; bool sendBinaryFrame(const char* buffer, int length) override { auto payload = std::make_shared(buffer, length, Message::Dir::Out); enqueueSendMessage(payload); return true; } bool sendTile(const std::string &header, const TileCache::Tile &tile) { // FIXME: performance - optimize away this copy ... std::vector output; output.resize(header.size() + tile->size()); std::memcpy(output.data(), header.data(), header.size()); std::memcpy(output.data() + header.size(), tile->data(), tile->size()); return sendBinaryFrame(output.data(), output.size()); } bool sendTextFrame(const char* buffer, const int length) override { auto payload = std::make_shared(buffer, length, Message::Dir::Out); enqueueSendMessage(payload); return true; } void enqueueSendMessage(const std::shared_ptr& data); /// Set the save-as socket which is used to send convert-to results. void setSaveAsSocket(const std::shared_ptr& socket) { _saveAsSocket = socket; } std::shared_ptr getDocumentBroker() const { return _docBroker.lock(); } /// Exact URI (including query params - access tokens etc.) with which /// client made the request to us /// /// Note: This URI is unsafe - when connecting to existing sessions, we must /// ignore everything but the access_token, and use the access_token with /// the URI of the initial request. const Poco::URI& getPublicUri() const { return _uriPublic; } /// The access token of this session. Authorization getAuthorization() const; const std::string& getCookies() const { return _cookies; } /// Set WOPI fileinfo object void setWopiFileInfo(std::unique_ptr& wopiFileInfo) { _wopiFileInfo = std::move(wopiFileInfo); } /// Get requested tiles waiting for sending to the client std::deque& getRequestedTiles() { return _requestedTiles; } /// Mark a new tile as sent void addTileOnFly(const TileDesc& tile); void clearTilesOnFly(); size_t getTilesOnFlyCount() const { return _tilesOnFly.size(); } void removeOutdatedTilesOnFly(); size_t countIdenticalTilesOnFly(const TileDesc& tile) const; Util::Rectangle getVisibleArea() const { return _clientVisibleArea; } /// Visible area can have negative value as position, but we have tiles only in the positive range Util::Rectangle getNormalizedVisibleArea() const; int getTileWidthInTwips() const { return _tileWidthTwips; } int getTileHeightInTwips() const { return _tileHeightTwips; } /// This method updates internal data related to sent tiles (wireID and tiles-on-fly) /// Call this method anytime when a new tile is sent to the client void traceTileBySend(const TileDesc& tile, bool deduplicated = false); /// Clear wireId map anytime when client visible area changes (visible area, zoom, part number) void resetWireIdMap(); bool isTextDocument() const { return _isTextDocument; } /// Do we recognize this clipboard ? bool matchesClipboardKeys(const std::string &viewId, const std::string &tag); /// Handle a clipboard fetch / put request. void handleClipboardRequest(DocumentBroker::ClipboardRequest type, const std::shared_ptr &socket, const std::string &tag, const std::shared_ptr &data); /// Create URI for transient clipboard content. std::string getClipboardURI(bool encode = true); /// Adds and/or modified the copied payload before sending on to the client. void postProcessCopyPayload(std::shared_ptr payload); /// Returns true if we're expired waiting for a clipboard and should be removed bool staleWaitDisconnect(const std::chrono::steady_clock::time_point &now); /// Generate and rotate a new clipboard hash, sending it if appropriate void rotateClipboardKey(bool notifyClient); private: std::shared_ptr client_from_this() { return std::static_pointer_cast(shared_from_this()); } /// SocketHandler: disconnection event. void onDisconnect() override; /// Does SocketHandler: have messages to send ? bool hasQueuedMessages() const override; /// SocketHandler: send those messages void writeQueuedMessages() override; virtual bool _handleInput(const char* buffer, int length) override; bool loadDocument(const char* buffer, int length, const StringVector& tokens, const std::shared_ptr& docBroker); bool getStatus(const char* buffer, int length, const std::shared_ptr& docBroker); bool getCommandValues(const char* buffer, int length, const StringVector& tokens, const std::shared_ptr& docBroker); bool sendTile(const char* buffer, int length, const StringVector& tokens, const std::shared_ptr& docBroker); bool sendCombinedTiles(const char* buffer, int length, const StringVector& tokens, const std::shared_ptr& docBroker); bool sendFontRendering(const char* buffer, int length, const StringVector& tokens, const std::shared_ptr& docBroker); bool forwardToChild(const std::string& message, const std::shared_ptr& docBroker); bool forwardToClient(const std::shared_ptr& 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; void dumpState(std::ostream& os) override; /// Handle invalidation message comming from a kit and transfer it to a tile request. void handleTileInvalidation(const std::string& message, const std::shared_ptr& docBroker); private: std::weak_ptr _docBroker; /// URI with which client made request to us const Poco::URI _uriPublic; /// The cookies we should pass on to the storage on saving. std::string _cookies; /// Whether this session is the owner of currently opened document bool _isDocumentOwner; /// The socket to which the converted (saveas) doc is sent. std::shared_ptr _saveAsSocket; /// The phase of our lifecycle that we're in. SessionState _state; /// Time of last state transition std::chrono::steady_clock::time_point _lastStateTime; /// Wopi FileInfo object std::unique_ptr _wopiFileInfo; /// Count of key-strokes uint64_t _keyEvents; SenderQueue> _senderQueue; /// Visible area of the client Util::Rectangle _clientVisibleArea; /// Selected part of the document viewed by the client (no parts in Writer) int _clientSelectedPart; /// Zoom properties of the client int _tileWidthPixel; int _tileHeightPixel; int _tileWidthTwips; int _tileHeightTwips; /// The integer id of the view in the Kit process int _kitViewId; /// Un-trusted hostname of our service from the client const std::string _hostNoTrust; /// Client is using a text document? bool _isTextDocument; /// Rotating clipboard remote access identifiers - protected by GlobalSessionMapMutex std::string _clipboardKeys[2]; /// TileID's of the sent tiles. Push by sending and pop by tileprocessed message from the client. std::list> _tilesOnFly; /// Requested tiles are stored in this list, before we can send them to the client std::deque _requestedTiles; /// Store wireID's of the sent tiles inside the actual visible area std::map _oldWireIds; /// Sockets to send binary selection content to std::vector> _clipSockets; ///Time when loading of view started std::chrono::steady_clock::time_point _viewLoadStart; }; #endif /* vim:set shiftwidth=4 softtabstop=4 expandtab: */