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