File indexing completed on 2024-05-19 05:29:09

0001 /*
0002  *   SPDX-FileCopyrightText: 2016 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
0003  *
0004  *   SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005  */
0006 
0007 #include "PackageKitSourcesBackend.h"
0008 #include "PackageKitBackend.h"
0009 #include "config-paths.h"
0010 #include <KDesktopFile>
0011 #include <KIO/ApplicationLauncherJob>
0012 #include <KLocalizedString>
0013 #include <PackageKit/Daemon>
0014 #include <QDebug>
0015 #include <QProcess>
0016 #include <QRegularExpression>
0017 #include <QStandardItemModel>
0018 #include <resources/AbstractResourcesBackend.h>
0019 #include <resources/DiscoverAction.h>
0020 #include <resources/SourcesModel.h>
0021 
0022 class PKSourcesModel : public QStandardItemModel
0023 {
0024 public:
0025     PKSourcesModel(PackageKitSourcesBackend *backend)
0026         : QStandardItemModel(backend)
0027         , m_backend(backend)
0028     {
0029     }
0030 
0031     bool setData(const QModelIndex &index, const QVariant &value, int role) override
0032     {
0033         auto item = itemFromIndex(index);
0034         if (!item) {
0035             return false;
0036         }
0037 
0038         switch (role) {
0039         case Qt::CheckStateRole: {
0040             auto transaction = PackageKit::Daemon::global()->repoEnable(item->data(AbstractSourcesBackend::IdRole).toString(), value.toInt() == Qt::Checked);
0041             connect(transaction, &PackageKit::Transaction::errorCode, m_backend, &PackageKitSourcesBackend::transactionError);
0042             return true;
0043         }
0044         }
0045         item->setData(value, role);
0046         return true;
0047     }
0048 
0049 private:
0050     PackageKitSourcesBackend *m_backend;
0051 };
0052 
0053 static DiscoverAction *createActionForService(const QString &servicePath, PackageKitSourcesBackend *backend)
0054 {
0055     DiscoverAction *action = new DiscoverAction(backend);
0056     KDesktopFile parser(servicePath);
0057     action->setIconName(parser.readIcon());
0058     action->setText(parser.readName());
0059     action->setToolTip(parser.readComment());
0060     QObject::connect(action, &DiscoverAction::triggered, action, [backend, servicePath]() {
0061         KService::Ptr service = KService::serviceByStorageId(servicePath);
0062         if (!service) {
0063             qWarning() << "Failed to find service" << servicePath;
0064             return;
0065         }
0066 
0067         auto *job = new KIO::ApplicationLauncherJob(service);
0068         QObject::connect(job, &KJob::finished, backend, [backend, service](KJob *job) {
0069             if (job->error()) {
0070                 Q_EMIT backend->passiveMessage(i18n("Failed to start '%1': %2", service->name(), job->errorString()));
0071             }
0072         });
0073         job->start();
0074     });
0075     return action;
0076 }
0077 
0078 PackageKitSourcesBackend::PackageKitSourcesBackend(AbstractResourcesBackend *parent)
0079     : AbstractSourcesBackend(parent)
0080     , m_sources(new PKSourcesModel(this))
0081 {
0082     connect(PackageKit::Daemon::global(), &PackageKit::Daemon::repoListChanged, this, &PackageKitSourcesBackend::resetSources);
0083     connect(SourcesModel::global(), &SourcesModel::showingNow, this, &PackageKitSourcesBackend::resetSources);
0084 
0085     // Kubuntu-based
0086     auto addNativeSourcesManager = [this](const QString &file) {
0087         auto service = PackageKitBackend::locateService(file);
0088         if (!service.isEmpty()) {
0089             m_actions += QVariant::fromValue<QObject *>(createActionForService(service, this));
0090         }
0091     };
0092 
0093     // New Ubuntu
0094     addNativeSourcesManager(QStringLiteral("software-properties-qt.desktop"));
0095 
0096     // Old Ubuntu
0097     addNativeSourcesManager(QStringLiteral("software-properties-kde.desktop"));
0098 
0099     // OpenSuse
0100     addNativeSourcesManager(QStringLiteral("YaST2/sw_source.desktop"));
0101 }
0102 
0103 QString PackageKitSourcesBackend::idDescription()
0104 {
0105     return i18n("Repository URL:");
0106 }
0107 
0108 QStandardItem *PackageKitSourcesBackend::findItemForId(const QString &id) const
0109 {
0110     for (int i = 0, c = m_sources->rowCount(); i < c; ++i) {
0111         auto it = m_sources->item(i);
0112         if (it->data(AbstractSourcesBackend::IdRole).toString() == id) {
0113             return it;
0114         }
0115     }
0116     return nullptr;
0117 }
0118 
0119 void PackageKitSourcesBackend::addRepositoryDetails(const QString &id, const QString &description, bool enabled)
0120 {
0121     bool add = false;
0122     QStandardItem *item = findItemForId(id);
0123 
0124     if (!item) {
0125         item = new QStandardItem(description);
0126         if (PackageKit::Daemon::backendName() == QLatin1String("aptcc")) {
0127             QRegularExpression exp(QStringLiteral("^/etc/apt/sources.list.d/(.+?).list:.*"));
0128 
0129             auto matchIt = exp.globalMatch(id);
0130             if (matchIt.hasNext()) {
0131                 auto match = matchIt.next();
0132                 item->setData(match.captured(1), Qt::ToolTipRole);
0133             }
0134         }
0135         item->setCheckable(PackageKit::Daemon::roles() & PackageKit::Transaction::RoleRepoEnable);
0136         add = true;
0137     }
0138     item->setData(id, IdRole);
0139     item->setCheckState(enabled ? Qt::Checked : Qt::Unchecked);
0140     item->setEnabled(true);
0141 
0142     if (add) {
0143         m_sources->appendRow(item);
0144     }
0145 }
0146 
0147 QAbstractItemModel *PackageKitSourcesBackend::sources()
0148 {
0149     return m_sources;
0150 }
0151 
0152 bool PackageKitSourcesBackend::addSource(const QString & /*id*/)
0153 {
0154     return false;
0155 }
0156 
0157 bool PackageKitSourcesBackend::removeSource(const QString &id)
0158 {
0159     auto transaction = PackageKit::Daemon::global()->repoRemove(id, false);
0160     connect(transaction, &PackageKit::Transaction::errorCode, this, &PackageKitSourcesBackend::transactionError);
0161     return false;
0162 }
0163 
0164 QVariantList PackageKitSourcesBackend::actions() const
0165 {
0166     return m_actions;
0167 }
0168 
0169 void PackageKitSourcesBackend::resetSources()
0170 {
0171     disconnect(SourcesModel::global(), &SourcesModel::showingNow, this, &PackageKitSourcesBackend::resetSources);
0172     for (int i = 0, count = m_sources->rowCount(); i < count; ++i) {
0173         m_sources->item(i, 0)->setEnabled(false);
0174     }
0175     auto transaction = PackageKit::Daemon::global()->getRepoList();
0176     connect(transaction, &PackageKit::Transaction::repoDetail, this, &PackageKitSourcesBackend::addRepositoryDetails);
0177     connect(transaction, &PackageKit::Transaction::errorCode, this, &PackageKitSourcesBackend::transactionError);
0178     connect(transaction, &PackageKit::Transaction::finished, this, [this] {
0179         for (int i = 0; i < m_sources->rowCount();) {
0180             if (!m_sources->item(i, 0)->isEnabled()) {
0181                 m_sources->removeRow(i);
0182             } else {
0183                 ++i;
0184             }
0185         }
0186     });
0187 }
0188 
0189 void PackageKitSourcesBackend::transactionError(PackageKit::Transaction::Error error, const QString &message)
0190 {
0191     Q_EMIT passiveMessage(message);
0192     qWarning() << "Transaction error: " << error << message << sender();
0193 }