From 951551bc6d1ab61e4d06fe48830459f23879097c Mon Sep 17 00:00:00 2001 From: Kai Uwe Broulik Date: Tue, 18 Jun 2019 17:21:42 +0200 Subject: [PATCH] [Notifications] Read BAMF_DESKTOP_FILE_HINT from environment This is a similar situation we have with Flatpak when the desktop entry the application thinks it owns is actually different. In Snap this environment variable is passed to the process. When no service was found, try reading it. CHANGELOG: Improved notification identification for Snap applications Differential Revision: https://phabricator.kde.org/D21881 --- libnotificationmanager/jobsmodel_p.cpp | 14 ++++++--- libnotificationmanager/notification.cpp | 31 +++++++++++++++----- libnotificationmanager/notification.h | 1 + libnotificationmanager/notification_p.h | 2 ++ libnotificationmanager/server_p.cpp | 25 +++++++++++++--- libnotificationmanager/utils.cpp | 39 ++++++++++++++++++------- libnotificationmanager/utils_p.h | 5 ++-- 7 files changed, 89 insertions(+), 28 deletions(-) diff --git a/libnotificationmanager/jobsmodel_p.cpp b/libnotificationmanager/jobsmodel_p.cpp index e07069d2e..284a75680 100644 --- a/libnotificationmanager/jobsmodel_p.cpp +++ b/libnotificationmanager/jobsmodel_p.cpp @@ -265,10 +265,16 @@ QDBusObjectPath JobsModelPrivate::requestView(const QString &desktopEntry, const QString serviceName = message().service(); if (job->applicationName().isEmpty()) { qCInfo(NOTIFICATIONMANAGER) << "JobView request from" << serviceName << "didn't contain any identification information, this is an application bug!"; - const QString processName = Utils::processNameFromDBusService(connection(), serviceName); - if (!processName.isEmpty()) { - qCDebug(NOTIFICATIONMANAGER) << "Resolved JobView request to be from" << processName; - job->setApplicationName(processName); + + QDBusReply pidReply = connection().interface()->servicePid(serviceName); + if (pidReply.isValid()) { + const auto pid = pidReply.value(); + + const QString processName = Utils::processNameFromPid(pid); + if (!processName.isEmpty()) { + qCDebug(NOTIFICATIONMANAGER) << "Resolved JobView request to be from" << processName; + job->setApplicationName(processName); + } } } diff --git a/libnotificationmanager/notification.cpp b/libnotificationmanager/notification.cpp index 8847670a5..6c3e57ec7 100644 --- a/libnotificationmanager/notification.cpp +++ b/libnotificationmanager/notification.cpp @@ -271,7 +271,13 @@ QSize Notification::Private::maximumImageSize() KService::Ptr Notification::Private::serviceForDesktopEntry(const QString &desktopEntry) { - KService::Ptr service = KService::serviceByDesktopName(desktopEntry); + KService::Ptr service; + + if (desktopEntry.startsWith(QLatin1Char('/'))) { + service = KService::serviceByDesktopPath(desktopEntry); + } else { + service = KService::serviceByDesktopName(desktopEntry); + } if (!service) { const QString lowerDesktopEntry = desktopEntry.toLower(); @@ -299,25 +305,20 @@ KService::Ptr Notification::Private::serviceForDesktopEntry(const QString &deskt return service; } -void Notification::Private::processHints(const QVariantMap &hints) +void Notification::Private::setDesktopEntry(const QString &desktopEntry) { - auto end = hints.end(); - - desktopEntry = hints.value(QStringLiteral("desktop-entry")).toString(); - QString serviceName; configurableService = false; KService::Ptr service = serviceForDesktopEntry(desktopEntry); if (service) { - desktopEntry = service->desktopEntryName(); + this->desktopEntry = service->desktopEntryName(); serviceName = service->name(); applicationIconName = service->icon(); configurableService = !service->noDisplay(); } - notifyRcName = hints.value(QStringLiteral("x-kde-appname")).toString(); const bool isDefaultEvent = (notifyRcName == defaultComponentName()); configurableNotifyRc = false; if (!notifyRcName.isEmpty()) { @@ -344,6 +345,15 @@ void Notification::Private::processHints(const QVariantMap &hints) const QRegularExpression regexp(QStringLiteral("^Event/([^/]*)$")); configurableNotifyRc = !config.groupList().filter(regexp).isEmpty(); } +} + +void Notification::Private::processHints(const QVariantMap &hints) +{ + auto end = hints.end(); + + notifyRcName = hints.value(QStringLiteral("x-kde-appname")).toString(); + + setDesktopEntry(hints.value(QStringLiteral("desktop-entry")).toString()); // Special override for KDE Connect since the notification is sent by kdeconnectd // but actually comes from a different app on the phone @@ -523,6 +533,11 @@ QString Notification::desktopEntry() const return d->desktopEntry; } +void Notification::setDesktopEntry(const QString &desktopEntry) +{ + d->setDesktopEntry(desktopEntry); +} + QString Notification::notifyRcName() const { return d->notifyRcName; diff --git a/libnotificationmanager/notification.h b/libnotificationmanager/notification.h index c2109e3f4..27c10ed70 100644 --- a/libnotificationmanager/notification.h +++ b/libnotificationmanager/notification.h @@ -71,6 +71,7 @@ public: void setImage(const QImage &image); QString desktopEntry() const; + void setDesktopEntry(const QString &desktopEntry); QString notifyRcName() const; QString eventId() const; diff --git a/libnotificationmanager/notification_p.h b/libnotificationmanager/notification_p.h index 979d357cc..a533aec93 100644 --- a/libnotificationmanager/notification_p.h +++ b/libnotificationmanager/notification_p.h @@ -52,6 +52,7 @@ public: static KService::Ptr serviceForDesktopEntry(const QString &desktopEntry); + void setDesktopEntry(const QString &desktopEntry); void processHints(const QVariantMap &hints); void setUrgency(Notifications::Urgency urgency); @@ -69,6 +70,7 @@ public: QString applicationName; QString desktopEntry; bool configurableService = false; + QString serviceName; // "Name" field in KService from desktopEntry QString applicationIconName; QString originName; diff --git a/libnotificationmanager/server_p.cpp b/libnotificationmanager/server_p.cpp index f9863d6f8..70c3a592d 100644 --- a/libnotificationmanager/server_p.cpp +++ b/libnotificationmanager/server_p.cpp @@ -138,13 +138,30 @@ uint ServerPrivate::Notify(const QString &app_name, uint replaces_id, const QStr notification.setIcon(app_icon); } - // No application name? Try to figure out the process name using the sender's PID - if (notification.applicationName().isEmpty()) { + uint pid = 0; + if (notification.desktopEntry().isEmpty() || notification.applicationName().isEmpty()) { qCInfo(NOTIFICATIONMANAGER) << "Notification from service" << message().service() << "didn't contain any identification information, this is an application bug!"; - const QString processName = Utils::processNameFromDBusService(connection(), message().service()); + QDBusReply pidReply = connection().interface()->servicePid(message().service()); + if (pidReply.isValid()) { + pid = pidReply.value(); + } + } + + // No desktop entry? Try to read the BAMF_DESKTOP_FILE_HINT in the environment of snaps + if (notification.desktopEntry().isEmpty() && pid > 0) { + const QString desktopEntry = Utils::desktopEntryFromPid(pid); + if (!desktopEntry.isEmpty()) { + qCDebug(NOTIFICATIONMANAGER) << "Resolved notification to be from desktop entry" << desktopEntry; + notification.setDesktopEntry(desktopEntry); + } + } + + // No application name? Try to figure out the process name using the sender's PID + if (notification.applicationName().isEmpty() && pid > 0) { + const QString processName = Utils::processNameFromPid(pid); if (!processName.isEmpty()) { - qCDebug(NOTIFICATIONMANAGER) << "Resolved notification to be from" << processName; + qCDebug(NOTIFICATIONMANAGER) << "Resolved notification to be from process name" << processName; notification.setApplicationName(processName); } } diff --git a/libnotificationmanager/utils.cpp b/libnotificationmanager/utils.cpp index 0110f9dd2..e433068c8 100644 --- a/libnotificationmanager/utils.cpp +++ b/libnotificationmanager/utils.cpp @@ -24,25 +24,18 @@ #include #include #include +#include +#include #include -#include - #include #include using namespace NotificationManager; -QString Utils::processNameFromDBusService(const QDBusConnection &connection, const QString &serviceName) +QString Utils::processNameFromPid(uint pid) { - QDBusReply pidReply = connection.interface()->servicePid(serviceName); - if (!pidReply.isValid()) { - return QString(); - } - - const auto pid = pidReply.value(); - KSysGuard::Processes procs; procs.updateOrAddProcess(pid); @@ -55,6 +48,32 @@ QString Utils::processNameFromDBusService(const QDBusConnection &connection, con return proc->name(); } +QString Utils::desktopEntryFromPid(uint pid) +{ + QFile environFile(QStringLiteral("/proc/%1/environ").arg(QString::number(pid))); + if (!environFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + return QString(); + } + + const QByteArray bamfDesktopFileHint = QByteArrayLiteral("BAMF_DESKTOP_FILE_HINT"); + + const auto lines = environFile.readAll().split('\0'); + for (const QByteArray &line : lines) { + const int equalsIdx = line.indexOf('='); + if (equalsIdx <= 0) { + continue; + } + + const QByteArray key = line.left(equalsIdx); + const QByteArray value = line.mid(equalsIdx + 1); + if (key == bamfDesktopFileHint) { + return value; + } + } + + return QString(); +} + QModelIndex Utils::mapToModel(const QModelIndex &idx, const QAbstractItemModel *sourceModel) { // KModelIndexProxyMapper can only map diferent indices to a single source diff --git a/libnotificationmanager/utils_p.h b/libnotificationmanager/utils_p.h index 00f909c86..ff17777d5 100644 --- a/libnotificationmanager/utils_p.h +++ b/libnotificationmanager/utils_p.h @@ -32,8 +32,9 @@ namespace NotificationManager namespace Utils { -QString processNameFromDBusService(const QDBusConnection &connection, - const QString &serviceName); +QString processNameFromPid(uint pid); + +QString desktopEntryFromPid(uint pid); QModelIndex mapToModel(const QModelIndex &idx, const QAbstractItemModel *sourceModel);