2 * Copyright (C) 2010-2011 David Edmundson
3 * Copyright (C) 2010-2011 Robert Ancell
4 * Author: David Edmundson <kde@davidedmundson.co.uk>
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
15 #include "QLightDM/Greeter"
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>
30 /* Messages from the greeter to the server */
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
42 /* Messages from the server to the greeter */
45 SERVER_MESSAGE_CONNECTED = 0,
46 SERVER_MESSAGE_PROMPT_AUTHENTICATION,
47 SERVER_MESSAGE_END_AUTHENTICATION,
48 SERVER_MESSAGE_SESSION_RESULT
53 using namespace QLightDM;
58 QHash<QString, QString> hints;
65 bool inAuthentication;
67 QString authenticationUser;
68 int authenticateSequenceNumber;
69 bool cancellingAuthentication;
72 Greeter::Greeter(QObject *parent) :
76 d->readBuffer = (char *)malloc(HEADER_SIZE);
78 d->authenticateSequenceNumber = 0;
87 static int intLength()
92 static int stringLength(QString value)
94 QByteArray a = value.toUtf8();
95 return intLength() + a.size();
98 void Greeter::writeInt(int value)
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";
110 void Greeter::writeString(QString value)
112 QByteArray a = value.toUtf8();
114 if (write(d->toServerFd, a.data(), a.size()) != a.size()) {
115 qDebug() << "Error writing to server";
119 void Greeter::writeHeader(int id, int length)
125 void Greeter::flush()
127 fsync(d->toServerFd);
130 static int readInt(char *message, int messageLength, int *offset)
132 if(messageLength - *offset < intLength()) {
133 qDebug() << "Not enough space for int, need " << intLength() << ", got " << (messageLength - *offset);
137 char *buffer = message + *offset;
138 int value = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
139 *offset += intLength();
143 static int getMessageLength(char *message, int messageLength)
145 int offset = intLength();
146 return readInt(message, messageLength, &offset);
149 static QString readString(char *message, int messageLength, int *offset)
151 int length = readInt(message, messageLength, offset);
152 if(messageLength - *offset < length) {
153 qDebug() << "Not enough space for string, need " << length << ", got " << (messageLength - *offset);
156 char *start = message + *offset;
158 return QString::fromUtf8(start, length);
161 bool Greeter::connectSync()
163 QDBusConnection busType = QDBusConnection::systemBus();
164 QString ldmBus(qgetenv("LIGHTDM_BUS"));
165 if(ldmBus == QLatin1String("SESSION")) {
166 busType = QDBusConnection::sessionBus();
169 char* fd = getenv("LIGHTDM_TO_SERVER_FD");
171 qDebug() << "No LIGHTDM_TO_SERVER_FD environment variable";
174 d->toServerFd = atoi(fd);
176 qDebug() << "***connecting to server";
178 qDebug() << toServer.open(d->toServerFd, QIODevice::WriteOnly);
180 fd = getenv("LIGHTDM_FROM_SERVER_FD");
182 qDebug() << "No LIGHTDM_FROM_SERVER_FD environment variable";
185 d->fromServerFd = atoi(fd);
187 d->n = new QSocketNotifier(d->fromServerFd, QSocketNotifier::Read);
188 connect(d->n, SIGNAL(activated(int)), this, SLOT(onRead(int)));
190 qDebug() << "Connecting to display manager...";
191 writeHeader(GREETER_MESSAGE_CONNECT, stringLength(VERSION));
192 writeString(VERSION);
196 char *response = readMessage(&responseLength, false);
201 int id = readInt(response, responseLength, &offset);
202 int length = readInt(response, responseLength, &offset);
203 bool connected = false;
204 if (id == SERVER_MESSAGE_CONNECTED)
206 QString version = readString(response, responseLength, &offset);
207 QString hintString = "";
208 while (offset < length)
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);
218 qDebug() << "Connected version=" << version << hintString;
222 qDebug() << "Expected CONNECTED message, got " << id;
228 void Greeter::authenticate(const QString &username)
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);
242 void Greeter::authenticateAsGuest()
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);
255 void Greeter::respond(const QString &response)
257 qDebug() << "Providing response to display manager";
258 writeHeader(GREETER_MESSAGE_CONTINUE_AUTHENTICATION, intLength() + stringLength(response));
259 // FIXME: Could be multiple response required
261 writeString(response);
265 void Greeter::cancelAuthentication()
267 qDebug() << "Cancelling authentication";
268 d->cancellingAuthentication = true;
269 writeHeader(GREETER_MESSAGE_CANCEL_AUTHENTICATION, 0);
273 bool Greeter::inAuthentication() const
275 return d->inAuthentication;
278 bool Greeter::isAuthenticated() const
280 return d->isAuthenticated;
283 QString Greeter::authenticationUser() const
285 return d->authenticationUser;
288 void Greeter::setLanguage (QString language)
290 writeHeader(GREETER_MESSAGE_SET_LANGUAGE, 0);
291 writeString (language);
295 bool Greeter::startSessionSync(const QString &session)
298 qDebug() << "Starting default session";
300 qDebug() << "Starting session " << session;
302 writeHeader(GREETER_MESSAGE_START_SESSION, stringLength(session));
303 writeString(session);
307 char *response = readMessage(&responseLength, false);
312 int id = readInt(response, responseLength, &offset);
313 readInt(response, responseLength, &offset);
315 if (id == SERVER_MESSAGE_SESSION_RESULT)
316 returnCode = readInt(response, responseLength, &offset);
318 qDebug() << "Expected SESSION_RESULT message, got " << id;
321 return returnCode == 0;
324 char *Greeter::readMessage(int *length, bool block)
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);
333 ssize_t nRead = read(d->fromServerFd, d->readBuffer + d->nRead, nToRead - d->nRead);
336 qDebug() << "Error reading from server";
341 qDebug() << "EOF reading from server";
345 qDebug() << "Read " << nRead << " octets from daemon";
347 } while(d->nRead < nToRead && block);
349 /* Stop if haven't got all the data we want */
350 if(d->nRead != nToRead)
353 /* If have header, rerun for content */
354 if(d->nRead == HEADER_SIZE)
356 nToRead = getMessageLength(d->readBuffer, d->nRead);
359 d->readBuffer = (char *)realloc(d->readBuffer, HEADER_SIZE + nToRead);
360 return readMessage(length, block);
364 char *buffer = d->readBuffer;
367 d->readBuffer = (char *)malloc(d->nRead);
373 void Greeter::onRead(int fd)
375 qDebug() << "Reading from server";
378 char *message = readMessage(&messageLength, false);
383 int id = readInt(message, messageLength, &offset);
384 int length = readInt(message, messageLength, &offset);
385 int nMessages, sequenceNumber, returnCode;
386 QString version, username;
389 case SERVER_MESSAGE_PROMPT_AUTHENTICATION:
390 sequenceNumber = readInt(message, messageLength, &offset);
391 username = readString(message, messageLength, &offset);
393 d->authenticationUser = username;
395 if (sequenceNumber == d->authenticateSequenceNumber &&
396 !d->cancellingAuthentication)
398 nMessages = readInt(message, messageLength, &offset);
399 qDebug() << "Prompt user with " << nMessages << " message(s)";
400 for(int i = 0; i < nMessages; i++)
402 int style = readInt(message, messageLength, &offset);
403 QString text = readString(message, messageLength, &offset);
405 // FIXME: Should stop on prompts?
408 case PAM_PROMPT_ECHO_OFF:
409 emit showPrompt(text, PROMPT_TYPE_SECRET);
411 case PAM_PROMPT_ECHO_ON:
412 emit showPrompt(text, PROMPT_TYPE_QUESTION);
415 emit showMessage(text, MESSAGE_TYPE_ERROR);
418 emit showMessage(text, MESSAGE_TYPE_INFO);
424 case SERVER_MESSAGE_END_AUTHENTICATION:
425 sequenceNumber = readInt(message, messageLength, &offset);
426 username = readString(message, messageLength, &offset);
427 returnCode = readInt(message, messageLength, &offset);
429 if (sequenceNumber == d->authenticateSequenceNumber)
431 qDebug() << "Authentication complete with return code " << returnCode;
433 d->cancellingAuthentication = false;
434 d->isAuthenticated = (returnCode == 0);
435 d->authenticationUser = username;
436 d->inAuthentication = false;
437 emit authenticationComplete();
440 qDebug () << "Ignoring end authentication with invalid sequence number " << sequenceNumber;
443 qDebug() << "Unknown message from server: " << id;
448 QString Greeter::getHint(QString name) const
450 return d->hints.value (name);
453 QString Greeter::defaultSessionHint() const
455 return getHint ("default-session");
458 bool Greeter::hideUsersHint() const
460 return d->hints.value ("hide-users", "true") == "true";
463 bool Greeter::hasGuestAccountHint() const
465 return d->hints.value ("has-guest-account", "false") == "true";
468 QString Greeter::selectUserHint() const
470 return getHint ("select-user");
473 bool Greeter::selectGuestHint() const
475 return d->hints.value ("select-guest", "false") == "true";
478 QString Greeter::autologinUserHint() const
480 return getHint ("autologin-user");
483 bool Greeter::autologinGuestHint() const
485 return d->hints.value ("autologin-guest", "false") == "true";
488 int Greeter::autologinTimeoutHint() const
490 return d->hints.value ("autologin-timeout", "0").toInt ();