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, ¶ms ))
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