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