Coverage Report

Created: 2025-12-31 10:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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, &params ))
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: */