1 //===========================================================================
3 // This file is part of the KDE project
5 // Copyright 1999 Martin R. Jones <mjones@kde.org>
6 // Copyright 2003 Chris Howells <howells@kde.org>
7 // Copyright 2003 Oswald Buddenhagen <ossi@kde.org>
9 #include <config-unix.h> // HAVE_PAM
11 #include "lockprocess.h"
14 #include <kcheckpass-enums.h>
15 #include <kdisplaymanager.h>
17 #include <KApplication>
19 #include <KPushButton>
21 #include <KStandardDirs>
22 #include <KGlobalSettings>
24 #include <KIconLoader>
25 #include <kdesu/defaults.h>
26 #include <KPasswordDialog>
29 #include <KMessageBox>
30 #include <KColorScheme>
32 #include <QtDBus/QtDBus>
35 #include <QPushButton>
36 // #include <QMessageBox>
38 #include <QFontMetrics>
40 #include <QApplication>
41 #include <QTreeWidget>
42 #include <QHeaderView>
44 #include <QGridLayout>
47 #include <QHBoxLayout>
49 #include <QTimerEvent>
50 #include <QVBoxLayout>
59 #include <sys/types.h>
60 #include <sys/socket.h>
63 #include <X11/Xutil.h>
64 #include <X11/keysym.h>
67 #include <kauthorized.h>
69 #include <KPluginLoader>
70 #include <KPluginFactory>
73 # define AF_LOCAL AF_UNIX
76 #define PASSDLG_HIDE_TIMEOUT 10000
78 //===========================================================================
80 // Simple dialog for entering a password.
82 PasswordDlg::PasswordDlg(LockProcess
*parent
, GreeterPluginHandle
*plugin
, const QString
&text
)
83 : KDialog(parent
, Qt::X11BypassWindowManagerHint
),
86 mUnlockingFailed(false)
88 QWidget
* w
= mainWidget();
90 QLabel
*pixLabel
= new QLabel( w
);
91 pixLabel
->setPixmap(DesktopIcon("system-lock-screen"));
93 KUser user
; QString fullName
=user
.property(KUser::FullName
).toString();
94 QString greetString
= text
;
96 greetString
= fullName
.isEmpty() ?
97 i18n("<nobr><b>The session is locked</b></nobr><br />") :
98 i18n("<nobr><b>The session was locked by %1</b></nobr><br />", fullName
);
100 QLabel
*greetLabel
= new QLabel(greetString
, w
);
102 mStatusLabel
= new QLabel( "<b> </b>", w
);
103 mStatusLabel
->setAlignment( Qt::AlignCenter
);
105 greet
= plugin
->info
->create(this, w
, QString(),
106 KGreeterPlugin::Authenticate
,
107 KGreeterPlugin::ExUnlock
);
109 KSeparator
*sep
= new KSeparator( Qt::Horizontal
, w
);
111 ok
= new KPushButton( i18n("Unl&ock"), w
);
112 cancel
= new KPushButton( KStandardGuiItem::cancel(), w
);
113 mNewSessButton
= new KPushButton( KGuiItem(i18n("Sw&itch User..."), "fork"), w
);
115 // Using KXKB component
116 KPluginFactory
*kxkbFactory
= KPluginLoader("libkdeinit4_kxkb").factory();
117 QWidget
*kxkbComponent
= NULL
;
119 kxkbComponent
= kxkbFactory
->create
<QWidget
>(this);
122 kDebug() << "can't load kxkb component library";
125 QHBoxLayout
*layStatus
= new QHBoxLayout();
126 layStatus
->addStretch();
127 layStatus
->addWidget( mStatusLabel
);
128 layStatus
->addStretch();
131 layStatus
->addWidget( kxkbComponent
, 0, Qt::AlignRight
);
133 QHBoxLayout
*layButtons
= new QHBoxLayout();
134 layButtons
->addWidget( mNewSessButton
);
135 layButtons
->addStretch();
136 layButtons
->addWidget( ok
);
137 layButtons
->addWidget( cancel
);
139 frameLayout
= new QGridLayout( w
);
140 frameLayout
->setSpacing( KDialog::spacingHint() );
141 frameLayout
->setMargin( KDialog::marginHint() );
142 frameLayout
->addWidget( pixLabel
, 0, 0, 3, 1, Qt::AlignTop
);
143 frameLayout
->addWidget( greetLabel
, 0, 1 );
144 frameLayout
->addWidget( greet
->getWidgets().first(), 1, 1 );
145 frameLayout
->addLayout( layStatus
, 2, 1 );
146 frameLayout
->addWidget( sep
, 3, 0, 1, 2 );
147 frameLayout
->addLayout( layButtons
, 4, 0, 1, 2 );
150 connect(cancel
, SIGNAL(clicked()), SLOT(reject()));
151 connect(ok
, SIGNAL(clicked()), SLOT(slotOK()));
152 connect(mNewSessButton
, SIGNAL(clicked()), SLOT(slotSwitchUser()));
154 if (!text
.isEmpty() || !KDisplayManager().isSwitchable() || !KAuthorized::authorizeKAction("switch_user"))
155 mNewSessButton
->hide();
157 installEventFilter(this);
160 mTimeoutTimerId
= startTimer(PASSDLG_HIDE_TIMEOUT
);
161 connect(qApp
, SIGNAL(activity()), SLOT(slotActivity()) );
168 PasswordDlg::~PasswordDlg()
174 void PasswordDlg::updateLabel()
176 if (mUnlockingFailed
)
179 KColorScheme::adjustForeground(palette
, KColorScheme::NormalText
, QPalette::WindowText
);
180 mStatusLabel
->setPalette(palette
);
181 mStatusLabel
->setText(i18n("<b>Unlocking failed</b>"));
186 QPalette palette
= mStatusLabel
->palette();
187 KColorScheme::adjustForeground(palette
, KColorScheme::NegativeText
, QPalette::WindowText
);
188 mStatusLabel
->setPalette(palette
);
189 mStatusLabel
->setText(i18n("<b>Warning: Caps Lock on</b>"));
193 mStatusLabel
->setText("<b> </b>");
197 //---------------------------------------------------------------------------
199 // Handle timer events.
201 void PasswordDlg::timerEvent(QTimerEvent
*ev
)
203 if (ev
->timerId() == mTimeoutTimerId
)
207 else if (ev
->timerId() == mFailedTimerId
)
209 killTimer(mFailedTimerId
);
211 // Show the normal password prompt.
212 mUnlockingFailed
= false;
214 ok
->setEnabled(true);
215 cancel
->setEnabled(true);
216 mNewSessButton
->setEnabled( true );
222 bool PasswordDlg::eventFilter(QObject
*, QEvent
*ev
)
224 if (ev
->type() == QEvent::KeyPress
|| ev
->type() == QEvent::KeyRelease
)
229 void PasswordDlg::slotActivity()
231 if (mTimeoutTimerId
) {
232 killTimer(mTimeoutTimerId
);
233 mTimeoutTimerId
= startTimer(PASSDLG_HIDE_TIMEOUT
);
237 ////// kckeckpass interface code
239 int PasswordDlg::Reader (void *buf
, int count
)
243 for (rlen
= 0; rlen
< count
; ) {
245 ret
= ::read (sFd
, (void *)((char *)buf
+ rlen
), count
- rlen
);
260 bool PasswordDlg::GRead (void *buf
, int count
)
262 return Reader (buf
, count
) == count
;
265 bool PasswordDlg::GWrite (const void *buf
, int count
)
267 return ::write (sFd
, buf
, count
) == count
;
270 bool PasswordDlg::GSendInt (int val
)
272 return GWrite (&val
, sizeof(val
));
275 bool PasswordDlg::GSendStr (const char *buf
)
277 int len
= buf
? ::strlen (buf
) + 1 : 0;
278 return GWrite (&len
, sizeof(len
)) && GWrite (buf
, len
);
281 bool PasswordDlg::GSendArr (int len
, const char *buf
)
283 return GWrite (&len
, sizeof(len
)) && GWrite (buf
, len
);
286 bool PasswordDlg::GRecvInt (int *val
)
288 return GRead (val
, sizeof(*val
));
291 bool PasswordDlg::GRecvArr (char **ret
)
302 if (!(buf
= (char *)::malloc (len
)))
305 if (GRead (buf
, len
)) {
314 void PasswordDlg::reapVerify()
318 ::waitpid( sPid
, &status
, 0 );
319 if (WIFEXITED(status
))
320 switch (WEXITSTATUS(status
)) {
327 mUnlockingFailed
= true;
329 mFailedTimerId
= startTimer(1500);
330 ok
->setEnabled(false);
331 cancel
->setEnabled(false);
332 mNewSessButton
->setEnabled( false );
340 void PasswordDlg::handleVerify()
345 while (GRecvInt( &ret
)) {
348 if (!GRecvArr( &arr
))
350 greet
->binaryPrompt( arr
, false );
355 if (!GRecvArr( &arr
))
357 greet
->textPrompt( arr
, true, false );
362 if (!GRecvArr( &arr
))
364 greet
->textPrompt( arr
, false, false );
369 if (!GRecvArr( &arr
))
371 if (!greet
->textMessage( arr
, false ))
372 static_cast< LockProcess
* >(parent())->msgBox( this, QMessageBox::Information
, QString::fromLocal8Bit( arr
) );
376 if (!GRecvArr( &arr
))
378 if (!greet
->textMessage( arr
, true ))
379 static_cast< LockProcess
* >(parent())->msgBox( this, QMessageBox::Warning
, QString::fromLocal8Bit( arr
) );
388 ////// greeter plugin callbacks
390 void PasswordDlg::gplugReturnText( const char *text
, int tag
)
398 void PasswordDlg::gplugReturnBinary( const char *data
)
401 unsigned const char *up
= (unsigned const char *)data
;
402 int len
= up
[3] | (up
[2] << 8) | (up
[1] << 16) | (up
[0] << 24);
406 GSendArr( len
, data
);
412 void PasswordDlg::gplugSetUser( const QString
& )
417 void PasswordDlg::cantCheck()
420 static_cast< LockProcess
* >(parent())->msgBox( this, QMessageBox::Critical
,
421 i18n("Cannot unlock the session because the authentication system failed to work;\n"
422 "you must kill kscreenlocker (pid %1) manually.", getpid()) );
426 //---------------------------------------------------------------------------
428 // Starts the kcheckpass process to check the user's password.
430 void PasswordDlg::gplugStart()
435 if (::socketpair(AF_LOCAL
, SOCK_STREAM
, 0, sfd
)) {
439 if ((sPid
= ::fork()) < 0) {
447 sprintf(fdbuf
, "%d", sfd
[1]);
448 execlp(QFile::encodeName(KStandardDirs::findExe("kcheckpass")).data(),
451 "-c", KSCREENSAVER_PAM_SERVICE
,
453 "-m", mPlugin
->info
->method
,
463 void PasswordDlg::gplugChanged()
467 void PasswordDlg::gplugActivity()
472 void PasswordDlg::gplugMsgBox( QMessageBox::Icon type
, const QString
&text
)
474 static_cast< LockProcess
* >(parent())->msgBox( this, type
, text
);
477 bool PasswordDlg::gplugHasNode( const QString
& )
482 void PasswordDlg::slotOK()
488 void PasswordDlg::setVisible( bool visible
)
490 QDialog::setVisible( visible
);
493 QApplication::flush();
496 void PasswordDlg::slotStartNewSession()
498 if (!KMessageBox::shouldBeShownContinue( ":confirmNewSession" )) {
499 KDisplayManager().startReserve();
503 killTimer(mTimeoutTimerId
);
506 KDialog
*dialog
= new KDialog( this, Qt::X11BypassWindowManagerHint
);
507 dialog
->setModal( true );
508 dialog
->setButtons( KDialog::Yes
| KDialog::No
);
509 dialog
->showButtonSeparator( true );
510 dialog
->setButtonGuiItem( KDialog::Yes
, KGuiItem(i18n("&Start New Session"), "fork") );
511 dialog
->setButtonGuiItem( KDialog::No
, KStandardGuiItem::cancel() );
512 dialog
->setDefaultButton( KDialog::Yes
);
513 dialog
->setEscapeButton( KDialog::No
);
515 bool dontAskAgain
= false;
517 KMessageBox::createKMessageBox( dialog
, QMessageBox::Warning
,
518 i18n("You have chosen to open another desktop session "
519 "instead of resuming the current one.\n"
520 "The current session will be hidden "
521 "and a new login screen will be displayed.\n"
522 "An F-key is assigned to each session; "
523 "F%1 is usually assigned to the first session, "
524 "F%2 to the second session and so on. "
525 "You can switch between sessions by pressing "
526 "Ctrl, Alt and the appropriate F-key at the same time. "
527 "Additionally, the KDE Panel and Desktop menus have "
528 "actions for switching between sessions.",
531 i18n("&Do not ask again"), &dontAskAgain
,
532 KMessageBox::NoExec
);
534 int ret
= static_cast< LockProcess
* >( parent())->execDialog( dialog
);
538 if (ret
== KDialog::Yes
) {
540 KMessageBox::saveDontShowAgainContinue( ":confirmNewSession" );
541 KDisplayManager().startReserve();
544 mTimeoutTimerId
= startTimer(PASSDLG_HIDE_TIMEOUT
);
547 class LockListViewItem
: public QTreeWidgetItem
{
549 LockListViewItem( QTreeWidget
*parent
,
550 const QString
&sess
, const QString
&loc
, int _vt
)
551 : QTreeWidgetItem( parent
)
561 void PasswordDlg::slotSwitchUser()
566 QDialog
dialog( this, Qt::X11BypassWindowManagerHint
);
567 dialog
.setModal( true );
569 QBoxLayout
*hbox
= new QHBoxLayout( &dialog
);
570 hbox
->setSpacing( KDialog::spacingHint() );
571 hbox
->setMargin( KDialog::marginHint() );
573 QBoxLayout
*vbox1
= new QVBoxLayout( );
574 hbox
->addItem( vbox1
);
575 QBoxLayout
*vbox2
= new QVBoxLayout( );
576 hbox
->addItem( vbox2
);
581 if (dm
.localSessions( sess
)) {
583 lv
= new QTreeWidget( &dialog
);
584 connect( lv
, SIGNAL(itemDoubleClicked(QTreeWidgetItem
*, int)), SLOT(slotSessionActivated()) );
585 connect( lv
, SIGNAL(itemDoubleClicked(QTreeWidgetItem
*, int)), &dialog
, SLOT(reject()) );
586 lv
->setAllColumnsShowFocus( true );
587 lv
->setHeaderLabels( QStringList() << i18n("Session") << i18n("Location") );
588 lv
->header()->setResizeMode( 0, QHeaderView::Stretch
);
589 lv
->header()->setResizeMode( 1, QHeaderView::Stretch
);
590 QTreeWidgetItem
*itm
= 0;
593 for (SessList::ConstIterator it
= sess
.constBegin(); it
!= sess
.constEnd(); ++it
) {
594 KDisplayManager::sess2Str2( *it
, user
, loc
);
595 itm
= new LockListViewItem( lv
, user
, loc
, (*it
).vt
);
597 itm
->setFlags( itm
->flags() & ~Qt::ItemIsEnabled
);
599 lv
->setCurrentItem( itm
);
600 itm
->setSelected( true );
604 int fw
= lv
->frameWidth() * 2;
605 QSize
hds( lv
->header()->sizeHint() );
606 lv
->setMinimumWidth( fw
+ hds
.width() +
607 (ns
> 10 ? style()->pixelMetric(QStyle::PM_ScrollBarExtent
) : 0 ) );
608 int ih
= lv
->itemDelegate()->sizeHint(
609 QStyleOptionViewItem(), lv
->model()->index( 0, 0 ) ).height();
610 lv
->setFixedHeight( fw
+ hds
.height() +
611 ih
* (ns
< 6 ? 6 : ns
> 10 ? 10 : ns
) );
612 lv
->header()->adjustSize();
613 vbox1
->addWidget( lv
);
615 btn
= new KPushButton( KGuiItem(i18nc("session", "&Activate"), "fork"), &dialog
);
616 connect( btn
, SIGNAL(clicked()), SLOT(slotSessionActivated()) );
617 connect( btn
, SIGNAL(clicked()), &dialog
, SLOT(reject()) );
618 vbox2
->addWidget( btn
);
619 vbox2
->addStretch( 2 );
622 if (KAuthorized::authorizeKAction("start_new_session") && (p
= dm
.numReserve()) >= 0)
624 btn
= new KPushButton( KGuiItem(i18n("Start &New Session"), "fork"), &dialog
);
625 connect( btn
, SIGNAL(clicked()), SLOT(slotStartNewSession()) );
626 connect( btn
, SIGNAL(clicked()), &dialog
, SLOT(reject()) );
628 btn
->setEnabled( false );
629 vbox2
->addWidget( btn
);
630 vbox2
->addStretch( 1 );
633 btn
= new KPushButton( KStandardGuiItem::cancel(), &dialog
);
634 connect( btn
, SIGNAL(clicked()), &dialog
, SLOT(reject()) );
635 vbox2
->addWidget( btn
);
637 static_cast< LockProcess
* >(parent())->execDialog( &dialog
);
640 void PasswordDlg::slotSessionActivated()
642 LockListViewItem
*itm
= (LockListViewItem
*)lv
->currentItem();
643 if (itm
&& itm
->vt
> 0)
644 KDisplayManager().switchVT( itm
->vt
);
647 void PasswordDlg::capsLocked()
650 Window dummy1
, dummy2
;
651 int dummy3
, dummy4
, dummy5
, dummy6
;
652 XQueryPointer(QX11Info::display(), DefaultRootWindow( QX11Info::display() ), &dummy1
, &dummy2
, &dummy3
, &dummy4
, &dummy5
, &dummy6
, &lmask
);
653 mCapsLocked
= lmask
& LockMask
;
657 #include "lockdlg.moc"