1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_desktop.hxx"
30 
31 
32 #include "dp_update.hxx"
33 #include "dp_version.hxx"
34 #include "dp_identifier.hxx"
35 #include "dp_descriptioninfoset.hxx"
36 
37 #include "rtl/bootstrap.hxx"
38 
39 using namespace ::com::sun::star;
40 using namespace ::com::sun::star::uno;
41 using ::rtl::OUString;
42 using ::rtl::OString;
43 
44 
45 namespace dp_misc {
46 namespace {
47 
48 int determineHighestVersion(
49     ::rtl::OUString const & userVersion,
50     ::rtl::OUString const & sharedVersion,
51     ::rtl::OUString const & bundledVersion,
52     ::rtl::OUString const & onlineVersion)
53 {
54     int index = 0;
55     OUString  greatest = userVersion;
56     if (dp_misc::compareVersions(sharedVersion, greatest) == dp_misc::GREATER)
57     {
58         index = 1;
59         greatest = sharedVersion;
60     }
61     if (dp_misc::compareVersions(bundledVersion, greatest) == dp_misc::GREATER)
62     {
63         index = 2;
64         greatest = bundledVersion;
65     }
66     if (dp_misc::compareVersions(onlineVersion, greatest) == dp_misc::GREATER)
67     {
68         index = 3;
69     }
70     return index;
71 }
72 
73 Sequence< Reference< xml::dom::XElement > >
74 getUpdateInformation( Reference<deployment::XUpdateInformationProvider > const & updateInformation,
75                       Sequence< OUString > const & urls,
76                       OUString const & identifier,
77                       uno::Any & out_error)
78 {
79     try {
80         return updateInformation->getUpdateInformation(urls, identifier);
81     } catch (uno::RuntimeException &) {
82         throw;
83     } catch (ucb::CommandFailedException & e) {
84         out_error = e.Reason;
85     } catch (ucb::CommandAbortedException &) {
86     } catch (uno::Exception & e) {
87         out_error = uno::makeAny(e);
88     }
89     return
90         Sequence<Reference< xml::dom::XElement > >();
91 }
92 
93 void getOwnUpdateInfos(
94         Reference<uno::XComponentContext> const & xContext,
95         Reference<deployment::XUpdateInformationProvider > const &  updateInformation,
96         UpdateInfoMap& inout_map, std::vector<std::pair<Reference<deployment::XPackage>, uno::Any> > & out_errors,
97         bool & out_allFound)
98 {
99     bool allHaveOwnUpdateInformation = true;
100     for (UpdateInfoMap::iterator i = inout_map.begin(); i != inout_map.end(); i++)
101     {
102         OSL_ASSERT(i->second.extension.is());
103         Sequence<OUString> urls(i->second.extension->getUpdateInformationURLs());
104         if (urls.getLength())
105         {
106             const OUString id =  dp_misc::getIdentifier(i->second.extension);
107             uno::Any anyError;
108             //It is unclear from the idl if there can be a null reference returned.
109             //However all valid information should be the same
110             Sequence<Reference< xml::dom::XElement > >
111                 infos(getUpdateInformation(updateInformation, urls, id, anyError));
112             if (anyError.hasValue())
113                 out_errors.push_back(std::make_pair(i->second.extension, anyError));
114 
115             for (sal_Int32 j = 0; j < infos.getLength(); ++j)
116             {
117                 dp_misc::DescriptionInfoset infoset(
118                     xContext,
119                     Reference< xml::dom::XNode >(infos[j], UNO_QUERY_THROW));
120                 if (!infoset.hasDescription())
121                     continue;
122                 boost::optional< OUString > id2(infoset.getIdentifier());
123                 if (!id2)
124                     continue;
125                 OSL_ASSERT(*id2 == id);
126                 if (*id2 == id)
127                 {
128                     i->second.version = infoset.getVersion();
129                     i->second.info = Reference< xml::dom::XNode >(
130                         infos[j], UNO_QUERY_THROW);
131                 }
132                 break;
133             }
134         }
135         else
136         {
137             allHaveOwnUpdateInformation &= false;
138         }
139     }
140     out_allFound = allHaveOwnUpdateInformation;
141 }
142 
143 void getDefaultUpdateInfos(
144     Reference<uno::XComponentContext> const & xContext,
145     Reference<deployment::XUpdateInformationProvider > const &  updateInformation,
146     UpdateInfoMap& inout_map,
147      std::vector<std::pair<Reference<deployment::XPackage>, uno::Any> > & out_errors)
148 {
149     const rtl::OUString sDefaultURL(dp_misc::getExtensionDefaultUpdateURL());
150     OSL_ASSERT(sDefaultURL.getLength());
151 
152     Any anyError;
153     Sequence< Reference< xml::dom::XElement > >
154         infos(
155             getUpdateInformation(
156                 updateInformation,
157                 Sequence< OUString >(&sDefaultURL, 1), OUString(), anyError));
158     if (anyError.hasValue())
159         out_errors.push_back(std::make_pair(Reference<deployment::XPackage>(), anyError));
160     for (sal_Int32 i = 0; i < infos.getLength(); ++i)
161     {
162         Reference< xml::dom::XNode > node(infos[i], UNO_QUERY_THROW);
163         dp_misc::DescriptionInfoset infoset(xContext, node);
164         boost::optional< OUString > id(infoset.getIdentifier());
165         if (!id) {
166             continue;
167         }
168         UpdateInfoMap::iterator j = inout_map.find(*id);
169         if (j != inout_map.end())
170         {
171             //skip those extension which provide its own update urls
172             if (j->second.extension->getUpdateInformationURLs().getLength())
173                 continue;
174             OUString v(infoset.getVersion());
175             //look for the highest version in the online repository
176             if (dp_misc::compareVersions(v, j->second.version) ==
177                 dp_misc::GREATER)
178             {
179                 j->second.version = v;
180                 j->second.info = node;
181             }
182         }
183     }
184 }
185 
186 bool containsBundledOnly(Sequence<Reference<deployment::XPackage> > const & sameIdExtensions)
187 {
188     OSL_ASSERT(sameIdExtensions.getLength() == 3);
189     if (!sameIdExtensions[0].is() && !sameIdExtensions[1].is() && sameIdExtensions[2].is())
190         return true;
191     else
192         return false;
193 }
194 /** Returns true if the list of extensions are bundled extensions and there are no
195     other extensions with the same identifier in the shared or user repository.
196     If extensionList is NULL, then it is checked if there are only bundled extensions.
197 */
198 bool onlyBundledExtensions(
199     Reference<deployment::XExtensionManager> const & xExtMgr,
200     std::vector< Reference<deployment::XPackage > > const * extensionList)
201 {
202     OSL_ASSERT(xExtMgr.is());
203     bool onlyBundled = true;
204     if (extensionList)
205     {
206         typedef std::vector<Reference<deployment::XPackage > >::const_iterator CIT;
207         for (CIT i = extensionList->begin(); i != extensionList->end(); i++)
208         {
209             Sequence<Reference<deployment::XPackage> > seqExt = xExtMgr->getExtensionsWithSameIdentifier(
210                 dp_misc::getIdentifier(*i), (*i)->getName(), Reference<ucb::XCommandEnvironment>());
211 
212             if (!containsBundledOnly(seqExt))
213             {
214                 onlyBundled = false;
215                 break;
216             }
217 
218         }
219     }
220     else
221     {
222         const uno::Sequence< uno::Sequence< Reference<deployment::XPackage > > > seqAllExt =
223             xExtMgr->getAllExtensions(Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>());
224 
225         for (int pos = seqAllExt.getLength(); pos --; )
226         {
227             if (!containsBundledOnly(seqAllExt[pos]))
228             {
229                 onlyBundled = false;
230                 break;
231             }
232         }
233     }
234     return onlyBundled;
235 }
236 
237 } // anon namespace
238 
239 
240 OUString getExtensionDefaultUpdateURL()
241 {
242     ::rtl::OUString sUrl(
243         RTL_CONSTASCII_USTRINGPARAM(
244         "${$BRAND_BASE_DIR/program/" SAL_CONFIGFILE("version")
245         ":Version:ExtensionUpdateURL}"));
246     ::rtl::Bootstrap::expandMacros(sUrl);
247     return sUrl;
248 }
249 
250 /* returns the index of the greatest version, starting with 0
251 
252  */
253 UPDATE_SOURCE isUpdateUserExtension(
254     bool bReadOnlyShared,
255     ::rtl::OUString const & userVersion,
256     ::rtl::OUString const & sharedVersion,
257     ::rtl::OUString const & bundledVersion,
258     ::rtl::OUString const & onlineVersion)
259 {
260     UPDATE_SOURCE retVal = UPDATE_SOURCE_NONE;
261     if (bReadOnlyShared)
262     {
263         if (userVersion.getLength())
264         {
265             int index = determineHighestVersion(
266                 userVersion, sharedVersion, bundledVersion, onlineVersion);
267             if (index == 1)
268                 retVal = UPDATE_SOURCE_SHARED;
269             else if (index == 2)
270                 retVal = UPDATE_SOURCE_BUNDLED;
271             else if (index == 3)
272                 retVal = UPDATE_SOURCE_ONLINE;
273         }
274         else if (sharedVersion.getLength())
275         {
276             int index = determineHighestVersion(
277                 OUString(), sharedVersion, bundledVersion, onlineVersion);
278             if (index == 2)
279                 retVal = UPDATE_SOURCE_BUNDLED;
280             else if (index == 3)
281                 retVal = UPDATE_SOURCE_ONLINE;
282 
283         }
284         //No update for bundled extensions, they are updated only by the setup
285         //else if (bundledVersion.getLength())
286         //{
287         //    int index = determineHighestVersion(
288         //        OUString(), OUString(), bundledVersion, onlineVersion);
289         //    if (index == 3)
290         //        retVal = UPDATE_SOURCE_ONLINE;
291         //}
292     }
293     else
294     {
295         if (userVersion.getLength())
296         {
297             int index = determineHighestVersion(
298                 userVersion, sharedVersion, bundledVersion, onlineVersion);
299             if (index == 1)
300                 retVal = UPDATE_SOURCE_SHARED;
301             else if (index == 2)
302                 retVal = UPDATE_SOURCE_BUNDLED;
303             else if (index == 3)
304                 retVal = UPDATE_SOURCE_ONLINE;
305         }
306     }
307 
308     return retVal;
309 }
310 
311 UPDATE_SOURCE isUpdateSharedExtension(
312     bool bReadOnlyShared,
313     ::rtl::OUString const & sharedVersion,
314     ::rtl::OUString const & bundledVersion,
315     ::rtl::OUString const & onlineVersion)
316 {
317     if (bReadOnlyShared)
318         return UPDATE_SOURCE_NONE;
319     UPDATE_SOURCE retVal = UPDATE_SOURCE_NONE;
320 
321     if (sharedVersion.getLength())
322     {
323         int index = determineHighestVersion(
324             OUString(), sharedVersion, bundledVersion, onlineVersion);
325         if (index == 2)
326             retVal = UPDATE_SOURCE_BUNDLED;
327         else if (index == 3)
328             retVal = UPDATE_SOURCE_ONLINE;
329     }
330     //No update for bundled extensions, they are updated only by the setup
331     //else if (bundledVersion.getLength())
332     //{
333     //    int index = determineHighestVersion(
334     //        OUString(), OUString(), bundledVersion, onlineVersion);
335     //    if (index == 3)
336     //        retVal = UPDATE_SOURCE_ONLINE;
337     //}
338     return retVal;
339 }
340 
341 Reference<deployment::XPackage>
342 getExtensionWithHighestVersion(
343     Sequence<Reference<deployment::XPackage> > const & seqExt)
344 {
345     if (seqExt.getLength() == 0)
346         return Reference<deployment::XPackage>();
347 
348     Reference<deployment::XPackage> greatest;
349     sal_Int32 len = seqExt.getLength();
350 
351     for (sal_Int32 i = 0; i < len; i++)
352     {
353         if (!greatest.is())
354         {
355             greatest = seqExt[i];
356             continue;
357         }
358         Reference<deployment::XPackage> const & current = seqExt[i];
359         //greatest has a value
360         if (! current.is())
361             continue;
362 
363         if (dp_misc::compareVersions(current->getVersion(), greatest->getVersion()) == dp_misc::GREATER)
364             greatest = current;
365     }
366     return greatest;
367 }
368 
369 UpdateInfo::UpdateInfo( Reference< deployment::XPackage> const & ext):
370 extension(ext)
371 {
372 }
373 
374 
375 
376 UpdateInfoMap getOnlineUpdateInfos(
377     Reference<uno::XComponentContext> const &xContext,
378     Reference<deployment::XExtensionManager> const & xExtMgr,
379     Reference<deployment::XUpdateInformationProvider > const & updateInformation,
380     std::vector<Reference<deployment::XPackage > > const * extensionList,
381     std::vector<std::pair< Reference<deployment::XPackage>, uno::Any> > & out_errors)
382 {
383     OSL_ASSERT(xExtMgr.is());
384     UpdateInfoMap infoMap;
385     if (!xExtMgr.is() || onlyBundledExtensions(xExtMgr, extensionList))
386         return infoMap;
387 
388     if (!extensionList)
389     {
390         const uno::Sequence< uno::Sequence< Reference<deployment::XPackage > > > seqAllExt =  xExtMgr->getAllExtensions(
391             Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>());
392 
393         //fill the UpdateInfoMap. key = extension identifier, value = UpdateInfo
394         for (int pos = seqAllExt.getLength(); pos --; )
395         {
396             uno::Sequence<Reference<deployment::XPackage> > const &   seqExt = seqAllExt[pos];
397 
398             Reference<deployment::XPackage> extension = getExtensionWithHighestVersion(seqExt);
399             OSL_ASSERT(extension.is());
400 
401             std::pair<UpdateInfoMap::iterator, bool> insertRet = infoMap.insert(
402                 UpdateInfoMap::value_type(
403                     dp_misc::getIdentifier(extension), UpdateInfo(extension)));
404             OSL_ASSERT(insertRet.second == true);
405         }
406     }
407     else
408     {
409         typedef std::vector<Reference<deployment::XPackage > >::const_iterator CIT;
410         for (CIT i = extensionList->begin(); i != extensionList->end(); i++)
411         {
412             OSL_ASSERT(i->is());
413             std::pair<UpdateInfoMap::iterator, bool> insertRet = infoMap.insert(
414                 UpdateInfoMap::value_type(
415                     dp_misc::getIdentifier(*i), UpdateInfo(*i)));
416             OSL_ASSERT(insertRet.second == true);
417         }
418     }
419 
420     //Now find the update information for the extensions which provide their own
421     //URLs to update information.
422     bool allInfosObtained = false;
423     getOwnUpdateInfos(xContext, updateInformation, infoMap, out_errors, allInfosObtained);
424 
425     if (!allInfosObtained)
426         getDefaultUpdateInfos(xContext, updateInformation, infoMap, out_errors);
427     return infoMap;
428 }
429 OUString getHighestVersion(
430     ::rtl::OUString const & userVersion,
431     ::rtl::OUString const & sharedVersion,
432     ::rtl::OUString const & bundledVersion,
433     ::rtl::OUString const & onlineVersion)
434 {
435     int index = determineHighestVersion(userVersion, sharedVersion, bundledVersion, onlineVersion);
436     switch (index)
437     {
438     case 0: return userVersion;
439     case 1: return sharedVersion;
440     case 2: return bundledVersion;
441     case 3: return onlineVersion;
442     default: OSL_ASSERT(0);
443     }
444 
445     return OUString();
446 }
447 } //namespace dp_misc
448