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