Makefile.am | 1 test/Makefile.am | 7 +- test/UnitOAuth.cpp | 115 +++++++++--------------------------------- test/UnitWOPI.cpp | 129 ++++++++++++++++++++++++++++++++++++++++++++++++ test/WopiTestServer.hpp | 129 ++++++++++++++++++++++++++++++++++++++++++++++++ wsd/ClientSession.cpp | 20 +++++-- wsd/DocumentBroker.cpp | 3 + wsd/Storage.cpp | 1 wsd/Storage.hpp | 9 ++- wsd/reference.txt | 11 ++++ 10 files changed, 328 insertions(+), 97 deletions(-)
New commits: commit 67ebb9a48ed3131749858b070ec4d3c81906530a Author: Andras Timar <andras.ti...@collabora.com> Date: Wed Sep 27 15:55:44 2017 +0200 add test/WopiTestServer.hpp to Makefile.am Change-Id: I9a4b10f0d1f12ed85d31f3dbbc1deb747ff35a2d diff --git a/Makefile.am b/Makefile.am index f83fb463..5e0d64b2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -187,6 +187,7 @@ noinst_HEADERS = $(wsd_headers) $(shared_headers) $(kit_headers) \ bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h \ bundled/include/LibreOfficeKit/LibreOfficeKitInit.h \ bundled/include/LibreOfficeKit/LibreOfficeKitTypes.h \ + test/WopiTestServer.hpp \ test/countloolkits.hpp \ test/test.hpp \ test/helpers.hpp commit 3141cfc99ba2e2fb3878e95373a2fa48a76205a5 Author: Jan Holesovsky <ke...@collabora.com> Date: Wed Sep 27 14:13:43 2017 +0200 PutFile ext: X-LOOL-WOPI-IsModifiedByUser unit test. Change-Id: I0b1ffc74dbbc771f0dcb68f87d46af3ba469ae9e Reviewed-on: https://gerrit.libreoffice.org/42855 Reviewed-by: pranavk <pran...@collabora.co.uk> Tested-by: pranavk <pran...@collabora.co.uk> diff --git a/test/Makefile.am b/test/Makefile.am index 57094967..7ee4bcf2 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -16,7 +16,8 @@ noinst_LTLIBRARIES = \ unit-timeout.la unit-prefork.la \ unit-storage.la unit-client.la \ unit-admin.la unit-tilecache.la \ - unit-fuzz.la unit-oob.la unit-oauth.la + unit-fuzz.la unit-oob.la unit-oauth.la \ + unit-wopi.la MAGIC_TO_FORCE_SHLIB_CREATION = -rpath /dummy AM_LDFLAGS = -pthread -module $(MAGIC_TO_FORCE_SHLIB_CREATION) $(ZLIB_LIBS) @@ -77,6 +78,8 @@ unit_storage_la_SOURCES = UnitStorage.cpp unit_tilecache_la_SOURCES = UnitTileCache.cpp unit_oauth_la_SOURCES = UnitOAuth.cpp unit_oauth_la_LIBADD = $(CPPUNIT_LIBS) +unit_wopi_la_SOURCES = UnitWOPI.cpp +unit_wopi_la_LIBADD = $(CPPUNIT_LIBS) if HAVE_LO_PATH SYSTEM_STAMP = @SYSTEMPLATE_PATH@/system_stamp @@ -90,7 +93,7 @@ check-local: ./run_unit.sh --log-file test.log --trs-file test.trs # FIXME 2: unit-oob.la fails with symbol undefined: # UnitWSD::testHandleRequest(UnitWSD::TestRequest, UnitHTTPServerRequest&, UnitHTTPServerResponse&) , -TESTS = unit-prefork.la unit-tilecache.la unit-timeout.la unit-oauth.la +TESTS = unit-prefork.la unit-tilecache.la unit-timeout.la unit-oauth.la unit-wopi.la # TESTS = unit-client.la # TESTS += unit-admin.la # TESTS += unit-storage.la diff --git a/test/UnitOAuth.cpp b/test/UnitOAuth.cpp index baf05c0e..dfba4aa1 100644 --- a/test/UnitOAuth.cpp +++ b/test/UnitOAuth.cpp @@ -26,7 +26,7 @@ class UnitOAuth : public WopiTestServer { LoadToken, // loading the document with Bearer token LoadHeader, // loading the document with Basic auth - Polling // let the loading progress, and when it succeeds, finish + Finish // assert all went fine and finish } _phase; bool _finishedToken; @@ -84,9 +84,9 @@ public: } } - bool wopiServerFinish() override + void assertPutFileRequest(const Poco::Net::HTTPRequest& /*request*/) override { - return _finishedToken && _finishedHeader; + // nothing to assert } void invokeTest() override @@ -116,12 +116,13 @@ public: if (_phase == Phase::LoadToken) _phase = Phase::LoadHeader; else - _phase = Phase::Polling; + _phase = Phase::Finish; break; } - case Phase::Polling: + case Phase::Finish: { - // let handleHttpRequest() perform the checks... + CPPUNIT_ASSERT(_finishedToken && _finishedHeader); + exitTest(TestResult::Ok); break; } } diff --git a/test/UnitWOPI.cpp b/test/UnitWOPI.cpp new file mode 100644 index 00000000..fade8d0f --- /dev/null +++ b/test/UnitWOPI.cpp @@ -0,0 +1,129 @@ +/* -*- 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 "WopiTestServer.hpp" +#include "Log.hpp" +#include "Unit.hpp" +#include "UnitHTTP.hpp" +#include "helpers.hpp" +#include <Poco/Net/HTTPRequest.h> +#include <Poco/Util/LayeredConfiguration.h> + +class UnitWOPI : public WopiTestServer +{ + enum class Phase + { + LoadAndSave, + Modify, + SaveModified, + Finish + } _phase; + + enum class SavingPhase + { + Unmodified, + Modified + } _savingPhase; + + bool _finishedSaveUnmodified; + bool _finishedSaveModified; + + std::unique_ptr<UnitWebSocket> _ws; + +public: + UnitWOPI() : + _phase(Phase::LoadAndSave), + _finishedSaveUnmodified(false), + _finishedSaveModified(false) + { + } + + void assertCheckFileInfoRequest(const Poco::Net::HTTPRequest& /*request*/) override + { + // nothing to assert in CheckFileInfo + } + + void assertGetFileRequest(const Poco::Net::HTTPRequest& /*request*/) override + { + // nothing to assert in GetFile + } + + void assertPutFileRequest(const Poco::Net::HTTPRequest& request) override + { + if (_savingPhase == SavingPhase::Unmodified) + { + CPPUNIT_ASSERT_EQUAL(std::string("false"), request.get("X-LOOL-WOPI-IsModifiedByUser")); + _finishedSaveUnmodified = true; + } + else if (_savingPhase == SavingPhase::Modified) + { + CPPUNIT_ASSERT_EQUAL(std::string("true"), request.get("X-LOOL-WOPI-IsModifiedByUser")); + _finishedSaveModified = true; + } + } + + void invokeTest() override + { + constexpr char testName[] = "UnitWOPI"; + + switch (_phase) + { + case Phase::LoadAndSave: + { + Poco::URI wopiURL(helpers::getTestServerURI() + "/wopi/files/0?access_token=anything"); + std::string wopiSrc; + Poco::URI::encode(wopiURL.toString(), ":/?", wopiSrc); + Poco::URI loolUri(helpers::getTestServerURI()); + + LOG_INF("Connecting to the fake WOPI server: /lool/" << wopiSrc << "/ws"); + + _ws.reset(new UnitWebSocket("/lool/" + wopiSrc + "/ws")); + assert(_ws.get()); + + helpers::sendTextFrame(*_ws->getLOOLWebSocket(), "load url=" + wopiSrc, testName); + helpers::sendTextFrame(*_ws->getLOOLWebSocket(), "save dontTerminateEdit=1 dontSaveIfUnmodified=0", testName); + + _phase = Phase::Modify; + _savingPhase = SavingPhase::Unmodified; + break; + } + case Phase::Modify: + { + helpers::sendTextFrame(*_ws->getLOOLWebSocket(), "key type=input char=97 key=0", testName); + helpers::sendTextFrame(*_ws->getLOOLWebSocket(), "key type=up char=0 key=512", testName); + + _phase = Phase::SaveModified; + break; + } + case Phase::SaveModified: + { + helpers::sendTextFrame(*_ws->getLOOLWebSocket(), "save dontTerminateEdit=0 dontSaveIfUnmodified=0", testName); + + _phase = Phase::Finish; + _savingPhase = SavingPhase::Modified; + break; + } + case Phase::Finish: + { + CPPUNIT_ASSERT(_finishedSaveUnmodified && _finishedSaveModified); + exitTest(TestResult::Ok); + break; + } + } + } +}; + +UnitBase *unit_create_wsd(void) +{ + return new UnitWOPI(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/test/WopiTestServer.hpp b/test/WopiTestServer.hpp index 63cf4c7f..5ac79e81 100644 --- a/test/WopiTestServer.hpp +++ b/test/WopiTestServer.hpp @@ -29,7 +29,7 @@ public: virtual void assertGetFileRequest(const Poco::Net::HTTPRequest& request) = 0; - virtual bool wopiServerFinish() = 0; + virtual void assertPutFileRequest(const Poco::Net::HTTPRequest& request) = 0; protected: /// Here we act as a WOPI server, so that we have a server that responds to @@ -81,7 +81,7 @@ protected: return true; } // GetFile - else if (uriReq.getPath() == "/wopi/files/0/contents" || uriReq.getPath() == "/wopi/files/1/contents") + else if (request.getMethod() == "GET" && (uriReq.getPath() == "/wopi/files/0/contents" || uriReq.getPath() == "/wopi/files/1/contents")) { LOG_INF("Fake wopi host request, handling GetFile: " << uriReq.getPath()); @@ -101,8 +101,22 @@ protected: socket->send(oss.str()); socket->shutdown(); - if (wopiServerFinish()) - exitTest(TestResult::Ok); + return true; + } + else if (request.getMethod() == "POST" && (uriReq.getPath() == "/wopi/files/0/contents" || uriReq.getPath() == "/wopi/files/1/contents")) + { + LOG_INF("Fake wopi host request, handling PutFile: " << uriReq.getPath()); + + assertPutFileRequest(request); + + std::ostringstream oss; + oss << "HTTP/1.1 200 OK\r\n" + << "Last-Modified: " << Poco::DateTimeFormatter::format(Poco::Timestamp(), Poco::DateTimeFormat::HTTP_FORMAT) << "\r\n" + << "User-Agent: " << WOPI_AGENT_STRING << "\r\n" + << "\r\n"; + + socket->send(oss.str()); + socket->shutdown(); return true; } commit a711d5b60c65042585ff85b574a526bac4ae0647 Author: Jan Holesovsky <ke...@collabora.com> Date: Tue Sep 26 16:12:58 2017 +0200 Separate the fake wopi server to an own class. Change-Id: Ibb1b06c491be0065aa12a05a43959165d6c86398 Reviewed-on: https://gerrit.libreoffice.org/42853 Reviewed-by: pranavk <pran...@collabora.co.uk> Tested-by: pranavk <pran...@collabora.co.uk> diff --git a/test/UnitOAuth.cpp b/test/UnitOAuth.cpp index 7a52c1ee..baf05c0e 100644 --- a/test/UnitOAuth.cpp +++ b/test/UnitOAuth.cpp @@ -9,24 +9,18 @@ #include "config.h" -//#include "Exceptions.hpp" +#include "WopiTestServer.hpp" #include "Log.hpp" #include "Unit.hpp" #include "UnitHTTP.hpp" #include "helpers.hpp" -#include <Poco/JSON/Object.h> -#include <Poco/LocalDateTime.h> -#include <Poco/DateTimeFormat.h> -#include <Poco/DateTimeFormatter.h> #include <Poco/Net/HTTPRequest.h> #include <Poco/Net/OAuth20Credentials.h> #include <Poco/Util/LayeredConfiguration.h> -using Poco::DateTimeFormatter; -using Poco::DateTimeFormat; using Poco::Net::OAuth20Credentials; -class UnitOAuth : public UnitWSD +class UnitOAuth : public WopiTestServer { enum class Phase { @@ -46,6 +40,7 @@ public: { } + /// The actual assert of the authentication. void assertRequest(const Poco::Net::HTTPRequest& request, int fileIndex) { // check that the request contains the Authorization: header @@ -68,91 +63,30 @@ public: } } - /// Here we act as a WOPI server, so that we have a server that responds to - /// the wopi requests without additional expensive setup. - virtual bool handleHttpRequest(const Poco::Net::HTTPRequest& request, std::shared_ptr<StreamSocket>& socket) override + void assertCheckFileInfoRequest(const Poco::Net::HTTPRequest& request) override { - static const std::string hello("Hello, world"); - - Poco::URI uriReq(request.getURI()); - LOG_INF("Fake wopi host request: " << uriReq.toString()); + std::string path = Poco::URI(request.getURI()).getPath(); + assertRequest(request, (path == "/wopi/files/0")? 0: 1); + } - // CheckFileInfo - if (uriReq.getPath() == "/wopi/files/0" || uriReq.getPath() == "/wopi/files/1") + void assertGetFileRequest(const Poco::Net::HTTPRequest& request) override + { + std::string path = Poco::URI(request.getURI()).getPath(); + if (path == "/wopi/files/0/contents") { - LOG_INF("Fake wopi host request, handling CheckFileInfo: " << uriReq.getPath()); - - assertRequest(request, (uriReq.getPath() == "/wopi/files/0")? 0: 1); - - Poco::LocalDateTime now; - Poco::JSON::Object::Ptr fileInfo = new Poco::JSON::Object(); - fileInfo->set("BaseFileName", "hello.txt"); - fileInfo->set("Size", hello.size()); - fileInfo->set("Version", "1.0"); - fileInfo->set("OwnerId", "test"); - fileInfo->set("UserId", "test"); - fileInfo->set("UserFriendlyName", "test"); - fileInfo->set("UserCanWrite", "true"); - fileInfo->set("PostMessageOrigin", "localhost"); - fileInfo->set("LastModifiedTime", DateTimeFormatter::format(now, DateTimeFormat::ISO8601_FORMAT)); - - std::ostringstream jsonStream; - fileInfo->stringify(jsonStream); - std::string responseString = jsonStream.str(); - - const std::string mimeType = "application/json; charset=utf-8"; - - std::ostringstream oss; - oss << "HTTP/1.1 200 OK\r\n" - << "Last-Modified: " << Poco::DateTimeFormatter::format(Poco::Timestamp(), Poco::DateTimeFormat::HTTP_FORMAT) << "\r\n" - << "User-Agent: " << WOPI_AGENT_STRING << "\r\n" - << "Content-Length: " << responseString.size() << "\r\n" - << "Content-Type: " << mimeType << "\r\n" - << "\r\n" - << responseString; - - socket->send(oss.str()); - socket->shutdown(); - - return true; + assertRequest(request, 0); + _finishedToken = true; } - // GetFile - else if (uriReq.getPath() == "/wopi/files/0/contents" || uriReq.getPath() == "/wopi/files/1/contents") + else { - LOG_INF("Fake wopi host request, handling GetFile: " << uriReq.getPath()); - - if (uriReq.getPath() == "/wopi/files/0/contents") - { - assertRequest(request, 0); - _finishedToken = true; - } - else - { - assertRequest(request, 1); - _finishedHeader = true; - } - - const std::string mimeType = "text/plain; charset=utf-8"; - - std::ostringstream oss; - oss << "HTTP/1.1 200 OK\r\n" - << "Last-Modified: " << Poco::DateTimeFormatter::format(Poco::Timestamp(), Poco::DateTimeFormat::HTTP_FORMAT) << "\r\n" - << "User-Agent: " << WOPI_AGENT_STRING << "\r\n" - << "Content-Length: " << hello.size() << "\r\n" - << "Content-Type: " << mimeType << "\r\n" - << "\r\n" - << hello; - - socket->send(oss.str()); - socket->shutdown(); - - if (_finishedToken && _finishedHeader) - exitTest(TestResult::Ok); - - return true; + assertRequest(request, 1); + _finishedHeader = true; } + } - return false; + bool wopiServerFinish() override + { + return _finishedToken && _finishedHeader; } void invokeTest() override diff --git a/test/WopiTestServer.hpp b/test/WopiTestServer.hpp new file mode 100644 index 00000000..63cf4c7f --- /dev/null +++ b/test/WopiTestServer.hpp @@ -0,0 +1,115 @@ +/* -*- 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 "Log.hpp" +#include "Unit.hpp" +#include "UnitHTTP.hpp" +#include <Poco/DateTimeFormat.h> +#include <Poco/DateTimeFormatter.h> +#include <Poco/JSON/Object.h> +#include <Poco/Net/HTTPRequest.h> +#include <Poco/URI.h> + +class WopiTestServer : public UnitWSD +{ +public: + WopiTestServer() : UnitWSD() + { + } + + virtual void assertCheckFileInfoRequest(const Poco::Net::HTTPRequest& request) = 0; + + virtual void assertGetFileRequest(const Poco::Net::HTTPRequest& request) = 0; + + virtual bool wopiServerFinish() = 0; + +protected: + /// Here we act as a WOPI server, so that we have a server that responds to + /// the wopi requests without additional expensive setup. + virtual bool handleHttpRequest(const Poco::Net::HTTPRequest& request, std::shared_ptr<StreamSocket>& socket) override + { + static const std::string hello("Hello, world"); + + Poco::URI uriReq(request.getURI()); + LOG_INF("Fake wopi host request: " << uriReq.toString()); + + // CheckFileInfo + if (uriReq.getPath() == "/wopi/files/0" || uriReq.getPath() == "/wopi/files/1") + { + LOG_INF("Fake wopi host request, handling CheckFileInfo: " << uriReq.getPath()); + + assertCheckFileInfoRequest(request); + + Poco::LocalDateTime now; + Poco::JSON::Object::Ptr fileInfo = new Poco::JSON::Object(); + fileInfo->set("BaseFileName", "hello.txt"); + fileInfo->set("Size", hello.size()); + fileInfo->set("Version", "1.0"); + fileInfo->set("OwnerId", "test"); + fileInfo->set("UserId", "test"); + fileInfo->set("UserFriendlyName", "test"); + fileInfo->set("UserCanWrite", "true"); + fileInfo->set("PostMessageOrigin", "localhost"); + fileInfo->set("LastModifiedTime", Poco::DateTimeFormatter::format(now, Poco::DateTimeFormat::ISO8601_FORMAT)); + + std::ostringstream jsonStream; + fileInfo->stringify(jsonStream); + std::string responseString = jsonStream.str(); + + const std::string mimeType = "application/json; charset=utf-8"; + + std::ostringstream oss; + oss << "HTTP/1.1 200 OK\r\n" + << "Last-Modified: " << Poco::DateTimeFormatter::format(Poco::Timestamp(), Poco::DateTimeFormat::HTTP_FORMAT) << "\r\n" + << "User-Agent: " << WOPI_AGENT_STRING << "\r\n" + << "Content-Length: " << responseString.size() << "\r\n" + << "Content-Type: " << mimeType << "\r\n" + << "\r\n" + << responseString; + + socket->send(oss.str()); + socket->shutdown(); + + return true; + } + // GetFile + else if (uriReq.getPath() == "/wopi/files/0/contents" || uriReq.getPath() == "/wopi/files/1/contents") + { + LOG_INF("Fake wopi host request, handling GetFile: " << uriReq.getPath()); + + assertGetFileRequest(request); + + const std::string mimeType = "text/plain; charset=utf-8"; + + std::ostringstream oss; + oss << "HTTP/1.1 200 OK\r\n" + << "Last-Modified: " << Poco::DateTimeFormatter::format(Poco::Timestamp(), Poco::DateTimeFormat::HTTP_FORMAT) << "\r\n" + << "User-Agent: " << WOPI_AGENT_STRING << "\r\n" + << "Content-Length: " << hello.size() << "\r\n" + << "Content-Type: " << mimeType << "\r\n" + << "\r\n" + << hello; + + socket->send(oss.str()); + socket->shutdown(); + + if (wopiServerFinish()) + exitTest(TestResult::Ok); + + return true; + } + + return false; + } + +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ commit f658067eaa49b55b634615da157a107582e6fbd0 Author: Jan Holesovsky <ke...@collabora.com> Date: Wed Sep 27 10:15:30 2017 +0200 Don't crash when the parameters are missing. Change-Id: I96ace7ad7757e7e0c74dd9f361c78ecff6171a96 Reviewed-on: https://gerrit.libreoffice.org/42854 Reviewed-by: pranavk <pran...@collabora.co.uk> Tested-by: pranavk <pran...@collabora.co.uk> diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp index c96a8542..cc152e25 100644 --- a/wsd/ClientSession.cpp +++ b/wsd/ClientSession.cpp @@ -66,7 +66,7 @@ bool ClientSession::_handleInput(const char *buffer, int length) { LOG_TRC(getName() << ": handling incoming [" << getAbbreviatedMessage(buffer, length) << "]."); const std::string firstLine = getFirstLine(buffer, length); - const auto tokens = LOOLProtocol::tokenize(firstLine.data(), firstLine.size()); + const std::vector<std::string> tokens = LOOLProtocol::tokenize(firstLine.data(), firstLine.size()); auto docBroker = getDocumentBroker(); if (!docBroker) @@ -85,6 +85,12 @@ bool ClientSession::_handleInput(const char *buffer, int length) } if (tokens[0] == "loolclient") { + if (tokens.size() < 1) + { + sendTextFrame("error: cmd=loolclient kind=badprotocolversion"); + return false; + } + const auto versionTuple = ParseVersion(tokens[1]); if (std::get<0>(versionTuple) != ProtocolMajorVersionNumber || std::get<1>(versionTuple) != ProtocolMinorVersionNumber) @@ -225,14 +231,20 @@ bool ClientSession::_handleInput(const char *buffer, int length) { int dontTerminateEdit = 1; int dontSaveIfUnmodified = 1; - getTokenInteger(tokens[1], "dontTerminateEdit", dontTerminateEdit); - getTokenInteger(tokens[2], "dontSaveIfUnmodified", dontSaveIfUnmodified); + if (tokens.size() > 1) + getTokenInteger(tokens[1], "dontTerminateEdit", dontTerminateEdit); + + if (tokens.size() > 2) + getTokenInteger(tokens[2], "dontSaveIfUnmodified", dontSaveIfUnmodified); + docBroker->sendUnoSave(getId(), dontTerminateEdit != 0, dontSaveIfUnmodified != 0); } else if (tokens[0] == "savetostorage") { int force = 0; - getTokenInteger(tokens[1], "force", force); + if (tokens.size() > 1) + getTokenInteger(tokens[1], "force", force); + if (docBroker->saveToStorage(getId(), true, "" /* This is irrelevant when success is true*/, true)) { docBroker->broadcastMessage("commandresult: { \"command\": \"savetostorage\", \"success\": true }"); commit 5c604e9f789fa7d7bf077e35aa7b2c909ac9c9d1 Author: Jan Holesovsky <ke...@collabora.com> Date: Mon Sep 25 19:16:48 2017 +0200 PutFile ext: X-LOOL-WOPI-IsModifiedByUser header to indicate modifications. Change-Id: I5d69903211045969d678df695717eae7452e7f04 Reviewed-on: https://gerrit.libreoffice.org/42852 Reviewed-by: pranavk <pran...@collabora.co.uk> Tested-by: pranavk <pran...@collabora.co.uk> diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp index 91d61c57..ed76b2a6 100644 --- a/wsd/DocumentBroker.cpp +++ b/wsd/DocumentBroker.cpp @@ -854,6 +854,9 @@ bool DocumentBroker::sendUnoSave(const std::string& sessionId, bool dontTerminat // arguments end oss << "}"; + assert(_storage); + _storage->setUserModified(_isModified); + const auto saveArgs = oss.str(); LOG_TRC(".uno:Save arguments: " << saveArgs); const auto command = "uno .uno:Save " + saveArgs; diff --git a/wsd/Storage.cpp b/wsd/Storage.cpp index c6b4339f..9003c75c 100644 --- a/wsd/Storage.cpp +++ b/wsd/Storage.cpp @@ -764,6 +764,7 @@ StorageBase::SaveResult WopiStorage::saveLocalFileToStorage(const Authorization& Poco::DateTimeFormatter::format(Poco::DateTime(_fileInfo._modifiedTime), Poco::DateTimeFormat::ISO8601_FRAC_FORMAT)); } + request.set("X-LOOL-WOPI-IsModifiedByUser", _isUserModified? "true": "false"); request.setContentType("application/octet-stream"); request.setContentLength(size); diff --git a/wsd/Storage.hpp b/wsd/Storage.hpp index 389c228d..31ab6c2f 100644 --- a/wsd/Storage.hpp +++ b/wsd/Storage.hpp @@ -78,7 +78,8 @@ public: _jailPath(jailPath), _fileInfo("", "lool", Poco::Timestamp::fromEpochTime(0), 0), _isLoaded(false), - _forceSave(false) + _forceSave(false), + _isUserModified(false) { LOG_DBG("Storage ctor: " << uri.toString()); } @@ -96,6 +97,9 @@ public: /// even if document turned out to be changed in storage void forceSave() { _forceSave = true; } + /// To be able to set the WOPI extension header appropriately. + void setUserModified(bool isUserModified) { _isUserModified = isUserModified; } + /// Returns the basic information about the file. const FileInfo& getFileInfo() const { return _fileInfo; } @@ -131,6 +135,9 @@ protected: bool _isLoaded; bool _forceSave; + /// The document has been modified by the user. + bool _isUserModified; + static bool FilesystemEnabled; static bool WopiEnabled; /// Allowed/denied WOPI hosts, if any and if WOPI is enabled. diff --git a/wsd/reference.txt b/wsd/reference.txt index 3d633f99..90eedfc3 100644 --- a/wsd/reference.txt +++ b/wsd/reference.txt @@ -92,3 +92,14 @@ The 'access_header' can be eg. of a form This header is then used in all the WOPI calls like PutFile, GetFile or CheckFileInfo, allowing Basic authentication to work. + +PutFile headers +--------------- + +PutFile additionally indicates whether the user has modified the document +before the save, or if they just pressed the Save button without any +modification. The following header: + + X-LOOL-WOPI-IsModifiedByUser + +will have the value 'true' or 'false' accordingly. _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits