File indexing completed on 2024-04-28 16:54:38

0001 /*
0002     SPDX-FileCopyrightText: 2019 Kai Uwe Broulik <kde@privat.broulik.de>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 #include "utils_p.h"
0008 
0009 #include "notifications.h"
0010 
0011 #include <QAbstractItemModel>
0012 #include <QAbstractProxyModel>
0013 #include <QConcatenateTablesProxyModel>
0014 #include <QCoreApplication>
0015 #include <QDBusConnection>
0016 #include <QDBusConnectionInterface>
0017 #include <QFile>
0018 #include <QFileInfo>
0019 #include <QMetaEnum>
0020 #include <QSettings>
0021 #include <QTextStream>
0022 
0023 #include <KProcessList>
0024 
0025 using namespace NotificationManager;
0026 
0027 QHash<int, QByteArray> Utils::roleNames()
0028 {
0029     static QHash<int, QByteArray> s_roles;
0030 
0031     if (s_roles.isEmpty()) {
0032         // This generates role names from the Roles enum in the form of: FooRole -> foo
0033         const QMetaEnum e = QMetaEnum::fromType<Notifications::Roles>();
0034 
0035         // Qt built-in roles we use
0036         s_roles.insert(Qt::DisplayRole, QByteArrayLiteral("display"));
0037         s_roles.insert(Qt::DecorationRole, QByteArrayLiteral("decoration"));
0038         s_roles.insert(Qt::AccessibleDescriptionRole, QByteArrayLiteral("accessibleDescription"));
0039 
0040         for (int i = 0; i < e.keyCount(); ++i) {
0041             const int value = e.value(i);
0042 
0043             QByteArray key(e.key(i));
0044             key[0] = key[0] + 32; // lower case first letter
0045             key.chop(4); // strip "Role" suffix
0046 
0047             s_roles.insert(value, key);
0048         }
0049 
0050         s_roles.insert(Notifications::IdRole, QByteArrayLiteral("notificationId")); // id is QML-reserved
0051     }
0052 
0053     return s_roles;
0054 }
0055 
0056 QString Utils::processNameFromPid(uint pid)
0057 {
0058     auto processInfo = KProcessList::processInfo(pid);
0059 
0060     if (!processInfo.isValid()) {
0061         return QString();
0062     }
0063 
0064     return processInfo.name();
0065 }
0066 
0067 QString Utils::desktopEntryFromPid(uint pid)
0068 {
0069     const QString flatpakInfoPath = QStringLiteral("/proc/%1/root/.flatpak-info").arg(QString::number(pid));
0070     if (QFileInfo::exists(flatpakInfoPath)) {
0071         QSettings flatpakInfo(flatpakInfoPath, QSettings::IniFormat);
0072 
0073         const QString name = flatpakInfo.value("Application/name").toString();
0074         if (!name.isEmpty()) {
0075             return name;
0076         }
0077 
0078         // If it's a flatpak, can't be a snap, bail out.
0079         return QString();
0080     }
0081 
0082     QFile environFile(QStringLiteral("/proc/%1/environ").arg(QString::number(pid)));
0083     if (environFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
0084         const QByteArray bamfDesktopFileHint = QByteArrayLiteral("BAMF_DESKTOP_FILE_HINT");
0085 
0086         const auto lines = environFile.readAll().split('\0');
0087         for (const QByteArray &line : lines) {
0088             const int equalsIdx = line.indexOf('=');
0089             if (equalsIdx <= 0) {
0090                 continue;
0091             }
0092 
0093             const QByteArray key = line.left(equalsIdx);
0094             if (key == bamfDesktopFileHint) {
0095                 const QByteArray value = line.mid(equalsIdx + 1);
0096                 return value;
0097             }
0098         }
0099     }
0100 
0101     return QString();
0102 }
0103 
0104 QModelIndex Utils::mapToModel(const QModelIndex &idx, const QAbstractItemModel *sourceModel)
0105 {
0106     // KModelIndexProxyMapper can only map different indices to a single source
0107     // but we have the other way round, a single index that splits into different source models
0108     QModelIndex resolvedIdx = idx;
0109     while (resolvedIdx.isValid() && resolvedIdx.model() != sourceModel) {
0110         if (auto *proxyModel = qobject_cast<const QAbstractProxyModel *>(resolvedIdx.model())) {
0111             resolvedIdx = proxyModel->mapToSource(resolvedIdx);
0112             // QConcatenateTablesProxyModel isn't a "real" proxy model, so we need to special case for it :(
0113         } else if (auto *concatenateModel = qobject_cast<const QConcatenateTablesProxyModel *>(resolvedIdx.model())) {
0114             resolvedIdx = concatenateModel->mapToSource(resolvedIdx);
0115         } else {
0116             if (resolvedIdx.model() != sourceModel) {
0117                 resolvedIdx = QModelIndex(); // give up
0118             }
0119         }
0120     }
0121     return resolvedIdx;
0122 }
0123 
0124 bool Utils::isDBusMaster()
0125 {
0126     return qApp->property("_plasma_dbus_master").toBool();
0127 }