You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdebase/tdm/kfrontend/kgreeter.cpp

1534 lines
41 KiB

/*
Greeter widget for tdm
Copyright (C) 1997, 1998, 2000 Steffen Hansen <hansen@kde.org>
Copyright (C) 2000-2004 Oswald Buddenhagen <ossi@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "kgreeter.h"
#include "kconsole.h"
#include "tdmconfig.h"
#include "tdmclock.h"
#include "tdm_greet.h"
#include "sakdlg.h"
#include "tdmadmindialog.h"
#include "themer/tdmthemer.h"
#include "themer/tdmitem.h"
#include "themer/tdmlabel.h"
#include <dmctl.h>
#ifdef WITH_TDEHWLIB
#include <ksslcertificate.h>
#include <tdehardwaredevices.h>
#include <tdecryptographiccarddevice.h>
#endif
#include <tdeapplication.h>
#include <tdelocale.h>
#include <kstandarddirs.h>
#include <kseparator.h>
#include <tdelistview.h>
#include <ksimpleconfig.h>
#include <kstringhandler.h>
#include <kdebug.h>
#include <kdialog.h>
#undef Unsorted // x headers suck - make tqdir.h work with --enable-final
#include <tqdir.h>
#include <tqfile.h>
#include <tqbuffer.h>
#include <tqmemarray.h>
#include <tqimage.h>
#include <tqmovie.h>
#include <tqpainter.h>
#include <tqpopupmenu.h>
#include <tqtimer.h>
#include <tqheader.h>
#include <tqstyle.h>
#include <tqlayout.h>
#include <tqlabel.h>
#include <tqpushbutton.h>
#include <tqtooltip.h>
#include <tqaccel.h>
#include <tqstring.h>
#include <tqeventloop.h>
#include <tqbitmap.h>
#include <pwd.h>
#include <grp.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#ifdef HAVE_UTMPX
# include <utmpx.h>
#else
# include <utmp.h>
#endif
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <sys/time.h>
#include <termios.h>
#include <signal.h>
#include <libgen.h>
#include <X11/Xlib.h>
#define FIFO_DIR "/tmp/tdesocket-global/tdm"
#define FIFO_FILE "/tmp/tdesocket-global/tdm/tdmctl-%1"
#define FIFO_SAK_FILE "/tmp/tdesocket-global/tdm/tdmctl-sak-%1"
class UserListView : public TDEListView {
public:
UserListView( bool _them, TQWidget *parent = 0, const char *name = 0 )
: TDEListView( parent, name )
, themed(_them), cachedSizeHint( -1, 0 )
{
setSizePolicy( TQSizePolicy::Preferred, TQSizePolicy::Ignored );
header()->hide();
addColumn( TQString::null );
setColumnAlignment( 0, AlignVCenter );
setResizeMode( TQListView::LastColumn );
// FIXME: This must be configurable, so disable
// painting of list background for now.
// if (themed) {
// setBackgroundMode( TQt::NoBackground );
// viewport()->setBackgroundMode( TQt::NoBackground );
// setFrameStyle( TQFrame::NoFrame );
// }
}
bool themed;
mutable TQSize cachedSizeHint;
int sumHeight() const
{
int sum = 0;
for (TQListViewItem *itm = firstChild(); itm; itm = itm->nextSibling()) {
sum += itm->height();
}
return sum;
}
public:
virtual TQSize sizeHint() const
{
if (themed)
return TDEListView::sizeHint();
if (!cachedSizeHint.isValid()) {
constPolish();
uint maxw = 0;
for (TQListViewItem *itm = firstChild(); itm; itm = itm->nextSibling()) {
uint thisw = itm->width( fontMetrics(), this, 0 );
if (thisw > maxw)
maxw = thisw;
}
cachedSizeHint.setWidth(
style().pixelMetric( TQStyle::PM_ScrollBarExtent ) +
frameWidth() * 2 + maxw );
}
return cachedSizeHint;
}
virtual void paintEmptyArea ( TQPainter * p, const TQRect & rect )
{
if (!themed)
return TDEListView::paintEmptyArea(p, rect );
// FIXME: This must be configurable, so disable
// painting of list background for now.
return TDEListView::paintEmptyArea(p, rect );
const TQPixmap *pm = paletteBackgroundPixmap();
if (!pm || pm->isNull()) {
return;
}
kdDebug() << "paintEmpty " << rect << endl;
TQRect devRect = p->xForm( rect );
kdDebug() << "paintEmpty2 " << devRect << endl;
p->drawPixmap(0, 0, *pm, devRect.left(), devRect.top() );
}
TQPixmap background;
};
int KGreeter::curPlugin = -1;
PluginList KGreeter::pluginList;
KGreeter::KGreeter( bool framed )
: inherited( framed )
, dName( dname )
, userView( 0 )
, userList( 0 )
, nNormals( 0 )
, nSpecials( 0 )
, curPrev( -1 )
, curSel( -1 )
, prevValid( true )
, needLoad( false )
, themed( framed )
, showInfoMessages( true )
, closingDown( false )
{
stsFile = new KSimpleConfig( _stsFile );
stsFile->setGroup( "PrevUser" );
if (_userList) {
readFacesList();
userView = new UserListView( framed, this );
connect( userView, TQ_SIGNAL(clicked( TQListViewItem * )),
TQ_SLOT(slotUserClicked( TQListViewItem * )) );
connect( userView, TQ_SIGNAL(doubleClicked( TQListViewItem * )),
TQ_SLOT(accept()) );
}
if (_userCompletion) {
userList = new TQStringList;
}
sessMenu = new TQPopupMenu( this );
connect( sessMenu, TQ_SIGNAL(activated( int )),
TQ_SLOT(slotSessionSelected( int )) );
insertSessions();
if (curPlugin < 0) {
curPlugin = 0;
pluginList = KGVerify::init( _pluginsLogin );
}
mControlPipeHandlerThread = new TQEventLoopThread();
mControlPipeHandler = new ControlPipeHandlerObject();
mControlPipeHandler->mKGreeterParent = this;
mControlPipeHandler->moveToThread(mControlPipeHandlerThread);
TQObject::connect(mControlPipeHandler, TQ_SIGNAL(processCommand(TQString)), this, TQ_SLOT(processInputPipeCommand(TQString)));
TQTimer::singleShot(0, mControlPipeHandler, TQ_SLOT(run()));
mControlPipeHandlerThread->start();
}
KGreeter::~KGreeter()
{
mControlPipeHandlerThread->terminate();
mControlPipeHandlerThread->wait();
delete mControlPipeHandler;
// delete mControlPipeHandlerThread;
hide();
delete userList;
delete verify;
delete stsFile;
}
void KGreeter::cryptographicCardWatcherSetup() {
cardLoginUser = TQString::null;
#ifdef WITH_TDEHWLIB
// Initialize SmartCard readers
TDEGenericDevice *hwdevice;
TDEHardwareDevices *hwdevices = TDEGlobal::hardwareDevices();
TDEGenericHardwareList cardReaderList = hwdevices->listByDeviceClass(TDEGenericDeviceType::CryptographicCard);
for (hwdevice = cardReaderList.first(); hwdevice; hwdevice = cardReaderList.next()) {
TDECryptographicCardDevice* cdevice = static_cast<TDECryptographicCardDevice*>(hwdevice);
connect(cdevice, TQ_SIGNAL(certificateListAvailable(TDECryptographicCardDevice*)), this, TQ_SLOT(cryptographicCardInserted(TDECryptographicCardDevice*)));
connect(cdevice, TQ_SIGNAL(cardRemoved(TDECryptographicCardDevice*)), this, TQ_SLOT(cryptographicCardRemoved(TDECryptographicCardDevice*)));
cdevice->enableCardMonitoring(true);
}
#endif
}
void KGreeter::done(int r) {
closingDown = true;
inherited::done(r);
}
void KGreeter::processInputPipeCommand(TQString command) {
command = command.replace('\n', "");
TQStringList commandList = TQStringList::split('\t', command, false);
if ((*(commandList.at(0))) == "LOGIN") {
if (verify) {
verify->setUser( (*(commandList.at(1))) );
verify->setPassword( (*(commandList.at(2))) );
accept();
}
}
}
void KGreeter::readFacesList()
{
FILE *f = fopen( TQFile::encodeName( _faceDir + "/.randomlist" ), "rt" );
if ( !f )
return;
TQTextIStream is( f );
while ( !is.eof() )
{
TQString line = is.readLine().simplifyWhiteSpace();
if ( line.isEmpty() )
continue;
TQString icon;
int index = line.find( ' ' );
if ( index > 0 ) {
icon = line.left( index );
line = line.mid( index );
} else {
icon = line;
line = TQString::null;
}
randomFaces.push_back( icon );
TQStringList list = TQStringList::split( ' ', line );
for ( TQStringList::ConstIterator it = list.begin(); it != list.end(); ++it )
randomFacesMap[*it] = icon;
}
}
class UserListViewItem : public TDEListViewItem {
public:
UserListViewItem( UserListView *parent, const TQString &text,
const TQPixmap &pixmap, const TQString &username )
: TDEListViewItem( parent )
, login( username )
{
setPixmap( 0, pixmap );
setMultiLinesEnabled( true );
setText( 0, text );
parent->cachedSizeHint.setWidth( -1 );
}
virtual void paintCell(TQPainter *p, const TQColorGroup &cg, int column, int width, int alignment)
{
if (((UserListView*)listView())->themed)
TQListViewItem::paintCell(p, cg, column, width, alignment);
else
TDEListViewItem::paintCell(p, cg, column, width, alignment);
}
TQString login;
};
#define FILE_LIMIT_ICON 20
#define FILE_LIMIT_IMAGE 200
void
KGreeter::insertUser( const TQImage &default_pix,
const TQString &username, struct passwd *ps )
{
if (setegid( ps->pw_gid ))
return;
if (seteuid( ps->pw_uid )) {
setegid(0);
return;
}
if (userList) {
userList->append( username );
}
if (!userView) {
seteuid(0);
setegid(0);
return;
}
int dp = 0, nd = 0;
if (_faceSource == FACE_USER_ONLY ||
_faceSource == FACE_PREFER_USER)
dp = 1;
if (_faceSource != FACE_USER_ONLY &&
_faceSource != FACE_ADMIN_ONLY)
nd = 1;
TQImage p;
do {
dp ^= 1;
TQCString fn = !dp ?
TQCString( ps->pw_dir ) + '/' :
TQCString(TQFile::encodeName( _faceDir + '/' + username ));
fn += ".face.icon";
int fd, ico;
if ((fd = open( fn.data(), O_RDONLY | O_NONBLOCK )) < 0) {
fn.truncate( fn.length() - 5 );
if ((fd = open( fn.data(), O_RDONLY | O_NONBLOCK )) < 0) {
continue;
}
ico = 0;
} else
ico = 1;
TQFile f;
f.open( IO_ReadOnly, fd );
int fs = f.size();
if (fs > (ico ? FILE_LIMIT_ICON : FILE_LIMIT_IMAGE) * 1000) {
LogWarn( "%s exceeds file size limit (%dkB)\n", fn.data(), ico ? FILE_LIMIT_ICON : FILE_LIMIT_IMAGE );
continue;
}
TQByteArray fc( fs );
int rfs = f.readBlock( fc.data(), fs );
::close( fd );
fc.resize( rfs > 0 ? rfs : 0 );
TQBuffer buf( fc );
buf.open( IO_ReadOnly );
TQImageIO ir;
ir.setIODevice( &buf );
if (!ir.read()) {
LogInfo( "%s is no valid image\n", fn.data() );
continue;
}
p = ir.image();
TQSize ns( 48, 48 );
if (p.size() != ns) {
p = p.convertDepth( 32 ).smoothScale( ns, TQImage::ScaleMin );
}
break;
} while (--nd >= 0);
if ( p.isNull() && randomFaces.count() ) {
TQString randomFace = randomFacesMap[username];
if ( randomFace.isNull() ) {
TQStringList::size_type index = 0;
for ( size_t i = 0; i < username.length(); ++i )
index += ( 0x7f - username.at( i ).latin1() ) % 37;
randomFace = randomFaces[ index % randomFaces.count() ];
}
p.load( _faceDir + "/../pics/users/" + randomFace + ".png" );
}
if ( p.isNull() ) {
p = default_pix;
}
TQString realname = KStringHandler::from8Bit( ps->pw_gecos );
realname.truncate( realname.find( ',' ) );
if (realname.isEmpty() || realname == username)
new UserListViewItem( userView, username, TQPixmap( p ), username );
else {
realname.append( "\n" ).append( username );
new UserListViewItem( userView, realname, TQPixmap( p ), username );
}
seteuid( 0 );
setegid( 0 );
}
class KCStringList : public TQValueList<TQCString> {
public:
bool contains( const char *str ) const
{
for (ConstIterator it = begin(); it != end(); ++it)
if (*it == str)
return true;
return false;
}
};
class KGreeterUserList {
public:
KGreeterUserList( char **in );
bool hasUser( const char *str ) const { return users.contains( str ); }
bool hasGroup( gid_t gid ) const
{ return groups.find( gid ) != groups.end(); }
bool hasGroups() const { return !groups.isEmpty(); }
KCStringList users;
private:
TQValueList<gid_t> groups;
};
KGreeterUserList::KGreeterUserList( char **in )
{
struct group *grp;
for (; *in; in++)
if (**in == '@') {
if ((grp = getgrnam( *in + 1 ))) {
for (; *grp->gr_mem; grp->gr_mem++)
users.append( *grp->gr_mem );
groups.append( grp->gr_gid );
}
} else
users.append( *in );
}
void
KGreeter::insertUsers(int limit_users)
{
struct passwd *ps;
if (!(ps = getpwnam( "nobody" ))) {
return;
}
TQImage default_pix;
if (userView) {
if (!default_pix.load( _faceDir + "/.default.face.icon" ))
if (!default_pix.load( _faceDir + "/.default.face" ))
LogError( "Can't open default user face\n" );
TQSize ns( 48, 48 );
if (default_pix.size() != ns)
default_pix =
default_pix.convertDepth( 32 ).smoothScale( ns, TQImage::ScaleMin );
}
if (_showUsers == SHOW_ALL) {
KGreeterUserList noUsers( _noUsers );
TQDict<int> dupes( 1000 ); // Potential crash risk with buffer overrun?
TQStringList toinsert;
int count = 0;
for (setpwent(); (ps = getpwent()) != 0;) {
if (*ps->pw_dir && *ps->pw_shell &&
((ps->pw_uid >= (unsigned)_lowUserId) ||
((!ps->pw_uid) && _showRoot)) &&
(ps->pw_uid <= (unsigned)_highUserId) &&
(!noUsers.hasUser( ps->pw_name )) &&
(!noUsers.hasGroup( ps->pw_gid )))
{
TQString username( TQFile::decodeName( ps->pw_name ) );
if (!dupes.find( username )) {
dupes.insert( username, (int *)-1 );
toinsert.append( username );
if ( limit_users >= 0 && ++count > limit_users )
break;
}
}
}
// FIXME: OpenSUSE added this code
// For some reason it does not allow LDAP users to be listed (!),
// therefore it was deactivated. It should be repaired and reactivated.
// if ( limit_users >= 0 && ++count > limit_users ) {
// utmpname( _PATH_WTMP );
// setutxent();
// toinsert = TQStringList();
// dupes.clear();
//
// for ( count = 0; count < limit_users; ) {
// struct utmpx * ent = getutxent();
// if ( !ent )
// break;
// struct passwd *ps = getpwnam( ent->ut_user );
// if (ps && *ps->pw_dir && *ps->pw_shell &&
// (ps->pw_uid >= (unsigned)_lowUserId ||
// !ps->pw_uid && _showRoot) &&
// ps->pw_uid <= (unsigned)_highUserId &&
// !noUsers.hasUser( ps->pw_name ) &&
// !noUsers.hasGroup( ps->pw_gid ))
// {
// TQString username( TQFile::decodeName( ent->ut_user ) );
// if (!dupes.find( username )) {
// dupes.insert( username, (int *)-1 );
// toinsert.append( username );
// count++;
// }
// }
//
//
// }
// endutxent();
// }
for ( TQStringList::ConstIterator it = toinsert.begin();
it != toinsert.end(); ++it )
{
// pretty stupid to do another lookup round, but the number is limited
// and caching struct passwd is pretty ugly
struct passwd *ps = getpwnam( TQFile::encodeName( *it ) );
if ( ps )
insertUser( default_pix, *it, ps );
}
} else {
KGreeterUserList users( _users );
if (users.hasGroups()) {
TQDict<int> dupes( 1000 );
for (setpwent(); (ps = getpwent()) != 0;) {
if (*ps->pw_dir && *ps->pw_shell &&
(ps->pw_uid >= (unsigned)_lowUserId ||
((!ps->pw_uid) && _showRoot)) &&
ps->pw_uid <= (unsigned)_highUserId &&
(users.hasUser( ps->pw_name ) ||
users.hasGroup( ps->pw_gid )))
{
TQString username( TQFile::decodeName( ps->pw_name ) );
if (!dupes.find( username )) {
dupes.insert( username, (int *)-1 );
insertUser( default_pix, username, ps );
}
}
}
} else {
KCStringList::ConstIterator it = users.users.begin();
for (; it != users.users.end(); ++it)
if ((ps = getpwnam( (*it).data() )) &&
(ps->pw_uid || _showRoot))
insertUser( default_pix, TQFile::decodeName( *it ), ps );
}
}
endpwent();
if (_sortUsers) {
if (userView)
userView->sort();
if (userList)
userList->sort();
}
}
void
KGreeter::putSession( const TQString &type, const TQString &name, bool hid, const char *exe )
{
int prio = exe ? (!strcmp( exe, "default" ) ? 0 :
!strcmp( exe, "custom" ) ? 1 :
!strcmp( exe, "failsafe" ) ? 3 : 2) : 2;
for (uint i = 0; i < sessionTypes.size(); i++)
if (sessionTypes[i].type == type) {
sessionTypes[i].prio = prio;
return;
}
sessionTypes.append( SessType( name, type, hid, prio ) );
}
void
KGreeter::insertSessions()
{
for (char **dit = _sessionsDirs; *dit; ++dit) {
TQStringList ents = TQDir( *dit ).entryList();
for (TQStringList::ConstIterator it = ents.begin(); it != ents.end(); ++it)
if ((*it).endsWith( ".desktop" ) && !(*it).endsWith("admin.desktop")) {
KSimpleConfig dsk( TQString( *dit ).append( '/' ).append( *it ) );
dsk.setGroup( "Desktop Entry" );
putSession( (*it).left( (*it).length() - 8 ),
dsk.readEntry( "Name" ),
(dsk.readBoolEntry( "Hidden", false ) ||
(dsk.hasKey( "TryExec" ) &&
TDEStandardDirs::findExe( dsk.readEntry( "TryExec" ) ).isEmpty())),
dsk.readEntry( "Exec" ).latin1() );
}
}
putSession( "default", i18n("Default"), false, "default" );
putSession( "custom", i18n("Custom"), false, "custom" );
putSession( "failsafe", i18n("Failsafe"), false, "failsafe" );
qBubbleSort( sessionTypes );
for (uint i = 0; i < sessionTypes.size() && !sessionTypes[i].hid; i++) {
sessMenu->insertItem( sessionTypes[i].name, i );
switch (sessionTypes[i].prio) {
case 0: case 1: nSpecials++; break;
case 2: nNormals++; break;
}
}
}
void
KGreeter::slotUserEntered()
{
if (userView) {
TQListViewItem *item;
for (item = userView->firstChild(); item; item = item->nextSibling()) {
if (((UserListViewItem *)item)->login == curUser) {
userView->setSelected( item, true );
userView->ensureItemVisible( item );
goto oke;
}
}
userView->clearSelection();
}
oke:
if (isVisible())
slotLoadPrevWM();
else
TQTimer::singleShot( 0, this, TQ_SLOT(slotLoadPrevWM()) );
}
void
KGreeter::slotUserClicked( TQListViewItem *item )
{
if (item) {
curUser = ((UserListViewItem *)item)->login;
verify->setUser( curUser );
slotLoadPrevWM();
}
}
void
KGreeter::slotSessionSelected( int id )
{
if (id != curSel) {
sessMenu->setItemChecked( curSel, false );
sessMenu->setItemChecked( id, true );
curSel = id;
verify->gplugActivity();
}
}
void
KGreeter::reject()
{
verify->reject();
}
void
KGreeter::accept()
{
if (userView && userView->hasFocus())
slotUserClicked( userView->currentItem() );
else
verify->accept();
}
void // private
KGreeter::setPrevWM( int wm )
{
if (curPrev != wm) {
if (curPrev != -1) {
sessMenu->changeItem( curPrev, sessionTypes[curPrev].name );
}
if (wm != -1) {
sessMenu->changeItem( wm, sessionTypes[wm].name + i18n(" (previous)") );
}
curPrev = wm;
}
}
void
KGreeter::slotLoadPrevWM()
{
int len, i, b;
unsigned long crc, by;
TQCString name;
char *sess;
if (verify->coreLock) {
needLoad = true;
return;
}
needLoad = false;
prevValid = true;
name = curUser.local8Bit();
GSendInt( G_ReadDmrc );
GSendStr( name.data() );
GRecvInt(); // ignore status code ...
if ((len = name.length())) {
GSendInt( G_GetDmrc );
GSendStr( "Session" );
sess = GRecvStr();
if (!sess) { /* no such user */
if (!userView && !userList) { // don't fake if user list shown
prevValid = false;
/* simple crc32 */
for (crc = _forgingSeed, i = 0; i < len; i++) {
by = (crc & 255) ^ name[i];
for (b = 0; b < 8; b++)
by = (by >> 1) ^ (-(by & 1) & 0xedb88320);
crc = (crc >> 8) ^ by;
}
/* forge a session with this hash - default & custom more probable */
/* XXX - this should do a statistical analysis of the real users */
#if 1
setPrevWM( crc % (nSpecials * 2 + nNormals) % (nSpecials + nNormals) );
#else
i = crc % (nSpecials * 2 + nNormals);
if (i < nNormals)
setPrevWM( i + nSpecials );
else
setPrevWM( (i - nNormals) / 2 );
#endif
return;
}
} else {
if (!strcmp(sess, "admin")) {
// need to get the original
GSendInt( G_GetDmrc);
GSendStr( "OrigSession");
sess = GRecvStr();
if (!sess) {
free(sess);
sess = strdup("default");
}
}
for (uint i = 0; i < sessionTypes.count() && !sessionTypes[i].hid; i++) {
if (sessionTypes[i].type == sess) {
free( sess );
setPrevWM( i );
curWMSession = sessionTypes[i].type.utf8();
return;
}
}
if (curSel == -1) {
MsgBox( sorrybox, i18n("Your saved session type '%1' is not valid any more.\n"
"Please select a new one, otherwise 'default' will be used.").arg( sess ) );
}
free( sess );
prevValid = false;
}
}
setPrevWM( -1 );
}
void // protected
KGreeter::pluginSetup()
{
int field = 0;
TQString ent, pn( verify->pluginName() ), dn( dName + '_' + pn );
if (_preselUser != PRESEL_PREV)
stsFile->deleteEntry( verify->entitiesLocal() ? dName : dn, false );
if (_preselUser != PRESEL_NONE && verify->entityPresettable()) {
if (verify->entitiesLocal())
ent = _preselUser == PRESEL_PREV ?
stsFile->readEntry( dName ) : _defaultUser;
else
ent = _preselUser == PRESEL_PREV ?
stsFile->readEntry( dn ) :
verify->getConf( 0, (pn + ".DefaultEntity").latin1(), TQVariant() ).toString();
field = verify->entitiesFielded() ?
verify->getConf( 0, (pn + ".FocusField").latin1(), TQVariant( 0 ) ).toInt() :
_focusPasswd;
}
verify->presetEntity( ent, field );
if (userList)
verify->loadUsers( *userList );
}
void
KGreeter::verifyPluginChanged( int id )
{
curPlugin = id;
pluginSetup();
}
void
KGreeter::verifyClear()
{
curUser = TQString::null;
slotUserEntered();
slotSessionSelected( -1 );
}
void
KGreeter::verifyOk()
{
if (_preselUser == PRESEL_PREV && verify->entityPresettable())
stsFile->writeEntry( verify->entitiesLocal() ?
dName :
dName + '_' + verify->pluginName(),
verify->getEntity() );
if (curSel != -1) {
GSendInt( G_PutDmrc );
GSendStr( "Session" );
GSendStr( sessionTypes[curSel].type.utf8() );
curWMSession = sessionTypes[curSel].type.utf8();
} else if (!prevValid) {
GSendInt( G_PutDmrc );
GSendStr( "Session" );
GSendStr( "default" );
}
GSendInt( G_Ready );
closingDown = true;
done( ex_exit );
}
void
KGreeter::verifyFailed()
{
if (needLoad) {
slotLoadPrevWM();
}
}
void
KGreeter::verifySetUser( const TQString &user )
{
curUser = user;
slotUserEntered();
}
void KGreeter::cryptographicCardInserted(TDECryptographicCardDevice* cdevice) {
#ifdef WITH_TDEHWLIB
#ifndef HAVE_KRB5
// Don't enable card-based logins if Kerberos integration was disabled
return;
#endif
// Make sure card logins are enabled before attempting one
KSimpleConfig *systemconfig = new KSimpleConfig( TQString::fromLatin1( KDE_CONFDIR "/ldap/ldapconfigrc" ));
systemconfig->setGroup(NULL);
bool enabled = systemconfig->readBoolEntry("EnablePKCS11Login", false);
delete systemconfig;
if (!enabled)
{
return;
}
TQString login_name = TQString::null;
X509CertificatePtrList certList = cdevice->cardX509Certificates();
if (certList.count() > 0) {
KSSLCertificate* card_cert = NULL;
card_cert = KSSLCertificate::fromX509(certList[0]);
TQStringList cert_subject_parts = TQStringList::split("/", card_cert->getSubject(), false);
for (TQStringList::Iterator it = cert_subject_parts.begin(); it != cert_subject_parts.end(); ++it ) {
TQString lcpart = (*it).lower();
if (lcpart.startsWith("cn=")) {
login_name = lcpart.right(lcpart.length() - strlen("cn="));
}
}
delete card_cert;
}
if (login_name != "") {
if (verify->cardLoginInProgress) {
return;
}
verify->cardLoginInProgress = true;
verify->cardLoginDevice = cdevice;
DM dm;
SessList sess;
bool vt_active = false;
bool user_active = false;
if (dm.localSessions(sess)) {
TQString user, loc;
for (SessList::ConstIterator it = sess.begin(); it != sess.end(); ++it) {
DM::sess2Str2(*it, user, loc);
if (user.startsWith(login_name + ": ")) {
// Found active session
user_active = true;
}
if ((*it).self) {
if ((*it).vt == dm.activeVT()) {
vt_active = true;
}
}
}
}
if (!user_active && vt_active) {
// Select the correct user
verify->setUser(login_name);
if (userView) {
userView->setEnabled(false);
}
verifySetUser(login_name);
verify->lockUserEntry(true);
// FIXME
// pam_pkcs11 is extremely chatty with no apparent way to disable the unwanted messages
verify->setInfoMessageDisplay(false);
// Set up password prompt
cardLoginUser = login_name;
verify->setPasswordPrompt(i18n("PIN:"));
// Bypass initial password prompt
verify->start();
verify->setPassword("");
verify->accept();
}
}
#endif
}
void KGreeter::cryptographicCardRemoved(TDECryptographicCardDevice* cdevice) {
#ifdef WITH_TDEHWLIB
cardLoginUser = TQString::null;
userView->setEnabled(false);
verify->lockUserEntry(false);
verify->requestAbort();
verify->setPasswordPrompt(TQString::null);
// Restore information message display settings
verify->setInfoMessageDisplay(showInfoMessages);
verify->cardLoginInProgress = false;
verify->cardLoginDevice = NULL;
#endif
}
KStdGreeter::KStdGreeter()
: KGreeter()
, clock( 0 )
, pixLabel( 0 )
{
TQBoxLayout *main_box;
#ifdef WITH_TDM_XCONSOLE
if (consoleView) {
TQBoxLayout *ex_box = new TQVBoxLayout( this, 10, 10 );
main_box = new TQHBoxLayout( ex_box, 10 );
ex_box->addWidget( consoleView );
} else
#endif
main_box = new TQHBoxLayout( this, 10, 10 );
if (userView)
main_box->addWidget( userView );
TQBoxLayout *inner_box = new TQVBoxLayout( main_box, 10 );
if (!_authorized && _authComplain) {
TQLabel *complainLabel = new TQLabel(
i18n("Warning: this is an unsecured session"), this );
TQToolTip::add( complainLabel,
i18n("This display requires no X authorization.\n"
"This means that anybody can connect to it,\n"
"open windows on it or intercept your input.") );
complainLabel->setAlignment( AlignCenter );
complainLabel->setFont( _failFont );
complainLabel->setPaletteForegroundColor( TQt::red );
inner_box->addWidget( complainLabel );
}
if (_logoArea == LOGO_NONE) {
KSMModalDialogHeader *theader = new KSMModalDialogHeader(this);
inner_box->addWidget( theader, AlignCenter );
}
if (!_greetString.isEmpty()) {
TQLabel *welcomeLabel = new TQLabel( _greetString, this );
welcomeLabel->setAlignment( AlignCenter );
welcomeLabel->setFont( _greetFont );
inner_box->addWidget( welcomeLabel );
if (_logoArea == LOGO_NONE) {
// Match caps lock warning font size
TQLabel *spacerLabel = new TQLabel( " ", this );
spacerLabel->setFont( _failFont );
inner_box->addWidget( spacerLabel, AlignCenter );
}
}
switch (_logoArea) {
case LOGO_CLOCK:
clock = new KdmClock( this, "clock" );
break;
case LOGO_LOGO:
{
TQMovie movie( _logo );
kapp->eventLoop()->processEvents( TQEventLoop::ExcludeUserInput | TQEventLoop::ExcludeSocketNotifiers, 100 );
TQPixmap pixmap;
if (!movie.framePixmap().isNull() || pixmap.load( _logo )) {
pixLabel = new TQLabel( this );
if (!movie.framePixmap().isNull()) {
pixLabel->setMovie( movie );
if (!movie.framePixmap().hasAlpha())
pixLabel->setFrameStyle( TQFrame::Panel | TQFrame::Sunken );
} else {
pixLabel->setPixmap( pixmap );
if (!pixmap.hasAlpha())
pixLabel->setFrameStyle( TQFrame::Panel | TQFrame::Sunken );
}
pixLabel->setIndent( 0 );
}
}
break;
}
if (userView) {
if (clock)
inner_box->addWidget( clock, 0, AlignCenter );
else if (pixLabel)
inner_box->addWidget( pixLabel, 0, AlignCenter );
} else {
if (clock)
main_box->addWidget( clock, 0, AlignCenter );
else if (pixLabel)
main_box->addWidget( pixLabel, 0, AlignCenter );
}
goButton = new TQPushButton( i18n("L&ogin"), this );
goButton->setDefault( true );
connect( goButton, TQ_SIGNAL(clicked()), TQ_SLOT(accept()) );
menuButton = new TQPushButton( i18n("&Menu"), this );
//helpButton
TQWidget *prec;
if (userView)
prec = userView;
#ifdef WITH_TDM_XCONSOLE
else if (consoleView)
prec = consoleView;
#endif
else
prec = menuButton;
KGStdVerify *sverify =
new KGStdVerify( this, this, prec, TQString::null,
pluginList, KGreeterPlugin::Authenticate,
KGreeterPlugin::Login );
inner_box->addLayout( sverify->getLayout() );
TQPopupMenu *plugMenu = sverify->getPlugMenu();
sverify->selectPlugin( curPlugin );
verify = sverify;
inner_box->addWidget( new KSeparator( KSeparator::HLine, this ) );
TQBoxLayout *hbox2 = new TQHBoxLayout( inner_box, 10 );
hbox2->addWidget( goButton );
hbox2->addStretch( 1 );
hbox2->addWidget( menuButton );
hbox2->addStretch( 1 );
if (sessMenu->count() > 1) {
inserten( i18n("Session &Type"), 0, sessMenu );
needSep = true;
}
if (plugMenu) {
inserten( i18n("&Authentication Method"), 0, plugMenu );
needSep = true;
}
#ifdef XDMCP
completeMenu( LOGIN_LOCAL_ONLY, ex_choose, i18n("&Remote Login"), 0 );
#else
completeMenu();
#endif
if (userView || userList)
insertUsers();
if (optMenu)
menuButton->setPopup( optMenu );
else
menuButton->hide();
pluginSetup();
verify->setInfoMessageDisplay(showInfoMessages);
verify->start();
TQTimer::singleShot(0, this, TQ_SLOT(cryptographicCardWatcherSetup()));
}
void
KStdGreeter::pluginSetup()
{
inherited::pluginSetup();
if (userView) {
if (verify->entitiesLocal() && verify->entityPresettable())
userView->show();
else
userView->hide();
}
adjustGeometry();
update();
}
void
KStdGreeter::verifyFailed()
{
goButton->setEnabled( false );
menuButton->setEnabled( false );
if (userView)
userView->setEnabled( false );
inherited::verifyFailed();
}
void
KStdGreeter::verifyRetry()
{
goButton->setEnabled( true );
menuButton->setEnabled( true );
if (userView)
userView->setEnabled( true );
}
KThemedGreeter::KThemedGreeter()
: KGreeter( true )
, themer( 0 )
// , clock( 0 )
{
// We do all painting ourselves
setBackgroundMode( NoBackground );
// Allow tracking the mouse position
setMouseTracking( true );
adjustGeometry();
themer = new KdmThemer( _theme, "console", this );
if (!themer->isOK()) {
delete themer;
themer = 0;
return;
}
connect( themer, TQ_SIGNAL(activated( const TQString & )),
TQ_SLOT(slotThemeActivated( const TQString & )) );
console_rect = themer->findNode( "xconsole" ); // tdm ext
userlist_rect = themer->findNode( "userlist" );
caps_warning = themer->findNode( "caps-lock-warning" );
xauth_warning = themer->findNode( "xauth-warning" ); // tdm ext
pam_error = themer->findNode( "pam-error" );
timed_label = themer->findNode( "timed-label" );
if (pam_error && pam_error->isA( "KdmLabel" )) {
static_cast<KdmLabel*>(pam_error)->setText( i18n("Login Failed.") );
}
KdmItem *itm;
if ((itm = themer->findNode( "pam-message" ))) // done via msgboxes
itm->hide( true );
if ((itm = themer->findNode( "language_button" ))) // not implemented yet
itm->hide( true );
#ifdef WITH_TDM_XCONSOLE
if (console_rect) {
if (consoleView)
console_rect->setWidget( consoleView );
else
console_rect->hide( true );
}
#endif
if (xauth_warning && (_authorized || !_authComplain))
xauth_warning->hide( true );
if (userView || userList)
insertUsers( 7 ); // TODO: find out how many are a good value
// if (!_greetString.isEmpty()) {
// }
// clock = new KdmClock( this, "clock" );
TQWidget *prec;
if (userView)
prec = userView;
#ifdef WITH_TDM_XCONSOLE
else if (consoleView)
prec = consoleView;
#endif
else
prec = 0;
KGThemedVerify *tverify =
new KGThemedVerify( this, themer, this, prec, TQString::null,
pluginList, KGreeterPlugin::Authenticate,
KGreeterPlugin::Login );
TQPopupMenu *plugMenu = tverify->getPlugMenu();
tverify->selectPlugin( curPlugin );
verify = tverify;
session_button = 0;
if ((itm = themer->findNode( "session_button" ))) {
if (sessMenu->count() <= 1)
itm->hide( true );
else
session_button = itm;
} else {
if (sessMenu->count() > 1) {
inserten( i18n("Session &Type"), ALT+Key_T, sessMenu );
needSep = true;
}
}
admin_button = themer->findNode( "admin_button");
if ( admin_button ) {
if ( !_useAdminSession )
admin_button->hide( true );
}
if (plugMenu) {
inserten( i18n("&Authentication Method"), ALT+Key_A, plugMenu );
needSep = true;
}
#ifdef XDMCP
completeMenu( LOGIN_LOCAL_ONLY, ex_choose, i18n("&Remote Login"), ALT+Key_R );
#else
completeMenu();
#endif
system_button = themer->findNode( "system_button" );
TQAccel *accel = new TQAccel( this );
accel->insertItem( ALT+Key_M, 0 );
connect( accel, TQ_SIGNAL(activated( int )), TQ_SLOT(slotActionMenu()) );
pluginSetup();
verify->setInfoMessageDisplay(showInfoMessages);
verify->start();
TQTimer::singleShot(0, this, TQ_SLOT(cryptographicCardWatcherSetup()));
}
bool
KThemedGreeter::event( TQEvent *e )
{
if (themer)
themer->widgetEvent( e );
return inherited::event( e );
}
void
KThemedGreeter::pluginSetup()
{
inherited::pluginSetup();
if (userView && verify->entitiesLocal() && verify->entityPresettable() && userlist_rect) {
// userView->setMaximumHeight( userView->sumHeight() );
userlist_rect->setWidget( userView );
} else {
if (userView)
userView->hide();
if (userlist_rect)
userlist_rect->hide( true );
}
update();
}
void
KThemedGreeter::verifyFailed()
{
// goButton->setEnabled( false );
inherited::verifyFailed();
if (userView)
userView->setEnabled(false);
}
void
KThemedGreeter::verifyRetry()
{
// goButton->setEnabled( true );
if (userView)
userView->setEnabled(true);
}
TQString KThemedGreeter::timedUser = TQString::null;
int KThemedGreeter::timedDelay = -1;
void
KThemedGreeter::updateStatus( bool fail, bool caps, int timedleft )
{
if (pam_error) {
if (fail)
pam_error->show( true );
else
pam_error->hide( true );
}
if (caps_warning) {
if (caps)
caps_warning->show( true );
else
caps_warning->hide( true );
}
if (timed_label) {
if (timedleft) {
if (timedleft != timedDelay) {
timedDelay = timedleft;
timedUser = curUser;
timed_label->show( true );
timed_label->update();
}
} else {
timedDelay = -1;
timed_label->hide( true );
}
}
#ifdef WITH_TDEHWLIB
if (cardLoginUser != TQString::null) {
verify->setPasswordPrompt(i18n("PIN:"));
}
#endif
}
void
KThemedGreeter::slotThemeActivated( const TQString &id )
{
if (id == "login_button")
accept();
else if (id == "session_button")
slotSessMenu();
else if (id == "system_button")
slotActionMenu();
else if (id == "admin_button")
slotAskAdminPassword();
}
void
KThemedGreeter::slotSessMenu()
{
sessMenu->popup( mapToGlobal( session_button->rect().center() ) );
}
void
KThemedGreeter::slotActionMenu()
{
if (system_button)
optMenu->popup( mapToGlobal( system_button->rect().center() ) );
else
optMenu->popup( mapToGlobal( rect().center() ) );
}
void
KThemedGreeter::keyPressEvent( TQKeyEvent *e )
{
inherited::keyPressEvent( e );
if (!(e->state() & KeyButtonMask) &&
(e->key() == Key_Return || e->key() == Key_Enter))
accept();
}
void
KThemedGreeter::slotAskAdminPassword()
{
TDMAdmin k(curUser, this);
if (k.exec()) {
GSendInt(G_Ready);
hide();
closingDown = true;
done(ex_exit);
}
}
//===========================================================================
//
// TDM control pipe handler
//
ControlPipeHandlerObject::ControlPipeHandlerObject() : TQObject() {
mPipe_fd = -1;
mKGreeterParent = NULL;
mSAKDlgParent = NULL;
}
ControlPipeHandlerObject::~ControlPipeHandlerObject() {
if (mPipe_fd != -1) {
if (mKGreeterParent) mKGreeterParent->closingDown = true;
if (mSAKDlgParent) mSAKDlgParent->closingDown = true;
::close(mPipe_fd);
::unlink(mPipeFilename.ascii());
}
}
void ControlPipeHandlerObject::run(void) {
while (1) {
if ((mKGreeterParent && (mKGreeterParent->closingDown)) || (mSAKDlgParent && (mSAKDlgParent->closingDown))) {
::unlink(mPipeFilename.ascii());
TQApplication::eventLoop()->exit(-1);
return;
}
if ((mKGreeterParent && (mKGreeterParent->isShown())) || (mSAKDlgParent && (mSAKDlgParent->isShown()))) {
char readbuf[2048];
int displayNumber;
TQString currentDisplay;
currentDisplay = TQString(getenv("DISPLAY"));
currentDisplay = currentDisplay.replace(":", "");
displayNumber = currentDisplay.toInt();
if (mKGreeterParent) {
mPipeFilename = TQString(FIFO_FILE).arg(displayNumber);
::unlink((TQString(FIFO_SAK_FILE).arg(displayNumber)).ascii());
}
if (mSAKDlgParent) {
mPipeFilename = TQString(FIFO_SAK_FILE).arg(displayNumber);
::unlink((TQString(FIFO_FILE).arg(displayNumber)).ascii());
}
/* Create the FIFOs if they do not exist */
umask(0);
struct stat buffer;
int status;
char *fifo_parent_dir;
char *fifo_dir = strdup(FIFO_DIR);
fifo_parent_dir = dirname(fifo_dir);
status = stat(fifo_parent_dir, &buffer);
if (status != 0) {
mkdir(fifo_parent_dir, 0755);
}
free(fifo_dir);
status = stat(FIFO_DIR, &buffer);
if (status == 0) {
int dir_mode = ((buffer.st_mode & S_IRWXU) >> 6) * 100;
dir_mode = dir_mode + ((buffer.st_mode & S_IRWXG) >> 3) * 10;
dir_mode = dir_mode + ((buffer.st_mode & S_IRWXO) >> 0) * 1;
if ((dir_mode != 700 && dir_mode != 600) || (buffer.st_uid != 0) || (buffer.st_gid != 0)) {
printf("[WARNING] Possible security breach! Please check permissions on " FIFO_DIR " (must be 700 and owned by root/root, got %d %d/%d). Not listening for login credentials on remote control socket.\n", dir_mode, buffer.st_uid, buffer.st_gid); fflush(stdout);
TQApplication::eventLoop()->exit(-1);
return;
}
}
else {
mkdir(FIFO_DIR, 0700);
}
status = stat(mPipeFilename.ascii(), &buffer);
if (status == 0) {
int file_mode = ((buffer.st_mode & S_IRWXU) >> 6) * 100;
file_mode = file_mode + ((buffer.st_mode & S_IRWXG) >> 3) * 10;
file_mode = file_mode + ((buffer.st_mode & S_IRWXO) >> 0) * 1;
if ((file_mode != 600) || (buffer.st_uid != 0) || (buffer.st_gid != 0)) {
::unlink(mPipeFilename.ascii());
printf("[WARNING] Possible security breach! Please check permissions on %s (must be 600 and owned by root/root, got %d %d/%d). Not listening for login credentials on remote control socket.\n", mPipeFilename.ascii(), file_mode, buffer.st_uid, buffer.st_gid); fflush(stdout);
TQApplication::eventLoop()->exit(-1);
return;
}
}
else {
mknod(mPipeFilename.ascii(), S_IFIFO|0600, 0);
chmod(mPipeFilename.ascii(), 0600);
}
mPipe_fd = ::open(mPipeFilename.ascii(), O_RDONLY | O_NONBLOCK);
int numread;
int retval;
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(mPipe_fd, &rfds);
TQString inputcommand = "";
while ((!inputcommand.contains('\n')) && ((mKGreeterParent && (!mKGreeterParent->closingDown)) || (mSAKDlgParent && (!mSAKDlgParent->closingDown)))) {
// Wait for mPipe_fd to receive input
retval = select(mPipe_fd + 1, &rfds, NULL, NULL, NULL);
if (retval < 0) {
// ERROR
}
else if (retval) {
// New data is available
numread = ::read(mPipe_fd, readbuf, 2048);
readbuf[numread] = 0;
readbuf[2047] = 0;
inputcommand += readbuf;
}
}
if ((mKGreeterParent && (mKGreeterParent->closingDown)) || (mSAKDlgParent && (mSAKDlgParent->closingDown))) {
::unlink(mPipeFilename.ascii());
TQApplication::eventLoop()->exit(-1);
return;
}
emit processCommand(inputcommand);
if ((mKGreeterParent && (!mKGreeterParent->closingDown)) || (mSAKDlgParent && (!mSAKDlgParent->closingDown))) {
::close(mPipe_fd);
::unlink(mPipeFilename.ascii());
}
else {
::unlink(mPipeFilename.ascii());
TQApplication::eventLoop()->exit(-1);
return;
}
}
// Thread cancellation point
usleep(1);
}
TQApplication::eventLoop()->exit(-1);
}
#include "kgreeter.moc"