Files
quickshell/src/services/polkit/agentimpl.cpp
T

183 lines
5.5 KiB
C++

#include "agentimpl.hpp"
#include <algorithm>
#include <utility>
#include <qlist.h>
#include <qlogging.h>
#include <qloggingcategory.h>
#include <qobject.h>
#include <qproperty.h>
#include <qtmetamacros.h>
#include "../../core/generation.hpp"
#include "../../core/logcat.hpp"
#include "gobjectref.hpp"
#include "listener.hpp"
#include "qml.hpp"
namespace {
QS_LOGGING_CATEGORY(logPolkit, "quickshell.service.polkit", QtWarningMsg);
}
namespace qs::service::polkit {
PolkitAgentImpl* PolkitAgentImpl::instance = nullptr;
PolkitAgentImpl::PolkitAgentImpl(PolkitAgent* agent)
: QObject(nullptr)
, listener(qs_polkit_agent_new(this), G_OBJECT_NO_REF)
, qmlAgent(agent)
, path(this->qmlAgent->path()) {
auto utf8Path = this->path.toUtf8();
qs_polkit_agent_register(this->listener.get(), utf8Path.constData());
}
PolkitAgentImpl::~PolkitAgentImpl() { this->cancelAllRequests("PolkitAgent is being destroyed"); }
void PolkitAgentImpl::cancelAllRequests(const QString& reason) {
for (; !this->queuedRequests.empty(); this->queuedRequests.pop_back()) {
AuthRequest* req = this->queuedRequests.back();
qCDebug(logPolkit) << "destroying queued authentication request for action" << req->actionId;
req->cancel(reason);
delete req;
}
auto* flow = this->bActiveFlow.value();
if (flow) {
QObject::disconnect(flow, nullptr, this, nullptr);
this->bActiveFlow = nullptr;
flow->cancelAuthenticationRequest();
flow->deleteLater();
}
if (this->bIsRegistered.value()) qs_polkit_agent_unregister(this->listener.get());
}
PolkitAgentImpl* PolkitAgentImpl::tryGetOrCreate(PolkitAgent* agent) {
if (instance == nullptr) instance = new PolkitAgentImpl(agent);
if (instance->qmlAgent == agent) return instance;
return nullptr;
}
PolkitAgentImpl* PolkitAgentImpl::tryGet(const PolkitAgent* agent) {
if (instance == nullptr) return nullptr;
if (instance->qmlAgent == agent) return instance;
return nullptr;
}
PolkitAgentImpl* PolkitAgentImpl::tryTakeoverOrCreate(PolkitAgent* agent) {
if (auto* impl = tryGetOrCreate(agent); impl != nullptr) return impl;
auto* prevGen = EngineGeneration::findObjectGeneration(instance->qmlAgent);
auto* myGen = EngineGeneration::findObjectGeneration(agent);
if (prevGen == myGen) return nullptr;
qCDebug(logPolkit) << "taking over listener from previous generation";
instance->qmlAgent = agent;
instance->setPath(agent->path());
return instance;
}
void PolkitAgentImpl::onEndOfQmlAgent(PolkitAgent* agent) {
if (instance != nullptr && instance->qmlAgent == agent) {
delete instance;
instance = nullptr;
}
}
void PolkitAgentImpl::setPath(const QString& path) {
if (this->path == path) return;
this->path = path;
auto utf8Path = path.toUtf8();
this->cancelAllRequests("PolkitAgent path changed");
qs_polkit_agent_unregister(this->listener.get());
this->bIsRegistered = false;
qs_polkit_agent_register(this->listener.get(), utf8Path.constData());
}
void PolkitAgentImpl::registerComplete(bool success) {
if (success) this->bIsRegistered = true;
else qCWarning(logPolkit) << "failed to register listener on path" << this->qmlAgent->path();
}
void PolkitAgentImpl::initiateAuthentication(AuthRequest* request) {
qCDebug(logPolkit) << "incoming authentication request for action" << request->actionId;
this->queuedRequests.emplace_back(request);
if (this->queuedRequests.size() == 1) {
this->activateAuthenticationRequest();
}
}
void PolkitAgentImpl::cancelAuthentication(AuthRequest* request) {
qCDebug(logPolkit) << "cancelling authentication request from agent";
auto* flow = this->bActiveFlow.value();
if (flow && flow->authRequest() == request) {
flow->cancelFromAgent();
} else if (auto it = std::ranges::find(this->queuedRequests, request);
it != this->queuedRequests.end())
{
qCDebug(logPolkit) << "removing queued authentication request for action" << (*it)->actionId;
(*it)->cancel("Authentication request was cancelled");
delete (*it);
this->queuedRequests.erase(it);
} else {
qCWarning(logPolkit) << "the cancelled request was not found in the queue.";
}
}
void PolkitAgentImpl::activateAuthenticationRequest() {
if (this->queuedRequests.empty()) return;
AuthRequest* req = this->queuedRequests.front();
this->queuedRequests.pop_front();
qCDebug(logPolkit) << "activating authentication request for action" << req->actionId
<< ", cookie: " << req->cookie;
QList<Identity*> identities;
for (auto& identity: req->identities) {
auto* obj = Identity::fromPolkitIdentity(identity);
if (obj) identities.append(obj);
}
if (identities.isEmpty()) {
qCWarning(
logPolkit
) << "no supported identities available for authentication request, cancelling.";
req->cancel("Error requesting authentication: no supported identities available.");
delete req;
return;
}
this->bActiveFlow = new AuthFlow(req, std::move(identities));
QObject::connect(
this->bActiveFlow.value(),
&AuthFlow::isCompletedChanged,
this,
&PolkitAgentImpl::finishAuthenticationRequest
);
emit this->qmlAgent->authenticationRequestStarted();
}
void PolkitAgentImpl::finishAuthenticationRequest() {
if (!this->bActiveFlow.value()) return;
qCDebug(logPolkit) << "finishing authentication request for action"
<< this->bActiveFlow.value()->actionId();
QObject::disconnect(this->bActiveFlow.value(), nullptr, this, nullptr);
this->bActiveFlow.value()->deleteLater();
this->bActiveFlow = nullptr;
if (!this->queuedRequests.empty()) {
this->activateAuthenticationRequest();
}
}
} // namespace qs::service::polkit