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