add more spacing
[personal-kdebase.git] / workspace / krunner / lock / lockdlg.cc
bloba55637a40e81e8ed8527faa91c09b519df4cac3a
1 //===========================================================================
2 //
3 // This file is part of the KDE project
4 //
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"
12 #include "lockdlg.h"
14 #include <kcheckpass-enums.h>
15 #include <kdisplaymanager.h>
17 #include <KApplication>
18 #include <KLocale>
19 #include <KPushButton>
20 #include <KSeparator>
21 #include <KStandardDirs>
22 #include <KGlobalSettings>
23 #include <KConfig>
24 #include <KIconLoader>
25 #include <kdesu/defaults.h>
26 #include <KPasswordDialog>
27 #include <KDebug>
28 #include <KUser>
29 #include <KMessageBox>
30 #include <KColorScheme>
32 #include <QtDBus/QtDBus>
34 #include <QLayout>
35 #include <QPushButton>
36 // #include <QMessageBox>
37 #include <QLabel>
38 #include <QFontMetrics>
39 #include <QStyle>
40 #include <QApplication>
41 #include <QTreeWidget>
42 #include <QHeaderView>
43 #include <QCheckBox>
44 #include <QGridLayout>
45 #include <QEvent>
46 #include <QFrame>
47 #include <QHBoxLayout>
48 #include <QBoxLayout>
49 #include <QTimerEvent>
50 #include <QVBoxLayout>
51 #include <QFile>
53 #include <ctype.h>
54 #include <unistd.h>
55 #include <stdlib.h>
56 #include <errno.h>
57 #include <pwd.h>
58 #include <stdio.h>
59 #include <sys/types.h>
60 #include <sys/socket.h>
61 #include <sys/wait.h>
63 #include <X11/Xutil.h>
64 #include <X11/keysym.h>
65 #include <fixx11h.h>
66 #include <QX11Info>
67 #include <kauthorized.h>
69 #include <KPluginLoader>
70 #include <KPluginFactory>
72 #ifndef AF_LOCAL
73 # define AF_LOCAL AF_UNIX
74 #endif
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),
84 mPlugin( plugin ),
85 mCapsLocked(-1),
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;
95 if (text.isEmpty()) {
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;
118 if (kxkbFactory) {
119 kxkbComponent = kxkbFactory->create<QWidget>(this);
121 else {
122 kDebug() << "can't load kxkb component library";
125 QHBoxLayout *layStatus = new QHBoxLayout();
126 layStatus->addStretch();
127 layStatus->addWidget( mStatusLabel );
128 layStatus->addStretch();
130 if( kxkbComponent )
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 );
149 setButtons(None);
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);
159 mFailedTimerId = 0;
160 mTimeoutTimerId = startTimer(PASSDLG_HIDE_TIMEOUT);
161 connect(qApp, SIGNAL(activity()), SLOT(slotActivity()) );
163 greet->start();
165 capsLocked();
168 PasswordDlg::~PasswordDlg()
170 hide();
171 delete greet;
174 void PasswordDlg::updateLabel()
176 if (mUnlockingFailed)
178 QPalette palette;
179 KColorScheme::adjustForeground(palette, KColorScheme::NormalText, QPalette::WindowText);
180 mStatusLabel->setPalette(palette);
181 mStatusLabel->setText(i18n("<b>Unlocking failed</b>"));
183 else
184 if (mCapsLocked)
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>"));
191 else
193 mStatusLabel->setText("<b> </b>");
197 //---------------------------------------------------------------------------
199 // Handle timer events.
201 void PasswordDlg::timerEvent(QTimerEvent *ev)
203 if (ev->timerId() == mTimeoutTimerId)
205 done(TIMEOUT_CODE);
207 else if (ev->timerId() == mFailedTimerId)
209 killTimer(mFailedTimerId);
210 mFailedTimerId = 0;
211 // Show the normal password prompt.
212 mUnlockingFailed = false;
213 updateLabel();
214 ok->setEnabled(true);
215 cancel->setEnabled(true);
216 mNewSessButton->setEnabled( true );
217 greet->revive();
218 greet->start();
222 bool PasswordDlg::eventFilter(QObject *, QEvent *ev)
224 if (ev->type() == QEvent::KeyPress || ev->type() == QEvent::KeyRelease)
225 capsLocked();
226 return false;
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)
241 int ret, rlen;
243 for (rlen = 0; rlen < count; ) {
244 dord:
245 ret = ::read (sFd, (void *)((char *)buf + rlen), count - rlen);
246 if (ret < 0) {
247 if (errno == EINTR)
248 goto dord;
249 if (errno == EAGAIN)
250 break;
251 return -1;
253 if (!ret)
254 break;
255 rlen += ret;
257 return 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)
293 int len;
294 char *buf;
296 if (!GRecvInt(&len))
297 return false;
298 if (!len) {
299 *ret = 0;
300 return true;
302 if (!(buf = (char *)::malloc (len)))
303 return false;
304 *ret = buf;
305 if (GRead (buf, len)) {
306 return true;
307 } else {
308 ::free(buf);
309 *ret = 0;
310 return false;
314 void PasswordDlg::reapVerify()
316 ::close( sFd );
317 int status;
318 ::waitpid( sPid, &status, 0 );
319 if (WIFEXITED(status))
320 switch (WEXITSTATUS(status)) {
321 case AuthOk:
322 greet->succeeded();
323 accept();
324 return;
325 case AuthBad:
326 greet->failed();
327 mUnlockingFailed = true;
328 updateLabel();
329 mFailedTimerId = startTimer(1500);
330 ok->setEnabled(false);
331 cancel->setEnabled(false);
332 mNewSessButton->setEnabled( false );
333 return;
334 case AuthAbort:
335 return;
337 cantCheck();
340 void PasswordDlg::handleVerify()
342 int ret;
343 char *arr;
345 while (GRecvInt( &ret )) {
346 switch (ret) {
347 case ConvGetBinary:
348 if (!GRecvArr( &arr ))
349 break;
350 greet->binaryPrompt( arr, false );
351 if (arr)
352 ::free( arr );
353 return;
354 case ConvGetNormal:
355 if (!GRecvArr( &arr ))
356 break;
357 greet->textPrompt( arr, true, false );
358 if (arr)
359 ::free( arr );
360 return;
361 case ConvGetHidden:
362 if (!GRecvArr( &arr ))
363 break;
364 greet->textPrompt( arr, false, false );
365 if (arr)
366 ::free( arr );
367 return;
368 case ConvPutInfo:
369 if (!GRecvArr( &arr ))
370 break;
371 if (!greet->textMessage( arr, false ))
372 static_cast< LockProcess* >(parent())->msgBox( this, QMessageBox::Information, QString::fromLocal8Bit( arr ) );
373 ::free( arr );
374 continue;
375 case ConvPutError:
376 if (!GRecvArr( &arr ))
377 break;
378 if (!greet->textMessage( arr, true ))
379 static_cast< LockProcess* >(parent())->msgBox( this, QMessageBox::Warning, QString::fromLocal8Bit( arr ) );
380 ::free( arr );
381 continue;
383 break;
385 reapVerify();
388 ////// greeter plugin callbacks
390 void PasswordDlg::gplugReturnText( const char *text, int tag )
392 GSendStr( text );
393 if (text)
394 GSendInt( tag );
395 handleVerify();
398 void PasswordDlg::gplugReturnBinary( const char *data )
400 if (data) {
401 unsigned const char *up = (unsigned const char *)data;
402 int len = up[3] | (up[2] << 8) | (up[1] << 16) | (up[0] << 24);
403 if (!len)
404 GSendArr( 4, data );
405 else
406 GSendArr( len, data );
407 } else
408 GSendArr( 0, 0 );
409 handleVerify();
412 void PasswordDlg::gplugSetUser( const QString & )
414 // ignore ...
417 void PasswordDlg::cantCheck()
419 greet->failed();
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()) );
423 greet->revive();
426 //---------------------------------------------------------------------------
428 // Starts the kcheckpass process to check the user's password.
430 void PasswordDlg::gplugStart()
432 int sfd[2];
433 char fdbuf[16];
435 if (::socketpair(AF_LOCAL, SOCK_STREAM, 0, sfd)) {
436 cantCheck();
437 return;
439 if ((sPid = ::fork()) < 0) {
440 ::close(sfd[0]);
441 ::close(sfd[1]);
442 cantCheck();
443 return;
445 if (!sPid) {
446 ::close(sfd[0]);
447 sprintf(fdbuf, "%d", sfd[1]);
448 execlp(QFile::encodeName(KStandardDirs::findExe("kcheckpass")).data(),
449 "kcheckpass",
450 #ifdef HAVE_PAM
451 "-c", KSCREENSAVER_PAM_SERVICE,
452 #endif
453 "-m", mPlugin->info->method,
454 "-S", fdbuf,
455 (char *)0);
456 exit(20);
458 ::close(sfd[1]);
459 sFd = sfd[0];
460 handleVerify();
463 void PasswordDlg::gplugChanged()
467 void PasswordDlg::gplugActivity()
469 slotActivity();
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 & )
479 return false;
482 void PasswordDlg::slotOK()
484 greet->next();
488 void PasswordDlg::setVisible( bool visible )
490 QDialog::setVisible( visible );
492 if ( visible )
493 QApplication::flush();
496 void PasswordDlg::slotStartNewSession()
498 if (!KMessageBox::shouldBeShownContinue( ":confirmNewSession" )) {
499 KDisplayManager().startReserve();
500 return;
503 killTimer(mTimeoutTimerId);
504 mTimeoutTimerId = 0;
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.",
529 7, 8),
530 QStringList(),
531 i18n("&Do not ask again"), &dontAskAgain,
532 KMessageBox::NoExec );
534 int ret = static_cast< LockProcess* >( parent())->execDialog( dialog );
536 delete dialog;
538 if (ret == KDialog::Yes) {
539 if (dontAskAgain)
540 KMessageBox::saveDontShowAgainContinue( ":confirmNewSession" );
541 KDisplayManager().startReserve();
544 mTimeoutTimerId = startTimer(PASSDLG_HIDE_TIMEOUT);
547 class LockListViewItem : public QTreeWidgetItem {
548 public:
549 LockListViewItem( QTreeWidget *parent,
550 const QString &sess, const QString &loc, int _vt )
551 : QTreeWidgetItem( parent )
552 , vt( _vt )
554 setText( 0, sess );
555 setText( 1, loc );
558 int vt;
561 void PasswordDlg::slotSwitchUser()
563 int p = 0;
564 KDisplayManager dm;
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 );
578 KPushButton *btn;
580 SessList sess;
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;
591 QString user, loc;
592 int ns = 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 );
596 if (!(*it).vt)
597 itm->setFlags( itm->flags() & ~Qt::ItemIsEnabled );
598 if ((*it).self) {
599 lv->setCurrentItem( itm );
600 itm->setSelected( true );
602 ns++;
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()) );
627 if (!p)
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()
649 unsigned int lmask;
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;
654 updateLabel();
657 #include "lockdlg.moc"