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