nss: upgrade to release 3.73
[LibreOffice.git] / desktop / source / pkgchk / unopkg / unopkg_app.cxx
blob6c9f8ce00baea28f07b00b07796bcf724720971e
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <dp_misc.h>
22 #include "unopkg_main.h"
23 #include "unopkg_shared.h"
24 #include <dp_identifier.hxx>
25 #include <tools/extendapplicationenvironment.hxx>
26 #include <rtl/bootstrap.hxx>
27 #include <osl/process.h>
28 #include <osl/conditn.hxx>
29 #include <unotools/tempfile.hxx>
30 #include <cppuhelper/implbase.hxx>
31 #include <cppuhelper/exc_hlp.hxx>
32 #include <comphelper/anytostring.hxx>
33 #include <comphelper/logging.hxx>
34 #include <comphelper/sequence.hxx>
35 #include <com/sun/star/deployment/DeploymentException.hpp>
36 #include <com/sun/star/deployment/ExtensionManager.hpp>
38 #include <com/sun/star/deployment/ui/PackageManagerDialog.hpp>
39 #include <com/sun/star/lang/IllegalArgumentException.hpp>
40 #include <com/sun/star/logging/ConsoleHandler.hpp>
41 #include <com/sun/star/logging/FileHandler.hpp>
42 #include <com/sun/star/logging/LogLevel.hpp>
43 #include <com/sun/star/logging/SimpleTextFormatter.hpp>
44 #include <com/sun/star/logging/XLogger.hpp>
45 #include <com/sun/star/ucb/CommandAbortedException.hpp>
46 #include <com/sun/star/ucb/CommandFailedException.hpp>
47 #include <com/sun/star/ui/dialogs/XDialogClosedListener.hpp>
48 #if defined(UNX)
49 #include <unistd.h>
50 #endif
51 #include <vector>
54 using namespace ::com::sun::star;
55 using namespace ::com::sun::star::logging;
56 using namespace ::com::sun::star::uno;
57 using namespace ::unopkg;
59 namespace {
61 struct ExtensionName
63 OUString m_str;
64 explicit ExtensionName( OUString const & str ) : m_str( str ) {}
65 bool operator () ( Reference<deployment::XPackage> const & e ) const
67 return m_str == dp_misc::getIdentifier(e)
68 || m_str == e->getName();
73 const char s_usingText [] =
74 "\n"
75 "using: " APP_NAME " add <options> extension-path...\n"
76 " " APP_NAME " validate <options> extension-identifier...\n"
77 " " APP_NAME " remove <options> extension-identifier...\n"
78 " " APP_NAME " list <options> extension-identifier...\n"
79 " " APP_NAME " reinstall <options>\n"
80 " " APP_NAME " gui\n"
81 " " APP_NAME " -V\n"
82 " " APP_NAME " -h\n"
83 "\n"
84 "sub-commands:\n"
85 " add add extension\n"
86 " validate checks the prerequisites of an installed extension and\n"
87 " registers it if possible\n"
88 " remove remove extensions by identifier\n"
89 " reinstall expert feature: reinstall all deployed extensions\n"
90 " list list information about deployed extensions\n"
91 " gui raise Extension Manager Graphical User Interface (GUI)\n"
92 "\n"
93 "options:\n"
94 " -h, --help this help\n"
95 " -V, --version version information\n"
96 " -v, --verbose verbose output\n"
97 " -f, --force force overwriting existing extensions\n"
98 " -s, --suppress-license prevents showing the license\n"
99 " --log-file <file> custom log file; default: <cache-dir>/log.txt\n"
100 " --shared expert feature: operate on shared installation\n"
101 " deployment context;\n"
102 " run only when no concurrent Office\n"
103 " process(es) are running!\n"
104 " --bundled expert feature: operate on bundled extensions. Only\n"
105 " works with list, validate, reinstall;\n"
106 " --deployment-context expert feature: explicit deployment context\n"
107 " <context>\n"
108 "\n"
109 "To learn more about the Extension Manager and extensions, see:\n"
110 "http://wiki.openoffice.org/wiki/Documentation/DevGuide/Extensions/Extensions\n\n";
113 const OptionInfo s_option_infos [] = {
114 { RTL_CONSTASCII_STRINGPARAM("help"), 'h', false },
115 { RTL_CONSTASCII_STRINGPARAM("version"), 'V', false },
116 { RTL_CONSTASCII_STRINGPARAM("verbose"), 'v', false },
117 { RTL_CONSTASCII_STRINGPARAM("force"), 'f', false },
118 { RTL_CONSTASCII_STRINGPARAM("log-file"), '\0', true },
119 { RTL_CONSTASCII_STRINGPARAM("shared"), '\0', false },
120 { RTL_CONSTASCII_STRINGPARAM("deployment-context"), '\0', true },
121 { RTL_CONSTASCII_STRINGPARAM("bundled"), '\0', false},
122 { RTL_CONSTASCII_STRINGPARAM("suppress-license"), 's', false},
124 { nullptr, 0, '\0', false }
127 class DialogClosedListenerImpl :
128 public ::cppu::WeakImplHelper< ui::dialogs::XDialogClosedListener >
130 osl::Condition & m_rDialogClosedCondition;
132 public:
133 explicit DialogClosedListenerImpl( osl::Condition & rDialogClosedCondition )
134 : m_rDialogClosedCondition( rDialogClosedCondition ) {}
136 // XEventListener (base of XDialogClosedListener)
137 virtual void SAL_CALL disposing( lang::EventObject const & Source ) override;
139 // XDialogClosedListener
140 virtual void SAL_CALL dialogClosed(
141 ui::dialogs::DialogClosedEvent const & aEvent ) override;
144 // XEventListener (base of XDialogClosedListener)
145 void DialogClosedListenerImpl::disposing( lang::EventObject const & )
147 // nothing to do
150 // XDialogClosedListener
151 void DialogClosedListenerImpl::dialogClosed(
152 ui::dialogs::DialogClosedEvent const & )
154 m_rDialogClosedCondition.set();
157 // If a package had been installed with a pre OOo 2.2, it could not normally be
158 // found via its identifier; similarly (and for ease of use), a package
159 // installed with OOo 2.2 or later could not normally be found via its file
160 // name.
161 Reference<deployment::XPackage> findPackage(
162 OUString const & repository,
163 Reference<deployment::XExtensionManager> const & manager,
164 Reference<ucb::XCommandEnvironment > const & environment,
165 OUString const & idOrFileName )
167 const Sequence< Reference<deployment::XPackage> > ps(
168 manager->getDeployedExtensions(repository,
169 Reference<task::XAbortChannel>(), environment ) );
170 for ( auto const & package : ps )
171 if ( dp_misc::getIdentifier( package ) == idOrFileName )
172 return package;
173 for ( auto const & package : ps )
174 if ( package->getName() == idOrFileName )
175 return package;
176 return Reference<deployment::XPackage>();
179 } // anon namespace
181 extern "C" int unopkg_main()
183 tools::extendApplicationEnvironment();
184 bool bShowFailedMsg = true;
185 OUString subCommand;
186 bool option_shared = false;
187 bool option_force = false;
188 bool option_verbose = false;
189 bool option_bundled = false;
190 bool option_suppressLicense = false;
191 bool option_help = false;
192 bool subcmd_gui = false;
193 OUString logFile;
194 OUString repository;
195 OUString cmdArg;
196 std::vector<OUString> cmdPackages;
197 Reference<XLogHandler> xFileHandler;
198 Reference<XLogHandler> xConsoleHandler;
199 std::unique_ptr<comphelper::EventLogger> logger;
200 std::unique_ptr<utl::TempFile> pUserProfileTempDir;
202 OptionInfo const * info_shared = getOptionInfo(
203 s_option_infos, "shared" );
204 OptionInfo const * info_force = getOptionInfo(
205 s_option_infos, "force" );
206 OptionInfo const * info_verbose = getOptionInfo(
207 s_option_infos, "verbose" );
208 OptionInfo const * info_log = getOptionInfo(
209 s_option_infos, "log-file" );
210 OptionInfo const * info_context = getOptionInfo(
211 s_option_infos, "deployment-context" );
212 OptionInfo const * info_help = getOptionInfo(
213 s_option_infos, "help" );
214 OptionInfo const * info_version = getOptionInfo(
215 s_option_infos, "version" );
216 OptionInfo const * info_bundled = getOptionInfo(
217 s_option_infos, "bundled" );
218 OptionInfo const * info_suppressLicense = getOptionInfo(
219 s_option_infos, "suppress-license" );
222 Reference<XComponentContext> xComponentContext;
223 Reference<XComponentContext> xLocalComponentContext;
225 try {
226 sal_uInt32 nPos = 0;
227 sal_uInt32 nCount = osl_getCommandArgCount();
228 if (nCount == 0 || isOption( info_help, &nPos ))
230 dp_misc::writeConsole(s_usingText);
231 return 0;
233 else if (isOption( info_version, &nPos )) {
234 dp_misc::writeConsole("\n" APP_NAME " Version 3.3\n");
235 return 0;
237 //consume all bootstrap variables which may occur before the sub-command
238 while(isBootstrapVariable(&nPos))
241 if(nPos >= nCount)
242 return 0;
243 //get the sub-command
244 osl_getCommandArg( nPos, &subCommand.pData );
245 ++nPos;
246 subCommand = subCommand.trim();
247 bool subcmd_add = subCommand == "add";
248 subcmd_gui = subCommand == "gui";
250 // sub-command options and packages:
251 while (nPos < nCount)
253 if (readArgument( &cmdArg, info_log, &nPos )) {
254 logFile = makeAbsoluteFileUrl(
255 cmdArg.trim(), getProcessWorkingDir() );
257 else if (!readOption( &option_verbose, info_verbose, &nPos ) &&
258 !readOption( &option_shared, info_shared, &nPos ) &&
259 !readOption( &option_force, info_force, &nPos ) &&
260 !readOption( &option_bundled, info_bundled, &nPos ) &&
261 !readOption( &option_suppressLicense, info_suppressLicense, &nPos ) &&
262 !readOption( &option_help, info_help, &nPos ) &&
263 !readArgument( &repository, info_context, &nPos ) &&
264 !isBootstrapVariable(&nPos))
266 osl_getCommandArg( nPos, &cmdArg.pData );
267 ++nPos;
268 cmdArg = cmdArg.trim();
269 if (!cmdArg.isEmpty())
271 if (cmdArg[ 0 ] == '-')
273 // is option:
274 dp_misc::writeConsoleError(
275 "\nERROR: unexpected option " +
276 cmdArg +
277 "!\n Use " APP_NAME " " +
278 toString(info_help) +
279 " to print all options.\n");
280 return 1;
282 else
284 // is package:
285 cmdPackages.push_back(
286 subcmd_add || subcmd_gui
287 ? makeAbsoluteFileUrl(
288 cmdArg, getProcessWorkingDir() )
289 : cmdArg );
295 // tdf#129917 Use temp user profile when installing shared extensions
296 if (option_shared)
298 pUserProfileTempDir.reset(new utl::TempFile(nullptr, true));
299 pUserProfileTempDir->EnableKillingFile();
302 xComponentContext = getUNO(option_verbose, subcmd_gui,
303 pUserProfileTempDir ? pUserProfileTempDir->GetURL() : "",
304 xLocalComponentContext);
306 // Initialize logging. This will log errors to the console and
307 // also to file if the --log-file parameter was provided.
308 logger.reset(new comphelper::EventLogger(xLocalComponentContext, "unopkg"));
309 const Reference<XLogger> xLogger(logger->getLogger());
310 xLogger->setLevel(LogLevel::WARNING);
311 Reference<XLogFormatter> xLogFormatter(SimpleTextFormatter::create(xLocalComponentContext));
312 Sequence < beans::NamedValue > aSeq { { "Formatter", Any(xLogFormatter) } };
314 xConsoleHandler.set(ConsoleHandler::createWithSettings(xLocalComponentContext, aSeq));
315 xLogger->addLogHandler(xConsoleHandler);
316 xConsoleHandler->setLevel(LogLevel::WARNING);
317 xLogger->setLevel(LogLevel::WARNING);
320 if (!logFile.isEmpty())
322 Sequence < beans::NamedValue > aSeq2 { { "Formatter", Any(xLogFormatter) }, {"FileURL", Any(logFile)} };
323 xFileHandler.set(css::logging::FileHandler::createWithSettings(xLocalComponentContext, aSeq2));
324 xFileHandler->setLevel(LogLevel::WARNING);
325 xLogger->addLogHandler(xFileHandler);
328 if (option_verbose)
330 xLogger->setLevel(LogLevel::INFO);
331 xConsoleHandler->setLevel(LogLevel::INFO);
332 if (xFileHandler.is())
333 xFileHandler->setLevel(LogLevel::INFO);
336 if (repository.isEmpty())
338 if (option_shared)
339 repository = "shared";
340 else if (option_bundled)
341 repository = "bundled";
342 else
343 repository = "user";
345 else
347 if ( repository == "shared" ) {
348 option_shared = true;
350 else if (option_shared)
352 logger->log(LogLevel::WARNING, "Explicit context given! Ignoring option '$1$'", toString(info_shared));
355 #if defined(UNX)
356 if ( geteuid() == 0 )
358 if ( !(option_shared || option_bundled || option_help) )
360 logger->log(LogLevel::SEVERE, "Cannot run $1$ as root without $2$ or $3$ option.",
361 APP_NAME, toString(info_shared), toString(info_bundled));
362 return 1;
365 #endif
367 if (subCommand == "reinstall")
369 //We must prevent that services and types are loaded by UNO,
370 //otherwise we cannot delete the registry data folder.
371 OUString extensionUnorc;
372 if (repository == "user")
373 extensionUnorc = "$UNO_USER_PACKAGES_CACHE/registry/com.sun.star.comp.deployment.component.PackageRegistryBackend/unorc";
374 else if (repository == "shared")
375 extensionUnorc = "$SHARED_EXTENSIONS_USER/registry/com.sun.star.comp.deployment.component.PackageRegistryBackend/unorc";
376 else if (repository == "bundled")
377 extensionUnorc = "$BUNDLED_EXTENSIONS_USER/registry/com.sun.star.comp.deployment.component.PackageRegistryBackend/unorc";
378 else
379 OSL_ASSERT(false);
381 ::rtl::Bootstrap::expandMacros(extensionUnorc);
382 oslFileError e = osl_removeFile(extensionUnorc.pData);
383 if (e != osl_File_E_None && e != osl_File_E_NOENT)
384 throw Exception("Could not delete " + extensionUnorc, nullptr);
387 Reference<deployment::XExtensionManager> xExtensionManager(
388 deployment::ExtensionManager::get( xComponentContext ) );
390 Reference<css::ucb::XCommandEnvironment> xCmdEnv(
391 createCmdEnv(xComponentContext, option_force, option_verbose, option_suppressLicense));
393 //synchronize bundled/shared extensions
394 //Do not synchronize when command is "reinstall". This could add types and services to UNO and
395 //prevent the deletion of the registry data folder
396 //syncing is done in XExtensionManager.reinstall
397 if (!subcmd_gui && subCommand != "reinstall"
398 && ! dp_misc::office_is_running())
399 dp_misc::syncRepositories(false, xCmdEnv);
401 if ( subcmd_add || subCommand == "remove" )
403 for (const OUString & cmdPackage : cmdPackages)
405 if (subcmd_add)
407 beans::NamedValue nvSuppress(
408 "SUPPRESS_LICENSE", option_suppressLicense ?
409 makeAny(OUString("1")):makeAny(OUString("0")));
410 xExtensionManager->addExtension(
411 cmdPackage, Sequence<beans::NamedValue>(&nvSuppress, 1),
412 repository, Reference<task::XAbortChannel>(), xCmdEnv);
414 else
418 xExtensionManager->removeExtension(
419 cmdPackage, cmdPackage, repository,
420 Reference<task::XAbortChannel>(), xCmdEnv );
422 catch (const lang::IllegalArgumentException &)
424 Reference<deployment::XPackage> p(
425 findPackage(repository,
426 xExtensionManager, xCmdEnv, cmdPackage ) );
427 if ( !p.is())
428 throw;
429 else if (p.is())
430 xExtensionManager->removeExtension(
431 ::dp_misc::getIdentifier(p), p->getName(),
432 repository,
433 Reference<task::XAbortChannel>(), xCmdEnv );
438 else if ( subCommand == "reinstall" )
440 xExtensionManager->reinstallDeployedExtensions(
441 false, repository, Reference<task::XAbortChannel>(), xCmdEnv);
443 else if ( subCommand == "list" )
445 std::vector<Reference<deployment::XPackage> > vecExtUnaccepted;
446 ::comphelper::sequenceToContainer(vecExtUnaccepted,
447 xExtensionManager->getExtensionsWithUnacceptedLicenses(
448 repository, xCmdEnv));
450 //This vector tells what XPackage in allExtensions has an
451 //unaccepted license.
452 std::vector<bool> vecUnaccepted;
453 std::vector<Reference<deployment::XPackage> > allExtensions;
454 if (cmdPackages.empty())
456 Sequence< Reference<deployment::XPackage> >
457 packages = xExtensionManager->getDeployedExtensions(
458 repository, Reference<task::XAbortChannel>(), xCmdEnv );
460 std::vector<Reference<deployment::XPackage> > vec_packages;
461 ::comphelper::sequenceToContainer(vec_packages, packages);
463 //First copy the extensions with the unaccepted license
464 //to vector allExtensions.
465 allExtensions.resize(vecExtUnaccepted.size() + vec_packages.size());
467 std::vector<Reference<deployment::XPackage> >::iterator i_all_ext =
468 std::copy(vecExtUnaccepted.begin(), vecExtUnaccepted.end(),
469 allExtensions.begin());
470 //Now copy those we got from getDeployedExtensions
471 std::copy(vec_packages.begin(), vec_packages.end(), i_all_ext);
473 //Now prepare the vector which tells what extension has an
474 //unaccepted license
475 vecUnaccepted.resize(vecExtUnaccepted.size() + vec_packages.size());
476 std::fill_n(vecUnaccepted.begin(), vecExtUnaccepted.size(), true);
477 std::fill_n(vecUnaccepted.begin() + vecExtUnaccepted.size(),
478 vec_packages.size(), false);
480 dp_misc::writeConsole(
481 "All deployed " + repository + " extensions:\n\n");
483 else
485 //The user provided the names (ids or file names) of the extensions
486 //which shall be listed
487 for (const OUString & cmdPackage : cmdPackages)
489 Reference<deployment::XPackage> extension;
492 extension = xExtensionManager->getDeployedExtension(
493 repository, cmdPackage, cmdPackage, xCmdEnv );
495 catch (const lang::IllegalArgumentException &)
497 extension = findPackage(repository,
498 xExtensionManager, xCmdEnv, cmdPackage );
501 //Now look if the requested extension has an unaccepted license
502 bool bUnacceptedLic = false;
503 if (!extension.is())
505 std::vector<Reference<deployment::XPackage> >::const_iterator
506 i = std::find_if(
507 vecExtUnaccepted.begin(),
508 vecExtUnaccepted.end(), ExtensionName(cmdPackage));
509 if (i != vecExtUnaccepted.end())
511 extension = *i;
512 bUnacceptedLic = true;
516 if (!extension.is())
517 throw lang::IllegalArgumentException(
518 "There is no such extension deployed: " +
519 cmdPackage,nullptr,-1);
520 allExtensions.push_back(extension);
521 vecUnaccepted.push_back(bUnacceptedLic);
526 printf_packages(allExtensions, vecUnaccepted, xCmdEnv );
528 else if ( subCommand == "validate" )
530 std::vector<Reference<deployment::XPackage> > vecExtUnaccepted;
531 ::comphelper::sequenceToContainer(
532 vecExtUnaccepted, xExtensionManager->getExtensionsWithUnacceptedLicenses(
533 repository, xCmdEnv));
535 for (const OUString & cmdPackage : cmdPackages)
537 Reference<deployment::XPackage> extension;
540 extension = xExtensionManager->getDeployedExtension(
541 repository, cmdPackage, cmdPackage, xCmdEnv );
543 catch (const lang::IllegalArgumentException &)
545 extension = findPackage(
546 repository, xExtensionManager, xCmdEnv, cmdPackage );
549 if (!extension.is())
551 std::vector<Reference<deployment::XPackage> >::const_iterator
552 i = std::find_if(
553 vecExtUnaccepted.begin(),
554 vecExtUnaccepted.end(), ExtensionName(cmdPackage));
555 if (i != vecExtUnaccepted.end())
557 extension = *i;
561 if (extension.is())
562 xExtensionManager->checkPrerequisitesAndEnable(
563 extension, Reference<task::XAbortChannel>(), xCmdEnv);
566 else if ( subCommand == "gui" )
568 Reference<ui::dialogs::XAsynchronousExecutableDialog> xDialog(
569 deployment::ui::PackageManagerDialog::createAndInstall(
570 xComponentContext,
571 !cmdPackages.empty() ? cmdPackages[0] : OUString() ));
573 osl::Condition dialogEnded;
574 dialogEnded.reset();
576 Reference< ui::dialogs::XDialogClosedListener > xListener(
577 new DialogClosedListenerImpl( dialogEnded ) );
579 xDialog->startExecuteModal(xListener);
580 dialogEnded.wait();
581 return 0;
583 else
585 logger->log(LogLevel::SEVERE,
586 "Unknown sub-command: '$1$'. Use $2$ $3$ to print all options.",
587 subCommand, APP_NAME, toString(info_help));
588 return 1;
591 logger->log(LogLevel::INFO, "$1$ done.", APP_NAME);
592 //Force to release all bridges which connect us to the child processes
593 dp_misc::disposeBridges(xLocalComponentContext);
594 css::uno::Reference<css::lang::XComponent>(
595 xLocalComponentContext, css::uno::UNO_QUERY_THROW)->dispose();
596 return 0;
598 catch (const ucb::CommandFailedException &e)
600 logger->log(LogLevel::SEVERE, "Exception occurred: $1$", e.Message);
602 catch (const ucb::CommandAbortedException &)
604 logger->log(LogLevel::SEVERE, "$1$ aborted.", APP_NAME);
605 bShowFailedMsg = false;
607 catch (const deployment::DeploymentException & exc)
609 logger->log(LogLevel::SEVERE, "Exception occurred: $1$", exc.Message);
610 logger->log(LogLevel::INFO, " Cause: $1$", comphelper::anyToString(exc.Cause));
612 catch (const LockFileException & e)
614 // No logger since it requires UNO which we don't have here
615 dp_misc::writeConsoleError(e.Message + "\n");
616 bShowFailedMsg = false;
618 catch (const css::uno::Exception & e ) {
619 Any exc( ::cppu::getCaughtException() );
621 logger->log(LogLevel::SEVERE, "Exception occurred: $1$", e.Message);
622 logger->log(LogLevel::INFO, " Cause: $1$", comphelper::anyToString(exc));
624 if (bShowFailedMsg)
625 logger->log(LogLevel::SEVERE, "$1$ failed.", APP_NAME);
626 dp_misc::disposeBridges(xLocalComponentContext);
627 if (xLocalComponentContext.is()) {
628 css::uno::Reference<css::lang::XComponent>(
629 xLocalComponentContext, css::uno::UNO_QUERY_THROW)->dispose();
631 return 1;
635 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */