]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - liblightdm-qt/QLightDM/greeter.cpp
Support hide-users hint
[sojka/lightdm.git] / liblightdm-qt / QLightDM / greeter.cpp
1 /*
2  * Copyright (C) 2010-2011 David Edmundson
3  * Copyright (C) 2010-2011 Robert Ancell
4  * Author: David Edmundson <kde@davidedmundson.co.uk>
5  *
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
10  * license.
11  */
12
13 #include "greeter.h"
14
15 #include "user.h"
16 #include "sessionsmodel.h"
17 #include "config.h"
18
19 #include <security/pam_appl.h>
20
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>
33
34 /* Messages from the greeter to the server */
35 typedef enum
36 {
37     GREETER_MESSAGE_CONNECT = 0,
38     GREETER_MESSAGE_LOGIN,
39     GREETER_MESSAGE_LOGIN_AS_GUEST,
40     GREETER_MESSAGE_CONTINUE_AUTHENTICATION,
41     GREETER_MESSAGE_START_SESSION,
42     GREETER_MESSAGE_CANCEL_AUTHENTICATION
43 } GreeterMessage;
44
45 /* Messages from the server to the greeter */
46 typedef enum
47 {
48     SERVER_MESSAGE_CONNECTED = 0,
49     SERVER_MESSAGE_QUIT,
50     SERVER_MESSAGE_PROMPT_AUTHENTICATION,
51     SERVER_MESSAGE_END_AUTHENTICATION,
52     SERVER_MESSAGE_SESSION_FAILED,
53 } ServerMessage;
54
55 #define HEADER_SIZE 8
56
57 using namespace QLightDM;
58
59 class GreeterPrivate
60 {
61 public:
62     SessionsModel *sessionsModel;
63
64     QDBusInterface* lightdmInterface;
65     QDBusInterface* powerManagementInterface;
66     QDBusInterface* consoleKitInterface;
67
68     QHash<QString, QString> hints;
69
70     int toServerFd;
71     int fromServerFd;
72     QSocketNotifier *n;
73     char *readBuffer;
74     int nRead;
75     bool inAuthentication;
76     bool isAuthenticated;
77     QString authenticationUser;
78     int authenticateSequenceNumber;
79     bool cancellingAuthentication;
80 };
81
82
83 Greeter::Greeter(QObject *parent) :
84     QObject(parent),
85     d(new GreeterPrivate)
86 {
87     d->readBuffer = (char *)malloc (HEADER_SIZE);
88     d->nRead = 0;
89     d->sessionsModel = new SessionsModel(this);
90     d->authenticateSequenceNumber = 0;
91 }
92
93 Greeter::~Greeter()
94 {
95     delete d->readBuffer;
96     delete d;
97 }
98
99 static int intLength()
100 {
101     return 4;
102 }
103
104 static int stringLength(QString value)
105 {
106     QByteArray a = value.toUtf8();
107     return intLength() + a.size();
108 }
109
110 void Greeter::writeInt(int value)
111 {
112     char buffer[4];
113     buffer[0] = value >> 24;
114     buffer[1] = (value >> 16) & 0xFF;
115     buffer[2] = (value >> 8) & 0xFF;
116     buffer[3] = value & 0xFF;
117     if (write(d->toServerFd, buffer, intLength()) != intLength()) {
118         qDebug() << "Error writing to server";
119     }
120 }
121
122 void Greeter::writeString(QString value)
123 {
124     QByteArray a = value.toUtf8();
125     writeInt(a.size());
126     if (write(d->toServerFd, a.data(), a.size()) != a.size()) {
127         qDebug() << "Error writing to server";
128     }
129 }
130
131 void Greeter::writeHeader(int id, int length)
132 {
133     writeInt(id);
134     writeInt(length);
135 }
136
137 void Greeter::flush()
138 {
139     fsync(d->toServerFd);
140 }
141
142 int Greeter::getPacketLength()
143 {
144     int offset = intLength();
145     return readInt(&offset);
146 }
147
148 int Greeter::readInt(int *offset)
149 {
150     if(d->nRead - *offset < intLength()) {
151         qDebug() << "Not enough space for int, need " << intLength() << ", got " << (d->nRead - *offset);
152         return 0;
153     }
154
155     char *buffer = d->readBuffer + *offset;
156     int value = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
157     *offset += intLength();
158     return value;
159 }
160
161 QString Greeter::readString(int *offset)
162 {
163     int length = readInt(offset);
164     if(d->nRead - *offset < length) {
165         qDebug() << "Not enough space for string, need " << length << ", got " << (d->nRead - *offset);
166         return "";
167     }
168     char *start = d->readBuffer + *offset;
169     *offset += length;
170     return QString::fromUtf8(start, length);
171 }
172
173 void Greeter::connectToServer()
174 {
175     QDBusConnection busType = QDBusConnection::systemBus();
176     QString ldmBus(qgetenv("LDM_BUS"));
177     if(ldmBus == QLatin1String("SESSION")) {
178         busType = QDBusConnection::sessionBus();
179     }
180
181     d->lightdmInterface = new QDBusInterface("org.freedesktop.DisplayManager", "/org/freedesktop/DisplayManager", "org.freedesktop.DisplayManager", busType);
182     d->powerManagementInterface = new QDBusInterface("org.freedesktop.PowerManagement","/org/freedesktop/PowerManagement", "org.freedesktop.PowerManagement");
183     d->consoleKitInterface = new QDBusInterface("org.freedesktop.ConsoleKit", "/org/freedesktop/ConsoleKit/Manager", "org.freedesktop.ConsoleKit");
184
185     char* fd = getenv("LIGHTDM_TO_SERVER_FD");
186     if(!fd) {
187        qDebug() << "No LIGHTDM_TO_SERVER_FD environment variable";
188        return;
189     }
190     d->toServerFd = atoi(fd);
191
192     qDebug() << "***connecting to server";
193     QFile toServer;
194     qDebug() << toServer.open(d->toServerFd, QIODevice::WriteOnly);
195
196     fd = getenv("LIGHTDM_FROM_SERVER_FD");
197     if(!fd) {
198        qDebug() << "No LIGHTDM_FROM_SERVER_FD environment variable";
199        return;
200     }
201     d->fromServerFd = atoi(fd);
202
203     d->n = new QSocketNotifier(d->fromServerFd, QSocketNotifier::Read);
204     connect(d->n, SIGNAL(activated(int)), this, SLOT(onRead(int)));
205
206     qDebug() << "Connecting to display manager...";
207     writeHeader(GREETER_MESSAGE_CONNECT, stringLength(VERSION));
208     writeString(VERSION);
209     flush();
210 }
211
212 void Greeter::login(const QString &username)
213 {
214     d->inAuthentication = true;
215     d->isAuthenticated = false;
216     d->cancellingAuthentication = false;
217     d->authenticationUser = username;
218     qDebug() << "Starting authentication for user " << username << "...";
219     writeHeader(GREETER_MESSAGE_LOGIN, intLength() + stringLength(username));
220     d->authenticateSequenceNumber++;
221     writeInt(d->authenticateSequenceNumber);
222     writeString(username);
223     flush();
224 }
225
226 void Greeter::loginAsGuest()
227 {
228     d->authenticateSequenceNumber++;
229     d->inAuthentication = true;
230     d->isAuthenticated = false;
231     d->cancellingAuthentication = false;
232     d->authenticationUser = "";
233     qDebug() << "Starting authentication for guest account";
234     writeHeader(GREETER_MESSAGE_LOGIN_AS_GUEST, intLength());
235     writeInt(d->authenticateSequenceNumber);
236     flush();
237 }
238
239 void Greeter::respond(const QString &response)
240 {
241     qDebug() << "Providing response to display manager";
242     writeHeader(GREETER_MESSAGE_CONTINUE_AUTHENTICATION, intLength() + stringLength(response));
243     // FIXME: Could be multiple response required
244     writeInt(1);
245     writeString(response);
246     flush();
247 }
248
249 void Greeter::cancelAuthentication()
250 {
251     qDebug() << "Cancelling authentication";
252     d->cancellingAuthentication = true;
253     writeHeader(GREETER_MESSAGE_CANCEL_AUTHENTICATION, 0);
254     flush();
255 }
256
257 bool Greeter::inAuthentication() const
258 {
259     return d->inAuthentication;
260 }
261
262 bool Greeter::isAuthenticated() const
263 {
264     return d->isAuthenticated;
265 }
266
267 QString Greeter::authenticationUser() const
268 {
269     return d->authenticationUser;
270 }
271
272 void Greeter::startSession(const QString &session)
273 {
274     qDebug() << "Starting session " << session;
275     writeHeader(GREETER_MESSAGE_START_SESSION, stringLength(session));
276     writeString(session);
277     flush();
278 }
279
280 void Greeter::onRead(int fd)
281 {
282     //qDebug() << "Reading from server";
283
284     int nToRead = HEADER_SIZE;
285     if(d->nRead >= HEADER_SIZE)
286         nToRead += getPacketLength();
287
288     ssize_t nRead;
289     nRead = read(fd, d->readBuffer + d->nRead, nToRead - d->nRead);
290     if(nRead < 0)
291     {
292         qDebug() << "Error reading from server";
293         return;
294     }
295     if (nRead == 0)
296     {
297         qDebug() << "EOF reading from server";
298         return;
299     }
300
301     //qDebug() << "Read " << nRead << "octets";
302     d->nRead += nRead;
303     if(d->nRead != nToRead)
304         return;
305
306     /* If have header, rerun for content */
307     if(d->nRead == HEADER_SIZE)
308     {
309         nToRead = getPacketLength();
310         if(nToRead > 0)
311         {
312             d->readBuffer = (char *)realloc(d->readBuffer, HEADER_SIZE + nToRead);
313             onRead(fd);
314             return;
315         }
316     }
317
318     int offset = 0;
319     int id = readInt(&offset);
320     int length = readInt(&offset);
321     int nMessages, sequenceNumber, returnCode;
322     QString version, username;
323     QString hintString = "";
324     switch(id)
325     {
326     case SERVER_MESSAGE_CONNECTED:
327         version = readString(&offset);
328         while (offset < length)
329         {
330             QString name = readString(&offset);
331             QString value = readString(&offset);
332             hintString.append (" ");
333             hintString.append (name);
334             hintString.append ("=");
335             hintString.append (value);
336         }
337
338         qDebug() << "Connected version=" << version << hintString;
339
340         emit connected();
341         break;
342     case SERVER_MESSAGE_QUIT:
343         qDebug() << "Got quit request from server";
344         emit quit();
345         break;
346     case SERVER_MESSAGE_PROMPT_AUTHENTICATION:
347         sequenceNumber = readInt(&offset);
348
349         if (sequenceNumber == d->authenticateSequenceNumber &&
350             !d->cancellingAuthentication)
351         {
352             nMessages = readInt(&offset);
353             qDebug() << "Prompt user with " << nMessages << " message(s)";
354             for(int i = 0; i < nMessages; i++)
355             {
356                 int msg_style = readInt (&offset);
357                 QString msg = readString (&offset);
358
359                 // FIXME: Should stop on prompts?
360                 switch (msg_style)
361                 {
362                 case PAM_PROMPT_ECHO_OFF:
363                 case PAM_PROMPT_ECHO_ON:
364                     emit showPrompt(msg);
365                     break;
366                 case PAM_ERROR_MSG:
367                     emit showError(msg);
368                     break;
369                 case PAM_TEXT_INFO:
370                     emit showMessage(msg);
371                     break;
372                 }
373             }
374         }
375         break;
376     case SERVER_MESSAGE_END_AUTHENTICATION:
377         sequenceNumber = readInt(&offset);
378         returnCode = readInt(&offset);
379
380         if (sequenceNumber == d->authenticateSequenceNumber)
381         {
382             qDebug() << "Authentication complete with return code " << returnCode;
383             d->cancellingAuthentication = false;
384             d->isAuthenticated = (returnCode == 0);
385             if(!d->isAuthenticated) {
386                 d->authenticationUser = "";
387             }
388             emit authenticationComplete(d->isAuthenticated);
389             d->inAuthentication = false;
390         }
391         else
392             qDebug () << "Ignoring end authentication with invalid sequence number " << sequenceNumber;
393         break;
394     case SERVER_MESSAGE_SESSION_FAILED:
395         qDebug() << "Session failed to start";
396         emit sessionFailed();
397         break;
398     default:
399         qDebug() << "Unknown message from server: " << id;
400     }
401
402     d->nRead = 0;
403 }
404
405 QString Greeter::hostname() const
406 {
407     return QHostInfo::localHostName();
408 }
409
410 QString Greeter::defaultLanguage() const
411 {
412     return getenv("LANG");
413 }
414
415 QString Greeter::getHint(QString name) const
416 {
417     return d->hints.value (name);
418 }
419
420 QString Greeter::defaultSessionHint() const
421 {
422     return getHint ("default-session");
423 }
424
425 bool Greeter::hideUsersHint() const
426 {
427     return d->hints.value ("hide-users", "true") == "true";
428 }
429
430 bool Greeter::hasGuestAccountHint() const
431 {
432     return d->hints.value ("has-guest-account", "false") == "true";
433 }
434
435 QString Greeter::selectUserHint() const
436 {
437     return getHint ("select-user");
438 }
439
440 bool Greeter::selectGuestHint() const
441 {
442     return d->hints.value ("select-guest", "false") == "true";
443 }
444
445 QString Greeter::autologinUserHint() const
446 {
447     return getHint ("autologin-user");
448 }
449
450 bool Greeter::autologinGuestHint() const
451 {
452     return d->hints.value ("autologin-guest", "false") == "true";
453 }
454
455 int Greeter::autologinTimeoutHint() const
456 {
457     return d->hints.value ("autologin-timeout", "0").toInt ();
458 }
459
460 bool Greeter::canSuspend() const
461 {
462     QDBusReply<bool> reply = d->powerManagementInterface->call("CanSuspend");
463     if (reply.isValid())
464         return reply.value();
465     else
466         return false;
467 }
468
469 void Greeter::suspend()
470 {
471     d->powerManagementInterface->call("Suspend");
472 }
473
474 bool Greeter::canHibernate() const
475 {
476     QDBusReply<bool> reply = d->powerManagementInterface->call("CanHibernate");
477     if (reply.isValid())
478         return reply.value();
479     else
480         return false;
481 }
482
483 void Greeter::hibernate()
484 {
485     d->powerManagementInterface->call("Hibernate");
486 }
487
488 bool Greeter::canShutdown() const
489 {
490     QDBusReply<bool> reply = d->consoleKitInterface->call("CanStop");
491     if (reply.isValid())
492         return reply.value();
493     else
494         return false;
495 }
496
497 void Greeter::shutdown()
498 {
499     d->consoleKitInterface->call("stop");
500 }
501
502 bool Greeter::canRestart() const
503 {
504     QDBusReply<bool> reply = d->consoleKitInterface->call("CanRestart");
505     if (reply.isValid())
506         return reply.value();
507     else
508         return false;
509 }
510
511 void Greeter::restart()
512 {
513     d->consoleKitInterface->call("Restart");
514 }