summaryrefslogtreecommitdiffstats
path: root/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatefinder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatefinder.cpp')
-rw-r--r--installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatefinder.cpp892
1 files changed, 892 insertions, 0 deletions
diff --git a/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatefinder.cpp b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatefinder.cpp
new file mode 100644
index 000000000..8d34586e8
--- /dev/null
+++ b/installerbuilder/libinstaller/kdtools/KDUpdater/kdupdaterupdatefinder.cpp
@@ -0,0 +1,892 @@
+/****************************************************************************
+** Copyright (C) 2001-2010 Klaralvdalens Datakonsult AB. All rights reserved.
+**
+** This file is part of the KD Tools library.
+**
+** Licensees holding valid commercial KD Tools licenses may use this file in
+** accordance with the KD Tools Commercial License Agreement provided with
+** the Software.
+**
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU Lesser General Public License version 2 and version 3 as published by the
+** Free Software Foundation and appearing in the file LICENSE.LGPL included.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** Contact info@kdab.com if any conditions of this licensing are not
+** clear to you.
+**
+**********************************************************************/
+
+#include "kdupdaterupdatefinder.h"
+#include "kdupdaterapplication.h"
+#include "kdupdaterupdatesourcesinfo.h"
+#include "kdupdaterpackagesinfo.h"
+#include "kdupdaterupdate.h"
+#include "kdupdaterfiledownloader_p.h"
+#include "kdupdaterfiledownloaderfactory.h"
+#include "kdupdaterupdatesinfo_p.h"
+#include "kdupdatersignatureverifier.h"
+
+#include <QCoreApplication>
+#include <QDebug>
+
+using namespace KDUpdater;
+
+/*!
+ \ingroup kdupdater
+ \class KDUpdater::UpdateFinder kdupdaterupdatefinder KDUpdaterUpdateFinder
+ \brief Finds updates applicable for a \ref KDUpdater::Application
+
+ The KDUpdater::UpdateFinder class helps in searching for updates and installing it on the application. The
+ class basically processes the application's \ref KDUpdater::PackagesInfo and the UpdateXMLs it aggregates
+ from all the update sources described in KDUpdater::UpdateSourcesInfo and populates a list of
+ \ref KDUpdater::Update objects. This list can then be passed to \ref KDUpdater::UpdateInstaller for
+ actually downloading and installing the updates.
+
+
+ Usage:
+ \code
+ KDUpdater::UpdateFinder updateFinder( application );
+ QProgressDialog finderProgressDlg;
+
+ QObject::connect( &updateFinder, SIGNAL(progressValue(int)),
+ &finderProgressDlg, SLOT(setValue(int)));
+ QObject::connect( &updateFinder, SIGNAL(computeUpdatesCompleted()),
+ &finderProgressDlg, SLOT(accept()));
+ QObject::connect( &updateFinder, SIGNAL(computeUpdatesCanceled()),
+ &finderProgressDlg, SLOT(reject()));
+
+ QObject::connect( &finderProgressDlg, SIGNAL(canceled()),
+ &updateFinder, SLOT(cancelComputeUpdates()));
+
+ updateFinder.run();
+ finderProgressDlg.exec();
+
+// Control comes here after update finding is done or canceled.
+
+QList<KDUpdater::Update*> updates = updateFinder.updates();
+KDUpdater::UpdateInstaller updateInstaller;
+updateInstaller.installUpdates( updates );
+
+\endcode
+*/
+
+
+//
+// Private
+//
+class KDUpdater::UpdateFinder::Private
+{
+public:
+ Private( UpdateFinder* qq ) :
+ q( qq ),
+ application(0),
+ updateType(KDUpdater::PackageUpdate)
+ {}
+
+ ~Private()
+ {
+ qDeleteAll( updates );
+ qDeleteAll( updatesInfoList );
+ qDeleteAll( updateXmlFDList );
+ }
+
+ UpdateFinder* q;
+ KDUpdater::Application* application;
+ QList<KDUpdater::Update*> updates;
+ UpdateTypes updateType;
+
+ // Temporary structure that notes down information about updates.
+ bool cancel;
+ int downloadCompleteCount;
+ QList<KDUpdater::UpdateSourceInfo> updateSourceInfoList;
+ QList<KDUpdater::UpdatesInfo*> updatesInfoList;
+ QList<KDUpdater::FileDownloader*> updateXmlFDList;
+
+ void clear();
+ void computeUpdates();
+ void cancelComputeUpdates();
+ bool downloadUpdateXMLFiles();
+ bool computeApplicableUpdates();
+
+ QList<KDUpdater::UpdateInfo> applicableUpdates(KDUpdater::UpdatesInfo* updatesInfo, bool addNewPackages = false );
+ void createUpdateObjects(const KDUpdater::UpdateSourceInfo& sourceInfo,
+ const QList<KDUpdater::UpdateInfo>& updateInfoList);
+ bool checkForUpdatePriority(const KDUpdater::UpdateSourceInfo& sourceInfo,
+ const KDUpdater::UpdateInfo& updateInfo);
+ int pickUpdateFileInfo(const QList<KDUpdater::UpdateFileInfo>& updateFiles);
+ void slotDownloadDone();
+};
+
+
+static int computeProgressPercentage(int min, int max, int percent)
+{
+ return min + qint64(max-min) * percent / 100;
+}
+
+static int computePercent(int done, int total)
+{
+ return total ? done * Q_INT64_C(100) / total : 0 ;
+}
+
+/*!
+ \internal
+
+ Releases all internal resources consumed while downloading and computing updates.
+*/
+void KDUpdater::UpdateFinder::Private::clear()
+{
+ qDeleteAll( updates );
+ updates.clear();
+ qDeleteAll( updatesInfoList );
+ updatesInfoList.clear();
+ qDeleteAll( updateXmlFDList );
+ updateXmlFDList.clear();
+ updateSourceInfoList.clear();
+
+ downloadCompleteCount = 0;
+}
+
+/*!
+ \internal
+
+ This method computes the updates that can be applied on the application by
+ studying the application's \ref KDUpdater::PackagesInfo object and the UpdateXML files
+ from each of the update sources described in \ref KDUpdater::UpdateSourcesInfo.
+
+ This function can take a long time to complete. The following signals are emitted
+ during the execution of this function
+
+ The function creates \ref KDUpdater::Update objects on the stack. All KDUpdater::Update objects
+ are made children of the application associated with this finder.
+
+ The update sources are fetched from the \ref KDUpdater::UpdateSourcesInfo object associated with
+ the application. Package information is extracted from the \ref KDUpdater::PackagesInfo object
+ associated with the application.
+
+ \note Each time this function is called, all the previously computed updates are discarded
+and its resources are freed.
+*/
+void KDUpdater::UpdateFinder::Private::computeUpdates()
+{
+ // Computing updates is done in two stages
+ // 1. Downloading Update XML files from all the update sources
+ // 2. Matching updates with Package XML and figuring out available updates
+
+ cancel = false;
+ clear();
+
+ // First do some quick sanity checks on the packages info
+ KDUpdater::PackagesInfo* packages = application->packagesInfo();
+ if( !packages ) {
+ q->reportError(tr("Could not access the package information of this application"));
+ return;
+ }
+ if( !packages->isValid() ) {
+ q->reportError(packages->errorString());
+ return;
+ }
+
+ // Now do some quick sanity checks on the update sources info
+ KDUpdater::UpdateSourcesInfo* sources = application->updateSourcesInfo();
+ if( !sources ) {
+ q->reportError(tr("Could not access the update sources information of this application"));
+ return;
+ }
+ if( !sources->isValid() ) {
+ q->reportError(sources->errorString());
+ return;
+ }
+
+ // Now we can start...
+
+ // Step 1: 0 - 49 percent
+ if(!downloadUpdateXMLFiles() || cancel)
+ {
+ clear();
+ return;
+ }
+
+ // Step 2: 50 - 100 percent
+ if(!computeApplicableUpdates() || cancel)
+ {
+ clear();
+ return;
+ }
+
+ // All done
+ q->reportProgress( 100, tr("%1 updates found").arg(updates.count()) );
+ q->reportDone();
+}
+
+/*!
+ \internal
+
+ Cancels the computation of updates.
+
+ \sa \ref computeUpdates()
+*/
+void KDUpdater::UpdateFinder::Private::cancelComputeUpdates()
+{
+ cancel = true;
+}
+
+/*!
+ \internal
+
+ This function downloads Updates.xml from all the update sources. A single application can potentially
+ have several update sources, hence we need to be asynchronous in downloading updates from different
+ sources.
+
+ The function basically does this for each update source
+ a) Create a KDUpdater::FileDownloader and KDUpdater::UpdatesInfo for each update
+ b) Triggers the download of Updates.xml from each file downloader.
+ c) The downloadCompleted(), downloadCanceled() and downloadAborted() signals are connected
+ in each of the downloaders. Once all the downloads are complete and/or aborted, the next stage
+ would be done.
+
+ The function gets into an event loop until all the downloads are complete.
+*/
+bool KDUpdater::UpdateFinder::Private::downloadUpdateXMLFiles()
+{
+ if( !application )
+ return false;
+
+ KDUpdater::UpdateSourcesInfo* updateSources = application->updateSourcesInfo();
+ if( !updateSources )
+ return false;
+
+ // Create KDUpdater::FileDownloader and KDUpdater::UpdatesInfo for each update
+ for(int i=0; i<updateSources->updateSourceInfoCount(); i++)
+ {
+ KDUpdater::UpdateSourceInfo info = updateSources->updateSourceInfo(i);
+ QUrl updateXmlUrl = QString::fromLatin1("%1/Updates.xml").arg(info.url.toString());
+
+ const SignatureVerifier* verifier = application->signatureVerifier( Application::Metadata );
+ KDUpdater::FileDownloader* downloader = FileDownloaderFactory::instance().create(updateXmlUrl.scheme(), verifier, QUrl(), q);
+ if( !downloader )
+ continue;
+
+ downloader->setUrl(updateXmlUrl);
+ downloader->setAutoRemoveDownloadedFile(true);
+
+ KDUpdater::UpdatesInfo* updatesInfo = new KDUpdater::UpdatesInfo;
+ updateSourceInfoList.append(info);
+ updateXmlFDList.append(downloader);
+ updatesInfoList.append(updatesInfo);
+
+ connect(downloader, SIGNAL(downloadCompleted()),
+ q, SLOT(slotDownloadDone()));
+ connect(downloader, SIGNAL(downloadCanceled()),
+ q, SLOT(slotDownloadDone()));
+ connect(downloader, SIGNAL(downloadAborted(QString)),
+ q, SLOT(slotDownloadDone()));
+ }
+
+ // Trigger download of Updates.xml file
+ downloadCompleteCount = 0;
+ for(int i=0; i<updateXmlFDList.count(); i++)
+ {
+ KDUpdater::FileDownloader* downloader = updateXmlFDList[i];
+ downloader->download();
+ }
+
+ // Wait until all downloaders have completed their downloads.
+ while(1)
+ {
+ QCoreApplication::processEvents();
+ if( cancel )
+ return false;
+ if( downloadCompleteCount == updateXmlFDList.count())
+ break;
+
+ int pc = computePercent(downloadCompleteCount, updateXmlFDList.count());
+ q->reportProgress(pc, tr("Downloading Updates.xml from update-sources"));
+ }
+
+ // All the downloaders have now either downloaded or aborted the
+ // donwload of update XML files.
+
+ // Lets now get rid of update sources whose Updates.xml could not be downloaded
+ for(int i=0; i<updateXmlFDList.count(); i++)
+ {
+ KDUpdater::FileDownloader* downloader = updateXmlFDList[i];
+ if( downloader->isDownloaded() )
+ continue;
+
+ KDUpdater::UpdateSourceInfo info = updateSourceInfoList[i];
+ QString msg = tr("Could not download updates from %1 ('%2')").arg(info.name, info.url.toString());
+ q->reportError(msg);
+
+ delete updatesInfoList[i];
+ delete downloader;
+ updateXmlFDList.removeAt(i);
+ updatesInfoList.removeAt(i);
+ updateSourceInfoList.removeAt(i);
+ --i;
+ }
+
+ if (updatesInfoList.isEmpty()) {
+ return false;
+ }
+
+ // Lets parse the downloaded update XML files and get rid of the downloaders.
+ for(int i=0; i<updateXmlFDList.count(); i++)
+ {
+ KDUpdater::FileDownloader* downloader = updateXmlFDList[i];
+ KDUpdater::UpdatesInfo* updatesInfo = updatesInfoList[i];
+
+ updatesInfo->setFileName( downloader->downloadedFileName() );
+
+ if (!updatesInfo->isValid()) {
+ QString msg = updatesInfo->errorString();
+ q->reportError(msg);
+
+ delete updatesInfoList[i];
+ delete downloader;
+ updateXmlFDList.removeAt(i);
+ updatesInfoList.removeAt(i);
+ --i;
+ }
+ }
+ qDeleteAll( updateXmlFDList );
+ updateXmlFDList.clear();
+
+ if (updatesInfoList.isEmpty()) {
+ return false;
+ }
+
+ q->reportProgress( 49, tr("Updates.xml file(s) downloaded from update sources") );
+ return true;
+}
+
+/*!
+ \internal
+
+ This function runs through all the KDUpdater::UpdatesInfo objects created during
+ the downloadUpdateXMLFiles() method and compares it with the data contained in
+ KDUpdater::PackagesInfo. There by figures out whether an update is applicable for
+ this application or not.
+*/
+bool KDUpdater::UpdateFinder::Private::computeApplicableUpdates()
+{
+ if( updateType & KDUpdater::CompatUpdate )
+ {
+ KDUpdater::UpdateInfo compatUpdateInfo;
+ KDUpdater::UpdateSourceInfo compatUpdateSourceInfo;
+
+ // Required compat level
+ int reqCompatLevel = application->compatLevel()+1;
+
+ q->reportProgress(60, tr("Looking for compatibility update..."));
+
+ // We are only interested in compat updates.
+ for(int i=0; i<updatesInfoList.count(); i++)
+ {
+ KDUpdater::UpdatesInfo* info = updatesInfoList[i];
+ KDUpdater::UpdateSourceInfo updateSource = updateSourceInfoList[i];
+
+ // If we already have a compat update, just check if the source currently being
+ // considered has a higher priority or not.
+ if(compatUpdateInfo.data.contains( QLatin1String( "CompatLevel" ) ) && updateSource.priority < compatUpdateSourceInfo.priority)
+ continue;
+
+ // Lets look for comapt updates that provide compat level one-higher than
+ // the application's current compat level.
+ QList<KDUpdater::UpdateInfo> updatesInfo = info->updatesInfo( KDUpdater::CompatUpdate, reqCompatLevel );
+
+ if( updatesInfo.count() == 0 )
+ continue;
+
+ compatUpdateInfo = updatesInfo.at( 0 );
+ compatUpdateSourceInfo = updateSource;
+ }
+
+ bool found = (compatUpdateInfo.data.contains( QLatin1String( "CompatLevel" ) ));
+ if(found)
+ {
+ q->reportProgress(80, tr("Found compatibility update.."));
+
+ // Lets create an update for this compat update.
+ QString updateName = tr("Compatibility level %1 update").arg(reqCompatLevel);
+ QUrl url;
+
+ // Pick a update file based on arch and OS.
+ int pickUpdateFileIndex = pickUpdateFileInfo(compatUpdateInfo.updateFiles);
+ if(pickUpdateFileIndex < 0)
+ {
+ q->reportError(tr("Compatibility update for the required architecture and hardware configuration was not found"));
+ q->reportProgress(100, tr("Compatibility update not found"));
+ return false;
+ }
+
+ KDUpdater::UpdateFileInfo fileInfo = compatUpdateInfo.updateFiles.at( pickUpdateFileIndex );
+
+ // Create an update for this entry
+ url = QString::fromLatin1( "%1/%2" ).arg( compatUpdateSourceInfo.url.toString(), fileInfo.fileName );
+ KDUpdater::Update* update = q->constructUpdate(application,
+ compatUpdateSourceInfo,
+ KDUpdater::CompatUpdate, url,
+ compatUpdateInfo.data, fileInfo.compressedSize, fileInfo.uncompressedSize, fileInfo.sha1sum );
+
+ // Register the update
+ updates.append(update);
+
+ // Done
+ q->reportProgress(100, tr("Compatibility update found"));
+ }
+ else
+ q->reportProgress(100, tr("No compatibility updates found"));
+ }
+ if ( updateType & PackageUpdate )
+ {
+ // We are not looking for normal updates, not compat ones.
+ for(int i=0; i<updatesInfoList.count(); i++)
+ {
+ // Fetch updates applicable to this application.
+ KDUpdater::UpdatesInfo* info = updatesInfoList[i];
+ QList<KDUpdater::UpdateInfo> updates = applicableUpdates(info , updateType & NewPackage );
+ if( !updates.count() )
+ continue;
+
+ if( cancel )
+ return false;
+ KDUpdater::UpdateSourceInfo updateSource = updateSourceInfoList[i];
+
+ // Create KDUpdater::Update objects for updates that have a valid
+ // UpdateFile
+ createUpdateObjects(updateSource, updates);
+ if( cancel )
+ return false;
+
+ // Report progress
+ int pc = computePercent(i, updatesInfoList.count());
+ pc = computeProgressPercentage(51, 100, pc);
+ q->reportProgress( pc, tr("Computing applicable updates") );
+ }
+ }
+
+ q->reportProgress( 99, tr("Application updates computed") );
+ return true;
+}
+
+QList<KDUpdater::UpdateInfo> KDUpdater::UpdateFinder::Private::applicableUpdates( KDUpdater::UpdatesInfo* updatesInfo, bool addNewPackages )
+{
+ QList<KDUpdater::UpdateInfo> retList;
+
+ if( !updatesInfo || updatesInfo->updateInfoCount( PackageUpdate ) == 0 )
+ return retList;
+
+ KDUpdater::PackagesInfo* packages = this->application->packagesInfo();
+ if( !packages )
+ return retList;
+
+ // Check to see if the updates info contains updates for any application
+ bool anyApp = updatesInfo->applicationName() == QLatin1String( "{AnyApplication}" );
+ int appNameIndex = -1;
+
+ if( !anyApp )
+ {
+ // updatesInfo->applicationName() describes one application or a series of
+ // application names separated by commas.
+ QString appName = updatesInfo->applicationName();
+ appName = appName.replace(QLatin1String( ", " ),
+ QLatin1String( "," ));
+ appName = appName.replace(QLatin1String( " ," ),
+ QLatin1String( "," ));
+
+ // Catch hold of app names contained updatesInfo->applicationName()
+ QStringList apps = appName.split(QLatin1String( "," ), QString::SkipEmptyParts);
+ appNameIndex = apps.indexOf(this->application->applicationName());
+
+ // If the application appName isnt one of the app names, then
+ // the updates are not applicable.
+ if( appNameIndex < 0 )
+ return retList;
+ }
+
+#if 0 //Nokia-SDK: ignore ApplicationVersion, it has no purpose and just causes problems if someone bumps the config.xml application version accidentally
+ // Check to see if the update repository versions match with app version
+ if( !anyApp )
+ {
+ QString appVersion = updatesInfo->applicationVersion();
+ appVersion = appVersion.replace(QLatin1String( ", " ), QLatin1String( "," ));
+ appVersion = appVersion.replace(QLatin1String( " ," ), QLatin1String( "," ));
+ QStringList versions = appVersion.split(QLatin1String( "," ), QString::SkipEmptyParts);
+
+ if( appNameIndex >= versions.count() )
+ return retList; // please give us well formatted Updates.xml files.
+
+ QString version = versions[appNameIndex];
+ if( KDUpdater::compareVersion(this->application->applicationVersion(), version) != 0 )
+ return retList;
+ }
+#endif
+
+ // Check to see if version numbers match. This means that the version
+ // number of the update should be greater than the version number of
+ // the package that is currently installed.
+ QList<KDUpdater::UpdateInfo> updateList = updatesInfo->updatesInfo( KDUpdater::PackageUpdate );
+ for(int i=0; i<updatesInfo->updateInfoCount( PackageUpdate ); i++)
+ {
+ KDUpdater::UpdateInfo updateInfo = updateList.at( i );
+ if( !addNewPackages )
+ {
+ int pkgInfoIdx = packages->findPackageInfo( updateInfo.data.value( QLatin1String( "Name" ) ).toString() );
+ if( pkgInfoIdx < 0 )
+ continue;
+
+ KDUpdater::PackageInfo pkgInfo = packages->packageInfo( pkgInfoIdx );
+
+ // First check to see if the update version is more than package version
+ QString updateVersion = updateInfo.data.value( QLatin1String( "Version" ) ).toString();
+ QString pkgVersion = pkgInfo.version;
+ if( KDUpdater::compareVersion(updateVersion, pkgVersion) <= 0 )
+ continue;
+
+ // It is quite possible that we may have already installed the update.
+ // Lets check the last update date of the package and the release date
+ // of the update. This way we can compare and figure out if the update
+ // has been installed or not.
+ QDate pkgDate = pkgInfo.lastUpdateDate;
+ QDate updateDate = updateInfo.data.value( QLatin1String( "ReleaseDate" ) ).toDate();
+ if( pkgDate > updateDate )
+ continue;
+ }
+
+ // Bingo!, we found an update :-)
+ retList.append(updateInfo);
+ }
+
+ return retList;
+}
+
+void KDUpdater::UpdateFinder::Private::createUpdateObjects(const KDUpdater::UpdateSourceInfo& sourceInfo, const QList<KDUpdater::UpdateInfo>& updateInfoList)
+{
+ for(int i=0; i<updateInfoList.count(); i++)
+ {
+ KDUpdater::UpdateInfo info = updateInfoList[i];
+ // Compat level checks
+ if( info.data.contains( QLatin1String( "RequiredCompatLevel" ) ) &&
+ info.data.value( QLatin1String( "RequiredCompatLevel" ) ).toInt() != application->compatLevel() )
+ {
+ qDebug() << "Update \"" << info.data.value( QLatin1String( "Name" ) ).toString() << "\" at \""
+ << sourceInfo.name << "\"(\"" << sourceInfo.url.toString() << "\") requires a different compat level";
+ continue; // Compatibility level mismatch
+ }
+
+ // If another update of the same name exists, then use the update coming from
+ // a higher priority.
+ if( !checkForUpdatePriority(sourceInfo, info) )
+ {
+ qDebug() << "Skipping Update \""
+ << info.data.value( QLatin1String( "Name" ) ).toString()
+ << "\" from \""
+ << sourceInfo.name
+ << "\"(\""
+ << sourceInfo.url.toString()
+ << "\") because an update with the same name was found from a higher priority location";
+
+ continue;
+ }
+
+ // Pick a update file based on arch and OS.
+ int pickUpdateFileIndex = this->pickUpdateFileInfo(info.updateFiles);
+ if(pickUpdateFileIndex < 0)
+ continue;
+
+ KDUpdater::UpdateFileInfo fileInfo = info.updateFiles.at( pickUpdateFileIndex );
+
+ // Create an update for this entry
+ QUrl url( QString::fromLatin1("%1/%2").arg( sourceInfo.url.toString(), fileInfo.fileName ) );
+ KDUpdater::Update* update = q->constructUpdate(application, sourceInfo, KDUpdater::PackageUpdate, url, info.data, fileInfo.compressedSize, fileInfo.uncompressedSize, fileInfo.sha1sum );
+
+ // Register the update
+ this->updates.append(update);
+ }
+}
+
+bool KDUpdater::UpdateFinder::Private::checkForUpdatePriority(const KDUpdater::UpdateSourceInfo& sourceInfo, const KDUpdater::UpdateInfo& updateInfo)
+{
+ for(int i=0; i<this->updates.count(); i++)
+ {
+ KDUpdater::Update* update = this->updates[i];
+ if( update->data( QLatin1String( "Name" ) ).toString() != updateInfo.data.value( QLatin1String( "Name" ) ).toString() )
+ continue;
+
+ // Bingo, update was previously found elsewhere.
+
+ // If the existing update comes from a higher priority server, then cool :)
+ if( update->sourceInfo().priority > sourceInfo.priority )
+ return false;
+
+ // If the existing update has a higher version number, keep it
+ if ( KDUpdater::compareVersion(update->data( QLatin1String( "Version" ) ).toString(),
+ updateInfo.data.value( QLatin1String( "Version" ) ).toString()) > 0)
+ return false;
+
+ // Otherwise the old update must be deleted.
+ this->updates.removeAll(update);
+ delete update;
+
+ return true;
+ }
+
+ // No update by that name was found, so what we have is a priority update.
+ return true;
+}
+
+int KDUpdater::UpdateFinder::Private::pickUpdateFileInfo(const QList<KDUpdater::UpdateFileInfo>& updateFiles)
+{
+#ifdef Q_WS_MAC
+ QString os = QLatin1String( "MacOSX" );
+#endif
+#ifdef Q_WS_WIN
+ QString os = QLatin1String( "Windows" );
+#endif
+#ifdef Q_WS_X11
+ QString os = QLatin1String( "Linux" );
+#endif
+
+ QString arch = QLatin1String( "i386" ); // only one architecture considered for now.
+
+ for(int i=0; i<updateFiles.count(); i++)
+ {
+ KDUpdater::UpdateFileInfo fileInfo = updateFiles[i];
+
+ if( fileInfo.arch != arch )
+ continue;
+
+ if( fileInfo.os != QLatin1String( "Any" ) && fileInfo.os != os )
+ continue;
+
+ return i;
+ }
+
+ return -1;
+}
+
+
+
+//
+// UpdateFinder
+//
+
+/*!
+ Constructs a update finder for a given \ref KDUpdater::Application.
+*/
+KDUpdater::UpdateFinder::UpdateFinder(KDUpdater::Application* application)
+ : KDUpdater::Task(QLatin1String( "UpdateFinder" ), Stoppable, application),
+ d( new Private( this ) )
+{
+ d->application = application;
+}
+
+/*!
+ Destructor
+*/
+KDUpdater::UpdateFinder::~UpdateFinder()
+{
+ delete d;
+}
+
+/*!
+ Returns a pointer to the update application for which this function computes all
+ the updates.
+*/
+KDUpdater::Application* KDUpdater::UpdateFinder::application() const
+{
+ return d->application;
+}
+
+/*!
+ Returns a list of KDUpdater::Update objects. The update objects returned in this list
+ are made children of the \ref KDUpdater::Application object associated with this class.
+*/
+QList<KDUpdater::Update*> KDUpdater::UpdateFinder::updates() const
+{
+ return d->updates;
+}
+
+/*!
+ Looks only for a certain type of update. By default, only package update
+*/
+void KDUpdater::UpdateFinder::setUpdateType(UpdateTypes type)
+{
+ d->updateType = type;
+}
+
+/*!
+ Returns the type of updates searched
+*/
+KDUpdater::UpdateTypes KDUpdater::UpdateFinder::updateType() const
+{
+ return d->updateType;
+}
+
+/*!
+ \internal
+
+ Implemented from \ref KDUpdater::Task::doStart().
+*/
+void KDUpdater::UpdateFinder::doRun()
+{
+ d->computeUpdates();
+}
+
+/*!
+ \internal
+
+ Implemented form \ref KDUpdater::Task::doStop()
+*/
+bool KDUpdater::UpdateFinder::doStop()
+{
+ d->cancelComputeUpdates();
+
+ // Wait until the cancel has actually happened, and then return.
+ // Thinking of using QMutex for this. Frank/Till any suggestions?
+
+ return true;
+}
+
+/*!
+ \internal
+
+ Implemented form \ref KDUpdater::Task::doStop()
+*/
+bool KDUpdater::UpdateFinder::doPause()
+{
+ // Not a pausable task
+ return false;
+}
+
+/*!
+ \internal
+
+ Implemented form \ref KDUpdater::Task::doStop()
+*/
+bool KDUpdater::UpdateFinder::doResume()
+{
+ // Not a pausable task, hence it is not resumable as well
+ return false;
+}
+
+/*!
+ \internal
+*/
+void KDUpdater::UpdateFinder::Private::slotDownloadDone()
+{
+ ++downloadCompleteCount;
+
+ int pc = computePercent(downloadCompleteCount, updateXmlFDList.count());
+ pc = computeProgressPercentage(0, 45, pc);
+ q->reportProgress( pc, tr("Downloading Updates.xml from update sources") );
+}
+
+/*!
+ \internal
+ */
+KDUpdater::Update* KDUpdater::UpdateFinder::constructUpdate( Application* application, const UpdateSourceInfo& sourceInfo,
+ UpdateType type, const QUrl& updateUrl, const QMap< QString, QVariant >& data, quint64 compressedSize, quint64 uncompressedSize, const QByteArray& sha1sum )
+{
+ return new Update( application, sourceInfo, type, updateUrl, data, compressedSize, uncompressedSize, sha1sum );
+}
+
+
+/*!
+ \ingroup kdupdater
+
+ This function compares two version strings \c v1 and \c v2 and returns
+ -1, 0 or +1 based on the following rule
+
+ \li Returns 0 if v1 == v2
+ \li Returns -1 if v1 < v2
+ \li Returns +1 if v1 > v2
+
+ The function is very similar to \c strcmp(), except that it works on version strings.
+
+ Example:
+ \code
+
+ KDUpdater::compareVersion("2.0", "2.1"); // Returns -1
+ KDUpdater::compareVersion("2.1", "2.0"); // Returns +1
+ KDUpdater::compareVersion("2.0", "2.0"); // Returns 0
+ KDUpdater::compareVersion("2.1", "2.1"); // Returns 0
+
+ KDUpdater::compareVersion("2.0", "2.x"); // Returns 0
+ KDUpdater::compareVersion("2.x", "2.0"); // Returns 0
+
+ KDUpdater::compareVersion("2.0.12.4", "2.1.10.4"); // Returns -1
+ KDUpdater::compareVersion("2.0.12.x", "2.0.x"); // Returns 0
+ KDUpdater::compareVersion("2.1.12.x", "2.0.x"); // Returns +1
+ KDUpdater::compareVersion("2.1.12.x", "2.x"); // Returns 0
+ KDUpdater::compareVersion("2.x", "2.1.12.x"); // Returns 0
+
+ \endcode
+*/
+int KDUpdater::compareVersion(const QString& v1, const QString& v2)
+{
+ // For tests refer VersionCompareFnTest testcase.
+
+ // Check for equality
+ if( v1 == v2 )
+ return 0;
+
+ // Split version numbers across .
+ const QStringList v1_comps = v1.split( QRegExp( QLatin1String( "\\.|-" ) ) );
+ const QStringList v2_comps = v2.split( QRegExp( QLatin1String( "\\.|-" ) ) );
+
+ // Check each component of the version
+ int index = 0;
+ while(1)
+ {
+ if( index == v1_comps.count() && index < v2_comps.count() )
+ return -1;
+ else if( index < v1_comps.count() && index == v2_comps.count() )
+ return +1;
+ else if( index >= v1_comps.count() || index >= v2_comps.count() )
+ break;
+
+ bool v1_ok, v2_ok;
+ int v1_comp = v1_comps[index].toInt(&v1_ok);
+ int v2_comp = v2_comps[index].toInt(&v2_ok);
+
+ if(!v1_ok)
+ {
+ if(v1_comps[index] == QLatin1String( "x" ) )
+ return 0;
+ }
+ if(!v2_ok)
+ {
+ if(v2_comps[index] == QLatin1String( "x") )
+ return 0;
+ }
+ if( !v1_ok && !v2_ok )
+ {
+ return v1_comps[ index ].compare( v2_comps[ index ] );
+ }
+
+ if( v1_comp < v2_comp )
+ return -1;
+
+ if( v1_comp > v2_comp )
+ return +1;
+
+ // v1_comp == v2_comp
+ ++index;
+ }
+
+ if( index < v2_comps.count() )
+ return +1;
+
+ if( index < v1_comps.count() )
+ return -1;
+
+ // Controversial return. I hope this never happens.
+ return 0;
+}
+
+#include "moc_kdupdaterupdatefinder.cpp"