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