1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
3
* Copyright (C) 2009-2010 Red Hat, Inc.
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 3 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
* Written by: Matthias Clasen <mclasen@redhat.com>
32
#include <sys/types.h>
37
#endif /* HAVE_PATHS_H */
40
#include <glib/gi18n.h>
41
#include <glib/gstdio.h>
42
#include <glib-object.h>
45
#include "um-user-manager.h"
55
static guint signals [LAST_SIGNAL] = { 0, };
57
static void um_user_manager_class_init (UmUserManagerClass *klass);
58
static void um_user_manager_init (UmUserManager *user_manager);
59
static void um_user_manager_finalize (GObject *object);
61
static gpointer user_manager_object = NULL;
63
G_DEFINE_TYPE (UmUserManager, um_user_manager, G_TYPE_OBJECT)
66
um_user_manager_class_init (UmUserManagerClass *klass)
68
GObjectClass *object_class = G_OBJECT_CLASS (klass);
70
object_class->finalize = um_user_manager_finalize;
72
signals [USERS_LOADED] =
73
g_signal_new ("users-loaded",
74
G_TYPE_FROM_CLASS (klass),
76
G_STRUCT_OFFSET (UmUserManagerClass, users_loaded),
78
g_cclosure_marshal_VOID__VOID,
81
signals [USER_ADDED] =
82
g_signal_new ("user-added",
83
G_TYPE_FROM_CLASS (klass),
85
G_STRUCT_OFFSET (UmUserManagerClass, user_added),
87
g_cclosure_marshal_VOID__OBJECT,
88
G_TYPE_NONE, 1, UM_TYPE_USER);
89
signals [USER_REMOVED] =
90
g_signal_new ("user-removed",
91
G_TYPE_FROM_CLASS (klass),
93
G_STRUCT_OFFSET (UmUserManagerClass, user_removed),
95
g_cclosure_marshal_VOID__OBJECT,
96
G_TYPE_NONE, 1, UM_TYPE_USER);
97
signals [USER_CHANGED] =
98
g_signal_new ("user-changed",
99
G_TYPE_FROM_CLASS (klass),
101
G_STRUCT_OFFSET (UmUserManagerClass, user_changed),
103
g_cclosure_marshal_VOID__OBJECT,
104
G_TYPE_NONE, 1, UM_TYPE_USER);
108
/* We maintain a ring for each group of users with the same real name.
109
* We need this to pick the right display names.
112
remove_user_from_dupe_ring (UmUserManager *manager,
118
um_user_show_short_display_name (user);
120
dupes = g_object_get_data (G_OBJECT (user), "dupes");
126
if (dupes->next == dupes->prev) {
127
dup = dupes->next->data;
128
um_user_show_short_display_name (dup);
129
g_signal_emit (manager, signals[USER_CHANGED], 0, dup);
131
g_list_free_1 (dupes->next);
132
g_object_set_data (G_OBJECT (dup), "dupes", NULL);
135
dupes->next->prev = dupes->prev;
136
dupes->prev->next = dupes->next;
139
g_list_free_1 (dupes);
140
g_object_set_data (G_OBJECT (user), "dupes", NULL);
144
match_real_name_hrfunc (gpointer key,
148
return (value != user && g_strcmp0 (um_user_get_real_name (user), um_user_get_real_name (value)) == 0);
152
add_user_to_dupe_ring (UmUserManager *manager,
159
dup = g_hash_table_find (manager->user_by_object_path,
160
match_real_name_hrfunc, user);
166
um_user_show_full_display_name (user);
168
dupes = g_object_get_data (G_OBJECT (dup), "dupes");
170
um_user_show_full_display_name (dup);
171
g_signal_emit (manager, signals[USER_CHANGED], 0, dup);
172
dupes = g_list_append (NULL, dup);
173
g_object_set_data (G_OBJECT (dup), "dupes", dupes);
174
dupes->next = dupes->prev = dupes;
177
l = g_list_append (NULL, user);
178
g_object_set_data (G_OBJECT (user), "dupes", l);
179
l->prev = dupes->prev;
180
dupes->prev->next = l;
186
user_changed_handler (UmUser *user,
187
UmUserManager *manager)
189
remove_user_from_dupe_ring (manager, user);
190
add_user_to_dupe_ring (manager, user);
191
g_signal_emit (manager, signals[USER_CHANGED], 0, user);
195
user_added_handler (DBusGProxy *proxy,
196
const char *object_path,
199
UmUserManager *manager = UM_USER_MANAGER (user_data);
202
if (g_hash_table_lookup (manager->user_by_object_path, object_path))
205
user = um_user_new_from_object_path (object_path);
209
if (um_user_is_system_account (user)) {
210
g_object_unref (user);
214
add_user_to_dupe_ring (manager, user);
216
g_signal_connect (user, "changed",
217
G_CALLBACK (user_changed_handler), manager);
218
g_hash_table_insert (manager->user_by_object_path, g_strdup (um_user_get_object_path (user)), g_object_ref (user));
219
g_hash_table_insert (manager->user_by_name, g_strdup (um_user_get_user_name (user)), g_object_ref (user));
221
g_signal_emit (manager, signals[USER_ADDED], 0, user);
222
g_object_unref (user);
226
user_deleted_handler (DBusGProxy *proxy,
227
const char *object_path,
230
UmUserManager *manager = UM_USER_MANAGER (user_data);
233
user = g_hash_table_lookup (manager->user_by_object_path, object_path);
237
g_signal_handlers_disconnect_by_func (user, user_changed_handler, manager);
239
remove_user_from_dupe_ring (manager, user);
241
g_hash_table_remove (manager->user_by_object_path, um_user_get_object_path (user));
242
g_hash_table_remove (manager->user_by_name, um_user_get_user_name (user));
243
g_signal_emit (manager, signals[USER_REMOVED], 0, user);
244
g_object_unref (user);
248
add_user (const gchar *object_path,
249
UmUserManager *manager)
251
user_added_handler (NULL, object_path, manager);
255
got_users (DBusGProxy *proxy,
256
DBusGProxyCall *call_id,
259
UmUserManager *manager = data;
260
GError *error = NULL;
263
if (!dbus_g_proxy_end_call (proxy,
266
dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), &paths,
268
manager->no_service = TRUE;
269
g_error_free (error);
273
g_ptr_array_foreach (paths, (GFunc)add_user, manager);
275
g_ptr_array_foreach (paths, (GFunc)g_free, NULL);
276
g_ptr_array_free (paths, TRUE);
279
g_signal_emit (G_OBJECT (manager), signals[USERS_LOADED], 0);
283
get_users (UmUserManager *manager)
285
g_debug ("calling 'ListCachedUsers'");
286
dbus_g_proxy_begin_call (manager->proxy,
295
um_user_manager_init (UmUserManager *manager)
297
GError *error = NULL;
299
manager->user_by_object_path = g_hash_table_new_full (g_str_hash,
303
manager->user_by_name = g_hash_table_new_full (g_str_hash,
308
manager->bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
309
if (manager->bus == NULL) {
310
g_warning ("Couldn't connect to system bus: %s", error->message);
311
g_error_free (error);
315
manager->proxy = dbus_g_proxy_new_for_name (manager->bus,
316
"org.freedesktop.Accounts",
317
"/org/freedesktop/Accounts",
318
"org.freedesktop.Accounts");
320
dbus_g_proxy_add_signal (manager->proxy, "UserAdded", DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
321
dbus_g_proxy_add_signal (manager->proxy, "UserDeleted", DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
323
dbus_g_proxy_connect_signal (manager->proxy, "UserAdded",
324
G_CALLBACK (user_added_handler), manager, NULL);
325
dbus_g_proxy_connect_signal (manager->proxy, "UserDeleted",
326
G_CALLBACK (user_deleted_handler), manager, NULL);
334
clear_dup (gpointer key,
340
/* don't bother maintaining the ring, we're destroying the
341
* entire hash table anyway
343
dupes = g_object_get_data (G_OBJECT (value), "dupes");
346
g_list_free_1 (dupes);
347
g_object_set_data (G_OBJECT (value), "dupes", NULL);
352
um_user_manager_finalize (GObject *object)
354
UmUserManager *manager;
356
manager = UM_USER_MANAGER (object);
358
g_hash_table_foreach (manager->user_by_object_path, clear_dup, NULL);
359
g_hash_table_destroy (manager->user_by_object_path);
360
g_hash_table_destroy (manager->user_by_name);
362
G_OBJECT_CLASS (um_user_manager_parent_class)->finalize (object);
366
um_user_manager_ref_default (void)
368
if (user_manager_object != NULL) {
369
g_object_ref (user_manager_object);
371
user_manager_object = g_object_new (UM_TYPE_USER_MANAGER, NULL);
372
g_object_add_weak_pointer (user_manager_object,
373
(gpointer *) &user_manager_object);
376
return UM_USER_MANAGER (user_manager_object);
380
UmUserManager *manager;
382
GAsyncReadyCallback callback;
384
GDestroyNotify destroy;
388
async_user_op_data_free (gpointer d)
390
AsyncUserOpData *data = d;
392
g_object_unref (data->manager);
394
g_free (data->user_name);
397
data->destroy (data->data);
403
create_user_done (DBusGProxy *proxy,
404
DBusGProxyCall *call_id,
407
AsyncUserOpData *data = user_data;
410
GSimpleAsyncResult *res;
412
res = g_simple_async_result_new (G_OBJECT (data->manager),
415
um_user_manager_create_user);
417
if (!dbus_g_proxy_end_call (proxy,
420
DBUS_TYPE_G_OBJECT_PATH, &path,
423
* We have to translate the errors manually here, since
424
* calling dbus_g_error_has_name on the error returned in
425
* um_user_manager_create_user_finish doesn't work.
427
if (dbus_g_error_has_name (error, "org.freedesktop.Accounts.Error.PermissionDenied")) {
428
g_simple_async_result_set_error (res,
429
UM_USER_MANAGER_ERROR,
430
UM_USER_MANAGER_ERROR_PERMISSION_DENIED,
433
else if (dbus_g_error_has_name (error, "org.freedesktop.Accounts.Error.UserExists")) {
434
g_simple_async_result_set_error (res,
435
UM_USER_MANAGER_ERROR,
436
UM_USER_MANAGER_ERROR_USER_EXISTS,
437
_("A user with name '%s' already exists."),
441
g_simple_async_result_set_from_error (res, error);
443
g_error_free (error);
446
g_simple_async_result_set_op_res_gpointer (res, path, g_free);
449
data->callback (G_OBJECT (data->manager), G_ASYNC_RESULT (res), data->data);
453
um_user_manager_create_user_finish (UmUserManager *manager,
454
GAsyncResult *result,
459
GSimpleAsyncResult *res;
461
res = G_SIMPLE_ASYNC_RESULT (result);
465
if (g_simple_async_result_propagate_error (res, error)) {
469
path = g_simple_async_result_get_op_res_gpointer (res);
470
*user = g_hash_table_lookup (manager->user_by_object_path, path);
476
um_user_manager_create_user (UmUserManager *manager,
477
const char *user_name,
478
const char *real_name,
480
GAsyncReadyCallback done,
482
GDestroyNotify destroy)
484
AsyncUserOpData *data;
486
data = g_new0 (AsyncUserOpData, 1);
487
data->manager = g_object_ref (manager);
488
data->user_name = g_strdup (user_name);
489
data->callback = done;
490
data->data = done_data;
491
data->destroy = destroy;
493
dbus_g_proxy_begin_call (manager->proxy,
497
async_user_op_data_free,
498
G_TYPE_STRING, user_name,
499
G_TYPE_STRING, real_name,
500
G_TYPE_INT, account_type,
505
delete_user_done (DBusGProxy *proxy,
506
DBusGProxyCall *call_id,
509
AsyncUserOpData *data = user_data;
511
GSimpleAsyncResult *res;
513
res = g_simple_async_result_new (G_OBJECT (data->manager),
516
um_user_manager_delete_user);
518
if (!dbus_g_proxy_end_call (proxy,
522
if (dbus_g_error_has_name (error, "org.freedesktop.Accounts.Error.PermissionDenied")) {
523
g_simple_async_result_set_error (res,
524
UM_USER_MANAGER_ERROR,
525
UM_USER_MANAGER_ERROR_PERMISSION_DENIED,
528
else if (dbus_g_error_has_name (error, "org.freedesktop.Accounts.Error.UserDoesntExists")) {
529
g_simple_async_result_set_error (res,
530
UM_USER_MANAGER_ERROR,
531
UM_USER_MANAGER_ERROR_USER_DOES_NOT_EXIST,
532
_("This user does not exist."));
535
g_simple_async_result_set_from_error (res, error);
536
g_error_free (error);
540
data->callback (G_OBJECT (data->manager), G_ASYNC_RESULT (res), data->data);
544
um_user_manager_delete_user_finish (UmUserManager *manager,
545
GAsyncResult *result,
548
GSimpleAsyncResult *res;
550
res = G_SIMPLE_ASYNC_RESULT (result);
552
if (g_simple_async_result_propagate_error (res, error)) {
560
um_user_manager_delete_user (UmUserManager *manager,
562
gboolean remove_files,
563
GAsyncReadyCallback done,
565
GDestroyNotify destroy)
567
AsyncUserOpData *data;
569
data = g_new0 (AsyncUserOpData, 1);
570
data->manager = g_object_ref (manager);
571
data->callback = done;
572
data->data = done_data;
573
data->destroy = destroy;
575
dbus_g_proxy_begin_call (manager->proxy,
579
async_user_op_data_free,
580
G_TYPE_INT64, um_user_get_uid (user),
581
G_TYPE_BOOLEAN, remove_files,
586
um_user_manager_list_users (UmUserManager *manager)
592
g_hash_table_iter_init (&iter, manager->user_by_name);
593
while (g_hash_table_iter_next (&iter, NULL, &value)) {
594
list = g_slist_prepend (list, value);
597
return g_slist_sort (list, (GCompareFunc) um_user_collate);
601
um_user_manager_get_user (UmUserManager *manager,
604
return g_hash_table_lookup (manager->user_by_name, name);
608
um_user_manager_get_user_by_id (UmUserManager *manager,
611
struct passwd *pwent;
613
pwent = getpwuid (uid);
618
return um_user_manager_get_user (manager, pwent->pw_name);
622
um_user_manager_no_service (UmUserManager *manager)
624
return manager->no_service;
628
um_user_manager_error_quark (void)
630
return g_quark_from_static_string ("um-user-manager-error-quark");