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"
16 #include "QLightDM/User"
17 #include "QLightDM/SessionsModel"
19 #include <security/pam_appl.h>
21 #include <QtNetwork/QHostInfo> //needed for localHostName
22 #include <QtCore/QDebug>
23 #include <QtCore/QDir>
24 #include <QtCore/QVariant>
25 #include <QtCore/QSettings>
26 #include <QtCore/QUrl>
27 #include <QtCore/QFile>
28 #include <QtCore/QHash>
29 #include <QtCore/QSocketNotifier>
30 #include <QtDBus/QDBusPendingReply>
31 #include <QtDBus/QDBusInterface>
32 #include <QtDBus/QDBusReply>
34 /* Messages from the greeter to the server */
37 GREETER_MESSAGE_CONNECT = 0,
38 GREETER_MESSAGE_AUTHENTICATE,
39 GREETER_MESSAGE_AUTHENTICATE_AS_GUEST,
40 GREETER_MESSAGE_CONTINUE_AUTHENTICATION,
41 GREETER_MESSAGE_START_SESSION,
42 GREETER_MESSAGE_CANCEL_AUTHENTICATION
45 /* Messages from the server to the greeter */
48 SERVER_MESSAGE_CONNECTED = 0,
50 SERVER_MESSAGE_PROMPT_AUTHENTICATION,
51 SERVER_MESSAGE_END_AUTHENTICATION,
52 SERVER_MESSAGE_SESSION_FAILED,
57 using namespace QLightDM;
62 SessionsModel *sessionsModel;
64 QHash<QString, QString> hints;
71 bool inAuthentication;
73 QString authenticationUser;
74 int authenticateSequenceNumber;
75 bool cancellingAuthentication;
79 Greeter::Greeter(QObject *parent) :
83 d->readBuffer = (char *)malloc (HEADER_SIZE);
85 d->sessionsModel = new SessionsModel(this);
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 Greeter::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(d->toServerFd, buffer, intLength()) != intLength()) {
114 qDebug() << "Error writing to server";
118 void Greeter::writeString(QString value)
120 QByteArray a = value.toUtf8();
122 if (write(d->toServerFd, a.data(), a.size()) != a.size()) {
123 qDebug() << "Error writing to server";
127 void Greeter::writeHeader(int id, int length)
133 void Greeter::flush()
135 fsync(d->toServerFd);
138 int Greeter::getPacketLength()
140 int offset = intLength();
141 return readInt(&offset);
144 int Greeter::readInt(int *offset)
146 if(d->nRead - *offset < intLength()) {
147 qDebug() << "Not enough space for int, need " << intLength() << ", got " << (d->nRead - *offset);
151 char *buffer = d->readBuffer + *offset;
152 int value = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
153 *offset += intLength();
157 QString Greeter::readString(int *offset)
159 int length = readInt(offset);
160 if(d->nRead - *offset < length) {
161 qDebug() << "Not enough space for string, need " << length << ", got " << (d->nRead - *offset);
164 char *start = d->readBuffer + *offset;
166 return QString::fromUtf8(start, length);
169 void Greeter::connectToServer()
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 writeHeader(GREETER_MESSAGE_CONNECT, stringLength(VERSION));
200 writeString(VERSION);
204 void Greeter::authenticate(const QString &username)
206 d->inAuthentication = true;
207 d->isAuthenticated = false;
208 d->cancellingAuthentication = false;
209 d->authenticationUser = username;
210 qDebug() << "Starting authentication for user " << username << "...";
211 writeHeader(GREETER_MESSAGE_AUTHENTICATE, intLength() + stringLength(username));
212 d->authenticateSequenceNumber++;
213 writeInt(d->authenticateSequenceNumber);
214 writeString(username);
218 void Greeter::authenticateAsGuest()
220 d->authenticateSequenceNumber++;
221 d->inAuthentication = true;
222 d->isAuthenticated = false;
223 d->cancellingAuthentication = false;
224 d->authenticationUser = "";
225 qDebug() << "Starting authentication for guest account";
226 writeHeader(GREETER_MESSAGE_AUTHENTICATE_AS_GUEST, intLength());
227 writeInt(d->authenticateSequenceNumber);
231 void Greeter::respond(const QString &response)
233 qDebug() << "Providing response to display manager";
234 writeHeader(GREETER_MESSAGE_CONTINUE_AUTHENTICATION, intLength() + stringLength(response));
235 // FIXME: Could be multiple response required
237 writeString(response);
241 void Greeter::cancelAuthentication()
243 qDebug() << "Cancelling authentication";
244 d->cancellingAuthentication = true;
245 writeHeader(GREETER_MESSAGE_CANCEL_AUTHENTICATION, 0);
249 bool Greeter::inAuthentication() const
251 return d->inAuthentication;
254 bool Greeter::isAuthenticated() const
256 return d->isAuthenticated;
259 QString Greeter::authenticationUser() const
261 return d->authenticationUser;
264 void Greeter::startSession(const QString &session)
266 qDebug() << "Starting session " << session;
267 writeHeader(GREETER_MESSAGE_START_SESSION, stringLength(session));
268 writeString(session);
272 void Greeter::onRead(int fd)
274 //qDebug() << "Reading from server";
276 int nToRead = HEADER_SIZE;
277 if(d->nRead >= HEADER_SIZE)
278 nToRead += getPacketLength();
281 nRead = read(fd, d->readBuffer + d->nRead, nToRead - d->nRead);
284 qDebug() << "Error reading from server";
289 qDebug() << "EOF reading from server";
293 //qDebug() << "Read " << nRead << "octets";
295 if(d->nRead != nToRead)
298 /* If have header, rerun for content */
299 if(d->nRead == HEADER_SIZE)
301 nToRead = getPacketLength();
304 d->readBuffer = (char *)realloc(d->readBuffer, HEADER_SIZE + nToRead);
311 int id = readInt(&offset);
312 int length = readInt(&offset);
313 int nMessages, sequenceNumber, returnCode;
314 QString version, username;
315 QString hintString = "";
318 case SERVER_MESSAGE_CONNECTED:
319 version = readString(&offset);
320 while (offset < length)
322 QString name = readString(&offset);
323 QString value = readString(&offset);
324 hintString.append (" ");
325 hintString.append (name);
326 hintString.append ("=");
327 hintString.append (value);
330 qDebug() << "Connected version=" << version << hintString;
334 case SERVER_MESSAGE_QUIT:
335 qDebug() << "Got quit request from server";
338 case SERVER_MESSAGE_PROMPT_AUTHENTICATION:
339 sequenceNumber = readInt(&offset);
341 if (sequenceNumber == d->authenticateSequenceNumber &&
342 !d->cancellingAuthentication)
344 nMessages = readInt(&offset);
345 qDebug() << "Prompt user with " << nMessages << " message(s)";
346 for(int i = 0; i < nMessages; i++)
348 int msg_style = readInt (&offset);
349 QString msg = readString (&offset);
351 // FIXME: Should stop on prompts?
354 case PAM_PROMPT_ECHO_OFF:
355 emit showPrompt(msg, PROMPT_TYPE_SECRET);
357 case PAM_PROMPT_ECHO_ON:
358 emit showPrompt(msg, PROMPT_TYPE_QUESTION);
361 emit showMessage(msg, MESSAGE_TYPE_ERROR);
364 emit showMessage(msg, MESSAGE_TYPE_INFO);
370 case SERVER_MESSAGE_END_AUTHENTICATION:
371 sequenceNumber = readInt(&offset);
372 returnCode = readInt(&offset);
374 if (sequenceNumber == d->authenticateSequenceNumber)
376 qDebug() << "Authentication complete with return code " << returnCode;
377 d->cancellingAuthentication = false;
378 d->isAuthenticated = (returnCode == 0);
379 if(!d->isAuthenticated) {
380 d->authenticationUser = "";
382 emit authenticationComplete();
383 d->inAuthentication = false;
386 qDebug () << "Ignoring end authentication with invalid sequence number " << sequenceNumber;
388 case SERVER_MESSAGE_SESSION_FAILED:
389 qDebug() << "Session failed to start";
390 emit sessionFailed();
393 qDebug() << "Unknown message from server: " << id;
399 QString Greeter::hostname() const
401 return QHostInfo::localHostName();
404 QString Greeter::defaultLanguage() const
406 return getenv("LANG");
409 QString Greeter::getHint(QString name) const
411 return d->hints.value (name);
414 QString Greeter::defaultSessionHint() const
416 return getHint ("default-session");
419 bool Greeter::hideUsersHint() const
421 return d->hints.value ("hide-users", "true") == "true";
424 bool Greeter::hasGuestAccountHint() const
426 return d->hints.value ("has-guest-account", "false") == "true";
429 QString Greeter::selectUserHint() const
431 return getHint ("select-user");
434 bool Greeter::selectGuestHint() const
436 return d->hints.value ("select-guest", "false") == "true";
439 QString Greeter::autologinUserHint() const
441 return getHint ("autologin-user");
444 bool Greeter::autologinGuestHint() const
446 return d->hints.value ("autologin-guest", "false") == "true";
449 int Greeter::autologinTimeoutHint() const
451 return d->hints.value ("autologin-timeout", "0").toInt ();