diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,6 +73,7 @@ add_subdirectory(kcheckpass) add_subdirectory(greeter) add_subdirectory(kcm) +add_subdirectory(auth-helper) add_definitions(-DTRANSLATION_DOMAIN=\"kscreenlocker\") diff --git a/auth-helper/CMakeLists.txt b/auth-helper/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/auth-helper/CMakeLists.txt @@ -0,0 +1,10 @@ +kauth_install_actions(org.kde.kcontrol.kscreenlocker kscreenlocker.actions) + +set(helper_SRCS kscreenlockerauthhelper.cpp) +kconfig_add_kcfg_files(helper_SRCS ../kcfg/kscreensaversettings.kcfgc) + +add_executable(kscreenlocker_authhelper ${helper_SRCS}) +target_link_libraries(kscreenlocker_authhelper Qt5::DBus KF5::Auth KF5::ConfigCore KF5::ConfigGui) + +kauth_install_helper_files(kscreenlocker_authhelper org.kde.kcontrol.kscreenlocker root) +install(TARGETS kscreenlocker_authhelper DESTINATION ${KAUTH_HELPER_INSTALL_DIR}) diff --git a/auth-helper/kscreenlocker.actions b/auth-helper/kscreenlocker.actions new file mode 100644 --- /dev/null +++ b/auth-helper/kscreenlocker.actions @@ -0,0 +1,7 @@ +[Domain] +Name=KDE Plasma Lock Screen + +[org.kde.kcontrol.kscreenlocker.save] +Name=Save Settings +Description=Save Lock Screen Settings +Policy=auth_self diff --git a/auth-helper/kscreenlockerauthhelper.h b/auth-helper/kscreenlockerauthhelper.h new file mode 100644 --- /dev/null +++ b/auth-helper/kscreenlockerauthhelper.h @@ -0,0 +1,35 @@ +/* +Copyright 2016 Martin Gräßlin + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of +the License or (at your option) version 3 or any later version +accepted by the membership of KDE e.V. (or its successor approved +by the membership of KDE e.V.), which shall act as a proxy +defined in Section 14 of version 3 of the license. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#ifndef KSCREENLOCKERAUTHHELPER_H +#define KSCREENLOCKERAUTHHELPER_H + +#include +using namespace KAuth; + +class KScreenLockerAuthHelper: public QObject +{ + Q_OBJECT +public Q_SLOTS: + ActionReply save(const QVariantMap &args); + +private: +}; + +#endif diff --git a/auth-helper/kscreenlockerauthhelper.cpp b/auth-helper/kscreenlockerauthhelper.cpp new file mode 100644 --- /dev/null +++ b/auth-helper/kscreenlockerauthhelper.cpp @@ -0,0 +1,169 @@ +/* +Copyright 2016 Martin Gräßlin + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of +the License or (at your option) version 3 or any later version +accepted by the membership of KDE e.V. (or its successor approved +by the membership of KDE e.V.), which shall act as a proxy +defined in Section 14 of version 3 of the license. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#include "kscreenlockerauthhelper.h" +#include "kscreensaversettings.h" + +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +static const int s_callingUserMissing = 1; +static const int s_pathMissing = 2; +static const int s_invalidConfigDirectory = 3; +static const int s_invalidOwner = 4; +static const int s_invalidConfigFile = 5; +static const int s_failedToOpen = 6; +static const int s_failedToGetFlags = 7; +static const int s_failedToSetFlags = 8; + +ActionReply KScreenLockerAuthHelper::save(const QVariantMap &args) +{ + auto user = KAuth::HelperSupport::callingUser(); + if (user.isNull()) { + ActionReply reply(ActionReply::HelperErrorReply()); + reply.setType(ActionReply::HelperErrorType); + reply.setErrorCode(static_cast(s_callingUserMissing)); + return reply; + } + + auto it = args.constFind(QStringLiteral("path")); + if (it == args.constEnd()) { + ActionReply reply(ActionReply::HelperErrorReply()); + reply.setType(ActionReply::HelperErrorType); + reply.setErrorCode(static_cast(s_pathMissing)); + return reply; + } + + QFileInfo pathInfo(it.value().toString()); + if (!pathInfo.isDir() || pathInfo.isSymLink() || !pathInfo.isAbsolute()) { + ActionReply reply(ActionReply::HelperErrorReply()); + reply.setType(ActionReply::HelperErrorType); + reply.setErrorCode(static_cast(s_invalidConfigDirectory)); + return reply; + } + if (pathInfo.ownerId() != user->userId().nativeId()) { + ActionReply reply(ActionReply::HelperErrorReply()); + reply.setType(ActionReply::HelperErrorType); + reply.setErrorCode(static_cast(s_invalidOwner)); + return reply; + } + // the directory is fine, let's verify the config file + long currentFlags = 0; + QFile configFile(it.value().toString() + QStringLiteral("/kscreenlockerrc")); + QFileInfo configInfo(configFile); + if (configInfo.exists()) { + if (configInfo.isSymLink()) { + ActionReply reply(ActionReply::HelperErrorReply()); + reply.setType(ActionReply::HelperErrorType); + reply.setErrorCode(static_cast(s_invalidConfigFile)); + return reply; + } + if (configInfo.ownerId() != user->userId().nativeId()) { + ActionReply reply(ActionReply::HelperErrorReply()); + reply.setType(ActionReply::HelperErrorType); + reply.setErrorCode(static_cast(s_invalidOwner)); + return reply; + } + // open the file read-only, remove immutable flag + if (!configFile.open(QIODevice::ReadOnly)) { + ActionReply reply(ActionReply::HelperErrorReply()); + reply.setType(ActionReply::HelperErrorType); + reply.setErrorCode(static_cast(s_failedToOpen)); + return reply; + } + if (ioctl(configFile.handle(), FS_IOC_GETFLAGS, ¤tFlags) < 0) { + ActionReply reply(ActionReply::HelperErrorReply()); + reply.setType(ActionReply::HelperErrorType); + reply.setErrorCode(static_cast(s_failedToGetFlags)); + return reply; + } + if (currentFlags & FS_IMMUTABLE_FL) { + // config file is immutable, let's remove it + const long flagsNonImmutable = currentFlags & ~FS_IMMUTABLE_FL; + if (ioctl(configFile.handle(), FS_IOC_SETFLAGS, &flagsNonImmutable) < 0) { + ActionReply reply(ActionReply::HelperErrorReply()); + reply.setType(ActionReply::HelperErrorType); + reply.setErrorCode(static_cast(s_failedToSetFlags)); + return reply; + } + } + } else { + // create the file + if (!configFile.open(QIODevice::WriteOnly)) { + ActionReply reply(ActionReply::HelperErrorReply()); + reply.setType(ActionReply::HelperErrorType); + reply.setErrorCode(static_cast(s_failedToOpen)); + return reply; + } + // change owner to user + fchown(configFile.handle(), user->userId().nativeId(), user->groupId().nativeId()); + } + + // we are ready to write + auto config = KSharedConfig::openConfig(configInfo.absoluteFilePath(), KConfig::SimpleConfig); + KScreenSaverSettings::self()->setSharedConfig(config); + KScreenSaverSettings::self()->load(); + + // write the values + for (auto it = args.constBegin(); it != args.constEnd(); it++) { + if (it.key() == QLatin1String("path")) { + continue; + } else if (it.key() == QLatin1String("Autolock")) { + KScreenSaverSettings::self()->setAutolock(it.value().toBool()); + } else if (it.key() == QLatin1String("Timeout")) { + KScreenSaverSettings::self()->setTimeout(it.value().toInt()); + } else if (it.key() == QLatin1String("Lock")) { + KScreenSaverSettings::self()->setLock(it.value().toBool()); + } else if (it.key() == QLatin1String("LockGrace")) { + KScreenSaverSettings::self()->setLockGrace(it.value().toInt()); + } else if (it.key() == QLatin1String("LockOnResume")) { + KScreenSaverSettings::self()->setLockOnResume(it.value().toBool()); + } else if (it.key() == QLatin1String("Theme")) { + KScreenSaverSettings::self()->setTheme(it.value().toString()); + } else if (it.key() == QLatin1String("ThemeBackground")) { + KScreenSaverSettings::self()->setThemeBackground(it.value().toString()); + } + } + + KScreenSaverSettings::self()->save(); + config->sync(); + + // set immutable + currentFlags = currentFlags | FS_IMMUTABLE_FL; + if (ioctl(configFile.handle(), FS_IOC_SETFLAGS, ¤tFlags) < 0) { + ActionReply reply(ActionReply::HelperErrorReply()); + reply.setType(ActionReply::HelperErrorType); + reply.setErrorCode(static_cast(s_failedToSetFlags)); + return reply; + } + + return ActionReply::SuccessReply(); +} + +KAUTH_HELPER_MAIN("org.kde.kcontrol.kscreenlocker", KScreenLockerAuthHelper); diff --git a/kcm/kcm.cpp b/kcm/kcm.cpp --- a/kcm/kcm.cpp +++ b/kcm/kcm.cpp @@ -22,6 +22,8 @@ #include "ui_kcm.h" #include "screenlocker_interface.h" #include +#include +#include #include #include #include @@ -60,6 +62,10 @@ , m_actionCollection(new KActionCollection(this, QStringLiteral("ksmserver"))) , m_ui(new ScreenLockerKcmForm(this)) { + KAboutData* aboutData = new KAboutData("kscreenlocker", i18n("Lock Screen configuration"), "1.0"); + setAboutData(aboutData); + setNeedsAuthorization(true); + qmlRegisterType(); KConfigDialogManager::changedMap()->insert(QStringLiteral("SelectImageButton"), SIGNAL(imagePathChanged(QString))); @@ -205,15 +211,43 @@ KCModule::save(); KScreenSaverSettings::setTheme(m_selectedPlugin); - - KScreenSaverSettings::self()->save(); if (m_ui->lockscreenShortcut->property("changed").toBool()) { if (QAction *a = m_actionCollection->action(s_lockActionName)) { KGlobalAccel::self()->setShortcut(a, QList{m_ui->lockscreenShortcut->keySequence()}, KGlobalAccel::NoAutoloading); m_actionCollection->writeSettings(); } m_ui->lockscreenShortcut->setProperty("changed", false); } + + KAuth::Action saveAction = authAction(); + + auto config = KScreenSaverSettings::self()->sharedConfig(); + + QVariantMap args({ + {QStringLiteral("path"), QVariant(QStandardPaths::writableLocation(config->locationType()))}, + {QStringLiteral("Autolock"), QVariant(KScreenSaverSettings::self()->autolock())}, + {QStringLiteral("Timeout"), QVariant(KScreenSaverSettings::self()->timeout())}, + {QStringLiteral("Lock"), QVariant(KScreenSaverSettings::self()->lock())}, + {QStringLiteral("LockGrace"), QVariant(KScreenSaverSettings::self()->lockGrace())}, + {QStringLiteral("LockOnResume"), QVariant(KScreenSaverSettings::self()->lockOnResume())}, + {QStringLiteral("Theme"), QVariant(KScreenSaverSettings::self()->theme())}, + {QStringLiteral("ThemeBackground"), QVariant(KScreenSaverSettings::self()->themeBackground())} + }); + saveAction.setHelperId("org.kde.kcontrol.kscreenlocker"); + saveAction.setArguments(args); + + auto job = saveAction.execute(); + job->exec(); + + if (job->error()){ + qDebug() << "Save Failed"; + qDebug() << job->errorString(); + qDebug() << job->errorText(); + } else { + changed(false); + qDebug() << "Option saved"; + } + // reconfigure through DBus OrgKdeScreensaverInterface interface(QStringLiteral("org.kde.screensaver"), QStringLiteral("/ScreenSaver"),