/******************************************************************** KSld - the KDE Screenlocker Daemon This file is part of the KDE project. Copyright (C) 1999 Martin R. Jones Copyright (C) 2002 Luboš Luňák Copyright (C) 2003 Oswald Buddenhagen Copyright (C) 2011 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) 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 . *********************************************************************/ #include "greeter.h" #include "kscreensaversettings.h" // workspace #include // KDE #include #include #include #include #include #include // Qt #include #include #include #include #include #include #include // kscreenlocker stuff #include #include #include #include #include #include #include #include #include #include //Plasma #include 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()) { 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(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 "greeter.moc"