mirror of
https://bitbucket.org/smil3y/kde-workspace.git
synced 2025-02-25 03:12:51 +00:00
543 lines
14 KiB
C++
543 lines
14 KiB
C++
/********************************************************************
|
|
KSld - the KDE Screenlocker Daemon
|
|
This file is part of the KDE project.
|
|
|
|
Copyright (C) 1999 Martin R. Jones <mjones@kde.org>
|
|
Copyright (C) 2002 Luboš Luňák <l.lunak@kde.org>
|
|
Copyright (C) 2003 Oswald Buddenhagen <ossi@kde.org>
|
|
Copyright (C) 2011 Martin Gräßlin <mgraesslin@kde.org>
|
|
|
|
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) any later version.
|
|
|
|
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 <http://www.gnu.org/licenses/>.
|
|
*********************************************************************/
|
|
#include "greeter.h"
|
|
#include "kscreensaversettings.h"
|
|
// workspace
|
|
#include <kcheckpass-enums.h>
|
|
// KDE
|
|
#include <KDebug>
|
|
#include <KLocale>
|
|
#include <KPluginFactory>
|
|
#include <KPluginLoader>
|
|
#include <KStandardDirs>
|
|
|
|
// Qt
|
|
#include <QtCore/QLibrary>
|
|
#include <QtCore/QFile>
|
|
#include <QtCore/QSocketNotifier>
|
|
#include <QtCore/QTimer>
|
|
#include <QtDeclarative/QDeclarativeContext>
|
|
#include <QtDeclarative/QDeclarativeEngine>
|
|
#include <QtGui/QGraphicsProxyWidget>
|
|
#include <QtGui/QLineEdit>
|
|
|
|
// kscreenlocker stuff
|
|
#include <ctype.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <pwd.h>
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/wait.h>
|
|
|
|
#include <QPalette>
|
|
|
|
//Plasma
|
|
#include <Plasma/Theme>
|
|
|
|
|
|
namespace ScreenLocker
|
|
{
|
|
|
|
GreeterItem::GreeterItem(QDeclarativeItem *parent)
|
|
: QDeclarativeItem(parent)
|
|
, m_proxy(new QGraphicsProxyWidget(this))
|
|
, m_unlocker(new Greeter(this))
|
|
{
|
|
init();
|
|
}
|
|
|
|
GreeterItem::~GreeterItem()
|
|
{
|
|
}
|
|
|
|
//#define SHOW_GREETER_BACKGROUND
|
|
void GreeterItem::init()
|
|
{
|
|
if (!m_unlocker->isValid()) {
|
|
exit(1);
|
|
return;
|
|
}
|
|
m_widget = m_unlocker->greeterWidget();
|
|
|
|
themeChanged();
|
|
connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()), this, SLOT(themeChanged()));
|
|
#ifndef SHOW_GREETER_BACKGROUND
|
|
m_widget->setAttribute(Qt::WA_TranslucentBackground);
|
|
#endif
|
|
|
|
m_proxy->setWidget(m_widget);
|
|
m_proxy->setFlag(QGraphicsItem::ItemIsFocusable);
|
|
setFlag(QGraphicsItem::ItemIsFocusable);
|
|
m_proxy->setFocus();
|
|
QGraphicsItem::setFocus();
|
|
connect(m_unlocker, SIGNAL(greeterFailed()), this, SIGNAL(greeterFailed()));
|
|
connect(m_unlocker, SIGNAL(greeterReady()), this, SIGNAL(greeterReady()));
|
|
connect(m_unlocker, SIGNAL(greeterMessage(QString)), this, SIGNAL(greeterMessage(QString)));
|
|
connect(m_unlocker, SIGNAL(greeterAccepted()), this, SIGNAL(greeterAccepted()));
|
|
setWidth(m_proxy->size().width());
|
|
setHeight(m_proxy->size().height());
|
|
}
|
|
|
|
void GreeterItem::themeChanged()
|
|
{
|
|
QPalette p = m_widget->palette();
|
|
p.setColor(QPalette::WindowText,
|
|
Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor));
|
|
m_widget->setPalette(p);
|
|
}
|
|
|
|
void GreeterItem::verify()
|
|
{
|
|
m_unlocker->verify();
|
|
}
|
|
|
|
void GreeterItem::clear()
|
|
{
|
|
m_unlocker->clear();
|
|
}
|
|
|
|
void GreeterItem::focusInEvent(QFocusEvent *event)
|
|
{
|
|
QGraphicsItem::focusInEvent(event);
|
|
if (QLineEdit *lineEdit = m_proxy->widget()->findChild<QLineEdit *>()) {
|
|
lineEdit->setFocus(Qt::OtherFocusReason);
|
|
}
|
|
}
|
|
|
|
// KeyboardItem
|
|
|
|
KeyboardItem::KeyboardItem(QDeclarativeItem *parent)
|
|
: QDeclarativeItem(parent)
|
|
, m_widget(new QWidget())
|
|
, m_proxy(new QGraphicsProxyWidget(this))
|
|
{
|
|
m_widget->setAttribute(Qt::WA_TranslucentBackground);
|
|
KPluginFactory *kxkbFactory = KPluginLoader(QLatin1String("keyboard_layout_widget")).factory();
|
|
if (kxkbFactory) {
|
|
kxkbFactory->create<QWidget>(m_widget);
|
|
} else {
|
|
kDebug() << "can't load keyboard layout widget library";
|
|
}
|
|
|
|
themeChanged();
|
|
connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()), this, SLOT(themeChanged()));
|
|
m_proxy->setWidget(m_widget);
|
|
|
|
setWidth(m_proxy->size().width());
|
|
setHeight(m_proxy->size().height());
|
|
}
|
|
|
|
KeyboardItem::~KeyboardItem()
|
|
{
|
|
}
|
|
|
|
void KeyboardItem::themeChanged()
|
|
{
|
|
QPalette p = m_widget->palette();
|
|
p.setColor(QPalette::WindowText,
|
|
Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor));
|
|
m_widget->setPalette(p);
|
|
}
|
|
|
|
Greeter::Greeter(QObject *parent)
|
|
: QObject(parent)
|
|
, m_greeterWidget(new QWidget())
|
|
, m_greet(0)
|
|
, m_valid(false)
|
|
, m_pid(0)
|
|
, m_fd(0)
|
|
, m_notifier(NULL)
|
|
, m_failedLock(false)
|
|
{
|
|
m_pluginHandle.library = 0;
|
|
initialize();
|
|
m_valid = loadGreetPlugin();
|
|
if (m_valid) {
|
|
|
|
m_greet = m_pluginHandle.info->create(this, m_greeterWidget, QString(),
|
|
KGreeterPlugin::Authenticate,
|
|
KGreeterPlugin::ExUnlock);
|
|
m_greet->start();
|
|
}
|
|
}
|
|
|
|
Greeter::~Greeter()
|
|
{
|
|
if (m_pluginHandle.library) {
|
|
if (m_pluginHandle.info->done) {
|
|
m_pluginHandle.info->done();
|
|
}
|
|
m_pluginHandle.library->unload();
|
|
}
|
|
}
|
|
|
|
void Greeter::initialize()
|
|
{
|
|
KScreenSaverSettings::self()->readConfig();
|
|
m_plugins = KScreenSaverSettings::pluginsUnlock();
|
|
if (m_plugins.isEmpty()) {
|
|
m_plugins << QLatin1String( "classic" ) << QLatin1String( "generic" );
|
|
}
|
|
m_pluginOptions = KScreenSaverSettings::pluginOptions();
|
|
const QStringList dmopt =
|
|
QString::fromLatin1( ::getenv( "XDM_MANAGED" )).split(QLatin1Char(','), QString::SkipEmptyParts);
|
|
for (QStringList::ConstIterator it = dmopt.constBegin(); it != dmopt.constEnd(); ++it) {
|
|
if ((*it).startsWith(QLatin1String( "method=" ))) {
|
|
m_method = (*it).mid(7);
|
|
}
|
|
}
|
|
}
|
|
|
|
// standard greeter stuff
|
|
// private static
|
|
QVariant Greeter::getConf(void *ctx, const char *key, const QVariant &dflt)
|
|
{
|
|
Greeter *that = (Greeter *)ctx;
|
|
QString fkey = QLatin1String( key ) + QLatin1Char( '=' );
|
|
for (QStringList::ConstIterator it = that->m_pluginOptions.constBegin();
|
|
it != that->m_pluginOptions.constEnd(); ++it)
|
|
if ((*it).startsWith( fkey ))
|
|
return (*it).mid( fkey.length() );
|
|
return dflt;
|
|
}
|
|
|
|
bool Greeter::loadGreetPlugin()
|
|
{
|
|
if (m_pluginHandle.library) {
|
|
//we were locked once before, so all the plugin loading's done already
|
|
//FIXME should I be unloading the plugin on unlock instead?
|
|
return true;
|
|
}
|
|
for (QStringList::ConstIterator it = m_plugins.constBegin(); it != m_plugins.constEnd(); ++it) {
|
|
GreeterPluginHandle plugin;
|
|
QLibrary *lib = new QLibrary( (*it)[0] == QLatin1Char( '/' ) ? *it : QLatin1String( "kgreet_" ) + *it );
|
|
if (lib->fileName().isEmpty()) {
|
|
kWarning() << "GreeterPlugin " << *it << " does not exist" ;
|
|
delete lib;
|
|
continue;
|
|
}
|
|
if (!lib->load()) {
|
|
kWarning() << "Cannot load GreeterPlugin " << *it << " (" << lib->fileName() << ")" ;
|
|
delete lib;
|
|
continue;
|
|
}
|
|
plugin.library = lib;
|
|
plugin.info = (KGreeterPluginInfo *)lib->resolve( "kgreeterplugin_info" );
|
|
if (!plugin.info ) {
|
|
kWarning() << "GreeterPlugin " << *it << " (" << lib->fileName() << ") is no valid greet widget plugin" ;
|
|
lib->unload();
|
|
delete lib;
|
|
continue;
|
|
}
|
|
if (plugin.info->method && !m_method.isEmpty() && m_method != QLatin1String( plugin.info->method )) {
|
|
kDebug() << "GreeterPlugin " << *it << " (" << lib->fileName() << ") serves " << plugin.info->method << ", not " << m_method;
|
|
lib->unload();
|
|
delete lib;
|
|
continue;
|
|
}
|
|
if (!plugin.info->init( m_method, getConf, this )) {
|
|
kDebug() << "GreeterPlugin " << *it << " (" << lib->fileName() << ") refuses to serve " << m_method;
|
|
lib->unload();
|
|
delete lib;
|
|
continue;
|
|
}
|
|
kDebug() << "GreeterPlugin " << *it << " (" << plugin.info->method << ", " << plugin.info->name << ") loaded";
|
|
m_pluginHandle = plugin;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Greeter::verify()
|
|
{
|
|
if (m_failedLock) {
|
|
// greeter blocked due to failed unlock attempt
|
|
return;
|
|
}
|
|
m_greet->next();
|
|
}
|
|
|
|
void Greeter::clear()
|
|
{
|
|
m_greet->clear();
|
|
}
|
|
|
|
void Greeter::failedTimer()
|
|
{
|
|
emit greeterReady();
|
|
m_greet->revive();
|
|
m_greet->start();
|
|
m_failedLock = false;
|
|
}
|
|
|
|
////// kckeckpass interface code
|
|
|
|
int Greeter::Reader(void *buf, int count)
|
|
{
|
|
int ret, rlen;
|
|
|
|
for (rlen = 0; rlen < count; ) {
|
|
dord:
|
|
ret = ::read(m_fd, (void *)((char *)buf + rlen), count - rlen);
|
|
if (ret < 0) {
|
|
if (errno == EINTR)
|
|
goto dord;
|
|
if (errno == EAGAIN)
|
|
break;
|
|
return -1;
|
|
}
|
|
if (!ret)
|
|
break;
|
|
rlen += ret;
|
|
}
|
|
return rlen;
|
|
}
|
|
|
|
bool Greeter::GRead(void *buf, int count)
|
|
{
|
|
return Reader(buf, count) == count;
|
|
}
|
|
|
|
bool Greeter::GWrite(const void *buf, int count)
|
|
{
|
|
return ::write(m_fd, buf, count) == count;
|
|
}
|
|
|
|
bool Greeter::GSendInt(int val)
|
|
{
|
|
return GWrite(&val, sizeof(val));
|
|
}
|
|
|
|
bool Greeter::GSendStr(const char *buf)
|
|
{
|
|
int len = buf ? ::strlen (buf) + 1 : 0;
|
|
return GWrite(&len, sizeof(len)) && GWrite (buf, len);
|
|
}
|
|
|
|
bool Greeter::GSendArr(int len, const char *buf)
|
|
{
|
|
return GWrite(&len, sizeof(len)) && GWrite (buf, len);
|
|
}
|
|
|
|
bool Greeter::GRecvInt(int *val)
|
|
{
|
|
return GRead(val, sizeof(*val));
|
|
}
|
|
|
|
bool Greeter::GRecvArr(char **ret)
|
|
{
|
|
int len;
|
|
char *buf;
|
|
|
|
if (!GRecvInt(&len))
|
|
return false;
|
|
if (!len) {
|
|
*ret = 0;
|
|
return true;
|
|
}
|
|
if (!(buf = (char *)::malloc (len)))
|
|
return false;
|
|
*ret = buf;
|
|
if (GRead (buf, len)) {
|
|
return true;
|
|
} else {
|
|
::free(buf);
|
|
*ret = 0;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void Greeter::reapVerify()
|
|
{
|
|
m_notifier->setEnabled( false );
|
|
m_notifier->deleteLater();
|
|
m_notifier = 0;
|
|
::close( m_fd );
|
|
int status;
|
|
while (::waitpid( m_pid, &status, 0 ) < 0)
|
|
if (errno != EINTR) { // This should not happen ...
|
|
cantCheck();
|
|
return;
|
|
}
|
|
if (WIFEXITED(status))
|
|
switch (WEXITSTATUS(status)) {
|
|
case AuthOk:
|
|
m_greet->succeeded();
|
|
emit greeterAccepted();
|
|
return;
|
|
case AuthBad:
|
|
m_greet->failed();
|
|
emit greeterFailed();
|
|
m_failedLock = true;
|
|
QTimer::singleShot(1500, this, SLOT(failedTimer()));
|
|
//KNotification::event( QLatin1String( "unlockfailed" ) );*/
|
|
return;
|
|
case AuthAbort:
|
|
return;
|
|
}
|
|
cantCheck();
|
|
}
|
|
|
|
void Greeter::handleVerify()
|
|
{
|
|
int ret;
|
|
char *arr;
|
|
|
|
if (GRecvInt( &ret )) {
|
|
switch (ret) {
|
|
case ConvGetBinary:
|
|
if (!GRecvArr( &arr ))
|
|
break;
|
|
m_greet->binaryPrompt( arr, false );
|
|
if (arr)
|
|
::free( arr );
|
|
return;
|
|
case ConvGetNormal:
|
|
if (!GRecvArr( &arr ))
|
|
break;
|
|
m_greet->textPrompt( arr, true, false );
|
|
if (arr)
|
|
::free( arr );
|
|
return;
|
|
case ConvGetHidden:
|
|
if (!GRecvArr( &arr ))
|
|
break;
|
|
m_greet->textPrompt( arr, false, false );
|
|
if (arr)
|
|
::free( arr );
|
|
return;
|
|
case ConvPutInfo:
|
|
if (!GRecvArr( &arr ))
|
|
break;
|
|
if (!m_greet->textMessage( arr, false ))
|
|
emit greeterMessage(QString::fromLocal8Bit(arr));
|
|
::free( arr );
|
|
return;
|
|
case ConvPutError:
|
|
if (!GRecvArr( &arr ))
|
|
break;
|
|
if (!m_greet->textMessage( arr, true ))
|
|
emit greeterMessage(QString::fromLocal8Bit(arr));
|
|
::free( arr );
|
|
return;
|
|
}
|
|
}
|
|
reapVerify();
|
|
}
|
|
|
|
////// greeter plugin callbacks
|
|
|
|
void Greeter::gplugReturnText( const char *text, int tag )
|
|
{
|
|
GSendStr( text );
|
|
if (text)
|
|
GSendInt( tag );
|
|
}
|
|
|
|
void Greeter::gplugReturnBinary( const char *data )
|
|
{
|
|
if (data) {
|
|
unsigned const char *up = (unsigned const char *)data;
|
|
int len = up[3] | (up[2] << 8) | (up[1] << 16) | (up[0] << 24);
|
|
if (!len)
|
|
GSendArr( 4, data );
|
|
else
|
|
GSendArr( len, data );
|
|
} else
|
|
GSendArr( 0, 0 );
|
|
}
|
|
|
|
void Greeter::gplugSetUser( const QString & )
|
|
{
|
|
// ignore ...
|
|
}
|
|
|
|
void Greeter::cantCheck()
|
|
{
|
|
m_greet->failed();
|
|
emit greeterMessage(i18n("Cannot unlock the session because the authentication system failed to work!"));
|
|
m_greet->revive();
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Starts the kcheckpass process to check the user's password.
|
|
//
|
|
void Greeter::gplugStart()
|
|
{
|
|
int sfd[2];
|
|
char fdbuf[16];
|
|
|
|
if (m_notifier)
|
|
return;
|
|
if (::socketpair(AF_LOCAL, SOCK_STREAM, 0, sfd)) {
|
|
cantCheck();
|
|
return;
|
|
}
|
|
if ((m_pid = ::fork()) < 0) {
|
|
::close(sfd[0]);
|
|
::close(sfd[1]);
|
|
cantCheck();
|
|
return;
|
|
}
|
|
if (!m_pid) {
|
|
::close(sfd[0]);
|
|
sprintf(fdbuf, "%d", sfd[1]);
|
|
execlp(QFile::encodeName(KStandardDirs::findExe(QLatin1String( "kcheckpass" ))).data(),
|
|
"kcheckpass",
|
|
"-m", m_pluginHandle.info->method,
|
|
"-S", fdbuf,
|
|
(char *)0);
|
|
_exit(20);
|
|
}
|
|
::close(sfd[1]);
|
|
m_fd = sfd[0];
|
|
m_notifier = new QSocketNotifier(m_fd, QSocketNotifier::Read, this);
|
|
connect(m_notifier, SIGNAL(activated(int)), SLOT(handleVerify()));
|
|
}
|
|
|
|
void Greeter::gplugChanged()
|
|
{
|
|
}
|
|
|
|
void Greeter::gplugActivity()
|
|
{
|
|
// ignore
|
|
}
|
|
|
|
void Greeter::gplugMsgBox(QMessageBox::Icon type, const QString &text)
|
|
{
|
|
Q_UNUSED(type)
|
|
emit greeterMessage(text);
|
|
}
|
|
|
|
bool Greeter::gplugHasNode(const QString &)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
} // end namespace
|
|
#include "moc_greeter.cpp"
|