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
14 #include "QLightDM/greeter.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 #include <security/pam_appl.h>
33 /* Messages from the greeter to the server */
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
45 /* Messages from the server to the greeter */
48 SERVER_MESSAGE_CONNECTED = 0,
49 SERVER_MESSAGE_PROMPT_AUTHENTICATION,
50 SERVER_MESSAGE_END_AUTHENTICATION,
51 SERVER_MESSAGE_SESSION_RESULT
56 using namespace QLightDM;
61 QHash<QString, QString> hints;
67 bool inAuthentication;
69 QString authenticationUser;
70 int authenticateSequenceNumber;
71 bool cancellingAuthentication;
73 void writeInt(int value);
74 void writeString(QString value);
75 void writeHeader(int id, int length);
77 char *readMessage(int *length, bool block);
80 Greeter::Greeter(QObject *parent) :
84 d->readBuffer = (char *)malloc(HEADER_SIZE);
86 d->authenticateSequenceNumber = 0;
95 static int intLength()
100 static int stringLength(QString value)
102 QByteArray a = value.toUtf8();
103 return intLength() + a.size();
106 void GreeterPrivate::writeInt(int value)
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";
118 void GreeterPrivate::writeString(QString value)
120 QByteArray a = value.toUtf8();
122 if (write(toServerFd, a.data(), a.size()) != a.size()) {
123 qDebug() << "Error writing to server";
127 void GreeterPrivate::writeHeader(int id, int length)
133 void GreeterPrivate::flush()
138 static int readInt(char *message, int messageLength, int *offset)
140 if(messageLength - *offset < intLength()) {
141 qDebug() << "Not enough space for int, need " << intLength() << ", got " << (messageLength - *offset);
145 char *buffer = message + *offset;
146 int value = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
147 *offset += intLength();
151 static int getMessageLength(char *message, int messageLength)
153 int offset = intLength();
154 return readInt(message, messageLength, &offset);
157 static QString readString(char *message, int messageLength, int *offset)
159 int length = readInt(message, messageLength, offset);
160 if(messageLength - *offset < length) {
161 qDebug() << "Not enough space for string, need " << length << ", got " << (messageLength - *offset);
164 char *start = message + *offset;
166 return QString::fromUtf8(start, length);
169 bool Greeter::connectSync()
171 QDBusConnection busType = QDBusConnection::systemBus();
172 QString ldmBus(qgetenv("LIGHTDM_BUS"));
173 if(ldmBus == QLatin1String("SESSION")) {
174 busType = QDBusConnection::sessionBus();
177 char* fd = getenv("LIGHTDM_TO_SERVER_FD");
179 qDebug() << "No LIGHTDM_TO_SERVER_FD environment variable";
182 d->toServerFd = atoi(fd);
184 qDebug() << "***connecting to server";
186 qDebug() << toServer.open(d->toServerFd, QIODevice::WriteOnly);
188 fd = getenv("LIGHTDM_FROM_SERVER_FD");
190 qDebug() << "No LIGHTDM_FROM_SERVER_FD environment variable";
193 d->fromServerFd = atoi(fd);
195 d->n = new QSocketNotifier(d->fromServerFd, QSocketNotifier::Read);
196 connect(d->n, SIGNAL(activated(int)), this, SLOT(onRead(int)));
198 qDebug() << "Connecting to display manager...";
199 d->writeHeader(GREETER_MESSAGE_CONNECT, stringLength(VERSION));
200 d->writeString(VERSION);
204 char *response = d->readMessage(&responseLength, false);
209 int id = readInt(response, responseLength, &offset);
210 int length = readInt(response, responseLength, &offset);
211 bool connected = false;
212 if (id == SERVER_MESSAGE_CONNECTED)
214 QString version = readString(response, responseLength, &offset);
215 QString hintString = "";
216 while (offset < length)
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);
226 qDebug() << "Connected version=" << version << hintString;
230 qDebug() << "Expected CONNECTED message, got " << id;
236 void Greeter::authenticate(const QString &username)
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);
250 void Greeter::authenticateAsGuest()
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);
263 void Greeter::respond(const QString &response)
265 qDebug() << "Providing response to display manager";
266 d->writeHeader(GREETER_MESSAGE_CONTINUE_AUTHENTICATION, intLength() + stringLength(response));
267 // FIXME: Could be multiple response required
269 d->writeString(response);
273 void Greeter::cancelAuthentication()
275 qDebug() << "Cancelling authentication";
276 d->cancellingAuthentication = true;
277 d->writeHeader(GREETER_MESSAGE_CANCEL_AUTHENTICATION, 0);
281 bool Greeter::inAuthentication() const
283 return d->inAuthentication;
286 bool Greeter::isAuthenticated() const
288 return d->isAuthenticated;
291 QString Greeter::authenticationUser() const
293 return d->authenticationUser;
296 void Greeter::setLanguage (QString language)
298 d->writeHeader(GREETER_MESSAGE_SET_LANGUAGE, stringLength(language));
299 d->writeString (language);
303 bool Greeter::startSessionSync(const QString &session)
305 if (session.isEmpty()) {
306 qDebug() << "Starting default session";
309 qDebug() << "Starting session " << session;
312 d->writeHeader(GREETER_MESSAGE_START_SESSION, stringLength(session));
313 d->writeString(session);
317 char *response = d->readMessage(&responseLength, false);
323 int id = readInt(response, responseLength, &offset);
324 readInt(response, responseLength, &offset);
326 if (id == SERVER_MESSAGE_SESSION_RESULT) {
327 returnCode = readInt(response, responseLength, &offset);
330 qDebug() << "Expected SESSION_RESULT message, got " << id;
334 return returnCode == 0;
337 char* GreeterPrivate::readMessage(int *length, bool block)
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);
346 ssize_t nRead = read(fromServerFd, readBuffer + nRead, nToRead - nRead);
348 qDebug() << "Error reading from server";
353 qDebug() << "EOF reading from server";
357 qDebug() << "Read " << nRead << " octets from daemon";
359 } while(nRead < nToRead && block);
361 /* Stop if haven't got all the data we want */
362 if(nRead != nToRead) {
366 /* If have header, rerun for content */
367 if(nRead == HEADER_SIZE) {
368 nToRead = getMessageLength(readBuffer, nRead);
370 readBuffer = (char *)realloc(readBuffer, HEADER_SIZE + nToRead);
371 return readMessage(length, block);
375 char *buffer = readBuffer;
378 readBuffer = (char *)malloc(nRead);
384 void Greeter::onRead(int fd)
386 qDebug() << "Reading from server";
389 char *message = d->readMessage(&messageLength, false);
395 int id = readInt(message, messageLength, &offset);
396 int length = readInt(message, messageLength, &offset);
397 int nMessages, sequenceNumber, returnCode;
398 QString version, username;
400 case SERVER_MESSAGE_PROMPT_AUTHENTICATION:
401 sequenceNumber = readInt(message, messageLength, &offset);
402 username = readString(message, messageLength, &offset);
404 d->authenticationUser = username;
406 if (sequenceNumber == d->authenticateSequenceNumber &&
407 !d->cancellingAuthentication)
409 nMessages = readInt(message, messageLength, &offset);
410 qDebug() << "Prompt user with " << nMessages << " message(s)";
411 for(int i = 0; i < nMessages; i++)
413 int style = readInt(message, messageLength, &offset);
414 QString text = readString(message, messageLength, &offset);
416 // FIXME: Should stop on prompts?
419 case PAM_PROMPT_ECHO_OFF:
420 emit showPrompt(text, Greeter::PromptTypeSecret);
422 case PAM_PROMPT_ECHO_ON:
423 emit showPrompt(text, Greeter::PromptTypeQuestion);
426 emit showMessage(text, Greeter::MessageTypeError);
429 emit showMessage(text, Greeter::MessageTypeInfo);
435 case SERVER_MESSAGE_END_AUTHENTICATION:
436 sequenceNumber = readInt(message, messageLength, &offset);
437 username = readString(message, messageLength, &offset);
438 returnCode = readInt(message, messageLength, &offset);
440 if (sequenceNumber == d->authenticateSequenceNumber)
442 qDebug() << "Authentication complete with return code " << returnCode;
444 d->cancellingAuthentication = false;
445 d->isAuthenticated = (returnCode == 0);
446 d->authenticationUser = username;
447 d->inAuthentication = false;
448 emit authenticationComplete();
451 qDebug () << "Ignoring end authentication with invalid sequence number " << sequenceNumber;
454 qDebug() << "Unknown message from server: " << id;
459 QString Greeter::getHint(QString name) const
461 return d->hints.value (name);
464 QString Greeter::defaultSessionHint() const
466 return getHint ("default-session");
469 bool Greeter::hideUsersHint() const
471 return d->hints.value ("hide-users", "true") == "true";
474 bool Greeter::hasGuestAccountHint() const
476 return d->hints.value ("has-guest-account", "false") == "true";
479 QString Greeter::selectUserHint() const
481 return getHint ("select-user");
484 bool Greeter::selectGuestHint() const
486 return d->hints.value ("select-guest", "false") == "true";
489 QString Greeter::autologinUserHint() const
491 return getHint ("autologin-user");
494 bool Greeter::autologinGuestHint() const
496 return d->hints.value ("autologin-guest", "false") == "true";
499 int Greeter::autologinTimeoutHint() const
501 return d->hints.value ("autologin-timeout", "0").toInt ();
504 #include "greeter_moc.cpp"