kde-workspace/ksmserver/screenlocker/greeter/greeter.cpp
2015-02-27 09:28:46 +00:00

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 <KDE/KDebug>
#include <KDE/KLibrary>
#include <KDE/KLocale>
#include <KDE/KPluginFactory>
#include <KDE/KPluginLoader>
#include <KDE/KStandardDirs>
// Qt
#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;
KLibrary *lib = new KLibrary( (*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->resolveSymbol( "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"