Coverage Report

Created: 2026-04-09 11:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/desktop/source/deployment/misc/dp_update.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_folders.h>
21
22
#include <dp_update.hxx>
23
#include <dp_version.hxx>
24
#include <dp_identifier.hxx>
25
#include <dp_descriptioninfoset.hxx>
26
27
#include <com/sun/star/ucb/CommandAbortedException.hpp>
28
#include <com/sun/star/ucb/CommandFailedException.hpp>
29
#include <osl/diagnose.h>
30
#include <rtl/bootstrap.hxx>
31
#include <sal/log.hxx>
32
33
using namespace ::com::sun::star;
34
using namespace ::com::sun::star::uno;
35
36
37
namespace dp_misc {
38
namespace {
39
40
int determineHighestVersion(
41
    OUString const & userVersion,
42
    OUString const & sharedVersion,
43
    OUString const & bundledVersion,
44
    std::u16string_view onlineVersion)
45
0
{
46
0
    int index = 0;
47
0
    OUString  greatest = userVersion;
48
0
    if (dp_misc::compareVersions(sharedVersion, greatest) == dp_misc::GREATER)
49
0
    {
50
0
        index = 1;
51
0
        greatest = sharedVersion;
52
0
    }
53
0
    if (dp_misc::compareVersions(bundledVersion, greatest) == dp_misc::GREATER)
54
0
    {
55
0
        index = 2;
56
0
        greatest = bundledVersion;
57
0
    }
58
0
    if (dp_misc::compareVersions(onlineVersion, greatest) == dp_misc::GREATER)
59
0
    {
60
0
        index = 3;
61
0
    }
62
0
    return index;
63
0
}
64
65
Sequence< Reference< xml::dom::XElement > >
66
getUpdateInformation( Reference<deployment::XUpdateInformationProvider > const & updateInformation,
67
                      Sequence< OUString > const & urls,
68
                      OUString const & identifier,
69
                      uno::Any & out_error)
70
0
{
71
0
    try {
72
0
        return updateInformation->getUpdateInformation(urls, identifier);
73
0
    } catch (const uno::RuntimeException &) {
74
0
        throw;
75
0
    } catch (const ucb::CommandFailedException & e) {
76
0
        out_error = e.Reason;
77
0
    } catch (const ucb::CommandAbortedException &) {
78
0
    } catch (const uno::Exception & e) {
79
0
        out_error <<= e;
80
0
    }
81
0
    return
82
0
        Sequence<Reference< xml::dom::XElement > >();
83
0
}
84
85
void getOwnUpdateInfos(
86
        Reference<uno::XComponentContext> const & xContext,
87
        Reference<deployment::XUpdateInformationProvider > const &  updateInformation,
88
        UpdateInfoMap& inout_map, std::vector<std::pair<Reference<deployment::XPackage>, uno::Any> > & out_errors,
89
        bool & out_allFound)
90
0
{
91
0
    bool bAllHaveOwnUpdateInformation = true;
92
0
    for (auto & inout : inout_map)
93
0
    {
94
0
        OSL_ASSERT(inout.second.extension.is());
95
0
        Sequence<OUString> urls(inout.second.extension->getUpdateInformationURLs());
96
0
        if (urls.hasElements())
97
0
        {
98
0
            const OUString search_id = dp_misc::getIdentifier(inout.second.extension);
99
0
            SAL_INFO( "extensions.update", "Searching update for " << search_id );
100
0
            uno::Any anyError;
101
            //It is unclear from the idl if there can be a null reference returned.
102
            //However all valid information should be the same
103
0
            const Sequence<Reference< xml::dom::XElement > >
104
0
                infos(getUpdateInformation(updateInformation, urls, search_id, anyError));
105
0
            if (anyError.hasValue())
106
0
                out_errors.emplace_back(inout.second.extension, anyError);
107
108
0
            for (const Reference< xml::dom::XElement >& element : infos)
109
0
            {
110
0
                dp_misc::DescriptionInfoset infoset(
111
0
                    xContext,
112
0
                    Reference< xml::dom::XNode >(element, UNO_QUERY_THROW));
113
0
                if (!infoset.hasDescription())
114
0
                    continue;
115
0
                std::optional< OUString > result_id(infoset.getIdentifier());
116
0
                if (!result_id)
117
0
                    continue;
118
0
                SAL_INFO( "extensions.update", "  found version "
119
0
                          << infoset.getVersion() << " for " << *result_id );
120
0
                if (*result_id != search_id)
121
0
                    continue;
122
0
                inout.second.version = infoset.getVersion();
123
0
                inout.second.info.set(element, UNO_QUERY_THROW);
124
0
                break;
125
0
            }
126
0
        }
127
0
        else
128
0
        {
129
0
            bAllHaveOwnUpdateInformation = false;
130
0
        }
131
0
    }
132
0
    out_allFound = bAllHaveOwnUpdateInformation;
133
0
}
134
135
void getDefaultUpdateInfos(
136
    Reference<uno::XComponentContext> const & xContext,
137
    Reference<deployment::XUpdateInformationProvider > const &  updateInformation,
138
    UpdateInfoMap& inout_map,
139
     std::vector<std::pair<Reference<deployment::XPackage>, uno::Any> > & out_errors)
140
0
{
141
0
    const OUString sDefaultURL(dp_misc::getExtensionDefaultUpdateURL());
142
0
    OSL_ASSERT(!sDefaultURL.isEmpty());
143
144
0
    Any anyError;
145
0
    const Sequence< Reference< xml::dom::XElement > >
146
0
        infos(
147
0
            getUpdateInformation(
148
0
                updateInformation,
149
0
                Sequence< OUString >(&sDefaultURL, 1), OUString(), anyError));
150
0
    if (anyError.hasValue())
151
0
        out_errors.emplace_back(Reference<deployment::XPackage>(), anyError);
152
0
    for (const Reference< xml::dom::XElement >& element : infos)
153
0
    {
154
0
        Reference< xml::dom::XNode > node(element, UNO_QUERY_THROW);
155
0
        dp_misc::DescriptionInfoset infoset(xContext, node);
156
0
        std::optional< OUString > id(infoset.getIdentifier());
157
0
        if (!id) {
158
0
            continue;
159
0
        }
160
0
        UpdateInfoMap::iterator j = inout_map.find(*id);
161
0
        if (j != inout_map.end())
162
0
        {
163
            //skip those extension which provide its own update urls
164
0
            if (j->second.extension->getUpdateInformationURLs().getLength())
165
0
                continue;
166
0
            OUString v(infoset.getVersion());
167
            //look for the highest version in the online repository
168
0
            if (dp_misc::compareVersions(v, j->second.version) ==
169
0
                dp_misc::GREATER)
170
0
            {
171
0
                j->second.version = v;
172
0
                j->second.info = std::move(node);
173
0
            }
174
0
        }
175
0
    }
176
0
}
177
178
bool containsBundledOnly(Sequence<Reference<deployment::XPackage> > const & sameIdExtensions)
179
0
{
180
0
    OSL_ASSERT(sameIdExtensions.getLength() == 3);
181
0
    return !sameIdExtensions[0].is() && !sameIdExtensions[1].is() && sameIdExtensions[2].is();
182
0
}
183
184
/** Returns true if the list of extensions are bundled extensions and there are no
185
    other extensions with the same identifier in the shared or user repository.
186
    If extensionList is NULL, then it is checked if there are only bundled extensions.
187
*/
188
bool onlyBundledExtensions(
189
    Reference<deployment::XExtensionManager> const & xExtMgr,
190
    std::vector< Reference<deployment::XPackage > > const * extensionList)
191
0
{
192
0
    OSL_ASSERT(xExtMgr.is());
193
0
    bool bOnlyBundled = true;
194
0
    if (extensionList)
195
0
    {
196
0
        for (auto const& elem : *extensionList)
197
0
        {
198
0
            Sequence<Reference<deployment::XPackage> > seqExt = xExtMgr->getExtensionsWithSameIdentifier(
199
0
                dp_misc::getIdentifier(elem), elem->getName(), Reference<ucb::XCommandEnvironment>());
200
201
0
            bOnlyBundled = containsBundledOnly(seqExt);
202
0
            if (!bOnlyBundled)
203
0
                break;
204
0
        }
205
0
    }
206
0
    else
207
0
    {
208
0
        const uno::Sequence< uno::Sequence< Reference<deployment::XPackage > > > seqAllExt =
209
0
            xExtMgr->getAllExtensions(Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>());
210
211
0
        for (int pos(0), nLen(seqAllExt.getLength()); bOnlyBundled && pos != nLen; ++pos)
212
0
        {
213
0
            bOnlyBundled = containsBundledOnly(seqAllExt[pos]);
214
0
        }
215
0
    }
216
0
    return bOnlyBundled;
217
0
}
218
219
} // anon namespace
220
221
222
OUString getExtensionDefaultUpdateURL()
223
0
{
224
0
    OUString sUrl(
225
0
        u"${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version")
226
0
        ":Version:ExtensionUpdateURL}"_ustr);
227
0
    ::rtl::Bootstrap::expandMacros(sUrl);
228
0
    return sUrl;
229
0
}
230
231
/* returns the index of the greatest version, starting with 0
232
233
 */
234
UPDATE_SOURCE isUpdateUserExtension(
235
    bool bReadOnlyShared,
236
    OUString const & userVersion,
237
    OUString const & sharedVersion,
238
    OUString const & bundledVersion,
239
    std::u16string_view onlineVersion)
240
0
{
241
0
    UPDATE_SOURCE retVal = UPDATE_SOURCE_NONE;
242
0
    if (bReadOnlyShared)
243
0
    {
244
0
        if (!userVersion.isEmpty())
245
0
        {
246
0
            int index = determineHighestVersion(
247
0
                userVersion, sharedVersion, bundledVersion, onlineVersion);
248
0
            if (index == 1)
249
0
                retVal = UPDATE_SOURCE_SHARED;
250
0
            else if (index == 2)
251
0
                retVal = UPDATE_SOURCE_BUNDLED;
252
0
            else if (index == 3)
253
0
                retVal = UPDATE_SOURCE_ONLINE;
254
0
        }
255
0
        else if (!sharedVersion.isEmpty())
256
0
        {
257
0
            int index = determineHighestVersion(
258
0
                OUString(), sharedVersion, bundledVersion, onlineVersion);
259
0
            if (index == 2)
260
0
                retVal = UPDATE_SOURCE_BUNDLED;
261
0
            else if (index == 3)
262
0
                retVal = UPDATE_SOURCE_ONLINE;
263
264
0
        }
265
0
    }
266
0
    else
267
0
    {
268
0
        if (!userVersion.isEmpty())
269
0
        {
270
0
            int index = determineHighestVersion(
271
0
                userVersion, sharedVersion, bundledVersion, onlineVersion);
272
0
            if (index == 1)
273
0
                retVal = UPDATE_SOURCE_SHARED;
274
0
            else if (index == 2)
275
0
                retVal = UPDATE_SOURCE_BUNDLED;
276
0
            else if (index == 3)
277
0
                retVal = UPDATE_SOURCE_ONLINE;
278
0
        }
279
0
    }
280
281
0
    return retVal;
282
0
}
283
284
UPDATE_SOURCE isUpdateSharedExtension(
285
    bool bReadOnlyShared,
286
    OUString const & sharedVersion,
287
    OUString const & bundledVersion,
288
    std::u16string_view onlineVersion)
289
0
{
290
0
    if (bReadOnlyShared)
291
0
        return UPDATE_SOURCE_NONE;
292
0
    UPDATE_SOURCE retVal = UPDATE_SOURCE_NONE;
293
294
0
    if (!sharedVersion.isEmpty())
295
0
    {
296
0
        int index = determineHighestVersion(
297
0
            OUString(), sharedVersion, bundledVersion, onlineVersion);
298
0
        if (index == 2)
299
0
            retVal = UPDATE_SOURCE_BUNDLED;
300
0
        else if (index == 3)
301
0
            retVal = UPDATE_SOURCE_ONLINE;
302
0
    }
303
0
    return retVal;
304
0
}
305
306
Reference<deployment::XPackage>
307
getExtensionWithHighestVersion(
308
    Sequence<Reference<deployment::XPackage> > const & seqExt)
309
0
{
310
0
    if (!seqExt.hasElements())
311
0
        return Reference<deployment::XPackage>();
312
313
0
    Reference<deployment::XPackage> greatest;
314
0
    sal_Int32 len = seqExt.getLength();
315
316
0
    for (sal_Int32 i = 0; i < len; i++)
317
0
    {
318
0
        if (!greatest.is())
319
0
        {
320
0
            greatest = seqExt[i];
321
0
            continue;
322
0
        }
323
0
        Reference<deployment::XPackage> const & current = seqExt[i];
324
        //greatest has a value
325
0
        if (! current.is())
326
0
            continue;
327
328
0
        if (dp_misc::compareVersions(current->getVersion(), greatest->getVersion()) == dp_misc::GREATER)
329
0
            greatest = current;
330
0
    }
331
0
    return greatest;
332
0
}
333
334
UpdateInfo::UpdateInfo( Reference< deployment::XPackage> const & ext):
335
0
extension(ext)
336
0
{
337
0
}
338
339
340
UpdateInfoMap getOnlineUpdateInfos(
341
    Reference<uno::XComponentContext> const &xContext,
342
    Reference<deployment::XExtensionManager> const & xExtMgr,
343
    Reference<deployment::XUpdateInformationProvider > const & updateInformation,
344
    std::vector<Reference<deployment::XPackage > > const * extensionList,
345
    std::vector<std::pair< Reference<deployment::XPackage>, uno::Any> > & out_errors)
346
0
{
347
0
    OSL_ASSERT(xExtMgr.is());
348
0
    UpdateInfoMap infoMap;
349
0
    if (!xExtMgr.is() || onlyBundledExtensions(xExtMgr, extensionList))
350
0
        return infoMap;
351
352
0
    if (!extensionList)
353
0
    {
354
0
        const uno::Sequence< uno::Sequence< Reference<deployment::XPackage > > > seqAllExt =  xExtMgr->getAllExtensions(
355
0
            Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>());
356
357
        //fill the UpdateInfoMap. key = extension identifier, value = UpdateInfo
358
0
        for (int pos = seqAllExt.getLength(); pos --; )
359
0
        {
360
0
            uno::Sequence<Reference<deployment::XPackage> > const &   seqExt = seqAllExt[pos];
361
362
0
            Reference<deployment::XPackage> extension = getExtensionWithHighestVersion(seqExt);
363
0
            OSL_ASSERT(extension.is());
364
365
0
            std::pair<UpdateInfoMap::iterator, bool> insertRet = infoMap.emplace(
366
0
                    dp_misc::getIdentifier(extension), UpdateInfo(extension));
367
0
            OSL_ASSERT(insertRet.second);
368
0
        }
369
0
    }
370
0
    else
371
0
    {
372
0
        for (auto const& elem : *extensionList)
373
0
        {
374
0
            OSL_ASSERT(elem.is());
375
0
            std::pair<UpdateInfoMap::iterator, bool> insertRet = infoMap.emplace(
376
0
                    dp_misc::getIdentifier(elem), UpdateInfo(elem));
377
0
            OSL_ASSERT(insertRet.second);
378
0
        }
379
0
    }
380
381
    //Now find the update information for the extensions which provide their own
382
    //URLs to update information.
383
0
    bool bAllInfosObtained = false;
384
0
    getOwnUpdateInfos(xContext, updateInformation, infoMap, out_errors, bAllInfosObtained);
385
386
0
    if (!bAllInfosObtained)
387
0
        getDefaultUpdateInfos(xContext, updateInformation, infoMap, out_errors);
388
0
    return infoMap;
389
0
}
390
OUString getHighestVersion(
391
    OUString const & sharedVersion,
392
    OUString const & bundledVersion,
393
    OUString const & onlineVersion)
394
0
{
395
0
    int index = determineHighestVersion(OUString(), sharedVersion, bundledVersion, onlineVersion);
396
0
    switch (index)
397
0
    {
398
0
    case 1: return sharedVersion;
399
0
    case 2: return bundledVersion;
400
0
    case 3: return onlineVersion;
401
0
    default: OSL_ASSERT(false);
402
0
    }
403
404
0
    return OUString();
405
0
}
406
} //namespace dp_misc
407
408
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */