diff options
Diffstat (limited to 'kdm/kfrontend/kgverify.cpp')
-rw-r--r-- | kdm/kfrontend/kgverify.cpp | 1211 |
1 files changed, 1211 insertions, 0 deletions
diff --git a/kdm/kfrontend/kgverify.cpp b/kdm/kfrontend/kgverify.cpp new file mode 100644 index 000000000..f9bb77ff8 --- /dev/null +++ b/kdm/kfrontend/kgverify.cpp @@ -0,0 +1,1211 @@ +/* + +Shell for kdm conversation plugins + +Copyright (C) 1997, 1998, 2000 Steffen Hansen <[email protected]> +Copyright (C) 2000-2004 Oswald Buddenhagen <[email protected]> + + +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, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +*/ + +#include <config.h> + +#include "kgverify.h" +#include "kdmconfig.h" +#include "kdm_greet.h" + +#include "themer/kdmthemer.h" +#include "themer/kdmitem.h" + +#include <kapplication.h> +#include <klocale.h> +#include <klibloader.h> +#include <kseparator.h> +#include <kstdguiitem.h> +#include <kpushbutton.h> + +#include <qregexp.h> +#include <qpopupmenu.h> +#include <qlayout.h> +#include <qfile.h> +#include <qlabel.h> + +#include <pwd.h> +#include <sys/types.h> +#include <stdlib.h> +#include <unistd.h> + +#include <X11/Xlib.h> // for updateLockStatus() +#include <fixx11h.h> // ... and make eventFilter() work again + +#define FULL_GREET_TO 40 // normal inactivity timeout +#define TIMED_GREET_TO 20 // inactivity timeout when persisting timed login +#define MIN_TIMED_TO 5 // minimal timed login delay +#define DEAD_TIMED_TO 2 // <enter> dead time after re-activating timed login +#define SECONDS 1000 // reduce to 100 to speed up testing + +void KGVerifyHandler::verifyClear() +{ +} + +void KGVerifyHandler::updateStatus( bool, bool, int ) +{ +} + +KGVerify::KGVerify( KGVerifyHandler *_handler, KdmThemer *_themer, + QWidget *_parent, QWidget *_predecessor, + const QString &_fixedUser, + const PluginList &_pluginList, + KGreeterPlugin::Function _func, + KGreeterPlugin::Context _ctx ) + : inherited() + , coreLock( 0 ) + , fixedEntity( _fixedUser ) + , pluginList( _pluginList ) + , handler( _handler ) + , themer( _themer ) + , parent( _parent ) + , predecessor( _predecessor ) + , plugMenu( 0 ) + , curPlugin( -1 ) + , timedLeft( 0 ) + , func( _func ) + , ctx( _ctx ) + , enabled( true ) + , running( false ) + , suspended( false ) + , failed( false ) + , isClear( true ) +{ + connect( &timer, SIGNAL(timeout()), SLOT(slotTimeout()) ); + connect( kapp, SIGNAL(activity()), SLOT(slotActivity()) ); + + _parent->installEventFilter( this ); +} + +KGVerify::~KGVerify() +{ + Debug( "delete %s\n", pName.data() ); + delete greet; +} + +QPopupMenu * +KGVerify::getPlugMenu() +{ + // assert( !cont ); + if (!plugMenu) { + uint np = pluginList.count(); + if (np > 1) { + plugMenu = new QPopupMenu( parent ); + connect( plugMenu, SIGNAL(activated( int )), + SLOT(slotPluginSelected( int )) ); + for (uint i = 0; i < np; i++) + plugMenu->insertItem( i18n(greetPlugins[pluginList[i]].info->name), pluginList[i] ); + } + } + return plugMenu; +} + +bool // public +KGVerify::entitiesLocal() const +{ + return greetPlugins[pluginList[curPlugin]].info->flags & kgreeterplugin_info::Local; +} + +bool // public +KGVerify::entitiesFielded() const +{ + return greetPlugins[pluginList[curPlugin]].info->flags & kgreeterplugin_info::Fielded; +} + +bool // public +KGVerify::entityPresettable() const +{ + return greetPlugins[pluginList[curPlugin]].info->flags & kgreeterplugin_info::Presettable; +} + +bool // public +KGVerify::isClassic() const +{ + return !strcmp( greetPlugins[pluginList[curPlugin]].info->method, "classic" ); +} + +QString // public +KGVerify::pluginName() const +{ + QString name( greetPlugins[pluginList[curPlugin]].library->fileName() ); + uint st = name.findRev( '/' ) + 1; + uint en = name.find( '.', st ); + if (en - st > 7 && QConstString( name.unicode() + st, 7 ).string() == "kgreet_") + st += 7; + return name.mid( st, en - st ); +} + +static void +showWidgets( QLayoutItem *li ) +{ + QWidget *w; + QLayout *l; + + if ((w = li->widget())) + w->show(); + else if ((l = li->layout())) { + QLayoutIterator it = l->iterator(); + for (QLayoutItem *itm = it.current(); itm; itm = ++it) + showWidgets( itm ); + } +} + +void // public +KGVerify::selectPlugin( int id ) +{ + if (pluginList.isEmpty()) { + MsgBox( errorbox, i18n("No greeter widget plugin loaded. Check the configuration.") ); + ::exit( EX_UNMANAGE_DPY ); + } + curPlugin = id; + if (plugMenu) + plugMenu->setItemChecked( id, true ); + pName = ("greet_" + pluginName()).latin1(); + Debug( "new %s\n", pName.data() ); + greet = greetPlugins[pluginList[id]].info->create( this, themer, parent, predecessor, fixedEntity, func, ctx ); + timeable = _autoLoginDelay && entityPresettable() && isClassic(); +} + +void // public +KGVerify::loadUsers( const QStringList &users ) +{ + Debug( "%s->loadUsers(...)\n", pName.data() ); + greet->loadUsers( users ); +} + +void // public +KGVerify::presetEntity( const QString &entity, int field ) +{ + presEnt = entity; + presFld = field; +} + +bool // private +KGVerify::applyPreset() +{ + if (!presEnt.isEmpty()) { + Debug( "%s->presetEntity(%\"s, %d)\n", pName.data(), + presEnt.latin1(), presFld ); + greet->presetEntity( presEnt, presFld ); + if (entitiesLocal()) { + curUser = presEnt; + handler->verifySetUser( presEnt ); + } + return true; + } + return false; +} + +bool // private +KGVerify::scheduleAutoLogin( bool initial ) +{ + if (timeable) { + Debug( "%s->presetEntity(%\"s, -1)\n", pName.data(), + _autoLoginUser.latin1(), -1 ); + greet->presetEntity( _autoLoginUser, -1 ); + curUser = _autoLoginUser; + handler->verifySetUser( _autoLoginUser ); + timer.start( 1000 ); + if (initial) { + timedLeft = _autoLoginDelay; + deadTicks = 0; + } else { + timedLeft = QMAX( _autoLoginDelay - TIMED_GREET_TO, MIN_TIMED_TO ); + deadTicks = DEAD_TIMED_TO; + } + updateStatus(); + running = false; + isClear = true; + return true; + } + return false; +} + +void // private +KGVerify::performAutoLogin() +{ +// timer.stop(); + GSendInt( G_AutoLogin ); + handleVerify(); +} + +QString // public +KGVerify::getEntity() const +{ + Debug( "%s->getEntity()\n", pName.data() ); + QString ent = greet->getEntity(); + Debug( " entity: %s\n", ent.latin1() ); + return ent; +} + +void +KGVerify::setUser( const QString &user ) +{ + // assert( fixedEntity.isEmpty() ); + curUser = user; + Debug( "%s->setUser(%\"s)\n", pName.data(), user.latin1() ); + greet->setUser( user ); + gplugActivity(); +} + +void +KGVerify::start() +{ + authTok = (func == KGreeterPlugin::ChAuthTok); + cont = false; + if (func == KGreeterPlugin::Authenticate && ctx == KGreeterPlugin::Login) { + if (scheduleAutoLogin( true )) { + if (!_autoLoginAgain) + _autoLoginDelay = 0, timeable = false; + return; + } else + applyPreset(); + } + running = true; + Debug( "%s->start()\n", pName.data() ); + greet->start(); + if (!(func == KGreeterPlugin::Authenticate || + ctx == KGreeterPlugin::ChangeTok || + ctx == KGreeterPlugin::ExChangeTok)) + { + cont = true; + handleVerify(); + } +} + +void +KGVerify::abort() +{ + Debug( "%s->abort()\n", pName.data() ); + greet->abort(); + running = false; +} + +void +KGVerify::suspend() +{ + // assert( !cont ); + if (running) { + Debug( "%s->abort()\n", pName.data() ); + greet->abort(); + } + suspended = true; + updateStatus(); + timer.suspend(); +} + +void +KGVerify::resume() +{ + timer.resume(); + suspended = false; + updateLockStatus(); + if (running) { + Debug( "%s->start()\n", pName.data() ); + greet->start(); + } else if (delayed) { + delayed = false; + running = true; + Debug( "%s->start()\n", pName.data() ); + greet->start(); + } +} + +void // not a slot - called manually by greeter +KGVerify::accept() +{ + Debug( "%s->next()\n", pName.data() ); + greet->next(); +} + +void // private +KGVerify::doReject( bool initial ) +{ + // assert( !cont ); + if (running) { + Debug( "%s->abort()\n", pName.data() ); + greet->abort(); + } + handler->verifyClear(); + Debug( "%s->clear()\n", pName.data() ); + greet->clear(); + curUser = QString::null; + if (!scheduleAutoLogin( initial )) { + isClear = !(isClear && applyPreset()); + if (running) { + Debug( "%s->start()\n", pName.data() ); + greet->start(); + } + if (!failed) + timer.stop(); + } +} + +void // not a slot - called manually by greeter +KGVerify::reject() +{ + doReject( true ); +} + +void +KGVerify::setEnabled( bool on ) +{ + Debug( "%s->setEnabled(%s)\n", pName.data(), on ? "true" : "false" ); + greet->setEnabled( on ); + enabled = on; + updateStatus(); +} + +void // private +KGVerify::slotTimeout() +{ + if (failed) { + failed = false; + updateStatus(); + Debug( "%s->revive()\n", pName.data() ); + greet->revive(); + handler->verifyRetry(); + if (suspended) + delayed = true; + else { + running = true; + Debug( "%s->start()\n", pName.data() ); + greet->start(); + slotActivity(); + gplugActivity(); + if (cont) + handleVerify(); + } + } else if (timedLeft) { + deadTicks--; + if (!--timedLeft) + performAutoLogin(); + else + timer.start( 1000 ); + updateStatus(); + } else { + // assert( ctx == Login ); + isClear = true; + doReject( false ); + } +} + +void +KGVerify::slotActivity() +{ + if (timedLeft) { + Debug( "%s->revive()\n", pName.data() ); + greet->revive(); + Debug( "%s->start()\n", pName.data() ); + greet->start(); + running = true; + timedLeft = 0; + updateStatus(); + timer.start( TIMED_GREET_TO * SECONDS ); + } else if (timeable) + timer.start( TIMED_GREET_TO * SECONDS ); +} + + +void // private static +KGVerify::VMsgBox( QWidget *parent, const QString &user, + QMessageBox::Icon type, const QString &mesg ) +{ + FDialog::box( parent, type, user.isEmpty() ? + mesg : i18n("Authenticating %1...\n\n").arg( user ) + mesg ); +} + +static const char *msgs[]= { + I18N_NOOP( "You are required to change your password immediately (password aged)." ), + I18N_NOOP( "You are required to change your password immediately (root enforced)." ), + I18N_NOOP( "You are not allowed to login at the moment." ), + I18N_NOOP( "Home folder not available." ), + I18N_NOOP( "Logins are not allowed at the moment.\nTry again later." ), + I18N_NOOP( "Your login shell is not listed in /etc/shells." ), + I18N_NOOP( "Root logins are not allowed." ), + I18N_NOOP( "Your account has expired; please contact your system administrator." ) +}; + +void // private static +KGVerify::VErrBox( QWidget *parent, const QString &user, const char *msg ) +{ + QMessageBox::Icon icon; + QString mesg; + + if (!msg) { + mesg = i18n("A critical error occurred.\n" + "Please look at KDM's logfile(s) for more information\n" + "or contact your system administrator."); + icon = errorbox; + } else { + mesg = QString::fromLocal8Bit( msg ); + QString mesg1 = mesg + '.'; + for (uint i = 0; i < as(msgs); i++) + if (mesg1 == msgs[i]) { + mesg = i18n(msgs[i]); + break; + } + icon = sorrybox; + } + VMsgBox( parent, user, icon, mesg ); +} + +void // private static +KGVerify::VInfoBox( QWidget *parent, const QString &user, const char *msg ) +{ + QString mesg = QString::fromLocal8Bit( msg ); + QRegExp rx( "^Warning: your account will expire in (\\d+) day" ); + if (rx.search( mesg ) >= 0) { + int expire = rx.cap( 1 ).toInt(); + mesg = expire ? + i18n("Your account expires tomorrow.", + "Your account expires in %n days.", expire) : + i18n("Your account expires today."); + } else { + rx.setPattern( "^Warning: your password will expire in (\\d+) day" ); + if (rx.search( mesg ) >= 0) { + int expire = rx.cap( 1 ).toInt(); + mesg = expire ? + i18n("Your password expires tomorrow.", + "Your password expires in %n days.", expire) : + i18n("Your password expires today."); + } + } + VMsgBox( parent, user, infobox, mesg ); +} + +bool // public static +KGVerify::handleFailVerify( QWidget *parent ) +{ + Debug( "handleFailVerify ...\n" ); + char *msg = GRecvStr(); + QString user = QString::fromLocal8Bit( msg ); + free( msg ); + + for (;;) { + int ret = GRecvInt(); + + // non-terminal status + switch (ret) { + /* case V_PUT_USER: cannot happen - we are in "classic" mode */ + /* case V_PRE_OK: cannot happen - not in ChTok dialog */ + /* case V_CHTOK: cannot happen - called by non-interactive verify */ + case V_CHTOK_AUTH: + Debug( " V_CHTOK_AUTH\n" ); + { + QStringList pgs( _pluginsLogin ); + pgs += _pluginsShutdown; + QStringList::ConstIterator it; + for (it = pgs.begin(); it != pgs.end(); ++it) + if (*it == "classic" || *it == "modern") { + pgs = *it; + goto gotit; + } else if (*it == "generic") { + pgs = "modern"; + goto gotit; + } + pgs = "classic"; + gotit: + KGChTok chtok( parent, user, init( pgs ), 0, + KGreeterPlugin::AuthChAuthTok, + KGreeterPlugin::Login ); + return chtok.exec(); + } + case V_MSG_ERR: + Debug( " V_MSG_ERR\n" ); + msg = GRecvStr(); + Debug( " message %\"s\n", msg ); + VErrBox( parent, user, msg ); + if (msg) + free( msg ); + continue; + case V_MSG_INFO: + Debug( " V_MSG_INFO\n" ); + msg = GRecvStr(); + Debug( " message %\"s\n", msg ); + VInfoBox( parent, user, msg ); + free( msg ); + continue; + } + + // terminal status + switch (ret) { + case V_OK: + Debug( " V_OK\n" ); + return true; + case V_AUTH: + Debug( " V_AUTH\n" ); + VMsgBox( parent, user, sorrybox, i18n("Authentication failed") ); + return false; + case V_FAIL: + Debug( " V_FAIL\n" ); + return false; + default: + LogPanic( "Unknown V_xxx code %d from core\n", ret ); + } + } +} + +void // private +KGVerify::handleVerify() +{ + QString user; + + Debug( "handleVerify ...\n" ); + for (;;) { + char *msg; + int ret, echo, ndelay; + KGreeterPlugin::Function nfunc; + + ret = GRecvInt(); + + // requests + coreLock = 1; + switch (ret) { + case V_GET_TEXT: + Debug( " V_GET_TEXT\n" ); + msg = GRecvStr(); + Debug( " prompt %\"s\n", msg ); + echo = GRecvInt(); + Debug( " echo = %d\n", echo ); + ndelay = GRecvInt(); + Debug( " ndelay = %d\n%s->textPrompt(...)\n", ndelay, pName.data() ); + greet->textPrompt( msg, echo, ndelay ); + if (msg) + free( msg ); + return; + case V_GET_BINARY: + Debug( " V_GET_BINARY\n" ); + msg = GRecvArr( &ret ); + Debug( " %d bytes prompt\n", ret ); + ndelay = GRecvInt(); + Debug( " ndelay = %d\n%s->binaryPrompt(...)\n", ndelay, pName.data() ); + greet->binaryPrompt( msg, ndelay ); + if (msg) + free( msg ); + return; + } + + // non-terminal status + coreLock = 2; + switch (ret) { + case V_PUT_USER: + Debug( " V_PUT_USER\n" ); + msg = GRecvStr(); + curUser = user = QString::fromLocal8Bit( msg ); + // greet needs this to be able to return something useful from + // getEntity(). but the backend is still unable to tell a domain ... + Debug( " %s->setUser(%\"s)\n", pName.data(), user.latin1() ); + greet->setUser( curUser ); + handler->verifySetUser( curUser ); + if (msg) + free( msg ); + continue; + case V_PRE_OK: // this is only for func == AuthChAuthTok + Debug( " V_PRE_OK\n" ); + // With the "classic" method, the wrong user simply cannot be + // authenticated, even with the generic plugin. Other methods + // could do so, but this applies only to ctx == ChangeTok, which + // is not implemented yet. + authTok = true; + cont = true; + Debug( "%s->succeeded()\n", pName.data() ); + greet->succeeded(); + continue; + case V_CHTOK_AUTH: + Debug( " V_CHTOK_AUTH\n" ); + nfunc = KGreeterPlugin::AuthChAuthTok; + user = curUser; + goto dchtok; + case V_CHTOK: + Debug( " V_CHTOK\n" ); + nfunc = KGreeterPlugin::ChAuthTok; + user = QString::null; + dchtok: + { + timer.stop(); + Debug( "%s->succeeded()\n", pName.data() ); + greet->succeeded(); + KGChTok chtok( parent, user, pluginList, curPlugin, nfunc, KGreeterPlugin::Login ); + if (!chtok.exec()) + goto retry; + handler->verifyOk(); + return; + } + case V_MSG_ERR: + Debug( " V_MSG_ERR\n" ); + msg = GRecvStr(); + Debug( " %s->textMessage(%\"s, true)\n", pName.data(), msg ); + if (!greet->textMessage( msg, true )) { + Debug( " message passed\n" ); + VErrBox( parent, user, msg ); + } else + Debug( " message swallowed\n" ); + if (msg) + free( msg ); + continue; + case V_MSG_INFO: + Debug( " V_MSG_INFO\n" ); + msg = GRecvStr(); + Debug( " %s->textMessage(%\"s, false)\n", pName.data(), msg ); + if (!greet->textMessage( msg, false )) { + Debug( " message passed\n" ); + VInfoBox( parent, user, msg ); + } else + Debug( " message swallowed\n" ); + free( msg ); + continue; + } + + // terminal status + coreLock = 0; + running = false; + timer.stop(); + + if (ret == V_OK) { + Debug( " V_OK\n" ); + if (!fixedEntity.isEmpty()) { + Debug( " %s->getEntity()\n", pName.data() ); + QString ent = greet->getEntity(); + Debug( " entity %\"s\n", ent.latin1() ); + if (ent != fixedEntity) { + Debug( "%s->failed()\n", pName.data() ); + greet->failed(); + MsgBox( sorrybox, + i18n("Authenticated user (%1) does not match requested user (%2).\n") + .arg( ent ).arg( fixedEntity ) ); + goto retry; + } + } + Debug( "%s->succeeded()\n", pName.data() ); + greet->succeeded(); + handler->verifyOk(); + return; + } + + Debug( "%s->failed()\n", pName.data() ); + greet->failed(); + + if (ret == V_AUTH) { + Debug( " V_AUTH\n" ); + failed = true; + updateStatus(); + handler->verifyFailed(); + timer.start( 1500 + kapp->random()/(RAND_MAX/1000) ); + return; + } + if (ret != V_FAIL) + LogPanic( "Unknown V_xxx code %d from core\n", ret ); + Debug( " V_FAIL\n" ); + retry: + Debug( "%s->revive()\n", pName.data() ); + greet->revive(); + running = true; + Debug( "%s->start()\n", pName.data() ); + greet->start(); + if (!cont) + return; + user = QString::null; + } +} + +void +KGVerify::gplugReturnText( const char *text, int tag ) +{ + Debug( "%s: gplugReturnText(%\"s, %d)\n", pName.data(), + tag & V_IS_SECRET ? "<masked>" : text, tag ); + GSendStr( text ); + if (text) { + GSendInt( tag ); + handleVerify(); + } else + coreLock = 0; +} + +void +KGVerify::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); + Debug( "%s: gplugReturnBinary(%d bytes)\n", pName.data(), len ); + GSendArr( len, data ); + handleVerify(); + } else { + Debug( "%s: gplugReturnBinary(NULL)\n", pName.data() ); + GSendArr( 0, 0 ); + coreLock = 0; + } +} + +void +KGVerify::gplugSetUser( const QString &user ) +{ + Debug( "%s: gplugSetUser(%\"s)\n", pName.data(), user.latin1() ); + curUser = user; + handler->verifySetUser( user ); +} + +void +KGVerify::gplugStart() +{ + // XXX handle func != Authenticate + Debug( "%s: gplugStart()\n", pName.data() ); + GSendInt( ctx == KGreeterPlugin::Shutdown ? G_VerifyRootOK : G_Verify ); + GSendStr( greetPlugins[pluginList[curPlugin]].info->method ); + handleVerify(); +} + +void +KGVerify::gplugActivity() +{ + Debug( "%s: gplugActivity()\n", pName.data() ); + if (func == KGreeterPlugin::Authenticate && + ctx == KGreeterPlugin::Login) + { + isClear = false; + if (!timeable) + timer.start( FULL_GREET_TO * SECONDS ); + } +} + +void +KGVerify::gplugMsgBox( QMessageBox::Icon type, const QString &text ) +{ + Debug( "%s: gplugMsgBox(%d, %\"s)\n", pName.data(), type, text.latin1() ); + MsgBox( type, text ); +} + +bool +KGVerify::eventFilter( QObject *o, QEvent *e ) +{ + switch (e->type()) { + case QEvent::KeyPress: + if (timedLeft) { + QKeyEvent *ke = (QKeyEvent *)e; + if (ke->key() == Key_Return || ke->key() == Key_Enter) { + if (deadTicks <= 0) { + timedLeft = 0; + performAutoLogin(); + } + return true; + } + } + /* fall through */ + case QEvent::KeyRelease: + updateLockStatus(); + /* fall through */ + default: + break; + } + return inherited::eventFilter( o, e ); +} + +void +KGVerify::updateLockStatus() +{ + unsigned int lmask; + Window dummy1, dummy2; + int dummy3, dummy4, dummy5, dummy6; + XQueryPointer( qt_xdisplay(), DefaultRootWindow( qt_xdisplay() ), + &dummy1, &dummy2, &dummy3, &dummy4, &dummy5, &dummy6, + &lmask ); + capsLocked = lmask & LockMask; + updateStatus(); +} + +void +KGVerify::MsgBox( QMessageBox::Icon typ, const QString &msg ) +{ + timer.suspend(); + FDialog::box( parent, typ, msg ); + timer.resume(); +} + + +QVariant // public static +KGVerify::getConf( void *, const char *key, const QVariant &dflt ) +{ + if (!qstrcmp( key, "EchoMode" )) + return QVariant( _echoMode ); + else { + QString fkey = QString::fromLatin1( key ) + '='; + for (QStringList::ConstIterator it = _pluginOptions.begin(); + it != _pluginOptions.end(); ++it) + if ((*it).startsWith( fkey )) + return (*it).mid( fkey.length() ); + return dflt; + } +} + +QValueVector<GreeterPluginHandle> KGVerify::greetPlugins; + +PluginList +KGVerify::init( const QStringList &plugins ) +{ + PluginList pluginList; + + for (QStringList::ConstIterator it = plugins.begin(); it != plugins.end(); ++it) { + GreeterPluginHandle plugin; + QString path = KLibLoader::self()->findLibrary( + ((*it)[0] == '/' ? *it : "kgreet_" + *it ).latin1() ); + if (path.isEmpty()) { + LogError( "GreeterPlugin %s does not exist\n", (*it).latin1() ); + continue; + } + uint i, np = greetPlugins.count(); + for (i = 0; i < np; i++) + if (greetPlugins[i].library->fileName() == path) + goto next; + if (!(plugin.library = KLibLoader::self()->library( path.latin1() ))) { + LogError( "Cannot load GreeterPlugin %s (%s)\n", (*it).latin1(), path.latin1() ); + continue; + } + if (!plugin.library->hasSymbol( "kgreeterplugin_info" )) { + LogError( "GreeterPlugin %s (%s) is no valid greet widget plugin\n", + (*it).latin1(), path.latin1() ); + plugin.library->unload(); + continue; + } + plugin.info = (kgreeterplugin_info*)plugin.library->symbol( "kgreeterplugin_info" ); + if (!plugin.info->init( QString::null, getConf, 0 )) { + LogError( "GreeterPlugin %s (%s) refuses to serve\n", + (*it).latin1(), path.latin1() ); + plugin.library->unload(); + continue; + } + Debug( "GreeterPlugin %s (%s) loaded\n", (*it).latin1(), plugin.info->name ); + greetPlugins.append( plugin ); + next: + pluginList.append( i ); + } + return pluginList; +} + +void +KGVerify::done() +{ + for (uint i = 0; i < greetPlugins.count(); i++) { + if (greetPlugins[i].info->done) + greetPlugins[i].info->done(); + greetPlugins[i].library->unload(); + } +} + + +KGStdVerify::KGStdVerify( KGVerifyHandler *_handler, QWidget *_parent, + QWidget *_predecessor, const QString &_fixedUser, + const PluginList &_pluginList, + KGreeterPlugin::Function _func, + KGreeterPlugin::Context _ctx ) + : inherited( _handler, 0, _parent, _predecessor, _fixedUser, + _pluginList, _func, _ctx ) + , failedLabelState( 0 ) +{ + grid = new QGridLayout; + grid->setAlignment( AlignCenter ); + + failedLabel = new QLabel( parent ); + failedLabel->setFont( _failFont ); + grid->addWidget( failedLabel, 1, 0, AlignCenter ); + + updateLockStatus(); +} + +KGStdVerify::~KGStdVerify() +{ + grid->removeItem( greet->getLayoutItem() ); +} + +void // public +KGStdVerify::selectPlugin( int id ) +{ + inherited::selectPlugin( id ); + grid->addItem( greet->getLayoutItem(), 0, 0 ); + showWidgets( greet->getLayoutItem() ); +} + +void // private slot +KGStdVerify::slotPluginSelected( int id ) +{ + if (failed) + return; + if (id != curPlugin) { + plugMenu->setItemChecked( curPlugin, false ); + parent->setUpdatesEnabled( false ); + grid->removeItem( greet->getLayoutItem() ); + Debug( "delete %s\n", pName.data() ); + delete greet; + selectPlugin( id ); + handler->verifyPluginChanged( id ); + if (running) + start(); + parent->setUpdatesEnabled( true ); + } +} + +void +KGStdVerify::updateStatus() +{ + int nfls; + + if (!enabled) + nfls = 1; + else if (failed) + nfls = 2; + else if (timedLeft) + nfls = -timedLeft; + else if (!suspended && capsLocked) + nfls = 3; + else + nfls = 1; + + if (failedLabelState != nfls) { + failedLabelState = nfls; + if (nfls < 0) { + failedLabel->setPaletteForegroundColor( Qt::black ); + failedLabel->setText( i18n( "Automatic login in 1 second...", + "Automatic login in %n seconds...", + timedLeft ) ); + } else { + switch (nfls) { + default: + failedLabel->clear(); + break; + case 3: + failedLabel->setPaletteForegroundColor( Qt::red ); + failedLabel->setText( i18n("Warning: Caps Lock on") ); + break; + case 2: + failedLabel->setPaletteForegroundColor( Qt::black ); + failedLabel->setText( authTok ? + i18n("Change failed") : + fixedEntity.isEmpty() ? + i18n("Login failed") : + i18n("Authentication failed") ); + break; + } + } + } +} + +KGThemedVerify::KGThemedVerify( KGVerifyHandler *_handler, + KdmThemer *_themer, + QWidget *_parent, QWidget *_predecessor, + const QString &_fixedUser, + const PluginList &_pluginList, + KGreeterPlugin::Function _func, + KGreeterPlugin::Context _ctx ) + : inherited( _handler, _themer, _parent, _predecessor, _fixedUser, + _pluginList, _func, _ctx ) +{ + updateLockStatus(); +} + +KGThemedVerify::~KGThemedVerify() +{ +} + +void // public +KGThemedVerify::selectPlugin( int id ) +{ + inherited::selectPlugin( id ); + QLayoutItem *l; + KdmItem *n; + if (themer && (l = greet->getLayoutItem())) { + if (!(n = themer->findNode( "talker" ))) + MsgBox( errorbox, + i18n("Theme not usable with authentication method '%1'.") + .arg( i18n(greetPlugins[pluginList[id]].info->name) ) ); + else { + n->setLayoutItem( l ); + showWidgets( l ); + } + } + if (themer) + themer->updateGeometry( true ); +} + +void // private slot +KGThemedVerify::slotPluginSelected( int id ) +{ + if (failed) + return; + if (id != curPlugin) { + plugMenu->setItemChecked( curPlugin, false ); + Debug( "delete %s\n", pName.data() ); + delete greet; + selectPlugin( id ); + handler->verifyPluginChanged( id ); + if (running) + start(); + } +} + +void +KGThemedVerify::updateStatus() +{ + handler->updateStatus( enabled && failed, + enabled && !suspended && capsLocked, + timedLeft ); +} + + +KGChTok::KGChTok( QWidget *_parent, const QString &user, + const PluginList &pluginList, int curPlugin, + KGreeterPlugin::Function func, + KGreeterPlugin::Context ctx ) + : inherited( _parent ) + , verify( 0 ) +{ + QSizePolicy fp( QSizePolicy::Fixed, QSizePolicy::Fixed ); + okButton = new KPushButton( KStdGuiItem::ok(), this ); + okButton->setSizePolicy( fp ); + okButton->setDefault( true ); + cancelButton = new KPushButton( KStdGuiItem::cancel(), this ); + cancelButton->setSizePolicy( fp ); + + verify = new KGStdVerify( this, this, cancelButton, user, pluginList, func, ctx ); + verify->selectPlugin( curPlugin ); + + QVBoxLayout *box = new QVBoxLayout( this, 10 ); + + box->addWidget( new QLabel( i18n("Changing authentication token"), this ), 0, AlignHCenter ); + + box->addLayout( verify->getLayout() ); + + box->addWidget( new KSeparator( KSeparator::HLine, this ) ); + + QHBoxLayout *hlay = new QHBoxLayout( box ); + hlay->addStretch( 1 ); + hlay->addWidget( okButton ); + hlay->addStretch( 1 ); + hlay->addWidget( cancelButton ); + hlay->addStretch( 1 ); + + connect( okButton, SIGNAL(clicked()), SLOT(accept()) ); + connect( cancelButton, SIGNAL(clicked()), SLOT(reject()) ); + + QTimer::singleShot( 0, verify, SLOT(start()) ); +} + +KGChTok::~KGChTok() +{ + hide(); + delete verify; +} + +void +KGChTok::accept() +{ + verify->accept(); +} + +void +KGChTok::verifyPluginChanged( int ) +{ + // cannot happen +} + +void +KGChTok::verifyOk() +{ + inherited::accept(); +} + +void +KGChTok::verifyFailed() +{ + okButton->setEnabled( false ); + cancelButton->setEnabled( false ); +} + +void +KGChTok::verifyRetry() +{ + okButton->setEnabled( true ); + cancelButton->setEnabled( true ); +} + +void +KGChTok::verifySetUser( const QString & ) +{ + // cannot happen +} + + +////// helper class, nuke when qtimer supports suspend()/resume() + +QXTimer::QXTimer() + : inherited( 0 ) + , left( -1 ) +{ + connect( &timer, SIGNAL(timeout()), SLOT(slotTimeout()) ); +} + +void +QXTimer::start( int msec ) +{ + left = msec; + timer.start( left, true ); + gettimeofday( &stv, 0 ); +} + +void +QXTimer::stop() +{ + timer.stop(); + left = -1; +} + +void +QXTimer::suspend() +{ + if (timer.isActive()) { + timer.stop(); + struct timeval tv; + gettimeofday( &tv, 0 ); + left -= (tv.tv_sec - stv.tv_sec) * 1000 + (tv.tv_usec - stv.tv_usec) / 1000; + if (left < 0) + left = 0; + } +} + +void +QXTimer::resume() +{ + if (left >= 0 && !timer.isActive()) { + timer.start( left, true ); + gettimeofday( &stv, 0 ); + } +} + +void +QXTimer::slotTimeout() +{ + left = 0; + emit timeout(); +} + + +#include "kgverify.moc" |