]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/display.c
Change language/layout/session when user selected in GTK+ greeter
[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_KEEP_COMMENTS, 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_KEEP_COMMENTS, &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
424             layout = g_key_file_get_string (dmrc_file, "Desktop", "Layout", NULL);
425
426             g_signal_connect (G_OBJECT (display->priv->user_session), "exited", G_CALLBACK (user_session_exited_cb), display);
427             g_signal_connect (G_OBJECT (display->priv->user_session), "killed", G_CALLBACK (user_session_killed_cb), display);
428             session_set_env (display->priv->user_session, "DISPLAY", xserver_get_address (display->priv->xserver));
429 #ifdef HAVE_CONSOLE_KIT
430             session_set_env (display->priv->user_session, "XDG_SESSION_COOKIE", ck_connector_get_cookie (display->priv->user_ck_session));
431 #endif
432             session_set_env (display->priv->user_session, "DESKTOP_SESSION", session); // FIXME: Apparently deprecated?
433             session_set_env (display->priv->user_session, "GDMSESSION", session); // FIXME: Not cross-desktop
434             session_set_env (display->priv->user_session, "PATH", "/usr/local/bin:/usr/bin:/bin");
435             session_set_env (display->priv->user_session, "LANG", session_language);
436             session_set_env (display->priv->user_session, "GDM_LANG", session_language); // FIXME: Not cross-desktop
437             session_set_env (display->priv->user_session, "GDM_KEYBOARD_LAYOUT", layout); // FIXME: Not cross-desktop
438
439             g_signal_emit (display, signals[START_SESSION], 0, display->priv->user_session);
440
441             session_start (display->priv->user_session);
442
443             data = g_key_file_to_data (dmrc_file, &length, NULL);
444
445             /* Update the users .dmrc */
446             if (user_info)
447             {
448                 path = g_build_filename (user_info->pw_dir, ".dmrc", NULL);
449                 g_file_set_contents (path, data, length, NULL);
450                 g_free (path);
451             }
452
453             /* Update the .dmrc cache */
454             path = g_build_filename (CACHE_DIR, "dmrc", NULL);          
455             g_mkdir_with_parents (path, 0700);
456             g_free (path);
457             filename = g_strdup_printf ("%s.dmrc", pam_session_get_username (display->priv->user_pam_session));
458             path = g_build_filename (CACHE_DIR, "dmrc", filename, NULL);
459             g_file_set_contents (path, data, length, NULL);
460             g_free (path);
461
462             g_free (data);
463             g_free (session_language); 
464             g_free (layout);         
465         }
466
467         g_free (command);
468     }
469
470     g_key_file_free (session_desktop_file);
471     g_key_file_free (dmrc_file);  
472 }
473
474 static void
475 start_default_session (Display *display, const gchar *session, const gchar *language)
476 {
477     /* Don't need to check authentication, just authorize */
478     if (display->priv->user_pam_session)
479         pam_session_end (display->priv->user_pam_session);    
480     display->priv->user_pam_session = pam_session_new (display->priv->default_user);
481     pam_session_authorize (display->priv->user_pam_session);
482
483 #ifdef HAVE_CONSOLE_KIT
484     display->priv->user_ck_session = start_ck_session (display, "", pam_session_get_username (display->priv->user_pam_session));
485 #endif
486     start_user_session (display, session, language);
487 }
488
489 static void
490 end_greeter_session (Display *display, gboolean clean_exit)
491 {  
492     gboolean greeter_connected;
493   
494     if (display->priv->greeter_quit_timeout)
495     {
496         g_source_remove (display->priv->greeter_quit_timeout);
497         display->priv->greeter_quit_timeout = 0;
498     }
499
500     g_signal_emit (display, signals[END_GREETER], 0, display->priv->greeter_session);
501
502     greeter_connected = display->priv->greeter_connected;
503
504     g_object_unref (display->priv->greeter_session);
505     display->priv->greeter_session = NULL;
506     display->priv->greeter_connected = FALSE;
507
508     pam_session_end (display->priv->greeter_pam_session);
509     g_object_unref (display->priv->greeter_pam_session);
510     display->priv->greeter_pam_session = NULL;
511
512 #ifdef HAVE_CONSOLE_KIT
513     end_ck_session (display->priv->greeter_ck_session);
514     display->priv->greeter_ck_session = NULL;
515 #endif
516
517     if (!clean_exit)
518         g_warning ("Greeter failed");
519     else if (!greeter_connected)
520         g_warning ("Greeter quit before connecting");
521     else if (!display->priv->user_session)
522         g_warning ("Greeter quit before session started");
523     else
524         return;
525
526     // FIXME: Issue with greeter, don't want to start a new one, report error to user
527 }
528
529 static void
530 greeter_session_exited_cb (Session *session, gint status, Display *display)
531 {
532     end_greeter_session (display, status == 0);
533 }
534
535 static void
536 greeter_session_killed_cb (Session *session, gint status, Display *display)
537 {
538     end_greeter_session (display, FALSE);
539 }
540
541 static void
542 start_greeter (Display *display)
543 {
544     GKeyFile *theme;
545     GError *error = NULL;
546   
547     theme = load_theme (display->priv->greeter_theme, &error);
548     if (!theme)
549         g_warning ("Failed to find theme %s: %s", display->priv->greeter_theme, error->message);
550     g_clear_error (&error);
551
552     if (theme)
553     {
554         gchar *command;
555
556         g_debug ("Starting greeter %s as user %s", display->priv->greeter_theme,
557                  display->priv->greeter_user ? display->priv->greeter_user : "<current>");
558
559         command = theme_get_command (theme);
560
561         display->priv->greeter_pam_session = pam_session_new (display->priv->greeter_user);
562         pam_session_authorize (display->priv->greeter_pam_session);
563
564 #ifdef HAVE_CONSOLE_KIT
565         display->priv->greeter_ck_session = start_ck_session (display,
566                                                               "LoginWindow",
567                                                               display->priv->greeter_user ? display->priv->greeter_user : getenv ("USER"));
568 #endif
569
570         display->priv->greeter_connected = FALSE;
571         display->priv->greeter_session = session_new (display->priv->greeter_user, command);
572         g_signal_connect (G_OBJECT (display->priv->greeter_session), "exited", G_CALLBACK (greeter_session_exited_cb), display);
573         g_signal_connect (G_OBJECT (display->priv->greeter_session), "killed", G_CALLBACK (greeter_session_killed_cb), display);
574         session_set_env (display->priv->greeter_session, "DISPLAY", xserver_get_address (display->priv->xserver));
575 #ifdef HAVE_CONSOLE_KIT
576         session_set_env (display->priv->greeter_session, "XDG_SESSION_COOKIE", ck_connector_get_cookie (display->priv->greeter_ck_session));
577 #endif
578
579         g_signal_emit (display, signals[START_GREETER], 0, display->priv->greeter_session);
580
581         session_start (display->priv->greeter_session);
582
583         g_free (command);
584         g_key_file_free (theme);
585     }
586 }
587
588 #define TYPE_MESSAGE dbus_g_type_get_struct ("GValueArray", G_TYPE_INT, G_TYPE_STRING, G_TYPE_INVALID)
589
590 static void
591 pam_messages_cb (PAMSession *session, int num_msg, const struct pam_message **msg, Display *display)
592 {
593     GPtrArray *request;
594     int i;
595     DBusGMethodInvocation *context;
596
597     /* Respond to d-bus query with messages */
598     request = g_ptr_array_new ();
599     for (i = 0; i < num_msg; i++)
600     {
601         GValue value = { 0 };
602       
603         g_value_init (&value, TYPE_MESSAGE);
604         g_value_take_boxed (&value, dbus_g_type_specialized_construct (TYPE_MESSAGE));
605         // FIXME: Need to convert to UTF-8
606         dbus_g_type_struct_set (&value, 0, msg[i]->msg_style, 1, msg[i]->msg, G_MAXUINT);
607         g_ptr_array_add (request, g_value_get_boxed (&value));
608     }
609
610     context = display->priv->dbus_context;
611     display->priv->dbus_context = NULL;
612     dbus_g_method_return (context, 0, request);
613 }
614
615 static void
616 authenticate_result_cb (PAMSession *session, int result, Display *display)
617 {
618     GPtrArray *request;
619     DBusGMethodInvocation *context;
620
621     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));
622
623     /* Respond to D-Bus request */
624     request = g_ptr_array_new ();
625     context = display->priv->dbus_context;
626     display->priv->dbus_context = NULL;
627
628     dbus_g_method_return (context, result, request);
629 }
630
631 static void
632 session_started_cb (PAMSession *session, Display *display)
633 {
634 #ifdef HAVE_CONSOLE_KIT
635     display->priv->user_ck_session = start_ck_session (display, "", pam_session_get_username (display->priv->user_pam_session));
636 #endif
637 }
638
639 gboolean
640 display_connect (Display *display,
641                  const gchar **theme,
642                  const gchar **language, const gchar **layout, const gchar **session,
643                  const gchar **username, gint *delay, GError *error)
644 {
645     if (!display->priv->greeter_connected)
646     {
647         display->priv->greeter_connected = TRUE;
648         g_debug ("Greeter connected");
649     }
650
651     *theme = g_build_filename (THEME_DIR, display->priv->greeter_theme, "index.theme", NULL);
652     *language = g_strdup (display->priv->default_language);
653     *layout = g_strdup (display->priv->default_layout);
654     *session = g_strdup (display->priv->default_session);
655     *username = g_strdup (display->priv->default_user);
656     *delay = display->priv->timeout;
657
658     return TRUE;
659 }
660
661 gboolean
662 display_start_authentication (Display *display, const gchar *username, DBusGMethodInvocation *context)
663 {
664     GError *error = NULL;
665
666     // FIXME: Only allow calls from the correct greeter
667
668     if (!display->priv->greeter_session || display->priv->user_session)
669     {
670         dbus_g_method_return_error (context, NULL);
671         return TRUE;
672     }
673
674     /* Abort existing authentication */
675     if (display->priv->user_pam_session)
676     {
677         g_signal_handlers_disconnect_matched (display->priv->user_pam_session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, display);
678         pam_session_end (display->priv->user_pam_session);
679         if (display->priv->dbus_context)
680             dbus_g_method_return_error (display->priv->dbus_context, NULL);
681
682         g_object_unref (display->priv->user_pam_session);
683     }
684
685     g_debug ("Greeter start authentication for %s", username);
686
687     /* Store D-Bus request to respond to */
688     display->priv->dbus_context = context;
689
690     display->priv->user_pam_session = pam_session_new (username);
691     g_signal_connect (G_OBJECT (display->priv->user_pam_session), "got-messages", G_CALLBACK (pam_messages_cb), display);
692     g_signal_connect (G_OBJECT (display->priv->user_pam_session), "authentication-result", G_CALLBACK (authenticate_result_cb), display);
693     g_signal_connect (G_OBJECT (display->priv->user_pam_session), "started", G_CALLBACK (session_started_cb), display);
694
695     if (!pam_session_start (display->priv->user_pam_session, &error))
696     {
697         g_warning ("Failed to start authentication: %s", error->message);
698         display->priv->dbus_context = NULL;
699         dbus_g_method_return_error (context, NULL);
700         return FALSE;
701     }
702     g_clear_error (&error);
703
704     return TRUE;
705 }
706
707 gboolean
708 display_continue_authentication (Display *display, gchar **secrets, DBusGMethodInvocation *context)
709 {
710     int num_messages;
711     const struct pam_message **messages;
712     struct pam_response *response;
713     int i, j, n_secrets = 0;
714
715     /* Not connected */
716     if (!display->priv->greeter_connected)
717     {
718         dbus_g_method_return_error (context, NULL);
719         return TRUE;
720     }
721
722     /* Not in authorization */
723     if (display->priv->user_pam_session == NULL)
724     {
725         dbus_g_method_return_error (context, NULL);
726         return TRUE;
727     }
728
729     /* Already in another call */
730     if (display->priv->dbus_context != NULL)
731     {
732         dbus_g_method_return_error (context, NULL);
733         return TRUE;
734     }
735
736     // FIXME: Only allow calls from the correct greeter
737
738     num_messages = pam_session_get_num_messages (display->priv->user_pam_session);
739     messages = pam_session_get_messages (display->priv->user_pam_session);
740
741     /* Check correct number of responses */
742     for (i = 0; i < num_messages; i++)
743     {
744         int msg_style = messages[i]->msg_style;
745         if (msg_style == PAM_PROMPT_ECHO_OFF || msg_style == PAM_PROMPT_ECHO_ON)
746             n_secrets++;
747     }
748     if (g_strv_length (secrets) != n_secrets)
749     {
750         pam_session_end (display->priv->user_pam_session);
751         // FIXME: Throw error
752         return FALSE;
753     }
754
755     g_debug ("Continue authentication");
756
757     /* Build response */
758     response = calloc (num_messages, sizeof (struct pam_response));  
759     for (i = 0, j = 0; i < num_messages; i++)
760     {
761         int msg_style = messages[i]->msg_style;
762         if (msg_style == PAM_PROMPT_ECHO_OFF || msg_style == PAM_PROMPT_ECHO_ON)
763         {
764             response[i].resp = strdup (secrets[j]); // FIXME: Need to convert from UTF-8
765             j++;
766         }
767     }
768
769     display->priv->dbus_context = context;
770     pam_session_respond (display->priv->user_pam_session, response);
771
772     return TRUE;
773 }
774
775 static gboolean
776 quit_greeter_cb (gpointer data)
777 {
778     Display *display = data;
779     g_warning ("Greeter did not quit, sending kill signal");
780     session_stop (display->priv->greeter_session);
781     display->priv->greeter_quit_timeout = 0;
782     return TRUE;
783 }
784
785 static void
786 quit_greeter (Display *display)
787 {
788     g_signal_emit (display, signals[QUIT_GREETER], 0);
789     if (display->priv->greeter_quit_timeout)
790         g_source_remove (display->priv->greeter_quit_timeout);
791     display->priv->greeter_quit_timeout = g_timeout_add (GREETER_QUIT_TIMEOUT, quit_greeter_cb, display);
792 }
793
794 static gboolean
795 session_timeout_cb (Display *display)
796 {
797     g_warning ("Session has not indicated it is ready, stopping greeter anyway");
798
799     /* Stop the greeter */
800     quit_greeter (display);
801
802     display->priv->user_session_timer = 0;
803     return FALSE;
804 }
805
806 gboolean
807 display_login (Display *display, gchar *username, gchar *session, gchar *language, GError *error)
808 {
809     if (display->priv->user_session != NULL)
810     {
811         g_warning ("Ignoring request to log in when already logged in");
812         return TRUE;
813     }
814
815     g_debug ("Greeter login for user %s on session %s", username, session);
816   
817     /* Default session requested */
818     if (strcmp (session, "") == 0)
819         session = display->priv->default_session;
820
821     /* Default language requested */
822     if (strcmp (language, "") == 0)
823         language = NULL;
824
825     if (display->priv->default_user && strcmp (username, display->priv->default_user) == 0)
826         start_default_session (display, session, language);
827     else if (display->priv->user_pam_session &&
828              pam_session_get_in_session (display->priv->user_pam_session) &&
829              strcmp (username, pam_session_get_username (display->priv->user_pam_session)) == 0)
830         start_user_session (display, session, language);
831     else
832     {
833         g_warning ("Ignoring request for login with unauthenticated user");
834         return FALSE;
835     }
836
837     /* Stop session, waiting for user session to indicate it is ready (if supported) */
838     // FIXME: Hard-coded timeout
839     if (display->priv->supports_transitions)
840         display->priv->user_session_timer = g_timeout_add (USER_SESSION_TIMEOUT, (GSourceFunc) session_timeout_cb, display);
841     else
842         quit_greeter (display);
843
844     return TRUE;
845 }
846
847 static void
848 xserver_exit_cb (XServer *server, Display *display)
849 {
850     g_object_unref (display->priv->xserver);
851     display->priv->xserver = NULL;
852     g_signal_emit (display, signals[EXITED], 0);
853 }
854
855 static void
856 xserver_ready_cb (XServer *xserver, Display *display)
857 {
858     /* Don't run any sessions on local terminals */
859     if (xserver_get_server_type (xserver) == XSERVER_TYPE_LOCAL_TERMINAL)
860         return;
861
862     /* If have user then automatically login the first time */
863     if (display->priv->default_user && display->priv->timeout == 0 && display->priv->login_count == 0)
864         start_default_session (display, display->priv->default_session, NULL);
865     else
866         start_greeter (display);
867 }
868
869 gboolean
870 display_start (Display *display)
871 {
872     g_return_val_if_fail (display->priv->xserver != NULL, FALSE);
873     g_signal_connect (G_OBJECT (display->priv->xserver), "ready", G_CALLBACK (xserver_ready_cb), display);
874     g_signal_connect (G_OBJECT (display->priv->xserver), "exited", G_CALLBACK (xserver_exit_cb), display);
875     return xserver_start (display->priv->xserver);
876 }
877
878 static void
879 display_init (Display *display)
880 {
881     display->priv = G_TYPE_INSTANCE_GET_PRIVATE (display, DISPLAY_TYPE, DisplayPrivate);
882     display->priv->greeter_user = g_strdup (GREETER_USER);
883     display->priv->greeter_theme = g_strdup (GREETER_THEME);
884     display->priv->default_language = getenv ("LANG") ? g_strdup (getenv ("LANG")) : g_strdup ("C");
885     display->priv->default_layout = g_strdup ("us"); // FIXME: Is there a better default to get?
886     display->priv->default_session = g_strdup (DEFAULT_SESSION);
887 }
888
889 static void
890 display_finalize (GObject *object)
891 {
892     Display *self;
893
894     self = DISPLAY (object);
895
896     if (self->priv->greeter_session)
897         g_object_unref (self->priv->greeter_session);
898     if (self->priv->user_session_timer)
899         g_source_remove (self->priv->user_session_timer);
900     if (self->priv->user_session)
901         g_object_unref (self->priv->user_session);
902     if (self->priv->user_pam_session)
903         g_object_unref (self->priv->user_pam_session);
904 #ifdef HAVE_CONSOLE_KIT
905     end_ck_session (self->priv->greeter_ck_session);
906     end_ck_session (self->priv->user_ck_session);
907 #endif
908     if (self->priv->xserver)  
909         g_object_unref (self->priv->xserver);
910     g_free (self->priv->greeter_user);
911     g_free (self->priv->greeter_theme);
912     g_free (self->priv->default_user);
913     g_free (self->priv->default_language);
914     g_free (self->priv->default_layout);
915     g_free (self->priv->default_session);
916 }
917
918 static void
919 display_class_init (DisplayClass *klass)
920 {
921     GObjectClass *object_class = G_OBJECT_CLASS (klass);
922
923     object_class->finalize = display_finalize;
924
925     g_type_class_add_private (klass, sizeof (DisplayPrivate));
926
927     signals[START_GREETER] =
928         g_signal_new ("start-greeter",
929                       G_TYPE_FROM_CLASS (klass),
930                       G_SIGNAL_RUN_LAST,
931                       G_STRUCT_OFFSET (DisplayClass, start_greeter),
932                       NULL, NULL,
933                       g_cclosure_marshal_VOID__OBJECT,
934                       G_TYPE_NONE, 1, SESSION_TYPE);
935
936     signals[END_GREETER] =
937         g_signal_new ("end-greeter",
938                       G_TYPE_FROM_CLASS (klass),
939                       G_SIGNAL_RUN_LAST,
940                       G_STRUCT_OFFSET (DisplayClass, end_greeter),
941                       NULL, NULL,
942                       g_cclosure_marshal_VOID__OBJECT,
943                       G_TYPE_NONE, 1, SESSION_TYPE);
944   
945     signals[START_SESSION] =
946         g_signal_new ("start-session",
947                       G_TYPE_FROM_CLASS (klass),
948                       G_SIGNAL_RUN_LAST,
949                       G_STRUCT_OFFSET (DisplayClass, start_session),
950                       NULL, NULL,
951                       g_cclosure_marshal_VOID__OBJECT,
952                       G_TYPE_NONE, 1, SESSION_TYPE);
953
954     signals[END_SESSION] =
955         g_signal_new ("end-session",
956                       G_TYPE_FROM_CLASS (klass),
957                       G_SIGNAL_RUN_LAST,
958                       G_STRUCT_OFFSET (DisplayClass, end_session),
959                       NULL, NULL,
960                       g_cclosure_marshal_VOID__OBJECT,
961                       G_TYPE_NONE, 1, SESSION_TYPE);
962
963     signals[EXITED] =
964         g_signal_new ("exited",
965                       G_TYPE_FROM_CLASS (klass),
966                       G_SIGNAL_RUN_LAST,
967                       G_STRUCT_OFFSET (DisplayClass, exited),
968                       NULL, NULL,
969                       g_cclosure_marshal_VOID__VOID,
970                       G_TYPE_NONE, 0);
971
972     signals[QUIT_GREETER] =
973         g_signal_new ("quit_greeter",
974                       G_TYPE_FROM_CLASS (klass),
975                       G_SIGNAL_RUN_LAST,
976                       G_STRUCT_OFFSET (DisplayClass, quit_greeter),
977                       NULL, NULL,
978                       g_cclosure_marshal_VOID__VOID,
979                       G_TYPE_NONE, 0);
980
981     dbus_g_object_type_install_info (DISPLAY_TYPE, &dbus_glib_display_object_info);
982 }