~ubuntu-branches/ubuntu/utopic/kde-workspace/utopic-proposed

« back to all changes in this revision

Viewing changes to kdm/kfrontend/kgverify.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Michał Zając
  • Date: 2011-07-09 08:31:15 UTC
  • Revision ID: james.westby@ubuntu.com-20110709083115-ohyxn6z93mily9fc
Tags: upstream-4.6.90
Import upstream version 4.6.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 
 
3
Shell for kdm conversation plugins
 
4
 
 
5
Copyright (C) 1997, 1998, 2000 Steffen Hansen <hansen@kde.org>
 
6
Copyright (C) 2000-2004 Oswald Buddenhagen <ossi@kde.org>
 
7
 
 
8
 
 
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.
 
13
 
 
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.
 
18
 
 
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.
 
22
 
 
23
*/
 
24
 
 
25
#include <config-workspace.h>
 
26
 
 
27
#include "kgverify.h"
 
28
#include "kdmconfig.h"
 
29
#include "kdm_greet.h"
 
30
 
 
31
#include "themer/kdmthemer.h"
 
32
#include "themer/kdmitem.h"
 
33
 
 
34
#include <KColorScheme>
 
35
#include <kguiitem.h>
 
36
#include <klibrary.h>
 
37
#include <klocale.h>
 
38
#include <kpushbutton.h>
 
39
#include <krandom.h>
 
40
#include <kseparator.h>
 
41
#include <KStandardGuiItem>
 
42
 
 
43
#include <QAction>
 
44
#include <QApplication>
 
45
#include <QEvent>
 
46
#include <QKeyEvent>
 
47
#include <QLabel>
 
48
#include <QMenu>
 
49
#include <QSocketNotifier>
 
50
#include <QX11Info>
 
51
 
 
52
#include <X11/Xlib.h> // for updateLockStatus()
 
53
#include <fixx11h.h> // ... and make eventFilter() work again
 
54
 
 
55
#define FULL_GREET_TO 40 // normal inactivity timeout
 
56
#define TIMED_GREET_TO 20 // inactivity timeout when persisting timed login
 
57
#define MIN_TIMED_TO 5 // minimal timed login delay
 
58
#define DEAD_TIMED_TO 2 // <enter> dead time after re-activating timed login
 
59
#define SECONDS 1000 // reduce to 100 to speed up testing
 
60
 
 
61
void KGVerifyHandler::verifyClear()
 
62
{
 
63
}
 
64
 
 
65
void KGVerifyHandler::updateStatus(bool, bool, int)
 
66
{
 
67
}
 
68
 
 
69
KGVerify::KGVerify(KGVerifyHandler *_handler,
 
70
                   QWidget *_parent, QWidget *_predecessor,
 
71
                   const QString &_fixedUser,
 
72
                   const PluginList &_pluginList,
 
73
                   KGreeterPlugin::Function _func,
 
74
                   KGreeterPlugin::Context _ctx)
 
75
    : inherited()
 
76
    , coreState(CoreIdle)
 
77
    , fixedEntity(_fixedUser)
 
78
    , pluginList(_pluginList)
 
79
    , handler(_handler)
 
80
    , parent(_parent)
 
81
    , predecessor(_predecessor)
 
82
    , plugMenu(0)
 
83
    , curPlugin(-1)
 
84
    , timedLeft(0)
 
85
    , func(_func)
 
86
    , ctx(_ctx)
 
87
    , enabled(true)
 
88
    , running(false)
 
89
    , suspended(false)
 
90
    , failed(false)
 
91
    , isClear(true)
 
92
{
 
93
    sockNot = new QSocketNotifier(rfd, QSocketNotifier::Read, this);
 
94
    sockNot->setEnabled(false);
 
95
    connect(sockNot, SIGNAL(activated(int)), SLOT(handleVerify()));
 
96
 
 
97
    connect(&timer, SIGNAL(timeout()), SLOT(slotTimeout()));
 
98
    connect(qApp, SIGNAL(activity()), SLOT(slotActivity()));
 
99
 
 
100
    _parent->installEventFilter(this);
 
101
}
 
102
 
 
103
KGVerify::~KGVerify()
 
104
{
 
105
    debug("delete %s\n", pName.data());
 
106
    delete greet;
 
107
}
 
108
 
 
109
QMenu *
 
110
KGVerify::getPlugMenu()
 
111
{
 
112
    // assert(coreState != CoreBusy);
 
113
    if (!plugMenu) {
 
114
        uint np = pluginList.count();
 
115
        if (np > 1) {
 
116
            plugMenu = new QMenu(parent);
 
117
            QActionGroup *plugGroup = new QActionGroup(parent);
 
118
            connect(plugMenu, SIGNAL(triggered(QAction *)),
 
119
                    SLOT(slotPluginSelected(QAction *)));
 
120
            for (uint i = 0; i < np; i++) {
 
121
                int pid = pluginList[i];
 
122
                greetPlugins[pid].action = plugGroup->addAction(
 
123
                    i18nc("@item:inmenu authentication method",
 
124
                          greetPlugins[pid].info->name));
 
125
                greetPlugins[pid].action->setData(i);
 
126
                greetPlugins[pid].action->setCheckable(true);
 
127
            }
 
128
            plugMenu->addActions(plugGroup->actions());
 
129
        }
 
130
    }
 
131
    return plugMenu;
 
132
}
 
133
 
 
134
bool // public
 
135
KGVerify::entitiesLocal() const
 
136
{
 
137
    return greetPlugins[pluginList[curPlugin]].info->flags & KGreeterPluginInfo::Local;
 
138
}
 
139
 
 
140
bool // public
 
141
KGVerify::entitiesFielded() const
 
142
{
 
143
    return greetPlugins[pluginList[curPlugin]].info->flags & KGreeterPluginInfo::Fielded;
 
144
}
 
145
 
 
146
bool // public
 
147
KGVerify::entityPresettable() const
 
148
{
 
149
    return greetPlugins[pluginList[curPlugin]].info->flags & KGreeterPluginInfo::Presettable;
 
150
}
 
151
 
 
152
bool // public
 
153
KGVerify::isClassic() const
 
154
{
 
155
    return !strcmp(greetPlugins[pluginList[curPlugin]].info->method, "classic");
 
156
}
 
157
 
 
158
QString // public
 
159
KGVerify::pluginName() const
 
160
{
 
161
    QString name(greetPlugins[pluginList[curPlugin]].library->fileName());
 
162
    uint st = name.lastIndexOf('/') + 1;
 
163
    uint en = name.indexOf('.', st);
 
164
    if (en - st > 7 && QString::fromRawData(name.unicode() + st, 7) == QLatin1String("kgreet_"))
 
165
        st += 7;
 
166
    return name.mid(st, en - st);
 
167
}
 
168
 
 
169
static void
 
170
setTabOrder(QWidget *&pred, const QObjectList &list)
 
171
{
 
172
    foreach (QObject *o, list)
 
173
        if (QWidget *w = qobject_cast<QWidget *>(o)) {
 
174
            if (w->focusPolicy() & Qt::TabFocus) {
 
175
                QWidget::setTabOrder(pred, w);
 
176
                pred = w;
 
177
            } else {
 
178
                setTabOrder(pred, o->children());
 
179
            }
 
180
        }
 
181
}
 
182
 
 
183
void // public
 
184
KGVerify::selectPlugin(int id)
 
185
{
 
186
    if (pluginList.isEmpty()) {
 
187
        msgBox(errorbox, i18n("No greeter widget plugin loaded. Check the configuration."));
 
188
        ::exit(EX_UNMANAGE_DPY);
 
189
    }
 
190
    curPlugin = id;
 
191
    if (plugMenu)
 
192
        greetPlugins[pluginList[id]].action->setChecked(true);
 
193
    pName = ("greet_" + pluginName()).toLatin1();
 
194
    debug("new %s\n", pName.data());
 
195
    greet = greetPlugins[pluginList[id]].info->create(this, parent, fixedEntity, func, ctx);
 
196
    if (QWidget *pred = predecessor)
 
197
        setTabOrder(pred, *(const QObjectList *)&greet->getWidgets());
 
198
    timeable = _autoLoginDelay && entityPresettable() && isClassic();
 
199
}
 
200
 
 
201
void // private slot
 
202
KGVerify::slotPluginSelected(QAction *action)
 
203
{
 
204
    if (failed)
 
205
        return;
 
206
    int id = action->data().toInt();
 
207
    if (id != curPlugin) {
 
208
        parent->setUpdatesEnabled(false);
 
209
        debug("delete %s\n", pName.data());
 
210
        delete greet;
 
211
        selectPlugin(id);
 
212
        handler->verifyPluginChanged(id);
 
213
        if (running)
 
214
            start();
 
215
        parent->setUpdatesEnabled(true);
 
216
    }
 
217
}
 
218
 
 
219
void // public
 
220
KGVerify::loadUsers(const QStringList &users)
 
221
{
 
222
    debug("%s->loadUsers(...)\n", pName.data());
 
223
    greet->loadUsers(users);
 
224
}
 
225
 
 
226
void // public
 
227
KGVerify::presetEntity(const QString &entity, int field)
 
228
{
 
229
    presEnt = entity;
 
230
    presFld = field;
 
231
}
 
232
 
 
233
bool // private
 
234
KGVerify::applyPreset()
 
235
{
 
236
    if (!presEnt.isEmpty()) {
 
237
        debug("%s->presetEntity(%\"s, %d)\n", pName.data(),
 
238
              qPrintable(presEnt), presFld);
 
239
        greet->presetEntity(presEnt, presFld);
 
240
        if (entitiesLocal()) {
 
241
            curUser = presEnt;
 
242
            pamUser.clear();
 
243
            handler->verifySetUser(presEnt);
 
244
        }
 
245
        return true;
 
246
    }
 
247
    return false;
 
248
}
 
249
 
 
250
bool // private
 
251
KGVerify::scheduleAutoLogin(bool initial)
 
252
{
 
253
    if (timeable) {
 
254
        debug("%s->presetEntity(%\"s, -1)\n", pName.data(),
 
255
              qPrintable(_autoLoginUser), -1);
 
256
        greet->presetEntity(_autoLoginUser, -1);
 
257
        curUser = _autoLoginUser;
 
258
        handler->verifySetUser(_autoLoginUser);
 
259
        timer.start(1000);
 
260
        if (initial) {
 
261
            timedLeft = _autoLoginDelay;
 
262
            deadTicks = 0;
 
263
        } else {
 
264
            timedLeft = qMax(_autoLoginDelay - TIMED_GREET_TO, MIN_TIMED_TO);
 
265
            deadTicks = DEAD_TIMED_TO;
 
266
        }
 
267
        updateStatus();
 
268
        sockNot->setEnabled(false);
 
269
        running = false;
 
270
        isClear = true;
 
271
        return true;
 
272
    }
 
273
    return false;
 
274
}
 
275
 
 
276
void // private
 
277
KGVerify::performAutoLogin()
 
278
{
 
279
//    timer.stop();
 
280
    gSendInt(G_AutoLogin);
 
281
    coreState = CoreBusy;
 
282
    sockNot->setEnabled(true);
 
283
}
 
284
 
 
285
QString // public
 
286
KGVerify::getEntity() const
 
287
{
 
288
    debug("%s->getEntity()\n", pName.data());
 
289
    QString ent = greet->getEntity();
 
290
    debug("  entity: %s\n", qPrintable(ent));
 
291
    return ent;
 
292
}
 
293
 
 
294
void
 
295
KGVerify::setUser(const QString &user)
 
296
{
 
297
    // assert(fixedEntity.isEmpty());
 
298
    curUser = user;
 
299
    debug("%s->setUser(%\"s)\n", pName.data(), qPrintable(user));
 
300
    greet->setUser(user);
 
301
    talkerEdits();
 
302
}
 
303
 
 
304
void
 
305
KGVerify::start()
 
306
{
 
307
    authTok = (func == KGreeterPlugin::ChAuthTok);
 
308
    if (func == KGreeterPlugin::Authenticate && ctx == KGreeterPlugin::Login) {
 
309
        if (scheduleAutoLogin(true)) {
 
310
            if (!_autoLoginAgain)
 
311
                _autoLoginDelay = 0, timeable = false;
 
312
            return;
 
313
        } else {
 
314
            applyPreset();
 
315
        }
 
316
        if (_isReserve)
 
317
            timer.start(FULL_GREET_TO * SECONDS);
 
318
    }
 
319
    sockNot->setEnabled(true);
 
320
    running = true;
 
321
    if (!(func == KGreeterPlugin::Authenticate ||
 
322
          ctx == KGreeterPlugin::ChangeTok ||
 
323
          ctx == KGreeterPlugin::ExChangeTok))
 
324
    {
 
325
        coreState = CoreBusy;
 
326
    }
 
327
    debug("%s->start()\n", pName.data());
 
328
    greet->start();
 
329
}
 
330
 
 
331
void
 
332
KGVerify::abort()
 
333
{
 
334
    debug("%s->abort()\n", pName.data());
 
335
    greet->abort();
 
336
    sockNot->setEnabled(false);
 
337
    running = false;
 
338
}
 
339
 
 
340
void
 
341
KGVerify::suspend()
 
342
{
 
343
    // assert(coreState != CoreBusy);
 
344
    if (running) {
 
345
        debug("%s->abort()\n", pName.data());
 
346
        greet->abort();
 
347
    }
 
348
    suspended = true;
 
349
    updateStatus();
 
350
    timer.suspend();
 
351
    sockNot->setEnabled(false);
 
352
}
 
353
 
 
354
void
 
355
KGVerify::resume()
 
356
{
 
357
    sockNot->setEnabled(true);
 
358
    timer.resume();
 
359
    suspended = false;
 
360
    updateLockStatus();
 
361
    if (running) {
 
362
        debug("%s->start()\n", pName.data());
 
363
        greet->start();
 
364
    } else if (delayed) {
 
365
        delayed = false;
 
366
        running = true;
 
367
        sockNot->setEnabled(true);
 
368
        debug("%s->start()\n", pName.data());
 
369
        greet->start();
 
370
    }
 
371
}
 
372
 
 
373
void // not a slot - called manually by greeter
 
374
KGVerify::accept()
 
375
{
 
376
    debug("%s->next()\n", pName.data());
 
377
    greet->next();
 
378
}
 
379
 
 
380
void // private
 
381
KGVerify::doReject(bool initial)
 
382
{
 
383
    // assert(coreState != CoreBusy);
 
384
    if (running) {
 
385
        debug("%s->abort()\n", pName.data());
 
386
        greet->abort();
 
387
    }
 
388
    handler->verifyClear();
 
389
    debug("%s->clear()\n", pName.data());
 
390
    greet->clear();
 
391
    curUser.clear();
 
392
    pamUser.clear();
 
393
    if (!scheduleAutoLogin(initial)) {
 
394
        isClear = !(isClear && applyPreset());
 
395
        if (running) {
 
396
            debug("%s->start()\n", pName.data());
 
397
            greet->start();
 
398
        }
 
399
        if (!failed)
 
400
            timer.stop();
 
401
    }
 
402
}
 
403
 
 
404
void // not a slot - called manually by greeter
 
405
KGVerify::reject()
 
406
{
 
407
    doReject(true);
 
408
}
 
409
 
 
410
void
 
411
KGVerify::setEnabled(bool on)
 
412
{
 
413
    debug("%s->setEnabled(%s)\n", pName.data(), on ? "true" : "false");
 
414
    greet->setEnabled(on);
 
415
    enabled = on;
 
416
    updateStatus();
 
417
}
 
418
 
 
419
void // private
 
420
KGVerify::slotTimeout()
 
421
{
 
422
    if (failed) {
 
423
        failed = false;
 
424
        updateStatus();
 
425
        debug("%s->revive()\n", pName.data());
 
426
        greet->revive();
 
427
        handler->verifyRetry();
 
428
        if (suspended) {
 
429
            delayed = true;
 
430
        } else {
 
431
            running = true;
 
432
            sockNot->setEnabled(true);
 
433
            debug("%s->start()\n", pName.data());
 
434
            greet->start();
 
435
            slotActivity();
 
436
            talkerEdits();
 
437
        }
 
438
    } else if (timedLeft) {
 
439
        deadTicks--;
 
440
        if (!--timedLeft)
 
441
            performAutoLogin();
 
442
        else
 
443
            timer.start(1000);
 
444
        updateStatus();
 
445
    } else if (_isReserve) {
 
446
        // assert(ctx == Login && running);
 
447
        abort();
 
448
        ::exit(EX_RESERVE);
 
449
    } else {
 
450
        // assert(ctx == Login);
 
451
        isClear = true;
 
452
        doReject(false);
 
453
    }
 
454
}
 
455
 
 
456
void
 
457
KGVerify::slotActivity()
 
458
{
 
459
    if (timedLeft) {
 
460
        // timed login countdown running. cancel and reschedule it.
 
461
        debug("%s->revive()\n", pName.data());
 
462
        greet->revive();
 
463
        debug("%s->start()\n", pName.data());
 
464
        greet->start();
 
465
        sockNot->setEnabled(true);
 
466
        running = true;
 
467
        timedLeft = 0;
 
468
        updateStatus();
 
469
        timer.start(TIMED_GREET_TO * SECONDS);
 
470
    } else if (timeable) {
 
471
        // timed login is possible and thus scheduled. reschedule it.
 
472
        timer.start(TIMED_GREET_TO * SECONDS);
 
473
    } else if (_isReserve) {
 
474
        timer.start(FULL_GREET_TO * SECONDS);
 
475
    }
 
476
}
 
477
 
 
478
void
 
479
KGVerify::talkerEdits()
 
480
{
 
481
    if (func == KGreeterPlugin::Authenticate &&
 
482
        ctx == KGreeterPlugin::Login)
 
483
    {
 
484
        isClear = false;
 
485
        if (!timeable)
 
486
            timer.start(FULL_GREET_TO * SECONDS);
 
487
    }
 
488
}
 
489
 
 
490
 
 
491
void // private static
 
492
KGVerify::vrfMsgBox(QWidget *parent, const QString &user,
 
493
                    QMessageBox::Icon type, const QString &mesg)
 
494
{
 
495
    KFMsgBox::box(parent, type, user.isEmpty() ?
 
496
                  mesg : i18n("Logging in %1...\n\n", user) + mesg);
 
497
}
 
498
 
 
499
static const char * const msgs[] = {
 
500
    I18N_NOOP("You are required to change your password immediately (password aged)."),
 
501
    I18N_NOOP("You are required to change your password immediately (root enforced)."),
 
502
    I18N_NOOP("You are not allowed to login at the moment."),
 
503
    I18N_NOOP("Home folder not available."),
 
504
    I18N_NOOP("Logins are not allowed at the moment.\nTry again later."),
 
505
    I18N_NOOP("Your login shell is not listed in /etc/shells."),
 
506
    I18N_NOOP("Root logins are not allowed."),
 
507
    I18N_NOOP("Your account has expired; please contact your system administrator.")
 
508
};
 
509
 
 
510
void // private static
 
511
KGVerify::vrfErrBox(QWidget *parent, const QString &user, const char *msg)
 
512
{
 
513
    QMessageBox::Icon icon;
 
514
    QString mesg;
 
515
 
 
516
    if (!msg) {
 
517
        mesg = i18n("A critical error occurred.\n"
 
518
                    "Please look at KDM's logfile(s) for more information\n"
 
519
                    "or contact your system administrator.");
 
520
        icon = errorbox;
 
521
    } else {
 
522
        mesg = QString::fromLocal8Bit(msg);
 
523
        QString mesg1 = mesg + '.';
 
524
        for (uint i = 0; i < as(msgs); i++)
 
525
            if (mesg1 == msgs[i]) {
 
526
                mesg = i18n(msgs[i]);
 
527
                break;
 
528
            }
 
529
        icon = sorrybox;
 
530
    }
 
531
    vrfMsgBox(parent, user, icon, mesg);
 
532
}
 
533
 
 
534
void // private static
 
535
KGVerify::vrfInfoBox(QWidget *parent, const QString &user, const char *msg)
 
536
{
 
537
    QString mesg = QString::fromLocal8Bit(msg);
 
538
    QRegExp rx("^Warning: your account will expire in (\\d+) day");
 
539
    if (rx.indexIn(mesg) >= 0) {
 
540
        int expire = rx.cap(1).toInt();
 
541
        mesg = expire ?
 
542
            i18np("Your account expires tomorrow.",
 
543
                  "Your account expires in %1 days.", expire) :
 
544
            i18n("Your account expires today.");
 
545
    } else {
 
546
        rx.setPattern("^Warning: your password will expire in (\\d+) day");
 
547
        if (rx.indexIn(mesg) >= 0) {
 
548
            int expire = rx.cap(1).toInt();
 
549
            mesg = expire ?
 
550
                i18np("Your password expires tomorrow.",
 
551
                      "Your password expires in %1 days.", expire) :
 
552
                i18n("Your password expires today.");
 
553
        }
 
554
    }
 
555
    vrfMsgBox(parent, user, infobox, mesg);
 
556
}
 
557
 
 
558
bool // public static
 
559
KGVerify::handleFailVerify(QWidget *parent, bool showUser)
 
560
{
 
561
    char *msg;
 
562
    QString user;
 
563
 
 
564
    debug("handleFailVerify ...\n");
 
565
 
 
566
    if (showUser) {
 
567
        msg = gRecvStr();
 
568
        user = QString::fromLocal8Bit(msg);
 
569
        free(msg);
 
570
    }
 
571
 
 
572
    for (;;) {
 
573
        int ret = gRecvInt();
 
574
 
 
575
        // non-terminal status
 
576
        switch (ret) {
 
577
        /* case V_PUT_USER: cannot happen - we are in "classic" mode */
 
578
        /* case V_PRE_OK: cannot happen - not in ChTok dialog */
 
579
        /* case V_CHTOK: cannot happen - called by non-interactive verify */
 
580
        case V_CHTOK_AUTH:
 
581
            debug(" V_CHTOK_AUTH\n");
 
582
            {
 
583
                QStringList pgs(_pluginsLogin);
 
584
                pgs += _pluginsShutdown;
 
585
                foreach (const QString& pg, pgs)
 
586
                    if (pg == "classic" || pg == "modern") {
 
587
                        pgs = QStringList(pg);
 
588
                        goto gotit;
 
589
                    } else if (pg == "generic") {
 
590
                        pgs = QStringList("modern");
 
591
                        goto gotit;
 
592
                    }
 
593
                pgs = QStringList("classic");
 
594
              gotit:
 
595
                KGChTok chtok(parent, user, init(pgs), 0,
 
596
                              KGreeterPlugin::AuthChAuthTok,
 
597
                              KGreeterPlugin::Login);
 
598
                return chtok.exec();
 
599
            }
 
600
        case V_MSG_ERR:
 
601
            debug(" V_MSG_ERR\n");
 
602
            msg = gRecvStr();
 
603
            debug("  message %\"s\n", msg);
 
604
            vrfErrBox(parent, user, msg);
 
605
            free(msg);
 
606
            gSendInt(0);
 
607
            continue;
 
608
        case V_MSG_INFO_AUTH: // should not happen
 
609
        case V_MSG_INFO:
 
610
            debug(" V_MSG_INFO\n");
 
611
            msg = gRecvStr();
 
612
            debug("  message %\"s\n", msg);
 
613
            vrfInfoBox(parent, user, msg);
 
614
            free(msg);
 
615
            gSendInt(0);
 
616
            continue;
 
617
        }
 
618
 
 
619
        // terminal status
 
620
        switch (ret) {
 
621
        case V_OK:
 
622
            debug(" V_OK\n");
 
623
            return true;
 
624
        case V_AUTH:
 
625
            debug(" V_AUTH\n");
 
626
            vrfMsgBox(parent, user, sorrybox, i18n("Authentication failed"));
 
627
            return false;
 
628
        case V_FAIL:
 
629
            debug(" V_FAIL\n");
 
630
            return false;
 
631
        default:
 
632
            logPanic("Unknown V_xxx code %d from core\n", ret);
 
633
        }
 
634
    }
 
635
}
 
636
 
 
637
void // private
 
638
KGVerify::handleVerify()
 
639
{
 
640
    debug("handleVerify ...\n");
 
641
    QString user;
 
642
    char *msg;
 
643
    int ret, echo, ndelay;
 
644
    KGreeterPlugin::Function nfunc;
 
645
 
 
646
    ret = gRecvInt();
 
647
 
 
648
    // requests
 
649
    coreState = CorePrompting;
 
650
    switch (ret) {
 
651
    case V_GET_TEXT:
 
652
        debug(" V_GET_TEXT\n");
 
653
        msg = gRecvStr();
 
654
        debug("  prompt %\"s\n", msg);
 
655
        echo = gRecvInt();
 
656
        debug("  echo = %d\n", echo);
 
657
        ndelay = gRecvInt();
 
658
        debug("  ndelay = %d\n%s->textPrompt(...)\n", ndelay, pName.data());
 
659
        greet->textPrompt(msg, echo, ndelay);
 
660
        free(msg);
 
661
        return;
 
662
    case V_GET_BINARY:
 
663
        debug(" V_GET_BINARY\n");
 
664
        msg = gRecvArr(&ret);
 
665
        debug("  %d bytes prompt\n", ret);
 
666
        ndelay = gRecvInt();
 
667
        debug("  ndelay = %d\n%s->binaryPrompt(...)\n", ndelay, pName.data());
 
668
        greet->binaryPrompt(msg, ndelay);
 
669
        free(msg);
 
670
        return;
 
671
    }
 
672
 
 
673
    // non-terminal status
 
674
    coreState = CoreBusy;
 
675
    switch (ret) {
 
676
    case V_PUT_USER:
 
677
        debug(" V_PUT_USER\n");
 
678
        msg = gRecvStr();
 
679
        curUser = pamUser = QString::fromLocal8Bit(msg);
 
680
        // greet needs this to be able to return something useful from
 
681
        // getEntity(). but the backend is still unable to tell a domain ...
 
682
        debug("  %s->setUser(%\"s)\n", pName.data(), qPrintable(user));
 
683
        greet->setUser(curUser);
 
684
        handler->verifySetUser(curUser);
 
685
        free(msg);
 
686
        return;
 
687
    case V_PRE_OK: // this is only for func == AuthChAuthTok
 
688
        debug(" V_PRE_OK\n");
 
689
        // With the "classic" method, the wrong user simply cannot be
 
690
        // authenticated, even with the generic plugin. Other methods
 
691
        // could do so, but this applies only to ctx == ChangeTok, which
 
692
        // is not implemented yet.
 
693
        authTok = true;
 
694
        debug("%s->succeeded()\n", pName.data());
 
695
        greet->succeeded();
 
696
        return;
 
697
    case V_MSG_ERR:
 
698
        debug(" V_MSG_ERR\n");
 
699
        timer.suspend();
 
700
        msg = gRecvStr();
 
701
        debug("  %s->textMessage(%\"s, true)\n", pName.data(), msg);
 
702
        if (!greet->textMessage(msg, true)) { // XXX little point in filtering
 
703
            debug("  message passed\n");
 
704
            vrfErrBox(parent, pamUser, msg);
 
705
        } else {
 
706
            debug("  message swallowed\n");
 
707
        }
 
708
        free(msg);
 
709
        gSendInt(0);
 
710
        timer.resume();
 
711
        return;
 
712
    case V_MSG_INFO_AUTH:
 
713
        debug(" V_MSG_INFO_AUTH\n");
 
714
        timer.suspend();
 
715
        msg = gRecvStr();
 
716
        debug("  %s->textMessage(%\"s, false)\n", pName.data(), msg);
 
717
        if (!greet->textMessage(msg, false)) {
 
718
            debug("  message passed\n");
 
719
            vrfInfoBox(parent, pamUser, msg);
 
720
        } else {
 
721
            debug("  message swallowed\n");
 
722
        }
 
723
        free(msg);
 
724
        gSendInt(0);
 
725
        timer.resume();
 
726
        return;
 
727
    case V_MSG_INFO:
 
728
        debug(" V_MSG_INFO\n");
 
729
        timer.suspend();
 
730
        msg = gRecvStr();
 
731
        debug("  display %\"s\n", msg);
 
732
        vrfInfoBox(parent, pamUser, msg);
 
733
        free(msg);
 
734
        gSendInt(0);
 
735
        timer.resume();
 
736
        return;
 
737
    }
 
738
 
 
739
    // terminal status
 
740
    coreState = CoreIdle;
 
741
    running = false;
 
742
    sockNot->setEnabled(false);
 
743
    timer.stop();
 
744
 
 
745
    // These codes are not really terminal as far as the core is concerned,
 
746
    // but the branches as a whole are.
 
747
    if (ret == V_CHTOK_AUTH) {
 
748
        debug(" V_CHTOK_AUTH\n");
 
749
        nfunc = KGreeterPlugin::AuthChAuthTok;
 
750
        user = curUser;
 
751
        goto dchtok;
 
752
    } else if (ret == V_CHTOK) {
 
753
        debug(" V_CHTOK\n");
 
754
        nfunc = KGreeterPlugin::ChAuthTok;
 
755
        user.clear();
 
756
      dchtok:
 
757
        debug("%s->succeeded()\n", pName.data());
 
758
        greet->succeeded();
 
759
        KGChTok chtok(parent, user, pluginList, curPlugin, nfunc, KGreeterPlugin::Login);
 
760
        if (!chtok.exec())
 
761
            goto retry;
 
762
        handler->verifyOk();
 
763
        return;
 
764
    }
 
765
 
 
766
    if (ret == V_OK) {
 
767
        debug(" V_OK\n");
 
768
        if (!fixedEntity.isEmpty()) {
 
769
            debug("  %s->getEntity()\n", pName.data());
 
770
            QString ent = greet->getEntity();
 
771
            debug("  entity %\"s\n", qPrintable(ent));
 
772
            if (ent != fixedEntity) {
 
773
                debug("%s->failed()\n", pName.data());
 
774
                greet->failed();
 
775
                msgBox(sorrybox,
 
776
                       i18n("Authenticated user (%1) does not match requested user (%2).\n",
 
777
                            ent, fixedEntity));
 
778
                goto retry;
 
779
            }
 
780
        }
 
781
        debug("%s->succeeded()\n", pName.data());
 
782
        greet->succeeded();
 
783
        handler->verifyOk();
 
784
        return;
 
785
    }
 
786
 
 
787
    debug("%s->failed()\n", pName.data());
 
788
    greet->failed();
 
789
 
 
790
    if (ret == V_AUTH) {
 
791
        debug(" V_AUTH\n");
 
792
        failed = true;
 
793
        updateStatus();
 
794
        handler->verifyFailed();
 
795
        timer.start(1500 + KRandom::random() / (RAND_MAX / 1000));
 
796
        return;
 
797
    }
 
798
    if (ret != V_FAIL)
 
799
        logPanic("Unknown V_xxx code %d from core\n", ret);
 
800
    debug(" V_FAIL\n");
 
801
  retry:
 
802
    debug("%s->revive()\n", pName.data());
 
803
    greet->revive();
 
804
    sockNot->setEnabled(true);
 
805
    running = true;
 
806
    debug("%s->start()\n", pName.data());
 
807
    greet->start();
 
808
    slotActivity();
 
809
    talkerEdits();
 
810
}
 
811
 
 
812
void
 
813
KGVerify::gplugReturnText(const char *text, int tag)
 
814
{
 
815
    debug("%s: gplugReturnText(%\"s, %d)\n", pName.data(),
 
816
          tag & V_IS_SECRET ? "<masked>" : text, tag);
 
817
    gSendStr(text);
 
818
    if (text) {
 
819
        gSendInt(tag);
 
820
        coreState = CoreBusy;
 
821
    } else {
 
822
        coreState = CoreIdle;
 
823
    }
 
824
}
 
825
 
 
826
void
 
827
KGVerify::gplugReturnBinary(const char *data)
 
828
{
 
829
    if (data) {
 
830
        unsigned const char *up = (unsigned const char *)data;
 
831
        int len = up[3] | (up[2] << 8) | (up[1] << 16) | (up[0] << 24);
 
832
        debug("%s: gplugReturnBinary(%d bytes)\n", pName.data(), len);
 
833
        gSendArr(len, data);
 
834
        coreState = CoreBusy;
 
835
    } else {
 
836
        debug("%s: gplugReturnBinary(NULL)\n", pName.data());
 
837
        gSendArr(0, 0);
 
838
        coreState = CoreIdle;
 
839
    }
 
840
}
 
841
 
 
842
void
 
843
KGVerify::gplugSetUser(const QString &user)
 
844
{
 
845
    debug("%s: gplugSetUser(%\"s)\n", pName.data(), qPrintable(user));
 
846
    curUser = user;
 
847
    handler->verifySetUser(user);
 
848
}
 
849
 
 
850
void
 
851
KGVerify::gplugStart()
 
852
{
 
853
    // XXX handle func != Authenticate
 
854
    if (coreState != CoreIdle)
 
855
        return;
 
856
    debug("%s: gplugStart()\n", pName.data());
 
857
    gSendInt(ctx == KGreeterPlugin::Shutdown ? G_VerifyRootOK : G_Verify);
 
858
    gSendStr(greetPlugins[pluginList[curPlugin]].info->method);
 
859
    coreState = CoreBusy;
 
860
}
 
861
 
 
862
void
 
863
KGVerify::gplugChanged()
 
864
{
 
865
    debug("%s: gplugChanged()\n", pName.data());
 
866
    if (parent->isActiveWindow())
 
867
        talkerEdits();
 
868
}
 
869
 
 
870
void
 
871
KGVerify::gplugActivity()
 
872
{
 
873
    debug("%s: gplugActivity()\n", pName.data());
 
874
    slotActivity();
 
875
}
 
876
 
 
877
void
 
878
KGVerify::gplugMsgBox(QMessageBox::Icon type, const QString &text)
 
879
{
 
880
    debug("%s: gplugMsgBox(%d, %\"s)\n", pName.data(), type, qPrintable(text));
 
881
    msgBox(type, text);
 
882
}
 
883
 
 
884
bool
 
885
KGVerify::eventFilter(QObject *o, QEvent *e)
 
886
{
 
887
    switch (e->type()) {
 
888
    case QEvent::KeyPress:
 
889
        if (timedLeft) {
 
890
            QKeyEvent *ke = (QKeyEvent *)e;
 
891
            if (ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter) {
 
892
                if (deadTicks <= 0) {
 
893
                    timedLeft = 0;
 
894
                    performAutoLogin();
 
895
                }
 
896
                return true;
 
897
            }
 
898
        }
 
899
        /* fall through */
 
900
    case QEvent::KeyRelease:
 
901
        updateLockStatus();
 
902
        /* fall through */
 
903
    default:
 
904
        break;
 
905
    }
 
906
    return inherited::eventFilter(o, e);
 
907
}
 
908
 
 
909
void
 
910
KGVerify::updateLockStatus()
 
911
{
 
912
    unsigned int lmask;
 
913
    Window dummy1, dummy2;
 
914
    int dummy3, dummy4, dummy5, dummy6;
 
915
    XQueryPointer(QX11Info::display(), DefaultRootWindow(QX11Info::display()),
 
916
                  &dummy1, &dummy2, &dummy3, &dummy4, &dummy5, &dummy6,
 
917
                  &lmask);
 
918
    capsLocked = lmask & LockMask;
 
919
    updateStatus();
 
920
}
 
921
 
 
922
void
 
923
KGVerify::msgBox(QMessageBox::Icon typ, const QString &msg)
 
924
{
 
925
    timer.suspend();
 
926
    KFMsgBox::box(parent, typ, msg);
 
927
    timer.resume();
 
928
}
 
929
 
 
930
 
 
931
QVariant // public static
 
932
KGVerify::getConf(void *, const char *key, const QVariant &dflt)
 
933
{
 
934
    if (!qstrcmp(key, "EchoPasswd")) {
 
935
        return QVariant(_echoPasswd);
 
936
    } else {
 
937
        QString fkey = QString::fromLatin1(key) + '=';
 
938
        foreach (const QString& pgo, _pluginOptions)
 
939
            if (pgo.startsWith(fkey))
 
940
                return pgo.mid(fkey.length());
 
941
        return dflt;
 
942
    }
 
943
}
 
944
 
 
945
QVector<GreeterPluginHandle> KGVerify::greetPlugins;
 
946
 
 
947
PluginList
 
948
KGVerify::init(const QStringList &plugins)
 
949
{
 
950
    PluginList pluginList;
 
951
 
 
952
    foreach (const QString& pg, plugins) {
 
953
        GreeterPluginHandle plugin;
 
954
        KLibrary *lib = new KLibrary(pg[0] == '/' ? pg : "kgreet_" + pg);
 
955
        if (lib->fileName().isEmpty()) {
 
956
            logError("GreeterPlugin %s does not exist\n", qPrintable(pg));
 
957
            delete lib;
 
958
            continue;
 
959
        }
 
960
        uint i, np = greetPlugins.count();
 
961
        for (i = 0; i < np; i++)
 
962
            if (greetPlugins[i].library->fileName() == lib->fileName()) {
 
963
                delete lib;
 
964
                goto next;
 
965
            }
 
966
        if (!lib->load()) {
 
967
            logError("Cannot load GreeterPlugin %s (%s)\n",
 
968
                     qPrintable(pg), qPrintable(lib->fileName()));
 
969
            delete lib;
 
970
            continue;
 
971
        }
 
972
        plugin.library = lib;
 
973
        plugin.info = (KGreeterPluginInfo *)lib->resolveSymbol("kgreeterplugin_info");
 
974
        if (!plugin.info) {
 
975
            logError("GreeterPlugin %s (%s) is no valid greet widget plugin\n",
 
976
                     qPrintable(pg), qPrintable(lib->fileName()));
 
977
            lib->unload();
 
978
            delete lib;
 
979
            continue;
 
980
        }
 
981
 
 
982
        if (!plugin.info->init(QString(), getConf, 0)) {
 
983
            logError("GreeterPlugin %s (%s) refuses to serve\n",
 
984
                     qPrintable(pg), qPrintable(lib->fileName()));
 
985
            lib->unload();
 
986
            delete lib;
 
987
            continue;
 
988
        }
 
989
        debug("GreeterPlugin %s (%s) loaded\n", qPrintable(pg), plugin.info->name);
 
990
        plugin.action = 0;
 
991
        greetPlugins.append(plugin);
 
992
      next:
 
993
        pluginList.append(i);
 
994
    }
 
995
    return pluginList;
 
996
}
 
997
 
 
998
void
 
999
KGVerify::done()
 
1000
{
 
1001
    for (int i = 0; i < greetPlugins.count(); i++) {
 
1002
        if (greetPlugins[i].info->done)
 
1003
            greetPlugins[i].info->done();
 
1004
        greetPlugins[i].library->unload();
 
1005
    }
 
1006
}
 
1007
 
 
1008
 
 
1009
KGStdVerify::KGStdVerify(KGVerifyHandler *_handler, QWidget *_parent,
 
1010
                         QWidget *_predecessor, const QString &_fixedUser,
 
1011
                         const PluginList &_pluginList,
 
1012
                         KGreeterPlugin::Function _func,
 
1013
                         KGreeterPlugin::Context _ctx)
 
1014
    : inherited(_handler, _parent, _predecessor, _fixedUser,
 
1015
                _pluginList, _func, _ctx)
 
1016
    , failedLabelState(0)
 
1017
{
 
1018
    grid = new QGridLayout;
 
1019
    grid->setAlignment(Qt::AlignCenter);
 
1020
 
 
1021
    failedLabel = new QLabel(parent);
 
1022
    failedLabel->setFont(*_failFont);
 
1023
    grid->addWidget(failedLabel, 1, 0, Qt::AlignCenter);
 
1024
 
 
1025
    updateLockStatus();
 
1026
}
 
1027
 
 
1028
KGStdVerify::~KGStdVerify()
 
1029
{
 
1030
}
 
1031
 
 
1032
bool
 
1033
KGStdVerify::gplugHasNode(const QString &)
 
1034
{
 
1035
    return false;
 
1036
}
 
1037
 
 
1038
void // public
 
1039
KGStdVerify::selectPlugin(int id)
 
1040
{
 
1041
    inherited::selectPlugin(id);
 
1042
    QWidget *w = greet->getWidgets().first();
 
1043
    grid->addWidget(w, 0, 0);
 
1044
    w->show();
 
1045
}
 
1046
 
 
1047
void
 
1048
KGStdVerify::updateStatus()
 
1049
{
 
1050
    int nfls;
 
1051
 
 
1052
    if (!enabled)
 
1053
        nfls = 1;
 
1054
    else if (failed)
 
1055
        nfls = 2;
 
1056
    else if (timedLeft)
 
1057
        nfls = -timedLeft;
 
1058
    else if (!suspended && capsLocked)
 
1059
        nfls = 3;
 
1060
    else
 
1061
        nfls = 1;
 
1062
 
 
1063
    if (failedLabelState != nfls) {
 
1064
        failedLabelState = nfls;
 
1065
        QPalette p;
 
1066
        if (nfls < 0) {
 
1067
            failedLabel->setText(i18np("Automatic login in 1 second...",
 
1068
                                       "Automatic login in %1 seconds...",
 
1069
                                       timedLeft));
 
1070
        } else {
 
1071
            switch (nfls) {
 
1072
            default:
 
1073
                failedLabel->clear();
 
1074
                break;
 
1075
            case 3:
 
1076
                p.setBrush(QPalette::WindowText,
 
1077
                    KColorScheme(QPalette::Active, KColorScheme::Window)
 
1078
                        .foreground(KColorScheme::NegativeText));
 
1079
                failedLabel->setText(i18n("Warning: Caps Lock is on"));
 
1080
                break;
 
1081
            case 2:
 
1082
                failedLabel->setText(authTok ?
 
1083
                                         i18n("Change failed") :
 
1084
                                         fixedEntity.isEmpty() ?
 
1085
                                            i18n("Login failed") :
 
1086
                                            i18n("Authentication failed"));
 
1087
                break;
 
1088
            }
 
1089
        }
 
1090
        failedLabel->setPalette(p);
 
1091
    }
 
1092
}
 
1093
 
 
1094
KGThemedVerify::KGThemedVerify(KGVerifyHandler *_handler,
 
1095
                               KdmThemer *_themer,
 
1096
                               QWidget *_parent, QWidget *_predecessor,
 
1097
                               const QString &_fixedUser,
 
1098
                               const PluginList &_pluginList,
 
1099
                               KGreeterPlugin::Function _func,
 
1100
                               KGreeterPlugin::Context _ctx)
 
1101
    : inherited(_handler, _parent, _predecessor, _fixedUser,
 
1102
                _pluginList, _func, _ctx)
 
1103
    , themer(_themer)
 
1104
{
 
1105
    updateLockStatus();
 
1106
}
 
1107
 
 
1108
KGThemedVerify::~KGThemedVerify()
 
1109
{
 
1110
}
 
1111
 
 
1112
bool
 
1113
KGThemedVerify::gplugHasNode(const QString &id)
 
1114
{
 
1115
    return themer->findNode(id) != 0;
 
1116
}
 
1117
 
 
1118
void // public
 
1119
KGThemedVerify::selectPlugin(int id)
 
1120
{
 
1121
    if (curPlugin != -1)
 
1122
        themer->setTypeVisible(QString("plugin-specific-").append(pluginName()), false);
 
1123
    inherited::selectPlugin(id);
 
1124
    themer->setTypeVisible(QString("plugin-specific-").append(pluginName()), true);
 
1125
    QSet<QString> oldTypes = showTypes;
 
1126
    showTypes.clear();
 
1127
    foreach (QWidget *w, greet->getWidgets())
 
1128
        if (KdmItem *n = themer->findNode(w->objectName())) {
 
1129
            QString tn(QString("plugin-").append(w->objectName()));
 
1130
            themer->setTypeVisible(tn, true);
 
1131
            showTypes.insert(tn);
 
1132
            oldTypes.remove(tn);
 
1133
            n->setWidget(w);
 
1134
        } else {
 
1135
            msgBox(errorbox,
 
1136
                   i18n("Theme not usable with authentication method '%1'.",
 
1137
                        i18n(greetPlugins[pluginList[id]].info->name)));
 
1138
            break;
 
1139
        }
 
1140
    foreach (const QString &t, oldTypes)
 
1141
        themer->setTypeVisible(t, false);
 
1142
}
 
1143
 
 
1144
void
 
1145
KGThemedVerify::updateStatus()
 
1146
{
 
1147
    handler->updateStatus(enabled && failed,
 
1148
                          enabled && !suspended && capsLocked,
 
1149
                          timedLeft);
 
1150
}
 
1151
 
 
1152
 
 
1153
KGChTok::KGChTok(QWidget *_parent, const QString &user,
 
1154
                 const PluginList &pluginList, int curPlugin,
 
1155
                 KGreeterPlugin::Function func,
 
1156
                 KGreeterPlugin::Context ctx)
 
1157
    : inherited(_parent)
 
1158
    , verify(0)
 
1159
{
 
1160
    QSizePolicy fp(QSizePolicy::Fixed, QSizePolicy::Fixed);
 
1161
    okButton = new KPushButton(KStandardGuiItem::ok(), this);
 
1162
    okButton->setSizePolicy(fp);
 
1163
    okButton->setDefault(true);
 
1164
    cancelButton = new KPushButton(KStandardGuiItem::cancel(), this);
 
1165
    cancelButton->setSizePolicy(fp);
 
1166
 
 
1167
    verify = new KGStdVerify(this, this, cancelButton, user, pluginList, func, ctx);
 
1168
    verify->selectPlugin(curPlugin);
 
1169
 
 
1170
    QVBoxLayout *box = new QVBoxLayout(this);
 
1171
 
 
1172
    box->addWidget(new QLabel(i18nc("@title:window",
 
1173
                                    "<qt><b>Changing authentication token</b></qt>"),
 
1174
                              this), 0, Qt::AlignHCenter | Qt::AlignTop);
 
1175
 
 
1176
    box->addLayout(verify->getLayout());
 
1177
 
 
1178
    box->addWidget(new KSeparator(Qt::Horizontal, this));
 
1179
 
 
1180
    QHBoxLayout *hlay = new QHBoxLayout();
 
1181
    box->addLayout(hlay);
 
1182
    hlay->addStretch(1);
 
1183
    hlay->addWidget(okButton);
 
1184
    hlay->addStretch(1);
 
1185
    hlay->addWidget(cancelButton);
 
1186
    hlay->addStretch(1);
 
1187
 
 
1188
    connect(okButton, SIGNAL(clicked()), SLOT(accept()));
 
1189
    connect(cancelButton, SIGNAL(clicked()), SLOT(reject()));
 
1190
 
 
1191
    QTimer::singleShot(0, verify, SLOT(start()));
 
1192
}
 
1193
 
 
1194
KGChTok::~KGChTok()
 
1195
{
 
1196
    hide();
 
1197
    delete verify;
 
1198
}
 
1199
 
 
1200
void
 
1201
KGChTok::accept()
 
1202
{
 
1203
    verify->accept();
 
1204
}
 
1205
 
 
1206
void
 
1207
KGChTok::verifyPluginChanged(int)
 
1208
{
 
1209
    // cannot happen
 
1210
}
 
1211
 
 
1212
void
 
1213
KGChTok::verifyOk()
 
1214
{
 
1215
    inherited::accept();
 
1216
}
 
1217
 
 
1218
void
 
1219
KGChTok::verifyFailed()
 
1220
{
 
1221
    okButton->setEnabled(false);
 
1222
    cancelButton->setEnabled(false);
 
1223
}
 
1224
 
 
1225
void
 
1226
KGChTok::verifyRetry()
 
1227
{
 
1228
    okButton->setEnabled(true);
 
1229
    cancelButton->setEnabled(true);
 
1230
}
 
1231
 
 
1232
void
 
1233
KGChTok::verifySetUser(const QString &)
 
1234
{
 
1235
    // cannot happen
 
1236
}
 
1237
 
 
1238
 
 
1239
////// helper class, nuke when qtimer supports suspend()/resume()
 
1240
 
 
1241
QXTimer::QXTimer()
 
1242
    : inherited(0)
 
1243
    , left(-1)
 
1244
{
 
1245
    connect(&timer, SIGNAL(timeout()), SLOT(slotTimeout()));
 
1246
}
 
1247
 
 
1248
void
 
1249
QXTimer::start(int msec)
 
1250
{
 
1251
    left = msec;
 
1252
    timer.setSingleShot(true);
 
1253
    timer.start(left);
 
1254
    gettimeofday(&stv, 0);
 
1255
}
 
1256
 
 
1257
void
 
1258
QXTimer::stop()
 
1259
{
 
1260
    timer.stop();
 
1261
    left = -1;
 
1262
}
 
1263
 
 
1264
void
 
1265
QXTimer::suspend()
 
1266
{
 
1267
    if (timer.isActive()) {
 
1268
        timer.stop();
 
1269
        struct timeval tv;
 
1270
        gettimeofday(&tv, 0);
 
1271
        left -= (tv.tv_sec - stv.tv_sec) * 1000 + (tv.tv_usec - stv.tv_usec) / 1000;
 
1272
        if (left < 0)
 
1273
            left = 0;
 
1274
    }
 
1275
}
 
1276
 
 
1277
void
 
1278
QXTimer::resume()
 
1279
{
 
1280
    if (left >= 0 && !timer.isActive()) {
 
1281
        timer.setSingleShot(true);
 
1282
        timer.start(left);
 
1283
        gettimeofday(&stv, 0);
 
1284
    }
 
1285
}
 
1286
 
 
1287
void
 
1288
QXTimer::slotTimeout()
 
1289
{
 
1290
    left = -1;
 
1291
    emit timeout();
 
1292
}
 
1293
 
 
1294
 
 
1295
#include "kgverify.moc"