]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/display.c
Add configuration for default language/layout
[sojka/lightdm.git] / src / display.c
1 /*
2  * Copyright (C) 2010 Robert Ancell.
3  * Author: Robert Ancell <robert.ancell@canonical.com>
4  * 
5  * This program is free software: you can redistribute it and/or modify it under
6  * the terms of the GNU General Public License as published by the Free Software
7  * Foundation, either version 3 of the License, or (at your option) any later
8  * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
9  * license.
10  */
11
12 #include <config.h>
13
14 #include <stdlib.h>
15 #include <string.h>
16 #include <errno.h>
17 #include <pwd.h>
18 #include <gio/gdesktopappinfo.h>
19
20 #ifdef HAVE_CONSOLE_KIT
21 #include <ck-connector.h>
22 #endif
23
24 #include "display.h"
25 #include "display-glue.h"
26 #include "pam-session.h"
27 #include "theme.h"
28 #include "ldm-marshal.h"
29
30 /* Length of time in milliseconds to wait for a session to load */
31 #define USER_SESSION_TIMEOUT 5000
32
33 /* Length of time in milliseconds to wait for a greeter to quit */
34 #define GREETER_QUIT_TIMEOUT 1000
35
36 enum {
37     START_GREETER,
38     END_GREETER,
39     START_SESSION,
40     END_SESSION,
41     EXITED,
42     QUIT_GREETER,
43     LAST_SIGNAL
44 };
45 static guint signals[LAST_SIGNAL] = { 0 };
46
47 typedef enum
48 {
49     SESSION_NONE = 0,
50     SESSION_GREETER_PRE_CONNECT,
51     SESSION_GREETER,
52     SESSION_GREETER_AUTHENTICATED,
53     SESSION_USER
54 } SessionType;
55
56 struct DisplayPrivate
57 {
58     gint index;
59
60     /* X server */
61     XServer *xserver;
62   
63     /* Number of times have logged in */
64     gint login_count;
65
66     /* Language to use in greeter/sessions */
67     gchar *default_language;
68
69     /* Layout to use in greeter/sessions */
70     gchar *default_layout;
71
72     /* User to run greeter as */
73     gchar *greeter_user;
74
75     /* Theme to use */
76     gchar *greeter_theme;
77
78     /* Greeter session process */
79     Session *greeter_session;
80     gboolean greeter_connected;
81     guint greeter_quit_timeout;
82     PAMSession *greeter_pam_session;
83 #ifdef HAVE_CONSOLE_KIT
84     CkConnector *greeter_ck_session;
85 #endif
86
87     gboolean supports_transitions;
88
89     /* User session process */
90     Session *user_session;
91     guint user_session_timer;
92     PAMSession *user_pam_session;
93 #ifdef HAVE_CONSOLE_KIT
94     CkConnector *user_ck_session;
95 #endif
96
97     /* Current D-Bus call context */
98     DBusGMethodInvocation *dbus_context;
99
100     /* Default login hint */
101     gchar *default_user;
102     gint timeout;
103
104     /* Default session */
105     gchar *default_session;
106 };
107
108 G_DEFINE_TYPE (Display, display, G_TYPE_OBJECT);
109
110 static void start_greeter (Display *display);
111
112 // FIXME: Remove the index, it is an external property
113 Display *
114 display_new (gint index)
115 {
116     Display *self = g_object_new (DISPLAY_TYPE, NULL);
117
118     self->priv->index = index;
119
120     return self;
121 }
122
123 gint
124 display_get_index (Display *display)
125 {
126     return display->priv->index;
127 }
128
129 void
130 display_set_default_user (Display *display, const gchar *username)
131 {
132     g_free (display->priv->default_user);
133     display->priv->default_user = g_strdup (username);
134 }
135
136 const gchar *
137 display_get_default_user (Display *display)
138 {
139     return display->priv->default_user;
140 }
141
142 void
143 display_set_default_user_timeout (Display *display, gint timeout)
144 {
145     display->priv->timeout = timeout;  
146 }
147
148 gint
149 display_get_default_user_timeout (Display *display)
150 {
151     return display->priv->timeout;
152 }
153
154 void
155 display_set_greeter_user (Display *display, const gchar *username)
156 {
157     g_free (display->priv->greeter_user);
158     display->priv->greeter_user = g_strdup (username);
159 }
160
161 const gchar *
162 display_get_greeter_user (Display *display)
163 {
164     return display->priv->greeter_user;  
165 }
166
167 const gchar *
168 display_get_session_user (Display *display)
169 {
170     if (display->priv->user_session)
171         return pam_session_get_username (display->priv->user_pam_session);
172     else
173         return NULL;
174 }
175
176 void
177 display_set_greeter_theme (Display *display, const gchar *greeter_theme)
178 {
179     g_free (display->priv->greeter_theme);
180     display->priv->greeter_theme = g_strdup (greeter_theme);
181 }
182
183 const gchar *
184 display_get_greeter_theme (Display *display)
185 {
186     return display->priv->greeter_theme;
187 }
188
189 void
190 display_set_default_language (Display *display, const gchar *language)
191 {
192     g_free (display->priv->default_language);
193     display->priv->default_language = g_strdup (language);
194 }
195
196 const gchar *
197 display_get_default_language (Display *display)
198 {
199     return display->priv->default_language;
200 }
201
202 void
203 display_set_default_layout (Display *display, const gchar *layout)
204 {
205     g_free (display->priv->default_layout);
206     display->priv->default_layout = g_strdup (layout);
207 }
208
209 const gchar *
210 display_get_default_layout (Display *display)
211 {
212     return display->priv->default_layout;
213 }
214
215 void
216 display_set_default_session (Display *display, const gchar *session)
217 {
218     g_free (display->priv->default_session);
219     display->priv->default_session = g_strdup (session);
220 }
221
222 const gchar *
223 display_get_default_session (Display *display)
224 {
225     return display->priv->default_session;
226 }
227
228 void
229 display_set_xserver (Display *display, XServer *xserver)
230 {
231     if (display->priv->xserver)
232         g_object_unref (display->priv->xserver);
233     display->priv->xserver = g_object_ref (xserver);  
234 }
235
236 XServer *
237 display_get_xserver (Display *display)
238 {
239     return display->priv->xserver;
240 }
241
242 static struct passwd *
243 get_user_info (const gchar *username)
244 {
245     struct passwd *user_info;
246
247     errno = 0;
248     user_info = getpwnam (username);
249     if (!user_info)
250     {
251         if (errno == 0)
252             g_warning ("Unable to get information on user %s: User does not exist", username);
253         else
254             g_warning ("Unable to get information on user %s: %s", username, strerror (errno));
255     }
256
257     return user_info;
258 }
259
260 #ifdef HAVE_CONSOLE_KIT
261 static CkConnector *
262 start_ck_session (Display *display, const gchar *session_type, const gchar *username)
263 {
264     CkConnector *session;
265     DBusError error;
266     const gchar *address, *hostname = "";
267     struct passwd *user_info;
268     gboolean is_local = TRUE;
269
270     session = ck_connector_new ();
271
272     user_info = get_user_info (username);
273     if (!user_info)
274         return session;
275
276     dbus_error_init (&error);
277     address = xserver_get_address (display->priv->xserver);
278     if (!ck_connector_open_session_with_parameters (session, &error,
279                                                     "session-type", &session_type,
280                                                     "unix-user", &user_info->pw_uid,
281                                                     //"display-device", &display->priv->display_device,
282                                                     //"x11-display-device", &display->priv->x11_display_device,
283                                                     "remote-host-name", &hostname,
284                                                     "x11-display", &address,
285                                                     "is-local", &is_local,
286                                                     NULL))
287         g_warning ("Failed to open CK session: %s: %s", error.name, error.message);
288
289     return session;
290 }
291
292 static void
293 end_ck_session (CkConnector *session)
294 {
295     if (!session)
296         return;
297     ck_connector_close_session (session, NULL); // FIXME: Handle errors
298     ck_connector_unref (session);
299 }
300
301 #endif
302
303 static void
304 end_user_session (Display *display, gboolean clean_exit)
305 {  
306     g_signal_emit (display, signals[END_SESSION], 0, display->priv->user_session);
307   
308     if (display->priv->user_session_timer)
309     {
310         g_source_remove (display->priv->user_session_timer);
311         display->priv->user_session_timer = 0;
312     }
313
314     g_object_unref (display->priv->user_session);
315     display->priv->user_session = NULL;
316
317     pam_session_end (display->priv->user_pam_session);
318     g_object_unref (display->priv->user_pam_session);
319     display->priv->user_pam_session = NULL;
320
321 #ifdef HAVE_CONSOLE_KIT
322     end_ck_session (display->priv->user_ck_session);
323     display->priv->user_ck_session = NULL;
324 #endif
325
326     if (!clean_exit)
327         g_warning ("Session exited unexpectedly");
328
329     xserver_disconnect_clients (display->priv->xserver);
330 }
331
332 static void
333 user_session_exited_cb (Session *session, gint status, Display *display)
334 {
335     end_user_session (display, status == 0);
336 }
337
338 static void
339 user_session_killed_cb (Session *session, gint status, Display *display)
340 {
341     end_user_session (display, FALSE);
342 }
343
344 static void
345 start_user_session (Display *display, const gchar *session, const gchar *language)
346 {
347     gchar *filename, *path;
348     struct passwd *user_info;
349     GKeyFile *dmrc_file, *session_desktop_file;
350     gboolean have_dmrc = FALSE, result;
351     GError *error = NULL;
352
353     g_debug ("Launching '%s' session for user %s", session, pam_session_get_username (display->priv->user_pam_session));
354     display->priv->login_count++;
355
356     /* Load the users login settings (~/.dmrc) */
357     dmrc_file = g_key_file_new ();
358     user_info = get_user_info (pam_session_get_username (display->priv->user_pam_session));
359     if (user_info)
360     {
361         /* Load from the user directory, if this fails (e.g. the user directory
362          * is not yet mounted) then load from the cache */
363         path = g_build_filename (user_info->pw_dir, ".dmrc", NULL);
364         have_dmrc = g_key_file_load_from_file (dmrc_file, path, G_KEY_FILE_NONE, NULL);
365         g_free (path);
366     }
367
368     /* If no .dmrc, then load from the cache */
369     if (!have_dmrc)
370     {
371         filename = g_strdup_printf ("%s.dmrc", user_info->pw_name);
372         path = g_build_filename (CACHE_DIR, "dmrc", filename, NULL);
373         g_free (filename);
374         if (!g_key_file_load_from_file (dmrc_file, path, G_KEY_FILE_NONE, &error))          
375             g_warning ("Failed to load .dmrc file %s: %s", path, error->message);
376         g_clear_error (&error);
377         g_free (path);
378     }
379   
380     /* Update the .dmrc with changed settings */
381     g_key_file_set_string (dmrc_file, "Desktop", "Session", session);
382     if (language)
383         g_key_file_set_string (dmrc_file, "Desktop", "Language", language);
384     else if (!g_key_file_has_key (dmrc_file, "Desktop", "Language", NULL))
385         g_key_file_set_string (dmrc_file, "Desktop", "Language", display->priv->default_language);
386     if (!g_key_file_has_key (dmrc_file, "Desktop", "Layout", NULL))
387         g_key_file_set_string (dmrc_file, "Desktop", "Layout", display->priv->default_layout);
388
389     filename = g_strdup_printf ("%s.desktop", session);
390     path = g_build_filename (XSESSIONS_DIR, filename, NULL);
391     g_free (filename);
392
393     session_desktop_file = g_key_file_new ();
394     result = g_key_file_load_from_file (session_desktop_file, path, G_KEY_FILE_NONE, &error);
395     g_free (path);
396
397     if (!result)
398         g_warning ("Failed to load session file %s: %s:", path, error->message);
399     g_clear_error (&error);
400
401     if (result)
402     {
403         gchar *session_command, *command = NULL;
404
405         session_command = g_key_file_get_string (session_desktop_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL);
406         if (!session_command)
407             g_warning ("No command in session file %s", path);
408         if (session_command)
409             command = g_strdup_printf ("/etc/X11/Xsession %s", session_command);
410         g_free (session_command);
411
412         display->priv->supports_transitions = g_key_file_get_boolean (session_desktop_file, G_KEY_FILE_DESKTOP_GROUP, "X-LightDM-Supports-Transitions", NULL);
413
414         if (command)
415         {
416             gchar *session_language, *layout;
417             gchar *data;
418             gsize length;
419
420             display->priv->user_session = session_new (pam_session_get_username (display->priv->user_pam_session), command);
421
422             session_language = g_key_file_get_string (dmrc_file, "Desktop", "Language", NULL);
423             g_debug ("session_language='%s'", session_language);
424
425             layout = g_key_file_get_string (dmrc_file, "Desktop", "Layout", NULL);
426
427             g_signal_connect (G_OBJECT (display->priv->user_session), "exited", G_CALLBACK (user_session_exited_cb), display);
428             g_signal_connect (G_OBJECT (display->priv->user_session), "killed", G_CALLBACK (user_session_killed_cb), display);
429             session_set_env (display->priv->user_session, "DISPLAY", xserver_get_address (display->priv->xserver));
430 #ifdef HAVE_CONSOLE_KIT
431             session_set_env (display->priv->user_session, "XDG_SESSION_COOKIE", ck_connector_get_cookie (display->priv->user_ck_session));
432 #endif
433             session_set_env (display->priv->user_session, "DESKTOP_SESSION", session); // FIXME: Apparently deprecated?
434             session_set_env (display->priv->user_session, "GDMSESSION", session); // FIXME: Not cross-desktop
435             session_set_env (display->priv->user_session, "PATH", "/usr/local/bin:/usr/bin:/bin");
436             session_set_env (display->priv->user_session, "LANG", session_language);
437             session_set_env (display->priv->user_session, "GDM_LANG", session_language); // FIXME: Not cross-desktop
438             session_set_env (display->priv->user_session, "GDM_KEYBOARD_LAYOUT", layout); // FIXME: Not cross-desktop
439
440             g_signal_emit (display, signals[START_SESSION], 0, display->priv->user_session);
441
442             session_start (display->priv->user_session);
443
444             data = g_key_file_to_data (dmrc_file, &length, NULL);
445
446             /* Update the users .dmrc */
447             if (user_info)
448             {
449                 path = g_build_filename (user_info->pw_dir, ".dmrc", NULL);
450                 g_file_set_contents (path, data, length, NULL);
451                 g_free (path);
452             }
453
454             /* Update the .dmrc cache */
455             filename = g_strdup_printf ("%s.dmrc", pam_session_get_username (display->priv->user_pam_session));
456             path = g_build_filename (CACHE_DIR, "dmrc", filename, NULL);
457             g_file_set_contents (path, data, length, NULL);
458             g_free (path);
459
460             g_free (data);
461             g_free (session_language); 
462             g_free (layout);         
463         }
464
465         g_free (command);
466     }
467
468     g_key_file_free (session_desktop_file);
469     g_key_file_free (dmrc_file);  
470 }
471
472 static void
473 start_default_session (Display *display, const gchar *session, const gchar *language)
474 {
475     /* Don't need to check authentication, just authorize */
476     if (display->priv->user_pam_session)
477         pam_session_end (display->priv->user_pam_session);    
478     display->priv->user_pam_session = pam_session_new (display->priv->default_user);
479     pam_session_authorize (display->priv->user_pam_session);
480
481 #ifdef HAVE_CONSOLE_KIT
482     display->priv->user_ck_session = start_ck_session (display, "", pam_session_get_username (display->priv->user_pam_session));
483 #endif
484     start_user_session (display, session, language);
485 }
486
487 static void
488 end_greeter_session (Display *display, gboolean clean_exit)
489 {  
490     gboolean greeter_connected;
491   
492     if (display->priv->greeter_quit_timeout)
493     {
494         g_source_remove (display->priv->greeter_quit_timeout);
495         display->priv->greeter_quit_timeout = 0;
496     }
497
498     g_signal_emit (display, signals[END_GREETER], 0, display->priv->greeter_session);
499
500     greeter_connected = display->priv->greeter_connected;
501
502     g_object_unref (display->priv->greeter_session);
503     display->priv->greeter_session = NULL;
504     display->priv->greeter_connected = FALSE;
505
506     pam_session_end (display->priv->greeter_pam_session);
507     g_object_unref (display->priv->greeter_pam_session);
508     display->priv->greeter_pam_session = NULL;
509
510 #ifdef HAVE_CONSOLE_KIT
511     end_ck_session (display->priv->greeter_ck_session);
512     display->priv->greeter_ck_session = NULL;
513 #endif
514
515     if (!clean_exit)
516         g_warning ("Greeter failed");
517     else if (!greeter_connected)
518         g_warning ("Greeter quit before connecting");
519     else if (!display->priv->user_session)
520         g_warning ("Greeter quit before session started");
521     else
522         return;
523
524     // FIXME: Issue with greeter, don't want to start a new one, report error to user
525 }
526
527 static void
528 greeter_session_exited_cb (Session *session, gint status, Display *display)
529 {
530     end_greeter_session (display, status == 0);
531 }
532
533 static void
534 greeter_session_killed_cb (Session *session, gint status, Display *display)
535 {
536     end_greeter_session (display, FALSE);
537 }
538
539 static void
540 start_greeter (Display *display)
541 {
542     GKeyFile *theme;
543     GError *error = NULL;
544   
545     theme = load_theme (display->priv->greeter_theme, &error);
546     if (!theme)
547         g_warning ("Failed to find theme %s: %s", display->priv->greeter_theme, error->message);
548     g_clear_error (&error);
549
550     if (theme)
551     {
552         gchar *command;
553
554         g_debug ("Starting greeter %s as user %s", display->priv->greeter_theme,
555                  display->priv->greeter_user ? display->priv->greeter_user : "<current>");
556
557         command = theme_get_command (theme);
558
559         display->priv->greeter_pam_session = pam_session_new (display->priv->greeter_user);
560         pam_session_authorize (display->priv->greeter_pam_session);
561
562 #ifdef HAVE_CONSOLE_KIT
563         display->priv->greeter_ck_session = start_ck_session (display,
564                                                               "LoginWindow",
565                                                               display->priv->greeter_user ? display->priv->greeter_user : getenv ("USER"));
566 #endif
567
568         display->priv->greeter_connected = FALSE;
569         display->priv->greeter_session = session_new (display->priv->greeter_user, command);
570         g_signal_connect (G_OBJECT (display->priv->greeter_session), "exited", G_CALLBACK (greeter_session_exited_cb), display);
571         g_signal_connect (G_OBJECT (display->priv->greeter_session), "killed", G_CALLBACK (greeter_session_killed_cb), display);
572         session_set_env (display->priv->greeter_session, "DISPLAY", xserver_get_address (display->priv->xserver));
573 #ifdef HAVE_CONSOLE_KIT
574         session_set_env (display->priv->greeter_session, "XDG_SESSION_COOKIE", ck_connector_get_cookie (display->priv->greeter_ck_session));
575 #endif
576
577         g_signal_emit (display, signals[START_GREETER], 0, display->priv->greeter_session);
578
579         session_start (display->priv->greeter_session);
580
581         g_free (command);
582         g_key_file_free (theme);
583     }
584 }
585
586 #define TYPE_MESSAGE dbus_g_type_get_struct ("GValueArray", G_TYPE_INT, G_TYPE_STRING, G_TYPE_INVALID)
587
588 static void
589 pam_messages_cb (PAMSession *session, int num_msg, const struct pam_message **msg, Display *display)
590 {
591     GPtrArray *request;
592     int i;
593     DBusGMethodInvocation *context;
594
595     /* Respond to d-bus query with messages */
596     request = g_ptr_array_new ();
597     for (i = 0; i < num_msg; i++)
598     {
599         GValue value = { 0 };
600       
601         g_value_init (&value, TYPE_MESSAGE);
602         g_value_take_boxed (&value, dbus_g_type_specialized_construct (TYPE_MESSAGE));
603         // FIXME: Need to convert to UTF-8
604         dbus_g_type_struct_set (&value, 0, msg[i]->msg_style, 1, msg[i]->msg, G_MAXUINT);
605         g_ptr_array_add (request, g_value_get_boxed (&value));
606     }
607
608     context = display->priv->dbus_context;
609     display->priv->dbus_context = NULL;
610     dbus_g_method_return (context, 0, request);
611 }
612
613 static void
614 authenticate_result_cb (PAMSession *session, int result, Display *display)
615 {
616     GPtrArray *request;
617     DBusGMethodInvocation *context;
618
619     g_debug ("Authenticate result for user %s: %s", pam_session_get_username (display->priv->user_pam_session), pam_session_strerror (display->priv->user_pam_session, result));
620
621     /* Respond to D-Bus request */
622     request = g_ptr_array_new ();
623     context = display->priv->dbus_context;
624     display->priv->dbus_context = NULL;
625
626     dbus_g_method_return (context, result, request);
627 }
628
629 static void
630 session_started_cb (PAMSession *session, Display *display)
631 {
632 #ifdef HAVE_CONSOLE_KIT
633     display->priv->user_ck_session = start_ck_session (display, "", pam_session_get_username (display->priv->user_pam_session));
634 #endif
635 }
636
637 gboolean
638 display_connect (Display *display,
639                  const gchar **theme,
640                  const gchar **language, const gchar **layout, const gchar **session,
641                  const gchar **username, gint *delay, GError *error)
642 {
643     if (!display->priv->greeter_connected)
644     {
645         display->priv->greeter_connected = TRUE;
646         g_debug ("Greeter connected");
647     }
648
649     *theme = g_build_filename (THEME_DIR, display->priv->greeter_theme, "index.theme", NULL);
650     *language = g_strdup (display->priv->default_language);
651     *layout = g_strdup (display->priv->default_layout);
652     *session = g_strdup (display->priv->default_session);
653     *username = g_strdup (display->priv->default_user);
654     *delay = display->priv->timeout;
655
656     return TRUE;
657 }
658
659 gboolean
660 display_start_authentication (Display *display, const gchar *username, DBusGMethodInvocation *context)
661 {
662     GError *error = NULL;
663
664     // FIXME: Only allow calls from the correct greeter
665
666     if (!display->priv->greeter_session || display->priv->user_session)
667     {
668         dbus_g_method_return_error (context, NULL);
669         return TRUE;
670     }
671
672     /* Abort existing authentication */
673     if (display->priv->user_pam_session)
674     {
675         g_signal_handlers_disconnect_matched (display->priv->user_pam_session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, display);
676         pam_session_end (display->priv->user_pam_session);
677         if (display->priv->dbus_context)
678             dbus_g_method_return_error (display->priv->dbus_context, NULL);
679
680         g_object_unref (display->priv->user_pam_session);
681     }
682
683     g_debug ("Greeter start authentication for %s", username);
684
685     /* Store D-Bus request to respond to */
686     display->priv->dbus_context = context;
687
688     display->priv->user_pam_session = pam_session_new (username);
689     g_signal_connect (G_OBJECT (display->priv->user_pam_session), "got-messages", G_CALLBACK (pam_messages_cb), display);
690     g_signal_connect (G_OBJECT (display->priv->user_pam_session), "authentication-result", G_CALLBACK (authenticate_result_cb), display);
691     g_signal_connect (G_OBJECT (display->priv->user_pam_session), "started", G_CALLBACK (session_started_cb), display);
692
693     if (!pam_session_start (display->priv->user_pam_session, &error))
694     {
695         g_warning ("Failed to start authentication: %s", error->message);
696         display->priv->dbus_context = NULL;
697         dbus_g_method_return_error (context, NULL);
698         return FALSE;
699     }
700     g_clear_error (&error);
701
702     return TRUE;
703 }
704
705 gboolean
706 display_continue_authentication (Display *display, gchar **secrets, DBusGMethodInvocation *context)
707 {
708     int num_messages;
709     const struct pam_message **messages;
710     struct pam_response *response;
711     int i, j, n_secrets = 0;
712
713     /* Not connected */
714     if (!display->priv->greeter_connected)
715     {
716         dbus_g_method_return_error (context, NULL);
717         return TRUE;
718     }
719
720     /* Not in authorization */
721     if (display->priv->user_pam_session == NULL)
722     {
723         dbus_g_method_return_error (context, NULL);
724         return TRUE;
725     }
726
727     /* Already in another call */
728     if (display->priv->dbus_context != NULL)
729     {
730         dbus_g_method_return_error (context, NULL);
731         return TRUE;
732     }
733
734     // FIXME: Only allow calls from the correct greeter
735
736     num_messages = pam_session_get_num_messages (display->priv->user_pam_session);
737     messages = pam_session_get_messages (display->priv->user_pam_session);
738
739     /* Check correct number of responses */
740     for (i = 0; i < num_messages; i++)
741     {
742         int msg_style = messages[i]->msg_style;
743         if (msg_style == PAM_PROMPT_ECHO_OFF || msg_style == PAM_PROMPT_ECHO_ON)
744             n_secrets++;
745     }
746     if (g_strv_length (secrets) != n_secrets)
747     {
748         pam_session_end (display->priv->user_pam_session);
749         // FIXME: Throw error
750         return FALSE;
751     }
752
753     g_debug ("Continue authentication");
754
755     /* Build response */
756     response = calloc (num_messages, sizeof (struct pam_response));  
757     for (i = 0, j = 0; i < num_messages; i++)
758     {
759         int msg_style = messages[i]->msg_style;
760         if (msg_style == PAM_PROMPT_ECHO_OFF || msg_style == PAM_PROMPT_ECHO_ON)
761         {
762             response[i].resp = strdup (secrets[j]); // FIXME: Need to convert from UTF-8
763             j++;
764         }
765     }
766
767     display->priv->dbus_context = context;
768     pam_session_respond (display->priv->user_pam_session, response);
769
770     return TRUE;
771 }
772
773 static gboolean
774 quit_greeter_cb (gpointer data)
775 {
776     Display *display = data;
777     g_warning ("Greeter did not quit, sending kill signal");
778     session_stop (display->priv->greeter_session);
779     display->priv->greeter_quit_timeout = 0;
780     return TRUE;
781 }
782
783 static void
784 quit_greeter (Display *display)
785 {
786     g_signal_emit (display, signals[QUIT_GREETER], 0);
787     if (display->priv->greeter_quit_timeout)
788         g_source_remove (display->priv->greeter_quit_timeout);
789     display->priv->greeter_quit_timeout = g_timeout_add (GREETER_QUIT_TIMEOUT, quit_greeter_cb, display);
790 }
791
792 static gboolean
793 session_timeout_cb (Display *display)
794 {
795     g_warning ("Session has not indicated it is ready, stopping greeter anyway");
796
797     /* Stop the greeter */
798     quit_greeter (display);
799
800     display->priv->user_session_timer = 0;
801     return FALSE;
802 }
803
804 gboolean
805 display_login (Display *display, gchar *username, gchar *session, gchar *language, GError *error)
806 {
807     if (display->priv->user_session != NULL)
808     {
809         g_warning ("Ignoring request to log in when already logged in");
810         return TRUE;
811     }
812
813     g_debug ("Greeter login for user %s on session %s", username, session);
814   
815     /* Default session requested */
816     if (strcmp (session, "") == 0)
817         session = display->priv->default_session;
818
819     /* Default language requested */
820     if (strcmp (language, "") == 0)
821         language = NULL;
822
823     if (display->priv->default_user && strcmp (username, display->priv->default_user) == 0)
824         start_default_session (display, session, language);
825     else if (display->priv->user_pam_session &&
826              pam_session_get_in_session (display->priv->user_pam_session) &&
827              strcmp (username, pam_session_get_username (display->priv->user_pam_session)) == 0)
828         start_user_session (display, session, language);
829     else
830     {
831         g_warning ("Ignoring request for login with unauthenticated user");
832         return FALSE;
833     }
834
835     /* Stop session, waiting for user session to indicate it is ready (if supported) */
836     // FIXME: Hard-coded timeout
837     if (display->priv->supports_transitions)
838         display->priv->user_session_timer = g_timeout_add (USER_SESSION_TIMEOUT, (GSourceFunc) session_timeout_cb, display);
839     else
840         quit_greeter (display);
841
842     return TRUE;
843 }
844
845 static void
846 xserver_exit_cb (XServer *server, Display *display)
847 {
848     g_object_unref (display->priv->xserver);
849     display->priv->xserver = NULL;
850     g_signal_emit (display, signals[EXITED], 0);
851 }
852
853 static void
854 xserver_ready_cb (XServer *xserver, Display *display)
855 {
856     /* Don't run any sessions on local terminals */
857     if (xserver_get_server_type (xserver) == XSERVER_TYPE_LOCAL_TERMINAL)
858         return;
859
860     /* If have user then automatically login the first time */
861     if (display->priv->default_user && display->priv->timeout == 0 && display->priv->login_count == 0)
862         start_default_session (display, display->priv->default_session, NULL);
863     else
864         start_greeter (display);
865 }
866
867 gboolean
868 display_start (Display *display)
869 {
870     g_return_val_if_fail (display->priv->xserver != NULL, FALSE);
871     g_signal_connect (G_OBJECT (display->priv->xserver), "ready", G_CALLBACK (xserver_ready_cb), display);
872     g_signal_connect (G_OBJECT (display->priv->xserver), "exited", G_CALLBACK (xserver_exit_cb), display);
873     return xserver_start (display->priv->xserver);
874 }
875
876 static void
877 display_init (Display *display)
878 {
879     display->priv = G_TYPE_INSTANCE_GET_PRIVATE (display, DISPLAY_TYPE, DisplayPrivate);
880     display->priv->greeter_user = g_strdup (GREETER_USER);
881     display->priv->greeter_theme = g_strdup (GREETER_THEME);
882     display->priv->default_language = getenv ("LANG") ? getenv ("LANG") : g_strdup ("C");
883     display->priv->default_layout = g_strdup ("us"); // FIXME: Is there a better default to get?
884     display->priv->default_session = g_strdup (DEFAULT_SESSION);
885 }
886
887 static void
888 display_finalize (GObject *object)
889 {
890     Display *self;
891
892     self = DISPLAY (object);
893
894     if (self->priv->greeter_session)
895         g_object_unref (self->priv->greeter_session);
896     if (self->priv->user_session_timer)
897         g_source_remove (self->priv->user_session_timer);
898     if (self->priv->user_session)
899         g_object_unref (self->priv->user_session);
900     if (self->priv->user_pam_session)
901         g_object_unref (self->priv->user_pam_session);
902 #ifdef HAVE_CONSOLE_KIT
903     end_ck_session (self->priv->greeter_ck_session);
904     end_ck_session (self->priv->user_ck_session);
905 #endif
906     if (self->priv->xserver)  
907         g_object_unref (self->priv->xserver);
908     g_free (self->priv->greeter_user);
909     g_free (self->priv->greeter_theme);
910     g_free (self->priv->default_user);
911     g_free (self->priv->default_language);
912     g_free (self->priv->default_layout);
913     g_free (self->priv->default_session);
914 }
915
916 static void
917 display_class_init (DisplayClass *klass)
918 {
919     GObjectClass *object_class = G_OBJECT_CLASS (klass);
920
921     object_class->finalize = display_finalize;
922
923     g_type_class_add_private (klass, sizeof (DisplayPrivate));
924
925     signals[START_GREETER] =
926         g_signal_new ("start-greeter",
927                       G_TYPE_FROM_CLASS (klass),
928                       G_SIGNAL_RUN_LAST,
929                       G_STRUCT_OFFSET (DisplayClass, start_greeter),
930                       NULL, NULL,
931                       g_cclosure_marshal_VOID__OBJECT,
932                       G_TYPE_NONE, 1, SESSION_TYPE);
933
934     signals[END_GREETER] =
935         g_signal_new ("end-greeter",
936                       G_TYPE_FROM_CLASS (klass),
937                       G_SIGNAL_RUN_LAST,
938                       G_STRUCT_OFFSET (DisplayClass, end_greeter),
939                       NULL, NULL,
940                       g_cclosure_marshal_VOID__OBJECT,
941                       G_TYPE_NONE, 1, SESSION_TYPE);
942   
943     signals[START_SESSION] =
944         g_signal_new ("start-session",
945                       G_TYPE_FROM_CLASS (klass),
946                       G_SIGNAL_RUN_LAST,
947                       G_STRUCT_OFFSET (DisplayClass, start_session),
948                       NULL, NULL,
949                       g_cclosure_marshal_VOID__OBJECT,
950                       G_TYPE_NONE, 1, SESSION_TYPE);
951
952     signals[END_SESSION] =
953         g_signal_new ("end-session",
954                       G_TYPE_FROM_CLASS (klass),
955                       G_SIGNAL_RUN_LAST,
956                       G_STRUCT_OFFSET (DisplayClass, end_session),
957                       NULL, NULL,
958                       g_cclosure_marshal_VOID__OBJECT,
959                       G_TYPE_NONE, 1, SESSION_TYPE);
960
961     signals[EXITED] =
962         g_signal_new ("exited",
963                       G_TYPE_FROM_CLASS (klass),
964                       G_SIGNAL_RUN_LAST,
965                       G_STRUCT_OFFSET (DisplayClass, exited),
966                       NULL, NULL,
967                       g_cclosure_marshal_VOID__VOID,
968                       G_TYPE_NONE, 0);
969
970     signals[QUIT_GREETER] =
971         g_signal_new ("quit_greeter",
972                       G_TYPE_FROM_CLASS (klass),
973                       G_SIGNAL_RUN_LAST,
974                       G_STRUCT_OFFSET (DisplayClass, quit_greeter),
975                       NULL, NULL,
976                       g_cclosure_marshal_VOID__VOID,
977                       G_TYPE_NONE, 0);
978
979     dbus_g_object_type_install_info (DISPLAY_TYPE, &dbus_glib_display_object_info);
980 }