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"