Commit 951551bc authored by Kai Uwe Broulik's avatar Kai Uwe Broulik 🍇
Browse files

[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
parent c9f2236c
......@@ -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<uint> 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);
}
}
}
......
......@@ -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;
......
......@@ -71,6 +71,7 @@ public:
void setImage(const QImage &image);
QString desktopEntry() const;
void setDesktopEntry(const QString &desktopEntry);
QString notifyRcName() const;
QString eventId() const;
......
......@@ -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;
......
......@@ -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<uint> 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);
}
}
......
......@@ -24,25 +24,18 @@
#include <QAbstractProxyModel>
#include <QDBusConnection>
#include <QDBusConnectionInterface>
#include <QFile>
#include <QTextStream>
#include <KConcatenateRowsProxyModel>
#include <QDebug>
#include <processcore/processes.h>
#include <processcore/process.h>
using namespace NotificationManager;
QString Utils::processNameFromDBusService(const QDBusConnection &connection, const QString &serviceName)
QString Utils::processNameFromPid(uint pid)
{
QDBusReply<uint> 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
......
......@@ -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);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment