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