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

Reply via email to