]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - liblightdm-qt/greeter.cpp
Update liblightdm-qt API to match liblightdm-gobject
[sojka/lightdm.git] / liblightdm-qt / greeter.cpp
1 /*
2  * Copyright (C) 2010-2011 David Edmundson
3  * Copyright (C) 2010-2011 Robert Ancell
4  * Author: David Edmundson <kde@davidedmundson.co.uk>
5  *
6  * This library is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU Lesser General Public License as published by the Free
8  * Software Foundation; either version 3 of the License, or (at your option) any
9  * later version. See http://www.gnu.org/copyleft/lgpl.html the full text of the
10  * license.
11  */
12
13 #include "config.h"
14
15 #include "QLightDM/Greeter"
16 #include "QLightDM/User"
17 #include "QLightDM/SessionsModel"
18
19 #include <security/pam_appl.h>
20
21 #include <QtNetwork/QHostInfo> //needed for localHostName
22 #include <QtCore/QDebug>
23 #include <QtCore/QDir>
24 #include <QtCore/QVariant>
25 #include <QtCore/QSettings>
26 #include <QtCore/QUrl>
27 #include <QtCore/QFile>
28 #include <QtCore/QHash>
29 #include <QtCore/QSocketNotifier>
30 #include <QtDBus/QDBusPendingReply>
31 #include <QtDBus/QDBusInterface>
32 #include <QtDBus/QDBusReply>
33
34 /* Messages from the greeter to the server */
35 typedef enum
36 {
37     GREETER_MESSAGE_CONNECT = 0,
38     GREETER_MESSAGE_LOGIN,
39     GREETER_MESSAGE_LOGIN_AS_GUEST,
40     GREETER_MESSAGE_CONTINUE_AUTHENTICATION,
41     GREETER_MESSAGE_START_SESSION,
42     GREETER_MESSAGE_CANCEL_AUTHENTICATION
43 } GreeterMessage;
44
45 /* Messages from the server to the greeter */
46 typedef enum
47 {
48     SERVER_MESSAGE_CONNECTED = 0,
49     SERVER_MESSAGE_QUIT,
50     SERVER_MESSAGE_PROMPT_AUTHENTICATION,
51     SERVER_MESSAGE_END_AUTHENTICATION,
52     SERVER_MESSAGE_SESSION_FAILED,
53 } ServerMessage;
54
55 #define HEADER_SIZE 8
56
57 using namespace QLightDM;
58
59 class GreeterPrivate
60 {
61 public:
62     SessionsModel *sessionsModel;
63
64     QDBusInterface* lightdmInterface;
65     QDBusInterface* powerManagementInterface;
66     QDBusInterface* consoleKitInterface;
67
68     QHash<QString, QString> hints;
69
70     int toServerFd;
71     int fromServerFd;
72     QSocketNotifier *n;
73     char *readBuffer;
74     int nRead;
75     bool inAuthentication;
76     bool isAuthenticated;
77     QString authenticationUser;
78     int authenticateSequenceNumber;
79     bool cancellingAuthentication;
80 };
81
82
83 Greeter::Greeter(QObject *parent) :
84     QObject(parent),
85     d(new GreeterPrivate)
86 {
87     d->readBuffer = (char *)malloc (HEADER_SIZE);
88     d->nRead = 0;
89     d->sessionsModel = new SessionsModel(this);
90     d->authenticateSequenceNumber = 0;
91 }
92
93 Greeter::~Greeter()
94 {
95     delete d->readBuffer;
96     delete d;
97 }
98
99 static int intLength()
100 {
101     return 4;
102 }
103
104 static int stringLength(QString value)
105 {
106     QByteArray a = value.toUtf8();
107     return intLength() + a.size();
108 }
109
110 void Greeter::writeInt(int value)
111 {
112     char buffer[4];
113     buffer[0] = value >> 24;
114     buffer[1] = (value >> 16) & 0xFF;
115     buffer[2] = (value >> 8) & 0xFF;
116     buffer[3] = value & 0xFF;
117     if (write(d->toServerFd, buffer, intLength()) != intLength()) {
118         qDebug() << "Error writing to server";
119     }
120 }
121
122 void Greeter::writeString(QString value)
123 {
124     QByteArray a = value.toUtf8();
125     writeInt(a.size());
126     if (write(d->toServerFd, a.data(), a.size()) != a.size()) {
127         qDebug() << "Error writing to server";
128     }
129 }
130
131 void Greeter::writeHeader(int id, int length)
132 {
133     writeInt(id);
134     writeInt(length);
135 }
136
137 void Greeter::flush()
138 {
139     fsync(d->toServerFd);
140 }
141
142 int Greeter::getPacketLength()
143 {
144     int offset = intLength();
145     return readInt(&offset);
146 }
147
148 int Greeter::readInt(int *offset)
149 {
150     if(d->nRead - *offset < intLength()) {
151         qDebug() << "Not enough space for int, need " << intLength() << ", got " << (d->nRead - *offset);
152         return 0;
153     }
154
155     char *buffer = d->readBuffer + *offset;
156     int value = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
157     *offset += intLength();
158     return value;
159 }
160
161 QString Greeter::readString(int *offset)
162 {
163     int length = readInt(offset);
164     if(d->nRead - *offset < length) {
165         qDebug() << "Not enough space for string, need " << length << ", got " << (d->nRead - *offset);
166         return "";
167     }
168     char *start = d->readBuffer + *offset;
169     *offset += length;
170     return QString::fromUtf8(start, length);
171 }
172
173 void Greeter::connectToServer()
174 {
175     QDBusConnection busType = QDBusConnection::systemBus();
176     QString ldmBus(qgetenv("LIGHTDM_BUS"));
177     if(ldmBus == QLatin1String("SESSION")) {
178         busType = QDBusConnection::sessionBus();
179     }
180
181     d->lightdmInterface = new QDBusInterface("org.freedesktop.DisplayManager", "/org/freedesktop/DisplayManager", "org.freedesktop.DisplayManager", busType);
182     d->powerManagementInterface = new QDBusInterface("org.freedesktop.PowerManagement","/org/freedesktop/PowerManagement", "org.freedesktop.PowerManagement");
183     d->consoleKitInterface = new QDBusInterface("org.freedesktop.ConsoleKit", "/org/freedesktop/ConsoleKit/Manager", "org.freedesktop.ConsoleKit");
184
185     char* fd = getenv("LIGHTDM_TO_SERVER_FD");
186     if(!fd) {
187        qDebug() << "No LIGHTDM_TO_SERVER_FD environment variable";
188        return;
189     }
190     d->toServerFd = atoi(fd);
191
192     qDebug() << "***connecting to server";
193     QFile toServer;
194     qDebug() << toServer.open(d->toServerFd, QIODevice::WriteOnly);
195
196     fd = getenv("LIGHTDM_FROM_SERVER_FD");
197     if(!fd) {
198        qDebug() << "No LIGHTDM_FROM_SERVER_FD environment variable";
199        return;
200     }
201     d->fromServerFd = atoi(fd);
202
203     d->n = new QSocketNotifier(d->fromServerFd, QSocketNotifier::Read);
204     connect(d->n, SIGNAL(activated(int)), this, SLOT(onRead(int)));
205
206     qDebug() << "Connecting to display manager...";
207     writeHeader(GREETER_MESSAGE_CONNECT, stringLength(VERSION));
208     writeString(VERSION);
209     flush();
210 }
211
212 void Greeter::login(const QString &username)
213 {
214     d->inAuthentication = true;
215     d->isAuthenticated = false;
216     d->cancellingAuthentication = false;
217     d->authenticationUser = username;
218     qDebug() << "Starting authentication for user " << username << "...";
219     writeHeader(GREETER_MESSAGE_LOGIN, intLength() + stringLength(username));
220     d->authenticateSequenceNumber++;
221     writeInt(d->authenticateSequenceNumber);
222     writeString(username);
223     flush();
224 }
225
226 void Greeter::loginAsGuest()
227 {
228     d->authenticateSequenceNumber++;
229     d->inAuthentication = true;
230     d->isAuthenticated = false;
231     d->cancellingAuthentication = false;
232     d->authenticationUser = "";
233     qDebug() << "Starting authentication for guest account";
234     writeHeader(GREETER_MESSAGE_LOGIN_AS_GUEST, intLength());
235     writeInt(d->authenticateSequenceNumber);
236     flush();
237 }
238
239 void Greeter::respond(const QString &response)
240 {
241     qDebug() << "Providing response to display manager";
242     writeHeader(GREETER_MESSAGE_CONTINUE_AUTHENTICATION, intLength() + stringLength(response));
243     // FIXME: Could be multiple response required
244     writeInt(1);
245     writeString(response);
246     flush();
247 }
248
249 void Greeter::cancelAuthentication()
250 {
251     qDebug() << "Cancelling authentication";
252     d->cancellingAuthentication = true;
253     writeHeader(GREETER_MESSAGE_CANCEL_AUTHENTICATION, 0);
254     flush();
255 }
256
257 bool Greeter::inAuthentication() const
258 {
259     return d->inAuthentication;
260 }
261
262 bool Greeter::isAuthenticated() const
263 {
264     return d->isAuthenticated;
265 }
266
267 QString Greeter::authenticationUser() const
268 {
269     return d->authenticationUser;
270 }
271
272 void Greeter::startSession(const QString &session)
273 {
274     qDebug() << "Starting session " << session;
275     writeHeader(GREETER_MESSAGE_START_SESSION, stringLength(session));
276     writeString(session);
277     flush();
278 }
279
280 void Greeter::onRead(int fd)
281 {
282     //qDebug() << "Reading from server";
283
284     int nToRead = HEADER_SIZE;
285     if(d->nRead >= HEADER_SIZE)
286         nToRead += getPacketLength();
287
288     ssize_t nRead;
289     nRead = read(fd, d->readBuffer + d->nRead, nToRead - d->nRead);
290     if(nRead < 0)
291     {
292         qDebug() << "Error reading from server";
293         return;
294     }
295     if (nRead == 0)
296     {
297         qDebug() << "EOF reading from server";
298         return;
299     }
300
301     //qDebug() << "Read " << nRead << "octets";
302     d->nRead += nRead;
303     if(d->nRead != nToRead)
304         return;
305
306     /* If have header, rerun for content */
307     if(d->nRead == HEADER_SIZE)
308     {
309         nToRead = getPacketLength();
310         if(nToRead > 0)
311         {
312             d->readBuffer = (char *)realloc(d->readBuffer, HEADER_SIZE + nToRead);
313             onRead(fd);
314             return;
315         }
316     }
317
318     int offset = 0;
319     int id = readInt(&offset);
320     int length = readInt(&offset);
321     int nMessages, sequenceNumber, returnCode;
322     QString version, username;
323     QString hintString = "";
324     switch(id)
325     {
326     case SERVER_MESSAGE_CONNECTED:
327         version = readString(&offset);
328         while (offset < length)
329         {
330             QString name = readString(&offset);
331             QString value = readString(&offset);
332             hintString.append (" ");
333             hintString.append (name);
334             hintString.append ("=");
335             hintString.append (value);
336         }
337
338         qDebug() << "Connected version=" << version << hintString;
339
340         emit connected();
341         break;
342     case SERVER_MESSAGE_QUIT:
343         qDebug() << "Got quit request from server";
344         emit quit();
345         break;
346     case SERVER_MESSAGE_PROMPT_AUTHENTICATION:
347         sequenceNumber = readInt(&offset);
348
349         if (sequenceNumber == d->authenticateSequenceNumber &&
350             !d->cancellingAuthentication)
351         {
352             nMessages = readInt(&offset);
353             qDebug() << "Prompt user with " << nMessages << " message(s)";
354             for(int i = 0; i < nMessages; i++)
355             {
356                 int msg_style = readInt (&offset);
357                 QString msg = readString (&offset);
358
359                 // FIXME: Should stop on prompts?
360                 switch (msg_style)
361                 {
362                 case PAM_PROMPT_ECHO_OFF:
363                     emit showPrompt(msg, PROMPT_TYPE_SECRET);
364                     break;
365                 case PAM_PROMPT_ECHO_ON:
366                     emit showPrompt(msg, PROMPT_TYPE_QUESTION);
367                     break;
368                 case PAM_ERROR_MSG:
369                     emit showMessage(msg, MESSAGE_TYPE_ERROR);
370                     break;
371                 case PAM_TEXT_INFO:
372                     emit showMessage(msg, MESSAGE_TYPE_INFO);
373                     break;
374                 }
375             }
376         }
377         break;
378     case SERVER_MESSAGE_END_AUTHENTICATION:
379         sequenceNumber = readInt(&offset);
380         returnCode = readInt(&offset);
381
382         if (sequenceNumber == d->authenticateSequenceNumber)
383         {
384             qDebug() << "Authentication complete with return code " << returnCode;
385             d->cancellingAuthentication = false;
386             d->isAuthenticated = (returnCode == 0);
387             if(!d->isAuthenticated) {
388                 d->authenticationUser = "";
389             }
390             emit authenticationComplete();
391             d->inAuthentication = false;
392         }
393         else
394             qDebug () << "Ignoring end authentication with invalid sequence number " << sequenceNumber;
395         break;
396     case SERVER_MESSAGE_SESSION_FAILED:
397         qDebug() << "Session failed to start";
398         emit sessionFailed();
399         break;
400     default:
401         qDebug() << "Unknown message from server: " << id;
402     }
403
404     d->nRead = 0;
405 }
406
407 QString Greeter::hostname() const
408 {
409     return QHostInfo::localHostName();
410 }
411
412 QString Greeter::defaultLanguage() const
413 {
414     return getenv("LANG");
415 }
416
417 QString Greeter::getHint(QString name) const
418 {
419     return d->hints.value (name);
420 }
421
422 QString Greeter::defaultSessionHint() const
423 {
424     return getHint ("default-session");
425 }
426
427 bool Greeter::hideUsersHint() const
428 {
429     return d->hints.value ("hide-users", "true") == "true";
430 }
431
432 bool Greeter::hasGuestAccountHint() const
433 {
434     return d->hints.value ("has-guest-account", "false") == "true";
435 }
436
437 QString Greeter::selectUserHint() const
438 {
439     return getHint ("select-user");
440 }
441
442 bool Greeter::selectGuestHint() const
443 {
444     return d->hints.value ("select-guest", "false") == "true";
445 }
446
447 QString Greeter::autologinUserHint() const
448 {
449     return getHint ("autologin-user");
450 }
451
452 bool Greeter::autologinGuestHint() const
453 {
454     return d->hints.value ("autologin-guest", "false") == "true";
455 }
456
457 int Greeter::autologinTimeoutHint() const
458 {
459     return d->hints.value ("autologin-timeout", "0").toInt ();
460 }
461
462 bool Greeter::canSuspend() const
463 {
464     QDBusReply<bool> reply = d->powerManagementInterface->call("CanSuspend");
465     if (reply.isValid())
466         return reply.value();
467     else
468         return false;
469 }
470
471 void Greeter::suspend()
472 {
473     d->powerManagementInterface->call("Suspend");
474 }
475
476 bool Greeter::canHibernate() const
477 {
478     QDBusReply<bool> reply = d->powerManagementInterface->call("CanHibernate");
479     if (reply.isValid())
480         return reply.value();
481     else
482         return false;
483 }
484
485 void Greeter::hibernate()
486 {
487     d->powerManagementInterface->call("Hibernate");
488 }
489
490 bool Greeter::canShutdown() const
491 {
492     QDBusReply<bool> reply = d->consoleKitInterface->call("CanStop");
493     if (reply.isValid())
494         return reply.value();
495     else
496         return false;
497 }
498
499 void Greeter::shutdown()
500 {
501     d->consoleKitInterface->call("stop");
502 }
503
504 bool Greeter::canRestart() const
505 {
506     QDBusReply<bool> reply = d->consoleKitInterface->call("CanRestart");
507     if (reply.isValid())
508         return reply.value();
509     else
510         return false;
511 }
512
513 void Greeter::restart()
514 {
515     d->consoleKitInterface->call("Restart");
516 }