File indexing completed on 2024-05-12 17:07:10

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_version.h>
0024 #if KAUTH_VERSION >= QT_VERSION_CHECK(5, 92, 0)
0025 #include <KAuth/Action>
0026 #include <KAuth/ExecuteJob>
0027 #else
0028 #include <KAuthAction>
0029 #include <KAuthExecuteJob>
0030 #endif
0031 
0032 #include "timedated_interface.h"
0033 
0034 K_PLUGIN_CLASS_WITH_JSON(KclockModule, "kcm_clock.json")
0035 
0036 KclockModule::KclockModule(QWidget *parent, const QVariantList &)
0037     : KCModule(parent)
0038 {
0039     auto reply = QDBusConnection::systemBus().call(QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.DBus"),
0040                                                                                   QStringLiteral("/org/freedesktop/DBus"),
0041                                                                                   QStringLiteral("org.freedesktop.DBus"),
0042                                                                                   QStringLiteral("ListActivatableNames")));
0043 
0044     if (!reply.arguments().isEmpty() && reply.arguments().constFirst().value<QStringList>().contains(QLatin1String("org.freedesktop.timedate1"))) {
0045         m_haveTimedated = true;
0046     }
0047 
0048     KAboutData *about = new KAboutData(QStringLiteral("kcmclock"),
0049                                        i18n("KDE Clock Control Module"),
0050                                        QStringLiteral("1.0"),
0051                                        QString(),
0052                                        KAboutLicense::GPL,
0053                                        i18n("(c) 1996 - 2001 Luca Montecchiani"));
0054 
0055     about->addAuthor(i18n("Luca Montecchiani"), i18n("Original author"), QStringLiteral("m.luca@usa.net"));
0056     about->addAuthor(i18n("Paul Campbell"), i18n("Current Maintainer"), QStringLiteral("paul@taniwha.com"));
0057     about->addAuthor(i18n("Benjamin Meyer"), i18n("Added NTP support"), QStringLiteral("ben+kcmclock@meyerhome.net"));
0058     setAboutData(about);
0059     setQuickHelp(
0060         i18n("<h1>Date & Time</h1> This control module can be used to set the system date and"
0061              " time. As these settings do not only affect you as a user, but rather the whole system, you"
0062              " can only change these settings when you start the System Settings as root. If you do not have"
0063              " the root password, but feel the system time should be corrected, please contact your system"
0064              " administrator."));
0065 
0066     QVBoxLayout *layout = new QVBoxLayout(this);
0067     layout->setContentsMargins(0, 0, 0, 0);
0068 
0069     dtime = new Dtime(this, m_haveTimedated);
0070     layout->addWidget(dtime);
0071     connect(dtime, SIGNAL(timeChanged(bool)), this, SIGNAL(changed(bool)));
0072 
0073     setButtons(Help | Apply);
0074 
0075     if (m_haveTimedated) {
0076         setAuthAction(KAuth::Action(QStringLiteral("org.freedesktop.timedate1.set-time")));
0077     } else {
0078         // auth action name will be automatically guessed from the KCM name
0079         qWarning() << "Timedated not found, using legacy saving mode";
0080         setNeedsAuthorization(true);
0081     }
0082 }
0083 
0084 bool KclockModule::kauthSave()
0085 {
0086     QVariantMap helperargs;
0087     helperargs[QStringLiteral("ntp")] = true;
0088     helperargs[QStringLiteral("ntpServers")] = dtime->ntpServers();
0089     helperargs[QStringLiteral("ntpEnabled")] = dtime->ntpEnabled();
0090 
0091     if (!dtime->ntpEnabled()) {
0092         QDateTime newTime = dtime->userTime();
0093         qDebug() << "Set date to " << dtime->userTime();
0094         helperargs[QStringLiteral("date")] = true;
0095         helperargs[QStringLiteral("newdate")] = QString::number(newTime.toTime_t());
0096         helperargs[QStringLiteral("olddate")] = QString::number(::time(nullptr));
0097     }
0098 
0099     QString selectedTimeZone = dtime->selectedTimeZone();
0100     if (!selectedTimeZone.isEmpty()) {
0101         helperargs[QStringLiteral("tz")] = true;
0102         helperargs[QStringLiteral("tzone")] = selectedTimeZone;
0103     } else {
0104         helperargs[QStringLiteral("tzreset")] = true; // make the helper reset the timezone
0105     }
0106 
0107     Action action = authAction();
0108     action.setArguments(helperargs);
0109 
0110     ExecuteJob *job = action.execute();
0111     bool rc = job->exec();
0112     if (!rc) {
0113         KMessageBox::error(this, i18n("Unable to authenticate/execute the action: %1, %2", job->error(), job->errorString()));
0114     }
0115     return rc;
0116 }
0117 
0118 bool KclockModule::timedatedSave()
0119 {
0120     OrgFreedesktopTimedate1Interface timedateIface(QStringLiteral("org.freedesktop.timedate1"),
0121                                                    QStringLiteral("/org/freedesktop/timedate1"),
0122                                                    QDBusConnection::systemBus());
0123 
0124     bool rc = true;
0125     // final arg in each method is "user-interaction" i.e whether it's OK for polkit to ask for auth
0126 
0127     // 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
0128     // timedated processes these in parallel and will return an error otherwise
0129 
0130     auto reply = timedateIface.SetNTP(dtime->ntpEnabled(), true);
0131     reply.waitForFinished();
0132     if (reply.isError()) {
0133         KMessageBox::error(this, i18n("Unable to change NTP settings"));
0134         qWarning() << "Failed to enable NTP" << reply.error().name() << reply.error().message();
0135         rc = false;
0136     }
0137 
0138     if (!dtime->ntpEnabled()) {
0139         qint64 timeDiff = dtime->userTime().toMSecsSinceEpoch() - QDateTime::currentMSecsSinceEpoch();
0140         //*1000 for milliseconds -> microseconds
0141         auto reply = timedateIface.SetTime(timeDiff * 1000, true, true);
0142         reply.waitForFinished();
0143         if (reply.isError()) {
0144             KMessageBox::error(this, i18n("Unable to set current time"));
0145             qWarning() << "Failed to set current time" << reply.error().name() << reply.error().message();
0146             rc = false;
0147         }
0148     }
0149     QString selectedTimeZone = dtime->selectedTimeZone();
0150     if (!selectedTimeZone.isEmpty()) {
0151         auto reply = timedateIface.SetTimezone(selectedTimeZone, true);
0152         reply.waitForFinished();
0153         if (reply.isError()) {
0154             KMessageBox::error(this, i18n("Unable to set timezone"));
0155             qWarning() << "Failed to set timezone" << reply.error().name() << reply.error().message();
0156             rc = false;
0157         }
0158     }
0159 
0160     return rc;
0161 }
0162 
0163 void KclockModule::save()
0164 {
0165     setDisabled(true);
0166 
0167     bool success = false;
0168     if (m_haveTimedated) {
0169         success = timedatedSave();
0170     } else {
0171         success = kauthSave();
0172     }
0173 
0174     if (success) {
0175         QDBusMessage msg = QDBusMessage::createSignal(QStringLiteral("/org/kde/kcmshell_clock"), //
0176                                                       QStringLiteral("org.kde.kcmshell_clock"),
0177                                                       QStringLiteral("clockUpdated"));
0178         QDBusConnection::sessionBus().send(msg);
0179     }
0180 
0181     // NOTE: super nasty hack #1
0182     // Try to work around time mismatch between KSystemTimeZones' update of local
0183     // timezone and reloading of data, so that the new timezone is taken into account.
0184     // The Ultimate solution to this would be if KSTZ emitted a signal when a new
0185     // local timezone was found.
0186 
0187     // setDisabled(false) happens in load(), since QTimer::singleShot is non-blocking
0188     if (!m_haveTimedated) {
0189         QTimer::singleShot(5000, this, SLOT(load()));
0190     } else {
0191         load();
0192     }
0193 }
0194 
0195 void KclockModule::load()
0196 {
0197     dtime->load();
0198     setDisabled(false);
0199 }
0200 
0201 #include "main.moc"