]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - liblightdm-gobject/greeter.c
queue up responses in greeter and send them at once to lightdm daemon
[sojka/lightdm.git] / liblightdm-gobject / greeter.c
1 /*
2  * Copyright (C) 2010 Robert Ancell.
3  * Author: Robert Ancell <robert.ancell@canonical.com>
4  *
5  * This library is free software; you can redistribute it and/or modify it under
6  * the terms of the GNU Lesser General Public License as published by the Free
7  * Software Foundation; either version 3 of the License, or (at your option) any
8  * later version. See http://www.gnu.org/copyleft/lgpl.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 <security/pam_appl.h>
17
18 #include "lightdm/greeter.h"
19
20 enum {
21     PROP_0,
22     PROP_DEFAULT_SESSION_HINT,
23     PROP_HIDE_USERS_HINT,
24     PROP_SHOW_MANUAL_LOGIN_HINT,
25     PROP_LOCK_HINT,
26     PROP_HAS_GUEST_ACCOUNT_HINT,
27     PROP_SELECT_USER_HINT,
28     PROP_SELECT_GUEST_HINT,
29     PROP_AUTOLOGIN_USER_HINT,
30     PROP_AUTOLOGIN_GUEST_HINT,
31     PROP_AUTOLOGIN_TIMEOUT_HINT,
32     PROP_AUTHENTICATION_USER,
33     PROP_IN_AUTHENTICATION,
34     PROP_IS_AUTHENTICATED,
35 };
36
37 enum {
38     SHOW_PROMPT,
39     SHOW_MESSAGE,
40     AUTHENTICATION_COMPLETE,
41     AUTOLOGIN_TIMER_EXPIRED,
42     LAST_SIGNAL
43 };
44 static guint signals[LAST_SIGNAL] = { 0 };
45
46 typedef struct
47 {
48     gboolean connected;
49
50     GIOChannel *to_server_channel, *from_server_channel;
51     guint8 *read_buffer;
52     gsize n_read;
53
54     gsize n_responses_waiting;
55     GList *responses_received;
56
57     GHashTable *hints;
58     guint autologin_timeout;
59
60     gchar *authentication_user;
61     gboolean in_authentication;
62     gboolean is_authenticated;
63     guint32 authenticate_sequence_number;
64     gboolean cancelling_authentication;
65 } LightDMGreeterPrivate;
66
67 G_DEFINE_TYPE (LightDMGreeter, lightdm_greeter, G_TYPE_OBJECT);
68
69 #define GET_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_GREETER, LightDMGreeterPrivate)
70
71 #define HEADER_SIZE 8
72 #define MAX_MESSAGE_LENGTH 1024
73
74 /* Messages from the greeter to the server */
75 typedef enum
76 {
77     GREETER_MESSAGE_CONNECT = 0,
78     GREETER_MESSAGE_AUTHENTICATE,
79     GREETER_MESSAGE_AUTHENTICATE_AS_GUEST,
80     GREETER_MESSAGE_CONTINUE_AUTHENTICATION,
81     GREETER_MESSAGE_START_SESSION,
82     GREETER_MESSAGE_CANCEL_AUTHENTICATION,
83     GREETER_MESSAGE_SET_LANGUAGE
84 } GreeterMessage;
85
86 /* Messages from the server to the greeter */
87 typedef enum
88 {
89     SERVER_MESSAGE_CONNECTED = 0,
90     SERVER_MESSAGE_PROMPT_AUTHENTICATION,
91     SERVER_MESSAGE_END_AUTHENTICATION,
92     SERVER_MESSAGE_SESSION_RESULT
93 } ServerMessage;
94
95 /**
96  * lightdm_greeter_new:
97  *
98  * Create a new greeter.
99  *
100  * Return value: the new #LightDMGreeter
101  **/
102 LightDMGreeter *
103 lightdm_greeter_new ()
104 {
105     return g_object_new (LIGHTDM_TYPE_GREETER, NULL);
106 }
107
108 static gboolean
109 timed_login_cb (gpointer data)
110 {
111     LightDMGreeter *greeter = data;
112     LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
113
114     priv->autologin_timeout = 0;
115     g_signal_emit (G_OBJECT (greeter), signals[AUTOLOGIN_TIMER_EXPIRED], 0);
116
117     return FALSE;
118 }
119
120 static guint32
121 int_length ()
122 {
123     return 4;
124 }
125
126 static void
127 write_int (guint8 *buffer, gint buffer_length, guint32 value, gsize *offset)
128 {
129     if (*offset + 4 >= buffer_length)
130         return;
131     buffer[*offset] = value >> 24;
132     buffer[*offset+1] = (value >> 16) & 0xFF;
133     buffer[*offset+2] = (value >> 8) & 0xFF;
134     buffer[*offset+3] = value & 0xFF;
135     *offset += 4;
136 }
137
138 static void
139 write_string (guint8 *buffer, gint buffer_length, const gchar *value, gsize *offset)
140 {
141     gint length = 0;
142
143     if (value)
144         length = strlen (value);
145     write_int (buffer, buffer_length, length, offset);
146     if (*offset + length >= buffer_length)
147         return;
148     memcpy (buffer + *offset, value, length);
149     *offset += length;
150 }
151
152 static guint32
153 read_int (guint8 *message, gsize message_length, gsize *offset)
154 {
155     guint32 value;
156     guint8 *buffer;
157
158     if (message_length - *offset < int_length ())
159     {
160         g_warning ("Not enough space for int, need %i, got %zi", int_length (), message_length - *offset);
161         return 0;
162     }
163
164     buffer = message + *offset;
165     value = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
166     *offset += int_length ();
167
168     return value;
169 }
170
171 static gchar *
172 read_string (guint8 *message, gsize message_length, gsize *offset)
173 {
174     guint32 length;
175     gchar *value;
176
177     length = read_int (message, message_length, offset);
178     if (message_length - *offset < length)
179     {
180         g_warning ("Not enough space for string, need %u, got %zu", length, message_length - *offset);
181         return g_strdup ("");
182     }
183
184     value = g_malloc (sizeof (gchar) * (length + 1));
185     memcpy (value, message + *offset, length);
186     value[length] = '\0';
187     *offset += length;
188
189     return value;
190 }
191
192 static guint32
193 string_length (const gchar *value)
194 {
195     if (value)
196         return int_length () + strlen (value);
197     else
198         return int_length ();
199 }
200
201 static void
202 write_header (guint8 *buffer, gint buffer_length, guint32 id, guint32 length, gsize *offset)
203 {
204     write_int (buffer, buffer_length, id, offset);
205     write_int (buffer, buffer_length, length, offset);
206 }
207
208 static guint32
209 get_message_length (guint8 *message, gsize message_length)
210 {
211     gsize offset = 4;
212     return read_int (message, message_length, &offset);
213 }
214
215 static void
216 write_message (LightDMGreeter *greeter, guint8 *message, gsize message_length)
217 {
218     LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
219     GIOStatus status;
220     GError *error = NULL;
221     guint32 stated_length;
222
223     /* Double check that we're sending well-formed messages.  If we say we're
224        sending more than we do, we end up DOS'ing lightdm as it waits for the
225        rest.  If we say we're sending less than we do, we confuse the heck out
226        of lightdm, as it starts reading headers from the middle of our
227        messages. */
228     stated_length = HEADER_SIZE + get_message_length (message, message_length);
229     if (stated_length != message_length)
230     {
231         g_warning ("Refusing to write malformed packet to daemon: declared size is %u, but actual size is %zu", stated_length, message_length);
232         return;
233     }
234
235     status = g_io_channel_write_chars (priv->to_server_channel, (gchar *) message, message_length, NULL, &error);
236     if (error)
237         g_warning ("Error writing to daemon: %s", error->message);
238     g_clear_error (&error);
239     if (status == G_IO_STATUS_NORMAL)
240         g_debug ("Wrote %zi bytes to daemon", message_length);
241     g_io_channel_flush (priv->to_server_channel, NULL);
242 }
243
244 static void
245 handle_connected (LightDMGreeter *greeter, guint8 *message, gsize message_length, gsize *offset)
246 {
247     LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
248     gchar *version;
249     GString *hint_string;
250     int timeout;
251
252     version = read_string (message, message_length, offset);
253     hint_string = g_string_new ("");
254     while (*offset < message_length)
255     {
256         gchar *name, *value;
257       
258         name = read_string (message, message_length, offset);
259         value = read_string (message, message_length, offset);
260         g_hash_table_insert (priv->hints, name, value);
261         g_string_append_printf (hint_string, " %s=%s", name, value);
262     }
263
264     g_debug ("Connected version=%s%s", version, hint_string->str);
265     g_free (version);
266     g_string_free (hint_string, TRUE);
267
268     /* Set timeout for default login */
269     timeout = lightdm_greeter_get_autologin_timeout_hint (greeter);
270     if (timeout)
271     {
272         g_debug ("Setting autologin timer for %d seconds", timeout);
273         priv->autologin_timeout = g_timeout_add (timeout * 1000, timed_login_cb, greeter);
274     }
275 }
276
277 static void
278 handle_prompt_authentication (LightDMGreeter *greeter, guint8 *message, gsize message_length, gsize *offset)
279 {
280     LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
281     guint32 sequence_number, n_messages, i;
282     gchar *username;
283
284     sequence_number = read_int (message, message_length, offset);
285     if (sequence_number != priv->authenticate_sequence_number)
286     {
287         g_debug ("Ignoring prompt authentication with invalid sequence number %d", sequence_number);
288         return;
289     }
290
291     if (priv->cancelling_authentication)
292     {
293         g_debug ("Ignoring prompt authentication as waiting for it to cancel");
294         return;
295     }
296
297     /* Update username */
298     username = read_string (message, message_length, offset);
299     if (strcmp (username, "") == 0)
300     {
301         g_free (username);
302         username = NULL;
303     }
304     g_free (priv->authentication_user);
305     priv->authentication_user = username;
306
307     g_list_free_full (priv->responses_received, g_free);
308     priv->responses_received = NULL;
309     priv->n_responses_waiting = 0;
310
311     n_messages = read_int (message, message_length, offset);
312     g_debug ("Prompt user with %d message(s)", n_messages);
313
314     for (i = 0; i < n_messages; i++)
315     {
316         int style;
317         gchar *text;
318
319         style = read_int (message, message_length, offset);
320         text = read_string (message, message_length, offset);
321
322         // FIXME: Should stop on prompts?
323         switch (style)
324         {
325         case PAM_PROMPT_ECHO_OFF:
326             priv->n_responses_waiting++;
327             g_signal_emit (G_OBJECT (greeter), signals[SHOW_PROMPT], 0, text, LIGHTDM_PROMPT_TYPE_SECRET);
328             break;
329         case PAM_PROMPT_ECHO_ON:
330             priv->n_responses_waiting++;
331             g_signal_emit (G_OBJECT (greeter), signals[SHOW_PROMPT], 0, text, LIGHTDM_PROMPT_TYPE_QUESTION);
332             break;
333         case PAM_ERROR_MSG:
334             g_signal_emit (G_OBJECT (greeter), signals[SHOW_MESSAGE], 0, text, LIGHTDM_MESSAGE_TYPE_ERROR);
335             break;
336         case PAM_TEXT_INFO:
337             g_signal_emit (G_OBJECT (greeter), signals[SHOW_MESSAGE], 0, text, LIGHTDM_MESSAGE_TYPE_INFO);
338             break;
339         }
340
341         g_free (text);
342     }
343 }
344
345 static void
346 handle_end_authentication (LightDMGreeter *greeter, guint8 *message, gsize message_length, gsize *offset)
347 {
348     LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
349     guint32 sequence_number, return_code;
350     gchar *username;
351
352     sequence_number = read_int (message, message_length, offset);
353
354     if (sequence_number != priv->authenticate_sequence_number)
355     {
356         g_debug ("Ignoring end authentication with invalid sequence number %d", sequence_number);
357         return;
358     }
359
360     username = read_string (message, message_length, offset);
361     return_code = read_int (message, message_length, offset);
362
363     g_debug ("Authentication complete for user %s with return code %d", username, return_code);
364
365     /* Update username */
366     if (strcmp (username, "") == 0)
367     {
368         g_free (username);
369         username = NULL;
370     }
371     g_free (priv->authentication_user);
372     priv->authentication_user = username;
373
374     priv->cancelling_authentication = FALSE;
375     priv->is_authenticated = (return_code == 0);
376
377     priv->in_authentication = FALSE;
378     g_signal_emit (G_OBJECT (greeter), signals[AUTHENTICATION_COMPLETE], 0);
379 }
380
381 static guint8 *
382 read_message (LightDMGreeter *greeter, gsize *length, gboolean block)
383 {
384     LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
385     gsize n_to_read, n_read;
386     guint8 *buffer;
387     GError *error = NULL;
388
389     /* Read the header, or the whole message if we already have that */
390     n_to_read = HEADER_SIZE;
391     if (priv->n_read >= HEADER_SIZE)
392         n_to_read += get_message_length (priv->read_buffer, priv->n_read);
393
394     do
395     {
396         GIOStatus status;
397         status = g_io_channel_read_chars (priv->from_server_channel,
398                                           (gchar *) priv->read_buffer + priv->n_read,
399                                           n_to_read - priv->n_read,
400                                           &n_read,
401                                           &error);
402         if (error)
403             g_warning ("Error reading from server: %s", error->message);
404         g_clear_error (&error);
405         if (status != G_IO_STATUS_NORMAL)
406             break;
407
408         g_debug ("Read %zi bytes from daemon", n_read);
409
410         priv->n_read += n_read;
411     } while (priv->n_read < n_to_read && block);
412
413     /* Stop if haven't got all the data we want */
414     if (priv->n_read != n_to_read)
415         return FALSE;
416
417     /* If have header, rerun for content */
418     if (priv->n_read == HEADER_SIZE)
419     {
420         n_to_read = get_message_length (priv->read_buffer, priv->n_read);
421         if (n_to_read > 0)
422         {
423             priv->read_buffer = g_realloc (priv->read_buffer, HEADER_SIZE + n_to_read);
424             return read_message (greeter, length, block);
425         }
426     }
427
428     buffer = priv->read_buffer;
429     *length = priv->n_read;
430
431     priv->read_buffer = g_malloc (priv->n_read);
432     priv->n_read = 0;
433
434     return buffer;
435 }
436
437 static gboolean
438 from_server_cb (GIOChannel *source, GIOCondition condition, gpointer data)
439 {
440     LightDMGreeter *greeter = data;
441     guint8 *message;
442     gsize message_length, offset;
443     guint32 id;
444
445     message = read_message (greeter, &message_length, FALSE);
446     if (!message)
447         return TRUE;
448
449     offset = 0;
450     id = read_int (message, message_length, &offset);
451     read_int (message, message_length, &offset);
452     switch (id)
453     {
454     case SERVER_MESSAGE_PROMPT_AUTHENTICATION:
455         handle_prompt_authentication (greeter, message, message_length, &offset);
456         break;
457     case SERVER_MESSAGE_END_AUTHENTICATION:
458         handle_end_authentication (greeter, message, message_length, &offset);
459         break;
460     default:
461         g_warning ("Unknown message from server: %d", id);
462         break;
463     }
464     g_free (message);
465
466     return TRUE;
467 }
468
469 /**
470  * lightdm_greeter_connect_sync:
471  * @greeter: The greeter to connect
472  * @error: return location for a #GError, or %NULL
473  *
474  * Connects the greeter to the display manager.  Will block until connected.
475  *
476  * Return value: #TRUE if successfully connected
477  **/
478 gboolean
479 lightdm_greeter_connect_sync (LightDMGreeter *greeter, GError **error)
480 {
481     LightDMGreeterPrivate *priv;
482     const gchar *fd;
483     guint8 message[MAX_MESSAGE_LENGTH];
484     guint8 *response;
485     gsize response_length, offset = 0;
486     guint32 id;
487
488     g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
489
490     priv = GET_PRIVATE (greeter);
491
492     fd = g_getenv ("LIGHTDM_TO_SERVER_FD");
493     if (!fd)
494     {
495         g_warning ("No LIGHTDM_TO_SERVER_FD environment variable");
496         return FALSE;
497     }
498     priv->to_server_channel = g_io_channel_unix_new (atoi (fd));
499     g_io_channel_set_encoding (priv->to_server_channel, NULL, NULL);
500
501     fd = g_getenv ("LIGHTDM_FROM_SERVER_FD");
502     if (!fd)
503     {
504         g_warning ("No LIGHTDM_FROM_SERVER_FD environment variable");
505         return FALSE;
506     }
507     priv->from_server_channel = g_io_channel_unix_new (atoi (fd));
508     g_io_channel_set_encoding (priv->from_server_channel, NULL, NULL);
509     g_io_add_watch (priv->from_server_channel, G_IO_IN, from_server_cb, greeter);
510
511     g_debug ("Connecting to display manager...");
512     write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_CONNECT, string_length (VERSION), &offset);
513     write_string (message, MAX_MESSAGE_LENGTH, VERSION, &offset);
514     write_message (greeter, message, offset);
515
516     response = read_message (greeter, &response_length, TRUE);
517     if (!response)
518         return FALSE;
519
520     offset = 0;
521     id = read_int (response, response_length, &offset);
522     read_int (response, response_length, &offset);
523     if (id == SERVER_MESSAGE_CONNECTED)
524         handle_connected (greeter, response, response_length, &offset);
525     g_free (response);
526     if (id != SERVER_MESSAGE_CONNECTED)
527     {
528         g_warning ("Expected CONNECTED message, got %d", id);
529         return FALSE;
530     }
531
532     priv->connected = TRUE;
533
534     return TRUE;
535 }
536
537 /**
538  * lightdm_greeter_get_hint:
539  * @greeter: A #LightDMGreeter
540  * @name: The hint name to query.
541  *
542  * Get a hint.
543  *
544  * Return value: The value for this hint or #NULL if not set.
545  **/
546 const gchar *
547 lightdm_greeter_get_hint (LightDMGreeter *greeter, const gchar *name)
548 {
549     g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), NULL);
550     return g_hash_table_lookup (GET_PRIVATE (greeter)->hints, name);
551 }
552
553 /**
554  * lightdm_greeter_get_default_session_hint:
555  * @greeter: A #LightDMGreeter
556  *
557  * Get the default session to use.
558  *
559  * Return value: The session name
560  **/
561 const gchar *
562 lightdm_greeter_get_default_session_hint (LightDMGreeter *greeter)
563 {
564     g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), NULL);
565     return lightdm_greeter_get_hint (greeter, "default-session");
566 }
567
568 /**
569  * lightdm_greeter_get_hide_users_hint:
570  * @greeter: A #LightDMGreeter
571  *
572  * Check if user accounts should be shown.  If this is TRUE then the list of
573  * accounts should be taken from #LightDMUserList and displayed in the greeter
574  * for the user to choose from.  Note that this list can be empty and it is
575  * recommended you show a method for the user to enter a username manually.
576  * 
577  * If this option is shown the greeter should only allow these users to be
578  * chosen for login unless the manual login hint is set.
579  *
580  * Return value: #TRUE if the available users should not be shown.
581  */
582 gboolean
583 lightdm_greeter_get_hide_users_hint (LightDMGreeter *greeter)
584 {
585     const gchar *value;
586
587     g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
588     value = lightdm_greeter_get_hint (greeter, "hide-users");
589
590     return g_strcmp0 (value, "true") == 0;
591 }
592
593 /**
594  * lightdm_greeter_get_show_manual_login_hint:
595  * @greeter: A #LightDMGreeter
596  *
597  * Check if a manual login option should be shown.  If set the GUI
598  * should provide a way for a username to be entered manually.
599  * Without this hint a greeter which is showing a user list can
600  * limit logins to only those users.
601  *
602  * Return value: #TRUE if a manual login option should be shown.
603  */
604 gboolean
605 lightdm_greeter_get_show_manual_login_hint (LightDMGreeter *greeter)
606 {
607     const gchar *value;
608
609     g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
610     value = lightdm_greeter_get_hint (greeter, "show-manual-login");
611
612     return g_strcmp0 (value, "true") == 0;
613 }
614
615 /**
616  * lightdm_greeter_get_lock_hint:
617  * @greeter: A #LightDMGreeter
618  *
619  * Check if the greeter is acting as a lock screen.
620  *
621  * Return value: #TRUE if the greeter was triggered by locking the seat.
622  */
623 gboolean
624 lightdm_greeter_get_lock_hint (LightDMGreeter *greeter)
625 {
626     const gchar *value;
627
628     g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
629     value = lightdm_greeter_get_hint (greeter, "lock-screen");
630
631     return g_strcmp0 (value, "true") == 0;
632 }
633
634 /**
635  * lightdm_greeter_get_has_guest_account_hint:
636  * @greeter: A #LightDMGreeter
637  *
638  * Check if guest sessions are supported.
639  *
640  * Return value: #TRUE if guest sessions are supported.
641  */
642 gboolean
643 lightdm_greeter_get_has_guest_account_hint (LightDMGreeter *greeter)
644 {
645     const gchar *value;
646
647     g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
648     value = lightdm_greeter_get_hint (greeter, "has-guest-account");
649   
650     return g_strcmp0 (value, "true") == 0;
651 }
652
653 /**
654  * lightdm_greeter_get_select_user_hint:
655  * @greeter: A #LightDMGreeter
656  *
657  * Get the user to select by default.
658  *
659  * Return value: A username
660  */
661 const gchar *
662 lightdm_greeter_get_select_user_hint (LightDMGreeter *greeter)
663 {
664     g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), NULL);
665     return lightdm_greeter_get_hint (greeter, "select-user");
666 }
667
668 /**
669  * lightdm_greeter_get_select_guest_hint:
670  * @greeter: A #LightDMGreeter
671  *
672  * Check if the guest account should be selected by default.
673  *
674  * Return value: #TRUE if the guest account should be selected by default.
675  */
676 gboolean
677 lightdm_greeter_get_select_guest_hint (LightDMGreeter *greeter)
678 {
679     const gchar *value;
680
681     g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
682     value = lightdm_greeter_get_hint (greeter, "select-guest");
683   
684     return g_strcmp0 (value, "true") == 0;
685 }
686
687 /**
688  * lightdm_greeter_get_autologin_user_hint:
689  * @greeter: A #LightDMGreeter
690  *
691  * Get the user account to automatically logg into when the timer expires.
692  *
693  * Return value: The user account to automatically log into.
694  */
695 const gchar *
696 lightdm_greeter_get_autologin_user_hint (LightDMGreeter *greeter)
697 {
698     g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), NULL);
699     return lightdm_greeter_get_hint (greeter, "autologin-user");
700 }
701
702 /**
703  * lightdm_greeter_get_autologin_guest_hint:
704  * @greeter: A #LightDMGreeter
705  *
706  * Check if the guest account should be automatically logged into when the timer expires.
707  *
708  * Return value: #TRUE if the guest account should be automatically logged into.
709  */
710 gboolean
711 lightdm_greeter_get_autologin_guest_hint (LightDMGreeter *greeter)
712 {
713     const gchar *value;
714
715     g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
716     value = lightdm_greeter_get_hint (greeter, "autologin-guest");
717   
718     return g_strcmp0 (value, "true") == 0;
719 }
720
721 /**
722  * lightdm_greeter_get_autologin_timeout_hint:
723  * @greeter: A #LightDMGreeter
724  *
725  * Get the number of seconds to wait before automaitcally logging in.
726  *
727  * Return value: The number of seconds to wait before automatically logging in or 0 for no timeout.
728  */
729 gint
730 lightdm_greeter_get_autologin_timeout_hint (LightDMGreeter *greeter)
731 {
732     const gchar *value;
733     gint timeout = 0;
734
735     g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
736     value = lightdm_greeter_get_hint (greeter, "autologin-timeout");
737     if (value)
738         timeout = atoi (value);
739     if (timeout < 0)
740         timeout = 0;
741
742     return timeout;
743 }
744
745 /**
746  * lightdm_greeter_cancel_autologin:
747  * @greeter: A #LightDMGreeter
748  *
749  * Cancel the automatic login.
750  */
751 void
752 lightdm_greeter_cancel_autologin (LightDMGreeter *greeter)
753 {
754     LightDMGreeterPrivate *priv;
755
756     g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
757
758     priv = GET_PRIVATE (greeter);
759
760     if (priv->autologin_timeout)
761        g_source_remove (priv->autologin_timeout);
762     priv->autologin_timeout = 0;
763 }
764
765 /**
766  * lightdm_greeter_authenticate:
767  * @greeter: A #LightDMGreeter
768  * @username: (allow-none): A username or #NULL to prompt for a username.
769  *
770  * Starts the authentication procedure for a user.
771  **/
772 void
773 lightdm_greeter_authenticate (LightDMGreeter *greeter, const char *username)
774 {
775     LightDMGreeterPrivate *priv;
776     guint8 message[MAX_MESSAGE_LENGTH];
777     gsize offset = 0;
778
779     g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
780
781     priv = GET_PRIVATE (greeter);
782
783     g_return_if_fail (priv->connected);
784
785     priv->cancelling_authentication = FALSE;
786     priv->authenticate_sequence_number++;
787     priv->in_authentication = TRUE;  
788     priv->is_authenticated = FALSE;
789     if (username != priv->authentication_user)
790     {
791         g_free (priv->authentication_user);
792         priv->authentication_user = g_strdup (username);
793     }
794
795     g_debug ("Starting authentication for user %s...", username);
796     write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_AUTHENTICATE, int_length () + string_length (username), &offset);
797     write_int (message, MAX_MESSAGE_LENGTH, priv->authenticate_sequence_number, &offset);
798     write_string (message, MAX_MESSAGE_LENGTH, username, &offset);
799     write_message (greeter, message, offset);
800 }
801
802 /**
803  * lightdm_greeter_authenticate_as_guest:
804  * @greeter: A #LightDMGreeter
805  *
806  * Starts the authentication procedure for the guest user.
807  **/
808 void
809 lightdm_greeter_authenticate_as_guest (LightDMGreeter *greeter)
810 {
811     LightDMGreeterPrivate *priv;
812     guint8 message[MAX_MESSAGE_LENGTH];
813     gsize offset = 0;
814
815     g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
816
817     priv = GET_PRIVATE (greeter);
818
819     g_return_if_fail (priv->connected);
820
821     priv->cancelling_authentication = FALSE;
822     priv->authenticate_sequence_number++;
823     priv->in_authentication = TRUE;
824     priv->is_authenticated = FALSE;
825     g_free (priv->authentication_user);
826     priv->authentication_user = NULL;
827
828     g_debug ("Starting authentication for guest account...");
829     write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_AUTHENTICATE_AS_GUEST, int_length (), &offset);
830     write_int (message, MAX_MESSAGE_LENGTH, priv->authenticate_sequence_number, &offset);
831     write_message (greeter, message, offset);
832 }
833
834 /**
835  * lightdm_greeter_respond:
836  * @greeter: A #LightDMGreeter
837  * @response: Response to a prompt
838  *
839  * Provide response to a prompt.  May be one in a series.
840  **/
841 void
842 lightdm_greeter_respond (LightDMGreeter *greeter, const gchar *response)
843 {
844     LightDMGreeterPrivate *priv;
845     guint8 message[MAX_MESSAGE_LENGTH];
846     gsize offset = 0;
847
848     g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
849     g_return_if_fail (response != NULL);
850
851     priv = GET_PRIVATE (greeter);
852
853     g_return_if_fail (priv->connected);
854     g_return_if_fail (priv->n_responses_waiting > 0);
855
856     priv->n_responses_waiting--;
857     priv->responses_received = g_list_append (priv->responses_received, g_strdup (response));
858
859     if (priv->n_responses_waiting == 0)
860     {
861       guint32 msg_length;
862       GList *iter;
863
864       g_debug ("Providing response to display manager");
865
866       msg_length = int_length ();
867       for (iter = priv->responses_received; iter; iter = iter->next)
868       {
869           msg_length += string_length ((gchar *)iter->data);
870       }
871
872       write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_CONTINUE_AUTHENTICATION, msg_length, &offset);
873       write_int (message, MAX_MESSAGE_LENGTH, g_list_length (priv->responses_received), &offset);
874       for (iter = priv->responses_received; iter; iter = iter->next)
875       {
876           write_string (message, MAX_MESSAGE_LENGTH, (gchar *)iter->data, &offset);
877       }
878       write_message (greeter, message, offset);
879
880       g_list_free_full (priv->responses_received, g_free);
881       priv->responses_received = NULL;
882     }
883 }
884
885 /**
886  * lightdm_greeter_cancel_authentication:
887  * @greeter: A #LightDMGreeter
888  *
889  * Cancel the current user authentication.
890  **/
891 void
892 lightdm_greeter_cancel_authentication (LightDMGreeter *greeter)
893 {
894     LightDMGreeterPrivate *priv;
895     guint8 message[MAX_MESSAGE_LENGTH];
896     gsize offset = 0;
897
898     g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
899
900     priv = GET_PRIVATE (greeter);
901
902     g_return_if_fail (priv->connected);
903
904     priv->cancelling_authentication = TRUE;
905     write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_CANCEL_AUTHENTICATION, 0, &offset);
906     write_message (greeter, message, offset);
907 }
908
909 /**
910  * lightdm_greeter_get_in_authentication:
911  * @greeter: A #LightDMGreeter
912  *
913  * Checks if the greeter is in the process of authenticating.
914  *
915  * Return value: #TRUE if the greeter is authenticating a user.
916  **/
917 gboolean
918 lightdm_greeter_get_in_authentication (LightDMGreeter *greeter)
919 {
920     g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
921     return GET_PRIVATE (greeter)->in_authentication;
922 }
923
924 /**
925  * lightdm_greeter_get_is_authenticated:
926  * @greeter: A #LightDMGreeter
927  *
928  * Checks if the greeter has successfully authenticated.
929  *
930  * Return value: #TRUE if the greeter is authenticated for login.
931  **/
932 gboolean
933 lightdm_greeter_get_is_authenticated (LightDMGreeter *greeter)
934 {
935     g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
936     return GET_PRIVATE (greeter)->is_authenticated;
937 }
938
939 /**
940  * lightdm_greeter_get_authentication_user:
941  * @greeter: A #LightDMGreeter
942  *
943  * Get the user that is being authenticated.
944  *
945  * Return value: The username of the authentication user being authenticated or #NULL if no authentication in progress.
946  */
947 const gchar *
948 lightdm_greeter_get_authentication_user (LightDMGreeter *greeter)
949 {
950     g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), NULL);
951     return GET_PRIVATE (greeter)->authentication_user;
952 }
953
954 /**
955  * lightdm_greeter_set_language:
956  * @greeter: A #LightDMGreeter
957  * @language: The language to use for this user.
958  *
959  * Set the language for the currently authenticated user.
960  **/
961 void
962 lightdm_greeter_set_language (LightDMGreeter *greeter, const gchar *language)
963 {
964     LightDMGreeterPrivate *priv;
965     guint8 message[MAX_MESSAGE_LENGTH];
966     gsize offset = 0;
967
968     g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
969
970     priv = GET_PRIVATE (greeter);
971
972     g_return_if_fail (priv->connected);
973
974     write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_SET_LANGUAGE, string_length (language), &offset);
975     write_string (message, MAX_MESSAGE_LENGTH, language, &offset);
976     write_message (greeter, message, offset);
977 }
978
979 /**
980  * lightdm_greeter_start_session_sync:
981  * @greeter: A #LightDMGreeter
982  * @session: (allow-none): The session to log into or #NULL to use the default.
983  * @error: return location for a #GError, or %NULL
984  *
985  * Start a session for the authenticated user.
986  *
987  * Return value: TRUE if the session was started.
988  **/
989 gboolean
990 lightdm_greeter_start_session_sync (LightDMGreeter *greeter, const gchar *session, GError **error)
991 {
992     LightDMGreeterPrivate *priv;
993     guint8 message[MAX_MESSAGE_LENGTH];
994     guint8 *response;
995     gsize response_length, offset = 0;
996     guint32 id, return_code = 1;
997
998     g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
999
1000     priv = GET_PRIVATE (greeter);
1001
1002     g_return_val_if_fail (priv->connected, FALSE);
1003     g_return_val_if_fail (priv->is_authenticated, FALSE);
1004
1005     if (session)
1006         g_debug ("Starting session %s", session);
1007     else
1008         g_debug ("Starting default session");
1009
1010     write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_START_SESSION, string_length (session), &offset);
1011     write_string (message, MAX_MESSAGE_LENGTH, session, &offset);
1012     write_message (greeter, message, offset);
1013
1014     response = read_message (greeter, &response_length, TRUE);
1015     if (!response)
1016         return FALSE;
1017
1018     offset = 0;
1019     id = read_int (response, response_length, &offset);
1020     read_int (response, response_length, &offset);
1021     if (id == SERVER_MESSAGE_SESSION_RESULT)
1022         return_code = read_int (response, response_length, &offset);
1023     else
1024         g_warning ("Expected SESSION_RESULT message, got %d", id);
1025
1026     g_free (response);
1027
1028     return return_code == 0;
1029 }
1030
1031 static void
1032 lightdm_greeter_init (LightDMGreeter *greeter)
1033 {
1034     LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
1035
1036     priv->read_buffer = g_malloc (HEADER_SIZE);
1037     priv->hints = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1038 }
1039
1040 static void
1041 lightdm_greeter_set_property (GObject      *object,
1042                           guint         prop_id,
1043                           const GValue *value,
1044                           GParamSpec   *pspec)
1045 {
1046     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1047 }
1048
1049 static void
1050 lightdm_greeter_get_property (GObject    *object,
1051                           guint       prop_id,
1052                           GValue     *value,
1053                           GParamSpec *pspec)
1054 {
1055     LightDMGreeter *self;
1056
1057     self = LIGHTDM_GREETER (object);
1058
1059     switch (prop_id) {
1060     case PROP_DEFAULT_SESSION_HINT:
1061         g_value_set_string (value, lightdm_greeter_get_default_session_hint (self));
1062         break;
1063     case PROP_HIDE_USERS_HINT:
1064         g_value_set_boolean (value, lightdm_greeter_get_hide_users_hint (self));
1065         break;
1066     case PROP_SHOW_MANUAL_LOGIN_HINT:
1067         g_value_set_boolean (value, lightdm_greeter_get_show_manual_login_hint (self));
1068         break;
1069     case PROP_LOCK_HINT:
1070         g_value_set_boolean (value, lightdm_greeter_get_lock_hint (self));
1071         break;
1072     case PROP_HAS_GUEST_ACCOUNT_HINT:
1073         g_value_set_boolean (value, lightdm_greeter_get_has_guest_account_hint (self));
1074         break;
1075     case PROP_SELECT_USER_HINT:
1076         g_value_set_string (value, lightdm_greeter_get_select_user_hint (self));
1077         break;
1078     case PROP_SELECT_GUEST_HINT:
1079         g_value_set_boolean (value, lightdm_greeter_get_select_guest_hint (self));
1080         break;
1081     case PROP_AUTOLOGIN_USER_HINT:
1082         g_value_set_string (value, lightdm_greeter_get_autologin_user_hint (self));
1083         break;
1084     case PROP_AUTOLOGIN_GUEST_HINT:
1085         g_value_set_boolean (value, lightdm_greeter_get_autologin_guest_hint (self));
1086         break;
1087     case PROP_AUTOLOGIN_TIMEOUT_HINT:
1088         g_value_set_int (value, lightdm_greeter_get_autologin_timeout_hint (self));
1089         break;
1090     case PROP_AUTHENTICATION_USER:
1091         g_value_set_string (value, lightdm_greeter_get_authentication_user (self));
1092         break;
1093     case PROP_IN_AUTHENTICATION:
1094         g_value_set_boolean (value, lightdm_greeter_get_in_authentication (self));
1095         break;
1096     case PROP_IS_AUTHENTICATED:
1097         g_value_set_boolean (value, lightdm_greeter_get_is_authenticated (self));
1098         break;
1099     default:
1100         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1101         break;
1102     }
1103 }
1104
1105 static void
1106 marshal_VOID__STRING_INT (GClosure     *closure,
1107                           GValue       *return_value G_GNUC_UNUSED,
1108                           guint         n_param_values,
1109                           const GValue *param_values,
1110                           gpointer      invocation_hint G_GNUC_UNUSED,
1111                           gpointer      marshal_data)
1112 {
1113     typedef void (*GMarshalFunc_VOID__STRING_INT) (gpointer     data1,
1114                                                    gpointer     arg_1,
1115                                                    gint         arg_2,
1116                                                    gpointer     data2);
1117     register GMarshalFunc_VOID__STRING_INT callback;
1118     register GCClosure *cc = (GCClosure*) closure;
1119     register gpointer data1, data2;
1120
1121     g_return_if_fail (n_param_values == 3);
1122
1123     if (G_CCLOSURE_SWAP_DATA (closure))
1124     {
1125         data1 = closure->data;
1126         data2 = g_value_peek_pointer (param_values + 0);
1127     }
1128     else
1129     {
1130         data1 = g_value_peek_pointer (param_values + 0);
1131         data2 = closure->data;
1132     }
1133     callback = (GMarshalFunc_VOID__STRING_INT) (marshal_data ? marshal_data : cc->callback);
1134
1135     callback (data1,
1136               (param_values + 1)->data[0].v_pointer,
1137               (param_values + 2)->data[0].v_int,
1138               data2);
1139 }
1140
1141 static void
1142 lightdm_greeter_finalize (GObject *object)
1143 {
1144     LightDMGreeter *self = LIGHTDM_GREETER (object);
1145     LightDMGreeterPrivate *priv = GET_PRIVATE (self);
1146
1147     if (priv->to_server_channel)
1148         g_io_channel_unref (priv->to_server_channel);
1149     if (priv->from_server_channel)
1150         g_io_channel_unref (priv->from_server_channel);
1151     g_free (priv->authentication_user);
1152     g_hash_table_unref (priv->hints);
1153
1154     G_OBJECT_CLASS (lightdm_greeter_parent_class)->finalize (object);
1155 }
1156
1157 static void
1158 lightdm_greeter_class_init (LightDMGreeterClass *klass)
1159 {
1160     GObjectClass *object_class = G_OBJECT_CLASS (klass);
1161
1162     g_type_class_add_private (klass, sizeof (LightDMGreeterPrivate));
1163
1164     object_class->set_property = lightdm_greeter_set_property;
1165     object_class->get_property = lightdm_greeter_get_property;
1166     object_class->finalize = lightdm_greeter_finalize;
1167
1168     g_object_class_install_property (object_class,
1169                                      PROP_DEFAULT_SESSION_HINT,
1170                                      g_param_spec_string ("default-session-hint",
1171                                                           "default-session-hint",
1172                                                           "Default session hint",
1173                                                           NULL,
1174                                                           G_PARAM_READWRITE));
1175
1176     g_object_class_install_property (object_class,
1177                                      PROP_HIDE_USERS_HINT,
1178                                      g_param_spec_boolean ("hide-users-hint",
1179                                                            "hide-users-hint",
1180                                                            "Hide users hint",
1181                                                            FALSE,
1182                                                            G_PARAM_READABLE));
1183
1184     g_object_class_install_property (object_class,
1185                                      PROP_SHOW_MANUAL_LOGIN_HINT,
1186                                      g_param_spec_boolean ("show-manual-login-hint",
1187                                                            "show-manual-login-hint",
1188                                                            "Show manual login hint",
1189                                                            FALSE,
1190                                                            G_PARAM_READABLE));
1191
1192     g_object_class_install_property (object_class,
1193                                      PROP_LOCK_HINT,
1194                                      g_param_spec_boolean ("lock-hint",
1195                                                            "lock-hint",
1196                                                            "Lock hint",
1197                                                            FALSE,
1198                                                            G_PARAM_READABLE));
1199
1200     g_object_class_install_property (object_class,
1201                                      PROP_HAS_GUEST_ACCOUNT_HINT,
1202                                      g_param_spec_boolean ("has-guest-account-hint",
1203                                                            "has-guest-account-hint",
1204                                                            "Has guest account hint",
1205                                                            FALSE,
1206                                                            G_PARAM_READABLE));
1207
1208     g_object_class_install_property (object_class,
1209                                      PROP_SELECT_USER_HINT,
1210                                      g_param_spec_string ("select-user-hint",
1211                                                           "select-user-hint",
1212                                                           "Select user hint",
1213                                                           NULL,
1214                                                           G_PARAM_READABLE));
1215
1216     g_object_class_install_property (object_class,
1217                                      PROP_SELECT_GUEST_HINT,
1218                                      g_param_spec_boolean ("select-guest-hint",
1219                                                            "select-guest-hint",
1220                                                            "Select guest account hint",
1221                                                            FALSE,
1222                                                            G_PARAM_READABLE));
1223
1224     g_object_class_install_property (object_class,
1225                                      PROP_AUTOLOGIN_USER_HINT,
1226                                      g_param_spec_string ("autologin-user-hint",
1227                                                           "autologin-user-hint",
1228                                                           "Autologin user hint",
1229                                                           NULL,
1230                                                           G_PARAM_READABLE));
1231
1232     g_object_class_install_property (object_class,
1233                                      PROP_AUTOLOGIN_GUEST_HINT,
1234                                      g_param_spec_boolean ("autologin-guest-hint",
1235                                                            "autologin-guest-hint",
1236                                                            "Autologin guest account hint",
1237                                                            FALSE,
1238                                                            G_PARAM_READABLE));
1239
1240     g_object_class_install_property (object_class,
1241                                      PROP_AUTOLOGIN_TIMEOUT_HINT,
1242                                      g_param_spec_int ("autologin-timeout-hint",
1243                                                        "autologin-timeout-hint",
1244                                                        "Autologin timeout hint",
1245                                                        0, G_MAXINT, 0,
1246                                                        G_PARAM_READABLE));
1247
1248     g_object_class_install_property (object_class,
1249                                      PROP_AUTHENTICATION_USER,
1250                                      g_param_spec_string ("authentication-user",
1251                                                           "authentication-user",
1252                                                           "The user being authenticated",
1253                                                           NULL,
1254                                                           G_PARAM_READABLE));
1255     g_object_class_install_property (object_class,
1256                                      PROP_IN_AUTHENTICATION,
1257                                      g_param_spec_boolean ("in-authentication",
1258                                                            "in-authentication",
1259                                                            "TRUE if a user is being authenticated",
1260                                                            FALSE,
1261                                                            G_PARAM_READABLE));
1262     g_object_class_install_property (object_class,
1263                                      PROP_IS_AUTHENTICATED,
1264                                      g_param_spec_boolean ("is-authenticated",
1265                                                            "is-authenticated",
1266                                                            "TRUE if the selected user is authenticated",
1267                                                            FALSE,
1268                                                            G_PARAM_READABLE));
1269
1270     /**
1271      * LightDMGreeter::show-prompt:
1272      * @greeter: A #LightDMGreeter
1273      * @text: Prompt text
1274      * @type: Prompt type
1275      *
1276      * The ::show-prompt signal gets emitted when the greeter should show a
1277      * prompt to the user.  The given text should be displayed and an input
1278      * field for the user to provide a response.
1279      *
1280      * Call lightdm_greeter_respond() with the resultant input or
1281      * lightdm_greeter_cancel_authentication() to abort the authentication.
1282      **/
1283     signals[SHOW_PROMPT] =
1284         g_signal_new ("show-prompt",
1285                       G_TYPE_FROM_CLASS (klass),
1286                       G_SIGNAL_RUN_LAST,
1287                       G_STRUCT_OFFSET (LightDMGreeterClass, show_prompt),
1288                       NULL, NULL,
1289                       marshal_VOID__STRING_INT,
1290                       G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT);
1291
1292     /**
1293      * LightDMGreeter::show-message:
1294      * @greeter: A #LightDMGreeter
1295      * @text: Message text
1296      * @type: Message type
1297      *
1298      * The ::show-message signal gets emitted when the greeter
1299      * should show a message to the user.
1300      **/
1301     signals[SHOW_MESSAGE] =
1302         g_signal_new ("show-message",
1303                       G_TYPE_FROM_CLASS (klass),
1304                       G_SIGNAL_RUN_LAST,
1305                       G_STRUCT_OFFSET (LightDMGreeterClass, show_message),
1306                       NULL, NULL,
1307                       marshal_VOID__STRING_INT,
1308                       G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT);
1309
1310     /**
1311      * LightDMGreeter::authentication-complete:
1312      * @greeter: A #LightDMGreeter
1313      *
1314      * The ::authentication-complete signal gets emitted when the greeter
1315      * has completed authentication.
1316      *
1317      * Call lightdm_greeter_get_is_authenticated() to check if the authentication
1318      * was successful.
1319      **/
1320     signals[AUTHENTICATION_COMPLETE] =
1321         g_signal_new ("authentication-complete",
1322                       G_TYPE_FROM_CLASS (klass),
1323                       G_SIGNAL_RUN_LAST,
1324                       G_STRUCT_OFFSET (LightDMGreeterClass, authentication_complete),
1325                       NULL, NULL,
1326                       g_cclosure_marshal_VOID__VOID,
1327                       G_TYPE_NONE, 0);
1328
1329     /**
1330      * LightDMGreeter::autologin-timer-expired:
1331      * @greeter: A #LightDMGreeter
1332      *
1333      * The ::timed-login signal gets emitted when the automatic login timer has expired.
1334      * The application should then call lightdm_greeter_login().
1335      **/
1336     signals[AUTOLOGIN_TIMER_EXPIRED] =
1337         g_signal_new ("autologin-timer-expired",
1338                       G_TYPE_FROM_CLASS (klass),
1339                       G_SIGNAL_RUN_LAST,
1340                       G_STRUCT_OFFSET (LightDMGreeterClass, autologin_timer_expired),
1341                       NULL, NULL,
1342                       g_cclosure_marshal_VOID__VOID,
1343                       G_TYPE_NONE, 0);
1344 }