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 2 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,
119
dupes = g_object_get_data (G_OBJECT (user), "dupes");
125
if (dupes->next == dupes->prev) {
126
dup = dupes->next->data;
127
g_list_free_1 (dupes->next);
128
g_object_set_data (G_OBJECT (dup), "dupes", NULL);
131
dupes->next->prev = dupes->prev;
132
dupes->prev->next = dupes->next;
135
g_list_free_1 (dupes);
136
g_object_set_data (G_OBJECT (user), "dupes", NULL);
140
um_user_show_short_display_name (dup);
141
g_signal_emit (manager, signals[USER_CHANGED], 0, dup);
143
um_user_show_short_display_name (user);
144
g_signal_emit (manager, signals[USER_CHANGED], 0, user);
148
match_real_name_hrfunc (gpointer key,
152
return (value != user && g_strcmp0 (um_user_get_real_name (user), um_user_get_real_name (value)) == 0);
156
add_user_to_dupe_ring (UmUserManager *manager,
163
dup = g_hash_table_find (manager->user_by_object_path,
164
match_real_name_hrfunc, user);
170
dupes = g_object_get_data (G_OBJECT (dup), "dupes");
172
dupes = g_list_append (NULL, dup);
173
g_object_set_data (G_OBJECT (dup), "dupes", dupes);
174
dupes->next = dupes->prev = dupes;
180
l = g_list_append (NULL, user);
181
g_object_set_data (G_OBJECT (user), "dupes", l);
182
l->prev = dupes->prev;
183
dupes->prev->next = l;
188
um_user_show_full_display_name (dup);
189
g_signal_emit (manager, signals[USER_CHANGED], 0, dup);
191
um_user_show_full_display_name (user);
192
g_signal_emit (manager, signals[USER_CHANGED], 0, user);
196
user_changed_handler (UmUser *user,
197
UmUserManager *manager)
199
remove_user_from_dupe_ring (manager, user);
200
add_user_to_dupe_ring (manager, user);
201
g_signal_emit (manager, signals[USER_CHANGED], 0, user);
205
user_added_handler (UmUserManager *manager,
206
const char *object_path)
210
if (g_hash_table_lookup (manager->user_by_object_path, object_path))
213
user = um_user_new_from_object_path (object_path);
217
if (um_user_is_system_account (user)) {
218
g_object_unref (user);
222
add_user_to_dupe_ring (manager, user);
224
g_signal_connect (user, "changed",
225
G_CALLBACK (user_changed_handler), manager);
226
g_hash_table_insert (manager->user_by_object_path, g_strdup (um_user_get_object_path (user)), g_object_ref (user));
227
g_hash_table_insert (manager->user_by_name, g_strdup (um_user_get_user_name (user)), g_object_ref (user));
229
g_signal_emit (manager, signals[USER_ADDED], 0, user);
230
g_object_unref (user);
234
user_deleted_handler (UmUserManager *manager,
235
const char *object_path)
239
user = g_hash_table_lookup (manager->user_by_object_path, object_path);
243
g_signal_handlers_disconnect_by_func (user, user_changed_handler, manager);
245
remove_user_from_dupe_ring (manager, user);
247
g_hash_table_remove (manager->user_by_object_path, um_user_get_object_path (user));
248
g_hash_table_remove (manager->user_by_name, um_user_get_user_name (user));
249
g_signal_emit (manager, signals[USER_REMOVED], 0, user);
250
g_object_unref (user);
254
manager_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, UmUserManager *manager)
256
if (strcmp (signal_name, "UserAdded") == 0) {
257
if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)"))) {
259
g_variant_get (parameters, "(&o)", &object_path);
260
user_added_handler (manager, object_path);
263
else if (strcmp (signal_name, "UserDeleted") == 0) {
264
if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)"))) {
266
g_variant_get (parameters, "(&o)", &object_path);
267
user_deleted_handler (manager, object_path);
273
got_users (GObject *object,
277
UmUserManager *manager = data;
279
GError *error = NULL;
281
result = g_dbus_proxy_call_finish (G_DBUS_PROXY (object), res, &error);
283
manager->no_service = TRUE;
284
g_error_free (error);
288
if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(ao)"))) {
292
g_variant_get (result, "(ao)", &iter);
293
while (g_variant_iter_loop (iter, "&o", &object_path))
294
user_added_handler (manager, object_path);
295
g_variant_iter_free (iter);
298
g_variant_unref (result);
301
g_signal_emit (G_OBJECT (manager), signals[USERS_LOADED], 0);
305
get_users (UmUserManager *manager)
307
g_debug ("calling 'ListCachedUsers'");
308
g_dbus_proxy_call (manager->proxy,
310
g_variant_new ("()"),
311
G_DBUS_CALL_FLAGS_NONE,
319
um_user_manager_init (UmUserManager *manager)
321
GError *error = NULL;
322
GDBusConnection *bus;
324
manager->user_by_object_path = g_hash_table_new_full (g_str_hash,
328
manager->user_by_name = g_hash_table_new_full (g_str_hash,
333
bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
335
g_warning ("Couldn't connect to system bus: %s", error->message);
336
g_error_free (error);
340
manager->proxy = g_dbus_proxy_new_sync (bus,
341
G_DBUS_PROXY_FLAGS_NONE,
343
"org.freedesktop.Accounts",
344
"/org/freedesktop/Accounts",
345
"org.freedesktop.Accounts",
348
if (manager->proxy == NULL) {
349
g_warning ("Couldn't get accounts proxy: %s", error->message);
350
g_error_free (error);
354
g_signal_connect (manager->proxy, "g-signal", G_CALLBACK (manager_signal_cb), manager);
360
clear_dup (gpointer key,
366
/* don't bother maintaining the ring, we're destroying the
367
* entire hash table anyway
369
dupes = g_object_get_data (G_OBJECT (value), "dupes");
372
g_list_free_1 (dupes);
373
g_object_set_data (G_OBJECT (value), "dupes", NULL);
378
um_user_manager_finalize (GObject *object)
380
UmUserManager *manager;
382
manager = UM_USER_MANAGER (object);
384
g_hash_table_foreach (manager->user_by_object_path, clear_dup, NULL);
385
g_hash_table_destroy (manager->user_by_object_path);
386
g_hash_table_destroy (manager->user_by_name);
388
g_object_unref (manager->proxy);
390
G_OBJECT_CLASS (um_user_manager_parent_class)->finalize (object);
394
um_user_manager_ref_default (void)
396
if (user_manager_object != NULL) {
397
g_object_ref (user_manager_object);
399
user_manager_object = g_object_new (UM_TYPE_USER_MANAGER, NULL);
400
g_object_add_weak_pointer (user_manager_object,
401
(gpointer *) &user_manager_object);
404
return UM_USER_MANAGER (user_manager_object);
408
UmUserManager *manager;
410
GAsyncReadyCallback callback;
412
GDestroyNotify destroy;
416
async_user_op_data_free (gpointer d)
418
AsyncUserOpData *data = d;
420
g_object_unref (data->manager);
422
g_free (data->user_name);
425
data->destroy (data->data);
430
/* Used for both create_user and cache_user */
432
user_call_done (GObject *proxy,
436
AsyncUserOpData *data = user_data;
437
GSimpleAsyncResult *res;
439
GError *error = NULL;
442
res = g_simple_async_result_new (G_OBJECT (data->manager),
445
um_user_manager_create_user);
446
result = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), r, &error);
449
* We have to translate the errors manually here, since
450
* calling dbus_g_error_has_name on the error returned in
451
* um_user_manager_create_user_finish doesn't work.
453
remote = g_dbus_error_get_remote_error (error);
454
if (g_dbus_error_is_remote_error (error) &&
455
strcmp (remote, "org.freedesktop.Accounts.Error.PermissionDenied") == 0) {
456
g_simple_async_result_set_error (res,
457
UM_USER_MANAGER_ERROR,
458
UM_USER_MANAGER_ERROR_PERMISSION_DENIED,
461
if (g_dbus_error_is_remote_error (error) &&
462
strcmp (remote, "org.freedesktop.Accounts.Error.UserExists") == 0) {
463
g_simple_async_result_set_error (res,
464
UM_USER_MANAGER_ERROR,
465
UM_USER_MANAGER_ERROR_USER_EXISTS,
466
_("A user with name '%s' already exists."),
468
} else if (g_dbus_error_is_remote_error (error) &&
469
strcmp (remote, "org.freedesktop.Accounts.Error.UserDoesNotExist") == 0) {
470
g_simple_async_result_set_error (res,
471
UM_USER_MANAGER_ERROR,
472
UM_USER_MANAGER_ERROR_USER_DOES_NOT_EXIST,
473
_("No user with the name '%s' exists."),
477
g_simple_async_result_set_from_error (res, error);
479
g_error_free (error);
483
if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(o)"))) {
485
g_variant_get (result, "(o)", &path);
486
g_simple_async_result_set_op_res_gpointer (res, path, g_free);
489
g_simple_async_result_set_error (res,
490
UM_USER_MANAGER_ERROR,
491
UM_USER_MANAGER_ERROR_FAILED,
492
"Got invalid response from AccountsService");
493
g_variant_unref (result);
496
data->callback (G_OBJECT (data->manager), G_ASYNC_RESULT (res), data->data);
497
async_user_op_data_free (data);
498
g_object_unref (res);
502
um_user_manager_create_user_finish (UmUserManager *manager,
503
GAsyncResult *result,
508
GSimpleAsyncResult *res;
510
res = G_SIMPLE_ASYNC_RESULT (result);
514
if (g_simple_async_result_propagate_error (res, error)) {
518
path = g_simple_async_result_get_op_res_gpointer (res);
519
*user = g_hash_table_lookup (manager->user_by_object_path, path);
525
um_user_manager_create_user (UmUserManager *manager,
526
const char *user_name,
527
const char *real_name,
529
GCancellable *cancellable,
530
GAsyncReadyCallback done,
532
GDestroyNotify destroy)
534
AsyncUserOpData *data;
536
data = g_new0 (AsyncUserOpData, 1);
537
data->manager = g_object_ref (manager);
538
data->user_name = g_strdup (user_name);
539
data->callback = done;
540
data->data = done_data;
541
data->destroy = destroy;
543
g_dbus_proxy_call (manager->proxy,
545
g_variant_new ("(ssi)", user_name, real_name, account_type),
546
G_DBUS_CALL_FLAGS_NONE,
554
um_user_manager_cache_user_finish (UmUserManager *manager,
555
GAsyncResult *result,
560
GSimpleAsyncResult *res;
562
res = G_SIMPLE_ASYNC_RESULT (result);
566
if (g_simple_async_result_propagate_error (res, error)) {
570
path = g_simple_async_result_get_op_res_gpointer (res);
571
*user = g_hash_table_lookup (manager->user_by_object_path, path);
577
um_user_manager_cache_user (UmUserManager *manager,
578
const char *user_name,
579
GCancellable *cancellable,
580
GAsyncReadyCallback done,
582
GDestroyNotify destroy)
584
AsyncUserOpData *data;
586
data = g_new0 (AsyncUserOpData, 1);
587
data->manager = g_object_ref (manager);
588
data->user_name = g_strdup (user_name);
589
data->callback = done;
590
data->data = done_data;
591
data->destroy = destroy;
593
g_dbus_proxy_call (manager->proxy,
595
g_variant_new ("(s)", user_name),
596
G_DBUS_CALL_FLAGS_NONE,
604
delete_user_done (GObject *proxy,
608
AsyncUserOpData *data = user_data;
609
GSimpleAsyncResult *res;
611
GError *error = NULL;
613
res = g_simple_async_result_new (G_OBJECT (data->manager),
616
um_user_manager_delete_user);
617
result = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), r, &error);
619
if (g_dbus_error_is_remote_error (error) &&
620
strcmp (g_dbus_error_get_remote_error(error), "org.freedesktop.Accounts.Error.PermissionDenied") == 0) {
621
g_simple_async_result_set_error (res,
622
UM_USER_MANAGER_ERROR,
623
UM_USER_MANAGER_ERROR_PERMISSION_DENIED,
626
else if (g_dbus_error_is_remote_error (error) &&
627
strcmp (g_dbus_error_get_remote_error(error), "org.freedesktop.Accounts.Error.UserExists") == 0) {
628
g_simple_async_result_set_error (res,
629
UM_USER_MANAGER_ERROR,
630
UM_USER_MANAGER_ERROR_USER_DOES_NOT_EXIST,
631
_("This user does not exist."));
634
g_simple_async_result_set_from_error (res, error);
635
g_error_free (error);
639
g_variant_unref (result);
641
data->callback (G_OBJECT (data->manager), G_ASYNC_RESULT (res), data->data);
642
async_user_op_data_free (data);
643
g_object_unref (res);
647
um_user_manager_delete_user_finish (UmUserManager *manager,
648
GAsyncResult *result,
651
GSimpleAsyncResult *res;
653
res = G_SIMPLE_ASYNC_RESULT (result);
655
if (g_simple_async_result_propagate_error (res, error)) {
663
um_user_manager_delete_user (UmUserManager *manager,
665
gboolean remove_files,
666
GAsyncReadyCallback done,
668
GDestroyNotify destroy)
670
AsyncUserOpData *data;
672
data = g_new0 (AsyncUserOpData, 1);
673
data->manager = g_object_ref (manager);
674
data->callback = done;
675
data->data = done_data;
676
data->destroy = destroy;
678
g_dbus_proxy_call (manager->proxy,
680
g_variant_new ("(xb)", (gint64) um_user_get_uid (user), remove_files),
681
G_DBUS_CALL_FLAGS_NONE,
689
um_user_manager_list_users (UmUserManager *manager)
695
g_hash_table_iter_init (&iter, manager->user_by_name);
696
while (g_hash_table_iter_next (&iter, NULL, &value)) {
697
list = g_slist_prepend (list, value);
700
return g_slist_sort (list, (GCompareFunc) um_user_collate);
704
um_user_manager_get_user (UmUserManager *manager,
707
return g_hash_table_lookup (manager->user_by_name, name);
711
um_user_manager_get_user_by_id (UmUserManager *manager,
714
struct passwd *pwent;
716
pwent = getpwuid (uid);
721
return um_user_manager_get_user (manager, pwent->pw_name);
725
um_user_manager_no_service (UmUserManager *manager)
727
return manager->no_service;
731
um_user_manager_error_quark (void)
733
return g_quark_from_static_string ("um-user-manager-error-quark");