[Libreoffice-commits] online.git: 3 commits - common/Util.cpp common/Util.hpp loleaflet/src loolwsd.xml.in wsd/ClientSession.cpp wsd/DocumentBroker.cpp wsd/DocumentBroker.hpp wsd/LOOLWSD.cpp wsd/Storage.cpp wsd/Storage.hpp

Michael Meeks (via logerrit) logerrit at kemper.freedesktop.org
Wed Nov 27 19:17:55 UTC 2019


 common/Util.cpp                                   |    9 ++
 common/Util.hpp                                   |    5 +
 loleaflet/src/control/Control.DownloadProgress.js |    2 
 loleaflet/src/layer/tile/TileLayer.js             |    4 -
 loleaflet/src/map/Clipboard.js                    |   42 +++++++++----
 loolwsd.xml.in                                    |    4 +
 wsd/ClientSession.cpp                             |    5 +
 wsd/DocumentBroker.cpp                            |   68 ++++++++++++++++------
 wsd/DocumentBroker.hpp                            |    4 +
 wsd/LOOLWSD.cpp                                   |    1 
 wsd/Storage.cpp                                   |   21 ++++++
 wsd/Storage.hpp                                   |    8 ++
 12 files changed, 136 insertions(+), 37 deletions(-)

New commits:
commit 84c218786b29f7e8b0d3688980a09043041138a2
Author:     Michael Meeks <michael.meeks at collabora.com>
AuthorDate: Wed Nov 27 19:05:57 2019 +0000
Commit:     Michael Meeks <michael.meeks at collabora.com>
CommitDate: Wed Nov 27 19:17:27 2019 +0000

    fix calc simple, single cell content copy; add origin to plain text.
    
    Change-Id: I7a6b0c90a74f6e3a91a840bf77c0935a300321f2

diff --git a/loleaflet/src/control/Control.DownloadProgress.js b/loleaflet/src/control/Control.DownloadProgress.js
index 8df498997..2fce6a714 100644
--- a/loleaflet/src/control/Control.DownloadProgress.js
+++ b/loleaflet/src/control/Control.DownloadProgress.js
@@ -159,7 +159,7 @@ L.Control.DownloadProgress = L.Control.extend({
 					var idx = text.indexOf('<!DOCTYPE HTML');
 					if (idx > 0)
 						text = text.substring(idx, text.length);
-					that._map._clip.setTextSelectionContent(text);
+					that._map._clip.setTextSelectionHTML(text);
 				};
 				// TODO: failure to parse ? ...
 				reader.readAsText(response);
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index 25cf5ba5a..1be76a03c 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -597,7 +597,7 @@ L.TileLayer = L.GridLayer.extend({
 			this._onTextSelectionMsg(textMsg);
 		}
 		else if (textMsg.startsWith('textselectioncontent:')) {
-			this._map._clip.setTextSelectionContent(textMsg.substr(22));
+			this._map._clip.setTextSelectionHTML(textMsg.substr(22));
 		}
 		else if (textMsg.startsWith('textselectionend:')) {
 			this._onTextSelectionEndMsg(textMsg);
@@ -733,7 +733,7 @@ L.TileLayer = L.GridLayer.extend({
 		// message is received from lowsd, *then* a 'celladdress' message.
 		var address = textMsg.substring(13);
 		if (!this._map['wopi'].DisableCopy) {
-			this._map._clip.setTextSelectionContent(this._lastFormula);
+			this._map._clip.setTextSelectionText(this._lastFormula);
 		}
 		this._map.fire('celladdress', {address: address});
 	},
diff --git a/loleaflet/src/map/Clipboard.js b/loleaflet/src/map/Clipboard.js
index 1acfff606..2808f2690 100644
--- a/loleaflet/src/map/Clipboard.js
+++ b/loleaflet/src/map/Clipboard.js
@@ -109,21 +109,30 @@ L.Clipboard = L.Class.extend({
 		return text.indexOf(this._getHtmlStubMarker()) > 0;
 	},
 
+	// wrap some content with our stub magic
+	_originWrapBody: function(body, isStub) {
+		var encodedOrigin = encodeURIComponent(this.getMetaPath());
+		var text =  '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">\n' +
+		            '<html>\n' +
+		            '  <head>\n';
+		if (isStub)
+			text += '    ' + this._getHtmlStubMarker() + '\n';
+		text +=     '    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>\n' +
+			    '    <meta name="origin" content="' + encodedOrigin + '"/>\n' +
+			    '  </head>\n'
+			    + body +
+			'</html>';
+		return text;
+	},
+
+	// what an empty clipboard has on it
 	_getStubHtml: function() {
 		var lang = 'en_US'; // FIXME: l10n
-		var encodedOrigin = encodeURIComponent(this.getMetaPath());
-		var stub = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">\n' +
-		    '<html>\n' +
-		    '  <head>\n' +
-		    '    ' + this._getHtmlStubMarker() + '\n' +
-		    '    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>\n' +
-		    '    <meta name="origin" content="' + encodedOrigin + '"/>\n' +
-		    '  </head>\n' +
+		return this._substProductName(this._originWrapBody(
 		    '  <body lang="' + lang + '" dir="ltr">\n' +
 		    '    <p>' + _('To paste outside %productName, please first click the \'download\' button') + '</p>\n' +
-		    '  </body>\n' +
-		    '</html>';
-		return this._substProductName(stub);
+		    '  </body>\n', true
+		));
 	},
 
 	_getMetaOrigin: function (html) {
@@ -688,9 +697,16 @@ L.Clipboard = L.Class.extend({
 	},
 
 	// textselectioncontent: message
-	setTextSelectionContent: function(text) {
+	setTextSelectionHTML: function(html) {
+		this._selectionType = 'text';
+		this._selectionContent = html;
+	},
+
+	// sets the selection to some (cell formula) text)
+	setTextSelectionText: function(text) {
 		this._selectionType = 'text';
-		this._selectionContent = text;
+		this._selectionContent = this._originWrapBody(
+			'<body>' + text + '</body>');
 	},
 
 	// complexselection: message
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index db51d5836..51ec92ef5 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -1006,7 +1006,10 @@ void ClientSession::postProcessCopyPayload(std::shared_ptr<Message> payload)
 {
     // Insert our meta origin if we can
     payload->rewriteDataBody([=](std::vector<char>& data) {
-            const size_t pos = Util::findInVector(data, "<meta name=\"generator\" content=\"");
+            size_t pos = Util::findInVector(data, "<meta name=\"generator\" content=\"");
+
+            if (pos == std::string::npos)
+                pos = Util::findInVector(data, "<meta http-equiv=\"content-type\" content=\"text/html;");
 
             // cf. TileLayer.js /_dataTransferToDocument/
             if (pos != std::string::npos) // assume text/html
commit 0d97efbfccc680e961086705e8a855a38a7af6c4
Author:     Michael Meeks <michael.meeks at collabora.com>
AuthorDate: Wed Nov 27 08:43:05 2019 +0000
Commit:     Michael Meeks <michael.meeks at collabora.com>
CommitDate: Wed Nov 27 19:17:27 2019 +0000

    locking: renew lock after timeout.
    
    Change-Id: I6191ee34239b978292aeb6795be74312a954e240

diff --git a/loolwsd.xml.in b/loolwsd.xml.in
index 416dd9b2a..d8e19b9c9 100644
--- a/loolwsd.xml.in
+++ b/loolwsd.xml.in
@@ -124,6 +124,10 @@
             <host desc="Regex pattern of hostname to allow or deny." allow="false">192\.168\.1\.1</host>
             <max_file_size desc="Maximum document size in bytes to load. 0 for unlimited." type="uint">0</max_file_size>
             <reuse_cookies desc="When enabled, cookies from the browser will be captured and set on WOPI requests." type="bool" default="false">false</reuse_cookies>
+	    <locking desc="Locking settings">
+	        <refresh desc="How frequently we should re-acquire a lock with the storage server, in seconds (default 15 mins) or 0 for no refresh"
+			 type="int" default="900">900</refresh>
+	    </locking>
         </wopi>
         <webdav desc="Allow/deny webdav storage. Mutually exclusive with wopi." allow="false">
             <host desc="Hostname to allow" allow="false">localhost</host>
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 8cb159e4d..ddfaa922d 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -323,6 +323,9 @@ void DocumentBroker::pollThread()
             adminRecv = recv;
             LOG_TRC("Doc [" << _docKey << "] added stats sent: " << sent << ", recv: " << recv << " bytes to totals.");
         }
+
+        if (_storage && _lockCtx->needsRefresh(now))
+            refreshLock();
 #endif
 
         if (isSaving() &&
@@ -1078,23 +1081,10 @@ void DocumentBroker::setLoaded()
     }
 }
 
-bool DocumentBroker::autoSave(const bool force, const bool dontSaveIfUnmodified)
+std::string DocumentBroker::getWriteableSessionId() const
 {
     assertCorrectThread();
 
-    LOG_TRC("autoSave(): forceful? " << force);
-    if (_sessions.empty() || _storage == nullptr || !_isLoaded ||
-        !_childProcess->isAlive() || (!_isModified && !force))
-    {
-        // Nothing to do.
-        LOG_TRC("Nothing to autosave [" << _docKey << "].");
-        return false;
-    }
-
-    // Remember the last save time, since this is the predicate.
-    LOG_TRC("Checking to autosave [" << _docKey << "].");
-
-    // Which session to use when auto saving ?
     std::string savingSessionId;
     for (auto& sessionIt : _sessions)
     {
@@ -1113,6 +1103,45 @@ bool DocumentBroker::autoSave(const bool force, const bool dontSaveIfUnmodified)
             break;
         }
     }
+    return savingSessionId;
+}
+
+void DocumentBroker::refreshLock()
+{
+    assertCorrectThread();
+
+    std::string savingSessionId = getWriteableSessionId();
+    LOG_TRC("Refresh lock " << _lockCtx->_lockToken << " with session " << savingSessionId);
+
+    auto it = _sessions.find(savingSessionId);
+    if (it == _sessions.end())
+        LOG_ERR("No write-able session to refresh lock with");
+    else
+    {
+        std::shared_ptr<ClientSession> session = it->second;
+        if (!session || !_storage->updateLockState(session->getAuthorization(), *_lockCtx, true))
+            LOG_ERR("Failed to refresh lock");
+    }
+}
+
+bool DocumentBroker::autoSave(const bool force, const bool dontSaveIfUnmodified)
+{
+    assertCorrectThread();
+
+    LOG_TRC("autoSave(): forceful? " << force);
+    if (_sessions.empty() || _storage == nullptr || !_isLoaded ||
+        !_childProcess->isAlive() || (!_isModified && !force))
+    {
+        // Nothing to do.
+        LOG_TRC("Nothing to autosave [" << _docKey << "].");
+        return false;
+    }
+
+    // Remember the last save time, since this is the predicate.
+    LOG_TRC("Checking to autosave [" << _docKey << "].");
+
+    // Which session to use when auto saving ?
+    std::string savingSessionId = getWriteableSessionId();
 
     bool sent = false;
     if (force)
@@ -2225,6 +2254,7 @@ void DocumentBroker::dumpState(std::ostream& os)
     os << "\n  idle time: " << getIdleTimeSecs();
     os << "\n  cursor " << _cursorPosX << ", " << _cursorPosY
       << "( " << _cursorWidth << "," << _cursorHeight << ")\n";
+    _lockCtx->dumpState(os);
     if (_tileCache)
         _tileCache->dumpState(os);
 
diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp
index f0a1ac061..91daee6a2 100644
--- a/wsd/DocumentBroker.hpp
+++ b/wsd/DocumentBroker.hpp
@@ -369,6 +369,10 @@ public:
     size_t getMemorySize() const;
 
 private:
+    /// get the session id of a session that can write the document for save / locking.
+    std::string getWriteableSessionId() const;
+
+    void refreshLock();
 
     /// Loads a document from the public URI into the jail.
     bool load(const std::shared_ptr<ClientSession>& session, const std::string& jailId);
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index c3e22d240..221d179ef 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -860,6 +860,7 @@ void LOOLWSD::initialize(Application& self)
             { "storage.wopi.host[0][@allow]", "true" },
             { "storage.wopi.max_file_size", "0" },
             { "storage.wopi[@allow]", "true" },
+            { "storage.wopi.locking.refresh", "900" },
             { "sys_template_path", "systemplate" },
             { "trace.path[@compress]", "true" },
             { "trace.path[@snapshot]", "false" },
diff --git a/wsd/Storage.cpp b/wsd/Storage.cpp
index efe0fc130..e8b769d3f 100644
--- a/wsd/Storage.cpp
+++ b/wsd/Storage.cpp
@@ -472,6 +472,24 @@ void LockContext::initSupportsLocks()
     _lockToken = "lool-lock" + Util::rng::getHexString(8);
 }
 
+bool LockContext::needsRefresh(const std::chrono::steady_clock::time_point &now) const
+{
+    static int refreshSeconds = LOOLWSD::getConfigValue<int>("storage.wopi.locking.refresh", 900);
+    return _supportsLocks && _isLocked && refreshSeconds > 0 &&
+        std::chrono::duration_cast<std::chrono::seconds>
+        (now - _lastLockTime).count() >= refreshSeconds;
+}
+
+void LockContext::dumpState(std::ostream& os)
+{
+    if (!_supportsLocks)
+        return;
+    os << "  lock:\n";
+    os << "    locked: " << _isLocked << "\n";
+    os << "    token: '" << _lockToken << "'\n";
+    os << "    last locked: " << Util::getSteadyClockAsString(_lastLockTime) << "\n";
+}
+
 std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const Authorization& auth, LockContext &lockCtx)
 {
     // update the access_token to the one matching to the session
@@ -734,11 +752,12 @@ bool WopiStorage::updateLockState(const Authorization &auth, LockContext &lockCt
         if (response.getStatus() == Poco::Net::HTTPResponse::HTTP_OK)
         {
             lockCtx._isLocked = lock;
+            lockCtx._lastLockTime = std::chrono::steady_clock::now();
             return true;
         }
         else
         {
-            LOG_WRN("Un-successfull " << wopiLog << " with status " << response.getStatus() <<
+            LOG_WRN("Un-successful " << wopiLog << " with status " << response.getStatus() <<
                     " and response: " << responseString);
         }
     }
diff --git a/wsd/Storage.hpp b/wsd/Storage.hpp
index cad148c7e..1e6eb18d3 100644
--- a/wsd/Storage.hpp
+++ b/wsd/Storage.hpp
@@ -13,6 +13,7 @@
 
 #include <set>
 #include <string>
+#include <chrono>
 
 #include <Poco/URI.h>
 #include <Poco/Util/Application.h>
@@ -34,11 +35,18 @@ struct LockContext
     bool        _isLocked;
     /// Name if we need it to use consistently for locking
     std::string _lockToken;
+    /// Time of last successful lock (re-)acquisition
+    std::chrono::steady_clock::time_point _lastLockTime;
 
     LockContext() : _supportsLocks(false), _isLocked(false) { }
 
     /// one-time setup for supporting locks & create token
     void initSupportsLocks();
+
+    /// do we need to refresh our lock ?
+    bool needsRefresh(const std::chrono::steady_clock::time_point &now) const;
+
+    void dumpState(std::ostream& os);
 };
 
 /// Base class of all Storage abstractions.
commit e3864a060e95f224edfed0cd35d4f12143bfba56
Author:     Michael Meeks <michael.meeks at collabora.com>
AuthorDate: Wed Nov 27 09:12:59 2019 +0000
Commit:     Michael Meeks <michael.meeks at collabora.com>
CommitDate: Wed Nov 27 19:17:27 2019 +0000

    Dump DocumentBroker state more completely.
    
    Change-Id: I3477fe70ba25d6e9a95c12f30138c3353994e384

diff --git a/common/Util.cpp b/common/Util.cpp
index 931de51d2..4dc0bdadf 100644
--- a/common/Util.cpp
+++ b/common/Util.cpp
@@ -900,6 +900,15 @@ namespace Util
 
         return timestamp;
     }
+
+    std::string getSteadyClockAsString(const std::chrono::steady_clock::time_point &time)
+    {
+        auto now = std::chrono::steady_clock::now();
+        const std::time_t t = std::chrono::system_clock::to_time_t(
+            std::chrono::time_point_cast<std::chrono::seconds>(
+                std::chrono::system_clock::now() + (time - now)));
+        return std::ctime(&t);
+    }
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/common/Util.hpp b/common/Util.hpp
index 66ce0b5b1..5dc39f942 100644
--- a/common/Util.hpp
+++ b/common/Util.hpp
@@ -939,9 +939,12 @@ int main(int argc, char**argv)
     /// Convert a time_point to iso8601 formatted string.
     std::string time_point_to_iso8601(std::chrono::system_clock::time_point tp);
 
-    //// Convert time from ISO8061 fraction format
+    /// Convert time from ISO8061 fraction format
     std::chrono::system_clock::time_point iso8601ToTimestamp(const std::string& iso8601Time, const std::string& logName);
 
+    /// conversion from steady_clock for debugging / tracing
+    std::string getSteadyClockAsString(const std::chrono::steady_clock::time_point &time);
+
     /// Automatically execute code at end of current scope.
     /// Used for exception-safe code.
     class ScopeGuard
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 7d669e00c..8cb159e4d 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -2217,10 +2217,12 @@ void DocumentBroker::dumpState(std::ostream& os)
     os << "\n  doc key: " << _docKey;
     os << "\n  doc id: " << _docId;
     os << "\n  num sessions: " << _sessions.size();
-    const std::time_t t = std::chrono::system_clock::to_time_t(
-        std::chrono::time_point_cast<std::chrono::seconds>(
-            std::chrono::system_clock::now() + (_lastSaveTime - now)));
-    os << "\n  last saved: " << std::ctime(&t);
+    os << "\n  last saved: " << Util::getSteadyClockAsString(_lastSaveTime);
+    os << "\n  last save request: " << Util::getSteadyClockAsString(_lastSaveRequestTime);
+    os << "\n  last save response: " << Util::getSteadyClockAsString(_lastSaveResponseTime);
+    os << "\n  last modifed: " << Util::getHttpTime(_documentLastModifiedTime);
+    os << "\n  file last modifed: " << Util::getHttpTime(_lastFileModifiedTime);
+    os << "\n  idle time: " << getIdleTimeSecs();
     os << "\n  cursor " << _cursorPosX << ", " << _cursorPosY
       << "( " << _cursorWidth << "," << _cursorHeight << ")\n";
     if (_tileCache)


More information about the Libreoffice-commits mailing list