1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <config_features.h>
21 
22 #include <dp_registry.hxx>
23 #include <dp_shared.hxx>
24 #include <strings.hrc>
25 #include <dp_ucb.h>
26 #include <dp_resource.h>
27 #include <dp_platform.hxx>
28 #include "dp_manager.h"
29 #include <dp_identifier.hxx>
30 #include <rtl/ustrbuf.hxx>
31 #include <rtl/string.hxx>
32 #include <rtl/uri.hxx>
33 #include <rtl/bootstrap.hxx>
34 #include <sal/log.hxx>
35 #include <tools/urlobj.hxx>
36 #include <tools/diagnose_ex.h>
37 #include <osl/diagnose.h>
38 #include <osl/file.hxx>
39 #include <osl/security.hxx>
40 #include <cppuhelper/weakref.hxx>
41 #include <cppuhelper/exc_hlp.hxx>
42 #include <cppuhelper/interfacecontainer.hxx>
43 #include <comphelper/logging.hxx>
44 #include <comphelper/servicedecl.hxx>
45 #include <comphelper/sequence.hxx>
46 #include <xmlscript/xml_helper.hxx>
47 #include <svl/inettype.hxx>
48 #include <com/sun/star/lang/DisposedException.hpp>
49 #include <com/sun/star/lang/IllegalArgumentException.hpp>
50 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
51 #include <com/sun/star/beans/UnknownPropertyException.hpp>
52 #include <com/sun/star/logging/LogLevel.hpp>
53 #include <com/sun/star/logging/FileHandler.hpp>
54 #include <com/sun/star/logging/SimpleTextFormatter.hpp>
55 #include <com/sun/star/logging/XLogger.hpp>
56 #include <com/sun/star/util/XUpdatable.hpp>
57 #include <com/sun/star/sdbc/XResultSet.hpp>
58 #include <com/sun/star/sdbc/XRow.hpp>
59 #include <com/sun/star/ucb/CommandAbortedException.hpp>
60 #include <com/sun/star/ucb/CommandFailedException.hpp>
61 #include <com/sun/star/ucb/ContentCreationException.hpp>
62 #include <com/sun/star/ucb/XContentAccess.hpp>
63 #include <com/sun/star/ucb/NameClash.hpp>
64 #include <com/sun/star/deployment/DeploymentException.hpp>
65 #include <com/sun/star/deployment/InvalidRemovedParameterException.hpp>
66 #include <com/sun/star/deployment/VersionException.hpp>
67 #include <com/sun/star/deployment/InstallException.hpp>
68 #include <com/sun/star/deployment/Prerequisites.hpp>
69 #include <com/sun/star/task/XInteractionApprove.hpp>
70 #include <com/sun/star/ucb/UnsupportedCommandException.hpp>
71 #include <unotools/tempfile.hxx>
72 
73 #include <dp_descriptioninfoset.hxx>
74 #include "dp_commandenvironments.hxx"
75 #include "dp_properties.hxx"
76 
77 #include <vector>
78 #include <list>
79 #include <algorithm>
80 
81 using namespace ::dp_misc;
82 using namespace ::com::sun::star;
83 using namespace ::com::sun::star::uno;
84 using namespace ::com::sun::star::ucb;
85 using namespace ::com::sun::star::logging;
86 
87 namespace dp_log {
88 extern comphelper::service_decl::ServiceDecl const serviceDecl;
89 }
90 
91 namespace dp_manager {
92 
93 struct MatchTempDir
94 {
95     OUString m_str;
MatchTempDirdp_manager::MatchTempDir96     explicit MatchTempDir( OUString const & str ) : m_str( str ) {}
operator ()dp_manager::MatchTempDir97     bool operator () ( ActivePackages::Entries::value_type const & v ) const {
98         return v.second.temporaryName.equalsIgnoreAsciiCase( m_str );
99     }
100 };
101 
102 
103 namespace {
getExtensionFolder(OUString const & parentFolder,Reference<ucb::XCommandEnvironment> const & xCmdEnv,Reference<uno::XComponentContext> const & xContext)104 OUString getExtensionFolder(OUString const &  parentFolder,
105                             Reference<ucb::XCommandEnvironment> const & xCmdEnv,
106                             Reference<uno::XComponentContext> const & xContext)
107 {
108     ::ucbhelper::Content tempFolder( parentFolder, xCmdEnv, xContext );
109     Reference<sdbc::XResultSet> xResultSet(
110                 StrTitle::createCursor (tempFolder, ::ucbhelper::INCLUDE_FOLDERS_ONLY ) );
111 
112     OUString title;
113     if (xResultSet->next())
114     {
115         title = Reference<sdbc::XRow>(
116             xResultSet, UNO_QUERY_THROW )->getString(1 /* Title */ ) ;
117     }
118     return title;
119 }
120 }
121 
initActivationLayer(Reference<XCommandEnvironment> const & xCmdEnv)122 void PackageManagerImpl::initActivationLayer(
123     Reference<XCommandEnvironment> const & xCmdEnv )
124 {
125     if (m_activePackages.isEmpty())
126     {
127         OSL_ASSERT( m_registryCache.isEmpty() );
128         // documents temp activation:
129         m_activePackagesDB.reset( new ActivePackages );
130         ::ucbhelper::Content ucbContent;
131         if (create_ucb_content( &ucbContent, m_context, xCmdEnv,
132                                 false /* no throw */ ))
133         {
134             // scan for all entries in m_packagesDir:
135             Reference<sdbc::XResultSet> xResultSet(
136                         StrTitle::createCursor (ucbContent, ::ucbhelper::INCLUDE_FOLDERS_AND_DOCUMENTS ) );
137 
138             while (xResultSet->next())
139             {
140                 Reference<sdbc::XRow> xRow( xResultSet, UNO_QUERY_THROW );
141                 OUString title( xRow->getString( 1 /* Title */ ) );
142                 // xxx todo: remove workaround for tdoc
143                 if ( title == "this_is_a_dummy_stream_just_there_as_a_workaround_for_a_temporary_limitation_of_the_storage_api_implementation" )
144                     continue;
145                 if ( title == "META-INF" )
146                     continue;
147 
148                 ::ucbhelper::Content sourceContent(
149                     Reference<XContentAccess>(
150                         xResultSet, UNO_QUERY_THROW )->queryContent(),
151                     xCmdEnv, m_xComponentContext );
152 
153                 OUString mediaType( detectMediaType( sourceContent,
154                                                      false /* no throw */) );
155                 if (!mediaType.isEmpty())
156                 {
157                     ActivePackages::Data dbData;
158                     insertToActivationLayer(
159                         Sequence<css::beans::NamedValue>(),mediaType, sourceContent,
160                         title, &dbData );
161 
162                     insertToActivationLayerDB( title, dbData );
163                         //TODO #i73136#: insertToActivationLayerDB needs id not
164                         // title, but the whole m_activePackages.getLength()==0
165                         // case (i.e., document-relative deployment) currently
166                         // does not work, anyway.
167                 }
168             }
169         }
170     }
171     else
172     {
173         // user|share:
174         OSL_ASSERT( !m_activePackages.isEmpty() );
175         m_activePackages_expanded = expandUnoRcUrl( m_activePackages );
176         m_registrationData_expanded = expandUnoRcUrl(m_registrationData);
177         if (!m_readOnly)
178             create_folder( nullptr, m_activePackages_expanded, xCmdEnv);
179 
180         OUString dbName;
181         if (m_context == "user")
182             dbName = m_activePackages_expanded + ".pmap";
183         else
184         {
185             // Create the extension data base in the user installation
186             create_folder( nullptr, m_registrationData_expanded, xCmdEnv);
187             dbName = m_registrationData_expanded + "/extensions.pmap";
188         }
189         // The data base can always be written because it is always in the user installation
190         m_activePackagesDB.reset( new ActivePackages( dbName ) );
191 
192         if (! m_readOnly && m_context != "bundled")
193         {
194             // clean up activation layer, scan for zombie temp dirs:
195             ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() );
196 
197             ::ucbhelper::Content tempFolder( m_activePackages_expanded, xCmdEnv, m_xComponentContext );
198             Reference<sdbc::XResultSet> xResultSet(
199                 StrTitle::createCursor (tempFolder,
200                                          ::ucbhelper::INCLUDE_DOCUMENTS_ONLY ) );
201 
202             // get all temp directories:
203             std::vector<OUString> tempEntries;
204             std::vector<OUString> removedEntries;
205             while (xResultSet->next())
206             {
207                 OUString title(
208                     Reference<sdbc::XRow>(
209                         xResultSet, UNO_QUERY_THROW )->getString(
210                             1 /* Title */ ) );
211                 if (title.endsWith("removed", &title))
212                 {
213                     //save the file name without the "removed" part
214                     removedEntries.push_back(::rtl::Uri::encode(
215                                                 title, rtl_UriCharClassPchar,
216                                                 rtl_UriEncodeIgnoreEscapes,
217                                                 RTL_TEXTENCODING_UTF8 ) );
218                 }
219                 else
220                 {
221                     tempEntries.push_back( ::rtl::Uri::encode(
222                                                title, rtl_UriCharClassPchar,
223                                                rtl_UriEncodeIgnoreEscapes,
224                                                RTL_TEXTENCODING_UTF8 ) );
225                 }
226             }
227 
228             bool bShared = (m_context == "shared");
229             for (const OUString & tempEntry : tempEntries)
230             {
231                 const MatchTempDir match( tempEntry );
232                 if (std::none_of( id2temp.begin(), id2temp.end(), match ))
233                 {
234                     const OUString url(
235                         makeURL(m_activePackages_expanded, tempEntry ) );
236 
237                     //In case of shared extensions, new entries are regarded as
238                     //added extensions if there is no xxx.tmpremoved file.
239                     if (bShared)
240                     {
241                         if (std::find(removedEntries.begin(), removedEntries.end(), tempEntry) ==
242                             removedEntries.end())
243                         {
244                             continue;
245                         }
246                         else
247                         {
248                             //Make sure only the same user removes the extension, who
249                             //previously unregistered it. This is avoid races if multiple instances
250                             //of OOo are running which all have write access to the shared installation.
251                             //For example, a user removes the extension, but keeps OOo
252                             //running. Parts of the extension may still be loaded and used by OOo.
253                             //Therefore the extension is only deleted the next time the extension manager is
254                             //run after restarting OOo. While OOo is still running, another user starts OOo
255                             //which would deleted the extension files. If the same user starts another
256                             //instance of OOo then the lock file will prevent this.
257                             OUString aUserName;
258                             ::osl::Security aSecurity;
259                             aSecurity.getUserName( aUserName );
260                             ucbhelper::Content remFileContent(
261                                 url + "removed", Reference<XCommandEnvironment>(), m_xComponentContext);
262                             std::vector<sal_Int8> data = dp_misc::readFile(remFileContent);
263                             OString osData(reinterpret_cast<const sal_Char*>(data.data()),
264                                                   data.size());
265                             OUString sData = OStringToOUString(
266                                 osData, RTL_TEXTENCODING_UTF8);
267                             if (sData != aUserName)
268                                 continue;
269                         }
270                     }
271                     // temp entry not needed anymore:
272                     erase_path( url + "_",
273                                 Reference<XCommandEnvironment>(),
274                                 false /* no throw: ignore errors */ );
275                     erase_path( url, Reference<XCommandEnvironment>(),
276                                 false /* no throw: ignore errors */ );
277                     //delete the xxx.tmpremoved file
278                     erase_path(url + "removed",
279                                Reference<XCommandEnvironment>(), false);
280                 }
281             }
282         }
283     }
284 }
285 
286 
initRegistryBackends()287 void PackageManagerImpl::initRegistryBackends()
288 {
289     if (!m_registryCache.isEmpty())
290         create_folder( nullptr, m_registryCache,
291                        Reference<XCommandEnvironment>(), false);
292     m_xRegistry.set( ::dp_registry::create(
293                          m_context, m_registryCache,
294                          m_xComponentContext ) );
295 }
296 
297 namespace {
298 
createDirectory(OUString const & url)299 osl::FileBase::RC createDirectory(OUString const & url) {
300     auto e = osl::Directory::create(url);
301     if (e != osl::FileBase::E_NOENT) {
302         return e;
303     }
304     INetURLObject o(url);
305     if (!o.removeSegment()) {
306         return osl::FileBase::E_INVAL; // anything but E_None/E_EXIST
307     }
308     e = createDirectory(o.GetMainURL(INetURLObject::DecodeMechanism::NONE));
309     if (e != osl::FileBase::E_None && e != osl::FileBase::E_EXIST) {
310         return e;
311     }
312     return osl::Directory::create(url);
313 }
314 
isMacroURLReadOnly(const OUString & rMacro)315 bool isMacroURLReadOnly( const OUString &rMacro )
316 {
317     OUString aDirURL( rMacro );
318     ::rtl::Bootstrap::expandMacros( aDirURL );
319 
320     ::osl::FileBase::RC aErr = createDirectory( aDirURL );
321     if ( aErr == ::osl::FileBase::E_None )
322         return false; // it will be writeable
323     if ( aErr != ::osl::FileBase::E_EXIST )
324         return true; // some serious problem creating it
325 
326     bool bError;
327     sal_uInt64 nWritten = 0;
328     OUString aFileURL( aDirURL + "/stamp.sys" );
329     ::osl::File aFile( aFileURL );
330 
331     bError = aFile.open( osl_File_OpenFlag_Read |
332                          osl_File_OpenFlag_Write |
333                          osl_File_OpenFlag_Create ) != ::osl::FileBase::E_None;
334     if (!bError)
335         bError = aFile.write( "1", 1, nWritten ) != ::osl::FileBase::E_None;
336     if (aFile.close() != ::osl::FileBase::E_None)
337         bError = true;
338     if (osl::File::remove( aFileURL ) != ::osl::FileBase::E_None)
339         bError = true;
340 
341     SAL_INFO(
342         "desktop.deployment",
343         "local url '" << rMacro << "' -> '" << aFileURL << "' "
344             << (bError ? "is" : "is not") << " readonly\n");
345     return bError;
346 }
347 
348 }
349 
create(Reference<XComponentContext> const & xComponentContext,OUString const & context)350 Reference<deployment::XPackageManager> PackageManagerImpl::create(
351     Reference<XComponentContext> const & xComponentContext,
352     OUString const & context )
353 {
354     PackageManagerImpl * that = new PackageManagerImpl(
355         xComponentContext, context );
356     Reference<deployment::XPackageManager> xPackageManager( that );
357 
358     OUString logFile, stamp;
359     if ( context == "user" ) {
360         that->m_activePackages = "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE/uno_packages";
361         that->m_registrationData = "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE";
362         that->m_registryCache = "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE/registry";
363         logFile = "$UNO_USER_PACKAGES_CACHE/log.txt";
364         //We use the extension .sys for the file because on Windows Vista a sys
365         //(as well as exe and dll) file
366         //will not be written in the VirtualStore. For example if the process has no
367         //admin right once cannot write to the %programfiles% folder. However, when
368         //virtualization is used, the file will be written into the VirtualStore and
369         //it appears as if one could write to %programfiles%. When we test for write
370         //access to the office/shared folder for shared extensions then this typically
371         //fails because a normal user typically cannot write to this folder. However,
372         //using virtualization it appears that he/she can. Then a shared extension can
373         //be installed but is only visible for the user (because the extension is in
374         //the virtual store).
375         stamp = "$UNO_USER_PACKAGES_CACHE";
376     }
377     else if ( context == "shared" ) {
378         that->m_activePackages = "vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE/uno_packages";
379         that->m_registrationData = "vnd.sun.star.expand:$SHARED_EXTENSIONS_USER";
380         that->m_registryCache = "vnd.sun.star.expand:$SHARED_EXTENSIONS_USER/registry";
381         logFile = "$SHARED_EXTENSIONS_USER/log.txt";
382 #if !HAVE_FEATURE_READONLY_INSTALLSET
383         // The "shared" extensions are read-only when we have a
384         // read-only installset.
385         stamp = "$UNO_SHARED_PACKAGES_CACHE";
386 #endif
387     }
388     else if ( context == "bundled" ) {
389         that->m_activePackages = "vnd.sun.star.expand:$BUNDLED_EXTENSIONS";
390         that->m_registrationData = "vnd.sun.star.expand:$BUNDLED_EXTENSIONS_USER";
391         that->m_registryCache = "vnd.sun.star.expand:$BUNDLED_EXTENSIONS_USER/registry";
392         logFile = "$BUNDLED_EXTENSIONS_USER/log.txt";
393         //No stamp file. We assume that bundled is always readonly. It must not be
394         //modified from ExtensionManager but only by the installer
395     }
396     else if ( context == "tmp" ) {
397         that->m_activePackages = "vnd.sun.star.expand:$TMP_EXTENSIONS/extensions";
398         that->m_registrationData = "vnd.sun.star.expand:$TMP_EXTENSIONS";
399         that->m_registryCache = "vnd.sun.star.expand:$TMP_EXTENSIONS/registry";
400         stamp = "$TMP_EXTENSIONS";
401     }
402     else if (context == "bak") {
403         that->m_activePackages = "vnd.sun.star.expand:$BAK_EXTENSIONS/extensions";
404         that->m_registrationData = "vnd.sun.star.expand:$BAK_EXTENSIONS";
405         that->m_registryCache = "vnd.sun.star.expand:$BAK_EXTENSIONS/registry";
406         stamp = "$BAK_EXTENSIONS";
407     }
408 
409     else if (! context.match("vnd.sun.star.tdoc:/")) {
410         throw lang::IllegalArgumentException(
411             "invalid context given: " + context,
412             Reference<XInterface>(), static_cast<sal_Int16>(-1) );
413     }
414 
415     Reference<XCommandEnvironment> xCmdEnv;
416 
417     try {
418         // There is no stamp for the bundled folder:
419         if (!stamp.isEmpty())
420             that->m_readOnly = isMacroURLReadOnly( stamp );
421 
422         if (!that->m_readOnly && !logFile.isEmpty())
423         {
424             // Initialize logger which will be used in ProgressLogImpl (created below)
425             rtl::Bootstrap::expandMacros(logFile);
426             comphelper::EventLogger logger(xComponentContext, "unopkg");
427             const Reference<XLogger> xLogger(logger.getLogger());
428             Reference<XLogFormatter> xLogFormatter(SimpleTextFormatter::create(xComponentContext));
429             Sequence < beans::NamedValue > aSeq2 { { "Formatter", Any(xLogFormatter) }, {"FileURL", Any(logFile)} };
430             Reference<XLogHandler> xFileHandler(css::logging::FileHandler::createWithSettings(xComponentContext, aSeq2));
431             xFileHandler->setLevel(LogLevel::WARNING);
432             xLogger->addLogHandler(xFileHandler);
433 
434             that->m_xLogFile.set(
435                 that->m_xComponentContext->getServiceManager()
436                 ->createInstanceWithArgumentsAndContext(
437                     dp_log::serviceDecl.getSupportedServiceNames()[0],
438                     Sequence<Any>(),
439                     that->m_xComponentContext ),
440                 UNO_QUERY_THROW );
441             xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv, that->m_xLogFile ) );
442         }
443 
444         that->initRegistryBackends();
445         that->initActivationLayer( xCmdEnv );
446 
447         return xPackageManager;
448 
449     }
450     catch (const RuntimeException &) {
451         throw;
452     }
453     catch (const Exception & e) {
454         Any exc( ::cppu::getCaughtException() );
455         throw lang::WrappedTargetRuntimeException(
456             ("[context=\"" + context + "\"] caught unexpected "
457              + exc.getValueType().getTypeName() + ": " + e.Message),
458             Reference<XInterface>(), exc );
459     }
460 }
461 
462 
~PackageManagerImpl()463 PackageManagerImpl::~PackageManagerImpl()
464 {
465 }
466 
467 
fireModified()468 void PackageManagerImpl::fireModified()
469 {
470     ::cppu::OInterfaceContainerHelper * pContainer = rBHelper.getContainer(
471         cppu::UnoType<util::XModifyListener>::get() );
472     if (pContainer != nullptr) {
473         pContainer->forEach<util::XModifyListener>(
474             [this] (uno::Reference<util::XModifyListener> const& xListener)
475                 { return xListener->modified(lang::EventObject(static_cast<OWeakObject *>(this))); });
476     }
477 }
478 
479 
disposing()480 void PackageManagerImpl::disposing()
481 {
482     try {
483 //     // xxx todo: guarding?
484 //     ::osl::MutexGuard guard( getMutex() );
485         try_dispose( m_xLogFile );
486         m_xLogFile.clear();
487         try_dispose( m_xRegistry );
488         m_xRegistry.clear();
489         m_activePackagesDB.reset();
490         m_xComponentContext.clear();
491 
492         t_pm_helper::disposing();
493 
494     }
495     catch (const RuntimeException &) {
496         throw;
497     }
498     catch (const Exception &) {
499         Any exc( ::cppu::getCaughtException() );
500         throw lang::WrappedTargetRuntimeException(
501             "caught unexpected exception while disposing...",
502             static_cast<OWeakObject *>(this), exc );
503     }
504 }
505 
506 // XComponent
507 
dispose()508 void PackageManagerImpl::dispose()
509 {
510     //Do not call check here. We must not throw an exception here if the object
511     //is being disposed or is already disposed. See com.sun.star.lang.XComponent
512     WeakComponentImplHelperBase::dispose();
513 }
514 
515 
addEventListener(Reference<lang::XEventListener> const & xListener)516 void PackageManagerImpl::addEventListener(
517     Reference<lang::XEventListener> const & xListener )
518 {
519     //Do not call check here. We must not throw an exception here if the object
520     //is being disposed or is already disposed. See com.sun.star.lang.XComponent
521     WeakComponentImplHelperBase::addEventListener( xListener );
522 }
523 
524 
removeEventListener(Reference<lang::XEventListener> const & xListener)525 void PackageManagerImpl::removeEventListener(
526     Reference<lang::XEventListener> const & xListener )
527 {
528     //Do not call check here. We must not throw an exception here if the object
529     //is being disposed or is already disposed. See com.sun.star.lang.XComponent
530     WeakComponentImplHelperBase::removeEventListener( xListener );
531 }
532 
533 // XPackageManager
534 
getContext()535 OUString PackageManagerImpl::getContext()
536 {
537     check();
538     return m_context;
539 }
540 
541 
542 Sequence< Reference<deployment::XPackageTypeInfo> >
getSupportedPackageTypes()543 PackageManagerImpl::getSupportedPackageTypes()
544 {
545     OSL_ASSERT( m_xRegistry.is() );
546     return m_xRegistry->getSupportedPackageTypes();
547 }
548 
549 
createAbortChannel()550 Reference<task::XAbortChannel> PackageManagerImpl::createAbortChannel()
551 {
552     check();
553     return new AbortChannel;
554 }
555 
556 // XModifyBroadcaster
557 
addModifyListener(Reference<util::XModifyListener> const & xListener)558 void PackageManagerImpl::addModifyListener(
559     Reference<util::XModifyListener> const & xListener )
560 {
561     check();
562     rBHelper.addListener( cppu::UnoType<decltype(xListener)>::get(), xListener );
563 }
564 
565 
removeModifyListener(Reference<util::XModifyListener> const & xListener)566 void PackageManagerImpl::removeModifyListener(
567     Reference<util::XModifyListener> const & xListener )
568 {
569     check();
570     rBHelper.removeListener( cppu::UnoType<decltype(xListener)>::get(), xListener );
571 }
572 
573 
detectMediaType(::ucbhelper::Content const & ucbContent_,bool throw_exc)574 OUString PackageManagerImpl::detectMediaType(
575     ::ucbhelper::Content const & ucbContent_, bool throw_exc )
576 {
577     ::ucbhelper::Content ucbContent(ucbContent_);
578     OUString url( ucbContent.getURL() );
579     OUString mediaType;
580     if (url.match( "vnd.sun.star.tdoc:" ) || url.match( "vnd.sun.star.pkg:" ))
581     {
582         try {
583             ucbContent.getPropertyValue( "MediaType" ) >>= mediaType;
584         }
585         catch (const beans::UnknownPropertyException &) {
586         }
587         OSL_ENSURE( !mediaType.isEmpty(), "### no media-type?!" );
588     }
589     if (mediaType.isEmpty())
590     {
591         try {
592             Reference<deployment::XPackage> xPackage(
593                 m_xRegistry->bindPackage(
594                     url, OUString(), false, OUString(), ucbContent.getCommandEnvironment() ) );
595             const Reference<deployment::XPackageTypeInfo> xPackageType(
596                 xPackage->getPackageType() );
597             OSL_ASSERT( xPackageType.is() );
598             if (xPackageType.is())
599                 mediaType = xPackageType->getMediaType();
600         }
601         catch (const lang::IllegalArgumentException &) {
602             if (throw_exc)
603                 throw;
604             css::uno::Any ex( cppu::getCaughtException() );
605             SAL_WARN( "desktop", exceptionToString(ex) );
606         }
607     }
608     return mediaType;
609 }
610 
611 
insertToActivationLayer(Sequence<beans::NamedValue> const & properties,OUString const & mediaType,::ucbhelper::Content const & sourceContent_,OUString const & title,ActivePackages::Data * dbData)612 OUString PackageManagerImpl::insertToActivationLayer(
613     Sequence<beans::NamedValue> const & properties,
614     OUString const & mediaType, ::ucbhelper::Content const & sourceContent_,
615     OUString const & title, ActivePackages::Data * dbData )
616 {
617     ::ucbhelper::Content sourceContent(sourceContent_);
618     Reference<XCommandEnvironment> xCmdEnv(
619         sourceContent.getCommandEnvironment() );
620 
621     OUString baseDir(m_activePackages_expanded);
622     ::utl::TempFile aTemp(&baseDir, false);
623     OUString tempEntry = aTemp.GetURL();
624     tempEntry = tempEntry.copy(tempEntry.lastIndexOf('/') + 1);
625     OUString destFolder = makeURL( m_activePackages, tempEntry) + "_";
626 
627     // prepare activation folder:
628     ::ucbhelper::Content destFolderContent;
629     create_folder( &destFolderContent, destFolder, xCmdEnv );
630 
631     // copy content into activation temp dir:
632     if (mediaType.matchIgnoreAsciiCase("application/vnd.sun.star.package-bundle") ||
633         // xxx todo: more sophisticated parsing
634         mediaType.matchIgnoreAsciiCase("application/vnd.sun.star.legacy-package-bundle"))
635     {
636         // inflate content:
637         OUStringBuffer buf;
638         if (!sourceContent.isFolder())
639         {
640             buf.append( "vnd.sun.star.zip://" );
641             buf.append( ::rtl::Uri::encode( sourceContent.getURL(),
642                                             rtl_UriCharClassRegName,
643                                             rtl_UriEncodeIgnoreEscapes,
644                                             RTL_TEXTENCODING_UTF8 ) );
645         }
646         else
647         {
648             //Folder. No need to unzip, just copy
649             buf.append(sourceContent.getURL());
650         }
651         buf.append( '/' );
652         sourceContent = ::ucbhelper::Content(
653             buf.makeStringAndClear(), xCmdEnv, m_xComponentContext );
654     }
655     destFolderContent.transferContent(
656             sourceContent, ::ucbhelper::InsertOperation::Copy,
657             title, NameClash::OVERWRITE );
658 
659 
660     // write to DB:
661     //bundled extensions should only be added by the synchronizeAddedExtensions
662     //functions. Moreover, there is no "temporary folder" for bundled extensions.
663     OSL_ASSERT(!(m_context == "bundled"));
664     OUString sFolderUrl = makeURLAppendSysPathSegment(destFolderContent.getURL(), title);
665     DescriptionInfoset info =
666         dp_misc::getDescriptionInfoset(sFolderUrl);
667     dbData->temporaryName = tempEntry;
668     dbData->fileName = title;
669     dbData->mediaType = mediaType;
670     dbData->version = info.getVersion();
671 
672     //No write the properties file next to the extension
673     ExtensionProperties props(sFolderUrl, properties, xCmdEnv, m_xComponentContext);
674     props.write();
675     return destFolder;
676 }
677 
678 
insertToActivationLayerDB(OUString const & id,ActivePackages::Data const & dbData)679 void PackageManagerImpl::insertToActivationLayerDB(
680     OUString const & id, ActivePackages::Data const & dbData )
681 {
682     //access to the database must be guarded. See removePackage
683     const ::osl::MutexGuard guard( getMutex() );
684     m_activePackagesDB->put( id, dbData );
685 }
686 
687 
688 /* The function returns true if there is an extension with the same id already
689     installed which needs to be uninstalled, before the new extension can be installed.
690 */
isInstalled(Reference<deployment::XPackage> const & package)691 bool PackageManagerImpl::isInstalled(
692     Reference<deployment::XPackage> const & package)
693 {
694     OUString id(dp_misc::getIdentifier(package));
695     OUString fn(package->getName());
696     bool bInstalled = false;
697     if (m_activePackagesDB->has( id, fn ))
698     {
699         bInstalled = true;
700     }
701     return bInstalled;
702 }
703 
704 // XPackageManager
705 
importExtension(Reference<deployment::XPackage> const & extension,Reference<task::XAbortChannel> const & xAbortChannel,Reference<XCommandEnvironment> const & xCmdEnv_)706 Reference<deployment::XPackage> PackageManagerImpl::importExtension(
707     Reference<deployment::XPackage> const & extension,
708     Reference<task::XAbortChannel> const & xAbortChannel,
709     Reference<XCommandEnvironment> const & xCmdEnv_ )
710 {
711     return addPackage(extension->getURL(), Sequence<beans::NamedValue>(),
712                       OUString(), xAbortChannel, xCmdEnv_);
713 }
714 
715 /* The function adds an extension but does not register it!!!
716     It may not do any user interaction. This is done in XExtensionManager::addExtension
717 */
addPackage(OUString const & url,css::uno::Sequence<css::beans::NamedValue> const & properties,OUString const & mediaType_,Reference<task::XAbortChannel> const & xAbortChannel,Reference<XCommandEnvironment> const & xCmdEnv_)718 Reference<deployment::XPackage> PackageManagerImpl::addPackage(
719     OUString const & url,
720     css::uno::Sequence<css::beans::NamedValue> const & properties,
721     OUString const & mediaType_,
722     Reference<task::XAbortChannel> const & xAbortChannel,
723     Reference<XCommandEnvironment> const & xCmdEnv_ )
724 {
725     check();
726     if (m_readOnly)
727     {
728         OUString message;
729         if (m_context == "shared")
730             message = "You need write permissions to install a shared extension!";
731         else
732             message = "You need write permissions to install this extension!";
733         throw deployment::DeploymentException(
734             message, static_cast<OWeakObject *>(this), Any() );
735     }
736     Reference<XCommandEnvironment> xCmdEnv;
737     if (m_xLogFile.is())
738         xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) );
739     else
740         xCmdEnv.set( xCmdEnv_ );
741 
742     try {
743         ::ucbhelper::Content sourceContent;
744         (void)create_ucb_content( &sourceContent, url, xCmdEnv ); // throws exc
745         const OUString title( StrTitle::getTitle( sourceContent ) );
746         const OUString title_enc( ::rtl::Uri::encode(
747                                       title, rtl_UriCharClassPchar,
748                                       rtl_UriEncodeIgnoreEscapes,
749                                       RTL_TEXTENCODING_UTF8 ) );
750         OUString destFolder;
751 
752         OUString mediaType(mediaType_);
753         if (mediaType.isEmpty())
754             mediaType = detectMediaType( sourceContent );
755 
756         Reference<deployment::XPackage> xPackage;
757         // copy file:
758         progressUpdate(
759             DpResId(RID_STR_COPYING_PACKAGE) + title, xCmdEnv );
760         if (m_activePackages.isEmpty())
761         {
762             ::ucbhelper::Content docFolderContent;
763             create_folder( &docFolderContent, m_context, xCmdEnv );
764             // copy into document, first:
765             docFolderContent.transferContent(
766                     sourceContent, ::ucbhelper::InsertOperation::Copy,
767                     OUString(),
768                     NameClash::ASK /* xxx todo: ASK not needed? */);
769             // set media-type:
770             ::ucbhelper::Content docContent(
771                 makeURL( m_context, title_enc ), xCmdEnv, m_xComponentContext );
772                 //TODO #i73136#: using title instead of id can lead to
773                 // clashes, but the whole m_activePackages.getLength()==0
774                 // case (i.e., document-relative deployment) currently does
775                 // not work, anyway.
776             docContent.setPropertyValue("MediaType", Any(mediaType) );
777 
778             // xxx todo: obsolete in the future
779             try {
780                 docFolderContent.executeCommand( "flush", Any() );
781             }
782             catch (const UnsupportedCommandException &) {
783             }
784         }
785         ActivePackages::Data dbData;
786         destFolder = insertToActivationLayer(
787             properties, mediaType, sourceContent, title, &dbData );
788 
789 
790         // bind activation package:
791         //Because every shared/user extension will be unpacked in a folder,
792         //which was created with a unique name we will always have two different
793         //XPackage objects, even if the second extension is the same.
794         //Therefore bindPackage does not need a guard here.
795         xPackage = m_xRegistry->bindPackage(
796             makeURL( destFolder, title_enc ), mediaType, false, OUString(), xCmdEnv );
797 
798         OSL_ASSERT( xPackage.is() );
799         if (xPackage.is())
800         {
801             bool install = false;
802             try
803             {
804                 OUString const id = dp_misc::getIdentifier( xPackage );
805 
806                 ::osl::MutexGuard g(m_addMutex);
807                 if (isInstalled(xPackage))
808                 {
809                     //Do not guard the complete function with the getMutex
810                     removePackage(id, xPackage->getName(), xAbortChannel,
811                                   xCmdEnv);
812                 }
813                 install = true;
814                 insertToActivationLayerDB(id, dbData);
815             }
816             catch (...)
817             {
818                 deletePackageFromCache( xPackage, destFolder );
819                 throw;
820             }
821             if (!install)
822             {
823                 deletePackageFromCache( xPackage, destFolder );
824             }
825             //ToDo: We should notify only if the extension is registered
826             fireModified();
827         }
828         return xPackage;
829     }
830     catch (const RuntimeException &) {
831         throw;
832     }
833     catch (const CommandFailedException & exc) {
834         logIntern( Any(exc) );
835         throw;
836     }
837     catch (const CommandAbortedException & exc) {
838         logIntern( Any(exc) );
839         throw;
840     }
841     catch (const deployment::DeploymentException & exc) {
842         logIntern( Any(exc) );
843         throw;
844     }
845     catch (const Exception &) {
846         Any exc( ::cppu::getCaughtException() );
847         logIntern( exc );
848         throw deployment::DeploymentException(
849             DpResId(RID_STR_ERROR_WHILE_ADDING) + url,
850             static_cast<OWeakObject *>(this), exc );
851     }
852 }
deletePackageFromCache(Reference<deployment::XPackage> const & xPackage,OUString const & destFolder)853 void PackageManagerImpl::deletePackageFromCache(
854     Reference<deployment::XPackage> const & xPackage,
855     OUString const & destFolder)
856 {
857     try_dispose( xPackage );
858 
859     //we remove the package from the uno cache
860     //no service from the package may be loaded at this time!!!
861     erase_path( destFolder, Reference<XCommandEnvironment>(),
862         false /* no throw: ignore errors */ );
863     //rm last character '_'
864     OUString url = destFolder.copy(0, destFolder.getLength() - 1);
865     erase_path( url, Reference<XCommandEnvironment>(),
866         false /* no throw: ignore errors */ );
867 
868 }
869 
removePackage(OUString const & id,OUString const & fileName,Reference<task::XAbortChannel> const &,Reference<XCommandEnvironment> const & xCmdEnv_)870 void PackageManagerImpl::removePackage(
871     OUString const & id, OUString const & fileName,
872     Reference<task::XAbortChannel> const & /*xAbortChannel*/,
873     Reference<XCommandEnvironment> const & xCmdEnv_ )
874 {
875     check();
876 
877     Reference<XCommandEnvironment> xCmdEnv;
878     if (m_xLogFile.is())
879         xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) );
880     else
881         xCmdEnv.set( xCmdEnv_ );
882 
883     try {
884         Reference<deployment::XPackage> xPackage;
885         {
886             const ::osl::MutexGuard guard(getMutex());
887             //Check if this extension exist and throw an IllegalArgumentException
888             //if it does not
889             //If the files of the extension are already removed, or there is a
890             //different extension at the same place, for example after updating the
891             //extension, then the returned object is that which uses the database data.
892             xPackage = getDeployedPackage_(id, fileName, xCmdEnv );
893 
894 
895             //Because the extension is only removed the next time the extension
896             //manager runs after restarting OOo, we need to indicate that a
897             //shared extension was "deleted". When a user starts OOo, then it
898             //will check if something changed in the shared repository. Based on
899             //the flag file it will then recognize, that the extension was
900             //deleted and can then update the extension database of the shared
901             //extensions in the user installation.
902             if ( xPackage.is() && !m_readOnly && !xPackage->isRemoved() && (m_context == "shared"))
903             {
904                 ActivePackages::Data val;
905                 m_activePackagesDB->get( & val, id, fileName);
906                 OSL_ASSERT(!val.temporaryName.isEmpty());
907                 OUString url(makeURL(m_activePackages_expanded,
908                                      val.temporaryName + "removed"));
909                 ::ucbhelper::Content contentRemoved(url, xCmdEnv, m_xComponentContext);
910                 OUString aUserName;
911                 ::osl::Security aSecurity;
912                 aSecurity.getUserName( aUserName );
913 
914                 OString stamp = OUStringToOString(aUserName, RTL_TEXTENCODING_UTF8);
915                 Reference<css::io::XInputStream> xData(
916                     ::xmlscript::createInputStream(
917                             reinterpret_cast<sal_Int8 const *>(stamp.getStr()),
918                             stamp.getLength() ) );
919                 contentRemoved.writeStream( xData, true /* replace existing */ );
920             }
921             m_activePackagesDB->erase( id, fileName ); // to be removed upon next start
922             //remove any cached data hold by the backend
923             m_xRegistry->packageRemoved(xPackage->getURL(), xPackage->getPackageType()->getMediaType());
924         }
925         try_dispose( xPackage );
926 
927         fireModified();
928     }
929     catch (const RuntimeException &) {
930         throw;
931     }
932     catch (const CommandFailedException & exc) {
933         logIntern( Any(exc) );
934         throw;
935     }
936     catch (const CommandAbortedException & exc) {
937         logIntern( Any(exc) );
938         throw;
939     }
940     catch (const deployment::DeploymentException & exc) {
941         logIntern( Any(exc) );
942         throw;
943     }
944     catch (const Exception &) {
945         Any exc( ::cppu::getCaughtException() );
946         logIntern( exc );
947         throw deployment::DeploymentException(
948             DpResId(RID_STR_ERROR_WHILE_REMOVING) + id,
949             static_cast<OWeakObject *>(this), exc );
950     }
951 }
952 
953 
getDeployPath(ActivePackages::Data const & data)954 OUString PackageManagerImpl::getDeployPath( ActivePackages::Data const & data )
955 {
956     OUStringBuffer buf;
957     buf.append( data.temporaryName );
958     //The bundled extensions are not contained in an additional folder
959     //with a unique name. data.temporaryName contains already the
960     //UTF8 encoded folder name. See PackageManagerImpl::synchronize
961     if (m_context != "bundled")
962     {
963         buf.append( "_/" );
964         buf.append( ::rtl::Uri::encode( data.fileName, rtl_UriCharClassPchar,
965                                     rtl_UriEncodeIgnoreEscapes,
966                                     RTL_TEXTENCODING_UTF8 ) );
967     }
968     return makeURL( m_activePackages, buf.makeStringAndClear() );
969 }
970 
971 
getDeployedPackage_(OUString const & id,OUString const & fileName,Reference<XCommandEnvironment> const & xCmdEnv)972 Reference<deployment::XPackage> PackageManagerImpl::getDeployedPackage_(
973     OUString const & id, OUString const & fileName,
974     Reference<XCommandEnvironment> const & xCmdEnv )
975 {
976     ActivePackages::Data val;
977     if (m_activePackagesDB->get( &val, id, fileName ))
978     {
979         return getDeployedPackage_( id, val, xCmdEnv );
980     }
981     throw lang::IllegalArgumentException(
982         DpResId(RID_STR_NO_SUCH_PACKAGE) + id,
983         static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
984 }
985 
986 
getDeployedPackage_(OUString const & id,ActivePackages::Data const & data,Reference<XCommandEnvironment> const & xCmdEnv,bool ignoreAlienPlatforms)987 Reference<deployment::XPackage> PackageManagerImpl::getDeployedPackage_(
988     OUString const & id, ActivePackages::Data const & data,
989     Reference<XCommandEnvironment> const & xCmdEnv, bool ignoreAlienPlatforms )
990 {
991     if (ignoreAlienPlatforms)
992     {
993         OUString type, subType;
994         INetContentTypeParameterList params;
995         if (INetContentTypes::parse( data.mediaType, type, subType, &params ))
996         {
997             auto const iter = params.find(OString("platform"));
998             if (iter != params.end() && !platform_fits(iter->second.m_sValue))
999                 throw lang::IllegalArgumentException(
1000                     DpResId(RID_STR_NO_SUCH_PACKAGE) + id,
1001                     static_cast<OWeakObject *>(this),
1002                     static_cast<sal_Int16>(-1) );
1003         }
1004     }
1005     Reference<deployment::XPackage> xExtension;
1006     try
1007     {
1008         //Ignore extensions where XPackage::checkPrerequisites failed.
1009         //They must not be usable for this user.
1010         if (data.failedPrerequisites == "0")
1011         {
1012             xExtension = m_xRegistry->bindPackage(
1013                 getDeployPath( data ), data.mediaType, false, OUString(), xCmdEnv );
1014         }
1015     }
1016     catch (const deployment::InvalidRemovedParameterException& e)
1017     {
1018         xExtension = e.Extension;
1019     }
1020     return xExtension;
1021 }
1022 
1023 
1024 Sequence< Reference<deployment::XPackage> >
getDeployedPackages_(Reference<XCommandEnvironment> const & xCmdEnv)1025 PackageManagerImpl::getDeployedPackages_(
1026     Reference<XCommandEnvironment> const & xCmdEnv )
1027 {
1028     std::vector< Reference<deployment::XPackage> > packages;
1029     ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() );
1030     for (auto const& elem : id2temp)
1031     {
1032         if (elem.second.failedPrerequisites != "0")
1033             continue;
1034         try {
1035             packages.push_back(
1036                 getDeployedPackage_(
1037                     elem.first, elem.second, xCmdEnv,
1038                     true /* xxx todo: think of GUI:
1039                             ignore other platforms than the current one */ ) );
1040         }
1041         catch (const lang::IllegalArgumentException &) {
1042             // ignore
1043             TOOLS_WARN_EXCEPTION( "desktop", "" );
1044         }
1045         catch (const deployment::DeploymentException&) {
1046             // ignore
1047             TOOLS_WARN_EXCEPTION( "desktop", "" );
1048         }
1049     }
1050     return comphelper::containerToSequence(packages);
1051 }
1052 
1053 
getDeployedPackage(OUString const & id,OUString const & fileName,Reference<XCommandEnvironment> const & xCmdEnv_)1054 Reference<deployment::XPackage> PackageManagerImpl::getDeployedPackage(
1055     OUString const & id, OUString const & fileName,
1056     Reference<XCommandEnvironment> const & xCmdEnv_ )
1057 {
1058     check();
1059     Reference<XCommandEnvironment> xCmdEnv;
1060     if (m_xLogFile.is())
1061         xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) );
1062     else
1063         xCmdEnv.set( xCmdEnv_ );
1064 
1065     try {
1066         const ::osl::MutexGuard guard( getMutex() );
1067         return getDeployedPackage_( id, fileName, xCmdEnv );
1068     }
1069     catch (const lang::IllegalArgumentException & exc) {
1070         logIntern( Any(exc) );
1071         throw;
1072     }
1073     catch (const RuntimeException &) {
1074         throw;
1075     }
1076     catch (const CommandFailedException & exc) {
1077         logIntern( Any(exc) );
1078         throw;
1079     }
1080     catch (const deployment::DeploymentException & exc) {
1081         logIntern( Any(exc) );
1082         throw;
1083     }
1084     catch (const Exception &) {
1085         Any exc( ::cppu::getCaughtException() );
1086         logIntern( exc );
1087         throw deployment::DeploymentException(
1088             // ought never occur...
1089             "error while accessing deployed package: " + id,
1090             static_cast<OWeakObject *>(this), exc );
1091     }
1092 }
1093 
1094 
1095 Sequence< Reference<deployment::XPackage> >
getDeployedPackages(Reference<task::XAbortChannel> const &,Reference<XCommandEnvironment> const & xCmdEnv_)1096 PackageManagerImpl::getDeployedPackages(
1097     Reference<task::XAbortChannel> const &,
1098     Reference<XCommandEnvironment> const & xCmdEnv_ )
1099 {
1100     check();
1101     Reference<XCommandEnvironment> xCmdEnv;
1102     if (m_xLogFile.is())
1103         xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) );
1104     else
1105         xCmdEnv.set( xCmdEnv_ );
1106 
1107     try {
1108         const ::osl::MutexGuard guard( getMutex() );
1109         return getDeployedPackages_( xCmdEnv );
1110     }
1111     catch (const RuntimeException &) {
1112         throw;
1113     }
1114     catch (const CommandFailedException & exc) {
1115         logIntern( Any(exc) );
1116         throw;
1117     }
1118     catch (const CommandAbortedException & exc) {
1119         logIntern( Any(exc) );
1120         throw;
1121     }
1122     catch (const deployment::DeploymentException & exc) {
1123         logIntern( Any(exc) );
1124         throw;
1125     }
1126     catch (const Exception &) {
1127         Any exc( ::cppu::getCaughtException() );
1128         logIntern( exc );
1129         throw deployment::DeploymentException(
1130             // ought never occur...
1131             "error while getting all deployed packages: " + m_context,
1132             static_cast<OWeakObject *>(this), exc );
1133     }
1134 }
1135 
1136 
1137 //ToDo: the function must not call registerPackage, do this in
1138 //XExtensionManager.reinstallDeployedExtensions
reinstallDeployedPackages(sal_Bool force,Reference<task::XAbortChannel> const &,Reference<XCommandEnvironment> const & xCmdEnv_)1139 void PackageManagerImpl::reinstallDeployedPackages(
1140     sal_Bool force, Reference<task::XAbortChannel> const &  /*xAbortChannel*/,
1141     Reference<XCommandEnvironment> const & xCmdEnv_ )
1142 {
1143     check();
1144     if (!force && office_is_running())
1145         throw RuntimeException(
1146             "You must close any running Office process before reinstalling packages!",
1147             static_cast<OWeakObject *>(this) );
1148 
1149     Reference<XCommandEnvironment> xCmdEnv;
1150     if (m_xLogFile.is())
1151         xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) );
1152     else
1153         xCmdEnv.set( xCmdEnv_ );
1154 
1155     try {
1156         ProgressLevel progress(
1157             xCmdEnv, "Reinstalling all deployed packages..." );
1158 
1159         try_dispose( m_xRegistry );
1160         m_xRegistry.clear();
1161         if (!m_registryCache.isEmpty())
1162             erase_path( m_registryCache, xCmdEnv );
1163         initRegistryBackends();
1164         Reference<util::XUpdatable> xUpdatable( m_xRegistry, UNO_QUERY );
1165         if (xUpdatable.is())
1166             xUpdatable->update();
1167 
1168         //registering is done by the ExtensionManager service.
1169     }
1170     catch (const RuntimeException &) {
1171         throw;
1172     }
1173     catch (const CommandFailedException & exc) {
1174         logIntern( Any(exc) );
1175         throw;
1176     }
1177     catch (const CommandAbortedException & exc) {
1178         logIntern( Any(exc) );
1179         throw;
1180     }
1181     catch (const deployment::DeploymentException & exc) {
1182         logIntern( Any(exc) );
1183         throw;
1184     }
1185     catch (const Exception &) {
1186         Any exc( ::cppu::getCaughtException() );
1187         logIntern( exc );
1188         throw deployment::DeploymentException(
1189             "Error while reinstalling all previously deployed packages of context " + m_context,
1190             static_cast<OWeakObject *>(this), exc );
1191     }
1192 }
1193 
1194 
isReadOnly()1195 sal_Bool SAL_CALL PackageManagerImpl::isReadOnly(  )
1196 {
1197     return m_readOnly;
1198 }
synchronizeRemovedExtensions(Reference<task::XAbortChannel> const & xAbortChannel,Reference<css::ucb::XCommandEnvironment> const & xCmdEnv)1199 bool PackageManagerImpl::synchronizeRemovedExtensions(
1200     Reference<task::XAbortChannel> const & xAbortChannel,
1201     Reference<css::ucb::XCommandEnvironment> const & xCmdEnv)
1202 {
1203 
1204     //find all which are in the extension data base but which
1205     //are removed already.
1206     OSL_ASSERT(!(m_context == "user"));
1207     bool bModified = false;
1208     ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() );
1209 
1210     bool bShared = (m_context == "shared");
1211 
1212     for (auto const& elem : id2temp)
1213     {
1214         try
1215         {
1216             //Get the URL to the extensions folder, first make the url for the
1217             //shared repository including the temporary name
1218             OUString url = makeURL(m_activePackages, elem.second.temporaryName);
1219             if (bShared)
1220                 url = makeURLAppendSysPathSegment( url + "_", elem.second.fileName);
1221 
1222             bool bRemoved = false;
1223             //Check if the URL to the extension is still the same
1224             ::ucbhelper::Content contentExtension;
1225 
1226             if (!create_ucb_content(
1227                     &contentExtension, url,
1228                     Reference<XCommandEnvironment>(), false))
1229             {
1230                 bRemoved = true;
1231             }
1232 
1233             //The folder is in the extension database, but it can still be deleted.
1234             //look for the xxx.tmpremoved file
1235             //There can also be the case that a different extension was installed
1236             //in a "temp" folder with name that is already used.
1237             if (!bRemoved && bShared)
1238             {
1239                 ::ucbhelper::Content contentRemoved;
1240 
1241                 if (create_ucb_content(
1242                         &contentRemoved,
1243                         m_activePackages_expanded + "/" +
1244                         elem.second.temporaryName + "removed",
1245                         Reference<XCommandEnvironment>(), false))
1246                 {
1247                     bRemoved = true;
1248                 }
1249             }
1250 
1251             if (!bRemoved)
1252             {
1253                 //There may be another extensions at the same place
1254                 dp_misc::DescriptionInfoset infoset =
1255                     dp_misc::getDescriptionInfoset(url);
1256                 OSL_ENSURE(infoset.hasDescription() && infoset.getIdentifier(),
1257                            "Extension Manager: bundled and shared extensions "
1258                            "must have an identifier and a version");
1259                 if (infoset.hasDescription() &&
1260                     infoset.getIdentifier() &&
1261                     ( elem.first != *(infoset.getIdentifier())
1262                       || elem.second.version != infoset.getVersion()))
1263                 {
1264                     bRemoved = true;
1265                 }
1266 
1267             }
1268             if (bRemoved)
1269             {
1270                 Reference<deployment::XPackage> xPackage = m_xRegistry->bindPackage(
1271                     url, elem.second.mediaType, true, elem.first, xCmdEnv );
1272                 OSL_ASSERT(xPackage.is()); //Even if the files are removed, we must get the object.
1273                 xPackage->revokePackage(true, xAbortChannel, xCmdEnv);
1274                 removePackage(xPackage->getIdentifier().Value, xPackage->getName(),
1275                               xAbortChannel, xCmdEnv);
1276                 bModified = true;
1277             }
1278         }
1279         catch( const uno::Exception & )
1280         {
1281             TOOLS_WARN_EXCEPTION("desktop.deployment", "");
1282         }
1283     }
1284     return bModified;
1285 }
1286 
1287 
synchronizeAddedExtensions(Reference<task::XAbortChannel> const & xAbortChannel,Reference<css::ucb::XCommandEnvironment> const & xCmdEnv)1288 bool PackageManagerImpl::synchronizeAddedExtensions(
1289     Reference<task::XAbortChannel> const & xAbortChannel,
1290     Reference<css::ucb::XCommandEnvironment> const & xCmdEnv)
1291 {
1292     bool bModified = false;
1293     OSL_ASSERT(!(m_context == "user"));
1294 
1295     ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() );
1296     //check if the folder exist at all. The shared extension folder
1297     //may not exist for a normal user.
1298     bool bOk=true;
1299     try
1300     {
1301         bOk = create_ucb_content(
1302                 nullptr, m_activePackages_expanded, Reference<css::ucb::XCommandEnvironment>(), false);
1303     }
1304     catch (const css::ucb::ContentCreationException&)
1305     {
1306         bOk = false;
1307     }
1308 
1309     if (!bOk)
1310         return bModified;
1311 
1312     ::ucbhelper::Content tempFolder( m_activePackages_expanded, xCmdEnv, m_xComponentContext );
1313     Reference<sdbc::XResultSet> xResultSet(
1314         StrTitle::createCursor( tempFolder,
1315                                 ::ucbhelper::INCLUDE_FOLDERS_ONLY ) );
1316 
1317     while (xResultSet->next())
1318     {
1319         try
1320         {
1321             OUString title(
1322                 Reference<sdbc::XRow>(
1323                     xResultSet, UNO_QUERY_THROW )->getString(
1324                         1 /* Title */ ) );
1325             //The temporary folders of user and shared have an '_' at then end.
1326             //But the name in ActivePackages.temporaryName is saved without.
1327             OUString title2 = title;
1328             bool bShared = (m_context == "shared");
1329             if (bShared)
1330             {
1331                 OSL_ASSERT(title2.endsWith("_"));
1332                 title2 = title2.copy(0, title2.getLength() -1);
1333             }
1334             OUString titleEncoded =  ::rtl::Uri::encode(
1335                 title2, rtl_UriCharClassPchar,
1336                 rtl_UriEncodeIgnoreEscapes,
1337                 RTL_TEXTENCODING_UTF8);
1338 
1339             //It is sufficient to check for the folder name, because when the administrator
1340             //installed the extension it was already checked if there is one with the
1341             //same identifier.
1342             const MatchTempDir match(titleEncoded);
1343             if (std::none_of( id2temp.begin(), id2temp.end(), match ))
1344             {
1345 
1346                 // The folder was not found in the data base, so it must be
1347                 // an added extension
1348                 OUString url(m_activePackages_expanded + "/" + titleEncoded);
1349                 OUString sExtFolder;
1350                 if (bShared) //that is, shared
1351                 {
1352                     //Check if the extension was not "deleted" already which is indicated
1353                     //by a xxx.tmpremoved file
1354                     ::ucbhelper::Content contentRemoved;
1355                     if (create_ucb_content(&contentRemoved, url + "removed",
1356                                            Reference<XCommandEnvironment>(), false))
1357                         continue;
1358                     sExtFolder = getExtensionFolder(
1359                         m_activePackages_expanded + "/" + titleEncoded + "_",
1360                         xCmdEnv, m_xComponentContext);
1361                     url = makeURLAppendSysPathSegment(m_activePackages_expanded, title);
1362                     url = makeURLAppendSysPathSegment(url, sExtFolder);
1363                 }
1364                 Reference<deployment::XPackage> xPackage = m_xRegistry->bindPackage(
1365                     url, OUString(), false, OUString(), xCmdEnv );
1366                 if (xPackage.is())
1367                 {
1368                     OUString id = dp_misc::getIdentifier( xPackage );
1369 
1370                     //Prepare the database entry
1371                     ActivePackages::Data dbData;
1372 
1373                     dbData.temporaryName = titleEncoded;
1374                     if (bShared)
1375                         dbData.fileName = sExtFolder;
1376                     else
1377                         dbData.fileName = title;
1378                     dbData.mediaType = xPackage->getPackageType()->getMediaType();
1379                     dbData.version = xPackage->getVersion();
1380                     SAL_WARN_IF(
1381                         dbData.version.isEmpty(), "desktop.deployment",
1382                         "bundled/shared extension " << id << " at <" << url
1383                             << "> has no explicit version");
1384 
1385                     //We provide a special command environment that will prevent
1386                     //showing a license if simple-license/@accept-by = "admin"
1387                     //It will also prevent showing the license for bundled extensions
1388                     //which is not supported.
1389                     OSL_ASSERT(!(m_context == "user"));
1390 
1391                     // shall the license be suppressed?
1392                     DescriptionInfoset info =
1393                         dp_misc::getDescriptionInfoset(url);
1394                     ::boost::optional<dp_misc::SimpleLicenseAttributes>
1395                           attr = info.getSimpleLicenseAttributes();
1396                     ExtensionProperties props(url, xCmdEnv, m_xComponentContext);
1397                     bool bNoLicense = false;
1398                     if (attr && attr->suppressIfRequired && props.isSuppressedLicense())
1399                         bNoLicense = true;
1400 
1401                     Reference<ucb::XCommandEnvironment> licCmdEnv(
1402                         new LicenseCommandEnv(xCmdEnv->getInteractionHandler(),
1403                                               bNoLicense, m_context));
1404                     sal_Int32 failedPrereq = xPackage->checkPrerequisites(
1405                         xAbortChannel, licCmdEnv, false);
1406                     //Remember that this failed. For example, the user
1407                     //could have declined the license. Then the next time the
1408                     //extension folder is investigated we do not want to
1409                     //try to install the extension again.
1410                     dbData.failedPrerequisites = OUString::number(failedPrereq);
1411                     insertToActivationLayerDB(id, dbData);
1412                     bModified = true;
1413                 }
1414             }
1415         }
1416         catch (const uno::Exception &)
1417         {
1418             // Looks like exceptions being caught here is not an uncommon case.
1419             TOOLS_WARN_EXCEPTION("desktop.deployment", "");
1420         }
1421     }
1422     return bModified;
1423 }
1424 
synchronize(Reference<task::XAbortChannel> const & xAbortChannel,Reference<css::ucb::XCommandEnvironment> const & xCmdEnv)1425 sal_Bool PackageManagerImpl::synchronize(
1426     Reference<task::XAbortChannel> const & xAbortChannel,
1427     Reference<css::ucb::XCommandEnvironment> const & xCmdEnv)
1428 {
1429     check();
1430     bool bModified = false;
1431     if (m_context == "user")
1432         return bModified;
1433     bModified |=
1434         synchronizeRemovedExtensions(xAbortChannel, xCmdEnv);
1435     bModified |= synchronizeAddedExtensions(xAbortChannel, xCmdEnv);
1436 
1437     return bModified;
1438 }
1439 
getExtensionsWithUnacceptedLicenses(Reference<ucb::XCommandEnvironment> const & xCmdEnv)1440 Sequence< Reference<deployment::XPackage> > PackageManagerImpl::getExtensionsWithUnacceptedLicenses(
1441     Reference<ucb::XCommandEnvironment> const & xCmdEnv)
1442 {
1443     std::vector<Reference<deployment::XPackage> > vec;
1444 
1445     try
1446     {
1447         const ::osl::MutexGuard guard( getMutex() );
1448         // clean up activation layer, scan for zombie temp dirs:
1449         ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() );
1450 
1451         bool bShared = (m_context == "shared");
1452 
1453         for (auto const& elem : id2temp)
1454         {
1455             //Get the database entry
1456             ActivePackages::Data const & dbData = elem.second;
1457             sal_Int32 failedPrereq = dbData.failedPrerequisites.toInt32();
1458             //If the installation failed for other reason then the license then we
1459             //ignore it.
1460             if (failedPrereq ^= deployment::Prerequisites::LICENSE)
1461                 continue;
1462 
1463             //Prepare the URL to the extension
1464             OUString url = makeURL(m_activePackages, elem.second.temporaryName);
1465             if (bShared)
1466                 url = makeURLAppendSysPathSegment( url + "_", elem.second.fileName);
1467 
1468             Reference<deployment::XPackage> p = m_xRegistry->bindPackage(
1469                 url, OUString(), false, OUString(), xCmdEnv );
1470 
1471             if (p.is())
1472                 vec.push_back(p);
1473 
1474         }
1475         return ::comphelper::containerToSequence(vec);
1476     }
1477     catch (const deployment::DeploymentException &)
1478     {
1479         throw;
1480     }
1481     catch (const RuntimeException&)
1482     {
1483         throw;
1484     }
1485     catch (...)
1486     {
1487         Any exc = ::cppu::getCaughtException();
1488         deployment::DeploymentException de(
1489             "PackageManagerImpl::getExtensionsWithUnacceptedLicenses",
1490             static_cast<OWeakObject*>(this), exc);
1491         exc <<= de;
1492         ::cppu::throwException(exc);
1493     }
1494 
1495     return ::comphelper::containerToSequence(vec);
1496 }
1497 
checkPrerequisites(css::uno::Reference<css::deployment::XPackage> const & extension,css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv)1498 sal_Int32 PackageManagerImpl::checkPrerequisites(
1499     css::uno::Reference<css::deployment::XPackage> const & extension,
1500     css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
1501     css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv )
1502 {
1503     try
1504     {
1505         if (!extension.is())
1506             return 0;
1507         if (m_context != extension->getRepositoryName())
1508             throw lang::IllegalArgumentException(
1509                 "PackageManagerImpl::checkPrerequisites: extension is not from this repository.",
1510                 nullptr, 0);
1511 
1512         ActivePackages::Data dbData;
1513         OUString id = dp_misc::getIdentifier(extension);
1514         if (!m_activePackagesDB->get( &dbData, id, OUString()))
1515         {
1516             throw lang::IllegalArgumentException(
1517                 "PackageManagerImpl::checkPrerequisites: unknown extension",
1518                 nullptr, 0);
1519 
1520         }
1521         //If the license was already displayed, then do not show it again
1522         Reference<ucb::XCommandEnvironment> _xCmdEnv = xCmdEnv;
1523         sal_Int32 prereq = dbData.failedPrerequisites.toInt32();
1524         if ( !(prereq & deployment::Prerequisites::LICENSE))
1525             _xCmdEnv = new NoLicenseCommandEnv(xCmdEnv->getInteractionHandler());
1526 
1527         sal_Int32 failedPrereq = extension->checkPrerequisites(
1528             xAbortChannel, _xCmdEnv, false);
1529         dbData.failedPrerequisites = OUString::number(failedPrereq);
1530         insertToActivationLayerDB(id, dbData);
1531         return 0;
1532     }
1533     catch ( const deployment::DeploymentException& ) {
1534         throw;
1535     } catch ( const ucb::CommandFailedException & ) {
1536         throw;
1537     } catch ( const ucb::CommandAbortedException & ) {
1538         throw;
1539     } catch (const lang::IllegalArgumentException &) {
1540         throw;
1541     } catch (const uno::RuntimeException &) {
1542         throw;
1543     } catch (...) {
1544         uno::Any excOccurred = ::cppu::getCaughtException();
1545         deployment::DeploymentException exc(
1546             "PackageManagerImpl::checkPrerequisites: exception ",
1547             static_cast<OWeakObject*>(this), excOccurred);
1548         throw exc;
1549     }
1550 }
1551 
1552 
~CmdEnvWrapperImpl()1553 PackageManagerImpl::CmdEnvWrapperImpl::~CmdEnvWrapperImpl()
1554 {
1555 }
1556 
1557 
CmdEnvWrapperImpl(Reference<XCommandEnvironment> const & xUserCmdEnv,Reference<XProgressHandler> const & xLogFile)1558 PackageManagerImpl::CmdEnvWrapperImpl::CmdEnvWrapperImpl(
1559     Reference<XCommandEnvironment> const & xUserCmdEnv,
1560     Reference<XProgressHandler> const & xLogFile )
1561     : m_xLogFile( xLogFile )
1562 {
1563     if (xUserCmdEnv.is()) {
1564         m_xUserProgress.set( xUserCmdEnv->getProgressHandler() );
1565         m_xUserInteractionHandler.set( xUserCmdEnv->getInteractionHandler() );
1566     }
1567 }
1568 
1569 // XCommandEnvironment
1570 
1571 Reference<task::XInteractionHandler>
getInteractionHandler()1572 PackageManagerImpl::CmdEnvWrapperImpl::getInteractionHandler()
1573 {
1574     return m_xUserInteractionHandler;
1575 }
1576 
1577 
1578 Reference<XProgressHandler>
getProgressHandler()1579 PackageManagerImpl::CmdEnvWrapperImpl::getProgressHandler()
1580 {
1581     return this;
1582 }
1583 
1584 // XProgressHandler
1585 
push(Any const & Status)1586 void PackageManagerImpl::CmdEnvWrapperImpl::push( Any const & Status )
1587 {
1588     if (m_xLogFile.is())
1589         m_xLogFile->push( Status );
1590     if (m_xUserProgress.is())
1591         m_xUserProgress->push( Status );
1592 }
1593 
1594 
update(Any const & Status)1595 void PackageManagerImpl::CmdEnvWrapperImpl::update( Any const & Status )
1596 {
1597     if (m_xLogFile.is())
1598         m_xLogFile->update( Status );
1599     if (m_xUserProgress.is())
1600         m_xUserProgress->update( Status );
1601 }
1602 
1603 
pop()1604 void PackageManagerImpl::CmdEnvWrapperImpl::pop()
1605 {
1606     if (m_xLogFile.is())
1607         m_xLogFile->pop();
1608     if (m_xUserProgress.is())
1609         m_xUserProgress->pop();
1610 }
1611 
1612 } // namespace dp_manager
1613 
1614 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1615