loleaflet/src/control/Control.LokDialog.js | 20 ++++---- loleaflet/src/control/Control.Toolbar.js | 12 ++--- loleaflet/src/layer/tile/TileLayer.js | 4 - loleaflet/src/map/Map.js | 47 +++++++------------- loleaflet/src/map/handler/Map.TouchGesture.js | 17 ++++--- wsd/ClientSession.cpp | 14 +++++- wsd/ClientSession.hpp | 5 ++ wsd/DocumentBroker.cpp | 17 +++---- wsd/Storage.cpp | 53 ++++++++++++++++------- wsd/Storage.hpp | 59 +++++++++++++++++++------- 10 files changed, 153 insertions(+), 95 deletions(-)
New commits: commit 7c1153d30a96320a72ddb7de96f0011e7058be32 Author: Ashod Nakashian <[email protected]> AuthorDate: Sun Jan 26 17:21:51 2020 -0500 Commit: Jan Holesovsky <[email protected]> CommitDate: Tue Jan 28 18:10:14 2020 +0100 leaflet: double-tap to show keyboard on mobile On mobile, we now do not show the keyboard by default when the user taps the cell in Calc or a text-area in Impress. Instead, the user should double-tap. This is especially useful when the user selects the cell on first tap, then double-tap to edit. As for Impress, editing-mode is enabled only by double-clicking, so showing the keyboard on mobile on single tap is not helpful as the user can't edit just yet. So it makes sense to show the keyboard only on double-tap, when the slide is in text editing mode. On desktop and for other document the behavior is unchanged. Change-Id: I4206291f60656391d061387f7bbad1b6e3caf80d diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js index 9804e5d65..4779f4ed9 100644 --- a/loleaflet/src/control/Control.LokDialog.js +++ b/loleaflet/src/control/Control.LokDialog.js @@ -411,8 +411,6 @@ L.Control.LokDialog = L.Control.extend({ this._map.setWinId(dlgId); if (dlgId in this._dialogs) { this._map.focus(); - } else { - this._map.blur(); } }, diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js index f5fb704a8..0b4926a43 100644 --- a/loleaflet/src/layer/tile/TileLayer.js +++ b/loleaflet/src/layer/tile/TileLayer.js @@ -2091,8 +2091,8 @@ L.TileLayer = L.GridLayer.extend({ this._map._textInput.showCursor(); if (this._map.editorHasFocus() /* && !L.Browser.mobile */) { - // On mobile, this is causing some key input to get lost. - this._map.focus(); + // User is editing, show the keyboard. + this._map.focus(true); } }, diff --git a/loleaflet/src/map/Map.js b/loleaflet/src/map/Map.js index 6a2e99f3e..45ab9273a 100644 --- a/loleaflet/src/map/Map.js +++ b/loleaflet/src/map/Map.js @@ -904,12 +904,13 @@ L.Map = L.Evented.extend({ return this.layerPointToLatLng(this.mouseEventToLayerPoint(e)); }, - // Give the focus to the text input to accept keyboard input. - // On mobile, it will show the virtual keyboard. - focus: function () { - //TODO: we should check if focus is going to edit the doc in - //TODO: read-only mode and prevent it (but allow for searching). + // Give the focus to the text input. + // @acceptInput (on mobile only) true if we want to + // accept key input, and show the virtual keyboard. + focus: function (acceptInput) { this._textInput.focus(); + if (window.mode.isMobile() && acceptInput !== true) + this.blur(); }, // Lose focus to stop accepting keyboard input. diff --git a/loleaflet/src/map/handler/Map.TouchGesture.js b/loleaflet/src/map/handler/Map.TouchGesture.js index 78fa4affc..7d9d07f5d 100644 --- a/loleaflet/src/map/handler/Map.TouchGesture.js +++ b/loleaflet/src/map/handler/Map.TouchGesture.js @@ -322,11 +322,8 @@ L.Map.TouchGesture = L.Handler.extend({ docLayer._postMouseEvent('buttondown', mousePos.x, mousePos.y, 1, 1, 0); docLayer._postMouseEvent('buttonup', mousePos.x, mousePos.y, 1, 1, 0); - if (this._state === L.Map.TouchGesture.MARKER || (this._state === L.Map.TouchGesture.GRAPHIC && !docLayer._isCursorVisible)) { - this._map.blur(); - } else if (!this._map.hasFocus()) { - this._map.focus(); - } + // Take focus, but keyboard show only in Writer (double-tap to edit Calc/Impress). + this._map.focus(this._map._docLayer._docType === 'text'); } }, @@ -337,8 +334,14 @@ L.Map.TouchGesture = L.Handler.extend({ latlng = this._map.layerPointToLatLng(layerPoint), mousePos = this._map._docLayer._latLngToTwips(latlng); - this._map._docLayer._postMouseEvent('buttondown', mousePos.x, mousePos.y, 2, 1, 0); - this._map._docLayer._postMouseEvent('buttonup', mousePos.x, mousePos.y, 2, 1, 0); + var docLayer = this._map._docLayer; + if (docLayer) { + docLayer._postMouseEvent('buttondown', mousePos.x, mousePos.y, 2, 1, 0); + docLayer._postMouseEvent('buttonup', mousePos.x, mousePos.y, 2, 1, 0); + + // Show keyboard. + this._map.focus(true); + } }, _onTripleTap: function (e) { commit c3d2b1233e3d8073468ea69552f55c72488a7d5d Author: Ashod Nakashian <[email protected]> AuthorDate: Sat Jan 25 13:13:49 2020 -0500 Commit: Jan Holesovsky <[email protected]> CommitDate: Tue Jan 28 18:10:14 2020 +0100 leaflet: remove delayed cursor update and simplify Change-Id: Ifcc165562d1f7aba611bb578688d9c5203681765 diff --git a/loleaflet/src/map/Map.js b/loleaflet/src/map/Map.js index 0f69fe74c..6a2e99f3e 100644 --- a/loleaflet/src/map/Map.js +++ b/loleaflet/src/map/Map.js @@ -246,7 +246,7 @@ L.Map = L.Evented.extend({ this.on('editorgotfocus', this._onEditorGotFocus, this); // Fired to signal that the input focus is being changed. - this.on('changefocuswidget', this._changeFocusWidget, this); + this.on('changefocuswidget', this._onChangeFocusWidget, this); // View info (user names and view ids) this._viewInfo = {}; @@ -1334,8 +1334,8 @@ L.Map = L.Evented.extend({ }, map.options.outOfFocusTimeoutSecs * 1000); }, - // Change the focus to a dialog. - onFocusDialog: function onFocusDialog(dialog, winId) { + // Change the focus to a dialog or editor. + _changeFocusWidget: function (dialog, winId) { if (!this._loaded) { return; } this._winId = winId; @@ -1353,22 +1353,7 @@ L.Map = L.Evented.extend({ // The editor got focus (probably a dialog closed or user clicked to edit). _onEditorGotFocus: function() { - if (!this._loaded) { return; } - - this._winId = 0; - this._activeDialog = null; - - var doclayer = this._docLayer; - if (doclayer) - { - // we restore the old cursor position by a small delay, so that if the user clicks - // inside the document we skip to restore it, so that the user does not see the cursor - // jumping from the old position to the new one - setTimeout(function () { - console.debug('apply focus change in timeout'); - doclayer._updateCursorAndOverlay(); - }, 300); - } + this._changeFocusWidget(null, 0); }, // Our browser tab got focus. @@ -1383,12 +1368,12 @@ L.Map = L.Evented.extend({ this._activate(); }, - // Focus is being changed, update states. - _changeFocusWidget: function (e) { + // Event to change the focus to dialog or editor. + _onChangeFocusWidget: function (e) { if (e.winId === 0) { this._onEditorGotFocus(); } else { - this.onFocusDialog(e.dialog, e.winId); + this._changeFocusWidget(e.dialog, e.winId); } }, commit aa921021007a8b49066dec5375b65e8a9bb4ba71 Author: Ashod Nakashian <[email protected]> AuthorDate: Sat Jan 18 11:15:07 2020 -0500 Commit: Jan Holesovsky <[email protected]> CommitDate: Tue Jan 28 18:10:14 2020 +0100 leaflet: focus the dialog/sidebar even when the cursor is not visible Change-Id: I96963957986ba29cc55670dd6ef79a0bf39a9132 diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js index 13b261e5e..9804e5d65 100644 --- a/loleaflet/src/control/Control.LokDialog.js +++ b/loleaflet/src/control/Control.LokDialog.js @@ -409,7 +409,7 @@ L.Control.LokDialog = L.Control.extend({ } this._map.setWinId(dlgId); - if (dlgId in this._dialogs && this._dialogs[dlgId].cursorVisible) { + if (dlgId in this._dialogs) { this._map.focus(); } else { this._map.blur(); @@ -964,6 +964,8 @@ L.Control.LokDialog = L.Control.extend({ if (e.winId === 0) { // We lost the focus. this._onEditorGotFocus(); + } else { + this.focus(e.winId); } }, @@ -1027,7 +1029,7 @@ L.Control.LokDialog = L.Control.extend({ // if dialog is hidden, show it if (container) - $(container).parent().show(); + $(container).parent().show(); if (parentId in that._dialogs) { // We might have closed the dialog by the time we render. @@ -1041,11 +1043,12 @@ L.Control.LokDialog = L.Control.extend({ // Binary dialog msg recvd from core _onDialogPaint: function (e) { - var parentId = this._getParentId(e.id); + var id = parseInt(e.id); + var parentId = this._getParentId(id); if (parentId) { this._paintDialogChild(parentId, e.img); } else { - this._paintDialog(e.id, e.rectangle, e.img); + this._paintDialog(id, e.rectangle, e.img); } }, commit 7e8914cae97fabb8936be44292c3c7ba002b2588 Author: Ashod Nakashian <[email protected]> AuthorDate: Mon Jan 13 23:21:01 2020 -0500 Commit: Jan Holesovsky <[email protected]> CommitDate: Tue Jan 28 18:10:14 2020 +0100 leaflet: change focus on rendering dialogs Change-Id: I73002acb82950fb6a207f77768956d7cbfcd8a0d diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js index 46892becc..13b261e5e 100644 --- a/loleaflet/src/control/Control.LokDialog.js +++ b/loleaflet/src/control/Control.LokDialog.js @@ -1032,9 +1032,8 @@ L.Control.LokDialog = L.Control.extend({ if (parentId in that._dialogs) { // We might have closed the dialog by the time we render. that._dialogs[parentId].isPainting = false; - if (!that._isSidebar(parentId)) { - that.focus(parentId); - } + if (!that._isSidebar(parentId) && !that._isCalcInputBar(parentId)) + that._map.fire('changefocuswidget', {winId: parentId, dialog: that}); } }; img.src = imgData; commit 09c52fef05264b12c456a3f9927b4b4b475a7c76 Author: Ashod Nakashian <[email protected]> AuthorDate: Mon Jan 13 15:49:13 2020 -0500 Commit: Jan Holesovsky <[email protected]> CommitDate: Tue Jan 28 18:10:14 2020 +0100 leaflet: improve refocusing the browser Change-Id: I8b0ca64bcdb04dd05c271b5a5a2b7cf927e62941 diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js index 84513d46f..46892becc 100644 --- a/loleaflet/src/control/Control.LokDialog.js +++ b/loleaflet/src/control/Control.LokDialog.js @@ -349,7 +349,7 @@ L.Control.LokDialog = L.Control.extend({ this._dialogs[e.id].cursorVisible = e.visible === 'true'; if (this._dialogs[e.id].cursorVisible) { $('#' + strId + '-cursor').css({display: 'block'}); - this._map.fire('changefocuswidget', {winId: e.id, dialog: this}); // Us. + this._map.onFocusDialog(this, e.id); } else { $('#' + strId + '-cursor').css({display: 'none'}); diff --git a/loleaflet/src/map/Map.js b/loleaflet/src/map/Map.js index 2b13de11c..0f69fe74c 100644 --- a/loleaflet/src/map/Map.js +++ b/loleaflet/src/map/Map.js @@ -1373,11 +1373,12 @@ L.Map = L.Evented.extend({ // Our browser tab got focus. _onGotFocus: function () { - if (this._activeDialog) { - this._activeDialog.focus(this._winId); - } else { + if (this._winId === 0) { this.fire('editorgotfocus'); } + else if (this._activeDialog) { + this._activeDialog.focus(this._winId); + } this._activate(); }, commit bcd7af96579181969f1d6c1c977731e030fe3c6e Author: Ashod Nakashian <[email protected]> AuthorDate: Sat Jan 25 11:40:19 2020 -0500 Commit: Jan Holesovsky <[email protected]> CommitDate: Tue Jan 28 18:10:14 2020 +0100 leaflet: Impress toolbar fixes for sidebar state Impress toolbar now reflects the state of the sidebar. All sidebars now show correctly for Impress docs, except for the properties deck, which conflicts with the sidebar button. Change-Id: I8802e5e35b388df20ca21c4f756b4d95197de570 diff --git a/loleaflet/src/control/Control.Toolbar.js b/loleaflet/src/control/Control.Toolbar.js index 947a19a3a..8bb1e3e05 100644 --- a/loleaflet/src/control/Control.Toolbar.js +++ b/loleaflet/src/control/Control.Toolbar.js @@ -961,10 +961,10 @@ function initNormalToolbar() { {type: 'spacer'}, {type: 'button', id: 'edit', img: 'edit'}, {type: 'button', id: 'sidebar', img: 'sidebar_modify_page', hint: _UNO('.uno:Sidebar', '', true), uno: '.uno:Sidebar', hidden: true}, - {type: 'button', id: 'sidebar-modify-page', img: 'sidebar_modify_page', hint: _UNO('.uno:ModifyPage', 'presentation', true), uno: 'ModifyPage', hidden: true}, - {type: 'button', id: 'sidebar-slide-change', img: 'sidebar_slide_change', hint: _UNO('.uno:SlideChangeWindow', 'presentation', true), uno: 'SlideChangeWindow', hidden: true}, - {type: 'button', id: 'sidebar-custom-animation', img: 'sidebar_custom_animation', hint: _UNO('.uno:CustomAnimation', 'presentation', true), uno: 'CustomAnimation', hidden: true}, - {type: 'button', id: 'sidebar-master-slides', img: 'sidebar_master_slides', hint: _UNO('.uno:MasterSlidesPanel', 'presentation', true), uno: 'MasterSlidesPanel', hidden: true}, + {type: 'button', id: 'modifypage', img: 'sidebar_modify_page', hint: _UNO('.uno:ModifyPage', 'presentation', true), uno: '.uno:ModifyPage', hidden: true}, + {type: 'button', id: 'slidechangewindow', img: 'sidebar_slide_change', hint: _UNO('.uno:SlideChangeWindow', 'presentation', true), uno: '.uno:SlideChangeWindow', hidden: true}, + {type: 'button', id: 'customanimation', img: 'sidebar_custom_animation', hint: _UNO('.uno:CustomAnimation', 'presentation', true), uno: '.uno:CustomAnimation', hidden: true}, + {type: 'button', id: 'masterslidespanel', img: 'sidebar_master_slides', hint: _UNO('.uno:MasterSlidesPanel', 'presentation', true), uno: '.uno:MasterSlidesPanel', hidden: true}, {type: 'break', id: 'breaksidebar', hidden: true}, {type: 'button', id: 'fold', img: 'fold', desktop: true, mobile: false, hidden: true}, {type: 'button', id: 'hamburger-tablet', img: 'hamburger', desktop: false, mobile: false, tablet: true, iosapptablet: false, hidden: true}, @@ -1639,7 +1639,7 @@ function onDocLayerInit() { break; case 'presentation': if (toolbarUp) { - toolbarUp.show('breaksidebar', 'sidebar-modify-page'); + toolbarUp.show('breaksidebar', 'sidebar', 'modifypage'); } var presentationToolbar = w2ui['presentation-toolbar']; @@ -1666,7 +1666,7 @@ function onDocLayerInit() { if (toolbarUp) toolbarUp.show('leftpara', 'centerpara', 'rightpara', 'justifypara', 'breakpara', 'linespacing', 'breakspacing', 'defaultbullet', 'defaultnumbering', 'breakbullet', 'inserttextbox', 'inserttable', 'backcolor', - 'breaksidebar', 'sidebar-modify-page', 'sidebar-slide-change', 'sidebar-custom-animation', 'sidebar-master-slides'); + 'breaksidebar', 'sidebar', 'modifypage', 'slidechangewindow', 'customanimation', 'masterslidespanel'); if (statusbar) statusbar.show('prev', 'next'); commit da0f3a950a03c7ac9544176ca9d2e48e9a0126b8 Author: Ashod Nakashian <[email protected]> AuthorDate: Sat Jan 18 16:04:26 2020 -0500 Commit: Jan Holesovsky <[email protected]> CommitDate: Tue Jan 28 18:10:14 2020 +0100 wsd: per-user cookies Cookies may be passed from the client to the storage, in which case each user may have its own unique set of cookies. These cookies are now preserved in the ClientSession, which is per connection, and are then passed to the storage to use when communicating with the WOPI-like backend. (cherry picked from commit 6022faf3cc9b622b490c3f8ca91efbff8e542414) Change-Id: Ic2e13fa541a5ee01b7383939bbbf7d46ea75684b diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp index beed136e5..60ed55c6f 100644 --- a/wsd/ClientSession.cpp +++ b/wsd/ClientSession.cpp @@ -60,7 +60,19 @@ ClientSession::ClientSession(const std::string& id, _isTextDocument(false) { const size_t curConnections = ++LOOLWSD::NumConnections; - LOG_INF("ClientSession ctor [" << getName() << "], current number of connections: " << curConnections); + LOG_INF("ClientSession ctor [" << getName() << "] for URI: [" << _uriPublic.toString() + << "], current number of connections: " << curConnections); + + for (const auto& param : _uriPublic.getQueryParameters()) + { + if (param.first == "reuse_cookies") + { + // Cache the cookies to avoid re-parsing the URI again. + _cookies = param.second; + LOG_INF("ClientSession [" << getName() << "] has cookies: " << _cookies); + break; + } + } // populate with random values. for (auto it : _clipboardKeys) diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp index 8ee184687..d714179ce 100644 --- a/wsd/ClientSession.hpp +++ b/wsd/ClientSession.hpp @@ -123,6 +123,8 @@ public: /// The access token of this session. Authorization getAuthorization() const; + const std::string& getCookies() const { return _cookies; } + /// Set WOPI fileinfo object void setWopiFileInfo(std::unique_ptr<WopiStorage::WOPIFileInfo>& wopiFileInfo) { _wopiFileInfo = std::move(wopiFileInfo); } @@ -220,6 +222,9 @@ private: /// URI with which client made request to us const Poco::URI _uriPublic; + /// The cookies we should pass on to the storage on saving. + std::string _cookies; + /// Whether this session is the owner of currently opened document bool _isDocumentOwner; diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp index 876436abe..1eed16ea0 100644 --- a/wsd/DocumentBroker.cpp +++ b/wsd/DocumentBroker.cpp @@ -596,8 +596,8 @@ bool DocumentBroker::load(const std::shared_ptr<ClientSession>& session, const s WopiStorage* wopiStorage = dynamic_cast<WopiStorage*>(_storage.get()); if (wopiStorage != nullptr) { - std::unique_ptr<WopiStorage::WOPIFileInfo> wopifileinfo = - wopiStorage->getWOPIFileInfo(session->getAuthorization(), *_lockCtx); + std::unique_ptr<WopiStorage::WOPIFileInfo> wopifileinfo = wopiStorage->getWOPIFileInfo( + session->getAuthorization(), session->getCookies(), *_lockCtx); userId = wopifileinfo->getUserId(); username = wopifileinfo->getUsername(); userExtraInfo = wopifileinfo->getUserExtraInfo(); @@ -754,9 +754,9 @@ bool DocumentBroker::load(const std::shared_ptr<ClientSession>& session, const s if (!_storage->isLoaded()) { std::string localPath = _storage->loadStorageFileToLocal( - session->getAuthorization(), *_lockCtx, templateSource); + session->getAuthorization(), session->getCookies(), *_lockCtx, templateSource); - if (!_storage->updateLockState(session->getAuthorization(), *_lockCtx, true)) + if (!_storage->updateLockState(session->getAuthorization(), session->getCookies(), *_lockCtx, true)) LOG_ERR("Failed to lock!"); #if !MOBILEAPP @@ -990,8 +990,8 @@ bool DocumentBroker::saveToStorageInternal(const std::string& sessionId, bool su LOG_DBG("Persisting [" << _docKey << "] after saving to URI [" << uriAnonym << "]."); assert(_storage && _tileCache); - StorageBase::SaveResult storageSaveResult = _storage->saveLocalFileToStorage( - auth, *_lockCtx, saveAsPath, saveAsFilename, isRename); + const StorageBase::SaveResult storageSaveResult = _storage->saveLocalFileToStorage( + auth, it->second->getCookies(), *_lockCtx, saveAsPath, saveAsFilename, isRename); if (storageSaveResult.getResult() == StorageBase::SaveResult::OK) { #if !MOBILEAPP @@ -1146,7 +1146,7 @@ void DocumentBroker::refreshLock() else { std::shared_ptr<ClientSession> session = it->second; - if (!session || !_storage->updateLockState(session->getAuthorization(), *_lockCtx, true)) + if (!session || !_storage->updateLockState(session->getAuthorization(), session->getCookies(), *_lockCtx, true)) LOG_ERR("Failed to refresh lock"); } } @@ -1418,7 +1418,7 @@ void DocumentBroker::disconnectSessionInternal(const std::string& id) if (_markToDestroy && // last session to remove; FIXME: Editable? _lockCtx->_isLocked && _storage) { - if (!_storage->updateLockState(it->second->getAuthorization(), *_lockCtx, false)) + if (!_storage->updateLockState(it->second->getAuthorization(), it->second->getCookies(), *_lockCtx, false)) LOG_ERR("Failed to unlock!"); } @@ -1782,7 +1782,6 @@ void DocumentBroker::sendRequestedTiles(const std::shared_ptr<ClientSession>& se float tilesOnFlyUpperLimit = 0; if (normalizedVisArea.hasSurface() && session->getTileWidthInTwips() != 0 && session->getTileHeightInTwips() != 0) { - const int tilesFitOnWidth = std::ceil(normalizedVisArea.getRight() / session->getTileWidthInTwips()) - std::ceil(normalizedVisArea.getLeft() / session->getTileWidthInTwips()) + 1; const int tilesFitOnHeight = std::ceil(normalizedVisArea.getBottom() / session->getTileHeightInTwips()) - diff --git a/wsd/Storage.cpp b/wsd/Storage.cpp index ab2ad71c6..f465e0a01 100644 --- a/wsd/Storage.cpp +++ b/wsd/Storage.cpp @@ -300,7 +300,10 @@ std::unique_ptr<LocalStorage::LocalFileInfo> LocalStorage::getLocalFileInfo() return std::unique_ptr<LocalStorage::LocalFileInfo>(new LocalFileInfo({"localhost" + std::to_string(LastLocalStorageId), "LocalHost#" + std::to_string(LastLocalStorageId++)})); } -std::string LocalStorage::loadStorageFileToLocal(const Authorization& /*auth*/, LockContext & /*lockCtx*/, const std::string& /*templateUri*/) +std::string LocalStorage::loadStorageFileToLocal(const Authorization& /*auth*/, + const std::string& /*cookies*/, + LockContext& /*lockCtx*/, + const std::string& /*templateUri*/) { #if !MOBILEAPP // /chroot/jailId/user/doc/childId/file.ext @@ -363,7 +366,10 @@ std::string LocalStorage::loadStorageFileToLocal(const Authorization& /*auth*/, } -StorageBase::SaveResult LocalStorage::saveLocalFileToStorage(const Authorization& /*auth*/, LockContext &/*lockCtx*/, const std::string& /*saveAsPath*/, const std::string& /*saveAsFilename*/, bool /*isRename*/) +StorageBase::SaveResult +LocalStorage::saveLocalFileToStorage(const Authorization& /*auth*/, const std::string& /*cookies*/, + LockContext& /*lockCtx*/, const std::string& /*saveAsPath*/, + const std::string& /*saveAsFilename*/, bool /*isRename*/) { try { @@ -428,6 +434,8 @@ static void addStorageReuseCookie(Poco::Net::HTTPRequest& request, const std::st if (!reuseStorageCookies.empty()) { Poco::Net::NameValueCollection nvcCookies; + request.getCookies(nvcCookies); // Preserve existing cookies. + std::vector<std::string> cookies = LOOLProtocol::tokenize(reuseStorageCookies, ':'); for (auto cookie : cookies) { @@ -435,9 +443,10 @@ static void addStorageReuseCookie(Poco::Net::HTTPRequest& request, const std::st if (cookieTokens.size() == 2) { nvcCookies.add(cookieTokens[0], cookieTokens[1]); - LOG_DBG("Added storage reuse cookie [" << cookieTokens[0] << "=" << cookieTokens[1] << "]."); + LOG_DBG("Added storage reuse cookie [" << cookieTokens[0] << '=' << cookieTokens[1] << "]."); } } + request.setCookies(nvcCookies); } } @@ -494,7 +503,9 @@ void LockContext::dumpState(std::ostream& os) const #if !MOBILEAPP -std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const Authorization& auth, LockContext &lockCtx) +std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const Authorization& auth, + const std::string& cookies, + LockContext& lockCtx) { // update the access_token to the one matching to the session Poco::URI uriObject(getUri()); @@ -513,7 +524,7 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const Au auth.authorizeRequest(request); addStorageDebugCookie(request); if (_reuseCookies) - addStorageReuseCookie(request, params["reuse_cookies"]); + addStorageReuseCookie(request, cookies); addWopiProof(request, params["access_token"]); const auto startTime = std::chrono::steady_clock::now(); @@ -712,7 +723,8 @@ std::unique_ptr<WopiStorage::WOPIFileInfo> WopiStorage::getWOPIFileInfo(const Au userCanRename, callDuration})); } -bool WopiStorage::updateLockState(const Authorization &auth, LockContext &lockCtx, bool lock) +bool WopiStorage::updateLockState(const Authorization& auth, const std::string& cookies, + LockContext& lockCtx, bool lock) { if (!lockCtx._supportsLocks) return true; @@ -743,7 +755,7 @@ bool WopiStorage::updateLockState(const Authorization &auth, LockContext &lockCt request.set("X-LOOL-WOPI-ExtendedData", getExtendedData()); addStorageDebugCookie(request); if (_reuseCookies) - addStorageReuseCookie(request, params["reuse_cookies"]); + addStorageReuseCookie(request, cookies); addWopiProof(request, params["access_token"]); psession->sendRequest(request); @@ -782,7 +794,10 @@ bool WopiStorage::updateLockState(const Authorization &auth, LockContext &lockCt } /// uri format: http://server/<...>/wopi*/files/<id>/content -std::string WopiStorage::loadStorageFileToLocal(const Authorization& auth, LockContext &/* lockCtx */, const std::string& templateUri) +std::string WopiStorage::loadStorageFileToLocal(const Authorization& auth, + const std::string& cookies, + LockContext& /*lockCtx*/, + const std::string& templateUri) { // WOPI URI to download files ends in '/contents'. // Add it here to get the payload instead of file info. @@ -819,7 +834,7 @@ std::string WopiStorage::loadStorageFileToLocal(const Authorization& auth, LockC auth.authorizeRequest(request); addStorageDebugCookie(request); if (_reuseCookies) - addStorageReuseCookie(request, params["reuse_cookies"]); + addStorageReuseCookie(request, cookies); addWopiProof(request, params["access_token"]); psession->sendRequest(request); @@ -876,7 +891,10 @@ std::string WopiStorage::loadStorageFileToLocal(const Authorization& auth, LockC return ""; } -StorageBase::SaveResult WopiStorage::saveLocalFileToStorage(const Authorization& auth, LockContext &lockCtx, const std::string& saveAsPath, const std::string& saveAsFilename, const bool isRename) +StorageBase::SaveResult +WopiStorage::saveLocalFileToStorage(const Authorization& auth, const std::string& cookies, + LockContext& lockCtx, const std::string& saveAsPath, + const std::string& saveAsFilename, const bool isRename) { // TODO: Check if this URI has write permission (canWrite = true) @@ -975,7 +993,7 @@ StorageBase::SaveResult WopiStorage::saveLocalFileToStorage(const Authorization& request.setContentLength(size); addStorageDebugCookie(request); if (_reuseCookies) - addStorageReuseCookie(request, params["reuse_cookies"]); + addStorageReuseCookie(request, cookies); addWopiProof(request, params["access_token"]); std::ostream& os = psession->sendRequest(request); @@ -1106,17 +1124,20 @@ StorageBase::SaveResult WopiStorage::saveLocalFileToStorage(const Authorization& return saveResult; } -std::string WebDAVStorage::loadStorageFileToLocal( - const Authorization& /*auth*/, LockContext &/*lockCtx*/, const std::string& /*templateUri*/) +std::string WebDAVStorage::loadStorageFileToLocal(const Authorization& /*auth*/, + const std::string& /*cookies*/, + LockContext& /*lockCtx*/, + const std::string& /*templateUri*/) { // TODO: implement webdav GET. setLoaded(true); return getUri().toString(); } -StorageBase::SaveResult WebDAVStorage::saveLocalFileToStorage( - const Authorization& /*auth*/, LockContext &/*lockCtx*/, const std::string& /*saveAsPath*/, - const std::string& /*saveAsFilename*/, bool /*isRename*/) +StorageBase::SaveResult +WebDAVStorage::saveLocalFileToStorage(const Authorization& /*auth*/, const std::string& /*cookies*/, + LockContext& /*lockCtx*/, const std::string& /*saveAsPath*/, + const std::string& /*saveAsFilename*/, bool /*isRename*/) { // TODO: implement webdav PUT. return StorageBase::SaveResult(StorageBase::SaveResult::OK); diff --git a/wsd/Storage.hpp b/wsd/Storage.hpp index 7060cd7a4..bb678ac8a 100644 --- a/wsd/Storage.hpp +++ b/wsd/Storage.hpp @@ -53,7 +53,6 @@ struct LockContext class StorageBase { public: - /// Represents basic file's attributes. /// Used for local and network files. class FileInfo @@ -217,15 +216,25 @@ public: std::string getFileExtension() const { return Poco::Path(_fileInfo.getFilename()).getExtension(); } /// Update the locking state (check-in/out) of the associated file - virtual bool updateLockState(const Authorization &auth, LockContext &lockCtx, bool lock) = 0; + virtual bool updateLockState(const Authorization& auth, const std::string& cookies, + LockContext& lockCtx, bool lock) + = 0; /// Returns a local file path for the given URI. /// If necessary copies the file locally first. - virtual std::string loadStorageFileToLocal(const Authorization& auth, LockContext &lockCtx, const std::string& templateUri) = 0; + virtual std::string loadStorageFileToLocal(const Authorization& auth, + const std::string& /*cookies*/, LockContext& lockCtx, + const std::string& templateUri) + = 0; /// Writes the contents of the file back to the source. + /// @param cookies A string representing key=value pairs that are set as cookies. /// @param savedFile When the operation was saveAs, this is the path to the file that was saved. - virtual SaveResult saveLocalFileToStorage(const Authorization& auth, LockContext &lockCtx, const std::string& saveAsPath, const std::string& saveAsFilename, const bool isRename) = 0; + virtual SaveResult + saveLocalFileToStorage(const Authorization& auth, const std::string& /*cookies*/, + LockContext& lockCtx, const std::string& saveAsPath, + const std::string& saveAsFilename, const bool isRename) + = 0; static size_t getFileSize(const std::string& filename); @@ -312,11 +321,19 @@ public: /// obtained using getFileInfo method std::unique_ptr<LocalFileInfo> getLocalFileInfo(); - bool updateLockState(const Authorization &, LockContext &, bool) override { return true; } + bool updateLockState(const Authorization&, const std::string&, LockContext&, bool) override + { + return true; + } - std::string loadStorageFileToLocal(const Authorization& auth, LockContext &lockCtx, const std::string& templateUri) override; + std::string loadStorageFileToLocal(const Authorization& auth, const std::string& /*cookies*/, + LockContext& lockCtx, + const std::string& templateUri) override; - SaveResult saveLocalFileToStorage(const Authorization& auth, LockContext &lockCtx, const std::string& saveAsPath, const std::string& saveAsFilename, const bool isRename) override; + SaveResult saveLocalFileToStorage(const Authorization& auth, const std::string& /*cookies*/, + LockContext& lockCtx, const std::string& saveAsPath, + const std::string& saveAsFilename, + const bool isRename) override; private: /// True if the jailed file is not linked but copied. @@ -545,15 +562,21 @@ public: /// which can then be obtained using getFileInfo() /// Also sets up the locking context for future operations. std::unique_ptr<WOPIFileInfo> getWOPIFileInfo(const Authorization& auth, - LockContext &lockCtx); + const std::string& cookies, LockContext& lockCtx); /// Update the locking state (check-in/out) of the associated file - bool updateLockState(const Authorization &auth, LockContext &lockCtx, bool lock) override; + bool updateLockState(const Authorization& auth, const std::string& cookies, + LockContext& lockCtx, bool lock) override; /// uri format: http://server/<...>/wopi*/files/<id>/content - std::string loadStorageFileToLocal(const Authorization& auth, LockContext &lockCtx, const std::string& templateUri) override; + std::string loadStorageFileToLocal(const Authorization& auth, const std::string& /*cookies*/, + LockContext& lockCtx, + const std::string& templateUri) override; - SaveResult saveLocalFileToStorage(const Authorization& auth, LockContext &lockCtx, const std::string& saveAsPath, const std::string& saveAsFilename, const bool isRename) override; + SaveResult saveLocalFileToStorage(const Authorization& auth, const std::string& /*cookies*/, + LockContext& lockCtx, const std::string& saveAsPath, + const std::string& saveAsFilename, + const bool isRename) override; /// Total time taken for making WOPI calls during load std::chrono::duration<double> getWopiLoadDuration() const { return _wopiLoadDuration; } @@ -585,11 +608,19 @@ public: // Implement me // WebDAVFileInfo getWebDAVFileInfo(const Poco::URI& uriPublic); - bool updateLockState(const Authorization &, LockContext &, bool) override { return true; } + bool updateLockState(const Authorization&, const std::string&, LockContext&, bool) override + { + return true; + } - std::string loadStorageFileToLocal(const Authorization& auth, LockContext &lockCtx, const std::string& templateUri) override; + std::string loadStorageFileToLocal(const Authorization& auth, const std::string& /*cookies*/, + LockContext& lockCtx, + const std::string& templateUri) override; - SaveResult saveLocalFileToStorage(const Authorization& auth, LockContext &lockCtx, const std::string& saveAsPath, const std::string& saveAsFilename, const bool isRename) override; + SaveResult saveLocalFileToStorage(const Authorization& auth, const std::string& /*cookies*/, + LockContext& lockCtx, const std::string& saveAsPath, + const std::string& saveAsFilename, + const bool isRename) override; private: std::unique_ptr<AuthBase> _authAgent; _______________________________________________ Libreoffice-commits mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits
