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