[Libreoffice-commits] online.git: Branch 'private/tml/ios-gen2' - 17 commits - android/lib android/README common/Authorization.hpp common/Protocol.cpp common/Protocol.hpp common/Util.cpp common/Util.hpp configure.ac ios/GEN2.txt loleaflet/css loleaflet/images loleaflet/reference.html loleaflet/src Makefile.am test/Makefile.am test/WopiProofTests.cpp wsd/Admin.cpp wsd/ClientSession.cpp wsd/ClientSession.hpp wsd/DocumentBroker.cpp wsd/DocumentBroker.hpp wsd/LOOLWSD.cpp wsd/LOOLWSD.hpp wsd/Storage.cpp wsd/Storage.hpp

Szymon KÅ‚os (via logerrit) logerrit at kemper.freedesktop.org
Thu Jul 2 13:21:33 UTC 2020


 Makefile.am                                                          |   28 +--
 android/README                                                       |   16 +
 android/lib/build.gradle                                             |   11 -
 android/lib/src/main/cpp/CMakeLists.txt.in                           |    5 
 android/lib/src/main/java/org/libreoffice/androidlib/LOActivity.java |   50 -----
 common/Authorization.hpp                                             |    2 
 common/Protocol.cpp                                                  |   42 ----
 common/Protocol.hpp                                                  |   23 ++
 common/Util.cpp                                                      |   19 ++
 common/Util.hpp                                                      |   91 +++++++++-
 configure.ac                                                         |   61 +++++-
 ios/GEN2.txt                                                         |    4 
 loleaflet/css/mobilewizard.css                                       |    1 
 loleaflet/images/note.svg                                            |   63 ++++++
 loleaflet/reference.html                                             |   15 -
 loleaflet/src/control/Control.DocumentNameInput.js                   |    2 
 loleaflet/src/control/Control.JSDialogBuilder.js                     |    1 
 loleaflet/src/control/Control.Toolbar.js                             |    7 
 loleaflet/src/core/Socket.js                                         |    4 
 loleaflet/src/map/Map.js                                             |    4 
 test/Makefile.am                                                     |    2 
 test/WopiProofTests.cpp                                              |   15 +
 wsd/Admin.cpp                                                        |   26 +-
 wsd/ClientSession.cpp                                                |   14 -
 wsd/ClientSession.hpp                                                |    1 
 wsd/DocumentBroker.cpp                                               |   23 ++
 wsd/DocumentBroker.hpp                                               |    8 
 wsd/LOOLWSD.cpp                                                      |   24 --
 wsd/LOOLWSD.hpp                                                      |    3 
 wsd/Storage.cpp                                                      |    1 
 wsd/Storage.hpp                                                      |   11 +
 31 files changed, 380 insertions(+), 197 deletions(-)

New commits:
commit 89afba185a3464110cf73e3f3f04db1cb41d15cd
Author:     Szymon Kłos <szymon.klos at collabora.com>
AuthorDate: Thu Jul 2 08:58:53 2020 +0200
Commit:     Tor Lillqvist <tml at collabora.com>
CommitDate: Thu Jul 2 16:20:47 2020 +0300

    jsdialog: use listbox for font size selector
    
    with newer core version it changed a name
    
    Change-Id: I3804f9f6e1acfc96123e4376aeb3b040deeebe4c
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/97707
    Tested-by: Jenkins
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Szymon Kłos <szymon.klos at collabora.com>

diff --git a/loleaflet/src/control/Control.JSDialogBuilder.js b/loleaflet/src/control/Control.JSDialogBuilder.js
index ba6ee8fce..41ad87376 100644
--- a/loleaflet/src/control/Control.JSDialogBuilder.js
+++ b/loleaflet/src/control/Control.JSDialogBuilder.js
@@ -1414,6 +1414,7 @@ L.Control.JSDialogBuilder = L.Control.extend({
 		if (data.id === 'applystyle' ||
 			data.id === 'fontnamecombobox' ||
 			data.id === 'fontsizecombobox' ||
+			data.id === 'fontsize' ||
 			data.id === 'FontBox') {
 			builder._listboxControl(parentContainer, data, builder);
 		} else if (data.id === 'searchterm' ||
commit db914a859fafccd255db075856d2d2560e8ec0b5
Author:     Tor Lillqvist <tml at collabora.com>
AuthorDate: Thu Jul 2 15:39:12 2020 +0300
Commit:     Tor Lillqvist <tml at collabora.com>
CommitDate: Thu Jul 2 16:20:39 2020 +0300

    Update the first step
    
    Won't bother with renaming the Objective-C classes I think, just to
    keep changes minimal.
    
    Change-Id: I683479fc32275a5ee2a1bc9c28275bc887c42474

diff --git a/ios/GEN2.txt b/ios/GEN2.txt
index 4ab55b990..e6b1b1204 100644
--- a/ios/GEN2.txt
+++ b/ios/GEN2.txt
@@ -28,8 +28,8 @@ Ideas:
 
 Steps:
 
-- Rename the Objective-C files/classes to have a CO prefix. Reduces
-  risk of confusion with C++ classes.
+- Re-factorings that don't affect any working of current Online or
+  apps, but which will make the iOS app re-plumbing easier.
 
 - ...
 
commit 9af9e38f28ab7acee5efbeb884d3d8845aaced86
Author:     Mike Kaganski <mike.kaganski at collabora.com>
AuthorDate: Wed Jul 1 13:38:06 2020 +0300
Commit:     Tor Lillqvist <tml at collabora.com>
CommitDate: Thu Jul 2 16:20:32 2020 +0300

    Only lock documents in editing sessions
    
    Change-Id: I97753541a944bb299b04c032790d6af7a9ee0f63
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/97609
    Tested-by: Jenkins
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Mike Kaganski <mike.kaganski at collabora.com>

diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 7753d87ac..e64c0f8e1 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -786,7 +786,9 @@ bool DocumentBroker::load(const std::shared_ptr<ClientSession>& session, const s
         std::string localPath = _storage->loadStorageFileToLocal(
             session->getAuthorization(), session->getCookies(), *_lockCtx, templateSource);
 
-        if (!_storage->updateLockState(session->getAuthorization(), session->getCookies(), *_lockCtx, true))
+        // Only lock the document on storage for editing sessions
+        if (!session->isReadOnly() &&
+            !_storage->updateLockState(session->getAuthorization(), session->getCookies(), *_lockCtx, true))
             LOG_ERR("Failed to lock!");
 
 #if !MOBILEAPP
commit bfd8760ca39285e68f1b2cf39ae19bf3e21fc6f3
Author:     Mike Kaganski <mike.kaganski at collabora.com>
AuthorDate: Thu Jul 2 10:54:59 2020 +0300
Commit:     Tor Lillqvist <tml at collabora.com>
CommitDate: Thu Jul 2 16:20:25 2020 +0300

    tdf#134041: add a unit test
    
    Change-Id: Ic040adb7a2a73635041146f6d6425db1b02c2e61
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/97721
    Tested-by: Jenkins
    Tested-by: Mike Kaganski <mike.kaganski at collabora.com>
    Reviewed-by: Michael Meeks <michael.meeks at collabora.com>

diff --git a/test/WopiProofTests.cpp b/test/WopiProofTests.cpp
index 3e4aa5418..cfa1f57dc 100644
--- a/test/WopiProofTests.cpp
+++ b/test/WopiProofTests.cpp
@@ -137,6 +137,21 @@ void WopiProofTests::testOurProof()
 
     int64_t ticks = std::stoll(timestamp.c_str(), nullptr, 10);
     verifySignature(access_token, uri, ticks, modulus, exponent, proof);
+
+    // tdf#134041: test another data
+
+    access_token = "~!@#$%^&*()_+`1234567890-=";
+    uri = "https://user2@short.com:12345/blah?query_string=bar";
+    pairs = gen.GetProofHeaders(access_token, uri);
+    len = pairs.size();
+    LOK_ASSERT_EQUAL(2, len);
+    LOK_ASSERT_EQUAL(pairs[0].first, std::string("X-WOPI-TimeStamp"));
+    timestamp = pairs[0].second;
+    LOK_ASSERT_EQUAL(pairs[1].first, std::string("X-WOPI-Proof"));
+    proof = pairs[1].second;
+
+    ticks = std::stoll(timestamp.c_str(), nullptr, 10);
+    verifySignature(access_token, uri, ticks, modulus, exponent, proof);
 }
 
 CPPUNIT_TEST_SUITE_REGISTRATION(WopiProofTests);
commit c73241ac178a9ed07b71658d691a091784110bb8
Author:     Jan Holesovsky <kendy at collabora.com>
AuthorDate: Thu Jul 2 09:28:31 2020 +0200
Commit:     Tor Lillqvist <tml at collabora.com>
CommitDate: Thu Jul 2 16:20:18 2020 +0300

    android: Default to building just the simple case: armeabi-v7a...
    
    ... unless more builddirs are provided in --with-lo-builddir, separated
    by colons.
    
    Change-Id: I49946cd932ec22804ecb51aba86f3dae2aba05f5
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/97672
    Tested-by: Jenkins
    Reviewed-by: Jan Holesovsky <kendy at collabora.com>

diff --git a/configure.ac b/configure.ac
index ff78941ef..2fd403c52 100644
--- a/configure.ac
+++ b/configure.ac
@@ -368,7 +368,7 @@ fi
 # to the Mac.
 # Android: We need these to setup the CMakeLists.txt properly.
 LOBUILDDIR=
-ANDROID_ABI=
+ANDROID_ABI="armeabi-v7a"
 LOBUILDDIR_ARM64_V8A=
 LOBUILDDIR_X86=
 LOBUILDDIR_X86_64=
@@ -385,10 +385,12 @@ CORE_VERSION_HASH=""
 if test \( "$enable_iosapp" = "yes" -a `uname -s` = "Darwin" \) -o \( "$enable_androidapp" = "yes" \); then
    if test "$enable_androidapp" = "yes" ; then
       AC_MSG_CHECKING([for Android ABI to build for])
-      if test -z "$with_android_abi" ; then
-         ANDROID_ABI="armeabi-v7a arm64-v8a x86 x86_64"
-      else
+      if test -n "$with_android_abi" ; then
          ANDROID_ABI=`echo $with_android_abi | sed 's/:/ /g'`
+      else
+         if echo "$with_lo_builddir" | grep -qs ':' ; then
+            ANDROID_ABI="armeabi-v7a arm64-v8a x86 x86_64"
+         fi
       fi
       AC_MSG_RESULT([$ANDROID_ABI])
    fi
commit 2992e4ffb0a73f5d6d647c72ec44fc9134746c20
Author:     Jan Holesovsky <kendy at collabora.com>
AuthorDate: Thu Jul 2 10:15:20 2020 +0200
Commit:     Tor Lillqvist <tml at collabora.com>
CommitDate: Thu Jul 2 16:20:10 2020 +0300

    android: Remove unneeded dependency + add some comments.
    
    Change-Id: Idbc271a398f6f0c037d478bda5ee0b149ca4f24f
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/97730
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Tested-by: Jenkins
    Reviewed-by: Jan Holesovsky <kendy at collabora.com>

diff --git a/android/lib/src/main/cpp/CMakeLists.txt.in b/android/lib/src/main/cpp/CMakeLists.txt.in
index 2e1a5fdb8..4c4e1344a 100644
--- a/android/lib/src/main/cpp/CMakeLists.txt.in
+++ b/android/lib/src/main/cpp/CMakeLists.txt.in
@@ -5,7 +5,6 @@ add_library(androidapp SHARED
             androidapp.cpp
             ../../../../../common/Authorization.cpp
             ../../../../../common/FileUtil.cpp
-            ../../../../../common/JailUtil.cpp
             ../../../../../common/Log.cpp
             ../../../../../common/MessageQueue.cpp
             ../../../../../common/Protocol.cpp
diff --git a/common/Authorization.hpp b/common/Authorization.hpp
index 14accb236..b92b0d565 100644
--- a/common/Authorization.hpp
+++ b/common/Authorization.hpp
@@ -16,7 +16,7 @@
 #include <Poco/Net/HTTPRequest.h>
 #include <Poco/URI.h>
 
-/// Class to keep the authorization data.
+/// Class to keep the authorization data, which can be either access_token or access_header.
 class Authorization
 {
 public:
diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp
index 8922b924e..cc153bdd8 100644
--- a/wsd/ClientSession.hpp
+++ b/wsd/ClientSession.hpp
@@ -226,6 +226,7 @@ private:
     /// URI with which client made request to us
     const Poco::URI _uriPublic;
 
+    /// Authorization data - either access_token or access_header.
     const Authorization _auth;
 
     /// The cookies we should pass on to the storage on saving.
commit 4781e351bc1bbc02c2995dd9d3d626a7b90819a3
Author:     Szymon Kłos <szymon.klos at collabora.com>
AuthorDate: Thu Jul 2 08:58:06 2020 +0200
Commit:     Tor Lillqvist <tml at collabora.com>
CommitDate: Thu Jul 2 16:20:03 2020 +0300

    mobilewizard: add bottom padding
    
    Change-Id: Ie1ae026a580c483f1a0a9cffffdd8eaac8679dca
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/97706
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Tested-by: Jenkins
    Reviewed-by: Szymon Kłos <szymon.klos at collabora.com>

diff --git a/loleaflet/css/mobilewizard.css b/loleaflet/css/mobilewizard.css
index 9cbad6825..00e915a6c 100644
--- a/loleaflet/css/mobilewizard.css
+++ b/loleaflet/css/mobilewizard.css
@@ -194,6 +194,7 @@ p.mobile-wizard.ui-combobox-text.selected {
 	top: 111px;
 	bottom: 0px;
 	width: 100%;
+	padding-bottom: 50px;
 }
 #mobile-wizard.funcwizard div#mobile-wizard-content.hideHelpBG {
 	background: none !important;
commit e17903bad0f8c26b7e600a7faaca884de9d2f26e
Author:     Tomaž Vajngerl <tomaz.vajngerl at collabora.co.uk>
AuthorDate: Thu Jul 2 10:22:24 2020 +0200
Commit:     Tor Lillqvist <tml at collabora.com>
CommitDate: Thu Jul 2 16:19:55 2020 +0300

    add missing note image
    
    Change-Id: Iebeac73a517d853c85d81856b9c64e18adc1e4ad

diff --git a/loleaflet/images/note.svg b/loleaflet/images/note.svg
new file mode 100644
index 000000000..2c7236a0c
--- /dev/null
+++ b/loleaflet/images/note.svg
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
+   sodipodi:docname="note.svg"
+   id="svg8"
+   version="1.1"
+   viewBox="0 0 32 32">
+  <metadata
+     id="metadata14">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs12" />
+  <sodipodi:namedview
+     inkscape:current-layer="svg8"
+     inkscape:window-maximized="0"
+     inkscape:window-y="23"
+     inkscape:window-x="26"
+     inkscape:cy="48.22105"
+     inkscape:cx="1.6214117"
+     inkscape:zoom="6.600599"
+     showgrid="false"
+     id="namedview10"
+     inkscape:window-height="1016"
+     inkscape:window-width="960"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0"
+     guidetolerance="10"
+     gridtolerance="10"
+     objecttolerance="10"
+     borderopacity="1"
+     bordercolor="#666666"
+     pagecolor="#ffffff" />
+  <g
+     style="stroke:#000000;stroke-opacity:1;fill:#eac282;fill-opacity:1"
+     id="g6"
+     fill-rule="evenodd">
+    <path
+       style="stroke:#000000;stroke-opacity:1;fill:#eac282;fill-opacity:1"
+       id="path2"
+       fill="#3a3a38"
+       d="m3.5 3a.50005.50005 0 0 0 -.5.5v19a.50005.50005 0 0 0 .5.5h4.5v5.5a.50005.50005 0 0 0 .8261719.378906l6.8593751-5.878906h12.814453a.50005.50005 0 0 0 .5-.5v-19a.50005.50005 0 0 0 -.5-.5z" />
+    <path
+       style="stroke:#000000;stroke-opacity:1;fill:#eac282;fill-opacity:1"
+       id="path4"
+       fill="#fafafa"
+       d="m4 4v18h4.5a.50005.50005 0 0 1 .5.5v4.912109l6.173828-5.291015a.50005.50005 0 0 1 .326172-.121094h12.5v-18z" />
+  </g>
+</svg>
commit f254196ee890421365c4d6b6af2299fc84e7ca1f
Author:     Jan Holesovsky <kendy at collabora.com>
AuthorDate: Wed Jul 1 21:05:42 2020 +0200
Commit:     Tor Lillqvist <tml at collabora.com>
CommitDate: Thu Jul 2 16:19:44 2020 +0300

    android: No need to copy fonts from system any more.
    
    After the update of fontconfig, the hack to not to use the Noto fonts is
    not needed any more, they load quickly now.
    
    Change-Id: Iebd2c944e9fcc04d6976002f24a3a47f0b49f2ad
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/97659
    Tested-by: Jenkins
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Jan Holesovsky <kendy at collabora.com>

diff --git a/android/lib/src/main/java/org/libreoffice/androidlib/LOActivity.java b/android/lib/src/main/java/org/libreoffice/androidlib/LOActivity.java
index 5074d637a..8465ff5e1 100644
--- a/android/lib/src/main/java/org/libreoffice/androidlib/LOActivity.java
+++ b/android/lib/src/main/java/org/libreoffice/androidlib/LOActivity.java
@@ -203,54 +203,6 @@ public class LOActivity extends AppCompatActivity {
         }
     }
 
-    /**
-     * Copies fonts except the NotoSans from the system to our location.
-     * This is necessary because the NotoSans is huge and fontconfig needs
-     * ages to parse them.
-     */
-    private static boolean copyFonts(String fromPath, String targetDir) {
-        try {
-            File target = new File(targetDir);
-            if (!target.exists())
-                target.mkdirs();
-
-            File from = new File(fromPath);
-            File[] files = from.listFiles();
-            for (File fontFile : files) {
-                String fontFileName = fontFile.getName();
-                if (!fontFileName.equals("Roboto-Regular.ttf")) {
-                    Log.i(TAG, "Ignored font file: " + fontFile);
-                    continue;
-                } else {
-                    Log.i(TAG, "Copying font file: " + fontFile);
-                }
-
-                // copy the font file over
-                InputStream in = new FileInputStream(fontFile);
-                try {
-                    OutputStream out = new FileOutputStream(targetDir + "/" + fontFile.getName());
-                    try {
-                        byte[] buffer = new byte[4096];
-                        int len;
-                        while ((len = in.read(buffer)) > 0) {
-                            out.write(buffer, 0, len);
-                        }
-                    } finally {
-                        out.close();
-                    }
-                } finally {
-                    in.close();
-                }
-            }
-        } catch (Exception e) {
-            e.printStackTrace();
-            Log.e(TAG, "copyFonts failed: " + e.getMessage());
-            return false;
-        }
-
-        return true;
-    }
-
     private Handler getMainHandler() {
         if (mMainHandler == null) {
             mMainHandler = new Handler(getMainLooper());
@@ -289,7 +241,7 @@ public class LOActivity extends AppCompatActivity {
             @Override
             protected Void doInBackground(Void... voids) {
                 // copy the new assets
-                if (copyFromAssets(getAssets(), "unpack", getApplicationInfo().dataDir) && copyFonts("/system/fonts", getApplicationInfo().dataDir + "/user/fonts")) {
+                if (copyFromAssets(getAssets(), "unpack", getApplicationInfo().dataDir)) {
                     sPrefs.edit().putString(ASSETS_EXTRACTED_GIT_COMMIT, BuildConfig.GIT_COMMIT).apply();
                 }
                 return null;
commit 53373f28044e630f71f8d742e081f5686b3f64a7
Author:     Jan Holesovsky <kendy at collabora.com>
AuthorDate: Wed Jul 1 20:41:41 2020 +0200
Commit:     Tor Lillqvist <tml at collabora.com>
CommitDate: Thu Jul 2 16:19:36 2020 +0300

    android: Use the system fonts.
    
    Now that we have upgraded the fontconfig to a version that can process
    the Noto fonts in reasonable time, use them.
    
    Change-Id: I7e46bef6827f5e257a0d583137aa9a26c2a22ed3
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/97658
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Tested-by: Jenkins
    Reviewed-by: Jan Holesovsky <kendy at collabora.com>

diff --git a/android/lib/build.gradle b/android/lib/build.gradle
index c3b3621b9..8882d8e5d 100644
--- a/android/lib/build.gradle
+++ b/android/lib/build.gradle
@@ -117,17 +117,6 @@ task copyUnpackAssets(type: Copy) {
             String line ->
                 line.replaceAll(
                     '@@APPLICATION_ID@@', new String("${liboApplicationId}")
-                ).replaceAll(
-                    // FIXME Avoid the Android system fonts for the moment,
-                    // the huge Noto Sans fonts have terrible impact on the 1st
-                    // start performance.
-                    // The real solution would be to either make fontconfig
-                    // faster, or at least find a way to avoid only the Noto
-                    // Sans, or present a progressbar or something.
-                    // For the moment, we just copy the Roboto font (needed
-                    // for the dialogs; see LOActivity.copyFonts()) and
-                    // remove the system fonts from the config.
-                    '<dir>/system/fonts</dir>', new String("")
                 )
         }
     }
commit 83c4f8786c9eb1451708ed3ff3166bcf6dbb1870
Author:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
AuthorDate: Thu Jun 25 21:53:26 2020 -0400
Commit:     Tor Lillqvist <tml at collabora.com>
CommitDate: Thu Jul 2 16:19:27 2020 +0300

    leaflet: set renameFilename before saving
    
    There is a race between the time we set the
    renameFilename value and the uno:Save response
    arrives. If renameFilename is not set by then
    we miss the opportunity to rename and instead
    simply end up saving the file.
    
    Change-Id: I8d7acbc95cef264de4385d506bfa34458ba80283
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/97189
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Tested-by: Jenkins
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>

diff --git a/loleaflet/src/control/Control.DocumentNameInput.js b/loleaflet/src/control/Control.DocumentNameInput.js
index cfc4e5998..3ad053094 100644
--- a/loleaflet/src/control/Control.DocumentNameInput.js
+++ b/loleaflet/src/control/Control.DocumentNameInput.js
@@ -27,8 +27,8 @@ L.Control.DocumentNameInput = L.Control.extend({
 						// same extension, just rename the file
 						// file name must be without the extension for rename
 						value = value.substr(0, value.lastIndexOf('.'));
+						this.map._renameFilename = value;
 						this.map.sendUnoCommand('.uno:Save');
-						this.map._RenameFile = value;
 					}
 				}
 			} else {
diff --git a/loleaflet/src/control/Control.Toolbar.js b/loleaflet/src/control/Control.Toolbar.js
index 1eccdce82..e8dce9bc3 100644
--- a/loleaflet/src/control/Control.Toolbar.js
+++ b/loleaflet/src/control/Control.Toolbar.js
@@ -924,9 +924,10 @@ function onCommandResult(e) {
 			map._everModified = true;
 
 			// document is saved for rename
-			if (map._RenameFile) {
-				map.renameFile(map._RenameFile);
-				map._RenameFile = '';
+			if (map._renameFilename) {
+				var renameFilename = map._renameFilename;
+				map._renameFilename = '';
+				map.renameFile(renameFilename);
 			}
 		}
 		var postMessageObj = {
diff --git a/loleaflet/src/map/Map.js b/loleaflet/src/map/Map.js
index 5f9882a53..285e4954d 100644
--- a/loleaflet/src/map/Map.js
+++ b/loleaflet/src/map/Map.js
@@ -259,8 +259,8 @@ L.Map = L.Evented.extend({
 		// This becomes true if document was ever modified by the user
 		this._everModified = false;
 
-		// This becomes new file name if document is renamed which used later on uno:Save result
-		this._RenameFile = '';
+		// This is the new file name, if the document is renamed, which is used on uno:Save's result.
+		this._renameFilename = '';
 
 		// Document is completely loaded or not
 		this._docLoaded = false;
commit e8f4add4e712f83ca7f7dba9404ee9b3da455ee8
Author:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
AuthorDate: Tue Jun 2 18:19:23 2020 -0400
Commit:     Tor Lillqvist <tml at collabora.com>
CommitDate: Thu Jul 2 16:19:19 2020 +0300

    wsd: move string-to-integer helper to Util
    
    Improved implementation.
    
    Change-Id: I0b426f8742c8b718f8c939d271f6645a8ed466d4
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/96374
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Tested-by: Jenkins
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>

diff --git a/common/Protocol.cpp b/common/Protocol.cpp
index 771b87b49..d02f2d340 100644
--- a/common/Protocol.cpp
+++ b/common/Protocol.cpp
@@ -48,48 +48,6 @@ namespace LOOLProtocol
         return std::make_tuple(major, minor, patch);
     }
 
-    bool stringToInteger(const std::string& input, int& value)
-    {
-        try
-        {
-            value = std::stoi(input);
-        }
-        catch (std::invalid_argument&)
-        {
-            return false;
-        }
-
-        return true;
-    }
-
-    bool stringToUInt32(const std::string& input, uint32_t& value)
-    {
-        try
-        {
-            value = std::stoul(input);
-        }
-        catch (std::invalid_argument&)
-        {
-            return false;
-        }
-
-        return true;
-    }
-
-    bool stringToUInt64(const std::string& input, uint64_t& value)
-    {
-        try
-        {
-            value = std::stoull(input);
-        }
-        catch (std::invalid_argument&)
-        {
-            return false;
-        }
-
-        return true;
-    }
-
     bool getTokenInteger(const std::string& token, const std::string& name, int& value)
     {
         if (token.size() > (name.size() + 1) &&
diff --git a/common/Protocol.hpp b/common/Protocol.hpp
index 5eb90941b..367d9ff27 100644
--- a/common/Protocol.hpp
+++ b/common/Protocol.hpp
@@ -41,9 +41,26 @@ namespace LOOLProtocol
     // Negative numbers for error.
     std::tuple<int, int, std::string> ParseVersion(const std::string& version);
 
-    bool stringToInteger(const std::string& input, int& value);
-    bool stringToUInt32(const std::string& input, uint32_t& value);
-    bool stringToUInt64(const std::string& input, uint64_t& value);
+    inline bool stringToInteger(const std::string& input, int& value)
+    {
+        bool res;
+        std::tie(value, res) = Util::i32FromString(input);
+        return res;
+    }
+
+    inline bool stringToUInt32(const std::string& input, uint32_t& value)
+    {
+        bool res;
+        std::tie(value, res) = Util::i32FromString(input);
+        return res;
+    }
+
+    inline bool stringToUInt64(const std::string& input, uint64_t& value)
+    {
+        bool res;
+        std::tie(value, res) = Util::u64FromString(input);
+        return res;
+    }
 
     inline
     bool parseNameValuePair(const std::string& token, std::string& name, std::string& value, const char delim = '=')
diff --git a/common/Util.hpp b/common/Util.hpp
index 28ec5154a..c634801fb 100644
--- a/common/Util.hpp
+++ b/common/Util.hpp
@@ -13,6 +13,7 @@
 #include <cerrno>
 #include <cinttypes>
 #include <cstddef>
+#include <cstdint>
 #include <cstring>
 #include <atomic>
 #include <functional>
@@ -22,6 +23,7 @@
 #include <sstream>
 #include <string>
 #include <map>
+#include <utility>
 #include <inttypes.h>
 
 #include <memory.h>
@@ -1134,10 +1136,86 @@ int main(int argc, char**argv)
      */
     std::map<std::string, std::string> stringVectorToMap(std::vector<std::string> sVector, const char delimiter);
 
-    #if !MOBILEAPP
-        // If OS is not mobile, it must be Linux.
-        std::string getLinuxVersion();
-    #endif
+#if !MOBILEAPP
+    // If OS is not mobile, it must be Linux.
+    std::string getLinuxVersion();
+#endif
+
+    /// Convert a string to 32-bit signed int.
+    /// Returs the parsed value and a boolean indiciating success or failure.
+    inline std::pair<std::int32_t, bool> i32FromString(const std::string& input)
+    {
+        const char* str = input.data();
+        char* endptr = nullptr;
+        const auto value = std::strtol(str, &endptr, 10);
+        return std::make_pair(value, endptr > str && errno != ERANGE);
+    }
+
+    /// Convert a string to 32-bit signed int. On failure, returns the default
+    /// value, and sets the bool to false (to signify that parsing had failed).
+    inline std::pair<std::int32_t, bool> i32FromString(const std::string& input,
+                                                       const std::int32_t def)
+    {
+        const auto pair = i32FromString(input);
+        return pair.second ? pair : std::make_pair(def, false);
+    }
+
+    /// Convert a string to 32-bit unsigned int.
+    /// Returs the parsed value and a boolean indiciating success or failure.
+    inline std::pair<std::uint32_t, bool> u32FromString(const std::string& input)
+    {
+        const char* str = input.data();
+        char* endptr = nullptr;
+        const auto value = std::strtoul(str, &endptr, 10);
+        return std::make_pair(value, endptr > str && errno != ERANGE);
+    }
+
+    /// Convert a string to 32-bit usigned int. On failure, returns the default
+    /// value, and sets the bool to false (to signify that parsing had failed).
+    inline std::pair<std::uint32_t, bool> u32FromString(const std::string& input,
+                                                        const std::uint32_t def)
+    {
+        const auto pair = u32FromString(input);
+        return pair.second ? pair : std::make_pair(def, false);
+    }
+
+    /// Convert a string to 64-bit signed int.
+    /// Returs the parsed value and a boolean indiciating success or failure.
+    inline std::pair<std::int64_t, bool> i64FromString(const std::string& input)
+    {
+        const char* str = input.data();
+        char* endptr = nullptr;
+        const auto value = std::strtol(str, &endptr, 10);
+        return std::make_pair(value, endptr > str && errno != ERANGE);
+    }
+
+    /// Convert a string to 64-bit signed int. On failure, returns the default
+    /// value, and sets the bool to false (to signify that parsing had failed).
+    inline std::pair<std::int64_t, bool> i64FromString(const std::string& input,
+                                                       const std::int64_t def)
+    {
+        const auto pair = i64FromString(input);
+        return pair.second ? pair : std::make_pair(def, false);
+    }
+
+    /// Convert a string to 64-bit unsigned int.
+    /// Returs the parsed value and a boolean indiciating success or failure.
+    inline std::pair<std::uint64_t, bool> u64FromString(const std::string& input)
+    {
+        const char* str = input.data();
+        char* endptr = nullptr;
+        const auto value = std::strtoul(str, &endptr, 10);
+        return std::make_pair(value, endptr > str && errno != ERANGE);
+    }
+
+    /// Convert a string to 64-bit usigned int. On failure, returns the default
+    /// value, and sets the bool to false (to signify that parsing had failed).
+    inline std::pair<std::uint64_t, bool> u64FromString(const std::string& input,
+                                                        const std::uint64_t def)
+    {
+        const auto pair = u64FromString(input);
+        return pair.second ? pair : std::make_pair(def, false);
+    }
 
 } // end namespace Util
 
commit 140f80b2a2887c302460de68a26becd47f13ae9e
Author:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
AuthorDate: Sat Jun 27 16:56:51 2020 -0400
Commit:     Tor Lillqvist <tml at collabora.com>
CommitDate: Thu Jul 2 16:18:50 2020 +0300

    wsd: admin: don't poll rapidly when cleanup is disabled
    
    When per_document.cleanup is disabled, the time
    between the last cleanup (which never happened)
    grows indefinitely, which results in minimal
    polling time intervals. This wastes valuable
    cpu cycles unnecessarily.
    
    When cleanup is disabled, there is no need to
    calculate the next cleanup time. The maximum
    is reasonable (although it should really be
    infinity).
    
    Change-Id: I71d065441c4c2ff96fe31e6a45a5ecfdd2f85d49
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/97471
    Tested-by: Jenkins
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>

diff --git a/wsd/Admin.cpp b/wsd/Admin.cpp
index c2f665cd4..a9b87cba4 100644
--- a/wsd/Admin.cpp
+++ b/wsd/Admin.cpp
@@ -415,14 +415,12 @@ Admin::~Admin()
 
 void Admin::pollingThread()
 {
-    std::chrono::steady_clock::time_point lastCPU, lastMem, lastNet, lastCleanup;
-
     _model.setThreadOwner(std::this_thread::get_id());
 
-    lastCPU = std::chrono::steady_clock::now();
-    lastMem = lastCPU;
-    lastNet = lastCPU;
-    lastCleanup = lastCPU;
+    std::chrono::steady_clock::time_point lastCPU = std::chrono::steady_clock::now();
+    std::chrono::steady_clock::time_point lastMem = lastCPU;
+    std::chrono::steady_clock::time_point lastNet = lastCPU;
+    std::chrono::steady_clock::time_point lastCleanup = lastCPU;
 
     while (!isStop() && !SigUtil::getTerminationFlag() && !SigUtil::getShutdownRequestFlag())
     {
@@ -484,14 +482,15 @@ void Admin::pollingThread()
             lastNet = now;
         }
 
-        int cleanupWait = _cleanupIntervalMs -
-                std::chrono::duration_cast<std::chrono::milliseconds>(now - lastCleanup).count();
-        if (cleanupWait <= MinStatsIntervalMs / 2) // Close enough
+        int cleanupWait = _cleanupIntervalMs;
+        if (_defDocProcSettings.getCleanupSettings().getEnable())
         {
-            if (_defDocProcSettings.getCleanupSettings().getEnable())
+            cleanupWait
+                -= std::chrono::duration_cast<std::chrono::milliseconds>(now - lastCleanup).count();
+            if (cleanupWait <= MinStatsIntervalMs / 2) // Close enough
             {
                 cleanupResourceConsumingDocs();
-                
+
                 cleanupWait += _cleanupIntervalMs;
                 lastCleanup = now;
             }
@@ -509,7 +508,8 @@ void Admin::pollingThread()
         }
 
         // Handle websockets & other work.
-        const int timeout = capAndRoundInterval(std::min(std::min(std::min(cpuWait, memWait), netWait), cleanupWait));
+        const int timeout = capAndRoundInterval(
+            std::min(std::min(std::min(cpuWait, memWait), netWait), cleanupWait));
         LOG_TRC("Admin poll for " << timeout << "ms.");
         poll(timeout * 1000); // continue with ms for admin, settings etc.
     }
commit eaf15c98efd4ee7acb3016806c7577cc8434de62
Author:     Ashod Nakashian <ashod.nakashian at collabora.co.uk>
AuthorDate: Wed Jul 1 13:06:46 2020 -0400
Commit:     Tor Lillqvist <tml at collabora.com>
CommitDate: Thu Jul 2 16:18:41 2020 +0300

    make: improve cleanup dependency graph
    
    Change-Id: I8a7edd3b49a272cb7bd8bff4d91b189a5856c5c8
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/97647
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
    Tested-by: Jenkins
    Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>

diff --git a/Makefile.am b/Makefile.am
index 0eba81cf5..074f05376 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -329,14 +329,25 @@ SYSTEM_STAMP = @SYSTEMPLATE_PATH@/system_stamp
 CAPABILITIES = $(if @ENABLE_SETCAP@,true,false)
 RUN_GDB = $(if $(GDB_FRONTEND),$(GDB_FRONTEND),gdb --tui --args)
 
-$(SYSTEM_STAMP) : ${top_srcdir}/loolwsd-systemplate-setup
-	if test -s ./loolwsd; then ./loolwsd --cleanup; fi
+# Add caps to the binaries that need them.
+caps_bins: loolforkit loolmount
+if ENABLE_SETCAP
+	    sudo @SETCAP@ cap_fowner,cap_mknod,cap_sys_chroot=ep loolforkit
+	    sudo @SETCAP@ cap_sys_admin=ep loolmount
+else
+	    echo "Skipping capability setting"
+endif
+
+# Build loolwsd and loolmount first, so we can cleanup before updating
+# the systemplate directory, which we can't rm if it's mounted.
+$(SYSTEM_STAMP): ${top_srcdir}/loolwsd-systemplate-setup loolwsd caps_bins
+	$(CLEANUP_COMMAND)
 	if test "z at SYSTEMPLATE_PATH@" != "z"; then rm -rf "@SYSTEMPLATE_PATH@"; fi
 	${top_srcdir}/loolwsd-systemplate-setup "@SYSTEMPLATE_PATH@" "@LO_PATH@" && touch $@
 
- at JAILS_PATH@ :
-	mkdir -p $@
+ at JAILS_PATH@:
 	$(CLEANUP_COMMAND)
+	mkdir -p $@
 
 clean-local:
 	$(CLEANUP_COMMAND)
@@ -464,16 +475,7 @@ endif
 # installing the RPM or Debian package.
 .PHONY: caps_bins
 
-caps_bins: loolforkit loolmount
-if ENABLE_SETCAP
-	    sudo @SETCAP@ cap_fowner,cap_mknod,cap_sys_chroot=ep loolforkit
-	    sudo @SETCAP@ cap_sys_admin=ep loolmount
-else
-	    echo "Skipping capability setting"
-endif
-
 all-local: loolwsd caps_bins @JAILS_PATH@ $(SYSTEM_STAMP)
-	$(CLEANUP_COMMAND)
 
 # just run the build without any tests
 build-nocheck: all-am
diff --git a/test/Makefile.am b/test/Makefile.am
index c12038b0d..327832f79 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -309,3 +309,5 @@ all-local: unittest
 	@echo
 	@fc-cache "@LO_PATH@"/share/fonts/truetype
 	@UNITTEST=1 ${top_builddir}/test/unittest
+	echo "Done test all-local"
+	$(CLEANUP_COMMAND)
commit 417ccb6923f82d48e549a3c4be916ecc9f781926
Author:     Samuel Mehrbrodt <Samuel.Mehrbrodt at cib.de>
AuthorDate: Wed Jul 1 12:46:58 2020 +0200
Commit:     Tor Lillqvist <tml at collabora.com>
CommitDate: Thu Jul 2 16:18:31 2020 +0300

    tdf#131123 Report back real save result
    
    665b1629de30a4a402c6b10dd542de158db1f428 was not correct, as it reported back
    the save result of the internal save (which usually succeeds).
    Instead we want to know the save result of the remote storage (WOPI/Webdav).
    So report that back instead.
    
    Change-Id: Iaaa42b8c817a19c2c77935a6f81c1951fdf2216c
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/97637
    Tested-by: Jenkins
    Reviewed-by: Samuel Mehrbrodt <Samuel.Mehrbrodt at cib.de>

diff --git a/loleaflet/reference.html b/loleaflet/reference.html
index b52d34688..99cae5ec6 100644
--- a/loleaflet/reference.html
+++ b/loleaflet/reference.html
@@ -3040,18 +3040,21 @@ Editor to WOPI host
 		<td><code>
 		    <nobr>success: <boolean></nobr>
 		    <nobr>result: <string></nobr>
+		    <nobr>errorMsg: <string></nobr>
 		</code></td>
 		<td>Acknowledgement when save finishes.<br/>
+		<i>This response is only emitted if <code>Notify</code> parameter
+		is mentioned by <code>Action_Save</code> PostMessage API.</i>
+		<br/>
 		<code>success</code> tells if LOOL was able to save the document
-		successfully. When this is false, then another
-		parameter, <code>result</code> is present which contains the
-		reason that document was not saved.
+		successfully.<br/>
+		<code>result</code> contains the reason the document was not saved.<br/>
 		In case, document was not saved because it was not modified,
 		then this parameter contains the string 'unmodified'. In this
 		case, WOPI hosts can be sure that there are no changes pending
-		in the document to be saved to the storage.
-		This response is only emitted if <code>Notify</code> parameter
-		is mentioned by <code>Action_Save</code> PostMessage API.
+		in the document to be saved to the storage.<br/>
+		<code>errorMsg</code> contains a detailed error message in case saving failed.
+		Probably it will contain the error message returned from the WOPI/Webdav host.
 		</td>
 	</tr>
 	<tr>
diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index 5a5568424..c3798a774 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -365,7 +365,9 @@ L.Socket = L.Class.extend({
 				}
 
 				var postMessageObj = {
-					success: commandresult['success']
+					success: commandresult['success'],
+					result: commandresult['result'],
+					errorMsg: commandresult['errorMsg']
 				};
 				this._map.fire('postMessage', {msgId: 'Action_Save_Resp', args: postMessageObj});
 			}
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 859052ea4..b313742ec 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -553,11 +553,8 @@ bool ClientSession::_handleInput(const char *buffer, int length)
 
             constexpr bool isAutosave = false;
             constexpr bool isExitSave = false;
-            bool result = docBroker->sendUnoSave(getId(), dontTerminateEdit != 0, dontSaveIfUnmodified != 0,
+            docBroker->sendUnoSave(getId(), dontTerminateEdit != 0, dontSaveIfUnmodified != 0,
                                     isAutosave, isExitSave, extendedData);
-            std::string resultstr = result ? "true" : "false";
-            std::string msg = "commandresult: { \"command\": \"save\", \"success\": " + resultstr + " }";
-            docBroker->broadcastMessage(msg);
         }
     }
     else if (tokens.equals(0, "savetostorage"))
@@ -566,10 +563,7 @@ bool ClientSession::_handleInput(const char *buffer, int length)
         if (tokens.size() > 1)
             getTokenInteger(tokens[1], "force", force);
 
-        bool result = docBroker->saveToStorage(getId(), true, "" /* This is irrelevant when success is true*/, true);
-        std::string resultstr = result ? "true" : "false";
-        std::string msg = "commandresult: { \"command\": \"savetostorage\", \"success\": " + resultstr + " }";
-        docBroker->broadcastMessage(msg);
+        docBroker->saveToStorage(getId(), true, "" /* This is irrelevant when success is true*/, true);
     }
     else if (tokens.equals(0, "clientvisiblearea"))
     {
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 8a41a6dd9..7753d87ac 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -969,6 +969,7 @@ bool DocumentBroker::saveToStorageInternal(const std::string& sessionId, bool su
         LOG_DBG("Save skipped as document [" << _docKey << "] was not modified.");
         _lastSaveTime = std::chrono::steady_clock::now();
         _poll->wakeup();
+        broadcastSaveResult(true, "unmodified");
         return true;
     }
 
@@ -978,6 +979,7 @@ bool DocumentBroker::saveToStorageInternal(const std::string& sessionId, bool su
         LOG_ERR("Session with sessionId ["
                 << sessionId << "] not found while storing document docKey [" << _docKey
                 << "]. The document will not be uploaded to storage at this time.");
+        broadcastSaveResult(false, "Session not found");
         return false;
     }
 
@@ -986,6 +988,7 @@ bool DocumentBroker::saveToStorageInternal(const std::string& sessionId, bool su
     {
         LOG_ERR("Cannot store docKey [" << _docKey << "] as .uno:Save has failed in LOK.");
         it->second->sendTextFrameAndLogError("error: cmd=storage kind=savefailed");
+        broadcastSaveResult(false, "Could not save document in LibreOfficeKit");
         return false;
     }
 
@@ -1021,6 +1024,7 @@ bool DocumentBroker::saveToStorageInternal(const std::string& sessionId, bool su
         LOG_DBG("Skipping unnecessary saving to URI [" << uriAnonym << "] with docKey [" << _docKey <<
                 "]. File last modified " << timeInSec.count() << " seconds ago, timestamp unchanged.");
         _poll->wakeup();
+        broadcastSaveResult(true, "unmodified");
         return true;
     }
 
@@ -1103,12 +1107,14 @@ bool DocumentBroker::saveToStorageInternal(const std::string& sessionId, bool su
         {
             sessionIt.second->sendTextFrameAndLogError("error: cmd=storage kind=savediskfull");
         }
+        broadcastSaveResult(false, "Disk full", storageSaveResult.getErrorMsg());
     }
     else if (storageSaveResult.getResult() == StorageBase::SaveResult::UNAUTHORIZED)
     {
         LOG_ERR("Cannot save docKey [" << _docKey << "] to storage URI [" << uriAnonym <<
                 "]. Invalid or expired access token. Notifying client.");
         it->second->sendTextFrameAndLogError("error: cmd=storage kind=saveunauthorized");
+        broadcastSaveResult(false, "Invalid or expired access token", storageSaveResult.getErrorMsg());
     }
     else if (storageSaveResult.getResult() == StorageBase::SaveResult::FAILED)
     {
@@ -1117,6 +1123,7 @@ bool DocumentBroker::saveToStorageInternal(const std::string& sessionId, bool su
         std::ostringstream oss;
         oss << "error: cmd=storage kind=" << (isRename ? "renamefailed" : "savefailed");
         it->second->sendTextFrame(oss.str());
+        broadcastSaveResult(false, "Save failed", storageSaveResult.getErrorMsg());
     }
     else if (storageSaveResult.getResult() == StorageBase::SaveResult::DOC_CHANGED
              || storageSaveResult.getResult() == StorageBase::SaveResult::CONFLICT)
@@ -1127,11 +1134,23 @@ bool DocumentBroker::saveToStorageInternal(const std::string& sessionId, bool su
             = isModified() ? "error: cmd=storage kind=documentconflict" : "close: documentconflict";
 
         broadcastMessage(message);
+        broadcastSaveResult(false, "Conflict: Document changed in storage", storageSaveResult.getErrorMsg());
     }
 
     return false;
 }
 
+void DocumentBroker::broadcastSaveResult(bool success, const std::string& result, const std::string& errorMsg)
+{
+    std::string resultstr = success ? "true" : "false";
+    // Some sane limit, otherwise we get problems transfering this to the client with large strings (can be a whole webpage)
+    std::string errorMsgFormatted = errorMsg.substr(0, 1000);
+    // Replace reserverd characters
+    errorMsgFormatted = Poco::translate(errorMsgFormatted, "\"", "'");
+    broadcastMessage("commandresult: { \"command\": \"save\", \"success\": " + resultstr +
+                     ", \"result\": \"" + result + "\", \"errorMsg\": \"" + errorMsgFormatted  + "\"}");
+}
+
 void DocumentBroker::setLoaded()
 {
     if (!_isLoaded)
diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp
index 4d09dff32..ce5bf7b7d 100644
--- a/wsd/DocumentBroker.hpp
+++ b/wsd/DocumentBroker.hpp
@@ -335,6 +335,14 @@ private:
                                const std::string& saveAsFilename = std::string(),
                                const bool isRename = false, const bool force = false);
 
+    /**
+     * Report back the save result to PostMessage users (Action_Save_Resp)
+     * @param success: Whether saving was successful
+     * @param result: Short message why saving was (not) successful
+     * @param errorMsg: Long error msg (Error message from WOPI host if any)
+     */
+    void broadcastSaveResult(bool success, const std::string& result = "", const std::string& errorMsg = "");
+
     /// True iff a save is in progress (requested but not completed).
     bool isSaving() const { return _lastSaveResponseTime < _lastSaveRequestTime; }
 
diff --git a/wsd/Storage.cpp b/wsd/Storage.cpp
index 0d616f23c..e15f1abcb 100644
--- a/wsd/Storage.cpp
+++ b/wsd/Storage.cpp
@@ -1048,6 +1048,7 @@ WopiStorage::saveLocalFileToStorage(const Authorization& auth, const std::string
         std::ostringstream oss;
         Poco::StreamCopier::copyStream(rs, oss);
         std::string responseString = oss.str();
+        saveResult.setErrorMsg(responseString);
 
         const std::string wopiLog(isSaveAs ? "WOPI::PutRelativeFile" : (isRename ? "WOPI::RenameFile":"WOPI::PutFile"));
 
diff --git a/wsd/Storage.hpp b/wsd/Storage.hpp
index 8f07b9229..d087fb174 100644
--- a/wsd/Storage.hpp
+++ b/wsd/Storage.hpp
@@ -132,10 +132,21 @@ public:
             return _saveAsUrl;
         }
 
+        void setErrorMsg(const std::string &msg)
+        {
+            _errorMsg = msg;
+        }
+
+        const std::string &getErrorMsg() const
+        {
+            return _errorMsg;
+        }
+
     private:
         Result _result;
         std::string _saveAsName;
         std::string _saveAsUrl;
+        std::string _errorMsg;
     };
 
     enum class LOOLStatusCode
commit c43910880afeb1192a7e6ac3a93afd456b97f2b2
Author:     Jan Holesovsky <kendy at collabora.com>
AuthorDate: Wed Jul 1 11:10:01 2020 +0200
Commit:     Tor Lillqvist <tml at collabora.com>
CommitDate: Thu Jul 2 16:18:18 2020 +0300

    android: Add support for x86 ABI too.
    
    Turns out that the ChromeOS uses the x86 Android runtime, not x86-64.
    
    Change-Id: Ic3b6f7a65d35d2298daa731f46e57068eaf2583d
    Reviewed-on: https://gerrit.libreoffice.org/c/online/+/97607
    Tested-by: Jenkins
    Reviewed-by: Jan Holesovsky <kendy at collabora.com>

diff --git a/android/README b/android/README
index 7223e121e..5cffa4b4f 100644
--- a/android/README
+++ b/android/README
@@ -68,6 +68,22 @@ build the native parts on Windows.
   # install
   PATH="$PATH":~/Android/Sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/bin make -j8 ANDROID_ABI=arm64-v8a CC=aarch64-linux-android21-clang CXX=aarch64-linux-android21-clang++ SYSLIBS=-static-libstdc++ install INSTALLDIR=/opt/poco-android-64bit
 
+* Poco for x86 (if you want to add the support for that into the APK too):
+
+  # checkout the 1.10.1 in yet another location
+  git clone https://github.com/pocoproject/poco poco-android-x86
+  cd poco-android-x86
+  git checkout -b poco-1.10.1 origin/poco-1.10.1
+
+  # configure
+  ./configure --config=Android --no-samples --no-tests --omit=Crypto,NetSSL_OpenSSL,Zip,Data,Data/SQLite,Data/ODBC,Data/MySQL,MongoDB,PDF,CppParser,PageCompiler,JWT
+
+  # build
+  PATH="$PATH":~/Android/Sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/bin make -j8 ANDROID_ABI=x86 CC=i686-linux-android21-clang CXX=i686-linux-android21-clang++ SYSLIBS=-static-libstdc++
+
+  # install
+  PATH="$PATH":~/Android/Sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/bin make -j8 ANDROID_ABI=x86 CC=i686-linux-android21-clang CXX=i686-linux-android21-clang++ SYSLIBS=-static-libstdc++ install INSTALLDIR=/opt/poco-android-x86
+
 * Poco for x86_64 (if you want to add the support for that into the APK too):
 
   # checkout the 1.10.1 in yet another location
diff --git a/android/lib/src/main/cpp/CMakeLists.txt.in b/android/lib/src/main/cpp/CMakeLists.txt.in
index 53a3fafd7..2e1a5fdb8 100644
--- a/android/lib/src/main/cpp/CMakeLists.txt.in
+++ b/android/lib/src/main/cpp/CMakeLists.txt.in
@@ -37,6 +37,10 @@ elseif(${ANDROID_ABI} STREQUAL "arm64-v8a")
     set(LOBUILDDIR_ABI @LOBUILDDIR_ARM64_V8A@)
     set(POCOINCLUDE_ABI @POCOINCLUDE_ARM64_V8A@)
     set(POCOLIB_ABI @POCOLIB_ARM64_V8A@)
+elseif(${ANDROID_ABI} STREQUAL "x86")
+    set(LOBUILDDIR_ABI @LOBUILDDIR_X86@)
+    set(POCOINCLUDE_ABI @POCOINCLUDE_X86@)
+    set(POCOLIB_ABI @POCOLIB_X86@)
 elseif(${ANDROID_ABI} STREQUAL "x86_64")
     set(LOBUILDDIR_ABI @LOBUILDDIR_X86_64@)
     set(POCOINCLUDE_ABI @POCOINCLUDE_X86_64@)
diff --git a/configure.ac b/configure.ac
index 7a8177e91..ff78941ef 100644
--- a/configure.ac
+++ b/configure.ac
@@ -168,12 +168,14 @@ AC_ARG_WITH(android-package-versioncode,
 
 AC_ARG_WITH(android-abi,
             AS_HELP_STRING([--with-android-abi=x86_64],
-                           [Allows specification of a concrete ABI that is to be built for.  By default, builds for all the 3
-                            supported ABIs at the same time: armeabi-v7a, arm64-v8a and x86_64.
+                           [Allows specification of a concrete ABI that is to be built for, defaults to armeabi-v7a
+                            (when only one build dir is provided in --with-lo-builddir) or to all 4 supported ABIs at
+                            the same time (when there are more builddirs provided in --with-lo-builddir, separated
+                            by colons).  The supported ABIs are: armeabi-v7a, arm64-v8a, x86 and x86_64.
                             Please note that you need to specify the parameters for --with-lo-builddir,
-                            --with-poco-includes and --with-poco-libs in the order of armeabi-v7a:arm64-v8a:x86_64.  For
-                            example, when you use --with-android-abi=x86_64,
-                            you have to specify --with-lo-builddir=::/path/to/x86-64/builddir]),
+                            --with-poco-includes and --with-poco-libs in the order of armeabi-v7a:arm64-v8a:x86:x86_64.
+                            For example, when you use --with-android-abi=x86_64,
+                            you have to specify --with-lo-builddir=:::/path/to/x86-64/builddir]),
 ,)
 
 AC_ARG_WITH([app-name],
@@ -368,12 +370,15 @@ fi
 LOBUILDDIR=
 ANDROID_ABI=
 LOBUILDDIR_ARM64_V8A=
+LOBUILDDIR_X86=
 LOBUILDDIR_X86_64=
 POCOINCLUDE=
 POCOINCLUDE_ARM64_V8A=
+POCOINCLUDE_X86=
 POCOINCLUDE_X86_64=
 POCOLIB=
 POCOLIB_ARM64_V8A=
+POCOLIB_X86=
 POCOLIB_X86_64=
 POCODEBUG=
 CORE_VERSION_HASH=""
@@ -381,7 +386,7 @@ if test \( "$enable_iosapp" = "yes" -a `uname -s` = "Darwin" \) -o \( "$enable_a
    if test "$enable_androidapp" = "yes" ; then
       AC_MSG_CHECKING([for Android ABI to build for])
       if test -z "$with_android_abi" ; then
-         ANDROID_ABI="armeabi-v7a arm64-v8a x86_64"
+         ANDROID_ABI="armeabi-v7a arm64-v8a x86 x86_64"
       else
          ANDROID_ABI=`echo $with_android_abi | sed 's/:/ /g'`
       fi
@@ -398,7 +403,8 @@ if test \( "$enable_iosapp" = "yes" -a `uname -s` = "Darwin" \) -o \( "$enable_a
       if echo "$LOBUILDDIR" | grep -qs ':' ; then
          LOBUILDDIR=`echo $with_lo_builddir | cut -d: -f1`
          LOBUILDDIR_ARM64_V8A=`echo $with_lo_builddir | cut -d: -f2`
-         LOBUILDDIR_X86_64=`echo $with_lo_builddir | cut -d: -f3`
+         LOBUILDDIR_X86=`echo $with_lo_builddir | cut -d: -f3`
+         LOBUILDDIR_X86_64=`echo $with_lo_builddir | cut -d: -f4`
       fi
    fi
 
@@ -420,6 +426,14 @@ if test \( "$enable_iosapp" = "yes" -a `uname -s` = "Darwin" \) -o \( "$enable_a
       fi
    fi
 
+   if test -n "$LOBUILDDIR_X86" ; then
+      if test -f "$LOBUILDDIR_X86/workdir/LinkTarget/StaticLibrary/liblibpng.a" ; then
+         AC_MSG_RESULT([$LOBUILDDIR_X86])
+      else
+         AC_MSG_ERROR([This is not a LibreOffice x86 core build directory: $LOBUILDDIR_X86])
+      fi
+   fi
+
    if test -n "$LOBUILDDIR_X86_64" ; then
       if test -f "$LOBUILDDIR_X86_64/workdir/LinkTarget/StaticLibrary/liblibpng.a" ; then
          AC_MSG_RESULT([$LOBUILDDIR_X86_64])
@@ -438,7 +452,8 @@ if test \( "$enable_iosapp" = "yes" -a `uname -s` = "Darwin" \) -o \( "$enable_a
       if echo "$POCOINCLUDE" | grep -qs ':' ; then
          POCOINCLUDE=`echo $with_poco_includes | cut -d: -f1`
          POCOINCLUDE_ARM64_V8A=`echo $with_poco_includes | cut -d: -f2`
-         POCOINCLUDE_X86_64=`echo $with_poco_includes | cut -d: -f3`
+         POCOINCLUDE_X86=`echo $with_poco_includes | cut -d: -f3`
+         POCOINCLUDE_X86_64=`echo $with_poco_includes | cut -d: -f4`
       fi
    fi
 
@@ -457,6 +472,14 @@ if test \( "$enable_iosapp" = "yes" -a `uname -s` = "Darwin" \) -o \( "$enable_a
       fi
    fi
 
+   if test -n "$POCOINCLUDE_X86" ; then
+      if test -f "$POCOINCLUDE_X86/Poco/Poco.h"; then
+         AC_MSG_RESULT([$POCOINCLUDE_X86])
+      else
+         AC_MSG_ERROR([This is not a Poco x86 include directory: $POCOINCLUDE_X86])
+      fi
+   fi
+
    if test -n "$POCOINCLUDE_X86_64" ; then
       if test -f "$POCOINCLUDE_X86_64/Poco/Poco.h"; then
          AC_MSG_RESULT([$POCOINCLUDE_X86_64])
@@ -475,7 +498,8 @@ if test \( "$enable_iosapp" = "yes" -a `uname -s` = "Darwin" \) -o \( "$enable_a
       if echo "$POCOLIB" | grep -qs ':' ; then
          POCOLIB=`echo $with_poco_libs | cut -d: -f1`
          POCOLIB_ARM64_V8A=`echo $with_poco_libs | cut -d: -f2`
-         POCOLIB_X86_64=`echo $with_poco_libs | cut -d: -f3`
+         POCOLIB_X86=`echo $with_poco_libs | cut -d: -f3`
+         POCOLIB_X86_64=`echo $with_poco_libs | cut -d: -f4`
       fi
    fi
 
@@ -494,6 +518,14 @@ if test \( "$enable_iosapp" = "yes" -a `uname -s` = "Darwin" \) -o \( "$enable_a
       fi
    fi
 
+   if test -n "$POCOLIB_X86" ; then
+      if test -f "$POCOLIB_X86/libPocoFoundation.a"; then
+         AC_MSG_RESULT([$POCOLIB_X86])
+      else
+         AC_MSG_ERROR([This is not a Poco x86 lib directory: $POCOLIB_X86])
+      fi
+   fi
+
    if test -n "$POCOLIB_X86_64" ; then
       if test -f "$POCOLIB_X86_64/libPocoFoundation.a"; then
          AC_MSG_RESULT([$POCOLIB_X86_64])
@@ -509,12 +541,15 @@ fi
 AC_SUBST(LOBUILDDIR)
 AC_SUBST(ANDROID_ABI)
 AC_SUBST(LOBUILDDIR_ARM64_V8A)
+AC_SUBST(LOBUILDDIR_X86)
 AC_SUBST(LOBUILDDIR_X86_64)
 AC_SUBST(POCOINCLUDE)
 AC_SUBST(POCOINCLUDE_ARM64_V8A)
+AC_SUBST(POCOINCLUDE_X86)
 AC_SUBST(POCOINCLUDE_X86_64)
 AC_SUBST(POCOLIB)
 AC_SUBST(POCOLIB_ARM64_V8A)
+AC_SUBST(POCOLIB_X86)
 AC_SUBST(POCOLIB_X86_64)
 AC_SUBST(POCODEBUG)
 AC_SUBST([CORE_VERSION_HASH])
commit 68479ccdb49bfc2285ef39738b8dbfedcd095d07
Author:     Tor Lillqvist <tml at collabora.com>
AuthorDate: Thu Jul 2 15:36:50 2020 +0300
Commit:     Tor Lillqvist <tml at collabora.com>
CommitDate: Thu Jul 2 16:03:48 2020 +0300

    Slight refactoring to make planned re-plumbing of iOS app easier
    
    Change-Id: I274cf167c6593de6f073301f7071f2173b40cbab

diff --git a/common/Util.cpp b/common/Util.cpp
index 16681cc56..e0ce00250 100644
--- a/common/Util.cpp
+++ b/common/Util.cpp
@@ -60,6 +60,7 @@
 
 #include "Common.hpp"
 #include "Log.hpp"
+#include "Protocol.hpp"
 #include "Util.hpp"
 
 using std::size_t;
@@ -640,6 +641,24 @@ namespace Util
         hash.resize(std::min(8, (int)hash.length()));
     }
 
+    std::string getProcessIdentifier()
+    {
+        static std::string id = Util::rng::getHexString(8);
+
+        return id;
+    }
+
+    std::string getVersionJSON()
+    {
+        std::string version, hash;
+        Util::getVersionInfo(version, hash);
+        return
+            "{ \"Version\":  \"" + version + "\", "
+            "\"Hash\":     \"" + hash + "\", "
+            "\"Protocol\": \"" + LOOLProtocol::GetProtocolVersion() + "\", "
+            "\"Id\":  \"" + Util::getProcessIdentifier() + "\" }";
+    }
+
     std::string UniqueId()
     {
         static std::atomic_int counter(0);
diff --git a/common/Util.hpp b/common/Util.hpp
index ba70cba47..28ec5154a 100644
--- a/common/Util.hpp
+++ b/common/Util.hpp
@@ -174,6 +174,11 @@ namespace Util
     /// Get version information
     void getVersionInfo(std::string& version, std::string& hash);
 
+    ///< A random hash that identifies the current process.
+    std::string getProcessIdentifier();
+
+    std::string getVersionJSON();
+
     /// Return a string that is unique across processes and calls.
     std::string UniqueId();
 
diff --git a/wsd/Admin.cpp b/wsd/Admin.cpp
index 996e7b75c..c2f665cd4 100644
--- a/wsd/Admin.cpp
+++ b/wsd/Admin.cpp
@@ -117,7 +117,7 @@ void AdminSocketHandler::handleMessage(const std::vector<char> &payload)
     else if (tokens.equals(0, "version"))
     {
         // Send LOOL version information
-        sendTextFrame("loolserver " + LOOLWSD::getVersionJSON());
+        sendTextFrame("loolserver " + Util::getVersionJSON());
         // Send LOKit version information
         sendTextFrame("lokitversion " + LOOLWSD::LOKitVersion);
     }
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 5f79a07fa..859052ea4 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -216,7 +216,7 @@ std::string ClientSession::getClipboardURI(bool encode)
 
     std::string meta = _serverURL.getSubURLForEndpoint(
         "/lool/clipboard?WOPISrc=" + encodedFrom +
-        "&ServerId=" + LOOLWSD::HostIdentifier +
+        "&ServerId=" + Util::getProcessIdentifier() +
         "&ViewId=" + std::to_string(getKitViewId()) +
         "&Tag=" + _clipboardKeys[0]);
 
@@ -363,7 +363,7 @@ bool ClientSession::_handleInput(const char *buffer, int length)
         }
 
         // Send LOOL version information
-        sendTextFrame("loolserver " + LOOLWSD::getVersionJSON());
+        sendTextFrame("loolserver " + Util::getVersionJSON());
         // Send LOKit version information
         sendTextFrame("lokitversion " + LOOLWSD::LOKitVersion);
 
diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp
index 6efb55655..c75bc37ef 100644
--- a/wsd/LOOLWSD.cpp
+++ b/wsd/LOOLWSD.cpp
@@ -720,7 +720,6 @@ std::string LOOLWSD::FileServerRoot;
 std::string LOOLWSD::WelcomeFilesRoot;
 std::string LOOLWSD::ServiceRoot;
 std::string LOOLWSD::LOKitVersion;
-std::string LOOLWSD::HostIdentifier;
 std::string LOOLWSD::ConfigFile = LOOLWSD_CONFIGDIR "/loolwsd.xml";
 std::string LOOLWSD::ConfigDir = LOOLWSD_CONFIGDIR "/conf.d";
 std::string LOOLWSD::LogLevel = "trace";
@@ -2685,7 +2684,7 @@ private:
             if (it != DocBrokers.end())
                 docBroker = it->second;
         }
-        if (docBroker && serverId == LOOLWSD::HostIdentifier)
+        if (docBroker && serverId == Util::getProcessIdentifier())
         {
             std::shared_ptr<std::string> data;
             DocumentBroker::ClipboardRequest type;
@@ -2729,8 +2728,8 @@ private:
                     " and broker: " << (docBroker ? "" : "not ") << "found");
 
             std::string errMsg;
-            if (serverId != LOOLWSD::HostIdentifier)
-                errMsg = "Cluster configuration error: mis-matching serverid " + serverId + " vs. " + LOOLWSD::HostIdentifier;
+            if (serverId != Util::getProcessIdentifier())
+                errMsg = "Cluster configuration error: mis-matching serverid " + serverId + " vs. " + Util::getProcessIdentifier();
             else
                 errMsg = "Empty clipboard item / session tag " + tag;
 
@@ -3542,7 +3541,7 @@ public:
            << "\n  WelcomeFilesRoot: " << LOOLWSD::WelcomeFilesRoot
            << "\n  ServiceRoot: " << LOOLWSD::ServiceRoot
            << "\n  LOKitVersion: " << LOOLWSD::LOKitVersion
-           << "\n  HostIdentifier: " << LOOLWSD::HostIdentifier
+           << "\n  HostIdentifier: " << Util::getProcessIdentifier()
            << "\n  ConfigFile: " << LOOLWSD::ConfigFile
            << "\n  ConfigDir: " << LOOLWSD::ConfigDir
            << "\n  LogLevel: " << LOOLWSD::LogLevel
@@ -3704,17 +3703,6 @@ private:
     }
 };
 
-std::string LOOLWSD::getVersionJSON()
-{
-    std::string version, hash;
-    Util::getVersionInfo(version, hash);
-    return
-        "{ \"Version\":  \"" + version + "\", "
-        "\"Hash\":     \"" + hash + "\", "
-        "\"Protocol\": \"" + GetProtocolVersion() + "\", "
-        "\"Id\":  \"" + HostIdentifier + "\" }";
-}
-
 static LOOLWSDServer srv;
 
 #if !MOBILEAPP
@@ -3740,11 +3728,9 @@ int LOOLWSD::innerMain()
     setenv("LD_BIND_NOW", "1", 1);
 #  endif
 
-    HostIdentifier = Util::rng::getHexString(8);
-
     std::string version, hash;
     Util::getVersionInfo(version, hash);
-    LOG_INF("Loolwsd version details: " << version << " - " << hash << " - id " << HostIdentifier << " - on " << Util::getLinuxVersion());
+    LOG_INF("Loolwsd version details: " << version << " - " << hash << " - id " << Util::getProcessIdentifier() << " - on " << Util::getLinuxVersion());
 #endif
 
     initializeSSL();
diff --git a/wsd/LOOLWSD.hpp b/wsd/LOOLWSD.hpp
index 654546d30..6f697f28a 100644
--- a/wsd/LOOLWSD.hpp
+++ b/wsd/LOOLWSD.hpp
@@ -241,7 +241,6 @@ public:
     static std::string WelcomeFilesRoot; ///< From where we should serve the release notes (or otherwise useful content) that is shown on first install or version update.
     static std::string ServiceRoot; ///< There are installations that need prefixing every page with some path.
     static std::string LOKitVersion;
-    static std::string HostIdentifier; ///< A unique random hash that identifies this server
     static std::string LogLevel;
     static bool AnonymizeUserData;
     static bool CheckLoolUser;
@@ -396,8 +395,6 @@ public:
     /// get correct server URL with protocol + port number for this running server
     static std::string getServerURL();
 
-    static std::string getVersionJSON();
-
     int innerMain();
 
 protected:


More information about the Libreoffice-commits mailing list