PackageKitBackend.cpp 25.5 KB
Newer Older
1 2
/***************************************************************************
 *   Copyright © 2012 Aleix Pol Gonzalez <aleixpol@blue-systems.com>       *
Lukas Appelhans's avatar
Lukas Appelhans committed
3
 *   Copyright © 2013 Lukas Appelhans <l.appelhans@gmx.de>                 *
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *                                                                         *
 *   This program is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU General Public License as        *
 *   published by the Free Software Foundation; either version 2 of        *
 *   the License or (at your option) version 3 or any later version        *
 *   accepted by the membership of KDE e.V. (or its successor approved     *
 *   by the membership of KDE e.V.), which shall act as a proxy            *
 *   defined in Section 14 of version 3 of the license.                    *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
 ***************************************************************************/

#include "PackageKitBackend.h"
23
#include "PackageKitSourcesBackend.h"
24
#include "PackageKitResource.h"
25
#include "PackageKitUpdater.h"
26
#include "AppPackageKitResource.h"
27
#include "PKTransaction.h"
28
#include "LocalFilePKResource.h"
29
#include "TransactionSet.h"
30
#include <resources/AbstractResource.h>
31
#include <resources/StandardBackendUpdater.h>
32
#include <resources/SourcesModel.h>
33 34
#include <appstream/OdrsReviewsBackend.h>
#include <appstream/AppStreamIntegration.h>
35

36
#include <QProcess>
37
#include <QStringList>
38
#include <QDebug>
39
#include <QTimer>
40
#include <QStandardPaths>
41
#include <QFile>
42

43 44 45
#include <PackageKit/Transaction>
#include <PackageKit/Daemon>
#include <PackageKit/Details>
46

47
#include <KLocalizedString>
48
#include <QAction>
49
#include <QMimeDatabase>
50

51
#include "utils.h"
52
#include "config-paths.h"
53

Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
54
DISCOVER_BACKEND_PLUGIN(PackageKitBackend)
55

56 57 58 59 60 61 62 63 64 65 66 67
template <typename T, typename W>
static void setWhenAvailable(const QDBusPendingReply<T>& pending, W func, QObject* parent)
{
    QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(pending, parent);
    QObject::connect(watcher, &QDBusPendingCallWatcher::finished,
                    parent, [func](QDBusPendingCallWatcher* watcher) {
                        watcher->deleteLater();
                        QDBusPendingReply<T> reply = *watcher;
                        func(reply.value());
                    });
}

68
QString PackageKitBackend::locateService(const QString &filename)
69 70 71 72
{
    return QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("applications/")+filename);
}

73
PackageKitBackend::PackageKitBackend(QObject* parent)
74
    : AbstractResourcesBackend(parent)
75
    , m_appdata(new AppStream::Pool)
76
    , m_updater(new PackageKitUpdater(this))
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
77
    , m_refresher(nullptr)
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
78
    , m_isFetching(0)
79
    , m_reviews(AppStreamIntegration::global()->reviews())
80
{
81
    QTimer* t = new QTimer(this);
82
    connect(t, &QTimer::timeout, this, &PackageKitBackend::checkForUpdates);
83 84 85
    t->setInterval(60 * 60 * 1000);
    t->setSingleShot(false);
    t->start();
86

87 88 89 90
    m_delayedDetailsFetch.setSingleShot(true);
    m_delayedDetailsFetch.setInterval(0);
    connect(&m_delayedDetailsFetch, &QTimer::timeout, this, &PackageKitBackend::performDetailsFetch);

91
    connect(PackageKit::Daemon::global(), &PackageKit::Daemon::updatesChanged, this, &PackageKitBackend::fetchUpdates);
92
    connect(PackageKit::Daemon::global(), &PackageKit::Daemon::isRunningChanged, this, &PackageKitBackend::checkDaemonRunning);
93
    connect(m_reviews.data(), &OdrsReviewsBackend::ratingsReady, this, &AbstractResourcesBackend::emitRatingsReady);
94 95

    SourcesModel::global()->addSourcesBackend(new PackageKitSourcesBackend(this));
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
96 97 98

    reloadPackageList();

99
    setWhenAvailable(PackageKit::Daemon::getTimeSinceAction(PackageKit::Transaction::RoleRefreshCache), [this](uint timeSince) {
100 101 102
        if (timeSince > 3600)
            checkForUpdates();
    }, this);
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
103 104
}

105 106
PackageKitBackend::~PackageKitBackend() = default;

107 108 109 110 111
bool PackageKitBackend::isFetching() const
{
    return m_isFetching;
}

112
void PackageKitBackend::acquireFetching(bool f)
113
{
114 115 116 117 118 119
    if (f)
        m_isFetching++;
    else
        m_isFetching--;

    if ((!f && m_isFetching==0) || (f && m_isFetching==1)) {
120 121
        emit fetchingChanged();
    }
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
122
    Q_ASSERT(m_isFetching>=0);
123 124
}

125
void PackageKitBackend::reloadPackageList()
126
{
127
    acquireFetching(true);
128
    if (m_refresher) {
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
129
        disconnect(m_refresher.data(), &PackageKit::Transaction::finished, this, &PackageKitBackend::reloadPackageList);
130
    }
131

132 133 134 135 136 137 138 139 140 141 142 143
    QString error;
    m_appdata.reset(new AppStream::Pool);
    const bool b = m_appdata->load(&error);
    if (!b && m_packages.packages.isEmpty()) {
        qWarning() << "Could not open the AppStream metadata pool" << error;

        QTimer::singleShot(0, this, [this]() {
            Q_EMIT passiveMessage(i18n("Please make sure that Appstream is properly set up on your system"));
        });
    }

    const auto components = m_appdata->components();
144 145
    QStringList neededPackages;
    neededPackages.reserve(components.size());
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
146
    foreach(const AppStream::Component& component, components) {
147 148 149
        if (component.kind() == AppStream::Component::KindFirmware)
            continue;

150 151
        const auto pkgNames = component.packageNames();
        if (pkgNames.isEmpty()) {
152 153 154
            auto launchable = component.launchable(AppStream::Launchable::KindDesktopId);
            if (component.kind() == AppStream::Component::KindDesktopApp && !launchable.entries().isEmpty()) {
                const QString file = locateService(launchable.entries().constFirst());
155 156 157 158 159 160 161 162 163 164 165 166
                if (!file.isEmpty()) {
                    auto trans = PackageKit::Daemon::searchFiles(file);
                    connect(trans, &PackageKit::Transaction::package, this, [trans](PackageKit::Transaction::Info info, const QString &packageID){
                        if (info == PackageKit::Transaction::InfoInstalled)
                            trans->setProperty("installedPackage", packageID);
                    });
                    connect(trans, &PackageKit::Transaction::finished, this, [this, trans, component](PackageKit::Transaction::Exit status) {
                        const auto pkgidVal = trans->property("installedPackage");
                        if (status == PackageKit::Transaction::ExitSuccess && !pkgidVal.isNull()) {
                            const auto pkgid = pkgidVal.toString();
                            acquireFetching(true);
                            auto res = addComponent(component, {PackageKit::Daemon::packageName(pkgid)});
167
                            res->clearPackageIds();
168 169 170 171 172 173 174 175
                            res->addPackageId(PackageKit::Transaction::InfoInstalled, pkgid, true);
                            acquireFetching(false);
                        }
                    });
                    continue;
                }
            }

176
            qDebug() << "no packages for" << component.id();
177 178
            continue;
        }
179
        neededPackages += pkgNames;
180

181
        addComponent(component, pkgNames);
182
    }
183

184
    acquireFetching(false);
185 186 187 188 189 190 191 192 193
    if (!neededPackages.isEmpty()) {
        neededPackages.removeDuplicates();
        resolvePackages(neededPackages);
    } else {
        qDebug() << "empty appstream db";
        if (PackageKit::Daemon::backendName() == QLatin1String("aptcc") || PackageKit::Daemon::backendName().isEmpty()) {
            checkForUpdates();
        }
    }
194 195
}

Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
196
AppPackageKitResource* PackageKitBackend::addComponent(const AppStream::Component& component, const QStringList& pkgNames)
197 198 199 200
{
    Q_ASSERT(isFetching());
    Q_ASSERT(!pkgNames.isEmpty());

201 202 203 204 205 206 207 208

    AppPackageKitResource* res = qobject_cast<AppPackageKitResource*>(m_packages.packages[component.id()]);
    if (!res) {
        res = new AppPackageKitResource(component, pkgNames.at(0), this);
        m_packages.packages[component.id()] = res;
    } else {
        res->clearPackageIds();
    }
209 210 211 212 213 214 215 216 217 218
    foreach (const QString& pkg, pkgNames) {
        m_packages.packageToApp[pkg] += component.id();
    }

    foreach (const QString& pkg, component.extends()) {
        m_packages.extendedBy[pkg] += res;
    }
    return res;
}

219 220 221 222 223 224 225 226
void PackageKitBackend::clearPackages(const QStringList& packageNames)
{
    const auto resources = resourcesByPackageNames<QVector<AbstractResource*>>(packageNames);
    for(auto res: resources) {
        qobject_cast<PackageKitResource*>(res)->clearPackageIds();
    }
}

227 228
void PackageKitBackend::resolvePackages(const QStringList &packageNames)
{
229 230 231
    PackageKit::Transaction * tArch = PackageKit::Daemon::resolve(packageNames, PackageKit::Transaction::FilterArch);
    connect(tArch, &PackageKit::Transaction::package, this, &PackageKitBackend::addPackageArch);
    connect(tArch, &PackageKit::Transaction::errorCode, this, &PackageKitBackend::transactionError);
232

233 234 235 236 237 238
    PackageKit::Transaction * tNotArch = PackageKit::Daemon::resolve(packageNames, PackageKit::Transaction::FilterNotArch);
    connect(tNotArch, &PackageKit::Transaction::package, this, &PackageKitBackend::addPackageNotArch);
    connect(tNotArch, &PackageKit::Transaction::errorCode, this, &PackageKitBackend::transactionError);

    TransactionSet* merge = new TransactionSet({tArch, tNotArch});
    connect(merge, &TransactionSet::allFinished, this, &PackageKitBackend::getPackagesFinished);
239 240 241 242 243
    fetchUpdates();
}

void PackageKitBackend::fetchUpdates()
{
244 245 246
    if (m_updater->isProgressing())
        return;

247
    PackageKit::Transaction * tUpdates = PackageKit::Daemon::getUpdates();
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
248 249 250
    connect(tUpdates, &PackageKit::Transaction::finished, this, &PackageKitBackend::getUpdatesFinished);
    connect(tUpdates, &PackageKit::Transaction::package, this, &PackageKitBackend::addPackageToUpdate);
    connect(tUpdates, &PackageKit::Transaction::errorCode, this, &PackageKitBackend::transactionError);
251
    m_updatesPackageId.clear();
252
    m_hasSecurityUpdates = false;
253 254

    m_updater->setProgressing(true);
255 256
}

257 258 259 260
void PackageKitBackend::addPackageArch(PackageKit::Transaction::Info info, const QString& packageId, const QString& summary)
{
    addPackage(info, packageId, summary, true);
}
261

262 263 264 265 266 267
void PackageKitBackend::addPackageNotArch(PackageKit::Transaction::Info info, const QString& packageId, const QString& summary)
{
    addPackage(info, packageId, summary, false);
}

void PackageKitBackend::addPackage(PackageKit::Transaction::Info info, const QString &packageId, const QString &summary, bool arch)
268
{
269
    const QString packageName = PackageKit::Daemon::packageName(packageId);
270
    QSet<AbstractResource*> r = resourcesByPackageName(packageName);
271
    if (r.isEmpty()) {
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
272 273
        auto pk = new PackageKitResource(packageName, summary, this);
        r = { pk };
274
        m_packagesToAdd.insert(pk);
275
    }
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
276
    foreach(auto res, r)
277
        static_cast<PackageKitResource*>(res)->addPackageId(info, packageId, arch);
278 279
}

280
void PackageKitBackend::getPackagesFinished()
281
{
282
    for(auto it = m_packages.packages.cbegin(); it != m_packages.packages.cend(); ++it) {
283 284
        auto pkr = qobject_cast<PackageKitResource*>(it.value());
        if (pkr->packages().isEmpty()) {
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
285
//             qWarning() << "Failed to find package for" << it.key();
286 287
            m_packagesToDelete += pkr;
        }
288
    }
289 290
    includePackagesToAdd();
}
291

292 293
void PackageKitBackend::includePackagesToAdd()
{
294
    if (m_packagesToAdd.isEmpty() && m_packagesToDelete.isEmpty())
295 296 297 298 299 300
        return;

    acquireFetching(true);
    foreach(PackageKitResource* res, m_packagesToAdd) {
        m_packages.packages[res->packageName()] = res;
    }
301
    foreach(PackageKitResource* res, m_packagesToDelete) {
302 303
        const auto pkgs = m_packages.packageToApp.value(res->packageName(), {res->packageName()});
        foreach(const auto &pkg, pkgs) {
304
            auto res = m_packages.packages.take(pkg);
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
305
            if (res) {
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
306 307 308 309 310
                if (AppPackageKitResource* ares = qobject_cast<AppPackageKitResource*>(res)) {
                    for(const auto &ext: res->extends())
                        m_packages.extendedBy[ext].removeAll(ares);
                }

Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
311 312 313
                emit resourceRemoved(res);
                res->deleteLater();
            }
314
        }
315 316 317
    }
    m_packagesToAdd.clear();
    m_packagesToDelete.clear();
318
    acquireFetching(false);
319 320 321 322 323
}

void PackageKitBackend::transactionError(PackageKit::Transaction::Error, const QString& message)
{
    qWarning() << "Transaction error: " << message << sender();
324
    Q_EMIT passiveMessage(message);
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
325 326
}

327 328
void PackageKitBackend::packageDetails(const PackageKit::Details& details)
{
329
    const QSet<AbstractResource*> resources = resourcesByPackageName(PackageKit::Daemon::packageName(details.packageId()));
330 331 332
    if (resources.isEmpty())
        qWarning() << "couldn't find package for" << details.packageId();

333
    foreach(AbstractResource* res, resources) {
334
        qobject_cast<PackageKitResource*>(res)->setDetails(details);
335
    }
336 337
}

338
QSet<AbstractResource*> PackageKitBackend::resourcesByPackageName(const QString& name) const
339
{
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
340
    return resourcesByPackageNames<QSet<AbstractResource*>>({name});
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
341 342
}

343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
template <typename T>
T PackageKitBackend::resourcesByPackageNames(const QStringList &pkgnames) const
{
    T ret;
    ret.reserve(pkgnames.size());
    for(const QString &name : pkgnames) {
        const QStringList names = m_packages.packageToApp.value(name, QStringList(name));
        foreach(const QString& name, names) {
            AbstractResource* res = m_packages.packages.value(name);
            if (res)
                ret += res;
        }
    }
    return ret;
}

359
void PackageKitBackend::checkForUpdates()
360 361
{
    if (!m_refresher) {
362
        acquireFetching(true);
363
        m_refresher = PackageKit::Daemon::refreshCache(false);
364
        connect(m_refresher.data(), &PackageKit::Transaction::errorCode, this, &PackageKitBackend::transactionError);
365
        connect(m_refresher.data(), &PackageKit::Transaction::finished, this, [this]() {
366
            m_refresher = nullptr;
367 368 369
            reloadPackageList();
            acquireFetching(false);
        });
370
    } else {
371
        qWarning() << "already resetting";
372
    }
373 374
}

375 376
QList<AppStream::Component> PackageKitBackend::componentsById(const QString& id) const
{
377
    return m_appdata->componentsById(id);
378 379
}

Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
380
ResultsStream* PackageKitBackend::search(const AbstractResourcesBackend::Filters& filter)
381
{
382 383
    if (!filter.resourceUrl.isEmpty()) {
        return findResourceByPackageName(filter.resourceUrl);
384 385 386
    } else if (!filter.extends.isEmpty()) {
        const auto ext = kTransform<QVector<AbstractResource*>>(m_packages.extendedBy[filter.extends], [](AppPackageKitResource* a){ return a; });
        return new ResultsStream(QStringLiteral("PackageKitStream-extends"), ext);
387 388
    } else if (filter.search.isEmpty()) {
        return new ResultsStream(QStringLiteral("PackageKitStream-all"), kFilter<QVector<AbstractResource*>>(m_packages.packages, [](AbstractResource* res) { return !res->isTechnical(); }));
389
    } else {
390
        const QList<AppStream::Component> components = m_appdata->search(filter.search);
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
391
        const QStringList ids = kTransform<QStringList>(components, [](const AppStream::Component& comp) { return comp.id(); });
392
        auto stream = new ResultsStream(QStringLiteral("PackageKitStream-search"));
393 394
        if (!ids.isEmpty()) {
            const auto resources = resourcesByPackageNames<QVector<AbstractResource*>>(ids);
395
            QTimer::singleShot(0, this, [stream, resources] () {
396 397 398 399 400 401
                stream->resourcesFound(resources);
            });
        }

        PackageKit::Transaction * tArch = PackageKit::Daemon::resolve(filter.search, PackageKit::Transaction::FilterArch);
        connect(tArch, &PackageKit::Transaction::package, this, &PackageKitBackend::addPackageArch);
402 403
        connect(tArch, &PackageKit::Transaction::package, stream, [stream](PackageKit::Transaction::Info /*info*/, const QString &packageId){
            stream->setProperty("packageId", packageId);
404
        });
405
        connect(tArch, &PackageKit::Transaction::finished, stream, [stream, ids, this](PackageKit::Transaction::Exit status) {
406
            getPackagesFinished();
407
            if (status == PackageKit::Transaction::Exit::ExitSuccess) {
408
                const auto packageId = stream->property("packageId");
409 410 411 412
                if (!packageId.isNull()) {
                    const auto res = resourcesByPackageNames<QVector<AbstractResource*>>({PackageKit::Daemon::packageName(packageId.toString())});
                    stream->resourcesFound(kFilter<QVector<AbstractResource*>>(res, [ids](AbstractResource* res){ return !ids.contains(res->appstreamId()); }));
                }
413
            }
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
414
            stream->finish();
415
        }, Qt::QueuedConnection);
416
        return stream;
417
    }
418 419
}

420
ResultsStream * PackageKitBackend::findResourceByPackageName(const QUrl& url)
421
{
422
    AbstractResource* pkg = nullptr;
423 424 425
    if (url.host().isEmpty())
        passiveMessage(i18n("Malformed appstream url '%1'", url.toDisplayString()));
    else if (url.scheme() == QLatin1String("appstream")) {
426 427 428 429 430 431 432 433 434 435
        static const QMap<QString, QString> deprecatedAppstreamIds = {
            { QStringLiteral("org.kde.krita.desktop"), QStringLiteral("krita.desktop") },
            { QStringLiteral("org.kde.digikam.desktop"), QStringLiteral("digikam.desktop") },
            { QStringLiteral("org.kde.ktorrent.desktop"), QStringLiteral("ktorrent.desktop") },
            { QStringLiteral("org.kde.gcompris.desktop"), QStringLiteral("gcompris.desktop") },
            { QStringLiteral("org.kde.kmymoney.desktop"), QStringLiteral("kmymoney.desktop") },
            { QStringLiteral("org.kde.kolourpaint.desktop"), QStringLiteral("kolourpaint.desktop") },
            { QStringLiteral("org.blender.blender.desktop"), QStringLiteral("blender.desktop") },
        };
        
436 437
        const auto host = url.host();
        if (host.isEmpty())
438
            passiveMessage(i18n("Malformed appstream url '%1'", url.toDisplayString()));
439
        else {
440
            const auto deprecatedHost = deprecatedAppstreamIds.value(url.host()); //try this as fallback
441
            for (auto it = m_packages.packages.constBegin(), itEnd = m_packages.packages.constEnd(); it != itEnd; ++it) {
442
                if (it.key().compare(host, Qt::CaseInsensitive) == 0 || it.key().compare(deprecatedHost, Qt::CaseInsensitive) == 0) {
443 444 445 446
                    pkg = it.value();
                    break;
                }
            }
447
            if (!pkg)
448
                qDebug() << "could not find" << host << deprecatedHost;
449
        }
450
    }
451
    return new ResultsStream(QStringLiteral("PackageKitStream-url"), pkg ? QVector<AbstractResource*>{pkg} : QVector<AbstractResource*>{});
452 453
}

454 455 456 457 458
bool PackageKitBackend::hasSecurityUpdates() const
{
    return m_hasSecurityUpdates;
}

459
int PackageKitBackend::updatesCount() const
460
{
461 462 463 464 465 466 467 468 469 470 471
    int ret = 0;
    QSet<QString> packages;
    for(auto res: upgradeablePackages()) {
        const auto packageName = res->packageName();
        if (packages.contains(packageName)) {
            continue;
        }
        packages.insert(packageName);
        ret += 1;
    }
    return ret;
472 473
}

474
Transaction* PackageKitBackend::installApplication(AbstractResource* app, const AddonList& addons)
475
{
476
    Transaction* t = nullptr;
477 478 479 480 481 482 483 484 485 486 487
    if(!addons.addonsToInstall().isEmpty())
    {
        QVector<AbstractResource*> appsToInstall;

        if(!app->isInstalled())
            appsToInstall << app;

        foreach(const QString& toInstall, addons.addonsToInstall()) {
            appsToInstall += m_packages.packages.value(toInstall);
            Q_ASSERT(appsToInstall.last());
        }
488
        t = new PKTransaction(appsToInstall, Transaction::ChangeAddonsRole);
489 490 491
    }

    if (!addons.addonsToRemove().isEmpty()) {
492
        QVector<AbstractResource*> appsToRemove = kTransform<QVector<AbstractResource*>>(addons.addonsToRemove(), [this](const QString& toRemove){ return m_packages.packages.value(toRemove); });
493
        t = new PKTransaction(appsToRemove, Transaction::RemoveRole);
494
    }
495 496

    if (!app->isInstalled())
497 498 499
        t = installApplication(app);

    return t;
500 501
}

502
Transaction* PackageKitBackend::installApplication(AbstractResource* app)
503
{
504
    return new PKTransaction({app}, Transaction::InstallRole);
505 506
}

507
Transaction* PackageKitBackend::removeApplication(AbstractResource* app)
508
{
509
    Q_ASSERT(!isFetching());
510
    return new PKTransaction({app}, Transaction::RemoveRole);
511 512
}

Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
513
QSet<AbstractResource*> PackageKitBackend::upgradeablePackages() const
514
{
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
515
    QSet<AbstractResource*> ret;
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
516
    ret.reserve(m_updatesPackageId.size());
517
    Q_FOREACH (const QString& pkgid, m_updatesPackageId) {
518
        const QString pkgname = PackageKit::Daemon::packageName(pkgid);
519
        const auto pkgs = resourcesByPackageName(pkgname);
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
520
        if (pkgs.isEmpty()) {
521 522
            qWarning() << "couldn't find resource for" << pkgid;
        }
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
523
        ret.unite(pkgs);
524
    }
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
525
    return ret;
526 527
}

528 529
void PackageKitBackend::addPackageToUpdate(PackageKit::Transaction::Info info, const QString& packageId, const QString& summary)
{
530 531
    if (info == PackageKit::Transaction::InfoBlocked) {
        return;
532
    }
533 534 535 536 537 538

    if (info == PackageKit::Transaction::InfoSecurity)
        m_hasSecurityUpdates = true;

    m_updatesPackageId += packageId;
    addPackage(info, packageId, summary, true);
539 540 541 542
}

void PackageKitBackend::getUpdatesFinished(PackageKit::Transaction::Exit, uint)
{
543 544
    if (!m_updatesPackageId.isEmpty()) {
        PackageKit::Transaction* transaction = PackageKit::Daemon::getDetails(m_updatesPackageId.toList());
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
545 546 547
        connect(transaction, &PackageKit::Transaction::details, this, &PackageKitBackend::packageDetails);
        connect(transaction, &PackageKit::Transaction::errorCode, this, &PackageKitBackend::transactionError);
        connect(transaction, &PackageKit::Transaction::finished, this, &PackageKitBackend::getUpdatesDetailsFinished);
548
    }
549

550 551
    m_updater->setProgressing(false);

552
    includePackagesToAdd();
553 554 555
    emit updatesCountChanged();
}

Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
556
void PackageKitBackend::getUpdatesDetailsFinished(PackageKit::Transaction::Exit exit, uint)
557
{
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
558 559 560
    if (exit != PackageKit::Transaction::ExitSuccess) {
        qWarning() << "Couldn't figure out the updates on PackageKit backend" << exit;
    }
561 562
}

563
bool PackageKitBackend::isPackageNameUpgradeable(const PackageKitResource* res) const
564
{
565 566 567
    return !upgradeablePackageId(res).isEmpty();
}

568
QString PackageKitBackend::upgradeablePackageId(const PackageKitResource* res) const
569 570
{
    QString name = res->packageName();
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
571
    foreach (const QString& pkgid, m_updatesPackageId) {
572
        if (PackageKit::Daemon::packageName(pkgid) == name)
573
            return pkgid;
574
    }
575
    return QString();
576 577
}

578 579
void PackageKitBackend::fetchDetails(const QString& pkgid)
{
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
580
    if (!m_delayedDetailsFetch.isActive()) {
581
        m_delayedDetailsFetch.start();
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
582
    }
583 584 585 586 587 588

    m_packageNamesToFetchDetails += pkgid;
}

void PackageKitBackend::performDetailsFetch()
{
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
589
    Q_ASSERT(!m_packageNamesToFetchDetails.isEmpty());
590 591 592
    const auto ids = m_packageNamesToFetchDetails.toList();

    PackageKit::Transaction* transaction = PackageKit::Daemon::getDetails(ids);
593 594 595 596
    connect(transaction, &PackageKit::Transaction::details, this, &PackageKitBackend::packageDetails);
    connect(transaction, &PackageKit::Transaction::errorCode, this, &PackageKitBackend::transactionError);
}

597 598 599 600 601 602 603
void PackageKitBackend::checkDaemonRunning()
{
    if (!PackageKit::Daemon::isRunning()) {
        qWarning() << "PackageKit stopped running!";
    }
}

604
AbstractBackendUpdater* PackageKitBackend::backendUpdater() const
605
{
606
    return m_updater;
607 608
}

609 610 611 612
QVector<AppPackageKitResource*> PackageKitBackend::extendedBy(const QString& id) const
{
    return m_packages.extendedBy[id];
}
613

614 615
AbstractReviewsBackend* PackageKitBackend::reviewsBackend() const
{
616
    return m_reviews.data();
617
}
618

619 620
AbstractResource * PackageKitBackend::resourceForFile(const QUrl& file)
{
621 622 623 624 625 626 627 628 629 630
    QMimeDatabase db;
    const auto mime = db.mimeTypeForUrl(file);
    if (    mime.inherits(QLatin1String("application/vnd.debian.binary-package"))
         || mime.inherits(QLatin1String("application/x-rpm"))
         || mime.inherits(QLatin1String("application/x-tar"))
         || mime.inherits(QLatin1String("application/x-xz-compressed-tar"))
    ) {
        return new LocalFilePKResource(file, this);
    }
    return nullptr;
631 632
}

633 634
static QString readDistroName()
{
635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653
    const QStringList osreleasenames = (QStringList() << QStringLiteral("/etc/os-release")
                                                      << QStringLiteral("/usr/lib/os-release"));
    foreach (QString osrelease, osreleasenames)
    {
        QFile file(osrelease);
        if (file.open(QIODevice::ReadOnly | QIODevice::Text))
        {
            QByteArray line;
            while (!file.atEnd()) {
                line = file.readLine().trimmed();
                if (line.startsWith("NAME=")) {
                    auto output = line.right(line.length()-5);
                    output = output.replace('\"',"");
                    return QString::fromLocal8Bit(output);
                }
            }
        }
    }

654 655 656 657 658 659 660 661 662 663 664 665 666 667 668
    QProcess process;
    process.setEnvironment({QStringLiteral("LC_ALL=C")});
    process.start(QStringLiteral("lsb_release"), {QStringLiteral("-sd")});
    process.waitForFinished();
    auto output = process.readAll().trimmed();
    if (output.startsWith('\"') && output.endsWith('\"'))
        output = output.mid(1, output.length()-2);
    return QString::fromLocal8Bit(output);
}

QString PackageKitBackend::displayName() const
{
    static const QString distro = readDistroName();
    return distro;
}
669

670
#include "PackageKitBackend.moc"