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