diff options
Diffstat (limited to 'kdmlib')
-rw-r--r-- | kdmlib/Makefile.am | 26 | ||||
-rw-r--r-- | kdmlib/dmctl.cpp | 442 | ||||
-rw-r--r-- | kdmlib/dmctl.h | 93 | ||||
-rw-r--r-- | kdmlib/kgreet_classic.cpp | 505 | ||||
-rw-r--r-- | kdmlib/kgreet_classic.h | 87 | ||||
-rw-r--r-- | kdmlib/kgreet_winbind.cpp | 671 | ||||
-rw-r--r-- | kdmlib/kgreet_winbind.h | 100 | ||||
-rw-r--r-- | kdmlib/kgreeterplugin.h | 401 |
8 files changed, 2325 insertions, 0 deletions
diff --git a/kdmlib/Makefile.am b/kdmlib/Makefile.am new file mode 100644 index 000000000..91218a8c3 --- /dev/null +++ b/kdmlib/Makefile.am @@ -0,0 +1,26 @@ +AM_CPPFLAGS = -I$(top_srcdir)/kdm/kfrontend $(all_includes) + +kde_module_LTLIBRARIES = kgreet_classic.la kgreet_winbind.la + +kgreet_classic_la_SOURCES = kgreet_classic.cpp +kgreet_classic_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries) +kgreet_classic_la_LIBADD = $(LIB_KDEUI) + +kgreet_winbind_la_SOURCES = kgreet_winbind.cpp +kgreet_winbind_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries) +kgreet_winbind_la_LIBADD = $(LIB_KDEUI) + +noinst_LTLIBRARIES = libdmctl.la +libdmctl_la_SOURCES = dmctl.cpp +libdmctl_la_LDFLAGS = $(all_libraries) -no-undefined +libdmctl_la_LIBADD = $(LIB_KDECORE) -lXau + +METASOURCES = AUTO + +noinst_HEADERS = dmctl.h kgreet_classic.h kgreet_winbind.h +include_HEADERS = kgreeterplugin.h + +messages: + $(XGETTEXT) $(kgreet_classic_la_SOURCES) -o $(podir)/kgreet_classic.pot + $(XGETTEXT) $(kgreet_winbind_la_SOURCES) -o $(podir)/kgreet_winbind.pot + $(XGETTEXT) $(libdmctl_la_SOURCES) -o $(podir)/libdmctl.pot diff --git a/kdmlib/dmctl.cpp b/kdmlib/dmctl.cpp new file mode 100644 index 000000000..4475ae1e7 --- /dev/null +++ b/kdmlib/dmctl.cpp @@ -0,0 +1,442 @@ +/* + Copyright (C) 2004 Oswald Buddenhagen <[email protected]> + + This program is free software; you can redistribute it and/or + modify it under the terms of the Lesser 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 Lesser GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "dmctl.h" + +#ifdef Q_WS_X11 + +#include <klocale.h> +#include <dcopclient.h> + +#include <qregexp.h> + +#include <X11/Xauth.h> +#include <X11/Xlib.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include <errno.h> + +static enum { Dunno, NoDM, NewKDM, OldKDM, GDM } DMType = Dunno; +static const char *ctl, *dpy; + +DM::DM() : fd( -1 ) +{ + const char *ptr; + struct sockaddr_un sa; + + if (DMType == Dunno) { + if (!(dpy = ::getenv( "DISPLAY" ))) + DMType = NoDM; + else if ((ctl = ::getenv( "DM_CONTROL" ))) + DMType = NewKDM; + else if ((ctl = ::getenv( "XDM_MANAGED" )) && ctl[0] == '/') + DMType = OldKDM; + else if (::getenv( "GDMSESSION" )) + DMType = GDM; + else + DMType = NoDM; + } + switch (DMType) { + default: + return; + case NewKDM: + case GDM: + if ((fd = ::socket( PF_UNIX, SOCK_STREAM, 0 )) < 0) + return; + sa.sun_family = AF_UNIX; + if (DMType == GDM) { + strcpy( sa.sun_path, "/var/run/gdm_socket" ); + if (::connect( fd, (struct sockaddr *)&sa, sizeof(sa) )) { + strcpy( sa.sun_path, "/tmp/.gdm_socket" ); + if (::connect( fd, (struct sockaddr *)&sa, sizeof(sa) )) { + ::close( fd ); + fd = -1; + break; + } + } + GDMAuthenticate(); + } else { + if ((ptr = strchr( dpy, ':' ))) + ptr = strchr( ptr, '.' ); + snprintf( sa.sun_path, sizeof(sa.sun_path), + "%s/dmctl-%.*s/socket", + ctl, ptr ? int(ptr - dpy) : 512, dpy ); + if (::connect( fd, (struct sockaddr *)&sa, sizeof(sa) )) { + ::close( fd ); + fd = -1; + } + } + break; + case OldKDM: + { + QString tf( ctl ); + tf.truncate( tf.find( ',' ) ); + fd = ::open( tf.latin1(), O_WRONLY ); + } + break; + } +} + +DM::~DM() +{ + if (fd >= 0) + close( fd ); +} + +bool +DM::exec( const char *cmd ) +{ + QCString buf; + + return exec( cmd, buf ); +} + +/** + * Execute a KDM/GDM remote control command. + * @param cmd the command to execute. FIXME: undocumented yet. + * @param buf the result buffer. + * @return result: + * @li If true, the command was successfully executed. + * @p ret might contain addional results. + * @li If false and @p ret is empty, a communication error occurred + * (most probably KDM is not running). + * @li If false and @p ret is non-empty, it contains the error message + * from KDM. + */ +bool +DM::exec( const char *cmd, QCString &buf ) +{ + bool ret = false; + int tl; + unsigned len = 0; + + if (fd < 0) + goto busted; + + tl = strlen( cmd ); + if (::write( fd, cmd, tl ) != tl) { + bust: + ::close( fd ); + fd = -1; + busted: + buf.resize( 0 ); + return false; + } + if (DMType == OldKDM) { + buf.resize( 0 ); + return true; + } + for (;;) { + if (buf.size() < 128) + buf.resize( 128 ); + else if (buf.size() < len * 2) + buf.resize( len * 2 ); + if ((tl = ::read( fd, buf.data() + len, buf.size() - len)) <= 0) { + if (tl < 0 && errno == EINTR) + continue; + goto bust; + } + len += tl; + if (buf[len - 1] == '\n') { + buf[len - 1] = 0; + if (len > 2 && (buf[0] == 'o' || buf[0] == 'O') && + (buf[1] == 'k' || buf[1] == 'K') && buf[2] <= 32) + ret = true; + break; + } + } + return ret; +} + +bool +DM::canShutdown() +{ + if (DMType == OldKDM) + return strstr( ctl, ",maysd" ) != 0; + + QCString re; + + if (DMType == GDM) + return exec( "QUERY_LOGOUT_ACTION\n", re ) && re.find("HALT") >= 0; + + return exec( "caps\n", re ) && re.find( "\tshutdown" ) >= 0; +} + +void +DM::shutdown( KApplication::ShutdownType shutdownType, + KApplication::ShutdownMode shutdownMode, /* NOT Default */ + const QString &bootOption ) +{ + if (shutdownType == KApplication::ShutdownTypeNone) + return; + + bool cap_ask; + if (DMType == NewKDM) { + QCString re; + cap_ask = exec( "caps\n", re ) && re.find( "\tshutdown ask" ) >= 0; + } else { + if (!bootOption.isEmpty()) + return; + cap_ask = false; + } + if (!cap_ask && shutdownMode == KApplication::ShutdownModeInteractive) + shutdownMode = KApplication::ShutdownModeForceNow; + + QCString cmd; + if (DMType == GDM) { + cmd.append( shutdownMode == KApplication::ShutdownModeForceNow ? + "SET_LOGOUT_ACTION " : "SET_SAFE_LOGOUT_ACTION " ); + cmd.append( shutdownType == KApplication::ShutdownTypeReboot ? + "REBOOT\n" : "HALT\n" ); + } else { + cmd.append( "shutdown\t" ); + cmd.append( shutdownType == KApplication::ShutdownTypeReboot ? + "reboot\t" : "halt\t" ); + if (!bootOption.isEmpty()) + cmd.append( "=" ).append( bootOption.local8Bit() ).append( "\t" ); + cmd.append( shutdownMode == KApplication::ShutdownModeInteractive ? + "ask\n" : + shutdownMode == KApplication::ShutdownModeForceNow ? + "forcenow\n" : + shutdownMode == KApplication::ShutdownModeTryNow ? + "trynow\n" : "schedule\n" ); + } + exec( cmd.data() ); +} + +bool +DM::bootOptions( QStringList &opts, int &defopt, int ¤t ) +{ + if (DMType != NewKDM) + return false; + + QCString re; + if (!exec( "listbootoptions\n", re )) + return false; + + opts = QStringList::split( '\t', QString::fromLocal8Bit( re.data() ) ); + if (opts.size() < 4) + return false; + + bool ok; + defopt = opts[2].toInt( &ok ); + if (!ok) + return false; + current = opts[3].toInt( &ok ); + if (!ok) + return false; + + opts = QStringList::split( ' ', opts[1] ); + for (QStringList::Iterator it = opts.begin(); it != opts.end(); ++it) + (*it).replace( "\\s", " " ); + + return true; +} + +void +DM::setLock( bool on ) +{ + if (DMType != GDM) + exec( on ? "lock\n" : "unlock\n" ); +} + +bool +DM::isSwitchable() +{ + if (DMType == OldKDM) + return dpy[0] == ':'; + + if (DMType == GDM) + return exec( "QUERY_VT\n" ); + + QCString re; + + return exec( "caps\n", re ) && re.find( "\tlocal" ) >= 0; +} + +int +DM::numReserve() +{ + if (DMType == GDM) + return 1; /* Bleh */ + + if (DMType == OldKDM) + return strstr( ctl, ",rsvd" ) ? 1 : -1; + + QCString re; + int p; + + if (!(exec( "caps\n", re ) && (p = re.find( "\treserve " )) >= 0)) + return -1; + return atoi( re.data() + p + 9 ); +} + +void +DM::startReserve() +{ + if (DMType == GDM) + exec("FLEXI_XSERVER\n"); + else + exec("reserve\n"); +} + +bool +DM::localSessions( SessList &list ) +{ + if (DMType == OldKDM) + return false; + + QCString re; + + if (DMType == GDM) { + if (!exec( "CONSOLE_SERVERS\n", re )) + return false; + QStringList sess = QStringList::split( QChar(';'), re.data() + 3 ); + for (QStringList::ConstIterator it = sess.begin(); it != sess.end(); ++it) { + QStringList ts = QStringList::split( QChar(','), *it, true ); + SessEnt se; + se.display = ts[0]; + se.user = ts[1]; + se.vt = ts[2].toInt(); + se.session = "<unknown>"; + se.self = ts[0] == ::getenv( "DISPLAY" ); /* Bleh */ + se.tty = false; + list.append( se ); + } + } else { + if (!exec( "list\talllocal\n", re )) + return false; + QStringList sess = QStringList::split( QChar('\t'), re.data() + 3 ); + for (QStringList::ConstIterator it = sess.begin(); it != sess.end(); ++it) { + QStringList ts = QStringList::split( QChar(','), *it, true ); + SessEnt se; + se.display = ts[0]; + if (ts[1][0] == '@') + se.from = ts[1].mid( 1 ), se.vt = 0; + else + se.vt = ts[1].mid( 2 ).toInt(); + se.user = ts[2]; + se.session = ts[3]; + se.self = (ts[4].find( '*' ) >= 0); + se.tty = (ts[4].find( 't' ) >= 0); + list.append( se ); + } + } + return true; +} + +void +DM::sess2Str2( const SessEnt &se, QString &user, QString &loc ) +{ + if (se.tty) { + user = i18n("user: ...", "%1: TTY login").arg( se.user ); + loc = se.vt ? QString("vt%1").arg( se.vt ) : se.display ; + } else { + user = + se.user.isEmpty() ? + se.session.isEmpty() ? + i18n("Unused") : + se.session == "<remote>" ? + i18n("X login on remote host") : + i18n("... host", "X login on %1").arg( se.session ) : + se.session == "<unknown>" ? + se.user : + i18n("user: session type", "%1: %2") + .arg( se.user ).arg( se.session ); + loc = + se.vt ? + QString("%1, vt%2").arg( se.display ).arg( se.vt ) : + se.display; + } +} + +QString +DM::sess2Str( const SessEnt &se ) +{ + QString user, loc; + + sess2Str2( se, user, loc ); + return i18n("session (location)", "%1 (%2)").arg( user ).arg( loc ); +} + +bool +DM::switchVT( int vt ) +{ + if (DMType == GDM) + return exec( QString("SET_VT %1\n").arg(vt).latin1() ); + + return exec( QString("activate\tvt%1\n").arg(vt).latin1() ); +} + +void +DM::lockSwitchVT( int vt ) +{ + if (switchVT( vt )) + kapp->dcopClient()->send( "kdesktop", "KScreensaverIface", "lock()", "" ); +} + +void +DM::GDMAuthenticate() +{ + FILE *fp; + const char *dpy, *dnum, *dne; + int dnl; + Xauth *xau; + + dpy = DisplayString( QPaintDevice::x11AppDisplay() ); + if (!dpy) { + dpy = ::getenv( "DISPLAY" ); + if (!dpy) + return; + } + dnum = strchr( dpy, ':' ) + 1; + dne = strchr( dpy, '.' ); + dnl = dne ? dne - dnum : strlen( dnum ); + + /* XXX should do locking */ + if (!(fp = fopen( XauFileName(), "r" ))) + return; + + while ((xau = XauReadAuth( fp ))) { + if (xau->family == FamilyLocal && + xau->number_length == dnl && !memcmp( xau->number, dnum, dnl ) && + xau->data_length == 16 && + xau->name_length == 18 && !memcmp( xau->name, "MIT-MAGIC-COOKIE-1", 18 )) + { + QString cmd( "AUTH_LOCAL " ); + for (int i = 0; i < 16; i++) + cmd += QString::number( (uchar)xau->data[i], 16 ).rightJustify( 2, '0'); + cmd += "\n"; + if (exec( cmd.latin1() )) { + XauDisposeAuth( xau ); + break; + } + } + XauDisposeAuth( xau ); + } + + fclose (fp); +} + +#endif // Q_WS_X11 diff --git a/kdmlib/dmctl.h b/kdmlib/dmctl.h new file mode 100644 index 000000000..aadc89bdb --- /dev/null +++ b/kdmlib/dmctl.h @@ -0,0 +1,93 @@ +/* + Copyright (C) 2004,2005 Oswald Buddenhagen <[email protected]> + Copyright (C) 2005 Stephan Kulow <[email protected]> + + This program is free software; you can redistribute it and/or + modify it under the terms of the Lesser 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 Lesser GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef DMCTL_H +#define DMCTL_H + +#include <kapplication.h> + +struct SessEnt { + QString display, from, user, session; + int vt; + bool self:1, tty:1; +}; + +typedef QValueList<SessEnt> SessList; + +class DM { + +#ifdef Q_WS_X11 + +public: + DM(); + ~DM(); + + bool canShutdown(); + void shutdown( KApplication::ShutdownType shutdownType, + KApplication::ShutdownMode shutdownMode, + const QString &bootOption = QString::null ); + + void setLock( bool on ); + + bool isSwitchable(); + int numReserve(); + void startReserve(); + bool localSessions( SessList &list ); + bool switchVT( int vt ); + void lockSwitchVT( int vt ); + + bool bootOptions( QStringList &opts, int &dflt, int &curr ); + + static QString sess2Str( const SessEnt &se ); + static void sess2Str2( const SessEnt &se, QString &user, QString &loc ); + +private: + int fd; + + bool exec( const char *cmd, QCString &ret ); + bool exec( const char *cmd ); + + void GDMAuthenticate(); + +#else // Q_WS_X11 + +public: + DM() {} + + bool canShutdown() { return false; } + void shutdown( KApplication::ShutdownType shutdownType, + KApplication::ShutdownMode shutdownMode, + const QString &bootOption = QString::null ) {} + + void setLock( bool ) {} + + bool isSwitchable() { return false; } + int numReserve() { return -1; } + void startReserve() {} + bool localSessions( SessList &list ) { return false; } + void switchVT( int vt ) {} + + bool bootOptions( QStringList &opts, int &dflt, int &curr ); + +#endif // Q_WS_X11 + +}; // class DM + +#endif // DMCTL_H diff --git a/kdmlib/kgreet_classic.cpp b/kdmlib/kgreet_classic.cpp new file mode 100644 index 000000000..b38e979d0 --- /dev/null +++ b/kdmlib/kgreet_classic.cpp @@ -0,0 +1,505 @@ +/* + +Conversation widget for kdm greeter + +Copyright (C) 1997, 1998, 2000 Steffen Hansen <[email protected]> +Copyright (C) 2000-2003 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 "kgreet_classic.h" +#include "themer/kdmthemer.h" +#include "themer/kdmitem.h" + +#include <klocale.h> +#include <klineedit.h> +#include <kpassdlg.h> +#include <kuser.h> + +#include <qregexp.h> +#include <qlayout.h> +#include <qlabel.h> + +class KDMPasswordEdit : public KPasswordEdit { +public: + KDMPasswordEdit( QWidget *parent ) : KPasswordEdit( parent, 0 ) {} + KDMPasswordEdit( KPasswordEdit::EchoModes echoMode, QWidget *parent ) : KPasswordEdit( echoMode, parent, 0 ) {} +protected: + virtual void contextMenuEvent( QContextMenuEvent * ) {} +}; + +static int echoMode; + +KClassicGreeter::KClassicGreeter( KGreeterPluginHandler *_handler, + KdmThemer *themer, + QWidget *parent, QWidget *pred, + const QString &_fixedEntity, + Function _func, Context _ctx ) : + QObject(), + KGreeterPlugin( _handler ), + fixedUser( _fixedEntity ), + func( _func ), + ctx( _ctx ), + exp( -1 ), + pExp( -1 ), + running( false ) +{ + KdmItem *user_entry = 0, *pw_entry = 0; + QGridLayout *grid = 0; + int line = 0; + + layoutItem = 0; + + if (themer && + (!(user_entry = themer->findNode( "user-entry" )) || + !(pw_entry = themer->findNode( "pw-entry" )))) + themer = 0; + + if (!themer) + layoutItem = grid = new QGridLayout( 0, 0, 10 ); + + loginLabel = passwdLabel = passwd1Label = passwd2Label = 0; + loginEdit = 0; + passwdEdit = passwd1Edit = passwd2Edit = 0; + if (ctx == ExUnlock || ctx == ExChangeTok) + fixedUser = KUser().loginName(); + if (func != ChAuthTok) { + if (fixedUser.isEmpty()) { + loginEdit = new KLineEdit( parent ); + loginEdit->setContextMenuEnabled( false ); + connect( loginEdit, SIGNAL(lostFocus()), SLOT(slotLoginLostFocus()) ); + connect( loginEdit, SIGNAL(lostFocus()), SLOT(slotActivity()) ); + connect( loginEdit, SIGNAL(textChanged( const QString & )), SLOT(slotActivity()) ); + connect( loginEdit, SIGNAL(selectionChanged()), SLOT(slotActivity()) ); + if (pred) { + parent->setTabOrder( pred, loginEdit ); + pred = loginEdit; + } + if (!grid) { + loginEdit->adjustSize(); + user_entry->setWidget( loginEdit ); + } else { + loginLabel = new QLabel( loginEdit, i18n("&Username:"), parent ); + grid->addWidget( loginLabel, line, 0 ); + grid->addWidget( loginEdit, line++, 1 ); + } + } else if (ctx != Login && ctx != Shutdown && grid) { + loginLabel = new QLabel( i18n("Username:"), parent ); + grid->addWidget( loginLabel, line, 0 ); + grid->addWidget( new QLabel( fixedUser, parent ), line++, 1 ); + } + if (echoMode == -1) + passwdEdit = new KDMPasswordEdit( parent ); + else + passwdEdit = new KDMPasswordEdit( (KPasswordEdit::EchoModes)echoMode, + parent ); + connect( passwdEdit, SIGNAL(textChanged( const QString & )), + SLOT(slotActivity()) ); + connect( passwdEdit, SIGNAL(lostFocus()), SLOT(slotActivity()) ); + if (pred) { + parent->setTabOrder( pred, passwdEdit ); + pred = passwdEdit; + } + if (!grid) { + passwdEdit->adjustSize(); + pw_entry->setWidget( passwdEdit ); + } else { + passwdLabel = new QLabel( passwdEdit, + func == Authenticate ? + i18n("&Password:") : + i18n("Current &password:"), + parent ); + grid->addWidget( passwdLabel, line, 0 ); + grid->addWidget( passwdEdit, line++, 1 ); + } + if (loginEdit) + loginEdit->setFocus(); + else + passwdEdit->setFocus(); + } + if (func != Authenticate) { + if (echoMode == -1) { + passwd1Edit = new KDMPasswordEdit( (KPasswordEdit::EchoModes)echoMode, parent ); + passwd2Edit = new KDMPasswordEdit( (KPasswordEdit::EchoModes)echoMode, parent ); + } else { + passwd1Edit = new KDMPasswordEdit( parent ); + passwd2Edit = new KDMPasswordEdit( parent ); + } + passwd1Label = new QLabel( passwd1Edit, i18n("&New password:"), parent ); + passwd2Label = new QLabel( passwd2Edit, i18n("Con&firm password:"), parent ); + if (pred) { + parent->setTabOrder( pred, passwd1Edit ); + parent->setTabOrder( passwd1Edit, passwd2Edit ); + } + if (grid) { + grid->addWidget( passwd1Label, line, 0 ); + grid->addWidget( passwd1Edit, line++, 1 ); + grid->addWidget( passwd2Label, line, 0 ); + grid->addWidget( passwd2Edit, line, 1 ); + } + if (!passwdEdit) + passwd1Edit->setFocus(); + } +} + +// virtual +KClassicGreeter::~KClassicGreeter() +{ + abort(); + if (!layoutItem) { + delete loginEdit; + delete passwdEdit; + return; + } + QLayoutIterator it = static_cast<QLayout *>(layoutItem)->iterator(); + for (QLayoutItem *itm = it.current(); itm; itm = ++it) + delete itm->widget(); + delete layoutItem; +} + +void // virtual +KClassicGreeter::loadUsers( const QStringList &users ) +{ + KCompletion *userNamesCompletion = new KCompletion; + userNamesCompletion->setItems( users ); + loginEdit->setCompletionObject( userNamesCompletion ); + loginEdit->setAutoDeleteCompletionObject( true ); + loginEdit->setCompletionMode( KGlobalSettings::CompletionAuto ); +} + +void // virtual +KClassicGreeter::presetEntity( const QString &entity, int field ) +{ + loginEdit->setText( entity ); + if (field == 1) + passwdEdit->setFocus(); + else { + loginEdit->setFocus(); + loginEdit->selectAll(); + if (field == -1) { + passwdEdit->setText( " " ); + passwdEdit->setEnabled( false ); + authTok = false; + } + } + curUser = entity; +} + +QString // virtual +KClassicGreeter::getEntity() const +{ + return fixedUser.isEmpty() ? loginEdit->text() : fixedUser; +} + +void // virtual +KClassicGreeter::setUser( const QString &user ) +{ + // assert( fixedUser.isEmpty() ); + curUser = user; + loginEdit->setText( user ); + passwdEdit->setFocus(); + passwdEdit->selectAll(); +} + +void // virtual +KClassicGreeter::setEnabled( bool enable ) +{ + // assert( !passwd1Label ); + // assert( func == Authenticate && ctx == Shutdown ); +// if (loginLabel) +// loginLabel->setEnabled( enable ); + passwdLabel->setEnabled( enable ); + setActive( enable ); + if (enable) + passwdEdit->setFocus(); +} + +void // private +KClassicGreeter::returnData() +{ + switch (exp) { + case 0: + handler->gplugReturnText( (loginEdit ? loginEdit->text() : + fixedUser).local8Bit(), + KGreeterPluginHandler::IsUser ); + break; + case 1: + handler->gplugReturnText( passwdEdit->password(), + KGreeterPluginHandler::IsPassword | + KGreeterPluginHandler::IsSecret ); + break; + case 2: + handler->gplugReturnText( passwd1Edit->password(), + KGreeterPluginHandler::IsSecret ); + break; + default: // case 3: + handler->gplugReturnText( passwd2Edit->password(), + KGreeterPluginHandler::IsNewPassword | + KGreeterPluginHandler::IsSecret ); + break; + } +} + +bool // virtual +KClassicGreeter::textMessage( const char *text, bool err ) +{ + if (!err && + QString( text ).find( QRegExp( "^Changing password for [^ ]+$" ) ) >= 0) + return true; + return false; +} + +void // virtual +KClassicGreeter::textPrompt( const char *prompt, bool echo, bool nonBlocking ) +{ + pExp = exp; + if (echo) + exp = 0; + else if (!authTok) + exp = 1; + else { + QString pr( prompt ); + if (pr.find( QRegExp( "\\bpassword\\b", false ) ) >= 0) { + if (pr.find( QRegExp( "\\b(re-?(enter|type)|again|confirm|repeat)\\b", + false ) ) >= 0) + exp = 3; + else if (pr.find( QRegExp( "\\bnew\\b", false ) ) >= 0) + exp = 2; + else { // QRegExp( "\\b(old|current)\\b", false ) is too strict + handler->gplugReturnText( "", + KGreeterPluginHandler::IsOldPassword | + KGreeterPluginHandler::IsSecret ); + return; + } + } else { + handler->gplugMsgBox( QMessageBox::Critical, + i18n("Unrecognized prompt \"%1\"") + .arg( prompt ) ); + handler->gplugReturnText( 0, 0 ); + exp = -1; + return; + } + } + + if (pExp >= 0 && pExp >= exp) { + revive(); + has = -1; + } + + if (has >= exp || nonBlocking) + returnData(); +} + +bool // virtual +KClassicGreeter::binaryPrompt( const char *, bool ) +{ + // this simply cannot happen ... :} + return true; +} + +void // virtual +KClassicGreeter::start() +{ + authTok = !(passwdEdit && passwdEdit->isEnabled()); + exp = has = -1; + running = true; +} + +void // virtual +KClassicGreeter::suspend() +{ +} + +void // virtual +KClassicGreeter::resume() +{ +} + +void // virtual +KClassicGreeter::next() +{ + // assert( running ); + if (loginEdit && loginEdit->hasFocus()) { + passwdEdit->setFocus(); // will cancel running login if necessary + has = 0; + } else if (passwdEdit && passwdEdit->hasFocus()) { + if (passwd1Edit) + passwd1Edit->setFocus(); + has = 1; + } else if (passwd1Edit) { + if (passwd1Edit->hasFocus()) { + passwd2Edit->setFocus(); + has = 1; // sic! + } else + has = 3; + } else + has = 1; + if (exp < 0) + handler->gplugStart(); + else if (has >= exp) + returnData(); +} + +void // virtual +KClassicGreeter::abort() +{ + running = false; + if (exp >= 0) { + exp = -1; + handler->gplugReturnText( 0, 0 ); + } +} + +void // virtual +KClassicGreeter::succeeded() +{ + // assert( running || timed_login ); + if (!authTok) { + setActive( false ); + if (passwd1Edit) { + authTok = true; + return; + } + } else + setActive2( false ); + exp = -1; + running = false; +} + +void // virtual +KClassicGreeter::failed() +{ + // assert( running || timed_login ); + setActive( false ); + setActive2( false ); + exp = -1; + running = false; +} + +void // virtual +KClassicGreeter::revive() +{ + // assert( !running ); + setActive2( true ); + if (authTok) { + passwd1Edit->erase(); + passwd2Edit->erase(); + passwd1Edit->setFocus(); + } else { + passwdEdit->erase(); + if (loginEdit && loginEdit->isEnabled()) + passwdEdit->setEnabled( true ); + else { + setActive( true ); + if (loginEdit && loginEdit->text().isEmpty()) + loginEdit->setFocus(); + else + passwdEdit->setFocus(); + } + } +} + +void // virtual +KClassicGreeter::clear() +{ + // assert( !running && !passwd1Edit ); + passwdEdit->erase(); + if (loginEdit) { + loginEdit->clear(); + loginEdit->setFocus(); + curUser = QString::null; + } else + passwdEdit->setFocus(); +} + + +// private + +void +KClassicGreeter::setActive( bool enable ) +{ + if (loginEdit) + loginEdit->setEnabled( enable ); + if (passwdEdit) + passwdEdit->setEnabled( enable ); +} + +void +KClassicGreeter::setActive2( bool enable ) +{ + if (passwd1Edit) { + passwd1Edit->setEnabled( enable ); + passwd2Edit->setEnabled( enable ); + } +} + +void +KClassicGreeter::slotLoginLostFocus() +{ + if (!running) + return; + if (exp > 0) { + if (curUser == loginEdit->text()) + return; + exp = -1; + handler->gplugReturnText( 0, 0 ); + } + curUser = loginEdit->text(); + handler->gplugSetUser( curUser ); +} + +void +KClassicGreeter::slotActivity() +{ + if (running) + handler->gplugActivity(); +} + +// factory + +static bool init( const QString &, + QVariant (*getConf)( void *, const char *, const QVariant & ), + void *ctx ) +{ + echoMode = getConf( ctx, "EchoMode", QVariant( -1 ) ).toInt(); + KGlobal::locale()->insertCatalogue( "kgreet_classic" ); + return true; +} + +static void done( void ) +{ + KGlobal::locale()->removeCatalogue( "kgreet_classic" ); +} + +static KGreeterPlugin * +create( KGreeterPluginHandler *handler, KdmThemer *themer, + QWidget *parent, QWidget *predecessor, + const QString &fixedEntity, + KGreeterPlugin::Function func, + KGreeterPlugin::Context ctx ) +{ + return new KClassicGreeter( handler, themer, parent, predecessor, fixedEntity, func, ctx ); +} + +KDE_EXPORT kgreeterplugin_info kgreeterplugin_info = { + I18N_NOOP("Username + password (classic)"), "classic", + kgreeterplugin_info::Local | kgreeterplugin_info::Presettable, + init, done, create +}; + +#include "kgreet_classic.moc" diff --git a/kdmlib/kgreet_classic.h b/kdmlib/kgreet_classic.h new file mode 100644 index 000000000..64d39799a --- /dev/null +++ b/kdmlib/kgreet_classic.h @@ -0,0 +1,87 @@ +/* + +Conversation widget for kdm greeter + +Copyright (C) 1997, 1998 Steffen Hansen <[email protected]> +Copyright (C) 2000-2003 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. + +*/ + + +#ifndef KGREET_CLASSIC_H +#define KGREET_CLASSIC_H + +#include "kgreeterplugin.h" + +#include <qobject.h> + +class KLineEdit; +class KPasswordEdit; +class KSimpleConfig; +class QGridLayout; +class QLabel; + +class KClassicGreeter : public QObject, public KGreeterPlugin { + Q_OBJECT + + public: + KClassicGreeter( KGreeterPluginHandler *handler, + KdmThemer *themer, + QWidget *parent, QWidget *predecessor, + const QString &fixedEntitiy, + Function func, Context ctx ); + ~KClassicGreeter(); + virtual void loadUsers( const QStringList &users ); + virtual void presetEntity( const QString &entity, int field ); + virtual QString getEntity() const; + virtual void setUser( const QString &user ); + virtual void setEnabled( bool on ); + virtual bool textMessage( const char *message, bool error ); + virtual void textPrompt( const char *prompt, bool echo, bool nonBlocking ); + virtual bool binaryPrompt( const char *prompt, bool nonBlocking ); + virtual void start(); + virtual void suspend(); + virtual void resume(); + virtual void next(); + virtual void abort(); + virtual void succeeded(); + virtual void failed(); + virtual void revive(); + virtual void clear(); + + public slots: + void slotLoginLostFocus(); + void slotActivity(); + + private: + void setActive( bool enable ); + void setActive2( bool enable ); + void returnData(); + + QLabel *loginLabel, *passwdLabel, *passwd1Label, *passwd2Label; + KLineEdit *loginEdit; + KPasswordEdit *passwdEdit, *passwd1Edit, *passwd2Edit; + KSimpleConfig *stsFile; + QString fixedUser, curUser; + Function func; + Context ctx; + int exp, pExp, has; + bool running, authTok; +}; + +#endif /* KGREET_CLASSIC_H */ diff --git a/kdmlib/kgreet_winbind.cpp b/kdmlib/kgreet_winbind.cpp new file mode 100644 index 000000000..eeef08b01 --- /dev/null +++ b/kdmlib/kgreet_winbind.cpp @@ -0,0 +1,671 @@ +/* + +Conversation widget for kdm greeter + +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 "kgreet_winbind.h" +#include "themer/kdmthemer.h" +#include "themer/kdmitem.h" + +#include <klocale.h> +#include <kdebug.h> +#include <kcombobox.h> +#include <klineedit.h> +#include <kpassdlg.h> +#include <kuser.h> +#include <kprocio.h> + +#include <qregexp.h> +#include <qlayout.h> +#include <qlabel.h> + +#include <stdlib.h> + +class KDMPasswordEdit : public KPasswordEdit { +public: + KDMPasswordEdit( QWidget *parent ) : KPasswordEdit( parent, 0 ) {} + KDMPasswordEdit( KPasswordEdit::EchoModes echoMode, QWidget *parent ) : KPasswordEdit( echoMode, parent, 0 ) {} +protected: + virtual void contextMenuEvent( QContextMenuEvent * ) {} +}; + +static int echoMode; +static char separator; +static QStringList staticDomains; +static QString defaultDomain; + +static void +splitEntity( const QString &ent, QString &dom, QString &usr ) +{ + int pos = ent.find( separator ); + if (pos < 0) + dom = "<local>", usr = ent; + else + dom = ent.left( pos ), usr = ent.mid( pos + 1 ); +} + +KWinbindGreeter::KWinbindGreeter( KGreeterPluginHandler *_handler, + KdmThemer *themer, + QWidget *parent, QWidget *pred, + const QString &_fixedEntity, + Function _func, Context _ctx ) : + QObject(), + KGreeterPlugin( _handler ), + func( _func ), + ctx( _ctx ), + exp( -1 ), + pExp( -1 ), + running( false ) +{ + KdmItem *user_entry = 0, *pw_entry = 0, *domain_entry = 0; + QGridLayout *grid = 0; + + int line = 0; + layoutItem = 0; + + if (themer && + (!(user_entry = themer->findNode( "user-entry" )) || + !(pw_entry = themer->findNode( "pw-entry" )) || + !(domain_entry = themer->findNode( "domain-entry" )))) + themer = 0; + + if (!themer) + layoutItem = grid = new QGridLayout( 0, 0, 10 ); + + domainLabel = loginLabel = passwdLabel = passwd1Label = passwd2Label = 0; + domainCombo = 0; + loginEdit = 0; + passwdEdit = passwd1Edit = passwd2Edit = 0; + m_domainLister = 0; + if (ctx == ExUnlock || ctx == ExChangeTok) + splitEntity( KUser().loginName(), fixedDomain, fixedUser ); + else + splitEntity( _fixedEntity, fixedDomain, fixedUser ); + if (func != ChAuthTok) { + if (fixedUser.isEmpty()) { + domainCombo = new KComboBox( parent ); + connect( domainCombo, SIGNAL(activated( const QString & )), + SLOT(slotChangedDomain( const QString & )) ); + connect( domainCombo, SIGNAL(activated( const QString & )), + SLOT(slotLoginLostFocus()) ); + connect( domainCombo, SIGNAL(activated( const QString & )), + SLOT(slotActivity()) ); + // should handle loss of focus + loginEdit = new KLineEdit( parent ); + loginEdit->setContextMenuEnabled( false ); + + if (pred) { + parent->setTabOrder( pred, domainCombo ); + parent->setTabOrder( domainCombo, loginEdit ); + pred = loginEdit; + } + if (!grid) { + loginEdit->adjustSize(); + domainCombo->adjustSize(); + user_entry->setWidget( loginEdit ); + domain_entry->setWidget( domainCombo ); + } else { + domainLabel = new QLabel( domainCombo, i18n("&Domain:"), parent ); + loginLabel = new QLabel( loginEdit, i18n("&Username:"), parent ); + grid->addWidget( domainLabel, line, 0 ); + grid->addWidget( domainCombo, line++, 1 ); + grid->addWidget( loginLabel, line, 0 ); + grid->addWidget( loginEdit, line++, 1 ); + } + connect( loginEdit, SIGNAL(lostFocus()), SLOT(slotLoginLostFocus()) ); + connect( loginEdit, SIGNAL(lostFocus()), SLOT(slotActivity()) ); + connect( loginEdit, SIGNAL(textChanged( const QString & )), SLOT(slotActivity()) ); + connect( loginEdit, SIGNAL(selectionChanged()), SLOT(slotActivity()) ); + connect(&mDomainListTimer, SIGNAL(timeout()), SLOT(slotStartDomainList())); + domainCombo->insertStringList( staticDomains ); + QTimer::singleShot(0, this, SLOT(slotStartDomainList())); + } else if (ctx != Login && ctx != Shutdown && grid) { + domainLabel = new QLabel( i18n("Domain:"), parent ); + grid->addWidget( domainLabel, line, 0 ); + grid->addWidget( new QLabel( fixedDomain, parent ), line++, 1 ); + loginLabel = new QLabel( i18n("Username:"), parent ); + grid->addWidget( loginLabel, line, 0 ); + grid->addWidget( new QLabel( fixedUser, parent ), line++, 1 ); + } + if (echoMode == -1) + passwdEdit = new KDMPasswordEdit( parent ); + else + passwdEdit = new KDMPasswordEdit( (KPasswordEdit::EchoModes)echoMode, + parent ); + connect( passwdEdit, SIGNAL(textChanged( const QString & )), + SLOT(slotActivity()) ); + connect( passwdEdit, SIGNAL(lostFocus()), SLOT(slotActivity()) ); + + if (!grid) { + passwdEdit->adjustSize(); + pw_entry->setWidget( passwdEdit ); + } else { + passwdLabel = new QLabel( passwdEdit, + func == Authenticate ? + i18n("&Password:") : + i18n("Current &password:"), + parent ); + if (pred) { + parent->setTabOrder( pred, passwdEdit ); + pred = passwdEdit; + } + grid->addWidget( passwdLabel, line, 0 ); + grid->addWidget( passwdEdit, line++, 1 ); + } + + if (loginEdit) + loginEdit->setFocus(); + else + passwdEdit->setFocus(); + } + if (func != Authenticate) { + if (echoMode == -1) { + passwd1Edit = new KDMPasswordEdit( (KPasswordEdit::EchoModes)echoMode, parent ); + passwd2Edit = new KDMPasswordEdit( (KPasswordEdit::EchoModes)echoMode, parent ); + } else { + passwd1Edit = new KDMPasswordEdit( parent ); + passwd2Edit = new KDMPasswordEdit( parent ); + } + passwd1Label = new QLabel( passwd1Edit, i18n("&New password:"), parent ); + passwd2Label = new QLabel( passwd2Edit, i18n("Con&firm password:"), parent ); + if (pred) { + parent->setTabOrder( pred, passwd1Edit ); + parent->setTabOrder( passwd1Edit, passwd2Edit ); + } + if (grid) { + grid->addWidget( passwd1Label, line, 0 ); + grid->addWidget( passwd1Edit, line++, 1 ); + grid->addWidget( passwd2Label, line, 0 ); + grid->addWidget( passwd2Edit, line, 1 ); + } + if (!passwdEdit) + passwd1Edit->setFocus(); + } +} + +// virtual +KWinbindGreeter::~KWinbindGreeter() +{ + abort(); + if (!layoutItem) { + delete loginEdit; + delete passwdEdit; + delete domainCombo; + return; + } + QLayoutIterator it = static_cast<QLayout *>(layoutItem)->iterator(); + for (QLayoutItem *itm = it.current(); itm; itm = ++it) + delete itm->widget(); + delete layoutItem; + delete m_domainLister; +} + +void +KWinbindGreeter::slotChangedDomain( const QString &dom ) +{ + if (!loginEdit->completionObject()) + return; + QStringList users; + if (dom == "<local>") { + for (QStringList::ConstIterator it = allUsers.begin(); it != allUsers.end(); ++it) + if ((*it).find( separator ) < 0) + users << *it; + } else { + QString st( dom + separator ); + for (QStringList::ConstIterator it = allUsers.begin(); it != allUsers.end(); ++it) + if ((*it).startsWith( st )) + users << (*it).mid( st.length() ); + } + loginEdit->completionObject()->setItems( users ); +} + +void // virtual +KWinbindGreeter::loadUsers( const QStringList &users ) +{ + allUsers = users; + KCompletion *userNamesCompletion = new KCompletion; + loginEdit->setCompletionObject( userNamesCompletion ); + loginEdit->setAutoDeleteCompletionObject( true ); + loginEdit->setCompletionMode( KGlobalSettings::CompletionAuto ); + slotChangedDomain( defaultDomain ); +} + +void // virtual +KWinbindGreeter::presetEntity( const QString &entity, int field ) +{ + QString dom, usr; + splitEntity( entity, dom, usr ); + domainCombo->setCurrentItem( dom, true ); + slotChangedDomain( dom ); + loginEdit->setText( usr ); + if (field > 1) + passwdEdit->setFocus(); + else if (field == 1 || field == -1) { + if (field == -1) { + passwdEdit->setText( " " ); + passwdEdit->setEnabled( false ); + authTok = false; + } + loginEdit->setFocus(); + loginEdit->selectAll(); + } + curUser = entity; +} + +QString // virtual +KWinbindGreeter::getEntity() const +{ + QString dom, usr; + if (fixedUser.isEmpty()) + dom = domainCombo->currentText(), usr = loginEdit->text(); + else + dom = fixedDomain, usr = fixedUser; + return dom == "<local>" ? usr : dom + separator + usr; +} + +void // virtual +KWinbindGreeter::setUser( const QString &user ) +{ + // assert (fixedUser.isEmpty()); + curUser = user; + QString dom, usr; + splitEntity( user, dom, usr ); + domainCombo->setCurrentItem( dom, true ); + slotChangedDomain( dom ); + loginEdit->setText( usr ); + passwdEdit->setFocus(); + passwdEdit->selectAll(); +} + +void // virtual +KWinbindGreeter::setEnabled( bool enable ) +{ + // assert( !passwd1Label ); + // assert( func == Authenticate && ctx == Shutdown ); +// if (domainCombo) +// domainCombo->setEnabled( enable ); +// if (loginLabel) +// loginLabel->setEnabled( enable ); + passwdLabel->setEnabled( enable ); + setActive( enable ); + if (enable) + passwdEdit->setFocus(); +} + +void // private +KWinbindGreeter::returnData() +{ + switch (exp) { + case 0: + handler->gplugReturnText( getEntity().local8Bit(), + KGreeterPluginHandler::IsUser ); + break; + case 1: + handler->gplugReturnText( passwdEdit->password(), + KGreeterPluginHandler::IsPassword | + KGreeterPluginHandler::IsSecret ); + break; + case 2: + handler->gplugReturnText( passwd1Edit->password(), + KGreeterPluginHandler::IsSecret ); + break; + default: // case 3: + handler->gplugReturnText( passwd2Edit->password(), + KGreeterPluginHandler::IsNewPassword | + KGreeterPluginHandler::IsSecret ); + break; + } +} + +bool // virtual +KWinbindGreeter::textMessage( const char *text, bool err ) +{ + if (!err && + QString( text ).find( QRegExp( "^Changing password for [^ ]+$" ) ) >= 0) + return true; + return false; +} + +void // virtual +KWinbindGreeter::textPrompt( const char *prompt, bool echo, bool nonBlocking ) +{ + pExp = exp; + if (echo) + exp = 0; + else if (!authTok) + exp = 1; + else { + QString pr( prompt ); + if (pr.find( QRegExp( "\\b(old|current)\\b", false ) ) >= 0) { + handler->gplugReturnText( "", + KGreeterPluginHandler::IsOldPassword | + KGreeterPluginHandler::IsSecret ); + return; + } else if (pr.find( QRegExp( "\\b(re-?(enter|type)|again|confirm|repeat)\\b", + false ) ) >= 0) + exp = 3; + else if (pr.find( QRegExp( "\\bnew\\b", false ) ) >= 0) + exp = 2; + else { + handler->gplugMsgBox( QMessageBox::Critical, + i18n("Unrecognized prompt \"%1\"") + .arg( prompt ) ); + handler->gplugReturnText( 0, 0 ); + exp = -1; + return; + } + } + + if (pExp >= 0 && pExp >= exp) { + revive(); + has = -1; + } + + if (has >= exp || nonBlocking) + returnData(); +} + +bool // virtual +KWinbindGreeter::binaryPrompt( const char *, bool ) +{ + // this simply cannot happen ... :} + return true; +} + +void // virtual +KWinbindGreeter::start() +{ + authTok = !(passwdEdit && passwdEdit->isEnabled()); + exp = has = -1; + running = true; +} + +void // virtual +KWinbindGreeter::suspend() +{ +} + +void // virtual +KWinbindGreeter::resume() +{ +} + +void // virtual +KWinbindGreeter::next() +{ + // assert( running ); + if (domainCombo && domainCombo->hasFocus()) + loginEdit->setFocus(); + else if (loginEdit && loginEdit->hasFocus()) { + passwdEdit->setFocus(); // will cancel running login if necessary + has = 0; + } else if (passwdEdit && passwdEdit->hasFocus()) { + if (passwd1Edit) + passwd1Edit->setFocus(); + has = 1; + } else if (passwd1Edit) { + if (passwd1Edit->hasFocus()) { + passwd2Edit->setFocus(); + has = 1; // sic! + } else + has = 3; + } else + has = 1; + if (exp < 0) + handler->gplugStart(); + else if (has >= exp) + returnData(); +} + +void // virtual +KWinbindGreeter::abort() +{ + running = false; + if (exp >= 0) { + exp = -1; + handler->gplugReturnText( 0, 0 ); + } +} + +void // virtual +KWinbindGreeter::succeeded() +{ + // assert( running || timed_login ); + if (!authTok) { + setActive( false ); + if (passwd1Edit) { + authTok = true; + return; + } + } else + setActive2( false ); + exp = -1; + running = false; +} + +void // virtual +KWinbindGreeter::failed() +{ + // assert( running || timed_login ); + setActive( false ); + setActive2( false ); + exp = -1; + running = false; +} + +void // virtual +KWinbindGreeter::revive() +{ + // assert( !running ); + setActive2( true ); + if (authTok) { + passwd1Edit->erase(); + passwd2Edit->erase(); + passwd1Edit->setFocus(); + } else { + passwdEdit->erase(); + if (loginEdit && loginEdit->isEnabled()) + passwdEdit->setEnabled( true ); + else { + setActive( true ); + if (loginEdit && loginEdit->text().isEmpty()) + loginEdit->setFocus(); + else + passwdEdit->setFocus(); + } + } +} + +void // virtual +KWinbindGreeter::clear() +{ + // assert( !running && !passwd1Edit ); + passwdEdit->erase(); + if (loginEdit) { + domainCombo->setCurrentItem( defaultDomain ); + slotChangedDomain( defaultDomain ); + loginEdit->clear(); + loginEdit->setFocus(); + curUser = QString::null; + } else + passwdEdit->setFocus(); +} + + +// private + +void +KWinbindGreeter::setActive( bool enable ) +{ + if (domainCombo) + domainCombo->setEnabled( enable ); + if (loginEdit) + loginEdit->setEnabled( enable ); + if (passwdEdit) + passwdEdit->setEnabled( enable ); +} + +void +KWinbindGreeter::setActive2( bool enable ) +{ + if (passwd1Edit) { + passwd1Edit->setEnabled( enable ); + passwd2Edit->setEnabled( enable ); + } +} + +void +KWinbindGreeter::slotLoginLostFocus() +{ + if (!running) + return; + QString ent( getEntity() ); + if (exp > 0) { + if (curUser == ent) + return; + exp = -1; + handler->gplugReturnText( 0, 0 ); + } + curUser = ent; + handler->gplugSetUser( curUser ); +} + +void +KWinbindGreeter::slotActivity() +{ + if (running) + handler->gplugActivity(); +} + +void +KWinbindGreeter::slotStartDomainList() +{ + mDomainListTimer.stop(); + mDomainListing.clear(); + + m_domainLister = new KProcIO; + connect(m_domainLister, SIGNAL(readReady(KProcIO*)), SLOT(slotReadDomainList())); + connect(m_domainLister, SIGNAL(processExited(KProcess*)), SLOT(slotEndDomainList())); + + (*m_domainLister) << "wbinfo" << "--own-domain" << "--trusted-domains"; + m_domainLister->setComm (KProcess::Stdout); + m_domainLister->start(); +} + +void +KWinbindGreeter::slotReadDomainList() +{ + QString line; + + while ( m_domainLister->readln( line ) != -1 ) { + mDomainListing.append(line); + } +} + +void +KWinbindGreeter::slotEndDomainList() +{ + delete m_domainLister; + m_domainLister = 0; + + QStringList domainList; + domainList = staticDomains; + + for (QStringList::const_iterator it = mDomainListing.begin(); + it != mDomainListing.end(); ++it) { + + if (!domainList.contains(*it)) + domainList.append(*it); + } + + QString current = domainCombo->currentText(); + + for (int i = 0; i < domainList.count(); ++i) { + if (i < domainCombo->count()) + domainCombo->changeItem(domainList[i], i); + else + domainCombo->insertItem(domainList[i], i); + } + + while (domainCombo->count() > domainList.count()) + domainCombo->removeItem(domainCombo->count()-1); + + domainCombo->setCurrentItem( current ); + + if (domainCombo->currentText() != current) + domainCombo->setCurrentItem( defaultDomain ); + + mDomainListTimer.start(5 * 1000); +} + +// factory + +static bool init( const QString &, + QVariant (*getConf)( void *, const char *, const QVariant & ), + void *ctx ) +{ + echoMode = getConf( ctx, "EchoMode", QVariant( -1 ) ).toInt(); + staticDomains = QStringList::split( ':', getConf( ctx, "winbind.Domains", QVariant( "" ) ).toString() ); + if (!staticDomains.contains("<local>")) + staticDomains << "<local>"; + + defaultDomain = getConf( ctx, "winbind.DefaultDomain", QVariant( staticDomains.first() ) ).toString(); + QString sepstr = getConf( ctx, "winbind.Separator", QVariant( QString::null ) ).toString(); + if (sepstr.isNull()) { + FILE *sepfile = popen( "wbinfo --separator 2>/dev/null", "r" ); + if (sepfile) { + QTextIStream( sepfile ) >> sepstr; + if (pclose( sepfile )) + sepstr = "\\"; + } else + sepstr = "\\"; + } + separator = sepstr[0].latin1(); + KGlobal::locale()->insertCatalogue( "kgreet_winbind" ); + return true; +} + +static void done( void ) +{ + KGlobal::locale()->removeCatalogue( "kgreet_winbind" ); + // avoid static deletion problems ... hopefully + staticDomains.clear(); + defaultDomain = QString::null; +} + +static KGreeterPlugin * +create( KGreeterPluginHandler *handler, KdmThemer *themer, + QWidget *parent, QWidget *predecessor, + const QString &fixedEntity, + KGreeterPlugin::Function func, + KGreeterPlugin::Context ctx ) +{ + return new KWinbindGreeter( handler, themer, parent, predecessor, fixedEntity, func, ctx ); +} + +KDE_EXPORT kgreeterplugin_info kgreeterplugin_info = { + I18N_NOOP("Winbind / Samba"), "classic", + kgreeterplugin_info::Local | kgreeterplugin_info::Fielded | kgreeterplugin_info::Presettable, + init, done, create +}; + +#include "kgreet_winbind.moc" diff --git a/kdmlib/kgreet_winbind.h b/kdmlib/kgreet_winbind.h new file mode 100644 index 000000000..7fc6a1c77 --- /dev/null +++ b/kdmlib/kgreet_winbind.h @@ -0,0 +1,100 @@ +/* + +Conversation widget for kdm greeter + +Copyright (C) 1997, 1998 Steffen Hansen <[email protected]> +Copyright (C) 2000-2003 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. + +*/ + + +#ifndef KGREET_WINBIND_H +#define KGREET_WINBIND_H + +#include "kgreeterplugin.h" + +#include <qobject.h> +#include <qtimer.h> + +class KComboBox; +class KLineEdit; +class KPasswordEdit; +class KSimpleConfig; +class QGridLayout; +class QLabel; +class KdmThemer; +class KProcIO; + +class KWinbindGreeter : public QObject, public KGreeterPlugin { + Q_OBJECT + + public: + KWinbindGreeter( KGreeterPluginHandler *handler, + KdmThemer *themer, + QWidget *parent, QWidget *predecessor, + const QString &fixedEntitiy, + Function func, Context ctx ); + ~KWinbindGreeter(); + virtual void loadUsers( const QStringList &users ); + virtual void presetEntity( const QString &entity, int field ); + virtual QString getEntity() const; + virtual void setUser( const QString &user ); + virtual void setEnabled( bool on ); + virtual bool textMessage( const char *message, bool error ); + virtual void textPrompt( const char *prompt, bool echo, bool nonBlocking ); + virtual bool binaryPrompt( const char *prompt, bool nonBlocking ); + virtual void start(); + virtual void suspend(); + virtual void resume(); + virtual void next(); + virtual void abort(); + virtual void succeeded(); + virtual void failed(); + virtual void revive(); + virtual void clear(); + + public slots: + void slotLoginLostFocus(); + void slotChangedDomain( const QString &dom ); + void slotActivity(); + void slotStartDomainList(); + void slotReadDomainList(); + void slotEndDomainList(); + + private: + void setActive( bool enable ); + void setActive2( bool enable ); + void returnData(); + + QLabel *domainLabel, *loginLabel, *passwdLabel, *passwd1Label, *passwd2Label; + KComboBox *domainCombo; + KLineEdit *loginEdit; + KPasswordEdit *passwdEdit, *passwd1Edit, *passwd2Edit; + KSimpleConfig *stsFile; + QString fixedDomain, fixedUser, curUser; + QStringList allUsers, mDomainListing; + KProcIO* m_domainLister; + QTimer mDomainListTimer; + + Function func; + Context ctx; + int exp, pExp, has; + bool running, authTok; +}; + +#endif /* KGREET_WINBIND_H */ diff --git a/kdmlib/kgreeterplugin.h b/kdmlib/kgreeterplugin.h new file mode 100644 index 000000000..e7677415c --- /dev/null +++ b/kdmlib/kgreeterplugin.h @@ -0,0 +1,401 @@ +/* + + Authentication method specific conversation plugin for KDE's greeter widgets + + Copyright (C) 2003 Oswald Buddenhagen <[email protected]> + Copyright (C) 2003 Fabian Kaiser <[email protected]> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KGREETERPLUGIN_H +#define KGREETERPLUGIN_H + +#include <qvariant.h> +#include <qmessagebox.h> +#include <kdemacros.h> + +class KdmThemer; + +class QWidget; +class QLayoutItem; + +class KGreeterPluginHandler { +public: + /* keep in sync with V_IS_* */ + enum { IsSecret = 1, IsUser = 2, IsPassword = 4, IsOldPassword = 8, + IsNewPassword = 16 }; + /** + * Reply to textPrompt(). + * @param text text to return to core; null to abort auth cycle + * @param tag zero or one of Is* + */ + virtual void gplugReturnText( const char *text, int tag ) = 0; + /** + * Reply to binaryPrompt(). + * @param data data in pam_client format to return to the core; + * null to abort auth cycle + */ + virtual void gplugReturnBinary( const char *data ) = 0; + /** + * Tell the greeter who is logging in. + * Call this preferably before gplugStart, as otherwise the .dmrc + * load will be delayed. Don't call at all if your plugin doesn't + * have the Local flag set. Call only for internally generated + * user changes. + * @param user the user logging in + */ + virtual void gplugSetUser( const QString &user ) = 0; + /** + * Start processing. + */ + virtual void gplugStart() = 0; + /** + * Plugins that expect user input from a different device than the mouse or + * keyboard must call this when user activity is detected to prevent the + * greeter from resetting/going away. Events should be compressed to no + * more than ten per second; one every five seconds is actually enough. + * Events should be actual changes to the input fields, not random motion. + */ + virtual void gplugActivity() = 0; + /** + * Show a message box on behalf of the talker. + * @param type message severity + * @param text message text + */ + virtual void gplugMsgBox( QMessageBox::Icon type, const QString &text ) = 0; +}; + +/** + * Abstract base class for conversation plugins ("talkers") to be used with + * KDM, kdesktop_lock, etc. + * The authentication method used by a particular instance of a plugin + * may be configurable, but the instance must handle exactly one method, + * i.e., info->method must be determined at the latest at init() time. + */ +class KGreeterPlugin { +public: + KGreeterPlugin( KGreeterPluginHandler *h ) : handler( h ) {} + virtual ~KGreeterPlugin() {} + + /** + * Variations of the talker: + * - Authenticate: authentication + * - AuthChAuthTok: authentication and password change + * - ChAuthTok: password change + */ + enum Function { Authenticate, AuthChAuthTok, ChAuthTok }; + + /** + * Contexts the talker can be used in: + * - Login: kdm login dialog + * - Shutdown: kdm shutdown dialog + * - Unlock: kdm unlock dialog (TODO) + * - ChangeTok: kdm password change dialog (TODO) + * - ExUnlock: kdesktop_lock unlock dialog + * - ExChangeTok: kdepasswd password change dialog (TODO) + * + * The Ex* contexts exist within a running session; the talker must know + * how to obtain the currently logged in user (+ domain/realm, etc.) + * itself (i.e., fixedEntity will be null). The non-Ex variants will have + * a fixedEntity passed in. + */ + enum Context { Login, Shutdown, Unlock, ChangeTok, + ExUnlock, ExChangeTok }; + + /** + * Provide the talker with a list of selectable users. This can be used + * for autocompletion, etc. + * Will be called only when not running. + * @param users the users to load. + */ + virtual void loadUsers( const QStringList &users ) = 0; + + /** + * Preload the talker with an (opaque to the greeter) entity. + * Will be called only when not running. + * @param entity the entity to preload the talker with. That + * will usually be something like "user" or "user@domain". + * @param field the sub-widget (probably line edit) to put the cursor into. + * If -1, preselect the user for timed login. This means pre-filling + * the password field with anything, disabling it, and placing the + * cursor in the user name field. + */ + virtual void presetEntity( const QString &entity, int field ) = 0; + + /** + * Obtain the actually logged in entity. + * Will be called only after succeeded() was called. + */ + virtual QString getEntity() const = 0; + + /** + * "Push" a user into the talker. That can be a click into the user list + * or successful authentication without the talker calling gplugSetUser. + * Will be called only when running. + * @param user the user to set. Note that this is a UNIX login, not a + * canonical entity + */ + virtual void setUser( const QString &user ) = 0; + + /** + * En-/disable any widgets contained in the talker. + * Will be called only when not running. + * @param on the state to set + */ + virtual void setEnabled( bool on ) = 0; + + /** + * Called when a message from the authentication backend arrives. + * @param message the message received from the backend + * @param error if true, @p message is an error message, otherwise it's + * an informational message + * @return true means that the talker already handled the message, false + * that the greeter should display it in a message box + * + * FIXME: Filtering a message usually means that the backend issued a + * prompt and obtains the authentication data itself. However, in that + * state the backend is unresponsive, e.g., no shutdown is possible. + * The frontend could send the backend a signal, but the "escape path" + * within the backend is unclear (PAM won't like simply longjmp()ing + * out of it). + */ + virtual bool textMessage( const char *message, bool error ) = 0; + + /** + * Prompt the user for data. Reply by calling handler->gplugReturnText(). + * @param propmt the prompt to display. It may be null, in which case + * "Username"/"Password" should be shown and the replies should be tagged + * with the respective Is* flag. + * @param echo if true, a normal input widget can be used, otherwise one that + * visually obscures the user's input. + * @param nonBlocking if true, report whatever is already available, + * otherwise wait for user input. + */ + virtual void textPrompt( const char *prompt, bool echo, bool nonBlocking ) = 0; + + /** + * Request binary authentication data from the talker. Reply by calling + * handler->gplugReturnBinary(). + * @param prompt prompt in pam_client format + * @param nonBlocking if true, report whatever is already available, + * otherwise wait for user input. + * @return always true for now + * + * TODO: + * @return if true, the prompt was handled by the talker, otherwise the + * handler has to use libpam_client to obtain the authentication data. + * In that state the talker still can abort the data fetch by + * gplugReturn()ing a null array. When the data was obtained, another + * binaryPrompt with a null prompt will be issued. + */ + virtual bool binaryPrompt( const char *prompt, bool nonBlocking ) = 0; + + /** + * This can either + * - Start a processing cycle. Will be called only when not running. + * - Restart authTok cycle - will be called while running and implies + * revive(). PAM is a bit too clever, so we need this. + * In any case the talker is running afterwards. + */ + virtual void start() = 0; + + /** + * Request to suspend the auth. Make sure that a second talker of any + * type will be able to operate while this one is suspended (no busy + * device nodes, etc.). + * Will be called only if running within Login context. (Actually it + * won't be called at all, but be prepared.) + */ + virtual void suspend() = 0; + + /** + * Request to resume the auth from the point it was suspended at. + * Will be called only when suspended. + */ + virtual void resume() = 0; + + /** + * The "login" button was pressed in the greeter. + * This might call gplugReturn* or gplugStart. + * Will be called only when running. + */ + virtual void next() = 0; + + /** + * Abort auth cycle. Note that this should _not_ clear out already + * entered auth tokens if they are still on the screen. + * Will be called only when running and stops it. + */ + virtual void abort() = 0; + + /** + * Indicate successful end of the current phase. + * This is more or less a request to disable editable widgets + * responsible for the that phase. + * There will be no further attempt to enter that phase until the + * widget is destroyed. + * Will be called only when running and stops it. + */ + virtual void succeeded() = 0; + + /** + * Indicate unsuccessful end of the current phase. + * This is mostly a request to disable all editable widgets. + * The widget will be treated as dead until revive() is called. + * Will be called only when running and stops it. + */ + virtual void failed() = 0; + + /** + * Prepare retrying the previously failed phase. + * This is mostly a request to re-enable all editable widgets failed() + * disabled previously, clear the probably incorrect authentication tokens + * and to set the input focus appropriately. + * Will be called only after failed() (possibly with clear() in between), + * or after presetEntity() with field -1. + */ + virtual void revive() = 0; + + /** + * Clear any edit widgets, particularily anything set by setUser. + * Will be called only when not running. + */ + virtual void clear() = 0; + + /** + * Obtain the QLayoutItem containg the widget(s) to actually handle the + * conversation. See QLayout and QWidgetItem for possible implementations. + */ + QLayoutItem *getLayoutItem() const { return layoutItem; } + +protected: + KGreeterPluginHandler *handler; + QLayoutItem *layoutItem; +}; + +struct KDE_EXPORT kgreeterplugin_info { + /** + * Human readable name of this plugin (should be a little more + * informative than just the libary name). Must be I18N_NOOP()ed. + */ + const char *name; + + /** + * The authentication method to use - the meaning is up to the backend, + * but will usually be related to the PAM service. + */ + const char *method; + + /** + * Capabilities. + */ + enum { + /** + * All users exist on the local system permanently (will be listed + * by getpwent()); an entity corresponds to a UNIX user. + */ + Local = 1, + /** + * The entities consist of multiple fields. + * PluginOptions/<plugin>.FocusField is used instead of FocusPasswd. + */ + Fielded = 2, + /** + * An entity can be preset, the talker has a widget where a user can + * be selected explicitly. If the method is "classic", timed login + * is possible, too. + * This also means that setUser/gplugSetUser can be used and a + * userlist can be shown at all - provided Local is set as well. + */ + Presettable = 4 + }; + + /* + * Capability flags. + */ + int flags; + + /** + * Call after loading the plugin. + * + * @param method if non-empty and the plugin is unable to handle that + * method, return false. If the plugin has a constant method defined + * above, it can ignore this parameter. + * @param getConf can be used to obtain configuration items from the + * greeter; you have to pass it the @p ctx pointer. + * The only predefined key (in KDM) is "EchoMode", which is an int + * (in fact, KPasswordEdit::EchoModes). + * Other keys are obtained from the PluginOptions option; see kdmrc + * for details. + * If the key is unknown, dflt is returned. + * @param ctx context pointer for @p getConf + * @return if false, unload the plugin again (don't call done() first) + */ + bool (*init)( const QString &method, + QVariant (*getConf)( void *ctx, const char *key, + const QVariant &dflt ), + void *ctx ); + + /** + * Call before unloading the plugin. + * This pointer can be null. + */ + void (*done)( void ); + + /** + * Factory method to create an instance of the plugin. + * Note that multiple instances can exist at one time, but only + * one of them is active at any moment (the others would be suspended + * or not running at all). + * @param handler the object offering the necessary callbacks + * @param parent parent widget + * @param predecessor the focus widget before the conversation widget + * @param fixedEntity see below + * @param func see below + * @param ctx see below + * @return an instance of this conversation plugin + * + * Valid combinations of Function and Context: + * - Authenticate:Login - init + * - Authenticate:Shutdown - init, for now "root" is passed as fixedEntitiy + * and it is not supposed to be displayed. Plugins with Local not set + * might have to conjure something up to make getEntity() return a + * canonical entitiy. FIXME: don't restrict shutdown to root. + * - AuthChAuthTok:Login, AuthChAuthTok:Shutdown - cont/cont, + * only relevant for classic method (as it is relevant only for password- + * less logins, which always use classic). The login should not be shown - + * it is known to the user already; the backend won't ask for it, either. + * - ChAuthTok:Login & ChAuthTok:Shutdown - cont + * - Authenticate:Unlock & Authenticate:ExUnlock - init, + * AuthChAuthTok:ChangeTok & AuthChAuthTok:ExChangeTok - init/cont, + * display fixedEntity as labels. The backend does not ask for the UNIX + * login, as it already knows it - but it will ask for all components of + * the entity if it is no UNIX login. + * + * "init" means that the plugin is supposed to call gplugStart, "cont" + * that the backend is already in a cycle of the method the plugin was + * initialized with. + */ + KGreeterPlugin *(*create)( KGreeterPluginHandler *handler, + KdmThemer *themer, + QWidget *parent, QWidget *predecessor, + const QString &fixedEntity, + KGreeterPlugin::Function func, + KGreeterPlugin::Context ctx ); +}; + +#endif |