5
Copyright (C) 1997, 1998, 2000 Steffen Hansen <hansen@kde.org>
6
Copyright (C) 2000-2004 Oswald Buddenhagen <ossi@kde.org>
9
This program is free software; you can redistribute it and/or modify
10
it under the terms of the GNU General Public License as published by
11
the Free Software Foundation; either version 2 of the License, or
12
(at your option) any later version.
14
This program is distributed in the hope that it will be useful,
15
but WITHOUT ANY WARRANTY; without even the implied warranty of
16
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
GNU General Public License for more details.
19
You should have received a copy of the GNU General Public License
20
along with this program; if not, write to the Free Software
21
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27
#include "kdmconfig.h"
29
#include "kdm_greet.h"
30
#include "themer/kdmthemer.h"
31
#include "themer/kdmitem.h"
32
#include "themer/kdmlabel.h"
35
#include <kseparator.h>
36
#include <kstandarddirs.h>
37
#include <kstringhandler.h>
38
#include <KConfigGroup>
45
#include <QListWidget>
46
#include <QListWidgetItem>
49
#include <QPushButton>
52
#include <sys/types.h>
60
class UserListView : public QListWidget {
62
UserListView( QWidget *parent = 0 )
63
: QListWidget( parent )
64
, cachedSizeHint( -1, 0 )
66
setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Ignored );
67
setUniformItemSizes( true );
68
setIconSize( QSize( 48, 48 ) );
71
mutable QSize cachedSizeHint;
74
virtual QSize sizeHint() const
76
if (!cachedSizeHint.isValid()) {
78
QStyleOptionViewItem vo( viewOptions() );
79
QAbstractListModel *md( static_cast<QAbstractListModel *>(model()) );
81
for (int i = 0, rc = md->rowCount(); i < rc; i++) {
82
QSize sh = itemDelegate()->sizeHint( vo, md->index( i ) );
83
uint thisw = sh.width();
88
cachedSizeHint.setWidth(
89
style()->pixelMetric( QStyle::PM_ScrollBarExtent ) +
90
frameWidth() * 2 + maxw );
91
cachedSizeHint.setHeight( frameWidth() * 2 + h );
93
return cachedSizeHint;
96
virtual void keyPressEvent( QKeyEvent *event )
98
switch (event->key()) {
101
if (currentIndex().isValid())
102
emit activated( currentIndex() );
106
QListWidget::keyPressEvent( event );
112
class UserListViewItem : public QListWidgetItem {
114
UserListViewItem( UserListView *parent, const QString &text,
115
const QPixmap &pixmap, const QString &username )
116
: QListWidgetItem( parent )
121
parent->cachedSizeHint.setWidth( -1 );
128
int KGreeter::curPlugin = -1;
129
PluginList KGreeter::pluginList;
131
KGreeter::KGreeter( bool framed )
132
: inherited( framed )
143
stsGroup = new KConfigGroup( KSharedConfig::openConfig( _stsFile ),
147
userView = new UserListView( this );
148
connect( userView, SIGNAL(itemPressed( QListWidgetItem * )),
149
SLOT(slotUserClicked( QListWidgetItem * )) );
150
connect( userView, SIGNAL(itemActivated( QListWidgetItem * )),
154
userList = new QStringList;
155
if (userView || userList)
158
sessMenu = new QMenu( this );
159
connect( sessMenu, SIGNAL(triggered( QAction * )),
160
SLOT(slotSessionSelected( QAction * )) );
165
pluginList = KGVerify::init( _pluginsLogin );
169
KGreeter::~KGreeter()
178
KGreeter::insertUser( const QImage &default_pix,
179
const QString &username, struct passwd *ps )
182
userList->append( username );
187
if (_faceSource == FACE_USER_ONLY ||
188
_faceSource == FACE_PREFER_USER)
190
if (_faceSource != FACE_USER_ONLY &&
191
_faceSource != FACE_ADMIN_ONLY)
196
QFile::decodeName( ps->pw_dir ) + "/.face" :
197
_faceDir + '/' + username + ".face";
198
if (p.load( fn + ".icon" ) || p.load( fn )) {
201
p = p.convertToFormat( QImage::Format_RGB32 ).scaled( ns, Qt::KeepAspectRatio, Qt::SmoothTransformation );
208
QString realname = KStringHandler::from8Bit( ps->pw_gecos );
209
realname.truncate( realname.indexOf( ',' ) & (~0U >> 1) );
210
if (realname.isEmpty() || realname == username)
211
new UserListViewItem( userView, username, QPixmap::fromImage( p ), username );
213
realname.append( "\n" ).append( username );
214
new UserListViewItem( userView, realname, QPixmap::fromImage( p ), username );
220
UserList( char **in );
221
bool hasUser( const char *str ) const { return users.contains( str ); }
222
bool hasGroup( gid_t gid ) const { return groups.contains( gid ); }
223
bool hasGroups() const { return !groups.isEmpty(); }
227
QSet<QByteArray> users;
230
UserList::UserList( char **in )
236
if ((grp = getgrnam( *in + 1 ))) {
237
for (; *grp->gr_mem; grp->gr_mem++)
238
users.insert( *grp->gr_mem );
239
groups.insert( grp->gr_gid );
246
KGreeter::insertUsers()
250
// XXX remove seteuid-voodoo when we run as nobody
251
if (!(ps = getpwnam( "nobody" )))
253
if (setegid( ps->pw_gid ))
255
if (seteuid( ps->pw_uid )) {
262
if (!default_pix.load( _faceDir + "/.default.face.icon" ))
263
if (!default_pix.load( _faceDir + "/.default.face" ))
264
logError( "Can't open default user face\n" );
266
if (default_pix.size() != ns)
268
default_pix.convertToFormat( QImage::Format_RGB32 ).scaled( ns, Qt::KeepAspectRatio, Qt::SmoothTransformation );
270
if (_showUsers == SHOW_ALL) {
271
UserList noUsers( _noUsers );
273
for (setpwent(); (ps = getpwent()) != 0;) {
274
if (*ps->pw_dir && *ps->pw_shell &&
275
(ps->pw_uid >= (unsigned)_lowUserId ||
276
!ps->pw_uid && _showRoot) &&
277
ps->pw_uid <= (unsigned)_highUserId &&
278
!noUsers.hasUser( ps->pw_name ) &&
279
!noUsers.hasGroup( ps->pw_gid ))
281
QString username( QFile::decodeName( ps->pw_name ) );
282
if (!dupes.contains( username )) {
283
dupes.insert( username );
284
insertUser( default_pix, username, ps );
289
UserList users( _users );
290
if (users.hasGroups()) {
292
for (setpwent(); (ps = getpwent()) != 0;) {
293
if (*ps->pw_dir && *ps->pw_shell &&
294
(ps->pw_uid >= (unsigned)_lowUserId ||
295
!ps->pw_uid && _showRoot) &&
296
ps->pw_uid <= (unsigned)_highUserId &&
297
(users.hasUser( ps->pw_name ) ||
298
users.hasGroup( ps->pw_gid )))
300
QString username( QFile::decodeName( ps->pw_name ) );
301
if (!dupes.contains( username )) {
302
dupes.insert( username );
303
insertUser( default_pix, username, ps );
308
for (int i = 0; _users[i]; i++)
309
if ((ps = getpwnam( _users[i] )) && (ps->pw_uid || _showRoot))
310
insertUser( default_pix, QFile::decodeName( _users[i] ), ps );
316
userView->sortItems();
321
// XXX remove seteuid-voodoo when we run as nobody
327
KGreeter::putSession( const QString &type, const QString &name, bool hid, const char *exe )
329
int prio = exe ? (!strcmp( exe, "default" ) ? 0 :
330
!strcmp( exe, "custom" ) ? 1 :
331
!strcmp( exe, "failsafe" ) ? 3 : 2) : 2;
332
for (int i = 0; i < sessionTypes.size(); i++)
333
if (sessionTypes[i].type == type) {
334
sessionTypes[i].prio = prio;
337
sessionTypes.append( SessType( name, type, hid, prio ) );
341
KGreeter::insertSessions()
343
for (char **dit = _sessionsDirs; *dit; ++dit)
344
foreach (QString ent, QDir( *dit ).entryList())
345
if (ent.endsWith( ".desktop" )) {
347
KSharedConfig::openConfig(
348
QString( *dit ).append( '/' ).append( ent ) ),
350
putSession( ent.left( ent.length() - 8 ),
351
dsk.readEntry( "Name" ),
352
(dsk.readEntry( "Hidden", false ) ||
353
(dsk.hasKey( "TryExec" ) &&
354
KStandardDirs::findExe(
355
dsk.readEntry( "TryExec" ) ).isEmpty())),
356
dsk.readEntry( "Exec" ).toLatin1() );
358
putSession( "default", i18n("Default"), false, "default" );
359
putSession( "custom", i18n("Custom"), false, "custom" );
360
putSession( "failsafe", i18n("Failsafe"), false, "failsafe" );
361
qSort( sessionTypes );
362
for (int i = 0; i < sessionTypes.size() && !sessionTypes[i].hid; i++) {
363
sessionTypes[i].action = sessMenu->addAction( sessionTypes[i].name );
364
sessionTypes[i].action->setData( i );
365
sessionTypes[i].action->setCheckable( true );
366
switch (sessionTypes[i].prio) {
367
case 0: case 1: nSpecials++; break;
368
case 2: nNormals++; break;
374
KGreeter::slotUserEntered()
377
for (int i = 0, rc = userView->model()->rowCount(); i < rc; i++) {
378
UserListViewItem *item =
379
static_cast<UserListViewItem *>(userView->item( i ));
380
if (item->login == curUser) {
381
userView->setCurrentItem( item );
385
userView->clearSelection();
391
QTimer::singleShot( 0, this, SLOT(slotLoadPrevWM()) );
395
KGreeter::slotUserClicked( QListWidgetItem *item )
398
curUser = ((UserListViewItem *)item)->login;
399
verify->setUser( curUser );
405
KGreeter::slotSessionSelected( QAction *action )
407
if (action != curSel) {
409
curSel->setChecked( false );
411
action->setChecked( true );
413
verify->gplugActivity();
426
if (userView && userView->hasFocus())
427
slotUserClicked( userView->currentItem() );
433
KGreeter::setPrevWM( QAction *wm )
437
curPrev->setText( sessionTypes[curPrev->data().toInt()].name );
439
wm->setText( sessionTypes[wm->data().toInt()].name + i18n(" (previous)") );
445
KGreeter::slotLoadPrevWM()
448
unsigned long crc, by;
452
if (verify->coreLock) {
459
name = curUser.toLocal8Bit();
460
gSendInt( G_ReadDmrc );
461
gSendStr( name.data() );
462
gRecvInt(); // ignore status code ...
463
if ((len = name.length())) {
464
gSendInt( G_GetDmrc );
465
gSendStr( "Session" );
467
if (!sess) { /* no such user */
468
if (!userView && !userList) { // don't fake if user list shown
471
for (crc = _forgingSeed, i = 0; i < len; i++) {
472
by = (crc & 255) ^ name[i];
473
for (b = 0; b < 8; b++)
474
by = (by >> 1) ^ (-(by & 1) & 0xedb88320);
475
crc = (crc >> 8) ^ by;
477
/* forge a session with this hash - default & custom more probable */
478
/* XXX - this should do a statistical analysis of the real users */
480
setPrevWM( sessionTypes[crc % (nSpecials * 2 + nNormals) % (nSpecials + nNormals)].action );
482
i = crc % (nSpecials * 2 + nNormals);
484
setPrevWM( sessionTypes[i + nSpecials].action );
486
setPrevWM( sessionTypes[(i - nNormals) / 2].action );
491
for (int i = 0; i < sessionTypes.count() && !sessionTypes[i].hid; i++)
492
if (sessionTypes[i].type == sess) {
494
setPrevWM( sessionTypes[i].action );
498
msgBox( sorrybox, i18n("Your saved session type '%1' is not valid any more.\n"
499
"Please select a new one, otherwise 'default' will be used.", sess ) );
508
KGreeter::pluginSetup()
511
QString ent, pn( verify->pluginName() ), dn( dName + '_' + pn );
513
if (_preselUser != PRESEL_PREV)
514
stsGroup->deleteEntry( verify->entitiesLocal() ? dName : dn, false );
515
if (_preselUser != PRESEL_NONE && verify->entityPresettable()) {
516
if (verify->entitiesLocal())
517
ent = _preselUser == PRESEL_PREV ?
518
stsGroup->readEntry( dName, QString() ) : _defaultUser;
520
ent = _preselUser == PRESEL_PREV ?
521
stsGroup->readEntry( dn, QString() ) :
522
verify->getConf( 0, (pn + ".DefaultEntity").toLatin1(), QVariant() ).toString();
523
field = verify->entitiesFielded() ?
524
verify->getConf( 0, (pn + ".FocusField").toLatin1(), QVariant( 0 ) ).toInt() :
527
verify->presetEntity( ent, field );
529
verify->loadUsers( *userList );
533
KGreeter::verifyPluginChanged( int id )
540
KGreeter::verifyClear()
544
slotSessionSelected( 0 );
550
if (_preselUser == PRESEL_PREV && verify->entityPresettable())
551
stsGroup->writeEntry( verify->entitiesLocal() ?
553
dName + '_' + verify->pluginName(),
554
verify->getEntity() );
556
gSendInt( G_PutDmrc );
557
gSendStr( "Session" );
558
gSendStr( sessionTypes[curSel->data().toInt()].type.toUtf8() );
559
} else if (!prevValid) {
560
gSendInt( G_PutDmrc );
561
gSendStr( "Session" );
562
gSendStr( "default" );
568
KGreeter::verifyFailed()
571
userView->setEnabled( false );
577
KGreeter::verifyRetry()
580
userView->setEnabled( true );
584
KGreeter::verifySetUser( const QString &user )
591
KStdGreeter::KStdGreeter()
596
QBoxLayout *main_box;
597
#ifdef WITH_KDM_XCONSOLE
599
QBoxLayout *ex_box = new QVBoxLayout( this );
600
main_box = new QHBoxLayout();
601
ex_box->addLayout( main_box );
602
main_box->setParent( ex_box );
603
ex_box->addWidget( consoleView );
607
main_box = new QHBoxLayout( this );
609
int rs = layout()->spacing();
610
main_box->setSpacing( layout()->margin() );
613
userView->setAlternatingRowColors( true );
614
main_box->addWidget( userView );
617
QBoxLayout *inner_box = new QVBoxLayout();
618
main_box->addLayout( inner_box );
619
inner_box->setParent( main_box );
620
inner_box->setSpacing( rs );
622
if (!_authorized && _authComplain) {
623
QLabel *complainLabel = new QLabel(
624
i18n("Warning: this is an unsecured session"), this );
625
complainLabel->setToolTip(
626
i18n("This display requires no X authorization.\n"
627
"This means that anybody can connect to it,\n"
628
"open windows on it or intercept your input.") );
629
complainLabel->setAlignment( Qt::AlignCenter );
630
complainLabel->setFont( *_failFont );
632
p.setColor( QPalette::WindowText, Qt::red );
633
complainLabel->setPalette( p );
634
inner_box->addWidget( complainLabel );
636
if (!_greetString.isEmpty()) {
637
QLabel *welcomeLabel = new QLabel( _greetString, this );
638
welcomeLabel->setAlignment( Qt::AlignCenter );
639
welcomeLabel->setFont( *_greetFont );
640
inner_box->addWidget( welcomeLabel );
645
clock = new KdmClock( this );
649
QMovie *movie = new QMovie( this );
650
movie->setFileName( _logo );
651
if (movie->isValid()) {
653
pixLabel = new QLabel( this );
654
pixLabel->setMovie( movie );
655
if (!movie->currentImage().hasAlphaChannel())
656
pixLabel->setFrameStyle( QFrame::Panel | QFrame::Sunken );
657
pixLabel->setIndent( 0 );
666
inner_box->addWidget( clock, 0, Qt::AlignCenter );
668
inner_box->addWidget( pixLabel, 0, Qt::AlignCenter );
669
inner_box->addSpacing( inner_box->spacing() );
672
main_box->addWidget( clock, 0, Qt::AlignCenter );
674
main_box->addWidget( pixLabel, 0, Qt::AlignCenter );
677
goButton = new QPushButton( i18n("L&ogin"), this );
678
goButton->setDefault( true );
679
connect( goButton, SIGNAL(clicked()), SLOT(accept()) );
680
QPushButton *menuButton = new QPushButton( i18n("&Menu"), this );
686
#ifdef WITH_KDM_XCONSOLE
687
else if (consoleView)
692
KGStdVerify *sverify =
693
new KGStdVerify( this, this, prec, QString(),
694
pluginList, KGreeterPlugin::Authenticate,
695
KGreeterPlugin::Login );
696
inner_box->addLayout( sverify->getLayout() );
697
sverify->getLayout()->setParent( inner_box );
698
QMenu *plugMenu = sverify->getPlugMenu();
699
sverify->selectPlugin( curPlugin );
702
inner_box->addWidget( new KSeparator( Qt::Horizontal, this ) );
704
QBoxLayout *hbox2 = new QHBoxLayout();
705
inner_box->addLayout( hbox2 );
706
hbox2->setParent( inner_box );
707
hbox2->addWidget( goButton );
708
hbox2->addStretch( 1 );
709
hbox2->addWidget( menuButton );
710
hbox2->addStretch( 1 );
712
if (sessMenu->actions().count() > 1) {
713
inserten( i18n("Session &Type"), Qt::ALT+Qt::Key_T, sessMenu );
718
inserten( i18n("&Authentication Method"), Qt::ALT+Qt::Key_A, plugMenu );
723
completeMenu( LOGIN_LOCAL_ONLY, ex_choose, i18n("&Remote Login"), Qt::ALT+Qt::Key_R );
729
menuButton->setMenu( optMenu );
739
KStdGreeter::pluginSetup()
741
inherited::pluginSetup();
743
if (verify->entitiesLocal() && verify->entityPresettable())
753
KStdGreeter::verifyFailed()
755
goButton->setEnabled( false );
756
inherited::verifyFailed();
760
KStdGreeter::verifyRetry()
762
goButton->setEnabled( true );
763
inherited::verifyRetry();
767
KThemedGreeter::KThemedGreeter( KdmThemer *_themer )
772
// We do all painting ourselves
773
setAttribute( Qt::WA_NoSystemBackground, true );
774
// Allow tracking the mouse position
775
setMouseTracking( true );
779
themer->setWidget( this );
781
connect( themer, SIGNAL(activated( const QString & )),
782
SLOT(slotThemeActivated( const QString & )) );
784
console_rect = themer->findNode( "xconsole" ); // kdm ext
785
userlist_rect = themer->findNode( "userlist" );
786
caps_warning = themer->findNode( "caps-lock-warning" );
787
xauth_warning = themer->findNode( "xauth-warning" ); // kdm ext
788
pam_error = themer->findNode( "pam-error" );
789
KdmLabel *pam_error_label = qobject_cast<KdmLabel *>(pam_error);
791
pam_error_label->setText( i18n("Login Failed.") );
792
timed_label = themer->findNode( "timed-label" );
795
if ((itm = themer->findNode( "pam-message" ))) // done via msgboxes
796
itm->setVisible( false );
797
if ((itm = themer->findNode( "language_button" ))) // not implemented yet
798
itm->setVisible( false );
800
#ifdef WITH_KDM_XCONSOLE
803
console_rect->setWidget( consoleView );
805
console_rect->setVisible( false );
809
if (xauth_warning && (_authorized || !_authComplain))
810
xauth_warning->setVisible( false );
812
// if (!_greetString.isEmpty()) {
814
// clock = new KdmClock( this, "clock" );
819
#ifdef WITH_KDM_XCONSOLE
820
else if (consoleView)
825
KGThemedVerify *tverify =
826
new KGThemedVerify( this, themer, this, prec, QString(),
827
pluginList, KGreeterPlugin::Authenticate,
828
KGreeterPlugin::Login );
829
QMenu *plugMenu = tverify->getPlugMenu();
830
tverify->selectPlugin( curPlugin );
833
if ((session_button = themer->findNode( "session_button" ))) {
834
if (sessMenu->actions().count() <= 1) {
835
session_button->setVisible( false );
839
if (sessMenu->actions().count() > 1) {
840
inserten( i18n("Session &Type"), Qt::ALT+Qt::Key_T, sessMenu );
846
inserten( i18n("&Authentication Method"), Qt::ALT+Qt::Key_A, plugMenu );
851
completeMenu( LOGIN_LOCAL_ONLY, ex_choose, i18n("&Remote Login"), Qt::ALT+Qt::Key_R );
856
if ((system_button = themer->findNode( "system_button" ))) {
858
addAction( optMenu->menuAction() );
860
system_button->setVisible( false );
868
KThemedGreeter::~KThemedGreeter()
870
themer->setWidget( 0 );
874
KThemedGreeter::event( QEvent *e )
877
themer->widgetEvent( e );
878
return inherited::event( e );
882
KThemedGreeter::pluginSetup()
884
inherited::pluginSetup();
886
if (userView && verify->entitiesLocal() && verify->entityPresettable() && userlist_rect) {
887
userlist_rect->setWidget( userView );
888
userlist_rect->setVisible( true );
893
userlist_rect->setVisible( false );
898
KThemedGreeter::verifyFailed()
900
// goButton->setEnabled( false );
902
userView->setEnabled( false );
903
inherited::verifyFailed();
907
KThemedGreeter::verifyRetry()
909
// goButton->setEnabled( true );
911
userView->setEnabled( true );
912
inherited::verifyRetry();
916
KThemedGreeter::updateStatus( bool fail, bool caps, int timedleft )
919
pam_error->setVisible( fail );
921
caps_warning->setVisible( caps );
924
if (timedleft != KdmLabel::timedDelay) {
925
KdmLabel::timedDelay = timedleft;
926
KdmLabel::timedUser = curUser;
927
timed_label->setVisible( true );
928
timed_label->update();
931
KdmLabel::timedDelay = -1;
932
timed_label->setVisible( false );
938
KThemedGreeter::slotThemeActivated( const QString &id )
940
if (id == "login_button")
942
else if (id == "session_button")
944
else if (id == "system_button")
949
KThemedGreeter::slotSessMenu()
951
sessMenu->popup( mapToGlobal( session_button->rect().center() ) );
955
KThemedGreeter::slotActionMenu()
958
optMenu->popup( mapToGlobal( system_button->rect().center() ) );
960
optMenu->popup( mapToGlobal( rect().center() ) );
964
KThemedGreeter::keyPressEvent( QKeyEvent *e )
966
inherited::keyPressEvent( e );
967
if (!(e->modifiers() & Qt::KeyboardModifierMask) &&
968
(e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter))
972
#include "kgreeter.moc"