408 lines
14 KiB
C++
408 lines
14 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#include <config_folders.h>
|
|
|
|
#include <dp_update.hxx>
|
|
#include <dp_version.hxx>
|
|
#include <dp_identifier.hxx>
|
|
#include <dp_descriptioninfoset.hxx>
|
|
|
|
#include <com/sun/star/ucb/CommandAbortedException.hpp>
|
|
#include <com/sun/star/ucb/CommandFailedException.hpp>
|
|
#include <osl/diagnose.h>
|
|
#include <rtl/bootstrap.hxx>
|
|
#include <sal/log.hxx>
|
|
|
|
using namespace ::com::sun::star;
|
|
using namespace ::com::sun::star::uno;
|
|
|
|
|
|
namespace dp_misc {
|
|
namespace {
|
|
|
|
int determineHighestVersion(
|
|
OUString const & userVersion,
|
|
OUString const & sharedVersion,
|
|
OUString const & bundledVersion,
|
|
std::u16string_view onlineVersion)
|
|
{
|
|
int index = 0;
|
|
OUString greatest = userVersion;
|
|
if (dp_misc::compareVersions(sharedVersion, greatest) == dp_misc::GREATER)
|
|
{
|
|
index = 1;
|
|
greatest = sharedVersion;
|
|
}
|
|
if (dp_misc::compareVersions(bundledVersion, greatest) == dp_misc::GREATER)
|
|
{
|
|
index = 2;
|
|
greatest = bundledVersion;
|
|
}
|
|
if (dp_misc::compareVersions(onlineVersion, greatest) == dp_misc::GREATER)
|
|
{
|
|
index = 3;
|
|
}
|
|
return index;
|
|
}
|
|
|
|
Sequence< Reference< xml::dom::XElement > >
|
|
getUpdateInformation( Reference<deployment::XUpdateInformationProvider > const & updateInformation,
|
|
Sequence< OUString > const & urls,
|
|
OUString const & identifier,
|
|
uno::Any & out_error)
|
|
{
|
|
try {
|
|
return updateInformation->getUpdateInformation(urls, identifier);
|
|
} catch (const uno::RuntimeException &) {
|
|
throw;
|
|
} catch (const ucb::CommandFailedException & e) {
|
|
out_error = e.Reason;
|
|
} catch (const ucb::CommandAbortedException &) {
|
|
} catch (const uno::Exception & e) {
|
|
out_error <<= e;
|
|
}
|
|
return
|
|
Sequence<Reference< xml::dom::XElement > >();
|
|
}
|
|
|
|
void 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)
|
|
{
|
|
bool bAllHaveOwnUpdateInformation = true;
|
|
for (auto & inout : inout_map)
|
|
{
|
|
OSL_ASSERT(inout.second.extension.is());
|
|
Sequence<OUString> urls(inout.second.extension->getUpdateInformationURLs());
|
|
if (urls.hasElements())
|
|
{
|
|
const OUString search_id = dp_misc::getIdentifier(inout.second.extension);
|
|
SAL_INFO( "extensions.update", "Searching update for " << search_id );
|
|
uno::Any anyError;
|
|
//It is unclear from the idl if there can be a null reference returned.
|
|
//However all valid information should be the same
|
|
const Sequence<Reference< xml::dom::XElement > >
|
|
infos(getUpdateInformation(updateInformation, urls, search_id, anyError));
|
|
if (anyError.hasValue())
|
|
out_errors.emplace_back(inout.second.extension, anyError);
|
|
|
|
for (const Reference< xml::dom::XElement >& element : infos)
|
|
{
|
|
dp_misc::DescriptionInfoset infoset(
|
|
xContext,
|
|
Reference< xml::dom::XNode >(element, UNO_QUERY_THROW));
|
|
if (!infoset.hasDescription())
|
|
continue;
|
|
std::optional< OUString > result_id(infoset.getIdentifier());
|
|
if (!result_id)
|
|
continue;
|
|
SAL_INFO( "extensions.update", " found version "
|
|
<< infoset.getVersion() << " for " << *result_id );
|
|
if (*result_id != search_id)
|
|
continue;
|
|
inout.second.version = infoset.getVersion();
|
|
inout.second.info.set(element, UNO_QUERY_THROW);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bAllHaveOwnUpdateInformation = false;
|
|
}
|
|
}
|
|
out_allFound = bAllHaveOwnUpdateInformation;
|
|
}
|
|
|
|
void 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)
|
|
{
|
|
const OUString sDefaultURL(dp_misc::getExtensionDefaultUpdateURL());
|
|
OSL_ASSERT(!sDefaultURL.isEmpty());
|
|
|
|
Any anyError;
|
|
const Sequence< Reference< xml::dom::XElement > >
|
|
infos(
|
|
getUpdateInformation(
|
|
updateInformation,
|
|
Sequence< OUString >(&sDefaultURL, 1), OUString(), anyError));
|
|
if (anyError.hasValue())
|
|
out_errors.emplace_back(Reference<deployment::XPackage>(), anyError);
|
|
for (const Reference< xml::dom::XElement >& element : infos)
|
|
{
|
|
Reference< xml::dom::XNode > node(element, UNO_QUERY_THROW);
|
|
dp_misc::DescriptionInfoset infoset(xContext, node);
|
|
std::optional< OUString > id(infoset.getIdentifier());
|
|
if (!id) {
|
|
continue;
|
|
}
|
|
UpdateInfoMap::iterator j = inout_map.find(*id);
|
|
if (j != inout_map.end())
|
|
{
|
|
//skip those extension which provide its own update urls
|
|
if (j->second.extension->getUpdateInformationURLs().getLength())
|
|
continue;
|
|
OUString v(infoset.getVersion());
|
|
//look for the highest version in the online repository
|
|
if (dp_misc::compareVersions(v, j->second.version) ==
|
|
dp_misc::GREATER)
|
|
{
|
|
j->second.version = v;
|
|
j->second.info = std::move(node);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool containsBundledOnly(Sequence<Reference<deployment::XPackage> > const & sameIdExtensions)
|
|
{
|
|
OSL_ASSERT(sameIdExtensions.getLength() == 3);
|
|
return !sameIdExtensions[0].is() && !sameIdExtensions[1].is() && sameIdExtensions[2].is();
|
|
}
|
|
|
|
/** Returns true if the list of extensions are bundled extensions and there are no
|
|
other extensions with the same identifier in the shared or user repository.
|
|
If extensionList is NULL, then it is checked if there are only bundled extensions.
|
|
*/
|
|
bool onlyBundledExtensions(
|
|
Reference<deployment::XExtensionManager> const & xExtMgr,
|
|
std::vector< Reference<deployment::XPackage > > const * extensionList)
|
|
{
|
|
OSL_ASSERT(xExtMgr.is());
|
|
bool bOnlyBundled = true;
|
|
if (extensionList)
|
|
{
|
|
for (auto const& elem : *extensionList)
|
|
{
|
|
Sequence<Reference<deployment::XPackage> > seqExt = xExtMgr->getExtensionsWithSameIdentifier(
|
|
dp_misc::getIdentifier(elem), elem->getName(), Reference<ucb::XCommandEnvironment>());
|
|
|
|
bOnlyBundled = containsBundledOnly(seqExt);
|
|
if (!bOnlyBundled)
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const uno::Sequence< uno::Sequence< Reference<deployment::XPackage > > > seqAllExt =
|
|
xExtMgr->getAllExtensions(Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>());
|
|
|
|
for (int pos(0), nLen(seqAllExt.getLength()); bOnlyBundled && pos != nLen; ++pos)
|
|
{
|
|
bOnlyBundled = containsBundledOnly(seqAllExt[pos]);
|
|
}
|
|
}
|
|
return bOnlyBundled;
|
|
}
|
|
|
|
} // anon namespace
|
|
|
|
|
|
OUString getExtensionDefaultUpdateURL()
|
|
{
|
|
OUString sUrl(
|
|
u"${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version")
|
|
":Version:ExtensionUpdateURL}"_ustr);
|
|
::rtl::Bootstrap::expandMacros(sUrl);
|
|
return sUrl;
|
|
}
|
|
|
|
/* returns the index of the greatest version, starting with 0
|
|
|
|
*/
|
|
UPDATE_SOURCE isUpdateUserExtension(
|
|
bool bReadOnlyShared,
|
|
OUString const & userVersion,
|
|
OUString const & sharedVersion,
|
|
OUString const & bundledVersion,
|
|
std::u16string_view onlineVersion)
|
|
{
|
|
UPDATE_SOURCE retVal = UPDATE_SOURCE_NONE;
|
|
if (bReadOnlyShared)
|
|
{
|
|
if (!userVersion.isEmpty())
|
|
{
|
|
int index = determineHighestVersion(
|
|
userVersion, sharedVersion, bundledVersion, onlineVersion);
|
|
if (index == 1)
|
|
retVal = UPDATE_SOURCE_SHARED;
|
|
else if (index == 2)
|
|
retVal = UPDATE_SOURCE_BUNDLED;
|
|
else if (index == 3)
|
|
retVal = UPDATE_SOURCE_ONLINE;
|
|
}
|
|
else if (!sharedVersion.isEmpty())
|
|
{
|
|
int index = determineHighestVersion(
|
|
OUString(), sharedVersion, bundledVersion, onlineVersion);
|
|
if (index == 2)
|
|
retVal = UPDATE_SOURCE_BUNDLED;
|
|
else if (index == 3)
|
|
retVal = UPDATE_SOURCE_ONLINE;
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!userVersion.isEmpty())
|
|
{
|
|
int index = determineHighestVersion(
|
|
userVersion, sharedVersion, bundledVersion, onlineVersion);
|
|
if (index == 1)
|
|
retVal = UPDATE_SOURCE_SHARED;
|
|
else if (index == 2)
|
|
retVal = UPDATE_SOURCE_BUNDLED;
|
|
else if (index == 3)
|
|
retVal = UPDATE_SOURCE_ONLINE;
|
|
}
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
UPDATE_SOURCE isUpdateSharedExtension(
|
|
bool bReadOnlyShared,
|
|
OUString const & sharedVersion,
|
|
OUString const & bundledVersion,
|
|
std::u16string_view onlineVersion)
|
|
{
|
|
if (bReadOnlyShared)
|
|
return UPDATE_SOURCE_NONE;
|
|
UPDATE_SOURCE retVal = UPDATE_SOURCE_NONE;
|
|
|
|
if (!sharedVersion.isEmpty())
|
|
{
|
|
int index = determineHighestVersion(
|
|
OUString(), sharedVersion, bundledVersion, onlineVersion);
|
|
if (index == 2)
|
|
retVal = UPDATE_SOURCE_BUNDLED;
|
|
else if (index == 3)
|
|
retVal = UPDATE_SOURCE_ONLINE;
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
Reference<deployment::XPackage>
|
|
getExtensionWithHighestVersion(
|
|
Sequence<Reference<deployment::XPackage> > const & seqExt)
|
|
{
|
|
if (!seqExt.hasElements())
|
|
return Reference<deployment::XPackage>();
|
|
|
|
Reference<deployment::XPackage> greatest;
|
|
sal_Int32 len = seqExt.getLength();
|
|
|
|
for (sal_Int32 i = 0; i < len; i++)
|
|
{
|
|
if (!greatest.is())
|
|
{
|
|
greatest = seqExt[i];
|
|
continue;
|
|
}
|
|
Reference<deployment::XPackage> const & current = seqExt[i];
|
|
//greatest has a value
|
|
if (! current.is())
|
|
continue;
|
|
|
|
if (dp_misc::compareVersions(current->getVersion(), greatest->getVersion()) == dp_misc::GREATER)
|
|
greatest = current;
|
|
}
|
|
return greatest;
|
|
}
|
|
|
|
UpdateInfo::UpdateInfo( Reference< deployment::XPackage> const & ext):
|
|
extension(ext)
|
|
{
|
|
}
|
|
|
|
|
|
UpdateInfoMap 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)
|
|
{
|
|
OSL_ASSERT(xExtMgr.is());
|
|
UpdateInfoMap infoMap;
|
|
if (!xExtMgr.is() || onlyBundledExtensions(xExtMgr, extensionList))
|
|
return infoMap;
|
|
|
|
if (!extensionList)
|
|
{
|
|
const uno::Sequence< uno::Sequence< Reference<deployment::XPackage > > > seqAllExt = xExtMgr->getAllExtensions(
|
|
Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>());
|
|
|
|
//fill the UpdateInfoMap. key = extension identifier, value = UpdateInfo
|
|
for (int pos = seqAllExt.getLength(); pos --; )
|
|
{
|
|
uno::Sequence<Reference<deployment::XPackage> > const & seqExt = seqAllExt[pos];
|
|
|
|
Reference<deployment::XPackage> extension = getExtensionWithHighestVersion(seqExt);
|
|
OSL_ASSERT(extension.is());
|
|
|
|
std::pair<UpdateInfoMap::iterator, bool> insertRet = infoMap.emplace(
|
|
dp_misc::getIdentifier(extension), UpdateInfo(extension));
|
|
OSL_ASSERT(insertRet.second);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (auto const& elem : *extensionList)
|
|
{
|
|
OSL_ASSERT(elem.is());
|
|
std::pair<UpdateInfoMap::iterator, bool> insertRet = infoMap.emplace(
|
|
dp_misc::getIdentifier(elem), UpdateInfo(elem));
|
|
OSL_ASSERT(insertRet.second);
|
|
}
|
|
}
|
|
|
|
//Now find the update information for the extensions which provide their own
|
|
//URLs to update information.
|
|
bool bAllInfosObtained = false;
|
|
getOwnUpdateInfos(xContext, updateInformation, infoMap, out_errors, bAllInfosObtained);
|
|
|
|
if (!bAllInfosObtained)
|
|
getDefaultUpdateInfos(xContext, updateInformation, infoMap, out_errors);
|
|
return infoMap;
|
|
}
|
|
OUString getHighestVersion(
|
|
OUString const & sharedVersion,
|
|
OUString const & bundledVersion,
|
|
OUString const & onlineVersion)
|
|
{
|
|
int index = determineHighestVersion(OUString(), sharedVersion, bundledVersion, onlineVersion);
|
|
switch (index)
|
|
{
|
|
case 1: return sharedVersion;
|
|
case 2: return bundledVersion;
|
|
case 3: return onlineVersion;
|
|
default: OSL_ASSERT(false);
|
|
}
|
|
|
|
return OUString();
|
|
}
|
|
} //namespace dp_misc
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|