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
41 /* Messages from the server to the greeter */
44 SERVER_MESSAGE_CONNECTED = 0,
45 SERVER_MESSAGE_PROMPT_AUTHENTICATION,
46 SERVER_MESSAGE_END_AUTHENTICATION,
47 SERVER_MESSAGE_SESSION_RESULT
52 using namespace QLightDM;
57 QHash<QString, QString> hints;
64 bool inAuthentication;
66 QString authenticationUser;
67 int authenticateSequenceNumber;
68 bool cancellingAuthentication;
71 Greeter::Greeter(QObject *parent) :
75 d->readBuffer = (char *)malloc(HEADER_SIZE);
77 d->authenticateSequenceNumber = 0;
86 static int intLength()
91 static int stringLength(QString value)
93 QByteArray a = value.toUtf8();
94 return intLength() + a.size();
97 void Greeter::writeInt(int value)
100 buffer[0] = value >> 24;
101 buffer[1] = (value >> 16) & 0xFF;
102 buffer[2] = (value >> 8) & 0xFF;
103 buffer[3] = value & 0xFF;
104 if (write(d->toServerFd, buffer, intLength()) != intLength()) {
105 qDebug() << "Error writing to server";
109 void Greeter::writeString(QString value)
111 QByteArray a = value.toUtf8();
113 if (write(d->toServerFd, a.data(), a.size()) != a.size()) {
114 qDebug() << "Error writing to server";
118 void Greeter::writeHeader(int id, int length)
124 void Greeter::flush()
126 fsync(d->toServerFd);
129 static int readInt(char *message, int messageLength, int *offset)
131 if(messageLength - *offset < intLength()) {
132 qDebug() << "Not enough space for int, need " << intLength() << ", got " << (messageLength - *offset);
136 char *buffer = message + *offset;
137 int value = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
138 *offset += intLength();
142 static int getMessageLength(char *message, int messageLength)
144 int offset = intLength();
145 return readInt(message, messageLength, &offset);
148 static QString readString(char *message, int messageLength, int *offset)
150 int length = readInt(message, messageLength, offset);
151 if(messageLength - *offset < length) {
152 qDebug() << "Not enough space for string, need " << length << ", got " << (messageLength - *offset);
155 char *start = message + *offset;
157 return QString::fromUtf8(start, length);
160 bool Greeter::connectSync()
162 QDBusConnection busType = QDBusConnection::systemBus();
163 QString ldmBus(qgetenv("LIGHTDM_BUS"));
164 if(ldmBus == QLatin1String("SESSION")) {
165 busType = QDBusConnection::sessionBus();
168 char* fd = getenv("LIGHTDM_TO_SERVER_FD");
170 qDebug() << "No LIGHTDM_TO_SERVER_FD environment variable";
173 d->toServerFd = atoi(fd);
175 qDebug() << "***connecting to server";
177 qDebug() << toServer.open(d->toServerFd, QIODevice::WriteOnly);
179 fd = getenv("LIGHTDM_FROM_SERVER_FD");
181 qDebug() << "No LIGHTDM_FROM_SERVER_FD environment variable";
184 d->fromServerFd = atoi(fd);
186 d->n = new QSocketNotifier(d->fromServerFd, QSocketNotifier::Read);
187 connect(d->n, SIGNAL(activated(int)), this, SLOT(onRead(int)));
189 qDebug() << "Connecting to display manager...";
190 writeHeader(GREETER_MESSAGE_CONNECT, stringLength(VERSION));
191 writeString(VERSION);
195 char *response = readMessage(&responseLength, false);
200 int id = readInt(response, responseLength, &offset);
201 int length = readInt(response, responseLength, &offset);
202 bool connected = false;
203 if (id == SERVER_MESSAGE_CONNECTED)
205 QString version = readString(response, responseLength, &offset);
206 QString hintString = "";
207 while (offset < length)
209 QString name = readString(response, responseLength, &offset);
210 QString value = readString(response, responseLength, &offset);
211 hintString.append (" ");
212 hintString.append (name);
213 hintString.append ("=");
214 hintString.append (value);
217 qDebug() << "Connected version=" << version << hintString;
221 qDebug() << "Expected CONNECTED message, got " << id;
227 void Greeter::authenticate(const QString &username)
229 d->inAuthentication = true;
230 d->isAuthenticated = false;
231 d->cancellingAuthentication = false;
232 d->authenticationUser = username;
233 qDebug() << "Starting authentication for user " << username << "...";
234 writeHeader(GREETER_MESSAGE_AUTHENTICATE, intLength() + stringLength(username));
235 d->authenticateSequenceNumber++;
236 writeInt(d->authenticateSequenceNumber);
237 writeString(username);
241 void Greeter::authenticateAsGuest()
243 d->authenticateSequenceNumber++;
244 d->inAuthentication = true;
245 d->isAuthenticated = false;
246 d->cancellingAuthentication = false;
247 d->authenticationUser = "";
248 qDebug() << "Starting authentication for guest account";
249 writeHeader(GREETER_MESSAGE_AUTHENTICATE_AS_GUEST, intLength());
250 writeInt(d->authenticateSequenceNumber);
254 void Greeter::respond(const QString &response)
256 qDebug() << "Providing response to display manager";
257 writeHeader(GREETER_MESSAGE_CONTINUE_AUTHENTICATION, intLength() + stringLength(response));
258 // FIXME: Could be multiple response required
260 writeString(response);
264 void Greeter::cancelAuthentication()
266 qDebug() << "Cancelling authentication";
267 d->cancellingAuthentication = true;
268 writeHeader(GREETER_MESSAGE_CANCEL_AUTHENTICATION, 0);
272 bool Greeter::inAuthentication() const
274 return d->inAuthentication;
277 bool Greeter::isAuthenticated() const
279 return d->isAuthenticated;
282 QString Greeter::authenticationUser() const
284 return d->authenticationUser;
287 bool Greeter::startSessionSync(const QString &session)
290 qDebug() << "Starting default session";
292 qDebug() << "Starting session " << session;
294 writeHeader(GREETER_MESSAGE_START_SESSION, stringLength(session));
295 writeString(session);
299 char *response = readMessage(&responseLength, false);
304 int id = readInt(response, responseLength, &offset);
305 readInt(response, responseLength, &offset);
307 if (id == SERVER_MESSAGE_SESSION_RESULT)
308 returnCode = readInt(response, responseLength, &offset);
310 qDebug() << "Expected SESSION_RESULT message, got " << id;
313 return returnCode == 0;
316 char *Greeter::readMessage(int *length, bool block)
318 /* Read the header, or the whole message if we already have that */
319 int nToRead = HEADER_SIZE;
320 if(d->nRead >= HEADER_SIZE)
321 nToRead += getMessageLength(d->readBuffer, d->nRead);
325 ssize_t nRead = read(d->fromServerFd, d->readBuffer + d->nRead, nToRead - d->nRead);
328 qDebug() << "Error reading from server";
333 qDebug() << "EOF reading from server";
337 qDebug() << "Read " << nRead << " octets from daemon";
339 } while(d->nRead < nToRead && block);
341 /* Stop if haven't got all the data we want */
342 if(d->nRead != nToRead)
345 /* If have header, rerun for content */
346 if(d->nRead == HEADER_SIZE)
348 nToRead = getMessageLength(d->readBuffer, d->nRead);
351 d->readBuffer = (char *)realloc(d->readBuffer, HEADER_SIZE + nToRead);
352 return readMessage(length, block);
356 char *buffer = d->readBuffer;
359 d->readBuffer = (char *)malloc(d->nRead);
365 void Greeter::onRead(int fd)
367 qDebug() << "Reading from server";
370 char *message = readMessage(&messageLength, false);
375 int id = readInt(message, messageLength, &offset);
376 int length = readInt(message, messageLength, &offset);
377 int nMessages, sequenceNumber, returnCode;
378 QString version, username;
381 case SERVER_MESSAGE_PROMPT_AUTHENTICATION:
382 sequenceNumber = readInt(message, messageLength, &offset);
384 if (sequenceNumber == d->authenticateSequenceNumber &&
385 !d->cancellingAuthentication)
387 nMessages = readInt(message, messageLength, &offset);
388 qDebug() << "Prompt user with " << nMessages << " message(s)";
389 for(int i = 0; i < nMessages; i++)
391 int style = readInt(message, messageLength, &offset);
392 QString text = readString(message, messageLength, &offset);
394 // FIXME: Should stop on prompts?
397 case PAM_PROMPT_ECHO_OFF:
398 emit showPrompt(text, PROMPT_TYPE_SECRET);
400 case PAM_PROMPT_ECHO_ON:
401 emit showPrompt(text, PROMPT_TYPE_QUESTION);
404 emit showMessage(text, MESSAGE_TYPE_ERROR);
407 emit showMessage(text, MESSAGE_TYPE_INFO);
413 case SERVER_MESSAGE_END_AUTHENTICATION:
414 sequenceNumber = readInt(message, messageLength, &offset);
415 returnCode = readInt(message, messageLength, &offset);
417 if (sequenceNumber == d->authenticateSequenceNumber)
419 qDebug() << "Authentication complete with return code " << returnCode;
420 d->cancellingAuthentication = false;
421 d->isAuthenticated = (returnCode == 0);
422 if(!d->isAuthenticated) {
423 d->authenticationUser = "";
425 emit authenticationComplete();
426 d->inAuthentication = false;
429 qDebug () << "Ignoring end authentication with invalid sequence number " << sequenceNumber;
432 qDebug() << "Unknown message from server: " << id;
437 QString Greeter::getHint(QString name) const
439 return d->hints.value (name);
442 QString Greeter::defaultSessionHint() const
444 return getHint ("default-session");
447 bool Greeter::hideUsersHint() const
449 return d->hints.value ("hide-users", "true") == "true";
452 bool Greeter::hasGuestAccountHint() const
454 return d->hints.value ("has-guest-account", "false") == "true";
457 QString Greeter::selectUserHint() const
459 return getHint ("select-user");
462 bool Greeter::selectGuestHint() const
464 return d->hints.value ("select-guest", "false") == "true";
467 QString Greeter::autologinUserHint() const
469 return getHint ("autologin-user");
472 bool Greeter::autologinGuestHint() const
474 return d->hints.value ("autologin-guest", "false") == "true";
477 int Greeter::autologinTimeoutHint() const
479 return d->hints.value ("autologin-timeout", "0").toInt ();