File indexing completed on 2024-05-12 05:35:39

0001 /*
0002     SPDX-FileCopyrightText: 1998 Luca Montecchiani <m.luca@usa.net>
0003     SPDX-FileCopyrightText: 2015 David Edmundson <davidedmundson@kde.org>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "main.h"
0009 
0010 #include <time.h>
0011 #include <unistd.h>
0012 
0013 #include <QVBoxLayout>
0014 
0015 #include <KAboutData>
0016 #include <KMessageBox>
0017 #include <KPluginFactory>
0018 #include <QDBusConnection>
0019 
0020 #include "dtime.h"
0021 #include "helper.h"
0022 
0023 #include <KAuth/Action>
0024 #include <KAuth/ExecuteJob>
0025 
0026 #include "timedated_interface.h"
0027 
0028 K_PLUGIN_CLASS_WITH_JSON(KclockModule, "kcm_clock.json")
0029 
0030 KclockModule::KclockModule(QObject *parent, const KPluginMetaData &metaData)
0031     : KCModule(parent, metaData)
0032 {
0033     auto reply = QDBusConnection::systemBus().call(QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.DBus"),
0034                                                                                   QStringLiteral("/org/freedesktop/DBus"),
0035                                                                                   QStringLiteral("org.freedesktop.DBus"),
0036                                                                                   QStringLiteral("ListActivatableNames")));
0037 
0038     if (!reply.arguments().isEmpty() && reply.arguments().constFirst().value<QStringList>().contains(QLatin1String("org.freedesktop.timedate1"))) {
0039         m_haveTimedated = true;
0040     }
0041 
0042     QVBoxLayout *layout = new QVBoxLayout(widget());
0043     layout->setContentsMargins(0, 0, 0, 0);
0044 
0045     dtime = new Dtime(widget(), m_haveTimedated);
0046     layout->addWidget(dtime);
0047     connect(dtime, &Dtime::timeChanged, this, &KCModule::setNeedsSave);
0048 
0049     setButtons(Help | Apply);
0050 
0051     if (m_haveTimedated) {
0052         setAuthActionName(QStringLiteral("org.freedesktop.timedate1.set-time"));
0053     } else {
0054         // auth action name will be automatically guessed from the KCM name
0055         qWarning() << "Timedated not found, using legacy saving mode";
0056         setAuthActionName(QStringLiteral("org.kde.kcontrol.kcmclock.save"));
0057     }
0058 }
0059 
0060 bool KclockModule::kauthSave()
0061 {
0062     QVariantMap helperargs;
0063     helperargs[QStringLiteral("ntp")] = true;
0064     helperargs[QStringLiteral("ntpServers")] = dtime->ntpServers();
0065     helperargs[QStringLiteral("ntpEnabled")] = dtime->ntpEnabled();
0066 
0067     if (!dtime->ntpEnabled()) {
0068         QDateTime newTime = dtime->userTime();
0069         qDebug() << "Set date to " << dtime->userTime();
0070         helperargs[QStringLiteral("date")] = true;
0071         helperargs[QStringLiteral("newdate")] = QString::number(newTime.currentSecsSinceEpoch());
0072         helperargs[QStringLiteral("olddate")] = QString::number(::time(nullptr));
0073     }
0074 
0075     QString selectedTimeZone = dtime->selectedTimeZone();
0076     if (!selectedTimeZone.isEmpty()) {
0077         helperargs[QStringLiteral("tz")] = true;
0078         helperargs[QStringLiteral("tzone")] = selectedTimeZone;
0079     } else {
0080         helperargs[QStringLiteral("tzreset")] = true; // make the helper reset the timezone
0081     }
0082 
0083     Action action(authActionName());
0084     action.setArguments(helperargs);
0085 
0086     ExecuteJob *job = action.execute();
0087     bool rc = job->exec();
0088     if (!rc) {
0089         KMessageBox::error(widget(), i18n("Unable to authenticate/execute the action: %1, %2", job->error(), job->errorString()));
0090     }
0091     return rc;
0092 }
0093 
0094 bool KclockModule::timedatedSave()
0095 {
0096     OrgFreedesktopTimedate1Interface timedateIface(QStringLiteral("org.freedesktop.timedate1"),
0097                                                    QStringLiteral("/org/freedesktop/timedate1"),
0098                                                    QDBusConnection::systemBus());
0099 
0100     bool rc = true;
0101     // final arg in each method is "user-interaction" i.e whether it's OK for polkit to ask for auth
0102 
0103     // 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
0104     // timedated processes these in parallel and will return an error otherwise
0105 
0106     auto reply = timedateIface.SetNTP(dtime->ntpEnabled(), true);
0107     reply.waitForFinished();
0108     if (reply.isError()) {
0109         KMessageBox::error(widget(), i18n("Unable to change NTP settings"));
0110         qWarning() << "Failed to enable NTP" << reply.error().name() << reply.error().message();
0111         rc = false;
0112     }
0113 
0114     if (!dtime->ntpEnabled()) {
0115         qint64 timeDiff = dtime->userTime().toMSecsSinceEpoch() - QDateTime::currentMSecsSinceEpoch();
0116         //*1000 for milliseconds -> microseconds
0117         auto reply = timedateIface.SetTime(timeDiff * 1000, true, true);
0118         reply.waitForFinished();
0119         if (reply.isError()) {
0120             KMessageBox::error(widget(), i18n("Unable to set current time"));
0121             qWarning() << "Failed to set current time" << reply.error().name() << reply.error().message();
0122             rc = false;
0123         }
0124     }
0125     QString selectedTimeZone = dtime->selectedTimeZone();
0126     if (!selectedTimeZone.isEmpty()) {
0127         auto reply = timedateIface.SetTimezone(selectedTimeZone, true);
0128         reply.waitForFinished();
0129         if (reply.isError()) {
0130             KMessageBox::error(widget(), i18n("Unable to set timezone"));
0131             qWarning() << "Failed to set timezone" << reply.error().name() << reply.error().message();
0132             rc = false;
0133         }
0134     }
0135 
0136     return rc;
0137 }
0138 
0139 void KclockModule::save()
0140 {
0141     widget()->setDisabled(true);
0142 
0143     bool success = false;
0144     if (m_haveTimedated) {
0145         success = timedatedSave();
0146     } else {
0147         success = kauthSave();
0148     }
0149 
0150     if (success) {
0151         QDBusMessage msg = QDBusMessage::createSignal(QStringLiteral("/org/kde/kcmshell_clock"), //
0152                                                       QStringLiteral("org.kde.kcmshell_clock"),
0153                                                       QStringLiteral("clockUpdated"));
0154         QDBusConnection::sessionBus().send(msg);
0155     }
0156 
0157     // NOTE: super nasty hack #1
0158     // Try to work around time mismatch between KSystemTimeZones' update of local
0159     // timezone and reloading of data, so that the new timezone is taken into account.
0160     // The Ultimate solution to this would be if KSTZ emitted a signal when a new
0161     // local timezone was found.
0162 
0163     // setDisabled(false) happens in load(), since QTimer::singleShot is non-blocking
0164     if (!m_haveTimedated) {
0165         QTimer::singleShot(5000, this, &KclockModule::load);
0166     } else {
0167         load();
0168     }
0169 }
0170 
0171 void KclockModule::load()
0172 {
0173     dtime->load();
0174     widget()->setDisabled(false);
0175 }
0176 
0177 #include "main.moc"