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,
46 SERVER_MESSAGE_PROMPT_AUTHENTICATION,
47 SERVER_MESSAGE_END_AUTHENTICATION,
48 SERVER_MESSAGE_SESSION_FAILED,
53 using namespace QLightDM;
58 QHash<QString, QString> hints;
65 bool inAuthentication;
67 QString authenticationUser;
68 int authenticateSequenceNumber;
69 bool cancellingAuthentication;
73 Greeter::Greeter(QObject *parent) :
77 d->readBuffer = (char *)malloc (HEADER_SIZE);
79 d->authenticateSequenceNumber = 0;
88 static int intLength()
93 static int stringLength(QString value)
95 QByteArray a = value.toUtf8();
96 return intLength() + a.size();
99 void Greeter::writeInt(int value)
102 buffer[0] = value >> 24;
103 buffer[1] = (value >> 16) & 0xFF;
104 buffer[2] = (value >> 8) & 0xFF;
105 buffer[3] = value & 0xFF;
106 if (write(d->toServerFd, buffer, intLength()) != intLength()) {
107 qDebug() << "Error writing to server";
111 void Greeter::writeString(QString value)
113 QByteArray a = value.toUtf8();
115 if (write(d->toServerFd, a.data(), a.size()) != a.size()) {
116 qDebug() << "Error writing to server";
120 void Greeter::writeHeader(int id, int length)
126 void Greeter::flush()
128 fsync(d->toServerFd);
131 int Greeter::getPacketLength()
133 int offset = intLength();
134 return readInt(&offset);
137 int Greeter::readInt(int *offset)
139 if(d->nRead - *offset < intLength()) {
140 qDebug() << "Not enough space for int, need " << intLength() << ", got " << (d->nRead - *offset);
144 char *buffer = d->readBuffer + *offset;
145 int value = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
146 *offset += intLength();
150 QString Greeter::readString(int *offset)
152 int length = readInt(offset);
153 if(d->nRead - *offset < length) {
154 qDebug() << "Not enough space for string, need " << length << ", got " << (d->nRead - *offset);
157 char *start = d->readBuffer + *offset;
159 return QString::fromUtf8(start, length);
162 void Greeter::connectToServer()
164 QDBusConnection busType = QDBusConnection::systemBus();
165 QString ldmBus(qgetenv("LIGHTDM_BUS"));
166 if(ldmBus == QLatin1String("SESSION")) {
167 busType = QDBusConnection::sessionBus();
170 char* fd = getenv("LIGHTDM_TO_SERVER_FD");
172 qDebug() << "No LIGHTDM_TO_SERVER_FD environment variable";
175 d->toServerFd = atoi(fd);
177 qDebug() << "***connecting to server";
179 qDebug() << toServer.open(d->toServerFd, QIODevice::WriteOnly);
181 fd = getenv("LIGHTDM_FROM_SERVER_FD");
183 qDebug() << "No LIGHTDM_FROM_SERVER_FD environment variable";
186 d->fromServerFd = atoi(fd);
188 d->n = new QSocketNotifier(d->fromServerFd, QSocketNotifier::Read);
189 connect(d->n, SIGNAL(activated(int)), this, SLOT(onRead(int)));
191 qDebug() << "Connecting to display manager...";
192 writeHeader(GREETER_MESSAGE_CONNECT, stringLength(VERSION));
193 writeString(VERSION);
197 void Greeter::authenticate(const QString &username)
199 d->inAuthentication = true;
200 d->isAuthenticated = false;
201 d->cancellingAuthentication = false;
202 d->authenticationUser = username;
203 qDebug() << "Starting authentication for user " << username << "...";
204 writeHeader(GREETER_MESSAGE_AUTHENTICATE, intLength() + stringLength(username));
205 d->authenticateSequenceNumber++;
206 writeInt(d->authenticateSequenceNumber);
207 writeString(username);
211 void Greeter::authenticateAsGuest()
213 d->authenticateSequenceNumber++;
214 d->inAuthentication = true;
215 d->isAuthenticated = false;
216 d->cancellingAuthentication = false;
217 d->authenticationUser = "";
218 qDebug() << "Starting authentication for guest account";
219 writeHeader(GREETER_MESSAGE_AUTHENTICATE_AS_GUEST, intLength());
220 writeInt(d->authenticateSequenceNumber);
224 void Greeter::respond(const QString &response)
226 qDebug() << "Providing response to display manager";
227 writeHeader(GREETER_MESSAGE_CONTINUE_AUTHENTICATION, intLength() + stringLength(response));
228 // FIXME: Could be multiple response required
230 writeString(response);
234 void Greeter::cancelAuthentication()
236 qDebug() << "Cancelling authentication";
237 d->cancellingAuthentication = true;
238 writeHeader(GREETER_MESSAGE_CANCEL_AUTHENTICATION, 0);
242 bool Greeter::inAuthentication() const
244 return d->inAuthentication;
247 bool Greeter::isAuthenticated() const
249 return d->isAuthenticated;
252 QString Greeter::authenticationUser() const
254 return d->authenticationUser;
257 void Greeter::startSession(const QString &session)
259 qDebug() << "Starting session " << session;
260 writeHeader(GREETER_MESSAGE_START_SESSION, stringLength(session));
261 writeString(session);
265 void Greeter::onRead(int fd)
267 //qDebug() << "Reading from server";
269 int nToRead = HEADER_SIZE;
270 if(d->nRead >= HEADER_SIZE)
271 nToRead += getPacketLength();
274 nRead = read(fd, d->readBuffer + d->nRead, nToRead - d->nRead);
277 qDebug() << "Error reading from server";
282 qDebug() << "EOF reading from server";
286 //qDebug() << "Read " << nRead << "octets";
288 if(d->nRead != nToRead)
291 /* If have header, rerun for content */
292 if(d->nRead == HEADER_SIZE)
294 nToRead = getPacketLength();
297 d->readBuffer = (char *)realloc(d->readBuffer, HEADER_SIZE + nToRead);
304 int id = readInt(&offset);
305 int length = readInt(&offset);
306 int nMessages, sequenceNumber, returnCode;
307 QString version, username;
308 QString hintString = "";
311 case SERVER_MESSAGE_CONNECTED:
312 version = readString(&offset);
313 while (offset < length)
315 QString name = readString(&offset);
316 QString value = readString(&offset);
317 hintString.append (" ");
318 hintString.append (name);
319 hintString.append ("=");
320 hintString.append (value);
323 qDebug() << "Connected version=" << version << hintString;
327 case SERVER_MESSAGE_QUIT:
328 qDebug() << "Got quit request from server";
331 case SERVER_MESSAGE_PROMPT_AUTHENTICATION:
332 sequenceNumber = readInt(&offset);
334 if (sequenceNumber == d->authenticateSequenceNumber &&
335 !d->cancellingAuthentication)
337 nMessages = readInt(&offset);
338 qDebug() << "Prompt user with " << nMessages << " message(s)";
339 for(int i = 0; i < nMessages; i++)
341 int msg_style = readInt (&offset);
342 QString msg = readString (&offset);
344 // FIXME: Should stop on prompts?
347 case PAM_PROMPT_ECHO_OFF:
348 emit showPrompt(msg, PROMPT_TYPE_SECRET);
350 case PAM_PROMPT_ECHO_ON:
351 emit showPrompt(msg, PROMPT_TYPE_QUESTION);
354 emit showMessage(msg, MESSAGE_TYPE_ERROR);
357 emit showMessage(msg, MESSAGE_TYPE_INFO);
363 case SERVER_MESSAGE_END_AUTHENTICATION:
364 sequenceNumber = readInt(&offset);
365 returnCode = readInt(&offset);
367 if (sequenceNumber == d->authenticateSequenceNumber)
369 qDebug() << "Authentication complete with return code " << returnCode;
370 d->cancellingAuthentication = false;
371 d->isAuthenticated = (returnCode == 0);
372 if(!d->isAuthenticated) {
373 d->authenticationUser = "";
375 emit authenticationComplete();
376 d->inAuthentication = false;
379 qDebug () << "Ignoring end authentication with invalid sequence number " << sequenceNumber;
381 case SERVER_MESSAGE_SESSION_FAILED:
382 qDebug() << "Session failed to start";
383 emit sessionFailed();
386 qDebug() << "Unknown message from server: " << id;
392 QString Greeter::getHint(QString name) const
394 return d->hints.value (name);
397 QString Greeter::defaultSessionHint() const
399 return getHint ("default-session");
402 bool Greeter::hideUsersHint() const
404 return d->hints.value ("hide-users", "true") == "true";
407 bool Greeter::hasGuestAccountHint() const
409 return d->hints.value ("has-guest-account", "false") == "true";
412 QString Greeter::selectUserHint() const
414 return getHint ("select-user");
417 bool Greeter::selectGuestHint() const
419 return d->hints.value ("select-guest", "false") == "true";
422 QString Greeter::autologinUserHint() const
424 return getHint ("autologin-user");
427 bool Greeter::autologinGuestHint() const
429 return d->hints.value ("autologin-guest", "false") == "true";
432 int Greeter::autologinTimeoutHint() const
434 return d->hints.value ("autologin-timeout", "0").toInt ();