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;
68 bool inAuthentication;
70 QString authenticationUser;
71 int authenticateSequenceNumber;
72 bool cancellingAuthentication;
75 Greeter::Greeter(QObject *parent) :
79 d->readBuffer = (char *)malloc(HEADER_SIZE);
81 d->authenticateSequenceNumber = 0;
90 static int intLength()
95 static int stringLength(QString value)
97 QByteArray a = value.toUtf8();
98 return intLength() + a.size();
101 void Greeter::writeInt(int value)
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";
113 void Greeter::writeString(QString value)
115 QByteArray a = value.toUtf8();
117 if (write(d->toServerFd, a.data(), a.size()) != a.size()) {
118 qDebug() << "Error writing to server";
122 void Greeter::writeHeader(int id, int length)
128 void Greeter::flush()
130 fsync(d->toServerFd);
133 static int readInt(char *message, int messageLength, int *offset)
135 if(messageLength - *offset < intLength()) {
136 qDebug() << "Not enough space for int, need " << intLength() << ", got " << (messageLength - *offset);
140 char *buffer = message + *offset;
141 int value = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
142 *offset += intLength();
146 static int getMessageLength(char *message, int messageLength)
148 int offset = intLength();
149 return readInt(message, messageLength, &offset);
152 static QString readString(char *message, int messageLength, int *offset)
154 int length = readInt(message, messageLength, offset);
155 if(messageLength - *offset < length) {
156 qDebug() << "Not enough space for string, need " << length << ", got " << (messageLength - *offset);
159 char *start = message + *offset;
161 return QString::fromUtf8(start, length);
164 bool Greeter::connectSync()
166 QDBusConnection busType = QDBusConnection::systemBus();
167 QString ldmBus(qgetenv("LIGHTDM_BUS"));
168 if(ldmBus == QLatin1String("SESSION")) {
169 busType = QDBusConnection::sessionBus();
172 char* fd = getenv("LIGHTDM_TO_SERVER_FD");
174 qDebug() << "No LIGHTDM_TO_SERVER_FD environment variable";
177 d->toServerFd = atoi(fd);
179 qDebug() << "***connecting to server";
181 qDebug() << toServer.open(d->toServerFd, QIODevice::WriteOnly);
183 fd = getenv("LIGHTDM_FROM_SERVER_FD");
185 qDebug() << "No LIGHTDM_FROM_SERVER_FD environment variable";
188 d->fromServerFd = atoi(fd);
190 d->n = new QSocketNotifier(d->fromServerFd, QSocketNotifier::Read);
191 connect(d->n, SIGNAL(activated(int)), this, SLOT(onRead(int)));
193 qDebug() << "Connecting to display manager...";
194 writeHeader(GREETER_MESSAGE_CONNECT, stringLength(VERSION));
195 writeString(VERSION);
199 char *response = readMessage(&responseLength, false);
204 int id = readInt(response, responseLength, &offset);
205 int length = readInt(response, responseLength, &offset);
206 bool connected = false;
207 if (id == SERVER_MESSAGE_CONNECTED)
209 QString version = readString(response, responseLength, &offset);
210 QString hintString = "";
211 while (offset < length)
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);
221 qDebug() << "Connected version=" << version << hintString;
225 qDebug() << "Expected CONNECTED message, got " << id;
231 void Greeter::authenticate(const QString &username)
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);
245 void Greeter::authenticateAsGuest()
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);
258 void Greeter::respond(const QString &response)
260 qDebug() << "Providing response to display manager";
261 writeHeader(GREETER_MESSAGE_CONTINUE_AUTHENTICATION, intLength() + stringLength(response));
262 // FIXME: Could be multiple response required
264 writeString(response);
268 void Greeter::cancelAuthentication()
270 qDebug() << "Cancelling authentication";
271 d->cancellingAuthentication = true;
272 writeHeader(GREETER_MESSAGE_CANCEL_AUTHENTICATION, 0);
276 bool Greeter::inAuthentication() const
278 return d->inAuthentication;
281 bool Greeter::isAuthenticated() const
283 return d->isAuthenticated;
286 QString Greeter::authenticationUser() const
288 return d->authenticationUser;
291 void Greeter::setLanguage (QString language)
293 writeHeader(GREETER_MESSAGE_SET_LANGUAGE, stringLength(language));
294 writeString (language);
298 bool Greeter::startSessionSync(const QString &session)
301 qDebug() << "Starting default session";
303 qDebug() << "Starting session " << session;
305 writeHeader(GREETER_MESSAGE_START_SESSION, stringLength(session));
306 writeString(session);
310 char *response = readMessage(&responseLength, false);
315 int id = readInt(response, responseLength, &offset);
316 readInt(response, responseLength, &offset);
318 if (id == SERVER_MESSAGE_SESSION_RESULT)
319 returnCode = readInt(response, responseLength, &offset);
321 qDebug() << "Expected SESSION_RESULT message, got " << id;
324 return returnCode == 0;
327 char *Greeter::readMessage(int *length, bool block)
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);
336 ssize_t nRead = read(d->fromServerFd, d->readBuffer + d->nRead, nToRead - d->nRead);
339 qDebug() << "Error reading from server";
344 qDebug() << "EOF reading from server";
348 qDebug() << "Read " << nRead << " octets from daemon";
350 } while(d->nRead < nToRead && block);
352 /* Stop if haven't got all the data we want */
353 if(d->nRead != nToRead)
356 /* If have header, rerun for content */
357 if(d->nRead == HEADER_SIZE)
359 nToRead = getMessageLength(d->readBuffer, d->nRead);
362 d->readBuffer = (char *)realloc(d->readBuffer, HEADER_SIZE + nToRead);
363 return readMessage(length, block);
367 char *buffer = d->readBuffer;
370 d->readBuffer = (char *)malloc(d->nRead);
376 void Greeter::onRead(int fd)
378 qDebug() << "Reading from server";
381 char *message = readMessage(&messageLength, false);
386 int id = readInt(message, messageLength, &offset);
387 int length = readInt(message, messageLength, &offset);
388 int nMessages, sequenceNumber, returnCode;
389 QString version, username;
392 case SERVER_MESSAGE_PROMPT_AUTHENTICATION:
393 sequenceNumber = readInt(message, messageLength, &offset);
394 username = readString(message, messageLength, &offset);
396 d->authenticationUser = username;
398 if (sequenceNumber == d->authenticateSequenceNumber &&
399 !d->cancellingAuthentication)
401 nMessages = readInt(message, messageLength, &offset);
402 qDebug() << "Prompt user with " << nMessages << " message(s)";
403 for(int i = 0; i < nMessages; i++)
405 int style = readInt(message, messageLength, &offset);
406 QString text = readString(message, messageLength, &offset);
408 // FIXME: Should stop on prompts?
411 case PAM_PROMPT_ECHO_OFF:
412 emit showPrompt(text, PROMPT_TYPE_SECRET);
414 case PAM_PROMPT_ECHO_ON:
415 emit showPrompt(text, PROMPT_TYPE_QUESTION);
418 emit showMessage(text, MESSAGE_TYPE_ERROR);
421 emit showMessage(text, MESSAGE_TYPE_INFO);
427 case SERVER_MESSAGE_END_AUTHENTICATION:
428 sequenceNumber = readInt(message, messageLength, &offset);
429 username = readString(message, messageLength, &offset);
430 returnCode = readInt(message, messageLength, &offset);
432 if (sequenceNumber == d->authenticateSequenceNumber)
434 qDebug() << "Authentication complete with return code " << returnCode;
436 d->cancellingAuthentication = false;
437 d->isAuthenticated = (returnCode == 0);
438 d->authenticationUser = username;
439 d->inAuthentication = false;
440 emit authenticationComplete();
443 qDebug () << "Ignoring end authentication with invalid sequence number " << sequenceNumber;
446 qDebug() << "Unknown message from server: " << id;
451 QString Greeter::getHint(QString name) const
453 return d->hints.value (name);
456 QString Greeter::defaultSessionHint() const
458 return getHint ("default-session");
461 bool Greeter::hideUsersHint() const
463 return d->hints.value ("hide-users", "true") == "true";
466 bool Greeter::hasGuestAccountHint() const
468 return d->hints.value ("has-guest-account", "false") == "true";
471 QString Greeter::selectUserHint() const
473 return getHint ("select-user");
476 bool Greeter::selectGuestHint() const
478 return d->hints.value ("select-guest", "false") == "true";
481 QString Greeter::autologinUserHint() const
483 return getHint ("autologin-user");
486 bool Greeter::autologinGuestHint() const
488 return d->hints.value ("autologin-guest", "false") == "true";
491 int Greeter::autologinTimeoutHint() const
493 return d->hints.value ("autologin-timeout", "0").toInt ();
496 #include "greeter_moc.cpp"