Improve --enable-online-update-mar Windows MOZ_MAINTENANCE_SERVICE feature

To get the MOZ_MAINTENANCE_SERVICE mode going at all, update.status needs to
contain a "pending-service" token.  For Mozilla, code in its
toolkit/mozapps/update/UpdateService.sys.mjs takes care of writing that.  For
us, lets always write that in update_checker() (even on Linux, where it's
apparently harmless).

Then, the MOZ_MAINTENANCE_SERVICE code is rather picky with its various sanity
checks:  Among other things, it expects argv[0] to be a full path to the updater
executable, and it expects the update.mar (and its status and log files) to be
in a directory hierarchy named updates/0/ rather than patch/.  So get all that
fixed in desktop/source/app/updater.cxx.  And patch in
external/onlineupdate/lo.patch where it expects to find the updater executable
(just updater.exe vs. our program/updater.exe).

And we shouldn't interfere with the upstream Mozilla maintenance service, so
also rename that in external/onlineupdate/lo.patch.

And `update_service install` wants to read version resources from the
update_service.exe, so provide that (via gb_Executable_add_default_nativeres).

Also, `update_service install` wants to read a MozillaMaintenanceDescription
value from an updater.ini, so provide one (with contents of that value inspired
by Mozilla's browser/locales/en-US/updater/updater.ini).

As we now have an updater.ini anyway (and which apparently works fine with Unix
line ends on both Linux and Windows), also use it on Linux and drop the
onlineupdate/source/update/updater/progressui_gtk.cpp again from
external/onlineupdate/lo.patch.  And update external/onlineupdate/README.md how
to manually execute that test against an updater.ini.

Change-Id: I0e3e5e5311be61e1224cda700af2e5d751113a99
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160996
Tested-by: Jenkins
Reviewed-by: Stephan Bergmann <stephan.bergmann@allotropia.de>
(cherry picked from commit 290f8f908dc8178c8bc34a8bf909246f591a13aa)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/161014
diff --git a/Repository.mk b/Repository.mk
index b9cf9c2..9ef08e8 100644
--- a/Repository.mk
+++ b/Repository.mk
@@ -1096,6 +1096,7 @@ $(eval $(call gb_Helper_register_packages_for_install,brand,\
	readlicense_oo_files \
	readlicense_oo_license \
	$(call gb_Helper_optional,DESKTOP,setup_native_packinfo) \
	$(if $(ENABLE_ONLINE_UPDATE_MAR),updater_ini) \
))

ifeq ($(USING_X11), TRUE)
diff --git a/desktop/source/app/updater.cxx b/desktop/source/app/updater.cxx
index 925b6b7..3221d68 100644
--- a/desktop/source/app/updater.cxx
+++ b/desktop/source/app/updater.cxx
@@ -32,6 +32,7 @@
#include <osl/file.hxx>
#include <rtl/process.h>
#include <sal/log.hxx>
#include <tools/stream.hxx>

#include <curl/curl.h>

@@ -173,17 +174,14 @@ void createStr(const OUString& rStr, CharT** pArgs, size_t i)
    pArgs[i] = pStr;
}

CharT** createCommandLine(int * argc)
CharT** createCommandLine(OUString const & argv0, int * argc)
{
    OUString aInstallDir = Updater::getInstallationPath();

    size_t nCommandLineArgs = rtl_getAppCommandArgCount();
    size_t nArgs = 8 + nCommandLineArgs;
    CharT** pArgs = new CharT*[nArgs];
    {
        OUString aUpdaterName = OUString::fromUtf8(pUpdaterName);
        createStr(aUpdaterName, pArgs, 0);
    }
    createStr(argv0, pArgs, 0);
    {
        // directory with the patch log
        OUString aPatchDir = Updater::getPatchDirURL();
@@ -307,7 +305,7 @@ bool update()

    Updater::log("Calling the updater with parameters: ");
    int argc;
    CharT** pArgs = createCommandLine(&argc);
    CharT** pArgs = createCommandLine(aUpdaterPath, &argc);

    bool bSuccess = true;
    const char* pUpdaterTestReplace = std::getenv("LIBO_UPDATER_TEST_REPLACE");
@@ -676,9 +674,11 @@ void download_file(const OUString& rURL, size_t nFileSize, const OUString& rHash
        throw invalid_hash(rHash, aHash);
    }

    OUString aPatchDirURL("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/patch/");
    OUString aPatchDirURL("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/updates/");
    rtl::Bootstrap::expandMacros(aPatchDirURL);
    osl::Directory::create(aPatchDirURL);
    aPatchDirURL += "0/";
    osl::Directory::create(aPatchDirURL);

    OUString aDestFile = aPatchDirURL + aFileName;
    Updater::log("Destination File: " + aDestFile);
@@ -764,6 +764,14 @@ void update_checker()
                        comphelper::ConfigurationChanges::create());
                officecfg::Office::Update::Update::SeeAlso::set(aSeeAlsoURL, batch);
                batch->commit();
                OUString const statUrl = Updater::getPatchDirURL() + "update.status";
                SvFileStream stat(statUrl, StreamMode::WRITE | StreamMode::TRUNC);
                stat.WriteOString("pending-service");
                stat.Flush();
                if (auto const e = stat.GetError()) {
                    Updater::log("Writing <" + statUrl + "> failed with " + e.toString());
                }
                stat.Close();
            }
        }
    }
@@ -796,7 +804,7 @@ void update_checker()

OUString Updater::getUpdateInfoLog()
{
    OUString aUpdateInfoURL("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/patch/updating.log");
    OUString aUpdateInfoURL("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/updates/updating.log");
    rtl::Bootstrap::expandMacros(aUpdateInfoURL);

    return aUpdateInfoURL;
@@ -804,7 +812,7 @@ OUString Updater::getUpdateInfoLog()

OUString Updater::getPatchDirURL()
{
    OUString aPatchDirURL("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/patch/");
    OUString aPatchDirURL("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/updates/0/");
    rtl::Bootstrap::expandMacros(aPatchDirURL);

    return aPatchDirURL;
diff --git a/external/onlineupdate/Executable_update_service.mk b/external/onlineupdate/Executable_update_service.mk
index 2fe188b..2d301c5 100644
--- a/external/onlineupdate/Executable_update_service.mk
+++ b/external/onlineupdate/Executable_update_service.mk
@@ -78,4 +78,6 @@ $(eval $(call gb_Executable_add_defs,update_service, \

$(eval $(call gb_Executable_set_warnings_disabled,update_service))

$(eval $(call gb_Executable_add_default_nativeres,update_service))

# vim:set shiftwidth=4 tabstop=4 noexpandtab: */
diff --git a/external/onlineupdate/Module_onlineupdate.mk b/external/onlineupdate/Module_onlineupdate.mk
index 8b5a09e..a6bc698 100644
--- a/external/onlineupdate/Module_onlineupdate.mk
+++ b/external/onlineupdate/Module_onlineupdate.mk
@@ -24,6 +24,7 @@ $(eval $(call gb_Module_add_targets,onlineupdate,\
	Executable_updater \
	Executable_mbsdiff \
	CustomTarget_generated \
	Package_updater_ini \
))
endif

diff --git a/external/onlineupdate/Package_updater_ini.mk b/external/onlineupdate/Package_updater_ini.mk
new file mode 100644
index 0000000..5ae65db
--- /dev/null
+++ b/external/onlineupdate/Package_updater_ini.mk
@@ -0,0 +1,16 @@
# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t; 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/.
#

$(eval $(call gb_Package_Package,updater_ini,$(SRCDIR)/external/onlineupdate))

$(eval $(call gb_Package_add_files,updater_ini,$(LIBO_BIN_FOLDER), \
    updater.ini \
))

# vim: set noet sw=4 ts=4:
diff --git a/external/onlineupdate/README.md b/external/onlineupdate/README.md
index ac4a7e6..5b0d081 100644
--- a/external/onlineupdate/README.md
+++ b/external/onlineupdate/README.md
@@ -33,3 +33,12 @@ updater.
* The `update` directory is inside of the user profile resulting in recursive copying.
* During the replacement request the updater log is in the user profile, which changes location from
the actual location to a backup location.

## Executable_test_updater_dialog

To run that manual test, do
```
$ cp instdir/program/updater.ini workdir/LinkTarget/Executable/test_updater_dialog.ini
$ workdir/LinkTarget/Executable/test_updater_dialog
$ rm workdir/LinkTarget/Executable/test_updater_dialog.ini
```
diff --git a/external/onlineupdate/lo.patch b/external/onlineupdate/lo.patch
index bad31b0..a8b9283 100644
--- a/external/onlineupdate/lo.patch
+++ b/external/onlineupdate/lo.patch
@@ -1,21 +1,132 @@
--- onlineupdate/source/update/updater/progressui_gtk.cpp
+++ onlineupdate/source/update/updater/progressui_gtk.cpp
@@ -5,6 +5,7 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
--- onlineupdate/source/service/serviceinstall.cpp
+++ onlineupdate/source/service/serviceinstall.cpp
@@ -25,7 +25,7 @@
 
 #include <gtk/gtk.h>
+#include <string.h>
 #include <unistd.h>
 #include "mozilla/Sprintf.h"
 #include "mozilla/Atomics.h"
@@ -52,8 +53,8 @@
     char ini_path[PATH_MAX];
     SprintfLiteral(ini_path, "%s.ini", (*pargv)[0]);
     if (ReadStrings(ini_path, &sStrings) != OK) {
-      sEnableUI = false;
-      return -1;
+      sStrings.title.reset(strdup("LibreOffice Update"));
+      sStrings.info.reset(strdup("Please wait while we update your installation."));
 // This uninstall key is defined originally in maintenanceservice_installer.nsi
 #define MAINT_UNINSTALL_KEY                                                    \
-  L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MozillaMaintenan" \
+  L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\LibreOfficeMaintenan" \
   L"ceService"
 
 static BOOL UpdateUninstallerVersionString(LPWSTR versionString) {
@@ -201,7 +201,7 @@
   size_t currentServicePathLen = wcslen(currentServicePath);
   bool doesServiceHaveCorrectPath =
       currentServicePathLen > 2 &&
-      !wcsstr(currentServicePath, L"maintenanceservice_tmp.exe") &&
+      !wcsstr(currentServicePath, L"update_service_tmp.exe") &&
       currentServicePath[0] == L'\"' &&
       currentServicePath[currentServicePathLen - 1] == L'\"';
 
@@ -222,7 +222,7 @@
     LOG_WARN(("Couldn't remove file spec.  (%lu)", GetLastError()));
     return FALSE;
   }
-  if (!PathAppendSafe(fixedPath, L"maintenanceservice.exe")) {
+  if (!PathAppendSafe(fixedPath, L"update_service.exe")) {
     LOG_WARN(("Couldn't append file spec.  (%lu)", GetLastError()));
     return FALSE;
   }
@@ -561,7 +561,7 @@
 
   // The service can be in a stopped state but the exe still in use
   // so make sure the process is really gone before proceeding
-  WaitForProcessExit(L"maintenanceservice.exe", 30);
+  WaitForProcessExit(L"update_service.exe", 30);
   LOG(("Done waiting for service stop, last service state: %lu", lastState));
 
   return lastState == SERVICE_STOPPED;
--- onlineupdate/source/service/serviceinstall.h
+++ onlineupdate/source/service/serviceinstall.h
@@ -4,7 +4,7 @@
 
 #include "readstrings.h"
 
-#define SVC_DISPLAY_NAME L"Mozilla Maintenance Service"
+#define SVC_DISPLAY_NAME L"LibreOffice Maintenance Service"
 
 enum SvcInstallAction { UpgradeSvc, InstallSvc, ForceInstallSvc };
 BOOL SvcInstall(SvcInstallAction action);
--- onlineupdate/source/update/common/pathhash.cpp
+++ onlineupdate/source/update/common/pathhash.cpp
@@ -119,7 +119,7 @@
   delete[] lowercasePath;
 
   LPCWSTR baseRegPath =
-      L"SOFTWARE\\Mozilla\\"
+      L"SOFTWARE\\LibreOffice\\"
       L"MaintenanceService\\";
   wcsncpy(registryPath, baseRegPath, MAX_PATH);
   BinaryDataToHexString(hash, hashSize, registryPath + wcslen(baseRegPath));
--- onlineupdate/source/update/common/updatehelper.cpp
+++ onlineupdate/source/update/common/updatehelper.cpp
@@ -78,7 +78,7 @@
   wcsncpy(outBuf, progFilesX86, MAX_PATH + 1);
   CoTaskMemFree(progFilesX86);
 
-  if (!PathAppendSafe(outBuf, L"Mozilla Maintenance Service")) {
+  if (!PathAppendSafe(outBuf, L"LibreOffice Maintenance Service")) {
     return FALSE;
   }
 
@@ -311,7 +311,7 @@
   // Obtain the temp path of the maintenance service binary
   WCHAR tmpService[MAX_PATH + 1] = {L'\0'};
   if (!PathGetSiblingFilePath(tmpService, serviceConfig.lpBinaryPathName,
-                              L"maintenanceservice_tmp.exe")) {
+                              L"update_service_tmp.exe")) {
     return FALSE;
   }
 
@@ -322,7 +322,7 @@
   // Get the new maintenance service path from the install dir
   WCHAR newMaintServicePath[MAX_PATH + 1] = {L'\0'};
   wcsncpy(newMaintServicePath, installDir, MAX_PATH);
-  PathAppendSafe(newMaintServicePath, L"maintenanceservice.exe");
+  PathAppendSafe(newMaintServicePath, L"update_service.exe");
 
   // Copy the temp file in alongside the maintenace service.
   // This is a requirement for maintenance service upgrades.
@@ -429,7 +429,7 @@
   // 2) The command being executed, which is "software-update"
   // 3) The path to updater.exe (from argv[0])
   LPCWSTR* updaterServiceArgv = new LPCWSTR[argc + 2];
-  updaterServiceArgv[0] = L"MozillaMaintenance";
+  updaterServiceArgv[0] = L"LibreOfficeMaintenance";
   updaterServiceArgv[1] = L"software-update";
 
   for (int i = 0; i < argc; ++i) {
--- onlineupdate/source/update/common/updatehelper.h
+++ onlineupdate/source/update/common/updatehelper.h
@@ -24,9 +24,9 @@
 #define PATCH_DIR_PATH L"\\updates\\0"
 
 #ifdef MOZ_MAINTENANCE_SERVICE
-#  define SVC_NAME L"MozillaMaintenance"
+#  define SVC_NAME L"LibreOfficeMaintenance"
 
-#  define BASE_SERVICE_REG_KEY L"SOFTWARE\\Mozilla\\MaintenanceService"
+#  define BASE_SERVICE_REG_KEY L"SOFTWARE\\LibreOffice\\MaintenanceService"
 
 // The test only fallback key, as its name implies, is only present on machines
 // that will use automated tests.  Since automated tests always run from a
--- onlineupdate/source/service/workmonitor.cpp
+++ onlineupdate/source/service/workmonitor.cpp
@@ -328,7 +328,7 @@
   // The installation dir that we are installing to is installDir.
   WCHAR installDirUpdater[MAX_PATH + 1] = {L'\0'};
   wcsncpy(installDirUpdater, installDir, MAX_PATH);
-  if (!PathAppendSafe(installDirUpdater, L"updater.exe")) {
+  if (!PathAppendSafe(installDirUpdater, L"program\\updater.exe")) {
     LOG_WARN(("Install directory updater could not be determined."));
     return false;
   }
@@ -746,7 +746,7 @@
 
     WCHAR installDirUpdater[MAX_PATH + 1] = {L'\0'};
     wcsncpy(installDirUpdater, installDir, MAX_PATH);
-    if (!PathAppendSafe(installDirUpdater, L"updater.exe")) {
+    if (!PathAppendSafe(installDirUpdater, L"program\\updater.exe")) {
       LOG_WARN(("Install directory updater could not be determined."));
       result = FALSE;
     }
 
     char icon_path[PATH_MAX];
diff --git a/external/onlineupdate/updater.ini b/external/onlineupdate/updater.ini
new file mode 100644
index 0000000..8ee6ad9c
--- /dev/null
+++ b/external/onlineupdate/updater.ini
@@ -0,0 +1,12 @@
;
; 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/.
;

[Strings]
Title=LibreOffice Update
Info=Please wait while we update your installation.
MozillaMaintenanceDescription=The LibreOffice Maintenace Service helps ensure that you have the latest and most secure version of LibreOffice on your computer.