* Fixes #3837 * Change objects to use DBusMgr rather than separate adaptors - Update all DBus invokable methods to new parameter order - Change all usage of DBusReturn to simpler DBusResult - Use DBusMgr to handle path and service registration - Remove adaptor/* - Set path in DBusObject - Unregister service when service is destroyed - Restore handling of invalid QVariant in prompt complete signal - Clean up meta type registration - Move dbus related file together - Convert to QSharedPointer as much as possible - Fix mapping of the Delete method - Handle dbus property get all * Add per-client states - Move cipher negotiation to DBusClient - Show list of clients instead of sessions in the settings page - Add settings for confirmation of accessing items - Fix infinite recursion when client disconnected - Use optional explicit DBusClient parameter instead. This makes accessing the client info in an async context explicit, and thus prevent accidental assertions in prompts. * Improve User Interface - Add per-item access confirmation (if enabled) - Remove the "disable for site" button for the access control dialog - Improve the text on the settings page to be more consistent - Fix disconnect buttons in settings page not working - Make the unlock prompt method nonblocking * Fix and cleanup unit tests - Use QTRY_COMPARE when checking signal spies, as dbus signals are threaded - Fixes in meta type registration and type conversion - Remove QStringLiteral in COMPARE macros, making diff output readable - Add testing for remembering auth decision
624 lines
22 KiB
C++
624 lines
22 KiB
C++
/*
|
|
* Copyright (C) 2020 Aetf <aetf@unlimitedcode.works>
|
|
*
|
|
* 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 or (at your option)
|
|
* 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 "DBusMgr.h"
|
|
|
|
#include "fdosecrets/dbus/DBusConstants.h"
|
|
#include "fdosecrets/dbus/DBusTypes.h"
|
|
#include "fdosecrets/objects/Collection.h"
|
|
#include "fdosecrets/objects/Item.h"
|
|
#include "fdosecrets/objects/Prompt.h"
|
|
#include "fdosecrets/objects/Service.h"
|
|
#include "fdosecrets/objects/Session.h"
|
|
|
|
#include "core/Entry.h"
|
|
#include "core/Tools.h"
|
|
|
|
#include <QCoreApplication>
|
|
#include <QFileInfo>
|
|
#include <QThread>
|
|
|
|
namespace FdoSecrets
|
|
{
|
|
static const auto IntrospectionService = R"xml(
|
|
<interface name="org.freedesktop.Secret.Service">
|
|
<property name="Collections" type="ao" access="read"/>
|
|
<signal name="CollectionCreated">
|
|
<arg name="collection" type="o" direction="out"/>
|
|
</signal>
|
|
<signal name="CollectionDeleted">
|
|
<arg name="collection" type="o" direction="out"/>
|
|
</signal>
|
|
<signal name="CollectionChanged">
|
|
<arg name="collection" type="o" direction="out"/>
|
|
</signal>
|
|
<method name="OpenSession">
|
|
<arg type="v" direction="out"/>
|
|
<arg name="algorithm" type="s" direction="in"/>
|
|
<arg name="input" type="v" direction="in"/>
|
|
<arg name="result" type="o" direction="out"/>
|
|
</method>
|
|
<method name="CreateCollection">
|
|
<arg type="o" direction="out"/>
|
|
<arg name="properties" type="a{sv}" direction="in"/>
|
|
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QVariantMap"/>
|
|
<arg name="alias" type="s" direction="in"/>
|
|
<arg name="prompt" type="o" direction="out"/>
|
|
</method>
|
|
<method name="SearchItems">
|
|
<arg type="ao" direction="out"/>
|
|
<arg name="attributes" type="a{ss}" direction="in"/>
|
|
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="StringStringMap"/>
|
|
<arg name="locked" type="ao" direction="out"/>
|
|
</method>
|
|
<method name="Unlock">
|
|
<arg type="ao" direction="out"/>
|
|
<arg name="paths" type="ao" direction="in"/>
|
|
<arg name="prompt" type="o" direction="out"/>
|
|
</method>
|
|
<method name="Lock">
|
|
<arg type="ao" direction="out"/>
|
|
<arg name="paths" type="ao" direction="in"/>
|
|
<arg name="prompt" type="o" direction="out"/>
|
|
</method>
|
|
<method name="GetSecrets">
|
|
<arg type="a{o(oayays)}" direction="out"/>
|
|
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="ObjectPathSecretMap"/>
|
|
<arg name="items" type="ao" direction="in"/>
|
|
<arg name="session" type="o" direction="in"/>
|
|
</method>
|
|
<method name="ReadAlias">
|
|
<arg type="o" direction="out"/>
|
|
<arg name="name" type="s" direction="in"/>
|
|
</method>
|
|
<method name="SetAlias">
|
|
<arg name="name" type="s" direction="in"/>
|
|
<arg name="collection" type="o" direction="in"/>
|
|
</method>
|
|
</interface>
|
|
)xml";
|
|
|
|
static const auto IntrospectionCollection = R"xml(
|
|
<interface name="org.freedesktop.Secret.Collection">
|
|
<property name="Items" type="ao" access="read"/>
|
|
<property name="Label" type="s" access="readwrite"/>
|
|
<property name="Locked" type="b" access="read"/>
|
|
<property name="Created" type="t" access="read"/>
|
|
<property name="Modified" type="t" access="read"/>
|
|
<signal name="ItemCreated">
|
|
<arg name="item" type="o" direction="out"/>
|
|
</signal>
|
|
<signal name="ItemDeleted">
|
|
<arg name="item" type="o" direction="out"/>
|
|
</signal>
|
|
<signal name="ItemChanged">
|
|
<arg name="item" type="o" direction="out"/>
|
|
</signal>
|
|
<method name="Delete">
|
|
<arg type="o" direction="out"/>
|
|
</method>
|
|
<method name="SearchItems">
|
|
<arg type="ao" direction="out"/>
|
|
<arg name="attributes" type="a{ss}" direction="in"/>
|
|
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="StringStringMap"/>
|
|
</method>
|
|
<method name="CreateItem">
|
|
<arg type="o" direction="out"/>
|
|
<arg name="properties" type="a{sv}" direction="in"/>
|
|
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QVariantMap"/>
|
|
<arg name="secret" type="(oayays)" direction="in"/>
|
|
<annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="FdoSecrets::wire::Secret"/>
|
|
<arg name="replace" type="b" direction="in"/>
|
|
<arg name="prompt" type="o" direction="out"/>
|
|
</method>
|
|
</interface>
|
|
)xml";
|
|
|
|
static const auto IntrospectionItem = R"xml(
|
|
<interface name="org.freedesktop.Secret.Item">
|
|
<property name="Locked" type="b" access="read"/>
|
|
<property name="Attributes" type="a{ss}" access="readwrite">
|
|
<annotation name="org.qtproject.QtDBus.QtTypeName" value="StringStringMap"/>
|
|
</property>
|
|
<property name="Label" type="s" access="readwrite"/>
|
|
<property name="Created" type="t" access="read"/>
|
|
<property name="Modified" type="t" access="read"/>
|
|
<method name="Delete">
|
|
<arg type="o" direction="out"/>
|
|
</method>
|
|
<method name="GetSecret">
|
|
<arg type="(oayays)" direction="out"/>
|
|
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="FdoSecrets::wire::Secret"/>
|
|
<arg name="session" type="o" direction="in"/>
|
|
</method>
|
|
<method name="SetSecret">
|
|
<arg name="secret" type="(oayays)" direction="in"/>
|
|
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="FdoSecrets::wire::Secret"/>
|
|
</method>
|
|
</interface>
|
|
)xml";
|
|
|
|
static const auto IntrospectionSession = R"xml(
|
|
<interface name="org.freedesktop.Secret.Session">
|
|
<method name="Close">
|
|
</method>
|
|
</interface>
|
|
)xml";
|
|
|
|
static const auto IntrospectionPrompt = R"xml(
|
|
<interface name="org.freedesktop.Secret.Prompt">
|
|
<signal name="Completed">
|
|
<arg name="dismissed" type="b" direction="out"/>
|
|
<arg name="result" type="v" direction="out"/>
|
|
</signal>
|
|
<method name="Prompt">
|
|
<arg name="windowId" type="s" direction="in"/>
|
|
</method>
|
|
<method name="Dismiss">
|
|
</method>
|
|
</interface>
|
|
)xml";
|
|
|
|
DBusMgr::DBusMgr()
|
|
: m_conn(QDBusConnection::sessionBus())
|
|
{
|
|
// remove client when it disappears on the bus
|
|
m_watcher.setWatchMode(QDBusServiceWatcher::WatchForUnregistration);
|
|
connect(&m_watcher, &QDBusServiceWatcher::serviceUnregistered, this, &DBusMgr::dbusServiceUnregistered);
|
|
m_watcher.setConnection(m_conn);
|
|
}
|
|
|
|
void DBusMgr::populateMethodCache()
|
|
{
|
|
// these are the methods we expose on DBus
|
|
populateMethodCache(Service::staticMetaObject);
|
|
populateMethodCache(Collection::staticMetaObject);
|
|
populateMethodCache(Item::staticMetaObject);
|
|
populateMethodCache(PromptBase::staticMetaObject);
|
|
populateMethodCache(Session::staticMetaObject);
|
|
}
|
|
|
|
DBusMgr::~DBusMgr() = default;
|
|
|
|
void DBusMgr::overrideClient(const DBusClientPtr& fake)
|
|
{
|
|
m_overrideClient = fake;
|
|
}
|
|
|
|
QList<DBusClientPtr> DBusMgr::clients() const
|
|
{
|
|
return m_clients.values();
|
|
}
|
|
|
|
bool DBusMgr::serviceInfo(const QString& addr, ProcessInfo& info) const
|
|
{
|
|
auto pid = m_conn.interface()->servicePid(addr);
|
|
if (!pid.isValid()) {
|
|
return false;
|
|
}
|
|
info.pid = pid.value();
|
|
// The /proc/pid/exe link is more reliable than /proc/pid/cmdline
|
|
// It's still weak and if the application does a prctl(PR_SET_DUMPABLE, 0) this link cannot be accessed.
|
|
QFileInfo proc(QStringLiteral("/proc/%1/exe").arg(pid.value()));
|
|
info.exePath = proc.canonicalFilePath();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool DBusMgr::sendDBusSignal(const QString& path,
|
|
const QString& interface,
|
|
const QString& name,
|
|
const QVariantList& arguments)
|
|
{
|
|
auto msg = QDBusMessage::createSignal(path, interface, name);
|
|
msg.setArguments(arguments);
|
|
return sendDBus(msg);
|
|
}
|
|
|
|
bool DBusMgr::sendDBus(const QDBusMessage& reply)
|
|
{
|
|
bool ok = m_conn.send(reply);
|
|
if (!ok) {
|
|
qDebug() << "Failed to send on DBus:" << reply;
|
|
emit error(tr("Failed to send reply on DBus"));
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
// `this` object is registered at multiple paths:
|
|
// /org/freedesktop/secrets
|
|
// /org/freedesktop/secrets/collection/xxx
|
|
// /org/freedesktop/secrets/collection/xxx/yyy
|
|
// /org/freedesktop/secrets/aliases/xxx
|
|
// /org/freedesktop/secrets/session/xxx
|
|
// /org/freedesktop/secrets/prompt/xxx
|
|
//
|
|
// The path validation is left to Qt, this method only do the minimum
|
|
// required to differentiate the paths.
|
|
DBusMgr::ParsedPath DBusMgr::parsePath(const QString& path)
|
|
{
|
|
Q_ASSERT(path.startsWith('/'));
|
|
Q_ASSERT(path == "/" || !path.endsWith('/'));
|
|
|
|
static const QString DBusPathSecrets = DBUS_PATH_SECRETS;
|
|
|
|
if (!path.startsWith(DBusPathSecrets)) {
|
|
return ParsedPath{};
|
|
}
|
|
auto parts = path.mid(DBusPathSecrets.size()).split('/');
|
|
// the first part is always empty
|
|
if (parts.isEmpty() || parts.first() != "") {
|
|
return ParsedPath{};
|
|
}
|
|
parts.takeFirst();
|
|
|
|
if (parts.isEmpty()) {
|
|
return ParsedPath{PathType::Service};
|
|
} else if (parts.size() == 2) {
|
|
if (parts.at(0) == "collection") {
|
|
return ParsedPath{PathType::Collection, parts.at(1)};
|
|
} else if (parts.at(0) == "aliases") {
|
|
return ParsedPath{PathType::Aliases, parts.at(1)};
|
|
} else if (parts.at(0) == "prompt") {
|
|
return ParsedPath{PathType::Prompt, parts.at(1)};
|
|
} else if (parts.at(0) == "session") {
|
|
return ParsedPath{PathType::Session, parts.at(1)};
|
|
}
|
|
} else if (parts.size() == 3) {
|
|
if (parts.at(0) == "collection") {
|
|
return ParsedPath{PathType::Item, parts.at(2), parts.at(1)};
|
|
}
|
|
}
|
|
return ParsedPath{};
|
|
}
|
|
|
|
QString DBusMgr::introspect(const QString& path) const
|
|
{
|
|
auto parsed = parsePath(path);
|
|
switch (parsed.type) {
|
|
case PathType::Service:
|
|
return IntrospectionService;
|
|
case PathType::Collection:
|
|
case PathType::Aliases:
|
|
return IntrospectionCollection;
|
|
case PathType::Prompt:
|
|
return IntrospectionPrompt;
|
|
case PathType::Session:
|
|
return IntrospectionSession;
|
|
case PathType::Item:
|
|
return IntrospectionItem;
|
|
case PathType::Unknown:
|
|
default:
|
|
return "";
|
|
}
|
|
}
|
|
|
|
bool DBusMgr::serviceOccupied() const
|
|
{
|
|
auto reply = m_conn.interface()->isServiceRegistered(DBUS_SERVICE_SECRET);
|
|
if (!reply.isValid()) {
|
|
return false;
|
|
}
|
|
if (reply.value()) {
|
|
auto pid = m_conn.interface()->servicePid(DBUS_SERVICE_SECRET);
|
|
if (pid.isValid() && pid.value() != qApp->applicationPid()) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
QString DBusMgr::reportExistingService() const
|
|
{
|
|
auto pidStr = tr("Unknown", "Unknown PID");
|
|
auto exeStr = tr("Unknown", "Unknown executable path");
|
|
|
|
ProcessInfo info{};
|
|
if (serviceInfo(DBUS_SERVICE_SECRET, info)) {
|
|
pidStr = QString::number(info.pid);
|
|
if (!info.exePath.isEmpty()) {
|
|
exeStr = info.exePath;
|
|
}
|
|
}
|
|
|
|
auto otherService = tr("<i>PID: %1, Executable: %2</i>", "<i>PID: 1234, Executable: /path/to/exe</i>")
|
|
.arg(pidStr, exeStr.toHtmlEscaped());
|
|
return tr("Another secret service is running (%1).<br/>"
|
|
"Please stop/remove it before re-enabling the Secret Service Integration.")
|
|
.arg(otherService);
|
|
}
|
|
|
|
bool DBusMgr::registerObject(const QString& path, DBusObject* obj, bool primary)
|
|
{
|
|
if (!m_conn.registerVirtualObject(path, this)) {
|
|
qDebug() << "failed to register" << obj << "at" << path;
|
|
return false;
|
|
}
|
|
connect(obj, &DBusObject::destroyed, this, &DBusMgr::unregisterObject);
|
|
m_objects.insert(path, obj);
|
|
if (primary) {
|
|
obj->setObjectPath(path);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool DBusMgr::registerObject(Service* service)
|
|
{
|
|
if (!m_conn.registerService(DBUS_SERVICE_SECRET)) {
|
|
const auto existing = reportExistingService();
|
|
qDebug() << "Failed to register DBus service at " << DBUS_SERVICE_SECRET;
|
|
qDebug() << existing;
|
|
emit error(tr("Failed to register DBus service at %1.<br/>").arg(DBUS_SERVICE_SECRET) + existing);
|
|
return false;
|
|
}
|
|
connect(service, &DBusObject::destroyed, this, [this]() { m_conn.unregisterService(DBUS_SERVICE_SECRET); });
|
|
|
|
if (!registerObject(DBUS_PATH_SECRETS, service)) {
|
|
qDebug() << "Failed to register service on DBus at path" << DBUS_PATH_SECRETS;
|
|
emit error(tr("Failed to register service on DBus at path '%1'").arg(DBUS_PATH_SECRETS));
|
|
return false;
|
|
}
|
|
|
|
connect(service, &Service::collectionCreated, this, &DBusMgr::emitCollectionCreated);
|
|
connect(service, &Service::collectionChanged, this, &DBusMgr::emitCollectionChanged);
|
|
connect(service, &Service::collectionDeleted, this, &DBusMgr::emitCollectionDeleted);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool DBusMgr::registerObject(Collection* coll)
|
|
{
|
|
auto name = encodePath(coll->name());
|
|
auto path = DBUS_PATH_TEMPLATE_COLLECTION.arg(DBUS_PATH_SECRETS, name);
|
|
if (!registerObject(path, coll)) {
|
|
// try again with a suffix
|
|
name.append(QString("_%1").arg(Tools::uuidToHex(QUuid::createUuid()).left(4)));
|
|
path = DBUS_PATH_TEMPLATE_COLLECTION.arg(DBUS_PATH_SECRETS, name);
|
|
|
|
if (!registerObject(path, coll)) {
|
|
qDebug() << "Failed to register database on DBus under name" << name;
|
|
emit error(tr("Failed to register database on DBus under the name '%1'").arg(name));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
connect(coll, &Collection::itemCreated, this, &DBusMgr::emitItemCreated);
|
|
connect(coll, &Collection::itemChanged, this, &DBusMgr::emitItemChanged);
|
|
connect(coll, &Collection::itemDeleted, this, &DBusMgr::emitItemDeleted);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool DBusMgr::registerObject(Session* sess)
|
|
{
|
|
auto path = DBUS_PATH_TEMPLATE_SESSION.arg(DBUS_PATH_SECRETS, sess->id());
|
|
if (!registerObject(path, sess)) {
|
|
emit error(tr("Failed to register session on DBus at path '%1'").arg(path));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool DBusMgr::registerObject(Item* item)
|
|
{
|
|
auto path = DBUS_PATH_TEMPLATE_ITEM.arg(item->collection()->objectPath().path(), item->backend()->uuidToHex());
|
|
if (!registerObject(path, item)) {
|
|
emit error(tr("Failed to register item on DBus at path '%1'").arg(path));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool DBusMgr::registerObject(PromptBase* prompt)
|
|
{
|
|
auto path = DBUS_PATH_TEMPLATE_PROMPT.arg(DBUS_PATH_SECRETS, Tools::uuidToHex(QUuid::createUuid()));
|
|
if (!registerObject(path, prompt)) {
|
|
emit error(tr("Failed to register prompt object on DBus at path '%1'").arg(path));
|
|
return false;
|
|
}
|
|
|
|
connect(prompt, &PromptBase::completed, this, &DBusMgr::emitPromptCompleted);
|
|
|
|
return true;
|
|
}
|
|
|
|
void DBusMgr::unregisterObject(DBusObject* obj)
|
|
{
|
|
auto count = m_objects.remove(obj->objectPath().path());
|
|
if (count > 0) {
|
|
m_conn.unregisterObject(obj->objectPath().path());
|
|
obj->setObjectPath("/");
|
|
}
|
|
}
|
|
|
|
bool DBusMgr::registerAlias(Collection* coll, const QString& alias)
|
|
{
|
|
auto path = DBUS_PATH_TEMPLATE_ALIAS.arg(DBUS_PATH_SECRETS, alias);
|
|
if (!registerObject(path, coll, false)) {
|
|
qDebug() << "Failed to register database on DBus under alias" << alias;
|
|
// usually this is reported back directly on dbus, so no need to show in UI
|
|
return false;
|
|
}
|
|
// alias signals are handled together with collections' primary path in emitCollection*
|
|
// but we need to handle object destroy here
|
|
connect(coll, &DBusObject::destroyed, this, [this, alias]() { unregisterAlias(alias); });
|
|
return true;
|
|
}
|
|
|
|
void DBusMgr::unregisterAlias(const QString& alias)
|
|
{
|
|
auto path = DBUS_PATH_TEMPLATE_ALIAS.arg(DBUS_PATH_SECRETS, alias);
|
|
// DBusMgr::unregisterObject only handles primary path
|
|
m_objects.remove(path);
|
|
m_conn.unregisterObject(path);
|
|
}
|
|
|
|
void DBusMgr::emitCollectionCreated(Collection* coll)
|
|
{
|
|
QVariantList args;
|
|
args += QVariant::fromValue(coll->objectPath());
|
|
sendDBusSignal(DBUS_PATH_SECRETS, DBUS_INTERFACE_SECRET_SERVICE, QStringLiteral("CollectionCreated"), args);
|
|
}
|
|
|
|
void DBusMgr::emitCollectionChanged(Collection* coll)
|
|
{
|
|
QVariantList args;
|
|
args += QVariant::fromValue(coll->objectPath());
|
|
sendDBusSignal(DBUS_PATH_SECRETS, DBUS_INTERFACE_SECRET_SERVICE, "CollectionChanged", args);
|
|
}
|
|
|
|
void DBusMgr::emitCollectionDeleted(Collection* coll)
|
|
{
|
|
QVariantList args;
|
|
args += QVariant::fromValue(coll->objectPath());
|
|
sendDBusSignal(DBUS_PATH_SECRETS, DBUS_INTERFACE_SECRET_SERVICE, QStringLiteral("CollectionDeleted"), args);
|
|
}
|
|
|
|
void DBusMgr::emitItemCreated(Item* item)
|
|
{
|
|
auto coll = item->collection();
|
|
QVariantList args;
|
|
args += QVariant::fromValue(item->objectPath());
|
|
// send on primary path
|
|
sendDBusSignal(
|
|
coll->objectPath().path(), DBUS_INTERFACE_SECRET_COLLECTION, QStringLiteral("ItemCreated"), args);
|
|
// also send on all alias path
|
|
for (const auto& alias : coll->aliases()) {
|
|
auto path = DBUS_PATH_TEMPLATE_ALIAS.arg(DBUS_PATH_SECRETS, alias);
|
|
sendDBusSignal(path, DBUS_INTERFACE_SECRET_COLLECTION, QStringLiteral("ItemCreated"), args);
|
|
}
|
|
}
|
|
|
|
void DBusMgr::emitItemChanged(Item* item)
|
|
{
|
|
auto coll = item->collection();
|
|
QVariantList args;
|
|
args += QVariant::fromValue(item->objectPath());
|
|
// send on primary path
|
|
sendDBusSignal(
|
|
coll->objectPath().path(), DBUS_INTERFACE_SECRET_COLLECTION, QStringLiteral("ItemChanged"), args);
|
|
// also send on all alias path
|
|
for (const auto& alias : coll->aliases()) {
|
|
auto path = DBUS_PATH_TEMPLATE_ALIAS.arg(DBUS_PATH_SECRETS, alias);
|
|
sendDBusSignal(path, DBUS_INTERFACE_SECRET_COLLECTION, QStringLiteral("ItemChanged"), args);
|
|
}
|
|
}
|
|
|
|
void DBusMgr::emitItemDeleted(Item* item)
|
|
{
|
|
auto coll = item->collection();
|
|
QVariantList args;
|
|
args += QVariant::fromValue(item->objectPath());
|
|
// send on primary path
|
|
sendDBusSignal(
|
|
coll->objectPath().path(), DBUS_INTERFACE_SECRET_COLLECTION, QStringLiteral("ItemDeleted"), args);
|
|
// also send on all alias path
|
|
for (const auto& alias : coll->aliases()) {
|
|
auto path = DBUS_PATH_TEMPLATE_ALIAS.arg(DBUS_PATH_SECRETS, alias);
|
|
sendDBusSignal(path, DBUS_INTERFACE_SECRET_COLLECTION, QStringLiteral("ItemDeleted"), args);
|
|
}
|
|
}
|
|
|
|
void DBusMgr::emitPromptCompleted(bool dismissed, QVariant result)
|
|
{
|
|
auto prompt = qobject_cast<PromptBase*>(sender());
|
|
if (!prompt) {
|
|
qDebug() << "Wrong sender in emitPromptCompleted";
|
|
return;
|
|
}
|
|
|
|
// make sure the result contains a valid value, otherwise QDBusVariant refuses to marshall it.
|
|
if (!result.isValid()) {
|
|
result = QString{};
|
|
}
|
|
|
|
QVariantList args;
|
|
args += QVariant::fromValue(dismissed);
|
|
args += QVariant::fromValue(QDBusVariant(result));
|
|
sendDBusSignal(prompt->objectPath().path(), DBUS_INTERFACE_SECRET_PROMPT, QStringLiteral("Completed"), args);
|
|
}
|
|
|
|
DBusClientPtr DBusMgr::findClient(const QString& addr)
|
|
{
|
|
if (m_overrideClient) {
|
|
return m_overrideClient;
|
|
}
|
|
|
|
auto it = m_clients.find(addr);
|
|
if (it == m_clients.end()) {
|
|
auto client = createClient(addr);
|
|
if (!client) {
|
|
return {};
|
|
}
|
|
it = m_clients.insert(addr, client);
|
|
}
|
|
// double check the client
|
|
ProcessInfo info{};
|
|
if (!serviceInfo(addr, info) || info.pid != it.value()->pid()) {
|
|
dbusServiceUnregistered(addr);
|
|
return {};
|
|
}
|
|
return it.value();
|
|
}
|
|
|
|
DBusClientPtr DBusMgr::createClient(const QString& addr)
|
|
{
|
|
ProcessInfo info{};
|
|
if (!serviceInfo(addr, info)) {
|
|
return {};
|
|
}
|
|
|
|
auto client = DBusClientPtr(new DBusClient(this, addr, info.pid, info.exePath.isEmpty() ? addr : info.exePath));
|
|
|
|
emit clientConnected(client);
|
|
m_watcher.addWatchedService(addr);
|
|
|
|
return client;
|
|
}
|
|
|
|
void DBusMgr::removeClient(DBusClient* client)
|
|
{
|
|
if (!client) {
|
|
return;
|
|
}
|
|
|
|
auto it = m_clients.find(client->address());
|
|
if (it == m_clients.end()) {
|
|
return;
|
|
}
|
|
|
|
emit clientDisconnected(*it);
|
|
m_clients.erase(it);
|
|
}
|
|
|
|
void DBusMgr::dbusServiceUnregistered(const QString& service)
|
|
{
|
|
auto removed = m_watcher.removeWatchedService(service);
|
|
if (!removed) {
|
|
qDebug("FdoSecrets: Failed to remove service watcher");
|
|
}
|
|
|
|
auto it = m_clients.find(service);
|
|
if (it == m_clients.end()) {
|
|
return;
|
|
}
|
|
auto client = it.value();
|
|
|
|
client->disconnectDBus();
|
|
}
|
|
} // namespace FdoSecrets
|