]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - liblightdm-qt/greeter.cpp
Split power management code out of QLightDM::Greeter
[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_AUTHENTICATE,
39     GREETER_MESSAGE_AUTHENTICATE_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     QHash<QString, QString> hints;
65
66     int toServerFd;
67     int fromServerFd;
68     QSocketNotifier *n;
69     char *readBuffer;
70     int nRead;
71     bool inAuthentication;
72     bool isAuthenticated;
73     QString authenticationUser;
74     int authenticateSequenceNumber;
75     bool cancellingAuthentication;
76 };
77
78
79 Greeter::Greeter(QObject *parent) :
80     QObject(parent),
81     d(new GreeterPrivate)
82 {
83     d->readBuffer = (char *)malloc (HEADER_SIZE);
84     d->nRead = 0;
85     d->sessionsModel = new SessionsModel(this);
86     d->authenticateSequenceNumber = 0;
87 }
88
89 Greeter::~Greeter()
90 {
91     delete d->readBuffer;
92     delete d;
93 }
94
95 static int intLength()
96 {
97     return 4;
98 }
99
100 static int stringLength(QString value)
101 {
102     QByteArray a = value.toUtf8();
103     return intLength() + a.size();
104 }
105
106 void Greeter::writeInt(int value)
107 {
108     char buffer[4];
109     buffer[0] = value >> 24;
110     buffer[1] = (value >> 16) & 0xFF;
111     buffer[2] = (value >> 8) & 0xFF;
112     buffer[3] = value & 0xFF;
113     if (write(d->toServerFd, buffer, intLength()) != intLength()) {
114         qDebug() << "Error writing to server";
115     }
116 }
117
118 void Greeter::writeString(QString value)
119 {
120     QByteArray a = value.toUtf8();
121     writeInt(a.size());
122     if (write(d->toServerFd, a.data(), a.size()) != a.size()) {
123         qDebug() << "Error writing to server";
124     }
125 }
126
127 void Greeter::writeHeader(int id, int length)
128 {
129     writeInt(id);
130     writeInt(length);
131 }
132
133 void Greeter::flush()
134 {
135     fsync(d->toServerFd);
136 }
137
138 int Greeter::getPacketLength()
139 {
140     int offset = intLength();
141     return readInt(&offset);
142 }
143
144 int Greeter::readInt(int *offset)
145 {
146     if(d->nRead - *offset < intLength()) {
147         qDebug() << "Not enough space for int, need " << intLength() << ", got " << (d->nRead - *offset);
148         return 0;
149     }
150
151     char *buffer = d->readBuffer + *offset;
152     int value = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
153     *offset += intLength();
154     return value;
155 }
156
157 QString Greeter::readString(int *offset)
158 {
159     int length = readInt(offset);
160     if(d->nRead - *offset < length) {
161         qDebug() << "Not enough space for string, need " << length << ", got " << (d->nRead - *offset);
162         return "";
163     }
164     char *start = d->readBuffer + *offset;
165     *offset += length;
166     return QString::fromUtf8(start, length);
167 }
168
169 void Greeter::connectToServer()
170 {
171     QDBusConnection busType = QDBusConnection::systemBus();
172     QString ldmBus(qgetenv("LIGHTDM_BUS"));
173     if(ldmBus == QLatin1String("SESSION")) {
174         busType = QDBusConnection::sessionBus();
175     }
176
177     char* fd = getenv("LIGHTDM_TO_SERVER_FD");
178     if(!fd) {
179        qDebug() << "No LIGHTDM_TO_SERVER_FD environment variable";
180        return;
181     }
182     d->toServerFd = atoi(fd);
183
184     qDebug() << "***connecting to server";
185     QFile toServer;
186     qDebug() << toServer.open(d->toServerFd, QIODevice::WriteOnly);
187
188     fd = getenv("LIGHTDM_FROM_SERVER_FD");
189     if(!fd) {
190        qDebug() << "No LIGHTDM_FROM_SERVER_FD environment variable";
191        return;
192     }
193     d->fromServerFd = atoi(fd);
194
195     d->n = new QSocketNotifier(d->fromServerFd, QSocketNotifier::Read);
196     connect(d->n, SIGNAL(activated(int)), this, SLOT(onRead(int)));
197
198     qDebug() << "Connecting to display manager...";
199     writeHeader(GREETER_MESSAGE_CONNECT, stringLength(VERSION));
200     writeString(VERSION);
201     flush();
202 }
203
204 void Greeter::authenticate(const QString &username)
205 {
206     d->inAuthentication = true;
207     d->isAuthenticated = false;
208     d->cancellingAuthentication = false;
209     d->authenticationUser = username;
210     qDebug() << "Starting authentication for user " << username << "...";
211     writeHeader(GREETER_MESSAGE_AUTHENTICATE, intLength() + stringLength(username));
212     d->authenticateSequenceNumber++;
213     writeInt(d->authenticateSequenceNumber);
214     writeString(username);
215     flush();
216 }
217
218 void Greeter::authenticateAsGuest()
219 {
220     d->authenticateSequenceNumber++;
221     d->inAuthentication = true;
222     d->isAuthenticated = false;
223     d->cancellingAuthentication = false;
224     d->authenticationUser = "";
225     qDebug() << "Starting authentication for guest account";
226     writeHeader(GREETER_MESSAGE_AUTHENTICATE_AS_GUEST, intLength());
227     writeInt(d->authenticateSequenceNumber);
228     flush();
229 }
230
231 void Greeter::respond(const QString &response)
232 {
233     qDebug() << "Providing response to display manager";
234     writeHeader(GREETER_MESSAGE_CONTINUE_AUTHENTICATION, intLength() + stringLength(response));
235     // FIXME: Could be multiple response required
236     writeInt(1);
237     writeString(response);
238     flush();
239 }
240
241 void Greeter::cancelAuthentication()
242 {
243     qDebug() << "Cancelling authentication";
244     d->cancellingAuthentication = true;
245     writeHeader(GREETER_MESSAGE_CANCEL_AUTHENTICATION, 0);
246     flush();
247 }
248
249 bool Greeter::inAuthentication() const
250 {
251     return d->inAuthentication;
252 }
253
254 bool Greeter::isAuthenticated() const
255 {
256     return d->isAuthenticated;
257 }
258
259 QString Greeter::authenticationUser() const
260 {
261     return d->authenticationUser;
262 }
263
264 void Greeter::startSession(const QString &session)
265 {
266     qDebug() << "Starting session " << session;
267     writeHeader(GREETER_MESSAGE_START_SESSION, stringLength(session));
268     writeString(session);
269     flush();
270 }
271
272 void Greeter::onRead(int fd)
273 {
274     //qDebug() << "Reading from server";
275
276     int nToRead = HEADER_SIZE;
277     if(d->nRead >= HEADER_SIZE)
278         nToRead += getPacketLength();
279
280     ssize_t nRead;
281     nRead = read(fd, d->readBuffer + d->nRead, nToRead - d->nRead);
282     if(nRead < 0)
283     {
284         qDebug() << "Error reading from server";
285         return;
286     }
287     if (nRead == 0)
288     {
289         qDebug() << "EOF reading from server";
290         return;
291     }
292
293     //qDebug() << "Read " << nRead << "octets";
294     d->nRead += nRead;
295     if(d->nRead != nToRead)
296         return;
297
298     /* If have header, rerun for content */
299     if(d->nRead == HEADER_SIZE)
300     {
301         nToRead = getPacketLength();
302         if(nToRead > 0)
303         {
304             d->readBuffer = (char *)realloc(d->readBuffer, HEADER_SIZE + nToRead);
305             onRead(fd);
306             return;
307         }
308     }
309
310     int offset = 0;
311     int id = readInt(&offset);
312     int length = readInt(&offset);
313     int nMessages, sequenceNumber, returnCode;
314     QString version, username;
315     QString hintString = "";
316     switch(id)
317     {
318     case SERVER_MESSAGE_CONNECTED:
319         version = readString(&offset);
320         while (offset < length)
321         {
322             QString name = readString(&offset);
323             QString value = readString(&offset);
324             hintString.append (" ");
325             hintString.append (name);
326             hintString.append ("=");
327             hintString.append (value);
328         }
329
330         qDebug() << "Connected version=" << version << hintString;
331
332         emit connected();
333         break;
334     case SERVER_MESSAGE_QUIT:
335         qDebug() << "Got quit request from server";
336         emit quit();
337         break;
338     case SERVER_MESSAGE_PROMPT_AUTHENTICATION:
339         sequenceNumber = readInt(&offset);
340
341         if (sequenceNumber == d->authenticateSequenceNumber &&
342             !d->cancellingAuthentication)
343         {
344             nMessages = readInt(&offset);
345             qDebug() << "Prompt user with " << nMessages << " message(s)";
346             for(int i = 0; i < nMessages; i++)
347             {
348                 int msg_style = readInt (&offset);
349                 QString msg = readString (&offset);
350
351                 // FIXME: Should stop on prompts?
352                 switch (msg_style)
353                 {
354                 case PAM_PROMPT_ECHO_OFF:
355                     emit showPrompt(msg, PROMPT_TYPE_SECRET);
356                     break;
357                 case PAM_PROMPT_ECHO_ON:
358                     emit showPrompt(msg, PROMPT_TYPE_QUESTION);
359                     break;
360                 case PAM_ERROR_MSG:
361                     emit showMessage(msg, MESSAGE_TYPE_ERROR);
362                     break;
363                 case PAM_TEXT_INFO:
364                     emit showMessage(msg, MESSAGE_TYPE_INFO);
365                     break;
366                 }
367             }
368         }
369         break;
370     case SERVER_MESSAGE_END_AUTHENTICATION:
371         sequenceNumber = readInt(&offset);
372         returnCode = readInt(&offset);
373
374         if (sequenceNumber == d->authenticateSequenceNumber)
375         {
376             qDebug() << "Authentication complete with return code " << returnCode;
377             d->cancellingAuthentication = false;
378             d->isAuthenticated = (returnCode == 0);
379             if(!d->isAuthenticated) {
380                 d->authenticationUser = "";
381             }
382             emit authenticationComplete();
383             d->inAuthentication = false;
384         }
385         else
386             qDebug () << "Ignoring end authentication with invalid sequence number " << sequenceNumber;
387         break;
388     case SERVER_MESSAGE_SESSION_FAILED:
389         qDebug() << "Session failed to start";
390         emit sessionFailed();
391         break;
392     default:
393         qDebug() << "Unknown message from server: " << id;
394     }
395
396     d->nRead = 0;
397 }
398
399 QString Greeter::hostname() const
400 {
401     return QHostInfo::localHostName();
402 }
403
404 QString Greeter::defaultLanguage() const
405 {
406     return getenv("LANG");
407 }
408
409 QString Greeter::getHint(QString name) const
410 {
411     return d->hints.value (name);
412 }
413
414 QString Greeter::defaultSessionHint() const
415 {
416     return getHint ("default-session");
417 }
418
419 bool Greeter::hideUsersHint() const
420 {
421     return d->hints.value ("hide-users", "true") == "true";
422 }
423
424 bool Greeter::hasGuestAccountHint() const
425 {
426     return d->hints.value ("has-guest-account", "false") == "true";
427 }
428
429 QString Greeter::selectUserHint() const
430 {
431     return getHint ("select-user");
432 }
433
434 bool Greeter::selectGuestHint() const
435 {
436     return d->hints.value ("select-guest", "false") == "true";
437 }
438
439 QString Greeter::autologinUserHint() const
440 {
441     return getHint ("autologin-user");
442 }
443
444 bool Greeter::autologinGuestHint() const
445 {
446     return d->hints.value ("autologin-guest", "false") == "true";
447 }
448
449 int Greeter::autologinTimeoutHint() const
450 {
451     return d->hints.value ("autologin-timeout", "0").toInt ();
452 }