summaryrefslogtreecommitdiffstats
path: root/desktop/source/pkgchk
diff options
context:
space:
mode:
Diffstat (limited to 'desktop/source/pkgchk')
-rw-r--r--desktop/source/pkgchk/unopkg/unopkg_app.cxx636
-rw-r--r--desktop/source/pkgchk/unopkg/unopkg_cmdenv.cxx390
-rw-r--r--desktop/source/pkgchk/unopkg/unopkg_main.c26
-rw-r--r--desktop/source/pkgchk/unopkg/unopkg_main.h34
-rw-r--r--desktop/source/pkgchk/unopkg/unopkg_misc.cxx458
-rw-r--r--desktop/source/pkgchk/unopkg/unopkg_shared.h119
6 files changed, 1663 insertions, 0 deletions
diff --git a/desktop/source/pkgchk/unopkg/unopkg_app.cxx b/desktop/source/pkgchk/unopkg/unopkg_app.cxx
new file mode 100644
index 000000000..a12ee6ae4
--- /dev/null
+++ b/desktop/source/pkgchk/unopkg/unopkg_app.cxx
@@ -0,0 +1,636 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <dp_misc.h>
+#include "unopkg_main.h"
+#include "unopkg_shared.h"
+#include <dp_identifier.hxx>
+#include <tools/extendapplicationenvironment.hxx>
+#include <rtl/bootstrap.hxx>
+#include <osl/process.h>
+#include <osl/conditn.hxx>
+#include <unotools/tempfile.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <comphelper/anytostring.hxx>
+#include <comphelper/logging.hxx>
+#include <comphelper/sequence.hxx>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/ExtensionManager.hpp>
+
+#include <com/sun/star/deployment/ui/PackageManagerDialog.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/logging/ConsoleHandler.hpp>
+#include <com/sun/star/logging/FileHandler.hpp>
+#include <com/sun/star/logging/LogLevel.hpp>
+#include <com/sun/star/logging/SimpleTextFormatter.hpp>
+#include <com/sun/star/logging/XLogger.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ui/dialogs/XDialogClosedListener.hpp>
+#if defined(UNX)
+ #include <unistd.h>
+#endif
+#include <utility>
+#include <vector>
+
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::logging;
+using namespace ::com::sun::star::uno;
+using namespace ::unopkg;
+
+namespace {
+
+struct ExtensionName
+{
+ OUString m_str;
+ explicit ExtensionName( OUString str ) : m_str(std::move( str )) {}
+ bool operator () ( Reference<deployment::XPackage> const & e ) const
+ {
+ return m_str == dp_misc::getIdentifier(e)
+ || m_str == e->getName();
+ }
+};
+
+
+const char16_t s_usingText [] =
+u"\n"
+"using: " APP_NAME " add <options> extension-path...\n"
+" " APP_NAME " validate <options> extension-identifier...\n"
+" " APP_NAME " remove <options> extension-identifier...\n"
+" " APP_NAME " list <options> extension-identifier...\n"
+" " APP_NAME " reinstall <options>\n"
+" " APP_NAME " gui\n"
+" " APP_NAME " -V\n"
+" " APP_NAME " -h\n"
+"\n"
+"sub-commands:\n"
+" add add extension\n"
+" validate checks the prerequisites of an installed extension and\n"
+" registers it if possible\n"
+" remove remove extensions by identifier\n"
+" reinstall expert feature: reinstall all deployed extensions\n"
+" list list information about deployed extensions\n"
+" gui raise Extension Manager Graphical User Interface (GUI)\n"
+"\n"
+"options:\n"
+" -h, --help this help\n"
+" -V, --version version information\n"
+" -v, --verbose verbose output\n"
+" -f, --force force overwriting existing extensions\n"
+" -s, --suppress-license prevents showing the license\n"
+" --log-file <file> custom log file; default: <cache-dir>/log.txt\n"
+" --shared expert feature: operate on shared installation\n"
+" deployment context;\n"
+" run only when no concurrent Office\n"
+" process(es) are running!\n"
+" --bundled expert feature: operate on bundled extensions. Only\n"
+" works with list, validate, reinstall;\n"
+" --deployment-context expert feature: explicit deployment context\n"
+" <context>\n"
+"\n"
+"To learn more about the Extension Manager and extensions, see:\n"
+"https://wiki.documentfoundation.org/Documentation/DevGuide/Extensions\n\n";
+
+
+const OptionInfo s_option_infos [] = {
+ { RTL_CONSTASCII_STRINGPARAM("help"), 'h', false },
+ { RTL_CONSTASCII_STRINGPARAM("version"), 'V', false },
+ { RTL_CONSTASCII_STRINGPARAM("verbose"), 'v', false },
+ { RTL_CONSTASCII_STRINGPARAM("force"), 'f', false },
+ { RTL_CONSTASCII_STRINGPARAM("log-file"), '\0', true },
+ { RTL_CONSTASCII_STRINGPARAM("shared"), '\0', false },
+ { RTL_CONSTASCII_STRINGPARAM("deployment-context"), '\0', true },
+ { RTL_CONSTASCII_STRINGPARAM("bundled"), '\0', false},
+ { RTL_CONSTASCII_STRINGPARAM("suppress-license"), 's', false},
+
+ { nullptr, 0, '\0', false }
+};
+
+class DialogClosedListenerImpl :
+ public ::cppu::WeakImplHelper< ui::dialogs::XDialogClosedListener >
+{
+ osl::Condition & m_rDialogClosedCondition;
+
+public:
+ explicit DialogClosedListenerImpl( osl::Condition & rDialogClosedCondition )
+ : m_rDialogClosedCondition( rDialogClosedCondition ) {}
+
+ // XEventListener (base of XDialogClosedListener)
+ virtual void SAL_CALL disposing( lang::EventObject const & Source ) override;
+
+ // XDialogClosedListener
+ virtual void SAL_CALL dialogClosed(
+ ui::dialogs::DialogClosedEvent const & aEvent ) override;
+};
+
+// XEventListener (base of XDialogClosedListener)
+void DialogClosedListenerImpl::disposing( lang::EventObject const & )
+{
+ // nothing to do
+}
+
+// XDialogClosedListener
+void DialogClosedListenerImpl::dialogClosed(
+ ui::dialogs::DialogClosedEvent const & )
+{
+ m_rDialogClosedCondition.set();
+}
+
+// If a package had been installed with a pre OOo 2.2, it could not normally be
+// found via its identifier; similarly (and for ease of use), a package
+// installed with OOo 2.2 or later could not normally be found via its file
+// name.
+Reference<deployment::XPackage> findPackage(
+ OUString const & repository,
+ Reference<deployment::XExtensionManager> const & manager,
+ Reference<ucb::XCommandEnvironment > const & environment,
+ std::u16string_view idOrFileName )
+{
+ const Sequence< Reference<deployment::XPackage> > ps(
+ manager->getDeployedExtensions(repository,
+ Reference<task::XAbortChannel>(), environment ) );
+ for ( auto const & package : ps )
+ if ( dp_misc::getIdentifier( package ) == idOrFileName )
+ return package;
+ for ( auto const & package : ps )
+ if ( package->getName() == idOrFileName )
+ return package;
+ return Reference<deployment::XPackage>();
+}
+
+} // anon namespace
+
+extern "C" int unopkg_main()
+{
+ tools::extendApplicationEnvironment();
+ bool bShowFailedMsg = true;
+ OUString subCommand;
+ bool option_shared = false;
+ bool option_force = false;
+ bool option_verbose = false;
+ bool option_bundled = false;
+ bool option_suppressLicense = false;
+ bool option_help = false;
+ bool subcmd_gui = false;
+ OUString logFile;
+ OUString repository;
+ OUString cmdArg;
+ std::vector<OUString> cmdPackages;
+ Reference<XLogHandler> xFileHandler;
+ Reference<XLogHandler> xConsoleHandler;
+ std::unique_ptr<comphelper::EventLogger> logger;
+ std::unique_ptr<utl::TempFile> pUserProfileTempDir;
+
+ OptionInfo const * info_shared = getOptionInfo(
+ s_option_infos, "shared" );
+ OptionInfo const * info_force = getOptionInfo(
+ s_option_infos, "force" );
+ OptionInfo const * info_verbose = getOptionInfo(
+ s_option_infos, "verbose" );
+ OptionInfo const * info_log = getOptionInfo(
+ s_option_infos, "log-file" );
+ OptionInfo const * info_context = getOptionInfo(
+ s_option_infos, "deployment-context" );
+ OptionInfo const * info_help = getOptionInfo(
+ s_option_infos, "help" );
+ OptionInfo const * info_version = getOptionInfo(
+ s_option_infos, "version" );
+ OptionInfo const * info_bundled = getOptionInfo(
+ s_option_infos, "bundled" );
+ OptionInfo const * info_suppressLicense = getOptionInfo(
+ s_option_infos, "suppress-license" );
+
+
+ Reference<XComponentContext> xComponentContext;
+ Reference<XComponentContext> xLocalComponentContext;
+
+ try {
+ sal_uInt32 nPos = 0;
+ sal_uInt32 nCount = osl_getCommandArgCount();
+ if (nCount == 0 || isOption( info_help, &nPos ))
+ {
+ dp_misc::writeConsole(s_usingText);
+ return 0;
+ }
+ else if (isOption( info_version, &nPos )) {
+ dp_misc::writeConsole(u"\n" APP_NAME " Version 3.3\n");
+ return 0;
+ }
+ //consume all bootstrap variables which may occur before the sub-command
+ while(isBootstrapVariable(&nPos))
+ ;
+
+ if(nPos >= nCount)
+ return 0;
+ //get the sub-command
+ osl_getCommandArg( nPos, &subCommand.pData );
+ ++nPos;
+ subCommand = subCommand.trim();
+ bool subcmd_add = subCommand == "add";
+ subcmd_gui = subCommand == "gui";
+
+ // sub-command options and packages:
+ while (nPos < nCount)
+ {
+ if (readArgument( &cmdArg, info_log, &nPos )) {
+ logFile = makeAbsoluteFileUrl(
+ cmdArg.trim(), getProcessWorkingDir() );
+ }
+ else if (!readOption( &option_verbose, info_verbose, &nPos ) &&
+ !readOption( &option_shared, info_shared, &nPos ) &&
+ !readOption( &option_force, info_force, &nPos ) &&
+ !readOption( &option_bundled, info_bundled, &nPos ) &&
+ !readOption( &option_suppressLicense, info_suppressLicense, &nPos ) &&
+ !readOption( &option_help, info_help, &nPos ) &&
+ !readArgument( &repository, info_context, &nPos ) &&
+ !isBootstrapVariable(&nPos))
+ {
+ osl_getCommandArg( nPos, &cmdArg.pData );
+ ++nPos;
+ cmdArg = cmdArg.trim();
+ if (!cmdArg.isEmpty())
+ {
+ if (cmdArg[ 0 ] == '-')
+ {
+ // is option:
+ dp_misc::writeConsoleError(OUStringConcatenation(
+ "\nERROR: unexpected option " +
+ cmdArg +
+ "!\n Use " APP_NAME " " +
+ toString(info_help) +
+ " to print all options.\n"));
+ return 1;
+ }
+ else
+ {
+ // is package:
+ cmdPackages.push_back(
+ subcmd_add || subcmd_gui
+ ? makeAbsoluteFileUrl(
+ cmdArg, getProcessWorkingDir() )
+ : cmdArg );
+ }
+ }
+ }
+ }
+
+ // tdf#129917 Use temp user profile when installing shared extensions
+ if (option_shared)
+ {
+ pUserProfileTempDir.reset(new utl::TempFile(nullptr, true));
+ pUserProfileTempDir->EnableKillingFile();
+ }
+
+ xComponentContext = getUNO(option_verbose, subcmd_gui,
+ pUserProfileTempDir ? pUserProfileTempDir->GetURL() : "",
+ xLocalComponentContext);
+
+ // Initialize logging. This will log errors to the console and
+ // also to file if the --log-file parameter was provided.
+ logger.reset(new comphelper::EventLogger(xLocalComponentContext, "unopkg"));
+ const Reference<XLogger> xLogger(logger->getLogger());
+ xLogger->setLevel(LogLevel::WARNING);
+ Reference<XLogFormatter> xLogFormatter(SimpleTextFormatter::create(xLocalComponentContext));
+ Sequence < beans::NamedValue > aSeq { { "Formatter", Any(xLogFormatter) } };
+
+ xConsoleHandler.set(ConsoleHandler::createWithSettings(xLocalComponentContext, aSeq));
+ xLogger->addLogHandler(xConsoleHandler);
+ xConsoleHandler->setLevel(LogLevel::WARNING);
+ xLogger->setLevel(LogLevel::WARNING);
+
+
+ if (!logFile.isEmpty())
+ {
+ Sequence < beans::NamedValue > aSeq2 { { "Formatter", Any(xLogFormatter) }, {"FileURL", Any(logFile)} };
+ xFileHandler.set(css::logging::FileHandler::createWithSettings(xLocalComponentContext, aSeq2));
+ xFileHandler->setLevel(LogLevel::WARNING);
+ xLogger->addLogHandler(xFileHandler);
+ }
+
+ if (option_verbose)
+ {
+ xLogger->setLevel(LogLevel::INFO);
+ xConsoleHandler->setLevel(LogLevel::INFO);
+ if (xFileHandler.is())
+ xFileHandler->setLevel(LogLevel::INFO);
+ }
+
+ if (repository.isEmpty())
+ {
+ if (option_shared)
+ repository = "shared";
+ else if (option_bundled)
+ repository = "bundled";
+ else
+ repository = "user";
+ }
+ else
+ {
+ if ( repository == "shared" ) {
+ option_shared = true;
+ }
+ else if (option_shared)
+ {
+ logger->log(LogLevel::WARNING, "Explicit context given! Ignoring option '$1$'", toString(info_shared));
+ }
+ }
+#if defined(UNX)
+ if ( geteuid() == 0 )
+ {
+ if ( !(option_shared || option_bundled || option_help) )
+ {
+ logger->log(LogLevel::SEVERE, "Cannot run $1$ as root without $2$ or $3$ option.",
+ APP_NAME, toString(info_shared), toString(info_bundled));
+ return 1;
+ }
+ }
+#endif
+
+ if (subCommand == "reinstall")
+ {
+ //We must prevent that services and types are loaded by UNO,
+ //otherwise we cannot delete the registry data folder.
+ OUString extensionUnorc;
+ if (repository == "user")
+ extensionUnorc = "$UNO_USER_PACKAGES_CACHE/registry/com.sun.star.comp.deployment.component.PackageRegistryBackend/unorc";
+ else if (repository == "shared")
+ extensionUnorc = "$SHARED_EXTENSIONS_USER/registry/com.sun.star.comp.deployment.component.PackageRegistryBackend/unorc";
+ else if (repository == "bundled")
+ extensionUnorc = "$BUNDLED_EXTENSIONS_USER/registry/com.sun.star.comp.deployment.component.PackageRegistryBackend/unorc";
+ else
+ OSL_ASSERT(false);
+
+ ::rtl::Bootstrap::expandMacros(extensionUnorc);
+ oslFileError e = osl_removeFile(extensionUnorc.pData);
+ if (e != osl_File_E_None && e != osl_File_E_NOENT)
+ throw Exception("Could not delete " + extensionUnorc, nullptr);
+ }
+
+ Reference<deployment::XExtensionManager> xExtensionManager(
+ deployment::ExtensionManager::get( xComponentContext ) );
+
+ Reference<css::ucb::XCommandEnvironment> xCmdEnv(
+ createCmdEnv(xComponentContext, option_force, option_verbose, option_suppressLicense));
+
+ //synchronize bundled/shared extensions
+ //Do not synchronize when command is "reinstall". This could add types and services to UNO and
+ //prevent the deletion of the registry data folder
+ //syncing is done in XExtensionManager.reinstall
+ if (!subcmd_gui && subCommand != "reinstall"
+ && ! dp_misc::office_is_running())
+ dp_misc::syncRepositories(false, xCmdEnv);
+
+ if ( subcmd_add || subCommand == "remove" )
+ {
+ for (const OUString & cmdPackage : cmdPackages)
+ {
+ if (subcmd_add)
+ {
+ beans::NamedValue nvSuppress(
+ "SUPPRESS_LICENSE", option_suppressLicense ?
+ Any(OUString("1")):Any(OUString("0")));
+ xExtensionManager->addExtension(
+ cmdPackage, Sequence<beans::NamedValue>(&nvSuppress, 1),
+ repository, Reference<task::XAbortChannel>(), xCmdEnv);
+ }
+ else
+ {
+ try
+ {
+ xExtensionManager->removeExtension(
+ cmdPackage, cmdPackage, repository,
+ Reference<task::XAbortChannel>(), xCmdEnv );
+ }
+ catch (const lang::IllegalArgumentException &)
+ {
+ Reference<deployment::XPackage> p(
+ findPackage(repository,
+ xExtensionManager, xCmdEnv, cmdPackage ) );
+ if ( !p.is())
+ throw;
+ else if (p.is())
+ xExtensionManager->removeExtension(
+ ::dp_misc::getIdentifier(p), p->getName(),
+ repository,
+ Reference<task::XAbortChannel>(), xCmdEnv );
+ }
+ }
+ }
+ }
+ else if ( subCommand == "reinstall" )
+ {
+ xExtensionManager->reinstallDeployedExtensions(
+ false, repository, Reference<task::XAbortChannel>(), xCmdEnv);
+ }
+ else if ( subCommand == "list" )
+ {
+ std::vector<Reference<deployment::XPackage> > vecExtUnaccepted;
+ ::comphelper::sequenceToContainer(vecExtUnaccepted,
+ xExtensionManager->getExtensionsWithUnacceptedLicenses(
+ repository, xCmdEnv));
+
+ //This vector tells what XPackage in allExtensions has an
+ //unaccepted license.
+ std::vector<bool> vecUnaccepted;
+ std::vector<Reference<deployment::XPackage> > allExtensions;
+ if (cmdPackages.empty())
+ {
+ Sequence< Reference<deployment::XPackage> >
+ packages = xExtensionManager->getDeployedExtensions(
+ repository, Reference<task::XAbortChannel>(), xCmdEnv );
+
+ std::vector<Reference<deployment::XPackage> > vec_packages;
+ ::comphelper::sequenceToContainer(vec_packages, packages);
+
+ //First copy the extensions with the unaccepted license
+ //to vector allExtensions.
+ allExtensions.resize(vecExtUnaccepted.size() + vec_packages.size());
+
+ std::vector<Reference<deployment::XPackage> >::iterator i_all_ext =
+ std::copy(vecExtUnaccepted.begin(), vecExtUnaccepted.end(),
+ allExtensions.begin());
+ //Now copy those we got from getDeployedExtensions
+ std::copy(vec_packages.begin(), vec_packages.end(), i_all_ext);
+
+ //Now prepare the vector which tells what extension has an
+ //unaccepted license
+ vecUnaccepted.resize(vecExtUnaccepted.size() + vec_packages.size());
+ std::fill_n(vecUnaccepted.begin(), vecExtUnaccepted.size(), true);
+ std::fill_n(vecUnaccepted.begin() + vecExtUnaccepted.size(),
+ vec_packages.size(), false);
+
+ dp_misc::writeConsole(
+ OUStringConcatenation("All deployed " + repository + " extensions:\n\n"));
+ }
+ else
+ {
+ //The user provided the names (ids or file names) of the extensions
+ //which shall be listed
+ for (const OUString & cmdPackage : cmdPackages)
+ {
+ Reference<deployment::XPackage> extension;
+ try
+ {
+ extension = xExtensionManager->getDeployedExtension(
+ repository, cmdPackage, cmdPackage, xCmdEnv );
+ }
+ catch (const lang::IllegalArgumentException &)
+ {
+ extension = findPackage(repository,
+ xExtensionManager, xCmdEnv, cmdPackage );
+ }
+
+ //Now look if the requested extension has an unaccepted license
+ bool bUnacceptedLic = false;
+ if (!extension.is())
+ {
+ std::vector<Reference<deployment::XPackage> >::const_iterator
+ i = std::find_if(
+ vecExtUnaccepted.begin(),
+ vecExtUnaccepted.end(), ExtensionName(cmdPackage));
+ if (i != vecExtUnaccepted.end())
+ {
+ extension = *i;
+ bUnacceptedLic = true;
+ }
+ }
+
+ if (!extension.is())
+ throw lang::IllegalArgumentException(
+ "There is no such extension deployed: " +
+ cmdPackage,nullptr,-1);
+ allExtensions.push_back(extension);
+ vecUnaccepted.push_back(bUnacceptedLic);
+ }
+
+ }
+
+ printf_packages(allExtensions, vecUnaccepted, xCmdEnv );
+ }
+ else if ( subCommand == "validate" )
+ {
+ std::vector<Reference<deployment::XPackage> > vecExtUnaccepted;
+ ::comphelper::sequenceToContainer(
+ vecExtUnaccepted, xExtensionManager->getExtensionsWithUnacceptedLicenses(
+ repository, xCmdEnv));
+
+ for (const OUString & cmdPackage : cmdPackages)
+ {
+ Reference<deployment::XPackage> extension;
+ try
+ {
+ extension = xExtensionManager->getDeployedExtension(
+ repository, cmdPackage, cmdPackage, xCmdEnv );
+ }
+ catch (const lang::IllegalArgumentException &)
+ {
+ extension = findPackage(
+ repository, xExtensionManager, xCmdEnv, cmdPackage );
+ }
+
+ if (!extension.is())
+ {
+ std::vector<Reference<deployment::XPackage> >::const_iterator
+ i = std::find_if(
+ vecExtUnaccepted.begin(),
+ vecExtUnaccepted.end(), ExtensionName(cmdPackage));
+ if (i != vecExtUnaccepted.end())
+ {
+ extension = *i;
+ }
+ }
+
+ if (extension.is())
+ xExtensionManager->checkPrerequisitesAndEnable(
+ extension, Reference<task::XAbortChannel>(), xCmdEnv);
+ }
+ }
+ else if ( subCommand == "gui" )
+ {
+ Reference<ui::dialogs::XAsynchronousExecutableDialog> xDialog(
+ deployment::ui::PackageManagerDialog::createAndInstall(
+ xComponentContext,
+ !cmdPackages.empty() ? cmdPackages[0] : OUString() ));
+
+ osl::Condition dialogEnded;
+ dialogEnded.reset();
+
+ Reference< ui::dialogs::XDialogClosedListener > xListener(
+ new DialogClosedListenerImpl( dialogEnded ) );
+
+ xDialog->startExecuteModal(xListener);
+ dialogEnded.wait();
+ return 0;
+ }
+ else
+ {
+ logger->log(LogLevel::SEVERE,
+ "Unknown sub-command: '$1$'. Use $2$ $3$ to print all options.",
+ subCommand, APP_NAME, toString(info_help));
+ return 1;
+ }
+
+ logger->log(LogLevel::INFO, "$1$ done.", APP_NAME);
+ //Force to release all bridges which connect us to the child processes
+ dp_misc::disposeBridges(xLocalComponentContext);
+ css::uno::Reference<css::lang::XComponent>(
+ xLocalComponentContext, css::uno::UNO_QUERY_THROW)->dispose();
+ return 0;
+ }
+ catch (const ucb::CommandFailedException &e)
+ {
+ logger->log(LogLevel::SEVERE, "Exception occurred: $1$", e.Message);
+ }
+ catch (const ucb::CommandAbortedException &)
+ {
+ logger->log(LogLevel::SEVERE, "$1$ aborted.", APP_NAME);
+ bShowFailedMsg = false;
+ }
+ catch (const deployment::DeploymentException & exc)
+ {
+ logger->log(LogLevel::SEVERE, "Exception occurred: $1$", exc.Message);
+ logger->log(LogLevel::INFO, " Cause: $1$", comphelper::anyToString(exc.Cause));
+ }
+ catch (const LockFileException & e)
+ {
+ // No logger since it requires UNO which we don't have here
+ dp_misc::writeConsoleError(OUStringConcatenation(e.Message + "\n"));
+ bShowFailedMsg = false;
+ }
+ catch (const css::uno::Exception & e ) {
+ Any exc( ::cppu::getCaughtException() );
+
+ logger->log(LogLevel::SEVERE, "Exception occurred: $1$", e.Message);
+ logger->log(LogLevel::INFO, " Cause: $1$", comphelper::anyToString(exc));
+ }
+ if (bShowFailedMsg)
+ logger->log(LogLevel::SEVERE, "$1$ failed.", APP_NAME);
+ dp_misc::disposeBridges(xLocalComponentContext);
+ if (xLocalComponentContext.is()) {
+ css::uno::Reference<css::lang::XComponent>(
+ xLocalComponentContext, css::uno::UNO_QUERY_THROW)->dispose();
+ }
+ return 1;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/pkgchk/unopkg/unopkg_cmdenv.cxx b/desktop/source/pkgchk/unopkg/unopkg_cmdenv.cxx
new file mode 100644
index 000000000..cca5b7ae3
--- /dev/null
+++ b/desktop/source/pkgchk/unopkg/unopkg_cmdenv.cxx
@@ -0,0 +1,390 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <strings.hrc>
+#include <dp_misc.h>
+#include <dp_shared.hxx>
+#include "unopkg_shared.h"
+#include <i18nlangtag/languagetag.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/anytostring.hxx>
+#include <tools/diagnose_ex.h>
+#include <unotools/configmgr.hxx>
+#include <com/sun/star/lang/WrappedTargetException.hpp>
+#include <com/sun/star/task/XInteractionAbort.hpp>
+#include <com/sun/star/task/XInteractionApprove.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/InstallException.hpp>
+#include <com/sun/star/deployment/LicenseException.hpp>
+#include <com/sun/star/deployment/VersionException.hpp>
+#include <com/sun/star/deployment/PlatformException.hpp>
+#include <com/sun/star/i18n/Collator.hpp>
+#include <com/sun/star/i18n/CollatorOptions.hpp>
+
+#include <dp_version.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::ucb;
+using namespace ::com::sun::star::uno;
+using namespace ::unopkg;
+
+
+namespace {
+
+
+class CommandEnvironmentImpl
+ : public ::cppu::WeakImplHelper< XCommandEnvironment,
+ task::XInteractionHandler,
+ XProgressHandler >
+{
+ sal_Int32 m_logLevel;
+ bool m_option_force_overwrite;
+ bool m_option_verbose;
+ bool m_option_suppress_license;
+ Reference< XComponentContext > m_xComponentContext;
+ Reference< XProgressHandler > m_xLogFile;
+
+ /// @throws RuntimeException
+ void update_( Any const & Status );
+ void printLicense(std::u16string_view sName,std::u16string_view sLicense,
+ bool & accept, bool & decline);
+
+public:
+ virtual ~CommandEnvironmentImpl() override;
+ CommandEnvironmentImpl(
+ Reference<XComponentContext> const & xComponentContext,
+ bool option_force_overwrite,
+ bool option_verbose,
+ bool option_suppress_license);
+
+ // XCommandEnvironment
+ virtual Reference< task::XInteractionHandler > SAL_CALL
+ getInteractionHandler() override;
+ virtual Reference< XProgressHandler > SAL_CALL getProgressHandler() override;
+
+ // XInteractionHandler
+ virtual void SAL_CALL handle(
+ Reference< task::XInteractionRequest > const & xRequest ) override;
+
+ // XProgressHandler
+ virtual void SAL_CALL push( Any const & Status ) override;
+ virtual void SAL_CALL update( Any const & Status ) override;
+ virtual void SAL_CALL pop() override;
+};
+
+
+CommandEnvironmentImpl::CommandEnvironmentImpl(
+ Reference<XComponentContext> const & xComponentContext,
+ bool option_force_overwrite,
+ bool option_verbose,
+ bool option_suppressLicense)
+ : m_logLevel(0),
+ m_option_force_overwrite( option_force_overwrite ),
+ m_option_verbose( option_verbose ),
+ m_option_suppress_license( option_suppressLicense ),
+ m_xComponentContext(xComponentContext)
+{
+ m_xLogFile.set(
+ xComponentContext->getServiceManager()
+ ->createInstanceWithArgumentsAndContext(
+ "com.sun.star.comp.deployment.ProgressLog",
+ Sequence<Any>(), xComponentContext ),
+ UNO_QUERY_THROW );
+}
+
+
+CommandEnvironmentImpl::~CommandEnvironmentImpl()
+{
+ try {
+ Reference< lang::XComponent > xComp( m_xLogFile, UNO_QUERY );
+ if (xComp.is())
+ xComp->dispose();
+ }
+ catch (const RuntimeException &) {
+ TOOLS_WARN_EXCEPTION( "desktop", "" );
+ }
+}
+
+//May throw exceptions
+void CommandEnvironmentImpl::printLicense(
+ std::u16string_view sName, std::u16string_view sLicense, bool & accept, bool &decline)
+{
+ OUString s1tmp(DpResId(RID_STR_UNOPKG_ACCEPT_LIC_1));
+ OUString s1(s1tmp.replaceAll("$NAME", sName));
+ OUString s2 = DpResId(RID_STR_UNOPKG_ACCEPT_LIC_2);
+ OUString s3 = DpResId(RID_STR_UNOPKG_ACCEPT_LIC_3);
+ OUString s4 = DpResId(RID_STR_UNOPKG_ACCEPT_LIC_4);
+ OUString sYES = DpResId(RID_STR_UNOPKG_ACCEPT_LIC_YES);
+ OUString sY = DpResId(RID_STR_UNOPKG_ACCEPT_LIC_Y);
+ OUString sNO = DpResId(RID_STR_UNOPKG_ACCEPT_LIC_NO);
+ OUString sN = DpResId(RID_STR_UNOPKG_ACCEPT_LIC_N);
+
+ OUString sNewLine("\n");
+
+ dp_misc::writeConsole(OUStringConcatenation(sNewLine + sNewLine + s1 + sNewLine + sNewLine));
+ dp_misc::writeConsole(OUStringConcatenation(sLicense + sNewLine + sNewLine));
+ dp_misc::writeConsole(OUStringConcatenation(s2 + sNewLine));
+ dp_misc::writeConsole(s3);
+
+ //the user may enter "yes" or "no", we compare in a case insensitive way
+ Reference< css::i18n::XCollator > xCollator =
+ css::i18n::Collator::create( m_xComponentContext );
+ xCollator->loadDefaultCollator(
+ LanguageTag(utl::ConfigManager::getUILocale()).getLocale(),
+ css::i18n::CollatorOptions::CollatorOptions_IGNORE_CASE);
+
+ do
+ {
+ OUString sAnswer = dp_misc::readConsole();
+ if (xCollator->compareString(sAnswer, sYES) == 0
+ || xCollator->compareString(sAnswer, sY) == 0)
+ {
+ accept = true;
+ break;
+ }
+ else if(xCollator->compareString(sAnswer, sNO) == 0
+ || xCollator->compareString(sAnswer, sN) == 0)
+ {
+ decline = true;
+ break;
+ }
+ else
+ {
+ dp_misc::writeConsole(OUStringConcatenation(sNewLine + sNewLine + s4 + sNewLine));
+ }
+ }
+ while(true);
+}
+
+// XCommandEnvironment
+
+Reference< task::XInteractionHandler >
+CommandEnvironmentImpl::getInteractionHandler()
+{
+ return this;
+}
+
+
+Reference< XProgressHandler > CommandEnvironmentImpl::getProgressHandler()
+{
+ return this;
+}
+
+// XInteractionHandler
+
+void CommandEnvironmentImpl::handle(
+ Reference<task::XInteractionRequest> const & xRequest )
+{
+ Any request( xRequest->getRequest() );
+ OSL_ASSERT( request.getValueTypeClass() == TypeClass_EXCEPTION );
+ dp_misc::TRACE("[unopkg_cmdenv.cxx] incoming request:\n"
+ + ::comphelper::anyToString(request) + "\n\n");
+
+ // selections:
+ bool approve = false;
+ bool abort = false;
+
+ lang::WrappedTargetException wtExc;
+ deployment::LicenseException licExc;
+ deployment::InstallException instExc;
+ deployment::PlatformException platExc;
+
+ if (request >>= wtExc) {
+ // ignore intermediate errors of legacy packages, i.e.
+ // former pkgchk behaviour:
+ const Reference<deployment::XPackage> xPackage(
+ wtExc.Context, UNO_QUERY );
+ OSL_ASSERT( xPackage.is() );
+ if (xPackage.is()) {
+ const Reference<deployment::XPackageTypeInfo> xPackageType(
+ xPackage->getPackageType() );
+ OSL_ASSERT( xPackageType.is() );
+ if (xPackageType.is()) {
+ approve = (xPackage->isBundle() &&
+ xPackageType->getMediaType().match(
+ "application/vnd.sun.star.legacy-package-bundle") );
+ }
+ }
+ abort = !approve;
+ if (abort) {
+ // notify cause as error:
+ request = wtExc.TargetException;
+ }
+ else {
+ // handable deployment error signalled, e.g.
+ // bundle item registration failed, notify as warning:
+ update_( wtExc.TargetException );
+ }
+ }
+ else if (request >>= licExc)
+ {
+ if ( !m_option_suppress_license )
+ printLicense(licExc.ExtensionName, licExc.Text, approve, abort);
+ else
+ {
+ approve = true;
+ abort = false;
+ }
+ }
+ else if (request >>= instExc)
+ {
+ //Only if the unopgk was started with gui + extension then the user is asked.
+ //In console mode there is no asking.
+ approve = true;
+ }
+ else if (request >>= platExc)
+ {
+ OUString sMsg(DpResId(RID_STR_UNSUPPORTED_PLATFORM));
+ sMsg = sMsg.replaceAll("%Name", platExc.package->getDisplayName());
+ dp_misc::writeConsole(OUStringConcatenation("\n" + sMsg + "\n\n"));
+ approve = true;
+ }
+ else {
+ deployment::VersionException nc_exc;
+ if (request >>= nc_exc) {
+ approve = m_option_force_overwrite ||
+ (::dp_misc::compareVersions(nc_exc.NewVersion, nc_exc.Deployed->getVersion())
+ == ::dp_misc::GREATER);
+ abort = !approve;
+ }
+ else
+ return; // unknown request => no selection at all
+ }
+
+ if (abort && m_option_verbose)
+ {
+ OUString msg = ::comphelper::anyToString(request);
+ dp_misc::writeConsoleError(OUStringConcatenation("\nERROR: " + msg + "\n"));
+ }
+
+ // select:
+ const css::uno::Sequence<css::uno::Reference<css::task::XInteractionContinuation>> xIC = xRequest->getContinuations();
+ for ( auto const& rCont : xIC )
+ {
+ if (approve) {
+ Reference<task::XInteractionApprove> xInteractionApprove(
+ rCont, UNO_QUERY );
+ if (xInteractionApprove.is()) {
+ xInteractionApprove->select();
+ break;
+ }
+ }
+ else if (abort) {
+ Reference<task::XInteractionAbort> xInteractionAbort(
+ rCont, UNO_QUERY );
+ if (xInteractionAbort.is()) {
+ xInteractionAbort->select();
+ break;
+ }
+ }
+ }
+}
+
+// XProgressHandler
+
+void CommandEnvironmentImpl::push( Any const & Status )
+{
+ update_( Status );
+ OSL_ASSERT( m_logLevel >= 0 );
+ ++m_logLevel;
+ if (m_xLogFile.is())
+ m_xLogFile->push( Status );
+}
+
+
+void CommandEnvironmentImpl::update_( Any const & Status )
+{
+ if (! Status.hasValue())
+ return;
+ bool bUseErr = false;
+ OUString msg;
+ if (Status >>= msg) {
+ if (! m_option_verbose)
+ return;
+ }
+ else {
+ OUStringBuffer buf;
+ buf.append( "WARNING: " );
+ deployment::DeploymentException dp_exc;
+ if (Status >>= dp_exc) {
+ buf.append( dp_exc.Message );
+ buf.append( ", Cause: " );
+ buf.append( ::comphelper::anyToString(dp_exc.Cause) );
+ }
+ else {
+ buf.append( ::comphelper::anyToString(Status) );
+ }
+ msg = buf.makeStringAndClear();
+ bUseErr = true;
+ }
+ OSL_ASSERT( m_logLevel >= 0 );
+ for ( sal_Int32 n = 0; n < m_logLevel; ++n )
+ {
+ if (bUseErr)
+ dp_misc::writeConsoleError(u" ");
+ else
+ dp_misc::writeConsole(u" ");
+ }
+
+ if (bUseErr)
+ dp_misc::writeConsoleError(OUStringConcatenation(msg + "\n"));
+ else
+ dp_misc::writeConsole(OUStringConcatenation(msg + "\n"));
+}
+
+
+void CommandEnvironmentImpl::update( Any const & Status )
+{
+ update_( Status );
+ if (m_xLogFile.is())
+ m_xLogFile->update( Status );
+}
+
+
+void CommandEnvironmentImpl::pop()
+{
+ OSL_ASSERT( m_logLevel > 0 );
+ --m_logLevel;
+ if (m_xLogFile.is())
+ m_xLogFile->pop();
+}
+
+
+} // anon namespace
+
+namespace unopkg {
+
+
+Reference< XCommandEnvironment > createCmdEnv(
+ Reference< XComponentContext > const & xContext,
+ bool option_force_overwrite,
+ bool option_verbose,
+ bool option_suppress_license)
+{
+ return new CommandEnvironmentImpl(
+ xContext, option_force_overwrite, option_verbose, option_suppress_license);
+}
+} // unopkg
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/pkgchk/unopkg/unopkg_main.c b/desktop/source/pkgchk/unopkg/unopkg_main.c
new file mode 100644
index 000000000..83b20b0b4
--- /dev/null
+++ b/desktop/source/pkgchk/unopkg/unopkg_main.c
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/main.h>
+
+#include "unopkg_main.h"
+
+SAL_IMPLEMENT_MAIN() { return unopkg_main(); }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/pkgchk/unopkg/unopkg_main.h b/desktop/source/pkgchk/unopkg/unopkg_main.h
new file mode 100644
index 000000000..0fcb1013d
--- /dev/null
+++ b/desktop/source/pkgchk/unopkg/unopkg_main.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <desktop/dllapi.h>
+
+#if defined __cplusplus
+extern "C" {
+#endif
+
+DESKTOP_DLLPUBLIC int unopkg_main(void);
+
+#if defined __cplusplus
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/pkgchk/unopkg/unopkg_misc.cxx b/desktop/source/pkgchk/unopkg/unopkg_misc.cxx
new file mode 100644
index 000000000..402833aa5
--- /dev/null
+++ b/desktop/source/pkgchk/unopkg/unopkg_misc.cxx
@@ -0,0 +1,458 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <config_folders.h>
+
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <rtl/bootstrap.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <osl/process.h>
+#include <osl/file.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/bootstrap.hxx>
+#include <cppuhelper/bootstrap.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/processfactory.hxx>
+
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/ucb/UniversalContentBroker.hpp>
+
+#include <strings.hrc>
+#include "unopkg_shared.h"
+#include <dp_identifier.hxx>
+#include <dp_misc.h>
+#include <dp_shared.hxx>
+#include <lockfile.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+
+namespace unopkg {
+
+OUString toString( OptionInfo const * info )
+{
+ assert(info != nullptr);
+ OUStringBuffer buf;
+ buf.append("--");
+ buf.appendAscii(info->m_name);
+ if (info->m_short_option != '\0')
+ {
+ buf.append(" (short -" );
+ buf.append(info->m_short_option );
+ buf.append(")");
+ }
+ if (info->m_has_argument)
+ buf.append(" <argument>" );
+ return buf.makeStringAndClear();
+}
+
+
+OptionInfo const * getOptionInfo(
+ OptionInfo const * list,
+ OUString const & opt )
+{
+ for ( ; list->m_name != nullptr; ++list )
+ {
+ OptionInfo const & option_info = *list;
+ if (!opt.isEmpty())
+ {
+ if (opt.equalsAsciiL(
+ option_info.m_name, option_info.m_name_length ))
+ {
+ return &option_info;
+ }
+ }
+ }
+ SAL_WARN( "desktop", opt );
+ return nullptr;
+}
+
+
+bool isOption( OptionInfo const * option_info, sal_uInt32 * pIndex )
+{
+ assert(option_info != nullptr);
+ if (osl_getCommandArgCount() <= *pIndex)
+ return false;
+
+ OUString arg;
+ osl_getCommandArg( *pIndex, &arg.pData );
+ sal_Int32 len = arg.getLength();
+
+ if (len < 2 || arg[ 0 ] != '-')
+ return false;
+
+ if (len == 2 && arg[ 1 ] == option_info->m_short_option)
+ {
+ ++(*pIndex);
+ dp_misc::TRACE(__FILE__ ": identified option \'\'"
+ + OUStringChar( option_info->m_short_option ) + "\n");
+ return true;
+ }
+ if (arg[ 1 ] == '-' && rtl_ustr_ascii_compare(
+ arg.pData->buffer + 2, option_info->m_name ) == 0)
+ {
+ ++(*pIndex);
+ dp_misc::TRACE(__FILE__ ": identified option \'"
+ + OUString::createFromAscii(option_info->m_name) + "\'\n");
+ return true;
+ }
+ return false;
+}
+
+
+bool isBootstrapVariable(sal_uInt32 * pIndex)
+{
+ OSL_ASSERT(osl_getCommandArgCount() >= *pIndex);
+
+ OUString arg;
+ osl_getCommandArg(*pIndex, &arg.pData);
+ if (arg.match("-env:"))
+ {
+ ++(*pIndex);
+ return true;
+ }
+ return false;
+}
+
+
+bool readArgument(
+ OUString * pValue, OptionInfo const * option_info, sal_uInt32 * pIndex )
+{
+ if (isOption( option_info, pIndex ))
+ {
+ if (*pIndex < osl_getCommandArgCount())
+ {
+ OSL_ASSERT( pValue != nullptr );
+ osl_getCommandArg( *pIndex, &pValue->pData );
+ dp_misc::TRACE(__FILE__ ": argument value: "
+ + *pValue + "\n");
+ ++(*pIndex);
+ return true;
+ }
+ --(*pIndex);
+ }
+ return false;
+}
+
+
+OUString const & getExecutableDir()
+{
+ static const OUString EXEC =
+ []()
+ {
+ OUString path;
+ if (osl_getExecutableFile( &path.pData ) != osl_Process_E_None) {
+ throw RuntimeException("cannot locate executable directory!",nullptr);
+ }
+ return path.copy( 0, path.lastIndexOf( '/' ) );
+ }();
+ return EXEC;
+}
+
+
+OUString const & getProcessWorkingDir()
+{
+ static const OUString WORKING =
+ []()
+ {
+ OUString workingDir;
+ utl::Bootstrap::getProcessWorkingDir(workingDir);
+ return workingDir;
+ }();
+ return WORKING;
+}
+
+
+OUString makeAbsoluteFileUrl(
+ OUString const & sys_path, OUString const & base_url )
+{
+ // system path to file url
+ OUString file_url;
+ oslFileError rc = osl_getFileURLFromSystemPath( sys_path.pData, &file_url.pData );
+ if ( rc != osl_File_E_None) {
+ OUString tempPath;
+ if ( osl_getSystemPathFromFileURL( sys_path.pData, &tempPath.pData) != osl_File_E_None )
+ {
+ throw RuntimeException("cannot get file url from system path: " +
+ sys_path );
+ }
+ file_url = sys_path;
+ }
+
+ OUString abs;
+ if (osl_getAbsoluteFileURL(
+ base_url.pData, file_url.pData, &abs.pData ) != osl_File_E_None)
+ {
+ throw RuntimeException(
+ "making absolute file url failed: \"" + base_url
+ + "\" (base-url) and \"" + file_url + "\" (file-url)!" );
+ }
+ return abs[ abs.getLength() -1 ] == '/'
+ ? abs.copy( 0, abs.getLength() -1 ) : abs;
+}
+
+
+namespace {
+
+
+void printf_space( sal_Int32 space )
+{
+ while (space--)
+ dp_misc::writeConsole(u" ");
+}
+
+
+void printf_line(
+ std::u16string_view name, std::u16string_view value, sal_Int32 level )
+{
+ printf_space( level );
+ dp_misc::writeConsole(OUStringConcatenation(OUString::Concat(name) + ": " + value + "\n"));
+}
+
+
+void printf_package(
+ Reference<deployment::XPackage> const & xPackage,
+ Reference<XCommandEnvironment> const & xCmdEnv, sal_Int32 level )
+{
+ beans::Optional< OUString > id(
+ level == 0
+ ? beans::Optional< OUString >(
+ true, dp_misc::getIdentifier( xPackage ) )
+ : xPackage->getIdentifier() );
+ if (id.IsPresent)
+ printf_line( u"Identifier", id.Value, level );
+ OUString version(xPackage->getVersion());
+ if (!version.isEmpty())
+ printf_line( u"Version", version, level + 1 );
+ printf_line( u"URL", xPackage->getURL(), level + 1 );
+
+ beans::Optional< beans::Ambiguous<sal_Bool> > option(
+ xPackage->isRegistered( Reference<task::XAbortChannel>(), xCmdEnv ) );
+ OUString value;
+ if (option.IsPresent) {
+ beans::Ambiguous<sal_Bool> const & reg = option.Value;
+ if (reg.IsAmbiguous)
+ value = "unknown";
+ else
+ value = reg.Value ? std::u16string_view(u"yes") : std::u16string_view(u"no");
+ }
+ else
+ value = "n/a";
+ printf_line( u"is registered", value, level + 1 );
+
+ const Reference<deployment::XPackageTypeInfo> xPackageType(
+ xPackage->getPackageType() );
+ OSL_ASSERT( xPackageType.is() );
+ if (xPackageType.is()) {
+ printf_line( u"Media-Type", xPackageType->getMediaType(), level + 1 );
+ }
+ printf_line( u"Description", xPackage->getDescription(), level + 1 );
+ if (!xPackage->isBundle())
+ return;
+
+ Sequence< Reference<deployment::XPackage> > seq(
+ xPackage->getBundle( Reference<task::XAbortChannel>(), xCmdEnv ) );
+ printf_space( level + 1 );
+ dp_misc::writeConsole(u"bundled Packages: {\n");
+ std::vector<Reference<deployment::XPackage> >vec_bundle;
+ ::comphelper::sequenceToContainer(vec_bundle, seq);
+ printf_packages( vec_bundle, std::vector<bool>(vec_bundle.size()),
+ xCmdEnv, level + 2 );
+ printf_space( level + 1 );
+ dp_misc::writeConsole(u"}\n");
+}
+
+} // anon namespace
+
+static void printf_unaccepted_licenses(
+ Reference<deployment::XPackage> const & ext)
+{
+ OUString id(
+ dp_misc::getIdentifier(ext) );
+ printf_line( u"Identifier", id, 0 );
+ printf_space(1);
+ dp_misc::writeConsole(u"License not accepted\n\n");
+}
+
+
+void printf_packages(
+ std::vector< Reference<deployment::XPackage> > const & allExtensions,
+ std::vector<bool> const & vecUnaccepted,
+ Reference<XCommandEnvironment> const & xCmdEnv, sal_Int32 level )
+{
+ OSL_ASSERT(allExtensions.size() == vecUnaccepted.size());
+
+ if (allExtensions.empty())
+ {
+ printf_space( level );
+ dp_misc::writeConsole(u"<none>\n");
+ }
+ else
+ {
+ int index = 0;
+ for (auto const& extension : allExtensions)
+ {
+ if (vecUnaccepted[index])
+ printf_unaccepted_licenses(extension);
+ else
+ printf_package( extension, xCmdEnv, level );
+ dp_misc::writeConsole(u"\n");
+ ++index;
+ }
+ }
+}
+
+
+namespace {
+
+
+Reference<XComponentContext> bootstrapStandAlone()
+{
+ Reference<XComponentContext> xContext =
+ ::cppu::defaultBootstrap_InitialComponentContext();
+
+ Reference<lang::XMultiServiceFactory> xServiceManager(
+ xContext->getServiceManager(), UNO_QUERY_THROW );
+ // set global process service factory used by unotools config helpers
+ ::comphelper::setProcessServiceFactory( xServiceManager );
+
+ // Initialize the UCB (for backwards compatibility, in case some code still
+ // uses plain createInstance w/o args directly to obtain an instance):
+ UniversalContentBroker::create( xContext );
+
+ return xContext;
+}
+
+
+Reference<XComponentContext> connectToOffice(
+ Reference<XComponentContext> const & xLocalComponentContext,
+ bool verbose )
+{
+ OUString pipeId( ::dp_misc::generateRandomPipeId() );
+ OUString acceptArg = "--accept=pipe,name=" + pipeId + ";urp;";
+
+ Sequence<OUString> args { "--nologo", "--nodefault", acceptArg };
+ OUString appURL( getExecutableDir() + "/soffice" );
+
+ if (verbose)
+ {
+ dp_misc::writeConsole(OUStringConcatenation(
+ "Raising process: " + appURL +
+ "\nArguments: --nologo --nodefault " + args[2] +
+ "\n"));
+ }
+
+ ::dp_misc::raiseProcess( appURL, args );
+
+ if (verbose)
+ dp_misc::writeConsole(u"OK. Connecting...");
+
+ OUString sUnoUrl = "uno:pipe,name=" + pipeId + ";urp;StarOffice.ComponentContext";
+ Reference<XComponentContext> xRet(
+ ::dp_misc::resolveUnoURL(
+ sUnoUrl, xLocalComponentContext ),
+ UNO_QUERY_THROW );
+ if (verbose)
+ dp_misc::writeConsole(u"OK.\n");
+
+ return xRet;
+}
+
+} // anon namespace
+
+/** returns the path to the lock file used by unopkg.
+ @return the path. An empty string signifies an error.
+*/
+static OUString getLockFilePath()
+{
+ OUString ret;
+ OUString sBootstrap("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}");
+ rtl::Bootstrap::expandMacros(sBootstrap);
+ OUString sAbs;
+ if (::osl::File::E_None == ::osl::File::getAbsoluteFileURL(
+ sBootstrap, ".lock", sAbs))
+ {
+ if (::osl::File::E_None ==
+ ::osl::File::getSystemPathFromFileURL(sAbs, sBootstrap))
+ {
+ ret = sBootstrap;
+ }
+ }
+
+ return ret;
+}
+
+Reference<XComponentContext> getUNO(
+ bool verbose, bool bGui, const OUString& sTempDir,
+ Reference<XComponentContext> & out_localContext)
+{
+ // do not create any user data (for the root user) in --shared mode:
+ if (!sTempDir.isEmpty())
+ rtl::Bootstrap::set("UserInstallation", sTempDir);
+
+ // hold lock during process runtime:
+ static ::desktop::Lockfile s_lockfile( false /* no IPC server */ );
+ Reference<XComponentContext> xComponentContext( bootstrapStandAlone() );
+ out_localContext = xComponentContext;
+ if (::dp_misc::office_is_running()) {
+ xComponentContext.set(
+ connectToOffice( xComponentContext, verbose ) );
+ }
+ else
+ {
+ if (! s_lockfile.check( nullptr ))
+ {
+ OUString sMsg(DpResId(RID_STR_CONCURRENTINSTANCE));
+ OUString sError(DpResId(RID_STR_UNOPKG_ERROR));
+
+ sMsg += "\n" + getLockFilePath();
+
+ if (bGui)
+ {
+ //We show a message box or print to the console that there
+ //is another instance already running
+ if ( ! InitVCL() )
+ throw RuntimeException( "Cannot initialize VCL!" );
+ {
+ std::unique_ptr<weld::MessageDialog> xWarn(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Warning, VclButtonsType::Ok,
+ sMsg));
+ xWarn->set_title(utl::ConfigManager::getProductName());
+ xWarn->run();
+ }
+ DeInitVCL();
+ }
+
+ throw LockFileException(sError + sMsg);
+ }
+ }
+
+ return xComponentContext;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/pkgchk/unopkg/unopkg_shared.h b/desktop/source/pkgchk/unopkg/unopkg_shared.h
new file mode 100644
index 000000000..21d0f6a92
--- /dev/null
+++ b/desktop/source/pkgchk/unopkg/unopkg_shared.h
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/deployment/XPackage.hpp>
+#include <osl/diagnose.h>
+#include <rtl/ustring.hxx>
+
+#include <utility>
+#include <vector>
+
+#define APP_NAME "unopkg"
+
+namespace unopkg {
+
+struct OptionInfo
+{
+ char const * m_name;
+ sal_uInt32 m_name_length;
+ sal_Unicode m_short_option;
+ bool m_has_argument;
+};
+
+struct LockFileException
+{
+ explicit LockFileException(OUString sMessage) :
+ Message(std::move(sMessage)) {}
+
+ OUString Message;
+};
+
+
+OUString toString( OptionInfo const * info );
+
+
+OptionInfo const * getOptionInfo(
+ OptionInfo const * list,
+ OUString const & opt );
+
+
+bool isOption( OptionInfo const * option_info, sal_uInt32 * pIndex );
+
+
+bool readArgument(
+ OUString * pValue, OptionInfo const * option_info,
+ sal_uInt32 * pIndex );
+
+
+inline bool readOption(
+ bool * flag, OptionInfo const * option_info, sal_uInt32 * pIndex )
+{
+ if (isOption( option_info, pIndex )) {
+ OSL_ASSERT( flag != nullptr );
+ *flag = true;
+ return true;
+ }
+ return false;
+}
+
+
+/** checks if an argument is a bootstrap variable. These start with -env:. For example
+ -env:UNO_JAVA_JFW_USER_DATA=file:///d:/user
+*/
+bool isBootstrapVariable(sal_uInt32 * pIndex);
+
+OUString const & getExecutableDir();
+
+
+OUString const & getProcessWorkingDir();
+
+
+OUString makeAbsoluteFileUrl(
+ OUString const & sys_path, OUString const & base_url );
+
+
+
+
+css::uno::Reference<css::ucb::XCommandEnvironment> createCmdEnv(
+ css::uno::Reference<css::uno::XComponentContext> const & xContext,
+ bool option_force_overwrite,
+ bool option_verbose,
+ bool option_suppressLicense);
+
+void printf_packages(
+ std::vector<
+ css::uno::Reference<css::deployment::XPackage> > const & allExtensions,
+ std::vector<bool> const & vecUnaccepted,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv,
+ sal_Int32 level = 0 );
+
+
+
+
+css::uno::Reference<css::uno::XComponentContext> getUNO(
+ bool verbose, bool bGui, const OUString& sTempDir,
+ css::uno::Reference<css::uno::XComponentContext> & out_LocalComponentContext);
+
+}
+
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */