mirror of
https://invent.kde.org/plasma/plasma-desktop.git
synced 2026-11-03 22:59:45 -09:00
Authorization for set-time implies set-ntp and set-timezone, if acquired first then the user will only see one polkit dialog. However ntp needs to be disabled first before the time can be changed. Unfortunately due to deficiencies inside KAuth with the Polkit backend it's not possible to authorize an arbitrary action, so Polkit-qt is used for this. The code "executes" still a KAuth action to make use of the side channel for parent window and activation token to our agent, but since the action doesn't have a helper will actually not execute anything.
134 lines
4.8 KiB
C++
134 lines
4.8 KiB
C++
/*
|
|
SPDX-FileCopyrightText: 1998 Luca Montecchiani <m.luca@usa.net>
|
|
SPDX-FileCopyrightText: 2015 David Edmundson <davidedmundson@kde.org>
|
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
#include "main.h"
|
|
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include <QVBoxLayout>
|
|
|
|
#include <KAboutData>
|
|
#include <KAuth/Action>
|
|
#include <KAuth/ExecuteJob>
|
|
#include <KMessageBox>
|
|
#include <KPluginFactory>
|
|
#include <PolkitQt1/Authority>
|
|
#include <PolkitQt1/Subject>
|
|
#include <QDBusConnection>
|
|
|
|
#include "dtime.h"
|
|
|
|
#include "timedated_interface.h"
|
|
|
|
K_PLUGIN_CLASS_WITH_JSON(KclockModule, "kcm_clock.json")
|
|
|
|
KclockModule::KclockModule(QObject *parent, const KPluginMetaData &metaData)
|
|
: KCModule(parent, metaData)
|
|
{
|
|
QVBoxLayout *layout = new QVBoxLayout(widget());
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
|
|
dtime = new Dtime(widget());
|
|
layout->addWidget(dtime);
|
|
connect(dtime, &Dtime::timeChanged, this, &KCModule::setNeedsSave);
|
|
connect(dtime, &Dtime::selectedTimeZoneChanged, this, &KCModule::setNeedsSave);
|
|
|
|
setButtons(Help | Apply);
|
|
|
|
setAuthActionName(QStringLiteral("org.freedesktop.timedate1.set-time"));
|
|
}
|
|
|
|
bool KclockModule::timedatedSave()
|
|
{
|
|
const qint64 timeDiff = dtime->userTime().toMSecsSinceEpoch() - QDateTime::currentMSecsSinceEpoch();
|
|
|
|
OrgFreedesktopTimedate1Interface timedateIface(QStringLiteral("org.freedesktop.timedate1"),
|
|
QStringLiteral("/org/freedesktop/timedate1"),
|
|
QDBusConnection::systemBus());
|
|
|
|
// Auth manually for "set-time", this implies authroization for all the other actions => the user only sees one popup
|
|
// HACK First We use KAuth here just so it sidechannels parent window and activation token for us, everything else is noop because no helper
|
|
KAuth::Action action(authActionName());
|
|
action.setParentWindow(widget()->window()->windowHandle());
|
|
auto job = action.execute();
|
|
job->exec();
|
|
// Second do the actual authorization manually
|
|
auto authority = PolkitQt1::Authority::instance();
|
|
auto authorized = authority->checkAuthorizationSync(authActionName(),
|
|
PolkitQt1::SystemBusNameSubject(QDBusConnection::systemBus().baseService()),
|
|
PolkitQt1::Authority::AllowUserInteraction);
|
|
if (authorized != PolkitQt1::Authority::Yes) {
|
|
return false;
|
|
}
|
|
|
|
// final arg in each method is "user-interaction" i.e whether it's OK for polkit to ask for auth
|
|
|
|
// we cannot send requests up front then block for all replies as we need NTP to be disabled before we can make a call to SetTime
|
|
// timedated processes these in parallel and will return an error otherwise
|
|
|
|
auto reply = timedateIface.SetNTP(dtime->ntpEnabled(), true);
|
|
reply.waitForFinished();
|
|
if (reply.isError()) {
|
|
if (reply.error().name() != QDBusError::errorString(QDBusError::AccessDenied)) {
|
|
KMessageBox::error(widget(), i18n("Unable to change NTP settings"));
|
|
}
|
|
qWarning() << "Failed to enable NTP" << reply.error().name() << reply.error().message();
|
|
return false;
|
|
}
|
|
if (!dtime->ntpEnabled()) {
|
|
//*1000 for milliseconds -> microseconds
|
|
auto reply = timedateIface.SetTime(timeDiff * 1000, true, true);
|
|
reply.waitForFinished();
|
|
if (reply.isError()) {
|
|
if (reply.error().name() != QDBusError::errorString(QDBusError::AccessDenied)) {
|
|
KMessageBox::error(widget(), i18n("Unable to set current time"));
|
|
}
|
|
qWarning() << "Failed to set current time" << reply.error().name() << reply.error().message();
|
|
return false;
|
|
}
|
|
}
|
|
QString selectedTimeZone = dtime->selectedTimeZone();
|
|
if (!selectedTimeZone.isEmpty()) {
|
|
auto reply = timedateIface.SetTimezone(selectedTimeZone, true);
|
|
reply.waitForFinished();
|
|
if (reply.isError()) {
|
|
if (reply.error().name() != QDBusError::errorString(QDBusError::AccessDenied)) {
|
|
KMessageBox::error(widget(), i18n("Unable to set timezone"));
|
|
}
|
|
qWarning() << "Failed to set timezone" << reply.error().name() << reply.error().message();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void KclockModule::save()
|
|
{
|
|
widget()->setDisabled(true);
|
|
|
|
if (timedatedSave()) {
|
|
QDBusMessage msg = QDBusMessage::createSignal(QStringLiteral("/org/kde/kcmshell_clock"), //
|
|
QStringLiteral("org.kde.kcmshell_clock"),
|
|
QStringLiteral("clockUpdated"));
|
|
QDBusConnection::sessionBus().send(msg);
|
|
}
|
|
|
|
load();
|
|
}
|
|
|
|
void KclockModule::load()
|
|
{
|
|
dtime->load();
|
|
|
|
widget()->setDisabled(false);
|
|
}
|
|
|
|
#include "main.moc"
|
|
|
|
#include "moc_main.cpp"
|