Rebased ref, commits from common ancestor: commit c0cd59deb2da338b5df9627727cc474f58bbd252 Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Fri Mar 13 18:48:20 2020 +0000 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Fri Mar 13 18:48:20 2020 +0000
split me up ... Change-Id: If96a87a617a65ef447eac3a8d859d609aef23270 diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp index f265cf2dd..5898946c8 100644 --- a/wsd/LOOLWSD.cpp +++ b/wsd/LOOLWSD.cpp @@ -256,8 +256,31 @@ inline void shutdownLimitReached(const std::shared_ptr<ProtocolHandlerInterface> } #endif -inline void checkSessionLimitsAndWarnClients() +#if !MOBILEAPP +/// Internal implementation to alert all clients +/// connected to any document. +void alertAllUsersInternal(const std::string& msg) +{ + std::lock_guard<std::mutex> docBrokersLock(DocBrokersMutex); + + LOG_INF("Alerting all users: [" << msg << "]"); + + if (UnitWSD::get().filterAlertAllusers(msg)) + return; + + for (auto& brokerIt : DocBrokers) + { + std::shared_ptr<DocumentBroker> docBroker = brokerIt.second; + docBroker->addCallback([msg, docBroker](){ docBroker->alertAllUsers(msg); }); + } +} +#endif + +} // end anonymous namespace + +void LOOLWSD::checkSessionLimitsAndWarnClients() { +#if !ENABLE_SUPPORT_KEY #if !MOBILEAPP ssize_t docBrokerCount = DocBrokers.size() - ConvertToBroker::getInstanceCount(); if (LOOLWSD::MaxDocuments < 10000 && @@ -276,29 +299,10 @@ inline void checkSessionLimitsAndWarnClients() } } #endif -} - -#if !MOBILEAPP -/// Internal implementation to alert all clients -/// connected to any document. -void alertAllUsersInternal(const std::string& msg) -{ - std::lock_guard<std::mutex> docBrokersLock(DocBrokersMutex); - - LOG_INF("Alerting all users: [" << msg << "]"); - - if (UnitWSD::get().filterAlertAllusers(msg)) - return; - - for (auto& brokerIt : DocBrokers) - { - std::shared_ptr<DocumentBroker> docBroker = brokerIt.second; - docBroker->addCallback([msg, docBroker](){ docBroker->alertAllUsers(msg); }); - } -} #endif +} -static void checkDiskSpaceAndWarnClients(const bool cacheLastCheck) +void LOOLWSD::checkDiskSpaceAndWarnClients(const bool cacheLastCheck) { #if !MOBILEAPP try @@ -317,8 +321,6 @@ static void checkDiskSpaceAndWarnClients(const bool cacheLastCheck) #endif } -} - /// Remove dead and idle DocBrokers. /// The client of idle document should've greyed-out long ago. /// Returns true if at least one is removed. @@ -378,7 +380,7 @@ static int forkChildren(const int number) if (number > 0) { - checkDiskSpaceAndWarnClients(false); + LOOLWSD::checkDiskSpaceAndWarnClients(false); #ifdef KIT_IN_PROCESS forkLibreOfficeKit(LOOLWSD::ChildRoot, LOOLWSD::SysTemplate, LOOLWSD::LoTemplate, LO_JAIL_SUBPATH, number); @@ -2811,16 +2813,20 @@ private: } } + const std::string hostNoTrust = (LOOLWSD::ServerName.empty() ? request.getHost() : LOOLWSD::ServerName); + LOG_INF("URL [" << LOOLWSD::anonymizeUrl(url) << "] is " << (isReadOnly ? "readonly" : "writable") << "."); (void)request; (void)message; (void)disposition; + std::shared_ptr<ProtocolHandlerInterface> none; // Request a kit process for this doc. std::shared_ptr<DocumentBroker> docBroker = findOrCreateDocBroker( - std::static_pointer_cast<ProtocolHandlerInterface>(ws), url, docKey, _id, uriPublic); + none, url, docKey, _id, uriPublic); if (docBroker) { // need to move into the DocumentBroker context before doing session lookup / creation etc. - disposition.setMove([docBroker, _id, uriPublic, isReadOnly, hostNoTrust, sessionId] + std::string id = _id; + disposition.setMove([docBroker, id, uriPublic, isReadOnly, hostNoTrust, sessionId] (const std::shared_ptr<Socket> &moveSocket) { // Make sure the thread is running before adding callback. @@ -2830,21 +2836,17 @@ private: moveSocket->setThreadOwner(std::thread::id()); - docBroker->addCallback([docBroker, _id, uriPublic, isReadOnly, hostNoTrust, sessionId]() + docBroker->addCallback([docBroker, id, uriPublic, isReadOnly, hostNoTrust, sessionId, moveSocket]() { // Now inside the document broker thread ... try { auto streamSocket = std::static_pointer_cast<StreamSocket>(moveSocket); docBroker->handleProxyRequest( - sessionId, _id, uriPublic, isReadOnly, + sessionId, id, uriPublic, isReadOnly, hostNoTrust, moveSocket); return; } - catch (const std::exception& exc) - { - LOG_ERR("Exception while loading : " << exc.what()); - } catch (const UnauthorizedRequestException& exc) { LOG_ERR("Unauthorized Request while loading session for " << docBroker->getDocKey() << ": " << exc.what()); @@ -2867,17 +2869,7 @@ private: socket->send(oss.str()); socket->shutdown(); }); - }); - } - else - { - LOG_WRN("Failed to create Client Session with id [" << _id << "] on docKey [" << docKey << "]."); - } - } - else - { - throw ServiceUnavailableException("Failed to create DocBroker with docKey [" + docKey + "]."); - } + }); } catch (const std::exception& exc) { @@ -2886,7 +2878,6 @@ private: ws->sendMessage(msg); ws->shutdown(WebSocketHandler::StatusCodes::ENDPOINT_GOING_AWAY, msg); } -#endif } void handleClientWsUpgrade(const Poco::Net::HTTPRequest& request, const std::string& url, @@ -2990,12 +2981,10 @@ private: // Add and load the session. docBroker->addSession(clientSession); - checkDiskSpaceAndWarnClients(true); -#if !ENABLE_SUPPORT_KEY + LOOLWSD::checkDiskSpaceAndWarnClients(true); // Users of development versions get just an info // when reaching max documents or connections - checkSessionLimitsAndWarnClients(); -#endif + LOOLWSD::checkSessionLimitsAndWarnClients(); } catch (const UnauthorizedRequestException& exc) { diff --git a/wsd/LOOLWSD.hpp b/wsd/LOOLWSD.hpp index 166de2817..383263314 100644 --- a/wsd/LOOLWSD.hpp +++ b/wsd/LOOLWSD.hpp @@ -191,6 +191,10 @@ public: /// child kit processes and cleans up DocBrokers. static void doHousekeeping(); + static void checkDiskSpaceAndWarnClients(const bool cacheLastCheck); + + static void checkSessionLimitsAndWarnClients(); + /// Close document with @docKey and a @message static void closeDocument(const std::string& docKey, const std::string& message); diff --git a/wsd/ProxyProtocol.cpp b/wsd/ProxyProtocol.cpp index 80da1c9ce..0f447e65b 100644 --- a/wsd/ProxyProtocol.cpp +++ b/wsd/ProxyProtocol.cpp @@ -10,7 +10,10 @@ #include <config.h> #include "DocumentBroker.hpp" +#include "ClientSession.hpp" #include "ProxyProtocol.hpp" +#include "LOOLWSD.hpp" +#include <Socket.hpp> #include <atomic> #include <cassert> @@ -26,25 +29,20 @@ void DocumentBroker::handleProxyRequest( std::shared_ptr<ClientSession> clientSession; if (sessionId == "fetchsession") { - clientSession = - docBroker->createNewClientSession( + clientSession = createNewClientSession( std::make_shared<ProxyProtocolHandler>(), - _id, uriPublic, isReadOnly, hostNoTrust); + id, uriPublic, isReadOnly, hostNoTrust); addSession(clientSession); - checkDiskSpaceAndWarnClients(true); -#if !ENABLE_SUPPORT_KEY - // Users of development versions get just an info - // when reaching max documents or connections - checkSessionLimitsAndWarnClients(); -#endif + LOOLWSD::checkDiskSpaceAndWarnClients(true); + LOOLWSD::checkSessionLimitsAndWarnClients(); } else { for (const auto &it : _sessions) { - if (it->getId() == sessionId) + if (it.second->getId() == sessionId) { - clientSession = it; + clientSession = it.second; break; } } @@ -56,7 +54,8 @@ void DocumentBroker::handleProxyRequest( } auto protocol = clientSession->getProtocol(); - socket->setHandler(protcol); + auto streamSocket = std::static_pointer_cast<StreamSocket>(socket); + streamSocket->setHandler(protocol); // this DocumentBroker's poll handles reading & writing addSocketToPoll(socket); diff --git a/wsd/ProxyProtocol.hpp b/wsd/ProxyProtocol.hpp index d2c7a9386..f051f53d1 100644 --- a/wsd/ProxyProtocol.hpp +++ b/wsd/ProxyProtocol.hpp @@ -25,21 +25,22 @@ public: } /// Will be called exactly once. - void onConnect(const std::shared_ptr<StreamSocket>& socket) override + void onConnect(const std::shared_ptr<StreamSocket>& /* socket */) override { assert("we handle sockets differently" && false); } /// Called after successful socket reads. - void handleIncomingMessage(SocketDisposition &disposition) override + void handleIncomingMessage(SocketDisposition &/* disposition */) override { assert("we get our data a different way" && false); } - int getPollEvents(std::chrono::steady_clock::time_point now, - int &timeoutMaxMs) override + int getPollEvents(std::chrono::steady_clock::time_point /* now */, + int &/* timeoutMaxMs */) override { - assert("hmm - polling is different ..." && false); + // underlying buffer based polling is fine. + return POLLIN; } void checkTimeout(std::chrono::steady_clock::time_point /* now */) override @@ -62,20 +63,25 @@ public: int sendTextMessage(const std::string &msg, const size_t len, bool flush = false) const override { LOG_TRC("ProxyHack - send text msg " + msg); + (void) flush; + return len; } int sendBinaryMessage(const char *data, const size_t len, bool flush = false) const override { + (void) data; (void) flush; LOG_TRC("ProxyHack - send binary msg len " << len); + return len; } void shutdown(bool goingAway = false, const std::string &statusMessage = "") override { - LOG_TRC("ProxyHack - shutdown " << statusMessage); + LOG_TRC("ProxyHack - shutdown " << goingAway << ": " << statusMessage); } void getIOStats(uint64_t &sent, uint64_t &recv) override { + sent = recv = 0; } void dumpState(std::ostream& os) commit 89bb7601b9989fca17563c73200d07528629c605 Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Wed Mar 4 13:54:04 2020 +0000 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Fri Mar 13 18:28:55 2020 +0000 Proxy websocket prototype. Try to read/write avoiding a websocket. Change-Id: I382039fa88f1030a63df1e47f687df2ee5a6055b diff --git a/Makefile.am b/Makefile.am index 5a67a5faf..47ec9c059 100644 --- a/Makefile.am +++ b/Makefile.am @@ -112,6 +112,7 @@ loolwsd_sources = common/Crypto.cpp \ wsd/AdminModel.cpp \ wsd/Auth.cpp \ wsd/DocumentBroker.cpp \ + wsd/ProxyProtocol.cpp \ wsd/LOOLWSD.cpp \ wsd/ClientSession.cpp \ wsd/FileServer.cpp \ @@ -203,6 +204,7 @@ wsd_headers = wsd/Admin.hpp \ wsd/Auth.hpp \ wsd/ClientSession.hpp \ wsd/DocumentBroker.hpp \ + wsd/ProxyProtocol.hpp \ wsd/Exceptions.hpp \ wsd/FileServer.hpp \ wsd/LOOLWSD.hpp \ diff --git a/loleaflet/js/global.js b/loleaflet/js/global.js index f98450fb0..d07854bdf 100644 --- a/loleaflet/js/global.js +++ b/loleaflet/js/global.js @@ -166,16 +166,97 @@ }; this.onopen = function() { }; + this.close = function() { + }; }; - - global.FakeWebSocket.prototype.close = function() { - }; - global.FakeWebSocket.prototype.send = function(data) { this.sendCounter++; window.postMobileMessage(data); }; + global.proxySocketCounter = 0; + global.ProxySocket = function (uri) { + this.uri = uri; + this.binaryType = 'arraybuffer'; + this.bufferedAmount = 0; + this.extensions = ''; + this.protocol = ''; + this.connected = true; + this.readyState = 0; // connecting + this.sessionId = 'fetchsession'; + this.id = window.proxySocketCounter++; + this.sendCounter = 0; + this.readWaiting = false; + this.onclose = function() { + }; + this.onerror = function() { + }; + this.onmessage = function() { + }; + this.send = function(msg) { + console.debug('send msg "' + msg + '"'); + var req = new XMLHttpRequest(); + req.open('POST', this.getEndPoint('write')); + req.setRequestHeader('SessionId', this.sessionId); + if (this.sessionId === 'fetchsession') + req.addEventListener('load', function() { + console.debug('got session: ' + this.responseText); + that.sessionId = this.responseText; + that.readyState = 1; + that.onopen(); + }); + req.send(msg); + }, + this.close = function() { + console.debug('close socket'); + this.readyState = 3; + this.onclose(); + }; + this.getEndPoint = function(type) { + var base = this.uri; + return base.replace(/^ws/, 'http') + '/' + type; + }; + console.debug('New proxy socket ' + this.id + ' ' + this.uri); + + // FIXME: perhaps a little risky. + this.send('fetchsession'); + var that = this; + + // horrors ... + this.readInterval = setInterval(function() { + if (this.readWaiting) // one at a time for now + return; + if (this.sessionId == 'fetchsession') + return; // waiting for our session id. + var req = new XMLHttpRequest(); + // fetch session id: + req.addEventListener('load', function() { + console.debug('read: ' + this.responseText); + if (this.status == 200) + { + that.onmessage({ data: this.response }); + } + else + { + console.debug('Handle error ' + this.status); + } + that.readWaiting = false; + }); + req.open('GET', that.getEndPoint('read')); + req.setRequestHeader('SessionId', this.sessionId); + req.send(that.sessionId); + that.readWaiting = true; + }, 250); + }; + + global.createWebSocket = function(uri) { + if (global.socketProxy) { + return new global.ProxySocket(uri); + } else { + return new WebSocket(uri); + } + }; + // If not debug, don't print anything on the console // except in tile debug mode (Ctrl-Shift-Alt-d) console.log2 = console.log; @@ -275,7 +356,7 @@ var websocketURI = global.host + global.serviceRoot + '/lool/' + encodeURIComponent(global.docURL + (docParams ? '?' + docParams : '')) + '/ws' + wopiSrc; try { - global.socket = new WebSocket(websocketURI); + global.socket = global.createWebSocket(websocketURI); } catch (err) { console.log(err); } diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js index a173ef784..0d74b9bd8 100644 --- a/loleaflet/src/core/Socket.js +++ b/loleaflet/src/core/Socket.js @@ -49,7 +49,7 @@ L.Socket = L.Class.extend({ } try { - this.socket = new WebSocket(this.getWebSocketBaseURI(map) + wopiSrc); + this.socket = window.createWebSocket(this.getWebSocketBaseURI(map) + wopiSrc); } catch (e) { // On IE 11 there is a limitation on the number of WebSockets open to a single domain (6 by default and can go to 128). // Detect this and hint the user. diff --git a/net/Socket.hpp b/net/Socket.hpp index 1e632516e..9692ac701 100644 --- a/net/Socket.hpp +++ b/net/Socket.hpp @@ -446,6 +446,11 @@ public: } } + std::shared_ptr<ProtocolHandlerInterface> getProtocol() const + { + return _protocol; + } + /// Do we have something to send ? virtual bool hasQueuedMessages() const = 0; /// Please send them to me then. diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp index b47285dd0..e347dbd74 100644 --- a/wsd/ClientSession.hpp +++ b/wsd/ClientSession.hpp @@ -37,9 +37,6 @@ public: void construct(); virtual ~ClientSession(); - /// Lookup any session by id. - static std::shared_ptr<ClientSession> getById(const std::string &id); - void setReadOnly() override; enum SessionState { @@ -288,7 +285,6 @@ private: std::chrono::steady_clock::time_point _viewLoadStart; }; - #endif /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp index 68369d274..dd84960d1 100644 --- a/wsd/DocumentBroker.hpp +++ b/wsd/DocumentBroker.hpp @@ -251,6 +251,15 @@ public: const bool isReadOnly, const std::string& hostNoTrust); + /// Find or create a new client session for the PHP proxy + void handleProxyRequest( + const std::string& sessionId, + const std::string& id, + const Poco::URI& uriPublic, + const bool isReadOnly, + const std::string& hostNoTrust, + const std::shared_ptr<Socket> &moveSocket); + /// Thread safe termination of this broker if it has a lingering thread void joinThread(); diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp index a06687788..f265cf2dd 100644 --- a/wsd/LOOLWSD.cpp +++ b/wsd/LOOLWSD.cpp @@ -2240,6 +2240,13 @@ private: // Util::dumpHex(std::cerr, "clipboard:\n", "", socket->getInBuffer()); // lots of data ... handleClipboardRequest(request, message, disposition); } + else if (request.has("ProxyPrefix") && reqPathTokens.count() > 2 && + (reqPathTokens[reqPathTokens.count()-2] == "ws")) + { + std::string decodedUri; // WOPISrc + Poco::URI::decode(reqPathTokens[1], decodedUri); + handleClientProxyRequest(request, decodedUri, message, disposition); + } else if (!(request.find("Upgrade") != request.end() && Poco::icompare(request["Upgrade"], "websocket") == 0) && reqPathTokens.count() > 0 && reqPathTokens[0] == "lool") { @@ -2773,6 +2780,115 @@ private: } #endif + void handleClientProxyRequest(const Poco::Net::HTTPRequest& request, + std::string url, + Poco::MemoryInputStream& message, + SocketDisposition &disposition) + { + if (!request.has("SessionId")) + throw BadRequestException("No session id header on proxied request"); + + std::string sessionId = request.get("SessionId"); + + LOG_INF("URL [" << url << "]."); + const auto uriPublic = DocumentBroker::sanitizeURI(url); + LOG_INF("URI [" << uriPublic.getPath() << "]."); + const auto docKey = DocumentBroker::getDocKey(uriPublic); + LOG_INF("DocKey [" << docKey << "]."); + const std::string fileId = Util::getFilenameFromURL(docKey); + Util::mapAnonymized(fileId, fileId); // Identity mapping, since fileId is already obfuscated + + LOG_INF("Starting Proxy request handler for session [" << _id << "] on url [" << LOOLWSD::anonymizeUrl(url) << "]."); + + // Check if readonly session is required + bool isReadOnly = false; + for (const auto& param : uriPublic.getQueryParameters()) + { + LOG_DBG("Query param: " << param.first << ", value: " << param.second); + if (param.first == "permission" && param.second == "readonly") + { + isReadOnly = true; + } + } + + LOG_INF("URL [" << LOOLWSD::anonymizeUrl(url) << "] is " << (isReadOnly ? "readonly" : "writable") << "."); + (void)request; (void)message; (void)disposition; + + // Request a kit process for this doc. + std::shared_ptr<DocumentBroker> docBroker = findOrCreateDocBroker( + std::static_pointer_cast<ProtocolHandlerInterface>(ws), url, docKey, _id, uriPublic); + if (docBroker) + { + // need to move into the DocumentBroker context before doing session lookup / creation etc. + disposition.setMove([docBroker, _id, uriPublic, isReadOnly, hostNoTrust, sessionId] + (const std::shared_ptr<Socket> &moveSocket) + { + // Make sure the thread is running before adding callback. + docBroker->startThread(); + + // We no longer own this socket. + moveSocket->setThreadOwner(std::thread::id()); + + + docBroker->addCallback([docBroker, _id, uriPublic, isReadOnly, hostNoTrust, sessionId]() + { + // Now inside the document broker thread ... + try + { + auto streamSocket = std::static_pointer_cast<StreamSocket>(moveSocket); + docBroker->handleProxyRequest( + sessionId, _id, uriPublic, isReadOnly, + hostNoTrust, moveSocket); + return; + } + catch (const std::exception& exc) + { + LOG_ERR("Exception while loading : " << exc.what()); + } + catch (const UnauthorizedRequestException& exc) + { + LOG_ERR("Unauthorized Request while loading session for " << docBroker->getDocKey() << ": " << exc.what()); + } + catch (const StorageConnectionException& exc) + { + LOG_ERR("Error while loading : " << exc.what()); + } + catch (const std::exception& exc) + { + LOG_ERR("Error while loading : " << exc.what()); + } + // badness occured: + std::ostringstream oss; + oss << "HTTP/1.1 400\r\n" + << "Date: " << Util::getHttpTimeNow() << "\r\n" + << "User-Agent: LOOLWSD WOPI Agent\r\n" + << "Content-Length: 0\r\n" + << "\r\n"; + socket->send(oss.str()); + socket->shutdown(); + }); + }); + } + else + { + LOG_WRN("Failed to create Client Session with id [" << _id << "] on docKey [" << docKey << "]."); + } + } + else + { + throw ServiceUnavailableException("Failed to create DocBroker with docKey [" + docKey + "]."); + } + } + catch (const std::exception& exc) + { + LOG_ERR("Error while handling Client WS Request: " << exc.what()); + const std::string msg = "error: cmd=internal kind=load"; + ws->sendMessage(msg); + ws->shutdown(WebSocketHandler::StatusCodes::ENDPOINT_GOING_AWAY, msg); + } +#endif + } + void handleClientWsUpgrade(const Poco::Net::HTTPRequest& request, const std::string& url, SocketDisposition &disposition) { @@ -2835,7 +2951,7 @@ private: // Request a kit process for this doc. std::shared_ptr<DocumentBroker> docBroker = findOrCreateDocBroker( std::static_pointer_cast<ProtocolHandlerInterface>(ws), url, docKey, _id, uriPublic); - if (docBroker) + if (docBroker) { #if MOBILEAPP const std::string hostNoTrust; diff --git a/wsd/ProxyProtocol.cpp b/wsd/ProxyProtocol.cpp new file mode 100644 index 000000000..80da1c9ce --- /dev/null +++ b/wsd/ProxyProtocol.cpp @@ -0,0 +1,70 @@ +/* -*- 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 <config.h> + +#include "DocumentBroker.hpp" +#include "ProxyProtocol.hpp" + +#include <atomic> +#include <cassert> + +void DocumentBroker::handleProxyRequest( + const std::string& sessionId, + const std::string& id, + const Poco::URI& uriPublic, + const bool isReadOnly, + const std::string& hostNoTrust, + const std::shared_ptr<Socket> &socket) +{ + std::shared_ptr<ClientSession> clientSession; + if (sessionId == "fetchsession") + { + clientSession = + docBroker->createNewClientSession( + std::make_shared<ProxyProtocolHandler>(), + _id, uriPublic, isReadOnly, hostNoTrust); + addSession(clientSession); + checkDiskSpaceAndWarnClients(true); +#if !ENABLE_SUPPORT_KEY + // Users of development versions get just an info + // when reaching max documents or connections + checkSessionLimitsAndWarnClients(); +#endif + } + else + { + for (const auto &it : _sessions) + { + if (it->getId() == sessionId) + { + clientSession = it; + break; + } + } + if (!clientSession) + { + LOG_ERR("Invalid session id used " << sessionId); + throw BadRequestException("invalid session id"); + } + } + + auto protocol = clientSession->getProtocol(); + socket->setHandler(protcol); + + // this DocumentBroker's poll handles reading & writing + addSocketToPoll(socket); + + auto proxy = std::static_pointer_cast<ProxyProtocolHandler>( + protocol); + + proxy->handleRequest(uriPublic, socket); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/wsd/ProxyProtocol.hpp b/wsd/ProxyProtocol.hpp new file mode 100644 index 000000000..d2c7a9386 --- /dev/null +++ b/wsd/ProxyProtocol.hpp @@ -0,0 +1,95 @@ +/* -*- 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_PROXY_PROTOCOL_HPP +#define INCLUDED_PROXY_PROTOCOL_HPP + +#include <net/Socket.hpp> + +/// Interface for building a websocket from this ... +class ProxyProtocolHandler : public ProtocolHandlerInterface +{ +public: + ProxyProtocolHandler() + { + } + + virtual ~ProxyProtocolHandler() + { + } + + /// Will be called exactly once. + void onConnect(const std::shared_ptr<StreamSocket>& socket) override + { + assert("we handle sockets differently" && false); + } + + /// Called after successful socket reads. + void handleIncomingMessage(SocketDisposition &disposition) override + { + assert("we get our data a different way" && false); + } + + int getPollEvents(std::chrono::steady_clock::time_point now, + int &timeoutMaxMs) override + { + assert("hmm - polling is different ..." && false); + } + + void checkTimeout(std::chrono::steady_clock::time_point /* now */) override + { + } + + void performWrites() override + { + } + + void onDisconnect() override + { + // connections & sockets come and go a lot. + } + +public: + /// Clear all external references + virtual void dispose() { _msgHandler.reset(); } + + int sendTextMessage(const std::string &msg, const size_t len, bool flush = false) const override + { + LOG_TRC("ProxyHack - send text msg " + msg); + } + + int sendBinaryMessage(const char *data, const size_t len, bool flush = false) const override + { + LOG_TRC("ProxyHack - send binary msg len " << len); + } + + void shutdown(bool goingAway = false, const std::string &statusMessage = "") override + { + LOG_TRC("ProxyHack - shutdown " << statusMessage); + } + + void getIOStats(uint64_t &sent, uint64_t &recv) override + { + } + + void dumpState(std::ostream& os) + { + os << "proxy protocol\n"; + } + + void handleRequest(const Poco::URI &uriPublic, + const std::shared_ptr<Socket> &socket); + +private: + std::vector<std::weak_ptr<StreamSocket>> _sockets; +}; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ commit 381b0f6c3ee0f4bd0aaf5ff6153ba55afae8d09f Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Wed Mar 4 13:52:51 2020 +0000 Commit: Michael Meeks <michael.me...@collabora.com> CommitDate: Fri Mar 13 15:43:58 2020 +0000 ProxyPrefix: allow the user to specify a custom prefix. This allows us to re-direct web traffic via a proxy quite simply during fetch, instead of changing the service root. Change-Id: I28d348467e48394d581fca4da4c199348a2ca8e0 diff --git a/loleaflet/html/loleaflet.html.m4 b/loleaflet/html/loleaflet.html.m4 index 84fd50b15..86e54249f 100644 --- a/loleaflet/html/loleaflet.html.m4 +++ b/loleaflet/html/loleaflet.html.m4 @@ -235,6 +235,7 @@ m4_ifelse(MOBILEAPP,[true], window.reuseCookies = ''; window.protocolDebug = false; window.frameAncestors = ''; + window.socketProxy = false; window.tileSize = 256;], [window.host = '%HOST%'; window.serviceRoot = '%SERVICE_ROOT%'; @@ -247,6 +248,7 @@ m4_ifelse(MOBILEAPP,[true], window.reuseCookies = '%REUSE_COOKIES%'; window.protocolDebug = %PROTOCOL_DEBUG%; window.frameAncestors = '%FRAME_ANCESTORS%'; + window.socketProxy = %SOCKET_PROXY%; window.tileSize = 256;]) m4_syscmd([cat ]GLOBAL_JS)m4_dnl </script> diff --git a/wsd/FileServer.cpp b/wsd/FileServer.cpp index ce9e12756..2b53999c8 100644 --- a/wsd/FileServer.cpp +++ b/wsd/FileServer.cpp @@ -595,6 +595,17 @@ constexpr char BRANDING[] = "branding"; constexpr char BRANDING_UNSUPPORTED[] = "branding-unsupported"; #endif +namespace { + // The user can override the ServerRoot with a new prefix. + std::string getResponseRoot(const HTTPRequest &request) + { + if (!request.has("ProxyPrefix")) + return LOOLWSD::ServiceRoot; + std::string proxyPrefix = request.get("ProxyPrefix", ""); + return proxyPrefix; + } +} + void FileServerRequestHandler::preprocessFile(const HTTPRequest& request, Poco::MemoryInputStream& message, const std::shared_ptr<StreamSocket>& socket) { @@ -641,15 +652,21 @@ void FileServerRequestHandler::preprocessFile(const HTTPRequest& request, Poco:: } } - const auto& config = Application::instance().config(); + std::string socketProxy = "false"; + if (request.has("ProxyPrefix")) + socketProxy = "true"; + Poco::replaceInPlace(preprocess, std::string("%SOCKET_PROXY%"), socketProxy); + + std::string responseRoot = getResponseRoot(request); Poco::replaceInPlace(preprocess, std::string("%ACCESS_TOKEN%"), escapedAccessToken); Poco::replaceInPlace(preprocess, std::string("%ACCESS_TOKEN_TTL%"), std::to_string(tokenTtl)); Poco::replaceInPlace(preprocess, std::string("%ACCESS_HEADER%"), escapedAccessHeader); Poco::replaceInPlace(preprocess, std::string("%HOST%"), host); Poco::replaceInPlace(preprocess, std::string("%VERSION%"), std::string(LOOLWSD_VERSION_HASH)); - Poco::replaceInPlace(preprocess, std::string("%SERVICE_ROOT%"), LOOLWSD::ServiceRoot); + Poco::replaceInPlace(preprocess, std::string("%SERVICE_ROOT%"), responseRoot); + const auto& config = Application::instance().config(); std::string protocolDebug = "false"; if (config.getBool("logging.protocol")) protocolDebug = "true"; @@ -658,16 +675,16 @@ void FileServerRequestHandler::preprocessFile(const HTTPRequest& request, Poco:: static const std::string linkCSS("<link rel=\"stylesheet\" href=\"%s/loleaflet/" LOOLWSD_VERSION_HASH "/%s.css\">"); static const std::string scriptJS("<script src=\"%s/loleaflet/" LOOLWSD_VERSION_HASH "/%s.js\"></script>"); - std::string brandCSS(Poco::format(linkCSS, LOOLWSD::ServiceRoot, std::string(BRANDING))); - std::string brandJS(Poco::format(scriptJS, LOOLWSD::ServiceRoot, std::string(BRANDING))); + std::string brandCSS(Poco::format(linkCSS, responseRoot, std::string(BRANDING))); + std::string brandJS(Poco::format(scriptJS, responseRoot, std::string(BRANDING))); #if ENABLE_SUPPORT_KEY const std::string keyString = config.getString("support_key", ""); SupportKey key(keyString); if (!key.verify() || key.validDaysRemaining() <= 0) { - brandCSS = Poco::format(linkCSS, LOOLWSD::ServiceRoot, std::string(BRANDING_UNSUPPORTED)); - brandJS = Poco::format(scriptJS, LOOLWSD::ServiceRoot, std::string(BRANDING_UNSUPPORTED)); + brandCSS = Poco::format(linkCSS, responseRoot, std::string(BRANDING_UNSUPPORTED)); + brandJS = Poco::format(scriptJS, responseRoot, std::string(BRANDING_UNSUPPORTED)); } #endif @@ -850,13 +867,15 @@ void FileServerRequestHandler::preprocessAdminFile(const HTTPRequest& request,co if (!FileServerRequestHandler::isAdminLoggedIn(request, response)) throw Poco::Net::NotAuthenticatedException("Invalid admin login"); + std::string responseRoot = getResponseRoot(request); + static const std::string scriptJS("<script src=\"%s/loleaflet/" LOOLWSD_VERSION_HASH "/%s.js\"></script>"); static const std::string footerPage("<div class=\"footer navbar-fixed-bottom text-info text-center\"><strong>Key:</strong> %s <strong>Expiry Date:</strong> %s</div>"); const std::string relPath = getRequestPathname(request); LOG_DBG("Preprocessing file: " << relPath); std::string adminFile = *getUncompressedFile(relPath); - std::string brandJS(Poco::format(scriptJS, LOOLWSD::ServiceRoot, std::string(BRANDING))); + std::string brandJS(Poco::format(scriptJS, responseRoot, std::string(BRANDING))); std::string brandFooter; #if ENABLE_SUPPORT_KEY @@ -874,7 +893,7 @@ void FileServerRequestHandler::preprocessAdminFile(const HTTPRequest& request,co Poco::replaceInPlace(adminFile, std::string("<!--%BRANDING_JS%-->"), brandJS); Poco::replaceInPlace(adminFile, std::string("<!--%FOOTER%-->"), brandFooter); Poco::replaceInPlace(adminFile, std::string("%VERSION%"), std::string(LOOLWSD_VERSION_HASH)); - Poco::replaceInPlace(adminFile, std::string("%SERVICE_ROOT%"), LOOLWSD::ServiceRoot); + Poco::replaceInPlace(adminFile, std::string("%SERVICE_ROOT%"), responseRoot); // Ask UAs to block if they detect any XSS attempt response.add("X-XSS-Protection", "1; mode=block"); commit dc15a4c398a1fbe2bd686e13d88aefa960a19eb1 Author: Tamás Zolnai <zolnaitamas2...@gmail.com> AuthorDate: Fri Mar 13 13:47:15 2020 +0100 Commit: Tamás Zolnai <tamas.zol...@collabora.com> CommitDate: Fri Mar 13 15:00:54 2020 +0100 cypress: integrate cypress-failed-log package. To get a better log in case of error. Change-Id: Id8f72c78cb8a80e30ba3012147caa8106e1e7ce2 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/90470 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Tamás Zolnai <tamas.zol...@collabora.com> diff --git a/cypress_test/.gitignore b/cypress_test/.gitignore index 3e5e8dbd2..0b6b5b539 100644 --- a/cypress_test/.gitignore +++ b/cypress_test/.gitignore @@ -2,3 +2,4 @@ node_modules cypress package-lock.json workdir +support/commands.js diff --git a/cypress_test/cypress.json b/cypress_test/cypress.json index 77aa372d3..1b6d041f9 100644 --- a/cypress_test/cypress.json +++ b/cypress_test/cypress.json @@ -3,6 +3,6 @@ "video" : false, "pluginsFile" : "plugins/index.js", "defaultCommandTimeout" : 6000, - "supportFile" : false, + "supportFile" : "support/index.js", "ignoreTestFiles" : "*helper.js" } diff --git a/cypress_test/package.json b/cypress_test/package.json index d43558232..9c9e1071e 100644 --- a/cypress_test/package.json +++ b/cypress_test/package.json @@ -5,6 +5,7 @@ "license": "MPL-2.0", "dependencies": { "cypress": "4.1.0", + "cypress-failed-log": "2.6.2", "eslint": "6.8.0", "get-port-cli": "2.0.0", "wait-on": "4.0.0" diff --git a/cypress_test/plugins/index.js b/cypress_test/plugins/index.js index 8bf86dcba..2d07606eb 100644 --- a/cypress_test/plugins/index.js +++ b/cypress_test/plugins/index.js @@ -4,7 +4,8 @@ var tasks = require('./tasks'); function plugin(on, config) { on('task', { - copyFile: tasks.copyFile + copyFile: tasks.copyFile, + failed: require('cypress-failed-log/src/failed')() }); return config; diff --git a/cypress_test/support/index.js b/cypress_test/support/index.js new file mode 100644 index 000000000..8f683db34 --- /dev/null +++ b/cypress_test/support/index.js @@ -0,0 +1,3 @@ +/* global require */ + +require('cypress-failed-log'); commit 52d9cde47cce8c02eb775c96fa5c2e8d503bd9bd Author: Tamás Zolnai <zolnaitamas2...@gmail.com> AuthorDate: Fri Mar 13 13:22:02 2020 +0100 Commit: Tamás Zolnai <tamas.zol...@collabora.com> CommitDate: Fri Mar 13 15:00:43 2020 +0100 cypress: mobile: improve selectAllMobile in writer_helper.js. Change-Id: I789a7b7c5b03b9f222730ec2fd06c8c9514578d7 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/90469 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Tamás Zolnai <tamas.zol...@collabora.com> diff --git a/cypress_test/integration_tests/mobile/writer/writer_helper.js b/cypress_test/integration_tests/mobile/writer/writer_helper.js index 88b591270..fb657b437 100644 --- a/cypress_test/integration_tests/mobile/writer/writer_helper.js +++ b/cypress_test/integration_tests/mobile/writer/writer_helper.js @@ -96,7 +96,8 @@ function clearMobileWizardState() { function selectAllMobile() { // Remove selection if exist - cy.get('#document-container').click(); + cy.get('#document-container') + .type('{downarrow}'); cy.get('.leaflet-marker-icon') .should('not.exist'); commit e4e24c6869ddc7c21de4913cd7347a90238aa2ce Author: Tamás Zolnai <zolnaitamas2...@gmail.com> AuthorDate: Fri Mar 13 12:39:33 2020 +0100 Commit: Tamás Zolnai <tamas.zol...@collabora.com> CommitDate: Fri Mar 13 15:00:37 2020 +0100 cypress: ignore helper.js files during running tests. Change-Id: I2d74c4138a2ed05fd97630edf16d3daf4a67de91 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/90468 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Tamás Zolnai <tamas.zol...@collabora.com> diff --git a/cypress_test/cypress.json b/cypress_test/cypress.json index a85b1ce92..77aa372d3 100644 --- a/cypress_test/cypress.json +++ b/cypress_test/cypress.json @@ -3,5 +3,6 @@ "video" : false, "pluginsFile" : "plugins/index.js", "defaultCommandTimeout" : 6000, - "supportFile" : false + "supportFile" : false, + "ignoreTestFiles" : "*helper.js" } commit 7455f69dd72e0c69705f1f2fbdad4f0033115f06 Author: Tamás Zolnai <zolnaitamas2...@gmail.com> AuthorDate: Fri Mar 13 12:36:50 2020 +0100 Commit: Tamás Zolnai <tamas.zol...@collabora.com> CommitDate: Fri Mar 13 14:47:53 2020 +0100 cypress: mobile: improve core detection code. Pushing ESC has an unexpected side-effect on some tests. Change-Id: I147fcc3aaac21e56450eececf60e8cb99b49655f Reviewed-on: https://gerrit.libreoffice.org/c/online/+/90467 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Tamás Zolnai <tamas.zol...@collabora.com> diff --git a/cypress_test/integration_tests/common/helper.js b/cypress_test/integration_tests/common/helper.js index fddafdf22..b9b26fa83 100644 --- a/cypress_test/integration_tests/common/helper.js +++ b/cypress_test/integration_tests/common/helper.js @@ -74,6 +74,9 @@ function detectLOCoreVersion() { .contains('About') .click(); + cy.get('.vex-content') + .should('exist'); + // Get the version cy.get('#lokit-version') .then(function(items) { @@ -86,8 +89,11 @@ function detectLOCoreVersion() { }); // Close about dialog - cy.get('body') - .type('{esc}'); + cy.get('.vex-close') + .click({force : true}); + + cy.get('.vex-content') + .should('not.exist'); } } commit e70975500be80e9d82bc840dc8d92d48875b397e Author: Tor Lillqvist <t...@collabora.com> AuthorDate: Fri Mar 13 13:36:34 2020 +0200 Commit: Tor Lillqvist <t...@collabora.com> CommitDate: Fri Mar 13 13:23:14 2020 +0100 Actually make the copy/paste menu commands work in the iOS app (and Android?) Change-Id: I472a9c7c2a192f068dedbfa2f9409afd3767ba90 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/90462 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Tor Lillqvist <t...@collabora.com> diff --git a/loleaflet/src/control/Control.Menubar.js b/loleaflet/src/control/Control.Menubar.js index b90f9f75b..a66321bbb 100644 --- a/loleaflet/src/control/Control.Menubar.js +++ b/loleaflet/src/control/Control.Menubar.js @@ -1085,7 +1085,7 @@ L.Control.Menubar = L.Control.extend({ }, _executeAction: function(itNode, itWizard) { - var id, data; + var id, mobileappuno, data; if (itNode === undefined) { // called from JSDialogBuilder id = itWizard.id; @@ -1094,6 +1094,7 @@ L.Control.Menubar = L.Control.extend({ else { // called from id = $(itNode).data('id'); + mobileappuno = $(itNode).data('mobileappuno'); data = $(itNode).data; } @@ -1208,8 +1209,8 @@ L.Control.Menubar = L.Control.extend({ self._map.focus(); } }); - } else if (window.ThisIsAMobileApp && data('mobileappuno')) { - this._map.sendUnoCommand(data('mobileappuno')); + } else if (window.ThisIsAMobileApp && mobileappuno) { + this._map.sendUnoCommand(mobileappuno); } // Inform the host if asked if (data('postmessage') === 'true') { commit f657455a8fbd63d7d7faa333fd115378cac4556d Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Wed Mar 11 16:26:12 2020 +0000 Commit: Tor Lillqvist <t...@collabora.com> CommitDate: Fri Mar 13 12:39:14 2020 +0100 android/iOS: do a native copy/paste UNO command on native mobile. As used from the hamburger menu. Change-Id: I3ac3fc2fa7492d5bd3e04e550a282d60b5d56784 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/90341 Reviewed-by: Michael Meeks <michael.me...@collabora.com> Reviewed-by: Tor Lillqvist <t...@collabora.com> Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> diff --git a/loleaflet/src/map/Clipboard.js b/loleaflet/src/map/Clipboard.js index 315fa1e90..2a5056824 100644 --- a/loleaflet/src/map/Clipboard.js +++ b/loleaflet/src/map/Clipboard.js @@ -619,6 +619,11 @@ L.Clipboard = L.Class.extend({ // Pull UNO clipboard commands out from menus and normal user input. // We try to massage and re-emit these, to get good security event / credentials. filterExecCopyPaste: function(cmd) { + if (window.ThisIsAMobileApp) { + // We do native copy/paste in the iOS and Android cases + return false; + } + if (cmd === '.uno:Copy') { this._execCopyCutPaste('copy'); } else if (cmd === '.uno:Cut') { commit 584e707ddb779fc909a98d03ae9488d6c3e39650 Author: Tamás Zolnai <tamas.zol...@collabora.com> AuthorDate: Fri Mar 13 11:23:32 2020 +0100 Commit: Tamás Zolnai <tamas.zol...@collabora.com> CommitDate: Fri Mar 13 12:03:56 2020 +0100 cypress: mobile: reenable a focus related test. This test does not work since: ac961d7b67fcb5fb97cb6a620c913debb74ba8c1 Now I just remove that for cypress test, later we need to improve the mobile emulation. Change-Id: I18310fe27cd727b63b6f0e4f3e939cdbada05520 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/90458 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Tamás Zolnai <tamas.zol...@collabora.com> diff --git a/cypress_test/integration_tests/mobile/writer/focus_spec.js b/cypress_test/integration_tests/mobile/writer/focus_spec.js index 919b25d72..5073315b9 100644 --- a/cypress_test/integration_tests/mobile/writer/focus_spec.js +++ b/cypress_test/integration_tests/mobile/writer/focus_spec.js @@ -201,10 +201,9 @@ describe('Focus tests', function() { .dblclick(posX, posY); }); - // Document still has the focus - // TODO: Focus is inconsistent here. - //cy.document().its('activeElement.className') - // .should('be.eq', 'clipboard'); + // Document grabs the focus + cy.document().its('activeElement.className') + .should('be.eq', 'clipboard'); }); it('Focus with hamburger menu.', function() { diff --git a/loleaflet/src/map/handler/Map.TouchGesture.js b/loleaflet/src/map/handler/Map.TouchGesture.js index 1fc7479c6..99ff04a9f 100644 --- a/loleaflet/src/map/handler/Map.TouchGesture.js +++ b/loleaflet/src/map/handler/Map.TouchGesture.js @@ -313,7 +313,7 @@ L.Map.TouchGesture = L.Handler.extend({ // by the double-tap and triple-tap handlers below. // Note: Hammer has requireFailure() which supressses this call // when multi-taps are detected. This isn't working for us. - if (e.tapCount > 1) + if (e.tapCount > 1 && !L.Browser.cypressTest) return; var point = e.pointers[0], commit 2154306e57d66a4a962f6beb1e917ff7079852c8 Author: Tor Lillqvist <t...@collabora.com> AuthorDate: Fri Mar 13 11:05:23 2020 +0200 Commit: Tor Lillqvist <t...@collabora.com> CommitDate: Fri Mar 13 10:50:36 2020 +0100 Make the iOS (and Android?) app work again Now one can open another document after closing the first one. Turns out that throwing an exception to return through the call stack to SocketPoll::poll() is not necessary after all. But doing document.reset() as before a86508d815013c40e3ae8c494b883e112a7195f2 is essential. Change-Id: I248df78bd9b0d3f0962df2126ca394cb746542b8 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/90456 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Tor Lillqvist <t...@collabora.com> diff --git a/kit/Kit.cpp b/kit/Kit.cpp index 8e2b2ddc5..3a3121b43 100644 --- a/kit/Kit.cpp +++ b/kit/Kit.cpp @@ -2159,9 +2159,7 @@ protected: #else LOG_INF("Setting TerminationFlag due to 'exit' command."); SigUtil::setTerminationFlag(); - - // Not really a logic error, but hey. This is expected to be caught in SocketPoll::poll(). - throw std::logic_error("exit"); + document.reset(); #endif } else if (tokens.equals(0, "tile") || tokens.equals(0, "tilecombine") || tokens.equals(0, "canceltiles") || commit d4e08350431a7662eef22a21b5b0fede226ae0e6 Author: Tor Lillqvist <t...@collabora.com> AuthorDate: Thu Mar 12 22:17:05 2020 +0200 Commit: Tor Lillqvist <t...@collabora.com> CommitDate: Fri Mar 13 00:28:19 2020 +0100 Make closing the document work again in the iOS (and Android?) app But opening a second document now hangs. Sigh, the plumbing in the mobile apps is so extremely fragile. But that is to be expected when turning a multi-process structure (where one class of processes exit as soon as they have done their job) into a single process running forever. Change-Id: I0fdb751f44e16efb42843189969e049bf14816f0 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/90443 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Tor Lillqvist <t...@collabora.com> diff --git a/kit/Kit.cpp b/kit/Kit.cpp index f8c76a06a..8e2b2ddc5 100644 --- a/kit/Kit.cpp +++ b/kit/Kit.cpp @@ -2149,14 +2149,21 @@ protected: LOG_DBG("CreateSession failed."); } } -#if !MOBILEAPP + else if (tokens.equals(0, "exit")) { +#if !MOBILEAPP LOG_INF("Terminating immediately due to parent 'exit' command."); Log::shutdown(); std::_Exit(EX_SOFTWARE); - } +#else + LOG_INF("Setting TerminationFlag due to 'exit' command."); + SigUtil::setTerminationFlag(); + + // Not really a logic error, but hey. This is expected to be caught in SocketPoll::poll(). + throw std::logic_error("exit"); #endif + } else if (tokens.equals(0, "tile") || tokens.equals(0, "tilecombine") || tokens.equals(0, "canceltiles") || tokens.equals(0, "paintwindow") || tokens.equals(0, "resizewindow") || LOOLProtocol::getFirstToken(tokens[0], '-') == "child") diff --git a/net/Socket.hpp b/net/Socket.hpp index 5427b99fa..1e632516e 100644 --- a/net/Socket.hpp +++ b/net/Socket.hpp @@ -673,6 +673,7 @@ public: LOG_ERR("Error while handling poll for socket #" << _pollFds[i].fd << " in " << _name << ": " << exc.what()); disposition.setClosed(); + rc = -1; } if (disposition.isMove() || disposition.isClosed()) diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp index 7096afd4c..68369d274 100644 --- a/wsd/DocumentBroker.hpp +++ b/wsd/DocumentBroker.hpp @@ -108,7 +108,6 @@ public: try { -#if !MOBILEAPP // There is no "child process" in a mobile app LOG_DBG("Closing ChildProcess [" << _pid << "]."); // Request the child to exit @@ -117,7 +116,7 @@ public: LOG_DBG("Stopping ChildProcess [" << _pid << "] by sending 'exit' command."); sendTextFrame("exit"); } -#endif + // Shutdown the socket. if (_ws) _ws->shutdown(); commit 188d00ce4a4b635fc43f653d12e92bd250d7c8a7 Author: Tor Lillqvist <t...@collabora.com> AuthorDate: Thu Mar 12 20:58:03 2020 +0200 Commit: Tor Lillqvist <t...@collabora.com> CommitDate: Thu Mar 12 20:01:59 2020 +0100 Bin code that has always been inside #if 0 Also, the comment in it was misleading. We don't have just a single buffer for a FakeSocket any longer. Change-Id: I8f45fba2342ef42040e467b631739a56664ce6e8 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/90440 Tested-by: Tor Lillqvist <t...@collabora.com> Reviewed-by: Tor Lillqvist <t...@collabora.com> diff --git a/net/Socket.hpp b/net/Socket.hpp index b15aeb21b..5427b99fa 100644 --- a/net/Socket.hpp +++ b/net/Socket.hpp @@ -697,21 +697,6 @@ public: #if !MOBILEAPP rc = ::write(fd, "w", 1); #else -#if 0 - // Our fake sockets are record-oriented with a single record buffer, so as we write one - // byte at a time to the wakeup pipe, we can't write another one before the previous one - // has been read. - // - // FIXME: Still, explicitly waiting here with fakeSocketPoll() for it to be writable - // doesn't seem to be any improvement. On the contrary, with this fakeSocketPoll(), we - // hang every time we run the app. Without it, we only hang occasionally. - - LOG_TRC("Wakeup pipe write"); - struct pollfd p; - p.fd = fd; - p.events = POLLOUT; - fakeSocketPoll(&p, 1, -1); -#endif rc = fakeSocketWrite(fd, "w", 1); #endif } while (rc == -1 && errno == EINTR); commit 60f2e0d709dc823a1c3668e9ed9c2ad6231ffa40 Author: RutuKendre <kendrerutuja...@gmail.com> AuthorDate: Wed Mar 11 15:03:04 2020 +0530 Commit: Jan Holesovsky <ke...@collabora.com> CommitDate: Thu Mar 12 19:27:10 2020 +0100 android: Fix overflow by changing height from 48dp to wrap_content for recent files textview Change-Id: I5a2341cb1f2921d93767fe88e3e7847bc71a443e Reviewed-on: https://gerrit.libreoffice.org/c/online/+/90317 Tested-by: Jan Holesovsky <ke...@collabora.com> Reviewed-by: Jan Holesovsky <ke...@collabora.com> diff --git a/android/app/src/main/res/layout/activity_document_browser.xml b/android/app/src/main/res/layout/activity_document_browser.xml index 44b01b52a..e7ac16548 100644 --- a/android/app/src/main/res/layout/activity_document_browser.xml +++ b/android/app/src/main/res/layout/activity_document_browser.xml @@ -59,7 +59,7 @@ <TextView android:id="@+id/header_recents" android:layout_width="match_parent" - android:layout_height="48dp" + android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center_vertical" android:padding="16dp" commit 5b9bbea3aaeeb756305d1ec1764a975bf235351b Author: Tor Lillqvist <t...@collabora.com> AuthorDate: Thu Mar 12 19:01:39 2020 +0200 Commit: Tor Lillqvist <t...@collabora.com> CommitDate: Thu Mar 12 19:15:56 2020 +0100 Fix assertion failure in the MOBILEAPP case Change-Id: I5c3647d1cc6975bd56b9c5276f6eba7585ac785c Reviewed-on: https://gerrit.libreoffice.org/c/online/+/90432 Tested-by: Tor Lillqvist <t...@collabora.com> Reviewed-by: Tor Lillqvist <t...@collabora.com> diff --git a/net/WebSocketHandler.hpp b/net/WebSocketHandler.hpp index 1c2977602..3a01fea17 100644 --- a/net/WebSocketHandler.hpp +++ b/net/WebSocketHandler.hpp @@ -616,16 +616,21 @@ private: out.insert(out.end(), data, data + len); } const size_t size = out.size() - oldSize; + + if (flush) + socket->writeOutgoingData(); #else LOG_TRC("WebSocketHandle::sendFrame: Writing to #" << socket->getFD() << " " << len << " bytes"); - assert(flush); - assert(out.size() == 0); + // We ignore the flush parameter and always flush in the MOBILEAPP case because there is no + // WebSocket framing, we put the messages as such into the FakeSocket queue. + + (void) flush; out.insert(out.end(), data, data + len); const size_t size = out.size(); + + socket->writeOutgoingData(); #endif - if (flush) - socket->writeOutgoingData(); return size; } commit f49783b7beb9bf02765fcdec458ca5c7153e75cf Author: Tamás Zolnai <tamas.zol...@collabora.com> AuthorDate: Thu Mar 12 19:01:14 2020 +0100 Commit: Tamás Zolnai <tamas.zol...@collabora.com> CommitDate: Thu Mar 12 19:02:32 2020 +0100 cypress: add package.json as a dependency of local npm installation. So packages will be reinstalled, when packages.json changes. Change-Id: I4731b4995bc969a474ea3fcd36e1135ed03ec62c diff --git a/cypress_test/Makefile.am b/cypress_test/Makefile.am index 3750c8a97..fb209bf1f 100644 --- a/cypress_test/Makefile.am +++ b/cypress_test/Makefile.am @@ -211,7 +211,7 @@ NODE_BINS = \ $(NODE_BINS): $(NPM_INSTALLED); -$(NPM_INSTALLED): +$(NPM_INSTALLED): package.json @npm install @mkdir -p $(dir $(NPM_INSTALLED)) @touch $(NPM_INSTALLED) commit 25e3beb65ec0f8399130d0ce89be75e46da577b0 Author: Tamás Zolnai <tamas.zol...@collabora.com> AuthorDate: Thu Mar 12 18:57:21 2020 +0100 Commit: Tamás Zolnai <tamas.zol...@collabora.com> CommitDate: Thu Mar 12 19:02:32 2020 +0100 cypress: fix tests failing with the new cypress version. Change-Id: I0a5b307b7770e0150304329ba136f4ed059c6928 diff --git a/cypress_test/integration_tests/mobile/writer/spellchecking_spec.js b/cypress_test/integration_tests/mobile/writer/spellchecking_spec.js index 595dc63bc..c62349b52 100644 --- a/cypress_test/integration_tests/mobile/writer/spellchecking_spec.js +++ b/cypress_test/integration_tests/mobile/writer/spellchecking_spec.js @@ -81,7 +81,7 @@ describe('Spell checking menu.', function() { // TODO: Why we have a non-breaking space here? cy.get('.context-menu-link') - .contains('Ignore\u00a0All') + .contains('Ignore All') .click(); openContextMenu(); @@ -100,7 +100,7 @@ describe('Spell checking menu.', function() { // English is selected cy.get('.menu-entry-checked') - .contains('English\u00a0(USA)'); + .contains('English (USA)'); }); it('Set None Language for selection.', function() { @@ -112,13 +112,13 @@ describe('Spell checking menu.', function() { // English is selected cy.get('.menu-entry-checked') - .contains('English\u00a0(USA)'); + .contains('English (USA)'); openContextMenu(); // We don't get the spell check context menu any more cy.get('.context-menu-link') - .contains('None\u00a0(Do not check spelling)'); + .contains('None (Do not check spelling)'); }); it('Check language status for paragraph.', function() { @@ -130,7 +130,7 @@ describe('Spell checking menu.', function() { // English is selected cy.get('.menu-entry-checked') - .contains('English\u00a0(USA)'); + .contains('English (USA)'); }); it('Set None Language for paragraph.', function() { @@ -142,12 +142,12 @@ describe('Spell checking menu.', function() { // English is selected cy.get('.menu-entry-checked') - .contains('English\u00a0(USA)'); + .contains('English (USA)'); openContextMenu(); // We don't get the spell check context menu any more cy.get('.context-menu-link') - .contains('None\u00a0(Do not check spelling)'); + .contains('None (Do not check spelling)'); }); }); commit 54c03079ea724b6265b31da3a7557aa7fdcd1e05 Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Thu Mar 12 14:56:17 2020 +0000 Commit: Tamás Zolnai <tamas.zol...@collabora.com> CommitDate: Thu Mar 12 19:01:51 2020 +0100 cypress: update to 4.1.0. This seems to make things work on openSUSE 15.1 Change-Id: I23f6b738d1c1a3737a968f244477bc8c8c5c7bb3 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/90420 Tested-by: Tamás Zolnai <tamas.zol...@collabora.com> Reviewed-by: Tamás Zolnai <tamas.zol...@collabora.com> diff --git a/cypress_test/package.json b/cypress_test/package.json index 07e378814..d43558232 100644 --- a/cypress_test/package.json +++ b/cypress_test/package.json @@ -4,7 +4,7 @@ "description": "Cypress integration test suit", "license": "MPL-2.0", "dependencies": { - "cypress": "3.8.3", + "cypress": "4.1.0", "eslint": "6.8.0", "get-port-cli": "2.0.0", "wait-on": "4.0.0" commit 9e0ec98aaf2ab817fc11ccda770a0982c37a8f2b Author: Michael Meeks <michael.me...@collabora.com> AuthorDate: Thu Mar 12 14:55:56 2020 +0000 Commit: Tamás Zolnai <tamas.zol...@collabora.com> CommitDate: Thu Mar 12 18:51:30 2020 +0100 cypress: rename the user-agent to not include a space. Makes some combinations of chrome etc. happy it seems. Change-Id: Ica8063e42a8719f2ba45de9084ad1fddd6fb58bd Reviewed-on: https://gerrit.libreoffice.org/c/online/+/90419 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Tamás Zolnai <tamas.zol...@collabora.com> diff --git a/cypress_test/Makefile.am b/cypress_test/Makefile.am index 84470784e..3750c8a97 100644 --- a/cypress_test/Makefile.am +++ b/cypress_test/Makefile.am @@ -16,7 +16,7 @@ DESKTOP_DATA_FOLDER = $(abs_srcdir)/data/desktop/ DESKTOP_WORKDIR = $(abs_builddir)/workdir/desktop/ DESKTOP_TRACK_FOLDER=$(abs_builddir)/workdir/track/desktop -MOBILE_USER_AGENT = "cypress mobile" +MOBILE_USER_AGENT = "cypress-mobile" MOBILE_TEST_FOLDER = $(abs_srcdir)/integration_tests/mobile MOBILE_DATA_FOLDER = $(abs_srcdir)/data/mobile/ MOBILE_WORKDIR = $(abs_builddir)/workdir/mobile/ commit 8c23b7a5112de8972d582d2388e61983651d03ae Author: Tamás Zolnai <tamas.zol...@collabora.com> AuthorDate: Thu Mar 12 17:50:32 2020 +0100 Commit: Tamás Zolnai <tamas.zol...@collabora.com> CommitDate: Thu Mar 12 18:19:26 2020 +0100 cypress: mobile: make shape related tests more stable. It seems the default client viewport is not alway the same. Selecting a long text and then go to the start of the paragraph will force to have the same view before testing shape insertion. Change-Id: I590c69f02faea1b45b087ee47f04b55bf3b08312 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/90431 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Tamás Zolnai <tamas.zol...@collabora.com> diff --git a/cypress_test/data/mobile/writer/shape_properties.odt b/cypress_test/data/mobile/writer/shape_properties.odt index 550752605..0c81da669 100644 Binary files a/cypress_test/data/mobile/writer/shape_properties.odt and b/cypress_test/data/mobile/writer/shape_properties.odt differ diff --git a/cypress_test/integration_tests/mobile/writer/shape_properties_spec.js b/cypress_test/integration_tests/mobile/writer/shape_properties_spec.js index a9e9994c3..21f666017 100644 --- a/cypress_test/integration_tests/mobile/writer/shape_properties_spec.js +++ b/cypress_test/integration_tests/mobile/writer/shape_properties_spec.js @@ -12,6 +12,14 @@ describe('Change shape properties via mobile wizard.', function() { // Click on edit button cy.get('#mobile-edit-button').click(); + writerHelper.selectAllMobile(); + + cy.get('#document-container') + .type('{home}'); + + cy.get('.blinking-cursor') + .should('be.visible'); + // Open insertion wizard cy.get('#tb_actionbar_item_insertion_mobile_wizard') .click(); commit 96b81b18d07787bea1ece9f1aa0062347f3eb0ee Author: Tamás Zolnai <tamas.zol...@collabora.com> AuthorDate: Thu Mar 12 16:36:32 2020 +0100 Commit: Tamás Zolnai <tamas.zol...@collabora.com> CommitDate: Thu Mar 12 17:47:50 2020 +0100 cypress: mobile: update shape related tests. Default client view was changed. Change-Id: Ie8506b133fba43bcddff238fc0cefc31911e3dee Reviewed-on: https://gerrit.libreoffice.org/c/online/+/90426 Tested-by: Tamás Zolnai <tamas.zol...@collabora.com> Reviewed-by: Tamás Zolnai <tamas.zol...@collabora.com> diff --git a/cypress_test/integration_tests/mobile/writer/shape_properties_spec.js b/cypress_test/integration_tests/mobile/writer/shape_properties_spec.js index 84dfdae51..a9e9994c3 100644 --- a/cypress_test/integration_tests/mobile/writer/shape_properties_spec.js +++ b/cypress_test/integration_tests/mobile/writer/shape_properties_spec.js @@ -4,7 +4,7 @@ var helper = require('../../common/helper'); var writerHelper = require('./writer_helper'); describe('Change shape properties via mobile wizard.', function() { - var defaultGeometry = 'M 1965,4810 L 7957,10802 1965,10802 1965,4810 1965,4810 Z'; + var defaultGeometry = 'M 1965,4863 L 7957,10855 1965,10855 1965,4863 1965,4863 Z'; beforeEach(function() { helper.beforeAllMobile('shape_properties.odt', 'writer'); @@ -106,7 +106,7 @@ describe('Change shape properties via mobile wizard.', function() { .should('have.attr', 'd', defaultGeometry); cy.get('.leaflet-pane.leaflet-overlay-pane svg g svg g g g path') - .should('have.attr', 'd', 'M 1965,4810 L 12635,10802 1965,10802 1965,4810 1965,4810 Z'); + .should('have.attr', 'd', 'M 1965,4863 L 12635,10855 1965,10855 1965,4863 1965,4863 Z'); }); it('Change shape height.', function() { @@ -125,7 +125,7 @@ describe('Change shape properties via mobile wizard.', function() { .should('not.have.attr', 'd', defaultGeometry); cy.get('.leaflet-pane.leaflet-overlay-pane svg g svg g g g path') - .should('have.attr', 'd', 'M 1965,4810 L 7957,18020 1965,18020 1965,4810 1965,4810 Z'); + .should('have.attr', 'd', 'M 1965,4863 L 7957,18073 1965,18073 1965,4863 1965,4863 Z'); }); it('Change size with keep ratio enabled.', function() { @@ -152,7 +152,7 @@ describe('Change shape properties via mobile wizard.', function() { .should('not.have.attr', 'd', defaultGeometry); cy.get('.leaflet-pane.leaflet-overlay-pane svg g svg g g g path') - .should('have.attr', 'd', 'M 1965,4810 L 15175,18020 1965,18020 1965,4810 1965,4810 Z'); + .should('have.attr', 'd', 'M 1965,4863 L 15175,18073 1965,18073 1965,4863 1965,4863 Z'); }); it('Vertical mirroring', function() { @@ -165,7 +165,7 @@ describe('Change shape properties via mobile wizard.', function() { .should('not.have.attr', 'd', defaultGeometry); cy.get('.leaflet-pane.leaflet-overlay-pane svg g svg g g g path') - .should('have.attr', 'd', 'M 1965,10800 L 7957,4808 1965,4808 1965,10800 1965,10800 Z'); + .should('have.attr', 'd', 'M 1965,10853 L 7957,4861 1965,4861 1965,10853 1965,10853 Z'); }); it('Horizontal mirroring', function() { @@ -180,7 +180,7 @@ describe('Change shape properties via mobile wizard.', function() { .should('not.have.attr', 'd', defaultGeometry); cy.get('.leaflet-pane.leaflet-overlay-pane svg g svg g g g path') - .should('have.attr', 'd', 'M 8010,4810 L 1963,10802 8010,10802 8010,4810 8010,4810 Z'); + .should('have.attr', 'd', 'M 8010,4863 L 1963,10855 8010,10855 8010,4863 8010,4863 Z'); }); it('Trigger moving backward / forward', function() { commit 19a7f1d288f4ff57129078c339c09a78c0b2c2f4 Author: Tamás Zolnai <tamas.zol...@collabora.com> AuthorDate: Thu Mar 12 16:59:14 2020 +0100 Commit: Tamás Zolnai <tamas.zol...@collabora.com> CommitDate: Thu Mar 12 17:47:35 2020 +0100 test: better fix for UnitWOPISaveAs test Don't check the full URL, but just test some parts of it. Change-Id: I5367bf4f41dc26f311e03de7ce06349f744d0b85 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/90428 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Tamás Zolnai <tamas.zol...@collabora.com> diff --git a/test/UnitWOPISaveAs.cpp b/test/UnitWOPISaveAs.cpp index 2831aa543..46f5a2309 100644 --- a/test/UnitWOPISaveAs.cpp +++ b/test/UnitWOPISaveAs.cpp @@ -45,20 +45,14 @@ public: { const std::string message(data, len); - const std::string expected( - "saveas: url=" + helpers::getTestServerURI() - + "/something%20wopi/files/1?access_token=anything&reuse_cookies=cook=well " - "filename=hello%20world%251.pdf xfilename=hello world%1.pdf"); - if (message.find(expected) == 0) + if (message.find("saveas: url=") != std::string::npos && + message.find(helpers::getTestServerURI()) != std::string::npos && + message.find("filename=hello%20world%251.pdf") != std::string::npos) { // successfully exit the test if we also got the outgoing message // notifying about saving the file exitTest(TestResult::Ok); } - else - { - LOG_DBG("UnitWOPISaveAs: Skipping unexpected SendMessage: " << message); - } return false; } commit 47d22d538ac5c09df6aa098922f8750cb939102b Author: Tor Lillqvist <t...@collabora.com> AuthorDate: Thu Mar 12 15:13:28 2020 +0200 Commit: Tor Lillqvist <t...@collabora.com> CommitDate: Thu Mar 12 17:08:19 2020 +0100 Fix build for MOBILEAPP Change-Id: I0903f13438493d167c263020d923536b4de71e5d Reviewed-on: https://gerrit.libreoffice.org/c/online/+/90385 Tested-by: Tor Lillqvist <t...@collabora.com> Reviewed-by: Tor Lillqvist <t...@collabora.com> diff --git a/kit/Kit.cpp b/kit/Kit.cpp index f14362c35..f8c76a06a 100644 --- a/kit/Kit.cpp +++ b/kit/Kit.cpp @@ -2149,12 +2149,14 @@ protected: LOG_DBG("CreateSession failed."); } } +#if !MOBILEAPP else if (tokens.equals(0, "exit")) { LOG_INF("Terminating immediately due to parent 'exit' command."); Log::shutdown(); std::_Exit(EX_SOFTWARE); } +#endif else if (tokens.equals(0, "tile") || tokens.equals(0, "tilecombine") || tokens.equals(0, "canceltiles") || tokens.equals(0, "paintwindow") || tokens.equals(0, "resizewindow") || LOOLProtocol::getFirstToken(tokens[0], '-') == "child") diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp index 68369d274..7096afd4c 100644 --- a/wsd/DocumentBroker.hpp +++ b/wsd/DocumentBroker.hpp @@ -108,6 +108,7 @@ public: try { +#if !MOBILEAPP // There is no "child process" in a mobile app LOG_DBG("Closing ChildProcess [" << _pid << "]."); // Request the child to exit @@ -116,7 +117,7 @@ public: LOG_DBG("Stopping ChildProcess [" << _pid << "] by sending 'exit' command."); sendTextFrame("exit"); } - +#endif // Shutdown the socket. if (_ws) _ws->shutdown(); commit 57cca53b6a5bafad841c16cfcf078a1846f12ecb Author: Pranam Lashkari <lpra...@collabora.com> AuthorDate: Fri Feb 28 13:49:30 2020 +0530 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Thu Mar 12 16:21:21 2020 +0100 tdf#130594: leaflet: Search bar issues pressing Ctrl-F opens browser search resolved pressing 'r' behaves weird resolved Change-Id: I9c54f37267a2f34b6d0923b46dc72a6efcf04786 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/89661 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Andras Timar <andras.ti...@collabora.com> diff --git a/loleaflet/src/control/Control.Toolbar.js b/loleaflet/src/control/Control.Toolbar.js index 54967a7bd..a26a63304 100644 --- a/loleaflet/src/control/Control.Toolbar.js +++ b/loleaflet/src/control/Control.Toolbar.js @@ -1244,7 +1244,7 @@ function initNormalToolbar() { function setupSearchInput() { $('#search-input').off('input', onSearchInput).on('input', onSearchInput); - $('#search-input').off('keypress', onSearchKeyPress).on('keypress', onSearchKeyPress); + $('#search-input').off('keydown', onSearchKeyDown).on('keydown', onSearchKeyDown); $('#search-input').off('focus', onSearchFocus).on('focus', onSearchFocus); $('#search-input').off('blur', onSearchBlur).on('blur', onSearchBlur); } @@ -1355,7 +1355,7 @@ function onSearchInput() { } } -function onSearchKeyPress(e) { +function onSearchKeyDown(e) { var entry = L.DomUtil.get('search-input'); if ((e.keyCode === 71 && e.ctrlKey) || e.keyCode === 114 || e.keyCode === 13) { if (e.shiftKey) { commit 316a011c08ddf1c7dd6a329f04c238bd2c040eb5 Author: Henry Castro <hcas...@collabora.com> AuthorDate: Wed Mar 11 11:55:49 2020 -0400 Commit: Henry Castro <hcas...@collabora.com> CommitDate: Thu Mar 12 13:33:19 2020 +0100 kit: fix download action when server is running with no capabilities Downloading pdf file does not work because the kit process is not jailed Change-Id: I1e67840eb58997f6de10948c8d8e260888abe326 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/90338 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Henry Castro <hcas...@collabora.com> diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp index 4842b8ffe..e5a76f060 100644 --- a/kit/ChildSession.cpp +++ b/kit/ChildSession.cpp @@ -49,6 +49,8 @@ using Poco::URI; using namespace LOOLProtocol; +bool ChildSession::NoCapsForKit = false; + namespace { std::vector<unsigned char> decodeBase64(const std::string & inputBase64) @@ -926,14 +928,22 @@ bool ChildSession::downloadAs(const char* /*buffer*/, int /*length*/, const Stri format.empty() ? nullptr : format.c_str(), filterOptions.empty() ? nullptr : filterOptions.c_str()); #else - // The file is removed upon downloading. - const std::string tmpDir = FileUtil::createRandomDir(JAILED_DOCUMENT_ROOT); // Prevent user inputting anything funny here. // A "name" should always be a name, not a path const Poco::Path filenameParam(name); - const std::string url = JAILED_DOCUMENT_ROOT + tmpDir + "/" + filenameParam.getFileName(); const std::string nameAnonym = anonymizeUrl(name); - const std::string urlAnonym = JAILED_DOCUMENT_ROOT + tmpDir + "/" + Poco::Path(nameAnonym).getFileName(); + + std::string jailDoc = JAILED_DOCUMENT_ROOT; + if (NoCapsForKit) + { + jailDoc = Poco::URI(getJailedFilePath()).getPath(); + jailDoc = jailDoc.substr(0, jailDoc.find(JAILED_DOCUMENT_ROOT)) + JAILED_DOCUMENT_ROOT; + } + + // The file is removed upon downloading. + const std::string tmpDir = FileUtil::createRandomDir(jailDoc); + const std::string url = jailDoc + tmpDir + "/" + filenameParam.getFileName(); + const std::string urlAnonym = jailDoc + tmpDir + "/" + Poco::Path(nameAnonym).getFileName(); LOG_DBG("Calling LOK's downloadAs with: url='" << urlAnonym << "', format='" << (format.empty() ? "(nullptr)" : format.c_str()) << "', ' filterOptions=" << diff --git a/kit/ChildSession.hpp b/kit/ChildSession.hpp index c7a248546..da44a5f94 100644 --- a/kit/ChildSession.hpp +++ b/kit/ChildSession.hpp @@ -192,6 +192,8 @@ public: class ChildSession final : public Session { public: + static bool NoCapsForKit; + /// Create a new ChildSession /// ws The socket between master and kit (jailed). /// loKit The LOKit instance. diff --git a/kit/Kit.cpp b/kit/Kit.cpp index 8b8e6514d..f14362c35 100644 --- a/kit/Kit.cpp +++ b/kit/Kit.cpp @@ -2402,7 +2402,7 @@ void lokit_main( // So we insure it lives until std::_Exit is called. std::shared_ptr<lok::Office> loKit; Path jailPath; - bool bRunInsideJail = !noCapabilities; + ChildSession::NoCapsForKit = noCapabilities; #else AnonymizeUserData = false; #endif // MOBILEAPP @@ -2415,7 +2415,7 @@ void lokit_main( File(jailPath).createDirectories(); chmod(jailPath.toString().c_str(), S_IXUSR | S_IWUSR | S_IRUSR); - if (bRunInsideJail) + if (!ChildSession::NoCapsForKit) { userdir_url = "file:///user"; instdir_path = "/" + loSubPath + "/program"; commit 82558288198e87e0c6f7fe41c6a3fa470ab82f97 Author: Pedro Pinto Silva <pedro.si...@collabora.com> AuthorDate: Tue Mar 10 12:55:47 2020 +0100 Commit: Pedro Pinto da Silva <pedro.si...@collabora.com> CommitDate: Thu Mar 12 13:31:54 2020 +0100 Mobile: Fix header-left alignments on mobilewizard and fix document container vertical(readonly and edit mode) position to respect the height of toolbar-up as well as the main-nav element; Set 0 vertical paddings for the toolbar-up. Change-Id: I4951b47a5c8b4fb34285c45b2ef9765245068b4a Reviewed-on: https://gerrit.libreoffice.org/c/online/+/90253 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Tested-by: Pedro Pinto da Silva <pedro.si...@collabora.com> Reviewed-by: Pedro Pinto da Silva <pedro.si...@collabora.com> diff --git a/loleaflet/css/loleaflet.css b/loleaflet/css/loleaflet.css index 1deeb8ad2..1691794d1 100644 --- a/loleaflet/css/loleaflet.css +++ b/loleaflet/css/loleaflet.css @@ -244,7 +244,7 @@ body { } #document-container.readonly { - top: 41px; + top: 37px; /*set equal to toolbar up's height*/ bottom: 0px; } @@ -268,7 +268,7 @@ body { } #document-container { - top: 41px; + top: 37px; /*set equal to toolbar up's height*/ right: 0px !important; bottom: 35px; } diff --git a/loleaflet/css/menubar.css b/loleaflet/css/menubar.css index 6233eccad..f25fb9cb9 100644 --- a/loleaflet/css/menubar.css +++ b/loleaflet/css/menubar.css @@ -283,7 +283,7 @@ position: absolute; height: 0; width: 100%; - top: 41px; + top: 37px; /*set equal to toolbar up's height*/ margin: 0; -webkit-overflow-scrolling: touch; overflow: scroll; diff --git a/loleaflet/css/mobilewizard.css b/loleaflet/css/mobilewizard.css index 452e351b5..00a109811 100644 --- a/loleaflet/css/mobilewizard.css +++ b/loleaflet/css/mobilewizard.css @@ -412,7 +412,7 @@ margin-right: 4%; } .ui-header-left { - margin-left: 2%; + margin-left: 4%; min-width: 42% !important; } .ui-header.level-1.mobile-wizard.ui-widget .ui-header-left span.menu-entry-with-icon { diff --git a/loleaflet/css/toolbar-mobile.css b/loleaflet/css/toolbar-mobile.css index 28c35c0ea..a5bbbafcd 100644 --- a/loleaflet/css/toolbar-mobile.css +++ b/loleaflet/css/toolbar-mobile.css @@ -20,6 +20,10 @@ height: 100%; overflow-y: scroll; } + #toolbar-up.w2ui-toolbar{ + padding-top: 0px; + padding-bottom: 0px; + } #formulabar { padding: 0px !important; border-top: 1px solid #bbbbbb !important; commit 3960af806402469d77048a4967b4345a46361a68 Author: Pedro Pinto Silva <pedro.si...@collabora.com> AuthorDate: Mon Mar 9 10:51:43 2020 +0100 Commit: Pedro Pinto da Silva <pedro.si...@collabora.com> CommitDate: Thu Mar 12 13:31:31 2020 +0100 Impress: Slide Sorter parent (wrapper): Fix vertical position when using a tablet Change-Id: I89b6d3257749da23f52d848777ff55753281b15b Reviewed-on: https://gerrit.libreoffice.org/c/online/+/90205 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Tested-by: Pedro Pinto da Silva <pedro.si...@collabora.com> Reviewed-by: Pedro Pinto da Silva <pedro.si...@collabora.com> diff --git a/loleaflet/src/control/Control.Toolbar.js b/loleaflet/src/control/Control.Toolbar.js index 7ba2ba800..54967a7bd 100644 --- a/loleaflet/src/control/Control.Toolbar.js +++ b/loleaflet/src/control/Control.Toolbar.js @@ -1782,7 +1782,7 @@ function onDocLayerInit() { obj.addClass('w2ui-icon unfold'); $(map.options.documentContainer).addClass('tablet'); $('#spreadsheet-row-column-frame').addClass('tablet'); - $('#presentation-controls-wrapper').css({'top': '41px'}); + $('#presentation-controls-wrapper').css({'top': '36px'}); $('#tb_editbar_item_fonts').css({'display': 'none'}); $('#tb_editbar_item_fontsizes').css({'display': 'none'}); commit b906c6985ab0c9478699a409eb4974cfb1e30d2e Author: Pedro Pinto Silva <pedro.si...@collabora.com> AuthorDate: Fri Mar 6 10:24:50 2020 +0100 Commit: Pedro Pinto da Silva <pedro.si...@collabora.com> CommitDate: Thu Mar 12 13:31:11 2020 +0100 Desktop: Calc: formular bar: Decrease size of selection handles and adjust position Change-Id: Ibe9743fe7fe476c8d561b301ca82faea89dbccc6 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/90078 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Tested-by: Pedro Pinto da Silva <pedro.si...@collabora.com> Reviewed-by: Pedro Pinto da Silva <pedro.si...@collabora.com> diff --git a/loleaflet/css/selectionMarkers.css b/loleaflet/css/selectionMarkers.css index a0dd4ccde..9eecb6b25 100644 --- a/loleaflet/css/selectionMarkers.css +++ b/loleaflet/css/selectionMarkers.css @@ -4,7 +4,7 @@ width: 30px; height: 44px; background-image: url('images/handle_start.svg'); - } +} .leaflet-selection-marker-end { position: absolute; @@ -23,8 +23,18 @@ .inputbar_selection_handles * { z-index: 1; } -#tb_formulabar_item_formula .inputbar_selection_handles{ - /*display: none;*/ +@media (min-width: 1024px) and (pointer: fine) { + #tb_formulabar_item_formula .inputbar_selection_handles{ + top:-12px; + } + #tb_formulabar_item_formula .inputbar_selection_handles *{ + background-size: 20px; + background-repeat: no-repeat; + background-position-x: 3px; + } + #tb_formulabar_item_formula .inputbar_selection_handles .leaflet-selection-marker-start{ + background-position-x: 8px; + } } @media (max-width: 767px), (max-device-height: 767px) and (pointer: coarse) { #tb_formulabar_item_formula, #tb_formulabar_item_address { commit cb76d01001a88545a956c1642f0dc4c7c4a69634 Author: Ashod Nakashian <ashod.nakash...@collabora.co.uk> AuthorDate: Sat Feb 29 22:08:04 2020 -0500 Commit: Ashod Nakashian <ashnak...@gmail.com> CommitDate: Thu Mar 12 13:28:04 2020 +0100 leaflet: resize the document after show/hide toolbar The toolbar changes the document size, so we should resize at the time of showing/hiding the toolbar. Failing to do this means the document is resized at the point of editing it, which causes a visual shift as the contents recenters. This avoids it by making sure the document dimensions are up-to-date at the time the toolbar shown/hidden. Change-Id: I728d13e1acac4b2dd118332a112d4b89b4d798b1 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/90359 Tested-by: Ashod Nakashian <ashnak...@gmail.com> Reviewed-by: Ashod Nakashian <ashnak...@gmail.com> diff --git a/loleaflet/src/control/Control.Toolbar.js b/loleaflet/src/control/Control.Toolbar.js index 5bd13e147..7ba2ba800 100644 --- a/loleaflet/src/control/Control.Toolbar.js +++ b/loleaflet/src/control/Control.Toolbar.js @@ -2399,6 +2399,9 @@ function onUpdatePermission(e) { $('#toolbar-down').hide(); } } + + // We've resized the document container. + map.invalidateSize(); } function onUseritemClicked(e) { // eslint-disable-line no-unused-vars commit a0cdafca41f41638214fc0f59006bd7f1e4e67f5 Author: Ashod Nakashian <ashod.nakash...@collabora.co.uk> AuthorDate: Sun Mar 8 14:33:47 2020 -0400 Commit: Ashod Nakashian <ashnak...@gmail.com> CommitDate: Thu Mar 12 13:23:57 2020 +0100 wsd: some logging improvements Change-Id: Icf4c4845e10f44fe1518e58ea598c2d1053b40c1 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/90357 Tested-by: Ashod Nakashian <ashnak...@gmail.com> Reviewed-by: Ashod Nakashian <ashnak...@gmail.com> diff --git a/kit/Kit.cpp b/kit/Kit.cpp index 496514e46..8b8e6514d 100644 --- a/kit/Kit.cpp +++ b/kit/Kit.cpp @@ -1869,7 +1869,7 @@ public: const TileQueue::Payload input = _tileQueue->pop(); - LOG_TRC("Kit Recv " << LOOLProtocol::getAbbreviatedMessage(input)); + LOG_TRC("Kit handling queue message: " << LOOLProtocol::getAbbreviatedMessage(input)); const StringVector tokens = LOOLProtocol::tokenize(input.data(), input.size()); diff --git a/net/Socket.hpp b/net/Socket.hpp index 99fdf259a..b15aeb21b 100644 --- a/net/Socket.hpp +++ b/net/Socket.hpp @@ -588,8 +588,8 @@ public: int rc; do { - LOG_TRC("Poll start"); #if !MOBILEAPP + LOG_TRC("Poll start, timeoutMs: " << timeoutMaxMs); rc = ::poll(&_pollFds[0], size + 1, std::max(timeoutMaxMs,0)); #else LOG_TRC("SocketPoll Poll"); @@ -1106,12 +1106,14 @@ protected: // Always try to read. closed = !readIncomingData() || closed; + LOG_TRC("#" << getFD() << ": Incoming data buffer " << _inBuffer.size() << + " bytes, closeSocket? " << closed); + +#ifdef LOG_SOCKET_DATA auto& log = Log::logger(); - if (log.trace()) { - LOG_TRC("#" << getFD() << ": Incoming data buffer " << _inBuffer.size() << - " bytes, closeSocket? " << closed); - // log.dump("", &_inBuffer[0], _inBuffer.size()); - } + if (log.trace() && _inBuffer.size() > 0) + log.dump("", &_inBuffer[0], _inBuffer.size()); +#endif // If we have data, allow the app to consume. size_t oldSize = 0; @@ -1174,12 +1176,14 @@ public: len = writeData(&_outBuffer[0], std::min((int)_outBuffer.size(), getSendBufferSize())); + LOG_TRC("#" << getFD() << ": Wrote outgoing data " << len << " bytes of " + << _outBuffer.size() << " bytes buffered."); + +#ifdef LOG_SOCKET_DATA auto& log = Log::logger(); - if (log.trace() && len > 0) { - LOG_TRC("#" << getFD() << ": Wrote outgoing data " << len << - " bytes of " << _outBuffer.size() << " bytes buffered."); - // log.dump("", &_outBuffer[0], len); - } + if (log.trace() && len > 0) + log.dump("", &_outBuffer[0], len); +#endif if (len <= 0 && errno != EAGAIN && errno != EWOULDBLOCK) LOG_SYS("#" << getFD() << ": Socket write returned " << len); diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp index 2a59b961c..a06687788 100644 --- a/wsd/LOOLWSD.cpp +++ b/wsd/LOOLWSD.cpp @@ -3654,12 +3654,11 @@ int LOOLWSD::main(const std::vector<std::string>& /*args*/) UnitWSD::get().returnValue(returnValue); - LOG_INF("Process [loolwsd] finished."); - #if MOBILEAPP fakeSocketDumpState(); #endif + LOG_INF("Process [loolwsd] finished."); return returnValue; } commit a86508d815013c40e3ae8c494b883e112a7195f2 Author: Ashod Nakashian <ashod.nakash...@collabora.co.uk> AuthorDate: Sun Mar 8 14:27:02 2020 -0400 Commit: Ashod Nakashian <ashnak...@gmail.com> CommitDate: Thu Mar 12 13:20:47 2020 +0100 wsd: kit now exits immediately when wsd send exit The 'exit' command from DocBroker to Kit is processed immediately. It set the termination flag and destroyed the Document instance in Kit. Unfortunately, this is highly problematic. There are a number of races here. Normal disconnection procedure from DocBroker is to first send 'disconnect'. This tells Kit to unload and close the view in question, and when 0 views are left, it simply exits the process. However, since 'disconnect' is processed in the message queue, the 'exit' (and indeed the socket disconnection that follows 'exit') may be handled before the message queue is drained, and so the 'disconnect' wouldn't have been processed yet. The end result of these two races is that Kit tries to exit the Core main loop, which fails a number of assertion (f.e. that no LOK Windows remain, which is presumably a leak check, which we care not about as we don't attempt to cleanup the sidebar). The fix here is to process 'exit' immediately, because DocBroker is gone, there is absolutely nothing for Kit to do anymore. Change-Id: I5c09fcfdb1713f4e0b56b717c747d919d0c6728f Reviewed-on: https://gerrit.libreoffice.org/c/online/+/90356 Reviewed-by: Michael Meeks <michael.me...@collabora.com> Tested-by: Ashod Nakashian <ashnak...@gmail.com> diff --git a/kit/Kit.cpp b/kit/Kit.cpp index 54ad81647..496514e46 100644 --- a/kit/Kit.cpp +++ b/kit/Kit.cpp @@ -2151,9 +2151,9 @@ protected: } else if (tokens.equals(0, "exit")) { - LOG_INF("Setting TerminationFlag due to 'exit' command from parent."); - SigUtil::setTerminationFlag(); - document.reset(); + LOG_INF("Terminating immediately due to parent 'exit' command."); + Log::shutdown(); + std::_Exit(EX_SOFTWARE); } else if (tokens.equals(0, "tile") || tokens.equals(0, "tilecombine") || tokens.equals(0, "canceltiles") || tokens.equals(0, "paintwindow") || tokens.equals(0, "resizewindow") || commit d8a9e11ad9644f447f6daa6d8905c31196889b87 Author: Ashod Nakashian <ashod.nakash...@collabora.co.uk> AuthorDate: Sun Mar 8 14:26:07 2020 -0400 Commit: Ashod Nakashian <ashnak...@gmail.com> CommitDate: Thu Mar 12 13:19:38 2020 +0100 wsd: fix UnitWOPISaveAs test Change-Id: I63da82da727e2902fdb35ff31b29df8c77976793 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/90355 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Ashod Nakashian <ashnak...@gmail.com> diff --git a/test/UnitWOPISaveAs.cpp b/test/UnitWOPISaveAs.cpp index ad79f4b59..2831aa543 100644 --- a/test/UnitWOPISaveAs.cpp +++ b/test/UnitWOPISaveAs.cpp @@ -45,13 +45,20 @@ public: { const std::string message(data, len); - const std::string expected("saveas: url=" + helpers::getTestServerURI() + "/something%20wopi/files/1?access_token=anything filename=hello%20world%251.pdf"); + const std::string expected( + "saveas: url=" + helpers::getTestServerURI() + + "/something%20wopi/files/1?access_token=anything&reuse_cookies=cook=well " + "filename=hello%20world%251.pdf xfilename=hello world%1.pdf"); if (message.find(expected) == 0) { // successfully exit the test if we also got the outgoing message // notifying about saving the file exitTest(TestResult::Ok); } + else + { + LOG_DBG("UnitWOPISaveAs: Skipping unexpected SendMessage: " << message); + } return false; } commit 6caa6ab7babee1af668f547a8b0cb7e1d54972a9 Author: Weblate <nore...@documentfoundation.org> AuthorDate: Thu Mar 12 12:37:28 2020 +0100 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Thu Mar 12 13:07:18 2020 +0100 update translations LibreOffice Online/loleaflet-ui (Finnish) Currently translated at 100.0% (288 of 288 strings) Change-Id: If57a91eb54a078857ffd3418adb28db81c5cd6e8 update translations LibreOffice Online/loleaflet-ui (Finnish) Currently translated at 100.0% (288 of 288 strings) Change-Id: I8577890c535e49f6b07d58c9d01cff02fd3d739c update translations LibreOffice Online/android-app (Ukrainian) Currently translated at 100.0% (99 of 99 strings) Change-Id: I8b98f6a52d2d456ad69b0ce5ad8487b01fa9878b update translations LibreOffice Online/android-app (Ukrainian) Currently translated at 71.7% (71 of 99 strings) Change-Id: Ieca33d499f44bf777d17aebcfe4c53935d707f57 update translations LibreOffice Online/android-app (Ukrainian) Currently translated at 68.7% (68 of 99 strings) Change-Id: Ia8f3de3a2bb2f2fa7fef270bd953ba83e79778b3 update translations LibreOffice Online/android-app (Ukrainian) Currently translated at 66.7% (66 of 99 strings) Change-Id: If6ace740b28b4684eb1608c3a6af05f3b2fc3008 update translations LibreOffice Online/loleaflet-help (Croatian) Currently translated at 63.7% (265 of 416 strings) Change-Id: I560d03be7e77ad5c31b91cfe30262980e53506f5 update translations LibreOffice Online/loleaflet-ui (Lao) Currently translated at 67.7% (195 of 288 strings) Change-Id: Ic2b68543e67a7ef65880f6963b52bf12121a9d8b update translations LibreOffice Online/loleaflet-ui (Lao) Currently translated at 63.9% (184 of 288 strings) Change-Id: I3198618a09b5dc6f2596fedf9516c19e6860d0ac update translations LibreOffice Online/android-app (Croatian) Currently translated at 100.0% (99 of 99 strings) Change-Id: Ia3a730199bb988896670daff6df30fc39dc99674 update translations LibreOffice Online/loleaflet-help (Bulgarian) Currently translated at 100.0% (416 of 416 strings) Change-Id: Ie6535eff3b2e80e429863838aea0d98c4fdecfb8 update translations LibreOffice Online/loleaflet-ui (Lao) Currently translated at 41.3% (119 of 288 strings) Change-Id: Iabaa7b0fd5845cf2f287c02643ce9d740b241cfb update translations LibreOffice Online/loleaflet-ui (Croatian) Currently translated at 100.0% (288 of 288 strings) Change-Id: I93921c47c99de2846318b68625c6469995bf66af update translations LibreOffice Online/loleaflet-help (Croatian) Currently translated at 62.3% (259 of 416 strings) Change-Id: I08c9843389a8f53ca6e520efad5964b66bbd3c64 update translations LibreOffice Online/loleaflet-help (Bulgarian) Currently translated at 91.1% (379 of 416 strings) Change-Id: Ie14fc630867acb34aff0bed2c0d4d6a9ad00d963 update translations LibreOffice Online/android-lib (Croatian) Currently translated at 100.0% (8 of 8 strings) Change-Id: I585a82d4db0294f97dc901015a2867684524613c update translations LibreOffice Online/loleaflet-help (Catalan) Currently translated at 71.9% (299 of 416 strings) Change-Id: Ie086e28a50e544d5db53a85977e7afb61820c3e3 update translations LibreOffice Online/loleaflet-help (Bulgarian) Currently translated at 90.9% (378 of 416 strings) Change-Id: Ia31337605c40b10086c987ec9139dc526eca0bc1 update translations LibreOffice Online/loleaflet-ui (Croatian) Currently translated at 79.5% (229 of 288 strings) Change-Id: I31c01812311c7ae26f181e6a38aa6b88b9ceecee update translations LibreOffice Online/android-app (Aragonese) Currently translated at 9.1% (9 of 99 strings) Change-Id: Ib22a413b55e0c4a109cb208b652eefd1963c6394 update translations LibreOffice Online/loleaflet-ui (Aragonese) Currently translated at 1.4% (4 of 288 strings) Change-Id: I5bc681a8028d166d71236fa5893962c4c94658af update translations LibreOffice Online/android-app (Aragonese) Currently translated at 8.1% (8 of 99 strings) Change-Id: Ieb5720fda9d01c59c1bce7f4a5fb0761d4e71b4e update translations LibreOffice Online/loleaflet-help (Bulgarian) Currently translated at 78.4% (326 of 416 strings) Change-Id: If34333b865321a6f5eaa98006977bb2f5ce0d70c update translations LibreOffice Online/loleaflet-ui (Aragonese) Currently translated at 1.0% (3 of 288 strings) Change-Id: I012650c8b702c1fca7c082bbd0cbcd8661feeb61 update translations LibreOffice Online/loleaflet-help (Bulgarian) Currently translated at 72.6% (302 of 416 strings) Change-Id: I4e18dccf7ddd558dd85a604fd53e6552728ef12f update translations LibreOffice Online/loleaflet-help (Czech) Currently translated at 100.0% (416 of 416 strings) Change-Id: I68b3470c0697558c9c878bc4159a33c1fa7e546e update translations LibreOffice Online/loleaflet-help (Bulgarian) Currently translated at 72.4% (301 of 416 strings) Change-Id: I074b9176f774aecf103132de2f92ee6d7bf93edb update translations LibreOffice Online/android-app (English (United Kingdom)) Currently translated at 100.0% (99 of 99 strings) Change-Id: Ia6ecca86c204206e412c0b6358f633f07153efef update translations LibreOffice Online/android-app (Korean) Currently translated at 4.0% (4 of 99 strings) Change-Id: I22ada69379bdb5a4dbe88d7696ccf15fd097b284 update translations LibreOffice Online/android-app (Spanish) Currently translated at 100.0% (99 of 99 strings) Change-Id: Id984d24b42d11775bdb80e35ca20e19426856b4f update translations LibreOffice Online/loleaflet-help (Portuguese) Currently translated at 55.0% (229 of 416 strings) Change-Id: Iccd408f6bcd91c81bb49c7cb2538b63549572095 update translations LibreOffice Online/loleaflet-help (Catalan) Currently translated at 71.4% (297 of 416 strings) Change-Id: I2a98f223b66b3013fd1ac7c400db4802f0504d2a update translations LibreOffice Online/loleaflet-help (Bulgarian) Currently translated at 70.4% (293 of 416 strings) Change-Id: I341b40437b05101f0d8f8d440827ce7c8412c6ae update translations LibreOffice Online/loleaflet-ui (Korean) Currently translated at 98.3% (283 of 288 strings) Change-Id: I3976f40c8b7464b9d4fe2257c05e77c78b92af44 update translations LibreOffice Online/android-app (Asturian) Currently translated at 72.7% (72 of 99 strings) Change-Id: I3c3cac6e7086e48ae5b1019efec86b69695f1d01 update translations LibreOffice Online/android-app (Asturian) Currently translated at 72.7% (72 of 99 strings) Change-Id: I20bfcc659448b7b11dfc8c0690563ef6e705c5b7 update translations LibreOffice Online/loleaflet-help (Bulgarian) Currently translated at 69.7% (290 of 416 strings) Change-Id: I5ba5ea15587eff03c874f9ea9ea47777dfba9a6a update translations LibreOffice Online/loleaflet-ui (Lao) Currently translated at 12.2% (35 of 288 strings) Change-Id: Ia42ac2ebc9eea728de5a709997748d7ccac0c693 update translations LibreOffice Online/loleaflet-help (Ukrainian) Currently translated at 55.5% (231 of 416 strings) Change-Id: I8b0d65823f5f9e1a1cda2f7357bc279098679d3b update translations LibreOffice Online/android-app (Italian) Currently translated at 100.0% (99 of 99 strings) Change-Id: I3c3b0e69e3fe560197f7e39cf57dd9ce2e7eaf52 update translations LibreOffice Online/loleaflet-ui (Finnish) Currently translated at 56.9% (164 of 288 strings) Change-Id: I1a90c92be5bbbbe727830ab5abd11299d4b3afd9 update translations LibreOffice Online/android-app (Russian) Currently translated at 99.0% (98 of 99 strings) Change-Id: Ie31f47f408a92817b1ff2e1a0ec7ec5d18b2df82 update translations LibreOffice Online/android-lib (Russian) Currently translated at 100.0% (8 of 8 strings) Change-Id: I5a0ae198d7a5c321234538212ea372eb84e9ab6e update translations LibreOffice Online/loleaflet-help (Polish) Currently translated at 55.0% (229 of 416 strings) Change-Id: I2a08052da49d3e9fd266b399933278015c5d27e5 update translations LibreOffice Online/loleaflet-help (Catalan) Currently translated at 71.2% (296 of 416 strings) Change-Id: I141b8b37eaa825eecb786e09967cc2cfa0b46fa5 update translations LibreOffice Online/android-lib (Russian) Currently translated at 12.5% (1 of 8 strings) Change-Id: I0d464192cd9afcfa10b38677fea1b485a8c71c27 update translations LibreOffice Online/loleaflet-help (Spanish) ... etc. - the rest is truncated _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits