1 /* -*- Mode: C; indent-tabs-mode:nil; tab-width:4 -*-
2 *
3 * Copyright (C) 2010 Robert Ancell.
4 * Copyright (C) 2014 Canonical, Ltd.
5 * Authors: Robert Ancell <robert.ancell@canonical.com>
6 * Michael Terry <michael.terry@canonical.com>
7 *
8 * This library is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU Lesser General Public License as published by the Free
10 * Software Foundation; either version 2 or version 3 of the License.
11 * See http://www.gnu.org/copyleft/lgpl.html the full text of the license.
12 */
13
14 #include <config.h>
15
16 #include <errno.h>
17 #include <string.h>
18 #include <sys/utsname.h>
19 #include <pwd.h>
20 #include <gio/gio.h>
21
22 #include "dmrc.h"
23 #include "user-list.h"
24
25 enum
26 {
27 LIST_PROP_NUM_USERS = 1,
28 LIST_PROP_USERS,
29 };
30
31 enum
32 {
33 USER_PROP_NAME = 1,
34 USER_PROP_REAL_NAME,
35 USER_PROP_DISPLAY_NAME,
36 USER_PROP_HOME_DIRECTORY,
37 USER_PROP_SHELL,
38 USER_PROP_IMAGE,
39 USER_PROP_BACKGROUND,
40 USER_PROP_LANGUAGE,
41 USER_PROP_LAYOUT,
42 USER_PROP_LAYOUTS,
43 USER_PROP_SESSION,
44 USER_PROP_LOGGED_IN,
45 USER_PROP_HAS_MESSAGES,
46 USER_PROP_UID,
47 USER_PROP_GID,
48 USER_PROP_IS_LOCKED,
49 };
50
51 enum
52 {
53 USER_ADDED,
54 USER_CHANGED,
55 USER_REMOVED,
56 LAST_LIST_SIGNAL
57 };
58 static guint list_signals[LAST_LIST_SIGNAL] = { 0 };
59
60 enum
61 {
62 CHANGED,
63 GET_LOGGED_IN,
64 LAST_USER_SIGNAL
65 };
66 static guint user_signals[LAST_USER_SIGNAL] = { 0 };
67
68 typedef struct
69 {
70 /* Bus connection being communicated on */
71 GDBusConnection *bus;
72
73 /* D-Bus signals for accounts service events */
74 guint user_added_signal;
75 guint user_removed_signal;
76
77 /* D-Bus signals for display manager events */
78 guint session_added_signal;
79 guint session_removed_signal;
80
81 /* File monitor for password file */
82 GFileMonitor *passwd_monitor;
83
84 /* TRUE if have scanned users */
85 gboolean have_users;
86
87 /* List of users */
88 GList *users;
89
90 /* List of sessions */
91 GList *sessions;
92 } CommonUserListPrivate;
93
94 typedef struct
95 {
96 /* TRUE if have loaded the DMRC file */
97 gboolean loaded_dmrc;
98
99 /* Bus we are listening for accounts service on */
100 GDBusConnection *bus;
101
102 /* Accounts service path */
103 gchar *path;
104
105 /* Update signal from accounts service */
106 guint changed_signal;
107
108 /* Username */
109 gchar *name;
110
111 /* Descriptive name for user */
112 gchar *real_name;
113
114 /* Home directory of user */
115 gchar *home_directory;
116
117 /* Shell for user */
118 gchar *shell;
119
120 /* Image for user */
121 gchar *image;
122
123 /* Background image for users */
124 gchar *background;
125
126 /* TRUE if this user has messages available */
127 gboolean has_messages;
128
129 /* UID of user */
130 guint64 uid;
131
132 /* GID of user */
133 guint64 gid;
134
135 /* User chosen language */
136 gchar *language;
137
138 /* User layout preferences */
139 gchar **layouts;
140
141 /* User default session */
142 gchar *session;
143
144 /* TRUE if this user is locked */
145 gboolean is_locked;
146 } CommonUserPrivate;
147
148 typedef struct
149 {
150 GObject parent_instance;
151 gchar *path;
152 gchar *username;
153 } CommonSession;
154
155 typedef struct
156 {
157 GObjectClass parent_class;
158 } CommonSessionClass;
159
160 G_DEFINE_TYPE_WITH_PRIVATE (CommonUserList, common_user_list, G_TYPE_OBJECT)
161 G_DEFINE_TYPE_WITH_PRIVATE (CommonUser, common_user, G_TYPE_OBJECT)
162 #define COMMON_SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), common_session_get_type (), CommonSession))
163 GType common_session_get_type (void);
164 G_DEFINE_TYPE (CommonSession, common_session, G_TYPE_OBJECT)
165
166 #define GET_LIST_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), COMMON_TYPE_USER_LIST, CommonUserListPrivate)
167 #define GET_USER_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), COMMON_TYPE_USER, CommonUserPrivate)
168
169 #define PASSWD_FILE "/etc/passwd"
170 #define USER_CONFIG_FILE "/etc/lightdm/users.conf"
171
172 static CommonUserList *singleton = NULL;
173
174 /**
175 * common_user_list_get_instance:
176 *
177 * Get the user list.
178 *
179 * Return value: (transfer none): the #CommonUserList
180 **/
181 CommonUserList *
common_user_list_get_instance(void)182 common_user_list_get_instance (void)
183 {
184 if (!singleton)
185 singleton = g_object_new (COMMON_TYPE_USER_LIST, NULL);
186 return singleton;
187 }
188
189 void
common_user_list_cleanup(void)190 common_user_list_cleanup (void)
191 {
192 g_clear_object (&singleton);
193 }
194
195 static CommonUser *
get_user_by_name(CommonUserList * user_list,const gchar * username)196 get_user_by_name (CommonUserList *user_list, const gchar *username)
197 {
198 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
199
200 for (GList *link = priv->users; link; link = link->next)
201 {
202 CommonUser *user = link->data;
203 if (g_strcmp0 (common_user_get_name (user), username) == 0)
204 return user;
205 }
206
207 return NULL;
208 }
209
210 static CommonUser *
get_user_by_path(CommonUserList * user_list,const gchar * path)211 get_user_by_path (CommonUserList *user_list, const gchar *path)
212 {
213 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
214
215 for (GList *link = priv->users; link; link = link->next)
216 {
217 CommonUser *user = link->data;
218 if (g_strcmp0 (GET_USER_PRIVATE (user)->path, path) == 0)
219 return user;
220 }
221
222 return NULL;
223 }
224
225 static gint
compare_user(gconstpointer a,gconstpointer b)226 compare_user (gconstpointer a, gconstpointer b)
227 {
228 CommonUser *user_a = (CommonUser *) a, *user_b = (CommonUser *) b;
229 return g_strcmp0 (common_user_get_display_name (user_a), common_user_get_display_name (user_b));
230 }
231
232 static gboolean
update_passwd_user(CommonUser * user,const gchar * real_name,const gchar * home_directory,const gchar * shell,const gchar * image)233 update_passwd_user (CommonUser *user, const gchar *real_name, const gchar *home_directory, const gchar *shell, const gchar *image)
234 {
235 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
236
237 /* Skip if already set to this */
238 if (g_strcmp0 (common_user_get_real_name (user), real_name) == 0 &&
239 g_strcmp0 (common_user_get_home_directory (user), home_directory) == 0 &&
240 g_strcmp0 (common_user_get_shell (user), shell) == 0 &&
241 g_strcmp0 (common_user_get_image (user), image) == 0)
242 return FALSE;
243
244 g_free (priv->real_name);
245 priv->real_name = g_strdup (real_name);
246 g_free (priv->home_directory);
247 priv->home_directory = g_strdup (home_directory);
248 g_free (priv->shell);
249 priv->shell = g_strdup (shell);
250 g_free (priv->image);
251 priv->image = g_strdup (image);
252
253 return TRUE;
254 }
255
256 static void load_sessions (CommonUserList *user_list);
257
258 static gboolean
get_logged_in_cb(CommonUser * user,CommonUserList * user_list)259 get_logged_in_cb (CommonUser *user, CommonUserList *user_list)
260 {
261 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
262
263 // Lazily decide to load/listen to sessions
264 if (priv->session_added_signal == 0)
265 load_sessions (user_list);
266
267 const gchar *username = GET_USER_PRIVATE (user)->name;
268 for (GList *link = priv->sessions; link; link = link->next)
269 {
270 CommonSession *session = link->data;
271 if (strcmp (session->username, username) == 0)
272 return TRUE;
273 }
274
275 return FALSE;
276 }
277
278 static void
user_changed_cb(CommonUser * user,CommonUserList * user_list)279 user_changed_cb (CommonUser *user, CommonUserList *user_list)
280 {
281 g_signal_emit (user_list, list_signals[USER_CHANGED], 0, user);
282 }
283
284 static CommonUser *
make_passwd_user(CommonUserList * user_list,struct passwd * entry)285 make_passwd_user (CommonUserList *user_list, struct passwd *entry)
286 {
287 CommonUser *user = g_object_new (COMMON_TYPE_USER, NULL);
288 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
289
290 g_signal_connect (user, "get-logged-in", G_CALLBACK (get_logged_in_cb), user_list);
291
292 g_auto(GStrv) tokens = g_strsplit (entry->pw_gecos, ",", -1);
293 gchar *real_name;
294 if (tokens[0] != NULL && tokens[0][0] != '\0')
295 real_name = g_strdup (tokens[0]);
296 else
297 real_name = g_strdup ("");
298
299 gchar *image = g_build_filename (entry->pw_dir, ".face", NULL);
300 if (!g_file_test (image, G_FILE_TEST_EXISTS))
301 {
302 g_free (image);
303 image = g_build_filename (entry->pw_dir, ".face.icon", NULL);
304 if (!g_file_test (image, G_FILE_TEST_EXISTS))
305 {
306 g_free (image);
307 image = NULL;
308 }
309 }
310
311 priv->name = g_strdup (entry->pw_name);
312 priv->real_name = real_name;
313 priv->home_directory = g_strdup (entry->pw_dir);
314 priv->shell = g_strdup (entry->pw_shell);
315 priv->image = image;
316 priv->uid = entry->pw_uid;
317 priv->gid = entry->pw_gid;
318
319 return user;
320 }
321
322 static void
load_passwd_file(CommonUserList * user_list,gboolean emit_add_signal)323 load_passwd_file (CommonUserList *user_list, gboolean emit_add_signal)
324 {
325 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
326
327 g_debug ("Loading user config from %s", USER_CONFIG_FILE);
328
329 g_autoptr(GKeyFile) config = g_key_file_new ();
330 g_autoptr(GError) error = NULL;
331 g_key_file_load_from_file (config, USER_CONFIG_FILE, G_KEY_FILE_NONE, &error);
332 if (error && !g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
333 g_warning ("Failed to load configuration from %s: %s", USER_CONFIG_FILE, error->message);
334
335 gint minimum_uid = 500;
336 if (g_key_file_has_key (config, "UserList", "minimum-uid", NULL))
337 minimum_uid = g_key_file_get_integer (config, "UserList", "minimum-uid", NULL);
338
339 g_autofree gchar *hidden_users_list = g_key_file_get_string (config, "UserList", "hidden-users", NULL);
340 if (!hidden_users_list)
341 hidden_users_list = g_strdup ("nobody nobody4 noaccess");
342 g_auto(GStrv) hidden_users = g_strsplit (hidden_users_list, " ", -1);
343
344 g_autofree gchar *hidden_shells_list = g_key_file_get_string (config, "UserList", "hidden-shells", NULL);
345 if (!hidden_shells_list)
346 hidden_shells_list = g_strdup ("/bin/false /usr/sbin/nologin");
347 g_auto(GStrv) hidden_shells = g_strsplit (hidden_shells_list, " ", -1);
348
349 setpwent ();
350
351 GList *users = NULL, *new_users = NULL, *changed_users = NULL;
352 while (TRUE)
353 {
354 errno = 0;
355 struct passwd *entry = getpwent ();
356 if (!entry)
357 break;
358
359 /* Ignore system users */
360 if (entry->pw_uid < minimum_uid)
361 continue;
362
363 /* Ignore users disabled by shell */
364 if (entry->pw_shell)
365 {
366 int i;
367 for (i = 0; hidden_shells[i] && strcmp (entry->pw_shell, hidden_shells[i]) != 0; i++);
368 if (hidden_shells[i])
369 continue;
370 }
371
372 /* Ignore certain users */
373 int i;
374 for (i = 0; hidden_users[i] && strcmp (entry->pw_name, hidden_users[i]) != 0; i++);
375 if (hidden_users[i])
376 continue;
377
378 CommonUser *user = make_passwd_user (user_list, entry);
379
380 /* Update existing users if have them */
381 GList *link;
382 for (link = priv->users; link; link = link->next)
383 {
384 CommonUser *info = link->data;
385 if (strcmp (common_user_get_name (info), common_user_get_name (user)) == 0)
386 {
387 if (update_passwd_user (info, common_user_get_real_name (user), common_user_get_home_directory (user), common_user_get_shell (user), common_user_get_image (user)))
388 changed_users = g_list_insert_sorted (changed_users, info, compare_user);
389 g_object_unref (user);
390 user = info;
391 break;
392 }
393 }
394 if (!link)
395 {
396 /* Only notify once we have loaded the user list */
397 if (priv->have_users)
398 new_users = g_list_insert_sorted (new_users, user, compare_user);
399 }
400 users = g_list_insert_sorted (users, user, compare_user);
401 }
402
403 if (errno != 0)
404 g_warning ("Failed to read password database: %s", strerror (errno));
405
406 endpwent ();
407
408 /* Use new user list */
409 GList *old_users = priv->users;
410 priv->users = users;
411
412 /* Notify of changes */
413 for (GList *link = new_users; link; link = link->next)
414 {
415 CommonUser *info = link->data;
416 g_debug ("User %s added", common_user_get_name (info));
417 g_signal_connect (info, USER_SIGNAL_CHANGED, G_CALLBACK (user_changed_cb), user_list);
418 if (emit_add_signal)
419 g_signal_emit (user_list, list_signals[USER_ADDED], 0, info);
420 }
421 g_list_free (new_users);
422 for (GList *link = changed_users; link; link = link->next)
423 {
424 CommonUser *info = link->data;
425 g_debug ("User %s changed", common_user_get_name (info));
426 g_signal_emit (info, user_signals[CHANGED], 0);
427 }
428 g_list_free (changed_users);
429 for (GList *link = old_users; link; link = link->next)
430 {
431 /* See if this user is in the current list */
432 GList *new_link;
433 for (new_link = priv->users; new_link; new_link = new_link->next)
434 {
435 if (new_link->data == link->data)
436 break;
437 }
438
439 if (!new_link)
440 {
441 CommonUser *info = link->data;
442 g_debug ("User %s removed", common_user_get_name (info));
443 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, info);
444 g_object_unref (info);
445 }
446 }
447 g_list_free (old_users);
448 }
449
450 static void
passwd_changed_cb(GFileMonitor * monitor,GFile * file,GFile * other_file,GFileMonitorEvent event_type,CommonUserList * user_list)451 passwd_changed_cb (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, CommonUserList *user_list)
452 {
453 if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
454 {
455 g_debug ("%s changed, reloading user list", g_file_get_path (file));
456 load_passwd_file (user_list, TRUE);
457 }
458 }
459
460 static gboolean load_accounts_user (CommonUser *user);
461
462 static void
accounts_user_changed_cb(GDBusConnection * connection,const gchar * sender_name,const gchar * object_path,const gchar * interface_name,const gchar * signal_name,GVariant * parameters,gpointer data)463 accounts_user_changed_cb (GDBusConnection *connection,
464 const gchar *sender_name,
465 const gchar *object_path,
466 const gchar *interface_name,
467 const gchar *signal_name,
468 GVariant *parameters,
469 gpointer data)
470 {
471 CommonUser *user = data;
472 /*CommonUserPrivate *priv = GET_USER_PRIVATE (user);*/
473
474 /* Log message disabled as AccountsService can have arbitrary plugins that
475 * might cause us to log when properties change we don't use. LP: #1376357
476 */
477 /*g_debug ("User %s changed", priv->path);*/
478 if (load_accounts_user (user))
479 g_signal_emit (user, user_signals[CHANGED], 0);
480 }
481
482 static gboolean
load_accounts_user(CommonUser * user)483 load_accounts_user (CommonUser *user)
484 {
485 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
486
487 /* Get the properties for this user */
488 if (!priv->changed_signal)
489 priv->changed_signal = g_dbus_connection_signal_subscribe (priv->bus,
490 "org.freedesktop.Accounts",
491 "org.freedesktop.Accounts.User",
492 "Changed",
493 priv->path,
494 NULL,
495 G_DBUS_SIGNAL_FLAGS_NONE,
496 accounts_user_changed_cb,
497 user,
498 NULL);
499
500 g_autoptr(GError) error = NULL;
501 g_autoptr(GVariant) result = g_dbus_connection_call_sync (priv->bus,
502 "org.freedesktop.Accounts",
503 priv->path,
504 "org.freedesktop.DBus.Properties",
505 "GetAll",
506 g_variant_new ("(s)", "org.freedesktop.Accounts.User"),
507 G_VARIANT_TYPE ("(a{sv})"),
508 G_DBUS_CALL_FLAGS_NONE,
509 -1,
510 NULL,
511 &error);
512 if (error)
513 g_warning ("Error updating user %s: %s", priv->path, error->message);
514 if (!result)
515 return FALSE;
516
517 /* Store the properties we need */
518 g_autoptr(GVariantIter) iter = NULL;
519 g_variant_get (result, "(a{sv})", &iter);
520 const gchar *name;
521 GVariant *value;
522 gboolean system_account = FALSE;
523 while (g_variant_iter_loop (iter, "{&sv}", &name, &value))
524 {
525 if (strcmp (name, "UserName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
526 {
527 g_free (priv->name);
528 priv->name = g_variant_dup_string (value, NULL);
529 }
530 else if (strcmp (name, "RealName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
531 {
532 g_free (priv->real_name);
533 priv->real_name = g_variant_dup_string (value, NULL);
534 }
535 else if (strcmp (name, "HomeDirectory") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
536 {
537 g_free (priv->home_directory);
538 priv->home_directory = g_variant_dup_string (value, NULL);
539 }
540 else if (strcmp (name, "Shell") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
541 {
542 g_free (priv->shell);
543 priv->shell = g_variant_dup_string (value, NULL);
544 }
545 else if (strcmp (name, "SystemAccount") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
546 system_account = g_variant_get_boolean (value);
547 else if (strcmp (name, "Language") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
548 {
549 if (priv->language)
550 g_free (priv->language);
551 priv->language = g_variant_dup_string (value, NULL);
552 }
553 else if (strcmp (name, "IconFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
554 {
555 g_free (priv->image);
556 priv->image = g_variant_dup_string (value, NULL);
557 if (strcmp (priv->image, "") == 0)
558 g_clear_pointer (&priv->image, g_free);
559 }
560 else if (strcmp (name, "XSession") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
561 {
562 g_free (priv->session);
563 priv->session = g_variant_dup_string (value, NULL);
564 }
565 else if (strcmp (name, "Uid") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_UINT64))
566 priv->uid = g_variant_get_uint64 (value);
567 else if (strcmp (name, "Locked") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
568 priv->is_locked = g_variant_get_boolean (value);
569 }
570
571 g_autoptr(GVariant) extra_result = g_dbus_connection_call_sync (priv->bus,
572 "org.freedesktop.Accounts",
573 priv->path,
574 "org.freedesktop.DBus.Properties",
575 "GetAll",
576 g_variant_new ("(s)", "org.freedesktop.DisplayManager.AccountsService"),
577 G_VARIANT_TYPE ("(a{sv})"),
578 G_DBUS_CALL_FLAGS_NONE,
579 -1,
580 NULL,
581 &error);
582 if (error)
583 g_warning ("Error updating user %s: %s", priv->path, error->message);
584 if (extra_result) {
585 g_autoptr(GVariantIter) extra_iter = NULL;
586
587 g_variant_get (extra_result, "(a{sv})", &extra_iter);
588 while (g_variant_iter_loop (extra_iter, "{&sv}", &name, &value))
589 {
590 if (strcmp (name, "BackgroundFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
591 {
592 g_free (priv->background);
593 priv->background = g_variant_dup_string (value, NULL);
594 if (strcmp (priv->background, "") == 0)
595 g_clear_pointer (&priv->background, g_free);
596 }
597 else if (strcmp (name, "HasMessages") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
598 priv->has_messages = g_variant_get_boolean (value);
599 else if (strcmp (name, "KeyboardLayouts") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING_ARRAY))
600 {
601 g_strfreev (priv->layouts);
602 priv->layouts = g_variant_dup_strv (value, NULL);
603 if (!priv->layouts)
604 {
605 priv->layouts = g_malloc (sizeof (gchar *) * 1);
606 priv->layouts[0] = NULL;
607 }
608 }
609 }
610 }
611
612 return !system_account;
613 }
614
615 static void
add_accounts_user(CommonUserList * user_list,const gchar * path,gboolean emit_signal)616 add_accounts_user (CommonUserList *user_list, const gchar *path, gboolean emit_signal)
617 {
618 CommonUserListPrivate *list_priv = GET_LIST_PRIVATE (user_list);
619
620 CommonUser *user = g_object_new (COMMON_TYPE_USER, NULL);
621 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
622
623 g_debug ("User %s added", path);
624 priv->bus = g_object_ref (list_priv->bus);
625 priv->path = g_strdup (path);
626 g_signal_connect (user, USER_SIGNAL_CHANGED, G_CALLBACK (user_changed_cb), user_list);
627 g_signal_connect (user, "get-logged-in", G_CALLBACK (get_logged_in_cb), user_list);
628 if (load_accounts_user (user))
629 {
630 list_priv->users = g_list_insert_sorted (list_priv->users, user, compare_user);
631 if (emit_signal)
632 g_signal_emit (user_list, list_signals[USER_ADDED], 0, user);
633 }
634 else
635 g_object_unref (user);
636 }
637
638 static void
accounts_user_added_cb(GDBusConnection * connection,const gchar * sender_name,const gchar * object_path,const gchar * interface_name,const gchar * signal_name,GVariant * parameters,gpointer data)639 accounts_user_added_cb (GDBusConnection *connection,
640 const gchar *sender_name,
641 const gchar *object_path,
642 const gchar *interface_name,
643 const gchar *signal_name,
644 GVariant *parameters,
645 gpointer data)
646 {
647 CommonUserList *user_list = data;
648
649 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
650 {
651 g_warning ("Got UserAccounts signal UserAdded with unknown parameters %s", g_variant_get_type_string (parameters));
652 return;
653 }
654
655 const gchar *path;
656 g_variant_get (parameters, "(&o)", &path);
657
658 /* Add user if we haven't got them */
659 CommonUser *user = get_user_by_path (user_list, path);
660 if (!user)
661 add_accounts_user (user_list, path, TRUE);
662 }
663
664 static void
accounts_user_deleted_cb(GDBusConnection * connection,const gchar * sender_name,const gchar * object_path,const gchar * interface_name,const gchar * signal_name,GVariant * parameters,gpointer data)665 accounts_user_deleted_cb (GDBusConnection *connection,
666 const gchar *sender_name,
667 const gchar *object_path,
668 const gchar *interface_name,
669 const gchar *signal_name,
670 GVariant *parameters,
671 gpointer data)
672 {
673 CommonUserList *user_list = data;
674 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
675
676 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
677 {
678 g_warning ("Got UserAccounts signal UserDeleted with unknown parameters %s", g_variant_get_type_string (parameters));
679 return;
680 }
681
682 const gchar *path;
683 g_variant_get (parameters, "(&o)", &path);
684
685 /* Delete user if we know of them */
686 CommonUser *user = get_user_by_path (user_list, path);
687 if (user)
688 {
689 g_debug ("User %s deleted", path);
690 priv->users = g_list_remove (priv->users, user);
691
692 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, user);
693
694 g_object_unref (user);
695 }
696 }
697
698 static CommonSession *
load_session(CommonUserList * user_list,const gchar * path)699 load_session (CommonUserList *user_list, const gchar *path)
700 {
701 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
702
703 g_autoptr(GError) error = NULL;
704 g_autoptr(GVariant) result = g_dbus_connection_call_sync (priv->bus,
705 "org.freedesktop.DisplayManager",
706 path,
707 "org.freedesktop.DBus.Properties",
708 "Get",
709 g_variant_new ("(ss)", "org.freedesktop.DisplayManager.Session", "UserName"),
710 G_VARIANT_TYPE ("(v)"),
711 G_DBUS_CALL_FLAGS_NONE,
712 -1,
713 NULL,
714 &error);
715 if (error)
716 g_warning ("Error getting UserName from org.freedesktop.DisplayManager.Session: %s", error->message);
717 if (!result)
718 return NULL;
719
720 g_autoptr(GVariant) username = NULL;
721 g_variant_get (result, "(v)", &username);
722 if (!g_variant_is_of_type (username, G_VARIANT_TYPE_STRING))
723 return NULL;
724
725 const gchar *name;
726 g_variant_get (username, "&s", &name);
727
728 g_debug ("Loaded session %s (%s)", path, name);
729 CommonSession *session = g_object_new (common_session_get_type (), NULL);
730 session->username = g_strdup (name);
731 session->path = g_strdup (path);
732 priv->sessions = g_list_append (priv->sessions, session);
733
734 return session;
735 }
736
737 static void
session_added_cb(GDBusConnection * connection,const gchar * sender_name,const gchar * object_path,const gchar * interface_name,const gchar * signal_name,GVariant * parameters,gpointer data)738 session_added_cb (GDBusConnection *connection,
739 const gchar *sender_name,
740 const gchar *object_path,
741 const gchar *interface_name,
742 const gchar *signal_name,
743 GVariant *parameters,
744 gpointer data)
745 {
746 CommonUserList *user_list = data;
747
748 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
749 {
750 g_warning ("Got DisplayManager signal SessionAdded with unknown parameters %s", g_variant_get_type_string (parameters));
751 return;
752 }
753
754 const gchar *path;
755 g_variant_get (parameters, "(&o)", &path);
756 CommonSession *session = load_session (user_list, path);
757 if (!session)
758 return;
759
760 CommonUser *user = get_user_by_name (user_list, session->username);
761 if (user)
762 g_signal_emit (user, user_signals[CHANGED], 0);
763 }
764
765 static void
session_removed_cb(GDBusConnection * connection,const gchar * sender_name,const gchar * object_path,const gchar * interface_name,const gchar * signal_name,GVariant * parameters,gpointer data)766 session_removed_cb (GDBusConnection *connection,
767 const gchar *sender_name,
768 const gchar *object_path,
769 const gchar *interface_name,
770 const gchar *signal_name,
771 GVariant *parameters,
772 gpointer data)
773 {
774 CommonUserList *user_list = data;
775 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
776
777 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
778 {
779 g_warning ("Got DisplayManager signal SessionRemoved with unknown parameters %s", g_variant_get_type_string (parameters));
780 return;
781 }
782
783 const gchar *path;
784 g_variant_get (parameters, "(&o)", &path);
785
786 for (GList *link = priv->sessions; link; link = link->next)
787 {
788 CommonSession *session = link->data;
789 if (strcmp (session->path, path) == 0)
790 {
791 g_debug ("Session %s removed", path);
792 priv->sessions = g_list_delete_link (priv->sessions, link);
793 CommonUser *user = get_user_by_name (user_list, session->username);
794 if (user)
795 g_signal_emit (user, user_signals[CHANGED], 0);
796 g_object_unref (session);
797 break;
798 }
799 }
800 }
801
802 static void
load_sessions(CommonUserList * user_list)803 load_sessions (CommonUserList *user_list)
804 {
805 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
806
807 priv->session_added_signal = g_dbus_connection_signal_subscribe (priv->bus,
808 "org.freedesktop.DisplayManager",
809 "org.freedesktop.DisplayManager",
810 "SessionAdded",
811 "/org/freedesktop/DisplayManager",
812 NULL,
813 G_DBUS_SIGNAL_FLAGS_NONE,
814 session_added_cb,
815 user_list,
816 NULL);
817 priv->session_removed_signal = g_dbus_connection_signal_subscribe (priv->bus,
818 "org.freedesktop.DisplayManager",
819 "org.freedesktop.DisplayManager",
820 "SessionRemoved",
821 "/org/freedesktop/DisplayManager",
822 NULL,
823 G_DBUS_SIGNAL_FLAGS_NONE,
824 session_removed_cb,
825 user_list,
826 NULL);
827
828 g_autoptr(GError) error = NULL;
829 g_autoptr(GVariant) result = g_dbus_connection_call_sync (priv->bus,
830 "org.freedesktop.DisplayManager",
831 "/org/freedesktop/DisplayManager",
832 "org.freedesktop.DBus.Properties",
833 "Get",
834 g_variant_new ("(ss)", "org.freedesktop.DisplayManager", "Sessions"),
835 G_VARIANT_TYPE ("(v)"),
836 G_DBUS_CALL_FLAGS_NONE,
837 -1,
838 NULL,
839 &error);
840 if (error)
841 g_warning ("Error getting session list from org.freedesktop.DisplayManager: %s", error->message);
842 if (result)
843 {
844 if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(v)")))
845 {
846 g_autoptr(GVariant) value = NULL;
847 g_variant_get (result, "(v)", &value);
848
849 g_debug ("Loading sessions from org.freedesktop.DisplayManager");
850 g_autoptr(GVariantIter) iter = NULL;
851 g_variant_get (value, "ao", &iter);
852 const gchar *path;
853 while (g_variant_iter_loop (iter, "&o", &path))
854 load_session (user_list, path);
855 }
856 else
857 g_warning ("Unexpected type from org.freedesktop.DisplayManager.Sessions: %s", g_variant_get_type_string (result));
858 }
859 }
860
861 static void
load_users(CommonUserList * user_list)862 load_users (CommonUserList *user_list)
863 {
864 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
865
866 if (priv->have_users)
867 return;
868 priv->have_users = TRUE;
869
870 /* Get user list from accounts service and fall back to /etc/passwd if that fails */
871 priv->user_added_signal = g_dbus_connection_signal_subscribe (priv->bus,
872 "org.freedesktop.Accounts",
873 "org.freedesktop.Accounts",
874 "UserAdded",
875 "/org/freedesktop/Accounts",
876 NULL,
877 G_DBUS_SIGNAL_FLAGS_NONE,
878 accounts_user_added_cb,
879 user_list,
880 NULL);
881 priv->user_removed_signal = g_dbus_connection_signal_subscribe (priv->bus,
882 "org.freedesktop.Accounts",
883 "org.freedesktop.Accounts",
884 "UserDeleted",
885 "/org/freedesktop/Accounts",
886 NULL,
887 G_DBUS_SIGNAL_FLAGS_NONE,
888 accounts_user_deleted_cb,
889 user_list,
890 NULL);
891
892 g_autoptr(GError) error = NULL;
893 g_autoptr(GVariant) result = g_dbus_connection_call_sync (priv->bus,
894 "org.freedesktop.Accounts",
895 "/org/freedesktop/Accounts",
896 "org.freedesktop.Accounts",
897 "ListCachedUsers",
898 g_variant_new ("()"),
899 G_VARIANT_TYPE ("(ao)"),
900 G_DBUS_CALL_FLAGS_NONE,
901 -1,
902 NULL,
903 &error);
904 if (error)
905 g_warning ("Error getting user list from org.freedesktop.Accounts: %s", error->message);
906 if (result)
907 {
908 g_debug ("Loading users from org.freedesktop.Accounts");
909 g_autoptr(GVariantIter) iter = NULL;
910 g_variant_get (result, "(ao)", &iter);
911 const gchar *path;
912 while (g_variant_iter_loop (iter, "&o", &path))
913 add_accounts_user (user_list, path, FALSE);
914 }
915 else
916 {
917 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_added_signal);
918 priv->user_added_signal = 0;
919 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_removed_signal);
920 priv->user_removed_signal = 0;
921
922 load_passwd_file (user_list, FALSE);
923
924 /* Watch for changes to user list */
925 g_autoptr(GFile) passwd_file = g_file_new_for_path (PASSWD_FILE);
926 g_autoptr(GError) e = NULL;
927 priv->passwd_monitor = g_file_monitor (passwd_file, G_FILE_MONITOR_NONE, NULL, &e);
928 if (e)
929 g_warning ("Error monitoring %s: %s", PASSWD_FILE, e->message);
930 else
931 g_signal_connect (priv->passwd_monitor, "changed", G_CALLBACK (passwd_changed_cb), user_list);
932 }
933 }
934
935 /**
936 * common_user_list_get_length:
937 * @user_list: a #CommonUserList
938 *
939 * Return value: The number of users able to log in
940 **/
941 gint
common_user_list_get_length(CommonUserList * user_list)942 common_user_list_get_length (CommonUserList *user_list)
943 {
944 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), 0);
945 load_users (user_list);
946 return g_list_length (GET_LIST_PRIVATE (user_list)->users);
947 }
948
949 /**
950 * common_user_list_get_users:
951 * @user_list: A #CommonUserList
952 *
953 * Get a list of users to present to the user. This list may be a subset of the
954 * available users and may be empty depending on the server configuration.
955 *
956 * Return value: (element-type CommonUser) (transfer none): A list of #CommonUser that should be presented to the user.
957 **/
958 GList *
common_user_list_get_users(CommonUserList * user_list)959 common_user_list_get_users (CommonUserList *user_list)
960 {
961 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), NULL);
962 load_users (user_list);
963 return GET_LIST_PRIVATE (user_list)->users;
964 }
965
966 /**
967 * common_user_list_get_user_by_name:
968 * @user_list: A #CommonUserList
969 * @username: Name of user to get.
970 *
971 * Get infomation about a given user or #NULL if this user doesn't exist.
972 * Includes hidden and system users, unlike the list from
973 * common_user_list_get_users.
974 *
975 * Return value: (transfer full): A #CommonUser entry for the given user.
976 **/
977 CommonUser *
common_user_list_get_user_by_name(CommonUserList * user_list,const gchar * username)978 common_user_list_get_user_by_name (CommonUserList *user_list, const gchar *username)
979 {
980 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), NULL);
981 g_return_val_if_fail (username != NULL, NULL);
982
983 load_users (user_list);
984
985 CommonUser *user = get_user_by_name (user_list, username);
986 if (user)
987 return g_object_ref (user);
988
989 /* Sometimes we need to look up users that aren't in AccountsService.
990 Notably we need to look up the user that the greeter runs as, which
991 is usually 'lightdm'. For such cases, we manually create a one-off
992 CommonUser object and pre-seed with passwd info. */
993 struct passwd *entry = getpwnam (username);
994 if (entry != NULL)
995 return make_passwd_user (user_list, entry);
996
997 return NULL;
998 }
999
1000 static void
common_user_list_init(CommonUserList * user_list)1001 common_user_list_init (CommonUserList *user_list)
1002 {
1003 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
1004
1005 priv->bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
1006 }
1007
1008 static void
common_user_list_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1009 common_user_list_set_property (GObject *object,
1010 guint prop_id,
1011 const GValue *value,
1012 GParamSpec *pspec)
1013 {
1014 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1015 }
1016
1017 static void
common_user_list_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1018 common_user_list_get_property (GObject *object,
1019 guint prop_id,
1020 GValue *value,
1021 GParamSpec *pspec)
1022 {
1023 CommonUserList *self = COMMON_USER_LIST (object);
1024
1025 switch (prop_id)
1026 {
1027 case LIST_PROP_NUM_USERS:
1028 g_value_set_int (value, common_user_list_get_length (self));
1029 break;
1030 default:
1031 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1032 break;
1033 }
1034 }
1035
1036 static void
common_user_list_finalize(GObject * object)1037 common_user_list_finalize (GObject *object)
1038 {
1039 CommonUserList *self = COMMON_USER_LIST (object);
1040 CommonUserListPrivate *priv = GET_LIST_PRIVATE (self);
1041
1042 /* Remove children first, they might access us */
1043 g_list_free_full (priv->users, g_object_unref);
1044 g_list_free_full (priv->sessions, g_object_unref);
1045
1046 if (priv->user_added_signal)
1047 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_added_signal);
1048 if (priv->user_removed_signal)
1049 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_removed_signal);
1050 if (priv->session_added_signal)
1051 g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_added_signal);
1052 if (priv->session_removed_signal)
1053 g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_removed_signal);
1054 g_object_unref (priv->bus);
1055 g_clear_object (&priv->passwd_monitor);
1056
1057 G_OBJECT_CLASS (common_user_list_parent_class)->finalize (object);
1058 }
1059
1060 static void
common_user_list_class_init(CommonUserListClass * klass)1061 common_user_list_class_init (CommonUserListClass *klass)
1062 {
1063 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1064
1065 object_class->set_property = common_user_list_set_property;
1066 object_class->get_property = common_user_list_get_property;
1067 object_class->finalize = common_user_list_finalize;
1068
1069 g_object_class_install_property (object_class,
1070 LIST_PROP_NUM_USERS,
1071 g_param_spec_int ("num-users",
1072 "num-users",
1073 "Number of login users",
1074 0, G_MAXINT, 0,
1075 G_PARAM_READABLE));
1076 /**
1077 * CommonUserList::user-added:
1078 * @user_list: A #CommonUserList
1079 * @user: The #CommonUser that has been added.
1080 *
1081 * The ::user-added signal gets emitted when a user account is created.
1082 **/
1083 list_signals[USER_ADDED] =
1084 g_signal_new (USER_LIST_SIGNAL_USER_ADDED,
1085 G_TYPE_FROM_CLASS (klass),
1086 G_SIGNAL_RUN_LAST,
1087 G_STRUCT_OFFSET (CommonUserListClass, user_added),
1088 NULL, NULL,
1089 NULL,
1090 G_TYPE_NONE, 1, COMMON_TYPE_USER);
1091
1092 /**
1093 * CommonUserList::user-changed:
1094 * @user_list: A #CommonUserList
1095 * @user: The #CommonUser that has been changed.
1096 *
1097 * The ::user-changed signal gets emitted when a user account is modified.
1098 **/
1099 list_signals[USER_CHANGED] =
1100 g_signal_new (USER_LIST_SIGNAL_USER_CHANGED,
1101 G_TYPE_FROM_CLASS (klass),
1102 G_SIGNAL_RUN_LAST,
1103 G_STRUCT_OFFSET (CommonUserListClass, user_changed),
1104 NULL, NULL,
1105 NULL,
1106 G_TYPE_NONE, 1, COMMON_TYPE_USER);
1107
1108 /**
1109 * CommonUserList::user-removed:
1110 * @user_list: A #CommonUserList
1111 * @user: The #CommonUser that has been removed.
1112 *
1113 * The ::user-removed signal gets emitted when a user account is removed.
1114 **/
1115 list_signals[USER_REMOVED] =
1116 g_signal_new (USER_LIST_SIGNAL_USER_REMOVED,
1117 G_TYPE_FROM_CLASS (klass),
1118 G_SIGNAL_RUN_LAST,
1119 G_STRUCT_OFFSET (CommonUserListClass, user_removed),
1120 NULL, NULL,
1121 NULL,
1122 G_TYPE_NONE, 1, COMMON_TYPE_USER);
1123 }
1124
1125 static gboolean
call_method(CommonUser * user,const gchar * method,GVariant * args,const gchar * expected,GVariant ** result)1126 call_method (CommonUser *user, const gchar *method, GVariant *args,
1127 const gchar *expected, GVariant **result)
1128 {
1129 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1130
1131 g_autoptr(GError) error = NULL;
1132 g_autoptr(GVariant) answer = g_dbus_connection_call_sync (priv->bus,
1133 "org.freedesktop.Accounts",
1134 priv->path,
1135 "org.freedesktop.Accounts.User",
1136 method,
1137 args,
1138 G_VARIANT_TYPE (expected),
1139 G_DBUS_CALL_FLAGS_NONE,
1140 -1,
1141 NULL,
1142 &error);
1143 if (error)
1144 g_warning ("Could not call %s: %s", method, error->message);
1145
1146 if (!answer)
1147 return FALSE;
1148
1149 if (result)
1150 *result = g_steal_pointer (&answer);
1151
1152 return TRUE;
1153 }
1154
1155 static void
save_string_to_dmrc(CommonUser * user,const gchar * group,const gchar * key,const gchar * value)1156 save_string_to_dmrc (CommonUser *user, const gchar *group,
1157 const gchar *key, const gchar *value)
1158 {
1159 g_autoptr(GKeyFile) dmrc = dmrc_load (user);
1160 g_key_file_set_string (dmrc, group, key, value);
1161 dmrc_save (dmrc, user);
1162 }
1163
1164 /* Loads language/layout/session info for user */
1165 static void
load_dmrc(CommonUser * user)1166 load_dmrc (CommonUser *user)
1167 {
1168 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1169
1170 /* We're using Accounts service instead */
1171 if (priv->path)
1172 return;
1173
1174 if (priv->loaded_dmrc)
1175 return;
1176 priv->loaded_dmrc = TRUE;
1177 g_autoptr(GKeyFile) dmrc = dmrc_load (user);
1178
1179 // FIXME: Watch for changes
1180
1181 /* The Language field contains the locale */
1182 g_free (priv->language);
1183 priv->language = g_key_file_get_string (dmrc, "Desktop", "Language", NULL);
1184
1185 if (g_key_file_has_key (dmrc, "Desktop", "Layout", NULL))
1186 {
1187 g_strfreev (priv->layouts);
1188 priv->layouts = g_malloc (sizeof (gchar *) * 2);
1189 priv->layouts[0] = g_key_file_get_string (dmrc, "Desktop", "Layout", NULL);
1190 priv->layouts[1] = NULL;
1191 }
1192
1193 g_free (priv->session);
1194 priv->session = g_key_file_get_string (dmrc, "Desktop", "Session", NULL);
1195 }
1196
1197 /**
1198 * common_user_get_name:
1199 * @user: A #CommonUser
1200 *
1201 * Get the name of a user.
1202 *
1203 * Return value: The name of the given user
1204 **/
1205 const gchar *
common_user_get_name(CommonUser * user)1206 common_user_get_name (CommonUser *user)
1207 {
1208 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1209 return GET_USER_PRIVATE (user)->name;
1210 }
1211
1212 /**
1213 * common_user_get_real_name:
1214 * @user: A #CommonUser
1215 *
1216 * Get the real name of a user.
1217 *
1218 * Return value: The real name of the given user
1219 **/
1220 const gchar *
common_user_get_real_name(CommonUser * user)1221 common_user_get_real_name (CommonUser *user)
1222 {
1223 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1224 return GET_USER_PRIVATE (user)->real_name;
1225 }
1226
1227 /**
1228 * common_user_get_display_name:
1229 * @user: A #CommonUser
1230 *
1231 * Get the display name of a user.
1232 *
1233 * Return value: The display name of the given user
1234 **/
1235 const gchar *
common_user_get_display_name(CommonUser * user)1236 common_user_get_display_name (CommonUser *user)
1237 {
1238 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1239
1240 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1241 if (!priv->real_name || strcmp (priv->real_name, "") == 0)
1242 return priv->name;
1243 else
1244 return priv->real_name;
1245 }
1246
1247 /**
1248 * common_user_get_home_directory:
1249 * @user: A #CommonUser
1250 *
1251 * Get the home directory for a user.
1252 *
1253 * Return value: The users home directory
1254 */
1255 const gchar *
common_user_get_home_directory(CommonUser * user)1256 common_user_get_home_directory (CommonUser *user)
1257 {
1258 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1259 return GET_USER_PRIVATE (user)->home_directory;
1260 }
1261
1262 /**
1263 * common_user_get_shell:
1264 * @user: A #CommonUser
1265 *
1266 * Get the shell for a user.
1267 *
1268 * Return value: The user's shell
1269 */
1270 const gchar *
common_user_get_shell(CommonUser * user)1271 common_user_get_shell (CommonUser *user)
1272 {
1273 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1274 return GET_USER_PRIVATE (user)->shell;
1275 }
1276
1277 /**
1278 * common_user_get_image:
1279 * @user: A #CommonUser
1280 *
1281 * Get the image URI for a user.
1282 *
1283 * Return value: The image URI for the given user or #NULL if no URI
1284 **/
1285 const gchar *
common_user_get_image(CommonUser * user)1286 common_user_get_image (CommonUser *user)
1287 {
1288 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1289 return GET_USER_PRIVATE (user)->image;
1290 }
1291
1292 /**
1293 * common_user_get_background:
1294 * @user: A #CommonUser
1295 *
1296 * Get the background file path for a user.
1297 *
1298 * Return value: The background file path for the given user or #NULL if no path
1299 **/
1300 const gchar *
common_user_get_background(CommonUser * user)1301 common_user_get_background (CommonUser *user)
1302 {
1303 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1304 return GET_USER_PRIVATE (user)->background;
1305 }
1306
1307 /**
1308 * common_user_get_language:
1309 * @user: A #CommonUser
1310 *
1311 * Get the language for a user.
1312 *
1313 * Return value: The language in the form of a local specification (e.g. "de_DE.UTF-8") for the given user or #NULL if using the system default locale.
1314 **/
1315 const gchar *
common_user_get_language(CommonUser * user)1316 common_user_get_language (CommonUser *user)
1317 {
1318 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1319 load_dmrc (user);
1320 const gchar *language = GET_USER_PRIVATE (user)->language;
1321 return (language && language[0] == 0) ? NULL : language; /* Treat "" as NULL */
1322 }
1323
1324 /**
1325 * common_user_set_language:
1326 * @user: A #CommonUser
1327 * @language: The user's new language
1328 *
1329 * Set the language for a user.
1330 **/
1331 void
common_user_set_language(CommonUser * user,const gchar * language)1332 common_user_set_language (CommonUser *user, const gchar *language)
1333 {
1334 g_return_if_fail (COMMON_IS_USER (user));
1335 if (g_strcmp0 (common_user_get_language (user), language) != 0)
1336 {
1337 call_method (user, "SetLanguage", g_variant_new ("(s)", language), "()", NULL);
1338 save_string_to_dmrc (user, "Desktop", "Language", language);
1339 }
1340 }
1341
1342 /**
1343 * common_user_get_layout:
1344 * @user: A #CommonUser
1345 *
1346 * Get the keyboard layout for a user.
1347 *
1348 * Return value: The keyboard layout for the given user or #NULL if using system defaults. Copy the value if you want to use it long term.
1349 **/
1350 const gchar *
common_user_get_layout(CommonUser * user)1351 common_user_get_layout (CommonUser *user)
1352 {
1353 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1354 load_dmrc (user);
1355 return GET_USER_PRIVATE (user)->layouts[0];
1356 }
1357
1358 /**
1359 * common_user_get_layouts:
1360 * @user: A #CommonUser
1361 *
1362 * Get the configured keyboard layouts for a user.
1363 *
1364 * Return value: (transfer none): A NULL-terminated array of keyboard layouts for the given user. Copy the values if you want to use them long term.
1365 **/
1366 const gchar * const *
common_user_get_layouts(CommonUser * user)1367 common_user_get_layouts (CommonUser *user)
1368 {
1369 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1370 load_dmrc (user);
1371 return (const gchar * const *) GET_USER_PRIVATE (user)->layouts;
1372 }
1373
1374 /**
1375 * common_user_get_session:
1376 * @user: A #CommonUser
1377 *
1378 * Get the session for a user.
1379 *
1380 * Return value: The session for the given user or #NULL if using system defaults.
1381 **/
1382 const gchar *
common_user_get_session(CommonUser * user)1383 common_user_get_session (CommonUser *user)
1384 {
1385 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1386 load_dmrc (user);
1387 const gchar *session = GET_USER_PRIVATE (user)->session;
1388 return (session && session[0] == 0) ? NULL : session; /* Treat "" as NULL */
1389 }
1390
1391 /**
1392 * common_user_set_session:
1393 * @user: A #CommonUser
1394 * @session: The user's new session
1395 *
1396 * Set the session for a user.
1397 **/
1398 void
common_user_set_session(CommonUser * user,const gchar * session)1399 common_user_set_session (CommonUser *user, const gchar *session)
1400 {
1401 g_return_if_fail (COMMON_IS_USER (user));
1402 if (g_strcmp0 (common_user_get_session (user), session) != 0)
1403 {
1404 call_method (user, "SetXSession", g_variant_new ("(s)", session), "()", NULL);
1405 save_string_to_dmrc (user, "Desktop", "Session", session);
1406 }
1407 }
1408
1409 /**
1410 * common_user_get_logged_in:
1411 * @user: A #CommonUser
1412 *
1413 * Check if a user is logged in.
1414 *
1415 * Return value: #TRUE if the user is currently logged in.
1416 **/
1417 gboolean
common_user_get_logged_in(CommonUser * user)1418 common_user_get_logged_in (CommonUser *user)
1419 {
1420 g_return_val_if_fail (COMMON_IS_USER (user), FALSE);
1421
1422 gboolean result;
1423 g_signal_emit (user, user_signals[GET_LOGGED_IN], 0, &result);
1424
1425 return result;
1426 }
1427
1428 /**
1429 * common_user_get_has_messages:
1430 * @user: A #CommonUser
1431 *
1432 * Check if a user has waiting messages.
1433 *
1434 * Return value: #TRUE if the user has waiting messages.
1435 **/
1436 gboolean
common_user_get_has_messages(CommonUser * user)1437 common_user_get_has_messages (CommonUser *user)
1438 {
1439 g_return_val_if_fail (COMMON_IS_USER (user), FALSE);
1440 return GET_USER_PRIVATE (user)->has_messages;
1441 }
1442
1443 /**
1444 * common_user_get_uid:
1445 * @user: A #CommonUser
1446 *
1447 * Get the uid of a user
1448 *
1449 * Return value: The user's uid
1450 **/
1451 uid_t
common_user_get_uid(CommonUser * user)1452 common_user_get_uid (CommonUser *user)
1453 {
1454 g_return_val_if_fail (COMMON_IS_USER (user), 0);
1455 return GET_USER_PRIVATE (user)->uid;
1456 }
1457
1458 /**
1459 * common_user_get_gid:
1460 * @user: A #CommonUser
1461 *
1462 * Get the gid of a user
1463 *
1464 * Return value: The user's gid
1465 **/
1466 gid_t
common_user_get_gid(CommonUser * user)1467 common_user_get_gid (CommonUser *user)
1468 {
1469 g_return_val_if_fail (COMMON_IS_USER (user), 0);
1470 /* gid is not actually stored in AccountsService, so if our user is from
1471 AccountsService, we have to look up manually in passwd. gid won't
1472 change, so just look up the first time we're asked and never again. */
1473 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1474 if (priv->uid != 0 && priv->gid == 0)
1475 {
1476 struct passwd *entry = getpwuid (priv->uid);
1477 if (entry != NULL)
1478 priv->gid = entry->pw_gid;
1479 }
1480 return priv->gid;
1481 }
1482
1483 /**
1484 * common_user_get_is_locked:
1485 * @user: A #CommonUser
1486 *
1487 * Check if a user is locked.
1488 *
1489 * Return value: %TRUE if the user is locked.
1490 **/
1491 gboolean
common_user_get_is_locked(CommonUser * user)1492 common_user_get_is_locked (CommonUser *user)
1493 {
1494 g_return_val_if_fail (COMMON_IS_USER (user), FALSE);
1495 return GET_USER_PRIVATE (user)->is_locked;
1496 }
1497
1498 static void
common_user_init(CommonUser * user)1499 common_user_init (CommonUser *user)
1500 {
1501 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1502 priv->layouts = g_malloc (sizeof (gchar *) * 1);
1503 priv->layouts[0] = NULL;
1504 }
1505
1506 static void
common_user_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1507 common_user_set_property (GObject *object,
1508 guint prop_id,
1509 const GValue *value,
1510 GParamSpec *pspec)
1511 {
1512 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1513 }
1514
1515 static void
common_user_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1516 common_user_get_property (GObject *object,
1517 guint prop_id,
1518 GValue *value,
1519 GParamSpec *pspec)
1520 {
1521 CommonUser *self = COMMON_USER (object);
1522
1523 switch (prop_id)
1524 {
1525 case USER_PROP_NAME:
1526 g_value_set_string (value, common_user_get_name (self));
1527 break;
1528 case USER_PROP_REAL_NAME:
1529 g_value_set_string (value, common_user_get_real_name (self));
1530 break;
1531 case USER_PROP_DISPLAY_NAME:
1532 g_value_set_string (value, common_user_get_display_name (self));
1533 break;
1534 case USER_PROP_HOME_DIRECTORY:
1535 g_value_set_string (value, common_user_get_home_directory (self));
1536 break;
1537 case USER_PROP_SHELL:
1538 g_value_set_string (value, common_user_get_shell (self));
1539 break;
1540 case USER_PROP_IMAGE:
1541 g_value_set_string (value, common_user_get_image (self));
1542 break;
1543 case USER_PROP_BACKGROUND:
1544 g_value_set_string (value, common_user_get_background (self));
1545 break;
1546 case USER_PROP_LANGUAGE:
1547 g_value_set_string (value, common_user_get_language (self));
1548 break;
1549 case USER_PROP_LAYOUT:
1550 g_value_set_string (value, common_user_get_layout (self));
1551 break;
1552 case USER_PROP_LAYOUTS:
1553 g_value_set_boxed (value, g_strdupv ((gchar **) common_user_get_layouts (self)));
1554 break;
1555 case USER_PROP_SESSION:
1556 g_value_set_string (value, common_user_get_session (self));
1557 break;
1558 case USER_PROP_LOGGED_IN:
1559 g_value_set_boolean (value, common_user_get_logged_in (self));
1560 break;
1561 case USER_PROP_HAS_MESSAGES:
1562 g_value_set_boolean (value, common_user_get_has_messages (self));
1563 break;
1564 case USER_PROP_UID:
1565 g_value_set_uint64 (value, common_user_get_uid (self));
1566 break;
1567 case USER_PROP_GID:
1568 g_value_set_uint64 (value, common_user_get_gid (self));
1569 break;
1570 case USER_PROP_IS_LOCKED:
1571 g_value_set_boolean (value, common_user_get_is_locked (self));
1572 break;
1573 default:
1574 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1575 break;
1576 }
1577 }
1578
1579 static void
common_user_finalize(GObject * object)1580 common_user_finalize (GObject *object)
1581 {
1582 CommonUser *self = COMMON_USER (object);
1583 CommonUserPrivate *priv = GET_USER_PRIVATE (self);
1584
1585 g_clear_pointer (&priv->path, g_free);
1586 if (priv->changed_signal)
1587 g_dbus_connection_signal_unsubscribe (priv->bus, priv->changed_signal);
1588 g_clear_object (&priv->bus);
1589 g_clear_pointer (&priv->name, g_free);
1590 g_clear_pointer (&priv->real_name, g_free);
1591 g_clear_pointer (&priv->home_directory, g_free);
1592 g_clear_pointer (&priv->shell, g_free);
1593 g_clear_pointer (&priv->image, g_free);
1594 g_clear_pointer (&priv->background, g_free);
1595 g_clear_pointer (&priv->language, g_free);
1596 g_clear_pointer (&priv->layouts, g_strfreev);
1597 g_clear_pointer (&priv->session, g_free);
1598 }
1599
1600 static void
common_user_class_init(CommonUserClass * klass)1601 common_user_class_init (CommonUserClass *klass)
1602 {
1603 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1604
1605 object_class->set_property = common_user_set_property;
1606 object_class->get_property = common_user_get_property;
1607 object_class->finalize = common_user_finalize;
1608
1609 g_object_class_install_property (object_class,
1610 USER_PROP_NAME,
1611 g_param_spec_string ("name",
1612 "name",
1613 "Username",
1614 NULL,
1615 G_PARAM_READWRITE));
1616 g_object_class_install_property (object_class,
1617 USER_PROP_REAL_NAME,
1618 g_param_spec_string ("real-name",
1619 "real-name",
1620 "Users real name",
1621 NULL,
1622 G_PARAM_READWRITE));
1623 g_object_class_install_property (object_class,
1624 USER_PROP_DISPLAY_NAME,
1625 g_param_spec_string ("display-name",
1626 "display-name",
1627 "Users display name",
1628 NULL,
1629 G_PARAM_READABLE));
1630 g_object_class_install_property (object_class,
1631 USER_PROP_HOME_DIRECTORY,
1632 g_param_spec_string ("home-directory",
1633 "home-directory",
1634 "Home directory",
1635 NULL,
1636 G_PARAM_READWRITE));
1637 g_object_class_install_property (object_class,
1638 USER_PROP_SHELL,
1639 g_param_spec_string ("shell",
1640 "shell",
1641 "Shell",
1642 NULL,
1643 G_PARAM_READWRITE));
1644 g_object_class_install_property (object_class,
1645 USER_PROP_IMAGE,
1646 g_param_spec_string ("image",
1647 "image",
1648 "Avatar image",
1649 NULL,
1650 G_PARAM_READWRITE));
1651 g_object_class_install_property (object_class,
1652 USER_PROP_BACKGROUND,
1653 g_param_spec_string ("background",
1654 "background",
1655 "User background",
1656 NULL,
1657 G_PARAM_READWRITE));
1658 g_object_class_install_property (object_class,
1659 USER_PROP_LANGUAGE,
1660 g_param_spec_string ("language",
1661 "language",
1662 "Language used by this user",
1663 NULL,
1664 G_PARAM_READABLE));
1665 g_object_class_install_property (object_class,
1666 USER_PROP_LAYOUT,
1667 g_param_spec_string ("layout",
1668 "layout",
1669 "Keyboard layout used by this user",
1670 NULL,
1671 G_PARAM_READABLE));
1672 g_object_class_install_property (object_class,
1673 USER_PROP_LAYOUTS,
1674 g_param_spec_boxed ("layouts",
1675 "layouts",
1676 "Keyboard layouts used by this user",
1677 G_TYPE_STRV,
1678 G_PARAM_READABLE));
1679 g_object_class_install_property (object_class,
1680 USER_PROP_SESSION,
1681 g_param_spec_string ("session",
1682 "session",
1683 "Session used by this user",
1684 NULL,
1685 G_PARAM_READABLE));
1686 g_object_class_install_property (object_class,
1687 USER_PROP_LOGGED_IN,
1688 g_param_spec_boolean ("logged-in",
1689 "logged-in",
1690 "TRUE if the user is currently in a session",
1691 FALSE,
1692 G_PARAM_READWRITE));
1693 g_object_class_install_property (object_class,
1694 USER_PROP_LOGGED_IN,
1695 g_param_spec_boolean ("has-messages",
1696 "has-messages",
1697 "TRUE if the user is has waiting messages",
1698 FALSE,
1699 G_PARAM_READWRITE));
1700 g_object_class_install_property (object_class,
1701 USER_PROP_UID,
1702 g_param_spec_uint64 ("uid",
1703 "uid",
1704 "Uid",
1705 0,
1706 G_MAXUINT64,
1707 0,
1708 G_PARAM_READWRITE));
1709 g_object_class_install_property (object_class,
1710 USER_PROP_GID,
1711 g_param_spec_uint64 ("gd",
1712 "gid",
1713 "Gid",
1714 0,
1715 G_MAXUINT64,
1716 0,
1717 G_PARAM_READWRITE));
1718 g_object_class_install_property (object_class,
1719 USER_PROP_IS_LOCKED,
1720 g_param_spec_boolean ("is-locked",
1721 "is-locked",
1722 "TRUE if the user is currently locked",
1723 FALSE,
1724 G_PARAM_READABLE));
1725 /**
1726 * CommonUser::changed:
1727 * @user: A #CommonUser
1728 *
1729 * The ::changed signal gets emitted this user account is modified.
1730 **/
1731 user_signals[CHANGED] =
1732 g_signal_new (USER_SIGNAL_CHANGED,
1733 G_TYPE_FROM_CLASS (klass),
1734 G_SIGNAL_RUN_LAST,
1735 G_STRUCT_OFFSET (CommonUserClass, changed),
1736 NULL, NULL,
1737 NULL,
1738 G_TYPE_NONE, 0);
1739
1740 user_signals[GET_LOGGED_IN] =
1741 g_signal_new ("get-logged-in",
1742 G_TYPE_FROM_CLASS (klass),
1743 G_SIGNAL_RUN_LAST,
1744 0,
1745 g_signal_accumulator_first_wins,
1746 NULL,
1747 NULL,
1748 G_TYPE_BOOLEAN, 0);
1749 }
1750
1751 static void
common_session_init(CommonSession * common_session)1752 common_session_init (CommonSession *common_session)
1753 {
1754 }
1755
1756 static void
common_session_finalize(GObject * object)1757 common_session_finalize (GObject *object)
1758 {
1759 CommonSession *self = COMMON_SESSION (object);
1760
1761 g_clear_pointer (&self->path, g_free);
1762 g_clear_pointer (&self->username, g_free);
1763 }
1764
1765 static void
common_session_class_init(CommonSessionClass * klass)1766 common_session_class_init (CommonSessionClass *klass)
1767 {
1768 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1769 object_class->finalize = common_session_finalize;
1770 }
1771