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