/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: */ |