4 #include "sessionsmodel.h"
6 #include <security/pam_appl.h>
10 #include <QtNetwork/QHostInfo> //needed for localHostName
11 #include <QtCore/QDebug>
12 #include <QtCore/QDir>
13 #include <QtCore/QVariant>
14 #include <QtCore/QFile>
15 #include <QtDBus/QDBusPendingReply>
20 /* Messages from the greeter to the server */
21 GREETER_MESSAGE_CONNECT = 1,
22 GREETER_MESSAGE_START_AUTHENTICATION = 2,
23 GREETER_MESSAGE_CONTINUE_AUTHENTICATION = 3,
24 GREETER_MESSAGE_LOGIN = 4,
25 GREETER_MESSAGE_CANCEL_AUTHENTICATION = 5,
27 /* Messages from the server to the greeter */
28 GREETER_MESSAGE_CONNECTED = 101,
29 GREETER_MESSAGE_QUIT = 102,
30 GREETER_MESSAGE_PROMPT_AUTHENTICATION = 103,
31 GREETER_MESSAGE_END_AUTHENTICATION = 104
36 using namespace QLightDM;
42 QString defaultLayout;
43 QString defaultSession;
47 SessionsModel *sessionsModel;
55 QDBusInterface* lightdmInterface;
56 QDBusInterface* powerManagementInterface;
57 QDBusInterface* consoleKitInterface;
64 bool inAuthentication;
66 QString authenticationUser;
70 Greeter::Greeter(QObject *parent) :
74 d->readBuffer = (char *)malloc (HEADER_SIZE);
76 d->haveConfig = false;
79 d->sessionsModel = new SessionsModel(this);
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("LDM_BUS"));
166 if(ldmBus == QLatin1String("SESSION")) {
167 busType = QDBusConnection::sessionBus();
171 d->lightdmInterface = new QDBusInterface("org.lightdm.LightDisplayManager", "/org/lightdm/LightDisplayManager", "org.lightdm.LightDisplayManager", busType);
172 d->powerManagementInterface = new QDBusInterface("org.freedesktop.PowerManagement","/org/freedesktop/PowerManagement", "org.freedesktop.PowerManagement");
173 d->consoleKitInterface = new QDBusInterface("org.freedesktop.ConsoleKit", "/org/freedesktop/ConsoleKit/Manager", "org.freedesktop.ConsoleKit");
175 char* fd = getenv("LDM_TO_SERVER_FD");
177 qDebug() << "No LDM_TO_SERVER_FD environment variable";
180 d->toServerFd = atoi(fd);
183 qDebug() << "***connecting to server";
185 qDebug() << toServer.open(d->toServerFd, QIODevice::WriteOnly);
187 fd = getenv("LDM_FROM_SERVER_FD");
189 qDebug() << "No LDM_FROM_SERVER_FD environment variable";
192 d->fromServerFd = atoi(fd);
194 d->n = new QSocketNotifier(d->fromServerFd, QSocketNotifier::Read);
195 connect(d->n, SIGNAL(activated(int)), this, SLOT(onRead(int)));
197 qDebug() << "Connecting to display manager...";
198 writeHeader(GREETER_MESSAGE_CONNECT, 0);
202 void Greeter::startAuthentication(const QString &username)
204 d->inAuthentication = true;
205 d->isAuthenticated = false;
206 d->authenticationUser = username;
207 qDebug() << "Starting authentication for user " << username << "...";
208 writeHeader(GREETER_MESSAGE_START_AUTHENTICATION, stringLength(username));
209 writeString(username);
213 void Greeter::provideSecret(const QString &secret)
215 qDebug() << "Providing secret to display manager";
216 writeHeader(GREETER_MESSAGE_CONTINUE_AUTHENTICATION, intLength() + stringLength(secret));
217 // FIXME: Could be multiple secrets required
223 void Greeter::cancelAuthentication()
225 qDebug() << "Cancelling authentication";
226 writeHeader(GREETER_MESSAGE_CANCEL_AUTHENTICATION, 0);
230 bool Greeter::inAuthentication() const
232 return d->inAuthentication;
235 bool Greeter::isAuthenticated() const
237 return d->isAuthenticated;
240 QString Greeter::authenticationUser() const
242 return d->authenticationUser;
245 void Greeter::login(const QString &username, const QString &session, const QString &language)
247 qDebug() << "Logging in as " << username << " for session " << session << " with language " << language;
248 writeHeader(GREETER_MESSAGE_LOGIN, stringLength(username) + stringLength(session) + stringLength(language));
249 writeString(username);
250 writeString(session);
251 writeString(language);
255 void Greeter::loginWithDefaults(const QString &username)
257 login(username, NULL, NULL);
260 void Greeter::onRead(int fd)
262 //qDebug() << "Reading from server";
264 int nToRead = HEADER_SIZE;
265 if(d->nRead >= HEADER_SIZE)
266 nToRead += getPacketLength();
269 nRead = read(fd, d->readBuffer + d->nRead, nToRead - d->nRead);
272 qDebug() << "Error reading from server";
277 qDebug() << "EOF reading from server";
281 //qDebug() << "Read " << nRead << "octets";
283 if(d->nRead != nToRead)
286 /* If have header, rerun for content */
287 if(d->nRead == HEADER_SIZE)
289 nToRead = getPacketLength();
292 d->readBuffer = (char *)realloc(d->readBuffer, HEADER_SIZE + nToRead);
299 int id = readInt(&offset);
301 int nMessages, returnCode;
304 case GREETER_MESSAGE_CONNECTED:
305 d->theme = readString(&offset);
306 d->defaultLayout = readString(&offset);
307 d->defaultSession = readString(&offset);
308 d->timedUser = readString(&offset);
309 d->loginDelay = readInt(&offset);
310 qDebug() << "Connected theme=" << d->theme << " default-layout=" << d->defaultLayout << " default-session=" << d->defaultSession << " timed-user=" << d->timedUser << " login-delay" << d->loginDelay;
312 /* Set timeout for default login */
313 if(d->timedUser != "" && d->loginDelay > 0)
315 qDebug() << "Logging in as " << d->timedUser << " in " << d->loginDelay << " seconds";
316 //FIXME: d->login_timeout = g_timeout_add (d->login_delay * 1000, timed_login_cb, greeter);
320 case GREETER_MESSAGE_QUIT:
321 qDebug() << "Got quit request from server";
324 case GREETER_MESSAGE_PROMPT_AUTHENTICATION:
325 nMessages = readInt(&offset);
326 qDebug() << "Prompt user with " << nMessages << " message(s)";
327 for(int i = 0; i < nMessages; i++)
329 int msg_style = readInt (&offset);
330 QString msg = readString (&offset);
332 // FIXME: Should stop on prompts?
335 case PAM_PROMPT_ECHO_OFF:
336 case PAM_PROMPT_ECHO_ON:
337 emit showPrompt(msg);
343 emit showMessage(msg);
348 case GREETER_MESSAGE_END_AUTHENTICATION:
349 returnCode = readInt(&offset);
350 qDebug() << "Authentication complete with return code " << returnCode;
351 d->isAuthenticated = (returnCode == 0);
352 if(!d->isAuthenticated) {
353 d->authenticationUser = "";
355 emit authenticationComplete(d->isAuthenticated);
356 d->inAuthentication = false;
359 qDebug() << "Unknown message from server: " << id;
365 QString Greeter::hostname() const
367 return QHostInfo::localHostName();
370 QString Greeter::theme() const
375 QVariant Greeter::getProperty(const QString &name) const
377 return QVariant(); //FIXME TODO
380 QString Greeter::defaultLanguage() const
382 return getenv("LANG");
385 QString Greeter::defaultLayout() const
387 return d->defaultLayout;
390 QString Greeter::defaultSession() const
392 return d->defaultSession;
395 QString Greeter::timedLoginUser() const
400 int Greeter::timedLoginDelay() const
402 return d->loginDelay;
405 void Greeter::loadConfig()
412 file = d->lightdmInterface->property("ConfigFile").toString();
413 qDebug() << "Loading configuration from " << file;
414 d->config = new QSettings(file, QSettings::IniFormat);
416 d->haveConfig = true;
419 void Greeter::loadUsers()
421 QStringList hiddenUsers, hiddenShells;
423 QList<User*> users, oldUsers, newUsers, changedUsers;
427 if(d->config->contains("UserManager/minimum-uid"))
428 minimumUid = d->config->value("UserManager/minimum-uid").toInt();
432 if (d->config->contains("UserManager/hidden-shells"))
433 hiddenUsers = d->config->value("UserManager/hidden-shells").toString().split(" ");
435 hiddenUsers << "nobody" << "nobody4" << "noaccess";
437 if (d->config->contains("UserManager/hidden-shells"))
438 hiddenShells = d->config->value("UserManager/hidden-shells").toString().split(" ");
440 hiddenShells << "/bin/false" << "/usr/sbin/nologin";
446 struct passwd *entry;
449 QString realName, image;
458 /* Ignore system users */
459 if(entry->pw_uid < minimumUid)
462 /* Ignore users disabled by shell */
465 for(i = 0; i < hiddenShells.size(); i++)
466 if(entry->pw_shell == hiddenShells.at(i))
468 if(i < hiddenShells.size())
472 /* Ignore certain users */
473 for(i = 0; i < hiddenUsers.size(); i++)
474 if(entry->pw_name == hiddenUsers.at(i))
476 if(i < hiddenUsers.size())
479 tokens = QString(entry->pw_gecos).split(",");
480 if(tokens.size() > 0 && tokens.at(i) != "")
481 realName = tokens.at(i);
483 QDir homeDir(entry->pw_dir);
484 imageFile = new QFile(homeDir.filePath(".face"));
485 if(!imageFile->exists())
488 imageFile = new QFile(homeDir.filePath(".face.icon"));
490 if(imageFile->exists())
491 image = "file://" + imageFile->fileName();
494 user = new User(entry->pw_name, realName, entry->pw_dir, image, FALSE);
496 /* Update existing users if have them */
497 bool matchedUser = false;
498 foreach(User *info, d->users)
500 if(info->name() == user->name())
503 if(info->update(user->realName(), user->homeDirectory(), user->image(), user->isLoggedIn()))
504 changedUsers.append(user);
512 /* Only notify once we have loaded the user list */
514 newUsers.append(user);
520 qDebug() << "Failed to read password database: " << strerror(errno);
524 /* Use new user list */
528 /* Notify of changes */
529 foreach(User *user, newUsers)
531 qDebug() << "User " << user->name() << " added";
532 emit userAdded(user);
535 foreach(User *user, changedUsers)
537 qDebug() << "User " << user->name() << " changed";
538 emit userChanged(user);
541 foreach(User *user, oldUsers)
543 /* See if this user is in the current list */
544 bool existing = false;
545 foreach(User *new_user, d->users)
547 if (new_user == user)
556 qDebug() << "User " << user->name() << " removed";
557 emit userRemoved(user);
563 void Greeter::updateUsers()
571 /* User listing is disabled */
572 if (d->config->contains("UserManager/load-users") &&
573 !d->config->value("UserManager/load-users").toBool())
584 QList<User*> Greeter::users()
590 SessionsModel* Greeter::sessionsModel() const
592 return d->sessionsModel;
596 bool Greeter::canSuspend() const
598 QDBusReply<bool> reply = d->powerManagementInterface->call("CanSuspend");
600 return reply.value();
605 void Greeter::suspend()
607 d->powerManagementInterface->call("Suspend");
610 bool Greeter::canHibernate() const
612 QDBusReply<bool> reply = d->powerManagementInterface->call("CanHibernate");
614 return reply.value();
619 void Greeter::hibernate()
621 d->powerManagementInterface->call("Hibernate");
624 bool Greeter::canShutdown() const
626 QDBusReply<bool> reply = d->consoleKitInterface->call("CanStop");
628 return reply.value();
633 void Greeter::shutdown()
635 d->consoleKitInterface->call("stop");
638 bool Greeter::canRestart() const
640 QDBusReply<bool> reply = d->consoleKitInterface->call("CanRestart");
642 return reply.value();
647 void Greeter::restart()
649 d->consoleKitInterface->call("Restart");