~ubuntu-branches/debian/stretch/lightdm/stretch

« back to all changes in this revision

Viewing changes to common/user-list.c

  • Committer: Package Import Robot
  • Author(s): Yves-Alexis Perez
  • Date: 2014-04-22 23:33:26 UTC
  • mfrom: (1.1.23) (16.1.11 experimental)
  • Revision ID: package-import@ubuntu.com-20140422233326-gpd7rgap1v53zkuy
Tags: 1.10.0-2
Upload to unstable.

Show diffs side-by-side

added added

removed removed

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