summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarius Vollmer <mvollmer@redhat.com>2013-10-23 11:42:40 +0300
committerMarius Vollmer <mvollmer@redhat.com>2013-10-23 11:58:03 +0300
commitb310371c25f2de9e848017400683ea5d0a87aa0a (patch)
treec25ced4ef3fa7b9ef9015e329f643311979d55b2
parentd67ad309e259e5579782fe881527a4483a9a8733 (diff)
Library: Support for groups.groups
-rw-r--r--doc/libaccountsservice/Makefile.am1
-rw-r--r--doc/libaccountsservice/libaccountsservice-docs.xml1
-rw-r--r--doc/libaccountsservice/libaccountsservice-sections.txt40
-rw-r--r--doc/libaccountsservice/libaccountsservice.types1
-rw-r--r--src/libaccountsservice/Makefile.am7
-rw-r--r--src/libaccountsservice/act-group-private.h43
-rw-r--r--src/libaccountsservice/act-group.c729
-rw-r--r--src/libaccountsservice/act-group.h62
-rw-r--r--src/libaccountsservice/act-types.h27
-rw-r--r--src/libaccountsservice/act-user-manager-private.h40
-rw-r--r--src/libaccountsservice/act-user-manager.c925
-rw-r--r--src/libaccountsservice/act-user-manager.h44
-rw-r--r--src/libaccountsservice/act-user-private.h6
-rw-r--r--src/libaccountsservice/act-user.c241
-rw-r--r--src/libaccountsservice/act-user.h18
-rw-r--r--src/libaccountsservice/act.h1
16 files changed, 2160 insertions, 26 deletions
diff --git a/doc/libaccountsservice/Makefile.am b/doc/libaccountsservice/Makefile.am
index 8f5486b..087c8ac 100644
--- a/doc/libaccountsservice/Makefile.am
+++ b/doc/libaccountsservice/Makefile.am
@@ -13,6 +13,7 @@ HFILE_GLOB=$(top_srcdir)/src/libaccountsservice/*.h
IGNORE_HFILES= \
accounts-generated.h \
accounts-user-generated.h \
+ accounts-group-generated.h \
ck-manager-generated.h \
ck-seat-generated.h \
ck-session-generated.h \
diff --git a/doc/libaccountsservice/libaccountsservice-docs.xml b/doc/libaccountsservice/libaccountsservice-docs.xml
index 69009c5..5247655 100644
--- a/doc/libaccountsservice/libaccountsservice-docs.xml
+++ b/doc/libaccountsservice/libaccountsservice-docs.xml
@@ -12,6 +12,7 @@
<chapter>
<title>libaccountsservice</title>
<xi:include href="xml/act-user.xml"/>
+ <xi:include href="xml/act-group.xml"/>
<xi:include href="xml/act-user-manager.xml"/>
</chapter>
diff --git a/doc/libaccountsservice/libaccountsservice-sections.txt b/doc/libaccountsservice/libaccountsservice-sections.txt
index 88b0a78..8ded960 100644
--- a/doc/libaccountsservice/libaccountsservice-sections.txt
+++ b/doc/libaccountsservice/libaccountsservice-sections.txt
@@ -15,7 +15,9 @@ act_user_get_locked
act_user_get_login_frequency
act_user_get_login_history
act_user_get_login_time
+act_user_get_manager
act_user_get_num_sessions
+act_user_get_num_sessions_anywhere
act_user_get_object_path
act_user_get_password_hint
act_user_get_password_mode
@@ -25,10 +27,13 @@ act_user_get_shell
act_user_get_uid
act_user_get_user_name
act_user_get_x_session
+act_user_get_cached_groups
act_user_is_loaded
act_user_is_local_account
act_user_is_logged_in
+act_user_is_logged_in_anywhere
act_user_is_system_account
+act_user_is_nonexistent
act_user_set_account_type
act_user_set_automatic_login
act_user_set_email
@@ -41,6 +46,9 @@ act_user_set_password_mode
act_user_set_real_name
act_user_set_user_name
act_user_set_x_session
+act_user_find_groups
+act_user_find_groups_async
+act_user_find_groups_finish
<SUBSECTION Standard>
ActUserClass
ACT_IS_USER
@@ -50,6 +58,27 @@ act_user_get_type
</SECTION>
<SECTION>
+<FILE>act-group</FILE>
+ActGroup
+act_group_get_gid
+act_group_get_group_name
+act_group_get_manager
+act_group_get_object_path
+act_group_get_users
+act_group_is_loaded
+act_group_is_nonexistent
+act_group_set_group_name
+act_group_add_user
+act_group_remove_user
+<SUBSECTION Standard>
+ACT_GROUP
+ACT_IS_GROUP
+ACT_TYPE_GROUP
+ActGroupClass
+act_group_get_type
+</SECTION>
+
+<SECTION>
<FILE>act-user-manager</FILE>
ACT_USER_MANAGER_ERROR
ActUserManager
@@ -72,6 +101,15 @@ act_user_manager_goto_login_session
act_user_manager_list_users
act_user_manager_no_service
act_user_manager_uncache_user
+act_user_manager_create_group
+act_user_manager_create_group_async
+act_user_manager_create_group_finish
+act_user_manager_delete_group
+act_user_manager_delete_group_async
+act_user_manager_delete_group_finish
+act_user_manager_get_group
+act_user_manager_get_group_by_id
+act_user_manager_list_groups
<SUBSECTION Standard>
ActUserManagerClass
ACT_IS_USER_MANAGER
@@ -108,4 +146,4 @@ act_user_manager_error_quark
<SECTION>
<FILE>ConsoleKitManager</FILE>
-</SECTION> \ No newline at end of file
+</SECTION>
diff --git a/doc/libaccountsservice/libaccountsservice.types b/doc/libaccountsservice/libaccountsservice.types
index f0dd87a..45803c5 100644
--- a/doc/libaccountsservice/libaccountsservice.types
+++ b/doc/libaccountsservice/libaccountsservice.types
@@ -1,2 +1,3 @@
act_user_get_type
+act_group_get_type
act_user_manager_get_type
diff --git a/src/libaccountsservice/Makefile.am b/src/libaccountsservice/Makefile.am
index 408d91f..8a61295 100644
--- a/src/libaccountsservice/Makefile.am
+++ b/src/libaccountsservice/Makefile.am
@@ -61,7 +61,9 @@ CLEANFILES += $(BUILT_SOURCES)
libaccountsservicedir = $(includedir)/accountsservice-1.0/act
libaccountsservice_headers = \
+ act-types.h \
act-user.h \
+ act-group.h \
act-user-manager.h \
act-user-enum-types.h \
$(END_OF_LIST)
@@ -80,7 +82,7 @@ libaccountsservice_la_LDFLAGS = \
$(END_OF_LIST)
libaccountsservice_la_LIBADD = \
- ../libaccounts-generated.la \
+ ../libaccounts-generated.la \
$(LIBACCOUNTSSERVICE_LIBS) \
-lcrypt \
$(END_OF_LIST)
@@ -88,12 +90,15 @@ libaccountsservice_la_LIBADD = \
libaccountsservice_la_sources = \
$(libaccountsservice_headers) \
act-user.c \
+ act-group.c \
act-user-manager.c \
$(END_OF_LIST)
libaccountsservice_la_SOURCES = \
$(libaccountsservice_la_sources) \
+ act-user-manager-private.h \
act-user-private.h \
+ act-group-private.h \
$(BUILT_SOURCES) \
$(END_OF_LIST)
diff --git a/src/libaccountsservice/act-group-private.h b/src/libaccountsservice/act-group-private.h
new file mode 100644
index 0000000..e73ac5b
--- /dev/null
+++ b/src/libaccountsservice/act-group-private.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Private interfaces to the ActGroup object
+ */
+
+#ifndef __ACT_GROUP_PRIVATE_H_
+#define __ACT_GROUP_PRIVATE_H_
+
+#include <pwd.h>
+
+#include "act-user-manager.h"
+#include "act-group.h"
+
+G_BEGIN_DECLS
+
+void _act_group_update_from_object_path (ActUserManager *manager,
+ ActGroup *group,
+ const char *object_path);
+void _act_group_update_as_nonexistent (ActGroup *group);
+void _act_group_load_from_group (ActGroup *group,
+ ActGroup *group_to_copy);
+
+G_END_DECLS
+
+#endif /* !__ACT_GROUP_PRIVATE_H_ */
diff --git a/src/libaccountsservice/act-group.c b/src/libaccountsservice/act-group.c
new file mode 100644
index 0000000..aa86fda
--- /dev/null
+++ b/src/libaccountsservice/act-group.c
@@ -0,0 +1,729 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
+ * Copyright (C) 2007-2008 William Jon McCann <mccann@jhu.edu>
+ * Copyright (C) 2013 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <float.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include "act-user-manager-private.h"
+#include "act-group-private.h"
+#include "accounts-group-generated.h"
+
+/**
+ * SECTION:act-group
+ * @title: ActGroup
+ * @short_description: information about a group
+ *
+ * An ActGroup object represents a group on the system.
+ */
+
+/**
+ * ActGroup:
+ *
+ * Represents a group on the system.
+ *
+ * Since: 0.6.36
+ */
+
+#define ACT_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ACT_TYPE_GROUP, ActGroupClass))
+#define ACT_IS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ACT_TYPE_GROUP))
+#define ACT_GROUP_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS ((object), ACT_TYPE_GROUP, ActGroupClass))
+
+#define ACCOUNTS_NAME "org.freedesktop.Accounts"
+#define ACCOUNTS_GROUP_INTERFACE "org.freedesktop.Accounts.Group"
+
+enum {
+ PROP_0,
+ PROP_GID,
+ PROP_GROUP_NAME,
+ PROP_LOCAL_GROUP,
+ PROP_USERS, // XXX - don't know how to define this
+ PROP_NONEXISTENT,
+ PROP_IS_LOADED
+};
+
+enum {
+ CHANGED,
+ LAST_SIGNAL
+};
+
+struct _ActGroup {
+ GObject parent;
+
+ ActUserManager *manager;
+ GDBusConnection *connection;
+ AccountsGroup *accounts_proxy;
+ GDBusProxy *object_proxy;
+ GCancellable *get_all_call;
+ char *object_path;
+
+ gid_t gid;
+ char *group_name;
+ ActUser **users; // NULL terminated
+
+ guint gid_set : 1;
+ guint is_loaded : 1;
+ guint local_group : 1;
+ guint nonexistent : 1;
+};
+
+struct _ActGroupClass
+{
+ GObjectClass parent_class;
+};
+
+static void act_group_finalize (GObject *object);
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (ActGroup, act_group, G_TYPE_OBJECT)
+
+static void
+act_group_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ ActGroup *group;
+
+ group = ACT_GROUP (object);
+
+ switch (param_id) {
+ case PROP_GID:
+ g_value_set_int (value, group->gid);
+ break;
+ case PROP_GROUP_NAME:
+ g_value_set_string (value, group->group_name);
+ break;
+ case PROP_LOCAL_GROUP:
+ g_value_set_boolean (value, group->local_group);
+ break;
+ case PROP_IS_LOADED:
+ g_value_set_boolean (value, group->is_loaded);
+ break;
+ case PROP_NONEXISTENT:
+ g_value_set_boolean (value, group->nonexistent);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+
+static void
+act_group_class_init (ActGroupClass *class)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (class);
+
+ gobject_class->finalize = act_group_finalize;
+ gobject_class->get_property = act_group_get_property;
+
+ g_object_class_install_property (gobject_class,
+ PROP_GID,
+ g_param_spec_int ("gid",
+ "Group ID",
+ "The GID for this group.",
+ 0, G_MAXINT, 0,
+ G_PARAM_READABLE));
+ g_object_class_install_property (gobject_class,
+ PROP_GROUP_NAME,
+ g_param_spec_string ("group-name",
+ "Group Name",
+ "The name for this group.",
+ NULL,
+ G_PARAM_READABLE));
+ g_object_class_install_property (gobject_class,
+ PROP_NONEXISTENT,
+ g_param_spec_boolean ("nonexistent",
+ "Doesn't exist",
+ "Determines whether or not the group object represents a valid group.",
+ FALSE,
+ G_PARAM_READABLE));
+ g_object_class_install_property (gobject_class,
+ PROP_IS_LOADED,
+ g_param_spec_boolean ("is-loaded",
+ "Is loaded",
+ "Determines whether or not the group object is loaded and ready to read from.",
+ FALSE,
+ G_PARAM_READABLE));
+ g_object_class_install_property (gobject_class,
+ PROP_LOCAL_GROUP,
+ g_param_spec_boolean ("local-group",
+ "Local Group",
+ "Local Group",
+ FALSE,
+ G_PARAM_READABLE));
+ /**
+ * ActGroup::changed:
+ *
+ * Emitted when the group changes in some way.
+ */
+ signals [CHANGED] =
+ g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static void
+act_group_init (ActGroup *group)
+{
+ GError *error = NULL;
+
+ group->local_group = TRUE;
+ group->group_name = NULL;
+
+ group->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
+ if (group->connection == NULL) {
+ g_warning ("Couldn't connect to system bus: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+free_user_array (ActUser **a)
+{
+ int i;
+ if (a) {
+ for (i = 0; a[i]; i++)
+ g_object_unref (a[i]);
+ g_free (a);
+ }
+}
+
+static void
+act_group_finalize (GObject *object)
+{
+ ActGroup *group;
+
+ group = ACT_GROUP (object);
+
+ g_free (group->group_name);
+ free_user_array (group->users);
+
+ if (G_OBJECT_CLASS (act_group_parent_class)->finalize)
+ (*G_OBJECT_CLASS (act_group_parent_class)->finalize) (object);
+}
+
+static void
+set_is_loaded (ActGroup *group,
+ gboolean is_loaded)
+{
+ if (group->is_loaded != is_loaded) {
+ group->is_loaded = is_loaded;
+ g_object_notify (G_OBJECT (group), "is-loaded");
+ }
+}
+
+/**
+ * _act_group_update_as_nonexistent:
+ * @group: the group object to update.
+ *
+ * Set's the 'non-existent' property of @group to #TRUE
+ * Can only be called before the group is loaded.
+ *
+ * Since: 0.6.36
+ **/
+void
+_act_group_update_as_nonexistent (ActGroup *group)
+{
+ g_return_if_fail (ACT_IS_GROUP (group));
+ g_return_if_fail (!act_group_is_loaded (group));
+ g_return_if_fail (group->object_path == NULL);
+
+ group->nonexistent = TRUE;
+ g_object_notify (G_OBJECT (group), "nonexistent");
+
+ set_is_loaded (group, TRUE);
+}
+
+/**
+ * act_group_get_gid:
+ * @group: the group object to examine.
+ *
+ * Retrieves the ID of @group.
+ *
+ * Returns: the group id.
+ *
+ * Since: 0.6.36
+ **/
+
+gid_t
+act_group_get_gid (ActGroup *group)
+{
+ g_return_val_if_fail (ACT_IS_GROUP (group), -1);
+
+ return group->gid;
+}
+
+/**
+ * act_group_get_object_path:
+ * @group: a #ActGroup
+ *
+ * Returns the group accounts service object path of @group,
+ * or %NULL if @group doesn't have an object path associated
+ * with it.
+ *
+ * Returns: (transfer none): the object path of the group
+ *
+ * Since: 0.6.36
+ */
+const char *
+act_group_get_object_path (ActGroup *group)
+{
+ g_return_val_if_fail (ACT_IS_GROUP (group), NULL);
+
+ return group->object_path;
+}
+
+/**
+ * act_group_get_manager:
+ * @group: a #ActGroup
+ *
+ * Returns the #ActUserManager that this #ActGroup object belongs to.
+ *
+ * Returns: (transfer none): the #ActUserManager
+ *
+ * Since: 0.6.36
+ */
+ActUserManager *
+act_group_get_manager (ActGroup *group)
+{
+ return group->manager;
+}
+
+/**
+ * act_group_get_group_name:
+ * @group: the group object to examine.
+ *
+ * Retrieves the name of @group.
+ *
+ * Returns: (transfer none): a pointer to an array of characters which must not be modified or
+ * freed, or %NULL.
+ *
+ * Since: 0.6.36
+ **/
+
+const char *
+act_group_get_group_name (ActGroup *group)
+{
+ g_return_val_if_fail (ACT_IS_GROUP (group), NULL);
+
+ return group->group_name;
+}
+
+/**
+ * act_group_get_users:
+ * @group: the group object to examine.
+ *
+ * Retrieves the members of @group that are users.
+ *
+ * Returns: (transfer none): a NULL-terminated array of pointers.
+ *
+ * Since: 0.6.36
+ **/
+
+ActUser **
+act_group_get_users (ActGroup *group)
+{
+ g_return_val_if_fail (ACT_IS_GROUP (group), NULL);
+
+ return group->users;
+}
+
+/**
+ * act_group_is_nonexistent:
+ * @group: the group object to examine.
+ *
+ * Retrieves whether the group is nonexistent or not.
+ *
+ * Returns: (transfer none): %TRUE if the group is nonexistent
+ *
+ * Since: 0.6.36
+ **/
+gboolean
+act_group_is_nonexistent (ActGroup *group)
+{
+ g_return_val_if_fail (ACT_IS_GROUP (group), FALSE);
+
+ return group->nonexistent;
+}
+
+static void
+collect_props (const gchar *key,
+ GVariant *value,
+ ActGroup *group)
+{
+ gboolean handled = TRUE;
+
+ if (strcmp (key, "Gid") == 0) {
+ guint64 new_gid;
+
+ new_gid = g_variant_get_uint64 (value);
+ if (!group->gid_set || (guint64) group->gid != new_gid) {
+ group->gid = (uid_t) new_gid;
+ group->gid_set = TRUE;
+ g_object_notify (G_OBJECT (group), "gid");
+ }
+ } else if (strcmp (key, "GroupName") == 0) {
+ const char *new_group_name;
+
+ new_group_name = g_variant_get_string (value, NULL);
+ if (g_strcmp0 (group->group_name, new_group_name) != 0) {
+ g_free (group->group_name);
+ group->group_name = g_strdup (new_group_name);
+ g_object_notify (G_OBJECT (group), "group-name");
+ }
+ } else if (strcmp (key, "LocalGroup") == 0) {
+ gboolean new_local;
+
+ new_local = g_variant_get_boolean (value);
+ if (group->local_group != new_local) {
+ group->local_group = new_local;
+ g_object_notify (G_OBJECT (group), "local-group");
+ }
+ } else if (strcmp (key, "Users") == 0) {
+ gboolean changed;
+ GVariantIter iter;
+ int i;
+ const gchar *user_path;
+
+ if (group->users == NULL)
+ changed = TRUE;
+ else {
+ changed = FALSE;
+ i = 0;
+ g_variant_iter_init (&iter, value);
+ while (group->users[i] && g_variant_iter_next (&iter, "&o", &user_path)) {
+ if (g_strcmp0 (act_user_get_object_path (group->users[i]), user_path) != 0) {
+ changed = TRUE;
+ break;
+ }
+ i++;
+ }
+ if (group->users[i] != NULL || i != g_variant_n_children (value))
+ changed = TRUE;
+ }
+
+ if (changed) {
+ free_user_array (group->users);
+ group->users = g_new0 (ActUser*, g_variant_n_children (value)+1);
+ i = 0;
+ g_variant_iter_init (&iter, value);
+ while (g_variant_iter_next (&iter, "&o", &user_path)) {
+ group->users[i] = g_object_ref(_act_user_manager_get_user (group->manager,
+ user_path));
+ i++;
+ }
+ // g_object_notify (G_OBJECT (group), "users");
+ }
+ } else {
+ handled = FALSE;
+ }
+
+ if (!handled) {
+ g_debug ("unhandled property %s", key);
+ }
+}
+
+static void
+on_get_all_finished (GObject *object,
+ GAsyncResult *result,
+ gpointer data)
+{
+ GDBusProxy *proxy = G_DBUS_PROXY (object);
+ ActGroup *group = data;
+ GError *error;
+ GVariant *res;
+ GVariantIter *iter;
+ gchar *key;
+ GVariant *value;
+
+ g_assert (G_IS_DBUS_PROXY (group->object_proxy));
+ g_assert (group->object_proxy == proxy);
+
+ error = NULL;
+ res = g_dbus_proxy_call_finish (proxy, result, &error);
+
+ if (! res) {
+ g_debug ("Error calling GetAll() when retrieving properties for %s: %s",
+ group->object_path, error->message);
+ g_error_free (error);
+
+ if (!group->is_loaded) {
+ set_is_loaded (group, TRUE);
+ }
+ return;
+ }
+
+ g_object_unref (group->get_all_call);
+ group->get_all_call = NULL;
+
+ g_variant_get (res, "(a{sv})", &iter);
+ while (g_variant_iter_next (iter, "{sv}", &key, &value)) {
+ collect_props (key, value, group);
+ g_free (key);
+ g_variant_unref (value);
+ }
+ g_variant_iter_free (iter);
+ g_variant_unref (res);
+
+ if (!group->is_loaded) {
+ set_is_loaded (group, TRUE);
+ }
+
+ g_signal_emit (group, signals[CHANGED], 0);
+}
+
+static void
+update_info (ActGroup *group)
+{
+ g_assert (G_IS_DBUS_PROXY (group->object_proxy));
+
+ if (group->get_all_call != NULL) {
+ g_cancellable_cancel (group->get_all_call);
+ g_object_unref (group->get_all_call);
+ }
+
+ group->get_all_call = g_cancellable_new ();
+ g_dbus_proxy_call (group->object_proxy,
+ "GetAll",
+ g_variant_new ("(s)", ACCOUNTS_GROUP_INTERFACE),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ group->get_all_call,
+ on_get_all_finished,
+ group);
+}
+
+static void
+changed_handler (AccountsGroup *object,
+ gpointer *data)
+{
+ ActGroup *group = ACT_GROUP (data);
+
+ update_info (group);
+}
+
+/**
+ * _act_group_update_from_object_path:
+ * @group: the group object to update.
+ * @object_path: the object path of the group to use.
+ *
+ * Updates the properties of @group from the accounts service via
+ * the object path in @object_path.
+ *
+ * Since: 0.6.36
+ **/
+void
+_act_group_update_from_object_path (ActUserManager *manager,
+ ActGroup *group,
+ const char *object_path)
+{
+ GError *error = NULL;
+
+ g_return_if_fail (ACT_IS_USER_MANAGER (manager));
+ g_return_if_fail (ACT_IS_GROUP (group));
+ g_return_if_fail (object_path != NULL);
+ g_return_if_fail (group->object_path == NULL);
+
+ group->manager = manager;
+ group->object_path = g_strdup (object_path);
+
+ group->accounts_proxy = accounts_group_proxy_new_sync (group->connection,
+ G_DBUS_PROXY_FLAGS_NONE,
+ ACCOUNTS_NAME,
+ group->object_path,
+ NULL,
+ &error);
+ if (!group->accounts_proxy) {
+ g_warning ("Couldn't create accounts proxy: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (group->accounts_proxy), INT_MAX);
+
+ g_signal_connect (group->accounts_proxy, "changed", G_CALLBACK (changed_handler), group);
+
+ group->object_proxy = g_dbus_proxy_new_sync (group->connection,
+ G_DBUS_PROXY_FLAGS_NONE,
+ 0,
+ ACCOUNTS_NAME,
+ group->object_path,
+ "org.freedesktop.DBus.Properties",
+ NULL,
+ &error);
+ if (!group->object_proxy) {
+ g_warning ("Couldn't create accounts property proxy: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ update_info (group);
+}
+
+void
+_act_group_load_from_group (ActGroup *group,
+ ActGroup *group_to_copy)
+{
+ if (!group_to_copy->is_loaded) {
+ return;
+ }
+
+ /* loading groups may already have a gid, group name, or session list
+ * from creation, so only update them if necessary
+ */
+ if (!group->gid_set) {
+ group->gid = group_to_copy->gid;
+ g_object_notify (G_OBJECT (group), "gid");
+ }
+
+ if (group->group_name == NULL) {
+ group->group_name = g_strdup (group_to_copy->group_name);
+ g_object_notify (G_OBJECT (group), "group-name");
+ }
+
+ set_is_loaded (group, TRUE);
+}
+
+/**
+ * act_group_is_loaded:
+ * @group: a #ActGroup
+ *
+ * Determines whether or not the group object is loaded and ready to read from.
+ *
+ * Returns: %TRUE or %FALSE
+ *
+ * Since: 0.6.36
+ */
+gboolean
+act_group_is_loaded (ActGroup *group)
+{
+ return group->is_loaded;
+}
+
+/**
+ * act_group_set_group_name:
+ * @group: the group object to alter
+ * @new_group_name: the new group name
+ *
+ * Changes the group name of @group to @new_group_name.
+ *
+ * Note this function is synchronous and ignores errors.
+ *
+ * Since: 0.6.36
+ **/
+void
+act_group_set_group_name (ActGroup *group,
+ const gchar *new_group_name)
+{
+ GError *error = NULL;
+
+ g_return_if_fail (ACT_IS_GROUP (group));
+ g_return_if_fail (ACCOUNTS_IS_GROUP (group->accounts_proxy));
+
+ if (!accounts_group_call_set_group_name_sync (group->accounts_proxy,
+ new_group_name,
+ NULL,
+ &error)) {
+ g_warning ("SetGroupName call failed: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+/**
+ * act_group_add_user:
+ * @group: the group object to alter
+ * @user: the user to add
+ *
+ * Makes @user a direct member of @group. The @user object must
+ * correspond to an existing user.
+ *
+ * Note this function is synchronous and ignores errors.
+ *
+ * Since: 0.6.36
+ **/
+void
+act_group_add_user (ActGroup *group,
+ ActUser *user)
+{
+ GError *error = NULL;
+
+ g_return_if_fail (ACT_IS_GROUP (group));
+ g_return_if_fail (ACT_IS_USER (user));
+ g_return_if_fail (act_user_get_object_path (user) != NULL);
+ g_return_if_fail (ACCOUNTS_IS_GROUP (group->accounts_proxy));
+
+ if (!accounts_group_call_add_user_sync (group->accounts_proxy,
+ act_user_get_object_path (user),
+ NULL,
+ &error)) {
+ g_warning ("AddUser call failed: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+/**
+ * act_group_remove_user:
+ * @group: the group object to alter
+ * @user: the user to remove
+ *
+ * Makes sure that @user is not a direct member of @group. The @user
+ * object must correspond to an existing user.
+ *
+ * Note this function is synchronous and ignores errors.
+ *
+ * Since: 0.6.36
+ **/
+void
+act_group_remove_user (ActGroup *group,
+ ActUser *user)
+{
+ GError *error = NULL;
+
+ g_return_if_fail (ACT_IS_GROUP (group));
+ g_return_if_fail (ACT_IS_USER (user));
+ g_return_if_fail (act_user_get_object_path (user) != NULL);
+ g_return_if_fail (ACCOUNTS_IS_GROUP (group->accounts_proxy));
+
+ if (!accounts_group_call_remove_user_sync (group->accounts_proxy,
+ act_user_get_object_path (user),
+ NULL,
+ &error)) {
+ g_warning ("RemoveUser call failed: %s", error->message);
+ g_error_free (error);
+ }
+}
diff --git a/src/libaccountsservice/act-group.h b/src/libaccountsservice/act-group.h
new file mode 100644
index 0000000..fb7dcfe
--- /dev/null
+++ b/src/libaccountsservice/act-group.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Facade object for group data, owned by ActUserManager
+ */
+
+#ifndef __ACT_GROUP_H__
+#define __ACT_GROUP_H__
+
+#include <sys/types.h>
+#include <glib.h>
+#include <glib-object.h>
+
+#include "act-types.h"
+
+G_BEGIN_DECLS
+
+#define ACT_TYPE_GROUP (act_group_get_type ())
+#define ACT_GROUP(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), ACT_TYPE_GROUP, ActGroup))
+#define ACT_IS_GROUP(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), ACT_TYPE_GROUP))
+
+typedef struct _ActGroupClass ActGroupClass;
+
+GType act_group_get_type (void) G_GNUC_CONST;
+ActUserManager *act_group_get_manager (ActGroup *group);
+
+const char *act_group_get_object_path (ActGroup *group);
+
+gboolean act_group_is_loaded (ActGroup *group);
+gboolean act_group_is_nonexistent (ActGroup *group);
+
+gid_t act_group_get_gid (ActGroup *group);
+const char *act_group_get_group_name (ActGroup *group);
+ActUser **act_group_get_users (ActGroup *group);
+
+void act_group_set_group_name (ActGroup *group,
+ const gchar *new_group_name);
+void act_group_add_user (ActGroup *group,
+ ActUser *user);
+void act_group_remove_user (ActGroup *group,
+ ActUser *user);
+
+G_END_DECLS
+
+#endif /* __ACT_GROUP_H__ */
diff --git a/src/libaccountsservice/act-types.h b/src/libaccountsservice/act-types.h
new file mode 100644
index 0000000..d6867c8
--- /dev/null
+++ b/src/libaccountsservice/act-types.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __ACT_TYPES_H__
+#define __ACT_TYPES_H__
+
+typedef struct _ActUser ActUser;
+typedef struct _ActGroup ActGroup;
+typedef struct _ActUserManager ActUserManager;
+
+#endif /* __ACT_TYPES_H__ */
diff --git a/src/libaccountsservice/act-user-manager-private.h b/src/libaccountsservice/act-user-manager-private.h
new file mode 100644
index 0000000..76e1752
--- /dev/null
+++ b/src/libaccountsservice/act-user-manager-private.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 Red Hat
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Private interfaces to the ActUserManager object
+ */
+
+#ifndef __ACT_USER_MANAGER_PRIVATE_H_
+#define __ACT_USER_MANAGER_PRIVATE_H_
+
+#include <pwd.h>
+
+#include "act-user-manager.h"
+
+G_BEGIN_DECLS
+
+ActUser *_act_user_manager_get_user (ActUserManager *manager,
+ const char *object_path);
+ActGroup *_act_user_manager_get_group (ActUserManager *manager,
+ const char *group_path);
+
+G_END_DECLS
+
+#endif /* !__ACT_GROUP_PRIVATE_H_ */
diff --git a/src/libaccountsservice/act-user-manager.c b/src/libaccountsservice/act-user-manager.c
index 888070d..6438546 100644
--- a/src/libaccountsservice/act-user-manager.c
+++ b/src/libaccountsservice/act-user-manager.c
@@ -49,7 +49,9 @@
#endif
#include "act-user-manager.h"
+#include "act-user-manager-private.h"
#include "act-user-private.h"
+#include "act-group-private.h"
#include "accounts-generated.h"
#include "ck-manager-generated.h"
#include "ck-seat-generated.h"
@@ -86,6 +88,8 @@
* @ACT_USER_MANAGER_ERROR_USER_DOES_NOT_EXIST: The user does not exist
* @ACT_USER_MANAGER_ERROR_PERMISSION_DENIED: Permission denied
* @ACT_USER_MANAGER_ERROR_NOT_SUPPORTED: Operation not supported
+ * @ACT_USER_MANAGER_ERROR_GROUP_EXISTS: The group already exists
+ * @ACT_USER_MANAGER_ERROR_GROUP_DOES_NOT_EXIST: The group does not exist
*
* Various error codes returned by the accounts service.
*/
@@ -158,6 +162,8 @@ typedef enum {
typedef enum {
ACT_USER_MANAGER_FETCH_USER_FROM_USERNAME_REQUEST,
ACT_USER_MANAGER_FETCH_USER_FROM_ID_REQUEST,
+ ACT_USER_MANAGER_FETCH_GROUP_FROM_GROUPNAME_REQUEST,
+ ACT_USER_MANAGER_FETCH_GROUP_FROM_ID_REQUEST,
} ActUserManagerFetchUserRequestType;
typedef struct
@@ -165,10 +171,13 @@ typedef struct
ActUserManager *manager;
ActUserManagerGetUserState state;
ActUser *user;
+ ActGroup *group;
ActUserManagerFetchUserRequestType type;
union {
char *username;
uid_t uid;
+ char *groupname;
+ gid_t gid;
};
char *object_path;
char *description;
@@ -179,6 +188,8 @@ struct ActUserManagerPrivate
GHashTable *normal_users_by_name;
GHashTable *system_users_by_name;
GHashTable *users_by_object_path;
+ GHashTable *groups_by_name;
+ GHashTable *groups_by_object_path;
GHashTable *sessions;
GDBusConnection *connection;
AccountsAccounts *accounts_proxy;
@@ -190,6 +201,8 @@ struct ActUserManagerPrivate
GSList *new_users;
GSList *new_users_inhibiting_load;
GSList *fetch_user_requests;
+ GSList *new_groups;
+ GSList *new_groups_inhibiting_load;
GSList *exclude_usernames;
GSList *include_usernames;
@@ -200,6 +213,7 @@ struct ActUserManagerPrivate
gboolean has_multiple_users;
gboolean getting_sessions;
gboolean listing_cached_users;
+ gboolean listing_cached_groups;
};
enum {
@@ -215,6 +229,9 @@ enum {
USER_REMOVED,
USER_IS_LOGGED_IN_CHANGED,
USER_CHANGED,
+ GROUP_ADDED,
+ GROUP_REMOVED,
+ GROUP_CHANGED,
LAST_SIGNAL
};
@@ -227,6 +244,7 @@ static void act_user_manager_finalize (GObject *object);
static gboolean load_seat_incrementally (ActUserManager *manager);
static void unload_seat (ActUserManager *manager);
static void load_users (ActUserManager *manager);
+static void load_groups (ActUserManager *manager);
static void act_user_manager_queue_load (ActUserManager *manager);
static void queue_load_seat_and_users (ActUserManager *manager);
@@ -236,6 +254,9 @@ static void set_is_loaded (ActUserManager *manager, gboolean is_loaded);
static void on_new_user_loaded (ActUser *user,
GParamSpec *pspec,
ActUserManager *manager);
+static void on_new_group_loaded (ActGroup *group,
+ GParamSpec *pspec,
+ ActUserManager *manager);
static void give_up (ActUserManager *manager,
ActUserManagerFetchUserRequest *request);
static void fetch_user_incrementally (ActUserManagerFetchUserRequest *request);
@@ -243,6 +264,8 @@ static void fetch_user_incrementally (ActUserManagerFetchUserRequest *
static void maybe_set_is_loaded (ActUserManager *manager);
static void update_user (ActUserManager *manager,
ActUser *user);
+static void update_group (ActUserManager *manager,
+ ActGroup *group);
static gpointer user_manager_object = NULL;
G_DEFINE_TYPE (ActUserManager, act_user_manager, G_TYPE_OBJECT)
@@ -252,7 +275,9 @@ static const GDBusErrorEntry error_entries[] = {
{ ACT_USER_MANAGER_ERROR_USER_EXISTS, "org.freedesktop.Accounts.Error.UserExists" },
{ ACT_USER_MANAGER_ERROR_USER_DOES_NOT_EXIST, "org.freedesktop.Accounts.Error.UserDoesNotExist" },
{ ACT_USER_MANAGER_ERROR_PERMISSION_DENIED, "org.freedesktop.Accounts.Error.PermissionDenied" },
- { ACT_USER_MANAGER_ERROR_NOT_SUPPORTED, "org.freedesktop.Accounts.Error.NotSupported" }
+ { ACT_USER_MANAGER_ERROR_NOT_SUPPORTED, "org.freedesktop.Accounts.Error.NotSupported" },
+ { ACT_USER_MANAGER_ERROR_GROUP_EXISTS, "org.freedesktop.Accounts.Error.GroupExists" },
+ { ACT_USER_MANAGER_ERROR_GROUP_DOES_NOT_EXIST, "org.freedesktop.Accounts.Error.GroupDoesNotExist" }
};
GQuark
@@ -638,6 +663,12 @@ describe_user (ActUser *user)
{
ActUserManagerFetchUserRequest *request;
+ request = g_object_get_data (G_OBJECT (user), "fetch-user-request");
+
+ if (request != NULL) {
+ return request->description;
+ }
+
if (act_user_is_loaded (user)) {
static char *description = NULL;
g_clear_pointer (&description, (GDestroyNotify) g_free);
@@ -646,7 +677,23 @@ describe_user (ActUser *user)
return description;
}
- request = g_object_get_data (G_OBJECT (user), "fetch-user-request");
+ return "user";
+}
+
+static const char *
+describe_group (ActGroup *group)
+{
+ ActUserManagerFetchUserRequest *request;
+
+ if (act_group_is_loaded (group)) {
+ static char *description = NULL;
+ g_clear_pointer (&description, (GDestroyNotify) g_free);
+
+ description = g_strdup_printf ("group %s", act_group_get_group_name (group));
+ return description;
+ }
+
+ request = g_object_get_data (G_OBJECT (group), "fetch-user-request");
if (request != NULL) {
return request->description;
@@ -694,6 +741,20 @@ on_user_changed (ActUser *user,
}
static void
+on_group_changed (ActGroup *group,
+ ActUserManager *manager)
+{
+ if (manager->priv->is_loaded) {
+ g_debug ("ActUserManager: %s changed",
+ describe_group (group));
+
+ g_signal_emit (manager, signals[GROUP_CHANGED], 0, group);
+
+ update_group (manager, group);
+ }
+}
+
+static void
queue_load_seat_incrementally (ActUserManager *manager)
{
if (manager->priv->seat.load_idle_id == 0) {
@@ -846,12 +907,24 @@ create_new_user (ActUserManager *manager)
return g_object_ref (user);
}
+static ActGroup *
+create_new_group (ActUserManager *manager)
+{
+ ActGroup *group;
+
+ group = g_object_new (ACT_TYPE_GROUP, NULL);
+
+ manager->priv->new_groups = g_slist_prepend (manager->priv->new_groups, group);
+
+ g_signal_connect_object (group, "notify::is-loaded", G_CALLBACK (on_new_group_loaded), manager, 0);
+
+ return g_object_ref (group);
+}
+
static void
add_user (ActUserManager *manager,
ActUser *user)
{
- const char *object_path;
-
g_debug ("ActUserManager: tracking user '%s'", act_user_get_user_name (user));
if (act_user_is_system_account (user)) {
g_hash_table_insert (manager->priv->system_users_by_name,
@@ -863,13 +936,6 @@ add_user (ActUserManager *manager,
g_object_ref (user));
}
- object_path = act_user_get_object_path (user);
- if (object_path != NULL) {
- g_hash_table_insert (manager->priv->users_by_object_path,
- (gpointer) object_path,
- g_object_ref (user));
- }
-
g_signal_connect_object (user,
"sessions-changed",
G_CALLBACK (on_user_sessions_changed),
@@ -892,6 +958,29 @@ add_user (ActUserManager *manager,
}
static void
+add_group (ActUserManager *manager,
+ ActGroup *group)
+{
+ g_debug ("ActUserManager: tracking group '%s'", act_group_get_group_name (group));
+
+ g_hash_table_insert (manager->priv->groups_by_name,
+ g_strdup (act_group_get_group_name (group)),
+ g_object_ref (group));
+
+ g_signal_connect_object (group,
+ "changed",
+ G_CALLBACK (on_group_changed),
+ manager, 0);
+
+ if (manager->priv->is_loaded) {
+ g_debug ("ActUserManager: loaded, so emitting group-added signal");
+ g_signal_emit (manager, signals[GROUP_ADDED], 0, group);
+ } else {
+ g_debug ("ActUserManager: not yet loaded, so not emitting group-added signal");
+ }
+}
+
+static void
remove_user (ActUserManager *manager,
ActUser *user)
{
@@ -927,6 +1016,34 @@ remove_user (ActUserManager *manager,
}
static void
+remove_group (ActUserManager *manager,
+ ActGroup *group)
+{
+ g_debug ("ActUserManager: no longer tracking group '%s' (with object path %s)",
+ act_group_get_group_name (group),
+ act_group_get_object_path (group));
+
+ g_object_ref (group);
+
+ g_signal_handlers_disconnect_by_func (group, on_group_changed, manager);
+ if (act_group_get_object_path (group) != NULL) {
+ g_hash_table_remove (manager->priv->groups_by_object_path, act_group_get_object_path (group));
+ }
+ if (act_group_get_group_name (group) != NULL) {
+ g_hash_table_remove (manager->priv->groups_by_name, act_group_get_group_name (group));
+ }
+
+ if (manager->priv->is_loaded) {
+ g_debug ("ActUserManager: loaded, so emitting group-removed signal");
+ g_signal_emit (manager, signals[GROUP_REMOVED], 0, group);
+ } else {
+ g_debug ("ActUserManager: not yet loaded, so not emitting group-removed signal");
+ }
+
+ g_object_unref (group);
+}
+
+static void
update_user (ActUserManager *manager,
ActUser *user)
{
@@ -960,6 +1077,13 @@ update_user (ActUserManager *manager,
}
}
+static void
+update_group (ActUserManager *manager,
+ ActGroup *group)
+{
+ /* Nothing to do... */
+}
+
static ActUser *
lookup_user_by_name (ActUserManager *manager,
const char *username)
@@ -975,6 +1099,13 @@ lookup_user_by_name (ActUserManager *manager,
return user;
}
+static ActGroup *
+lookup_group_by_name (ActUserManager *manager,
+ const char *groupname)
+{
+ return g_hash_table_lookup (manager->priv->groups_by_name, groupname);
+}
+
static void
on_new_user_loaded (ActUser *user,
GParamSpec *pspec,
@@ -1049,6 +1180,74 @@ out:
}
}
+static void
+on_new_group_loaded (ActGroup *group,
+ GParamSpec *pspec,
+ ActUserManager *manager)
+{
+ const char *groupname;
+ ActGroup *old_group;
+
+ if (!act_group_is_loaded (group)) {
+ g_debug ("ActUserManager: %s loaded function called when not loaded",
+ describe_group (group));
+ return;
+ }
+ g_signal_handlers_disconnect_by_func (group, on_new_group_loaded, manager);
+
+ manager->priv->new_groups = g_slist_remove (manager->priv->new_groups,
+ group);
+ manager->priv->new_groups_inhibiting_load = g_slist_remove (manager->priv->new_groups_inhibiting_load,
+ group);
+
+ groupname = act_group_get_group_name (group);
+
+ if (groupname == NULL) {
+ if (!act_group_is_nonexistent (group)) {
+ const char *object_path;
+
+ object_path = act_group_get_object_path (group);
+
+ if (object_path != NULL) {
+ g_warning ("ActUserManager: %s has no groupname "
+ "(object path: %s, gid: %d)",
+ describe_group (group),
+ object_path, (int) act_group_get_gid (group));
+ } else {
+ g_warning ("ActUserManager: %s has no groupname (gid: %d)",
+ describe_group (group),
+ (int) act_group_get_gid (group));
+ }
+ }
+ g_object_unref (group);
+ goto out;
+ }
+
+ g_debug ("ActUserManager: %s is now loaded", describe_group (group));
+
+ old_group = lookup_group_by_name (manager, groupname);
+
+ /* If groupname hasn't been added, yet, add it now
+ */
+ if (old_group == NULL) {
+ g_debug ("ActUserManager: %s was not yet known, adding it",
+ describe_group (group));
+ add_group (manager, group);
+ } else {
+ _act_group_load_from_group (old_group, group);
+ }
+
+ g_object_unref (group);
+
+out:
+ if (manager->priv->new_groups_inhibiting_load == NULL) {
+ g_debug ("ActUserManager: no pending groups, trying to set loaded property");
+ maybe_set_is_loaded (manager);
+ } else {
+ g_debug ("ActUserManager: not all groups loaded yet");
+ }
+}
+
static ActUser *
add_new_user_for_object_path (const char *object_path,
ActUserManager *manager)
@@ -1066,11 +1265,39 @@ add_new_user_for_object_path (const char *object_path,
g_debug ("ActUserManager: tracking new user with object path %s", object_path);
user = create_new_user (manager);
- _act_user_update_from_object_path (user, object_path);
+ _act_user_update_from_object_path (manager, user, object_path);
+ g_hash_table_insert (manager->priv->users_by_object_path,
+ (gchar *)act_user_get_object_path (user),
+ g_object_ref (user));
return user;
}
+static ActGroup *
+add_new_group_for_object_path (const char *object_path,
+ ActUserManager *manager)
+{
+ ActGroup *group;
+
+ group = g_hash_table_lookup (manager->priv->groups_by_object_path, object_path);
+
+ if (group != NULL) {
+ g_debug ("ActUserManager: tracking existing %s with object path %s",
+ describe_group (group), object_path);
+ return group;
+ }
+
+ g_debug ("ActUserManager: tracking new group with object path %s", object_path);
+
+ group = create_new_group (manager);
+ _act_group_update_from_object_path (manager, group, object_path);
+ g_hash_table_insert (manager->priv->groups_by_object_path,
+ (gchar *)act_group_get_object_path (group),
+ g_object_ref (group));
+
+ return group;
+}
+
static void
on_new_user_in_accounts_service (GDBusProxy *proxy,
const char *object_path,
@@ -1088,6 +1315,22 @@ on_new_user_in_accounts_service (GDBusProxy *proxy,
}
static void
+on_new_group_in_accounts_service (GDBusProxy *proxy,
+ const char *object_path,
+ gpointer user_data)
+{
+ ActUserManager *manager = ACT_USER_MANAGER (user_data);
+
+ if (!manager->priv->is_loaded) {
+ g_debug ("ActUserManager: ignoring new group in accounts service with object path %s since not loaded yet", object_path);
+ return;
+ }
+
+ g_debug ("ActUserManager: new group in accounts service with object path %s", object_path);
+ add_new_group_for_object_path (object_path, manager);
+}
+
+static void
on_user_removed_in_accounts_service (GDBusProxy *proxy,
const char *object_path,
gpointer user_data)
@@ -1110,6 +1353,28 @@ on_user_removed_in_accounts_service (GDBusProxy *proxy,
}
static void
+on_group_removed_in_accounts_service (GDBusProxy *proxy,
+ const char *object_path,
+ gpointer user_data)
+{
+ ActUserManager *manager = ACT_USER_MANAGER (user_data);
+ ActGroup *group;
+
+ group = g_hash_table_lookup (manager->priv->groups_by_object_path, object_path);
+
+ if (group == NULL) {
+ g_debug ("ActUserManager: ignoring untracked group %s", object_path);
+ return;
+ } else {
+ g_debug ("ActUserManager: tracked group %s removed from accounts service", object_path);
+ }
+
+ manager->priv->new_groups = g_slist_remove (manager->priv->new_groups, group);
+
+ remove_group (manager, group);
+}
+
+static void
on_get_current_session_finished (GObject *object,
GAsyncResult *result,
gpointer data)
@@ -1398,6 +1663,37 @@ on_find_user_by_name_finished (GObject *object,
}
static void
+on_find_group_by_name_finished (GObject *object,
+ GAsyncResult *result,
+ gpointer data)
+{
+ AccountsAccounts *proxy = ACCOUNTS_ACCOUNTS (object);
+ ActUserManagerFetchUserRequest *request = data;
+ GError *error = NULL;
+ char *group;
+
+ if (!accounts_accounts_call_find_group_by_name_finish (proxy, &group, result, &error)) {
+ if (error != NULL) {
+ g_debug ("ActUserManager: Failed to find %s: %s",
+ request->description, error->message);
+ g_error_free (error);
+ } else {
+ g_debug ("ActUserManager: Failed to find %s",
+ request->description);
+ }
+ give_up (request->manager, request);
+ return;
+ }
+
+ g_debug ("ActUserManager: Found object path of %s: %s",
+ request->description, group);
+ request->object_path = group;
+ request->state++;
+
+ fetch_user_incrementally (request);
+}
+
+static void
on_find_user_by_id_finished (GObject *object,
GAsyncResult *result,
gpointer data)
@@ -1429,6 +1725,37 @@ on_find_user_by_id_finished (GObject *object,
}
static void
+on_find_group_by_id_finished (GObject *object,
+ GAsyncResult *result,
+ gpointer data)
+{
+ AccountsAccounts *proxy = ACCOUNTS_ACCOUNTS (object);
+ ActUserManagerFetchUserRequest *request = data;
+ GError *error = NULL;
+ char *group;
+
+ if (!accounts_accounts_call_find_group_by_id_finish (proxy, &group, result, &error)) {
+ if (error != NULL) {
+ g_debug ("ActUserManager: Failed to find user %lu: %s",
+ (gulong) request->uid, error->message);
+ g_error_free (error);
+ } else {
+ g_debug ("ActUserManager: Failed to find user with id %lu",
+ (gulong) request->uid);
+ }
+ give_up (request->manager, request);
+ return;
+ }
+
+ g_debug ("ActUserManager: Found object path of %s: %s",
+ request->description, group);
+ request->object_path = group;
+ request->state++;
+
+ fetch_user_incrementally (request);
+}
+
+static void
find_user_in_accounts_service (ActUserManager *manager,
ActUserManagerFetchUserRequest *request)
{
@@ -1452,7 +1779,20 @@ find_user_in_accounts_service (ActUserManager *manager,
on_find_user_by_id_finished,
request);
break;
-
+ case ACT_USER_MANAGER_FETCH_GROUP_FROM_GROUPNAME_REQUEST:
+ accounts_accounts_call_find_group_by_name (manager->priv->accounts_proxy,
+ request->groupname,
+ NULL,
+ on_find_group_by_name_finished,
+ request);
+ break;
+ case ACT_USER_MANAGER_FETCH_GROUP_FROM_ID_REQUEST:
+ accounts_accounts_call_find_group_by_id (manager->priv->accounts_proxy,
+ request->gid,
+ NULL,
+ on_find_group_by_id_finished,
+ request);
+ break;
}
}
@@ -1535,6 +1875,50 @@ on_list_cached_users_finished (GObject *object,
}
static void
+on_list_cached_groups_finished (GObject *object,
+ GAsyncResult *result,
+ gpointer data)
+{
+ AccountsAccounts *proxy = ACCOUNTS_ACCOUNTS (object);
+ ActUserManager *manager = data;
+ gchar **group_paths;
+ GError *error = NULL;
+
+ manager->priv->listing_cached_groups = FALSE;
+ if (!accounts_accounts_call_list_cached_groups_finish (proxy, &group_paths, result, &error)) {
+ g_debug ("ActUserManager: ListCachedGroups failed: %s", error->message);
+ g_error_free (error);
+
+ g_object_unref (manager->priv->accounts_proxy);
+ manager->priv->accounts_proxy = NULL;
+
+ g_object_unref (manager);
+ return;
+ }
+
+ if (g_strv_length (group_paths) > 0) {
+ int i;
+
+ g_debug ("ActUserManager: ListCachedGroups finished, will set loaded property after list is fully loaded");
+ for (i = 0; group_paths[i] != NULL; i++) {
+ ActGroup *group;
+
+ group = add_new_group_for_object_path (group_paths[i], manager);
+ if (!manager->priv->is_loaded) {
+ manager->priv->new_groups_inhibiting_load = g_slist_prepend (manager->priv->new_groups_inhibiting_load, group);
+ }
+ }
+ } else {
+ g_debug ("ActUserManager: ListCachedGroups finished with empty list, maybe setting loaded property now");
+ maybe_set_is_loaded (manager);
+ }
+
+ g_strfreev (group_paths);
+
+ g_object_unref (manager);
+}
+
+static void
on_get_x11_display_finished (GObject *object,
GAsyncResult *result,
gpointer data)
@@ -2150,12 +2534,18 @@ free_fetch_user_request (ActUserManagerFetchUserRequest *request)
manager = request->manager;
- g_object_set_data (G_OBJECT (request->user), "fetch-user-request", NULL);
+ if (request->user)
+ g_object_set_data (G_OBJECT (request->user), "fetch-user-request", NULL);
+ if (request->group)
+ g_object_set_data (G_OBJECT (request->group), "fetch-user-request", NULL);
manager->priv->fetch_user_requests = g_slist_remove (manager->priv->fetch_user_requests, request);
if (request->type == ACT_USER_MANAGER_FETCH_USER_FROM_USERNAME_REQUEST) {
g_free (request->username);
}
+ if (request->type == ACT_USER_MANAGER_FETCH_GROUP_FROM_GROUPNAME_REQUEST) {
+ g_free (request->groupname);
+ }
g_free (request->object_path);
g_free (request->description);
@@ -2175,6 +2565,8 @@ give_up (ActUserManager *manager,
if (request->user)
_act_user_update_as_nonexistent (request->user);
+ if (request->group)
+ _act_group_update_as_nonexistent (request->group);
}
static void
@@ -2226,7 +2618,10 @@ fetch_user_incrementally (ActUserManagerFetchUserRequest *request)
break;
case ACT_USER_MANAGER_GET_USER_STATE_FETCHED:
g_debug ("ActUserManager: %s fetched", request->description);
- _act_user_update_from_object_path (request->user, request->object_path);
+ if (request->user)
+ _act_user_update_from_object_path (request->manager, request->user, request->object_path);
+ if (request->group)
+ _act_group_update_from_object_path (request->manager, request->group, request->object_path);
break;
case ACT_USER_MANAGER_GET_USER_STATE_UNFETCHED:
g_debug ("ActUserManager: %s was not fetched", request->description);
@@ -2266,6 +2661,28 @@ fetch_user_with_username_from_accounts_service (ActUserManager *manager,
}
static void
+fetch_group_with_groupname_from_accounts_service (ActUserManager *manager,
+ ActGroup *group,
+ const char *groupname)
+{
+ ActUserManagerFetchUserRequest *request;
+
+ request = g_slice_new0 (ActUserManagerFetchUserRequest);
+
+ request->manager = g_object_ref (manager);
+ request->type = ACT_USER_MANAGER_FETCH_GROUP_FROM_GROUPNAME_REQUEST;
+ request->groupname = g_strdup (groupname);
+ request->group = group;
+ request->state = ACT_USER_MANAGER_GET_USER_STATE_UNFETCHED + 1;
+ request->description = g_strdup_printf ("group '%s'", request->groupname);
+
+ manager->priv->fetch_user_requests = g_slist_prepend (manager->priv->fetch_user_requests,
+ request);
+ g_object_set_data (G_OBJECT (group), "fetch-user-request", request);
+ fetch_user_incrementally (request);
+}
+
+static void
fetch_user_with_id_from_accounts_service (ActUserManager *manager,
ActUser *user,
uid_t id)
@@ -2287,6 +2704,28 @@ fetch_user_with_id_from_accounts_service (ActUserManager *manager,
fetch_user_incrementally (request);
}
+static void
+fetch_group_with_id_from_accounts_service (ActUserManager *manager,
+ ActGroup *group,
+ gid_t id)
+{
+ ActUserManagerFetchUserRequest *request;
+
+ request = g_slice_new0 (ActUserManagerFetchUserRequest);
+
+ request->manager = g_object_ref (manager);
+ request->type = ACT_USER_MANAGER_FETCH_GROUP_FROM_ID_REQUEST;
+ request->gid = id;
+ request->group = group;
+ request->state = ACT_USER_MANAGER_GET_USER_STATE_UNFETCHED + 1;
+ request->description = g_strdup_printf ("group with id %lu", (gulong) request->gid);
+
+ manager->priv->fetch_user_requests = g_slist_prepend (manager->priv->fetch_user_requests,
+ request);
+ g_object_set_data (G_OBJECT (group), "fetch-user-request", request);
+ fetch_user_incrementally (request);
+}
+
/**
* act_user_manager_get_user:
* @manager: the manager to query.
@@ -2324,6 +2763,44 @@ act_user_manager_get_user (ActUserManager *manager,
}
/**
+ * act_user_manager_get_group:
+ * @manager: the manager to query.
+ * @groupname: the login name of the group to get.
+ *
+ * Retrieves a pointer to the #ActGroup object for the login @groupname
+ * from @manager. Trying to use this object before its
+ * #ActGroup:is-loaded property is %TRUE will result in undefined
+ * behavior.
+ *
+ * Returns: (transfer none): #ActGroup object
+ *
+ * Since: 0.6.36
+ **/
+ActGroup *
+act_user_manager_get_group (ActUserManager *manager,
+ const char *groupname)
+{
+ ActGroup *group;
+
+ g_return_val_if_fail (ACT_IS_USER_MANAGER (manager), NULL);
+ g_return_val_if_fail (groupname != NULL && groupname[0] != '\0', NULL);
+
+ group = lookup_group_by_name (manager, groupname);
+
+ /* if we don't have it loaded try to load it now */
+ if (group == NULL) {
+ g_debug ("ActUserManager: trying to track new group with groupname %s", groupname);
+ group = create_new_group (manager);
+
+ if (manager->priv->accounts_proxy != NULL) {
+ fetch_group_with_groupname_from_accounts_service (manager, group, groupname);
+ }
+ }
+
+ return group;
+}
+
+/**
* act_user_manager_get_user_by_id:
* @manager: the manager to query.
* @id: the uid of the user to get.
@@ -2362,6 +2839,45 @@ act_user_manager_get_user_by_id (ActUserManager *manager,
return user;
}
+/**
+ * act_user_manager_get_group_by_id:
+ * @manager: the manager to query.
+ * @id: the uid of the group to get.
+ *
+ * Retrieves a pointer to the #ActGroup object for the group with the
+ * given uid from @manager. Trying to use this object before its
+ * #ActGroup:is-loaded property is %TRUE will result in undefined
+ * behavior.
+ *
+ * Returns: (transfer none): #ActGroup object
+ */
+ActGroup *
+act_user_manager_get_group_by_id (ActUserManager *manager,
+ gid_t id)
+{
+ ActGroup *group;
+ gchar *object_path;
+
+ g_return_val_if_fail (ACT_IS_USER_MANAGER (manager), NULL);
+
+ object_path = g_strdup_printf ("/org/freedesktop/Accounts/Group%lu", (gulong) id);
+ group = g_hash_table_lookup (manager->priv->groups_by_object_path, object_path);
+ g_free (object_path);
+
+ if (group != NULL) {
+ return g_object_ref (group);
+ } else {
+ g_debug ("ActUserManager: trying to track new group with uid %lu", (gulong) id);
+ group = create_new_group (manager);
+
+ if (manager->priv->accounts_proxy != NULL) {
+ fetch_group_with_id_from_accounts_service (manager, group, id);
+ }
+ }
+
+ return group;
+}
+
static void
listify_hash_values_hfunc (gpointer key,
gpointer value,
@@ -2393,6 +2909,29 @@ act_user_manager_list_users (ActUserManager *manager)
return g_slist_sort (retval, (GCompareFunc) act_user_collate);
}
+/**
+ * act_user_manager_list_groups:
+ * @manager: a #ActUserManager
+ *
+ * Get a list of groups
+ *
+ * Returns: (element-type ActGroup) (transfer container): List of #ActGroup objects
+ *
+ * Since: 0.6.36
+ */
+GSList *
+act_user_manager_list_groups (ActUserManager *manager)
+{
+ GSList *retval;
+
+ g_return_val_if_fail (ACT_IS_USER_MANAGER (manager), NULL);
+
+ retval = NULL;
+ g_hash_table_foreach (manager->priv->groups_by_name, listify_hash_values_hfunc, &retval);
+
+ return retval;
+}
+
static void
maybe_set_is_loaded (ActUserManager *manager)
{
@@ -2416,6 +2955,16 @@ maybe_set_is_loaded (ActUserManager *manager)
return;
}
+ if (manager->priv->listing_cached_groups) {
+ g_debug ("ActUserManager: Listing cached groups, so not setting loaded property");
+ return;
+ }
+
+ if (manager->priv->new_groups_inhibiting_load != NULL) {
+ g_debug ("ActUserManager: Loading new groups, so not setting loaded property");
+ return;
+ }
+
/* Don't set is_loaded yet unless the seat is already loaded
* or failed to load.
*/
@@ -2526,6 +3075,19 @@ load_users (ActUserManager *manager)
manager->priv->listing_cached_users = TRUE;
}
+static void
+load_groups (ActUserManager *manager)
+{
+ g_assert (manager->priv->accounts_proxy != NULL);
+ g_debug ("ActUserManager: calling 'ListCachedGroups'");
+
+ accounts_accounts_call_list_cached_groups (manager->priv->accounts_proxy,
+ NULL,
+ on_list_cached_groups_finished,
+ g_object_ref (manager));
+ manager->priv->listing_cached_groups = TRUE;
+}
+
static gboolean
load_seat_incrementally (ActUserManager *manager)
{
@@ -2565,9 +3127,10 @@ load_idle (ActUserManager *manager)
{
/* The order below is important: load_seat_incrementally might
set "is-loaded" immediately and we thus need to call
- load_users before it.
+ load_users and load_groups before it.
*/
load_users (manager);
+ load_groups (manager);
manager->priv->seat.state = ACT_USER_MANAGER_SEAT_STATE_UNLOADED + 1;
load_seat_incrementally (manager);
manager->priv->load_id = 0;
@@ -2749,6 +3312,46 @@ act_user_manager_class_init (ActUserManagerClass *klass)
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1, ACT_TYPE_USER);
+ /**
+ * ActUserManager::group-added:
+ *
+ * Emitted when a group is added to the user manager.
+ */
+ signals [GROUP_ADDED] =
+ g_signal_new ("group-added",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ActUserManagerClass, group_added),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, ACT_TYPE_GROUP);
+ /**
+ * ActUserManager::group-removed:
+ *
+ * Emitted when a group is removed from the user manager.
+ */
+ signals [GROUP_REMOVED] =
+ g_signal_new ("group-removed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ActUserManagerClass, group_removed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, ACT_TYPE_GROUP);
+ /**
+ * ActUserManager::group-changed:
+ *
+ * One of the groups has changed
+ */
+ signals [GROUP_CHANGED] =
+ g_signal_new ("group-changed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ActUserManagerClass, group_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, ACT_TYPE_GROUP);
+
g_type_class_add_private (klass, sizeof (ActUserManagerPrivate));
}
@@ -2798,6 +3401,14 @@ act_user_manager_init (ActUserManager *manager)
g_str_equal,
NULL,
g_object_unref);
+ manager->priv->groups_by_name = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_object_unref);
+ manager->priv->groups_by_object_path = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ NULL,
+ g_object_unref);
error = NULL;
manager->priv->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
@@ -2837,6 +3448,15 @@ act_user_manager_init (ActUserManager *manager)
G_CALLBACK (on_user_removed_in_accounts_service),
manager);
+ g_signal_connect (manager->priv->accounts_proxy,
+ "group-added",
+ G_CALLBACK (on_new_group_in_accounts_service),
+ manager);
+ g_signal_connect (manager->priv->accounts_proxy,
+ "group-deleted",
+ G_CALLBACK (on_group_removed_in_accounts_service),
+ manager);
+
manager->priv->seat.state = ACT_USER_MANAGER_SEAT_STATE_UNLOADED;
}
@@ -2929,6 +3549,8 @@ act_user_manager_finalize (GObject *object)
g_hash_table_destroy (manager->priv->normal_users_by_name);
g_hash_table_destroy (manager->priv->system_users_by_name);
g_hash_table_destroy (manager->priv->users_by_object_path);
+ g_hash_table_destroy (manager->priv->groups_by_name);
+ g_hash_table_destroy (manager->priv->groups_by_object_path);
G_OBJECT_CLASS (act_user_manager_parent_class)->finalize (object);
}
@@ -3420,3 +4042,274 @@ act_user_manager_delete_user_finish (ActUserManager *manager,
return success;
}
+
+/**
+ * act_user_manager_create_group:
+ * @manager: a #ActUserManager
+ * @groupname: a unix group name
+ * @error: a #GError
+ *
+ * Creates a group on the system.
+ *
+ * Returns: (transfer full): group object
+ *
+ * Since: 0.6.36
+ */
+ActGroup *
+act_user_manager_create_group (ActUserManager *manager,
+ const char *groupname,
+ GError **error)
+{
+ GError *local_error = NULL;
+ gboolean res;
+ gchar *path;
+ ActGroup *group;
+
+ g_debug ("ActUserManager: Creating group '%s'",
+ groupname);
+
+ g_assert (manager->priv->accounts_proxy != NULL);
+
+ local_error = NULL;
+ res = accounts_accounts_call_create_group_sync (manager->priv->accounts_proxy,
+ groupname,
+ &path,
+ NULL,
+ &local_error);
+ if (! res) {
+ g_propagate_error (error, local_error);
+ return NULL;
+ }
+
+ group = add_new_group_for_object_path (path, manager);
+
+ g_free (path);
+
+ return group;
+}
+
+/**
+ * act_user_manager_create_group_async:
+ * @manager: a #ActUserManager
+ * @groupname: a unix group name
+ * @cancellable: (allow-none): optional #GCancellable object,
+ * %NULL to ignore
+ * @callback: (scope async): a #GAsyncReadyCallback to call
+ * when the request is satisfied
+ * @user_data: (closure): the data to pass to @callback
+ *
+ * Asynchronously creates a group on the system.
+ *
+ * For more details, see act_user_manager_create_group(), which
+ * is the synchronous version of this call.
+ *
+ * Since: 0.6.36
+ */
+void
+act_user_manager_create_group_async (ActUserManager *manager,
+ const char *groupname,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *res;
+
+ g_return_if_fail (ACT_IS_USER_MANAGER (manager));
+ g_return_if_fail (manager->priv->accounts_proxy != NULL);
+
+ g_debug ("ActUserManager: Creating group (async) '%s'",
+ groupname);
+
+ g_assert (manager->priv->accounts_proxy != NULL);
+
+ res = g_simple_async_result_new (G_OBJECT (manager),
+ callback, user_data,
+ act_user_manager_create_group_async);
+ g_simple_async_result_set_check_cancellable (res, cancellable);
+
+ accounts_accounts_call_create_group (manager->priv->accounts_proxy,
+ groupname,
+ cancellable,
+ act_user_manager_async_complete_handler, res);
+}
+
+/**
+ * act_user_manager_create_group_finish:
+ * @manager: a #ActUserManager
+ * @result: a #GAsyncResult
+ * @error: a #GError
+ *
+ * Finishes an asynchronous user creation.
+ *
+ * See act_user_manager_create_user_async().
+ *
+ * Returns: (transfer full): group object
+ *
+ * Since: 0.6.37
+ */
+ActGroup *
+act_user_manager_create_group_finish (ActUserManager *manager,
+ GAsyncResult *result,
+ GError **error)
+{
+ GAsyncResult *inner_result;
+ ActGroup *group = NULL;
+ gchar *path;
+ GSimpleAsyncResult *res;
+ GError *remote_error = NULL;
+
+ g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (manager), act_user_manager_create_group_async), FALSE);
+
+ res = G_SIMPLE_ASYNC_RESULT (result);
+ inner_result = g_simple_async_result_get_op_res_gpointer (res);
+ g_assert (inner_result);
+
+ if (accounts_accounts_call_create_group_finish (manager->priv->accounts_proxy,
+ &path, inner_result, &remote_error)) {
+ group = add_new_group_for_object_path (path, manager);
+ g_free (path);
+ }
+
+ if (remote_error) {
+ g_dbus_error_strip_remote_error (remote_error);
+ g_propagate_error (error, remote_error);
+ }
+
+ return group;
+}
+
+/**
+ * act_user_manager_delete_group:
+ * @manager: a #ActUserManager
+ * @group: an #ActGroup object
+ * @error: a #GError
+ *
+ * Deletes a group account on the system.
+ *
+ * Returns: %TRUE if the group account was successfully deleted
+ *
+ * Since: 0.6.36
+ */
+gboolean
+act_user_manager_delete_group (ActUserManager *manager,
+ ActGroup *group,
+ GError **error)
+{
+ GError *local_error;
+ gboolean res = TRUE;
+
+ g_return_val_if_fail (ACT_IS_USER_MANAGER (manager), FALSE);
+ g_return_val_if_fail (ACT_IS_GROUP (group), FALSE);
+ g_return_val_if_fail (manager->priv->accounts_proxy != NULL, FALSE);
+
+ g_debug ("ActUserManager: Deleting group '%s' (gid %ld)",
+ act_group_get_group_name (group), (long) act_group_get_gid (group));
+
+ local_error = NULL;
+ if (!accounts_accounts_call_delete_group_sync (manager->priv->accounts_proxy,
+ act_group_get_gid (group),
+ NULL,
+ &local_error)) {
+ g_propagate_error (error, local_error);
+ res = FALSE;
+ }
+
+ return res;
+}
+
+/**
+ * act_user_manager_delete_group_async:
+ * @manager: a #ActUserManager
+ * @group: a #ActGroup object
+ * @cancellable: (allow-none): optional #GCancellable object,
+ * %NULL to ignore
+ * @callback: (scope async): a #GAsyncReadyCallback to call
+ * when the request is satisfied
+ * @user_data: (closure): the data to pass to @callback
+ *
+ * Asynchronously deletes a group account from the system.
+ *
+ * For more details, see act_user_manager_delete_group(), which
+ * is the synchronous version of this call.
+ *
+ * Since: 0.6.36
+ */
+void
+act_user_manager_delete_group_async (ActUserManager *manager,
+ ActGroup *group,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *res;
+
+ g_return_if_fail (ACT_IS_USER_MANAGER (manager));
+ g_return_if_fail (ACT_IS_GROUP (group));
+ g_return_if_fail (manager->priv->accounts_proxy != NULL);
+
+ res = g_simple_async_result_new (G_OBJECT (manager),
+ callback, user_data,
+ act_user_manager_delete_group_async);
+ g_simple_async_result_set_check_cancellable (res, cancellable);
+
+ g_debug ("ActUserManager: Deleting (async) group '%s' (gid %ld)",
+ act_group_get_group_name (group), (long) act_group_get_gid (group));
+
+ accounts_accounts_call_delete_group (manager->priv->accounts_proxy,
+ act_group_get_gid (group),
+ cancellable,
+ act_user_manager_async_complete_handler, res);
+}
+
+/**
+ * act_user_manager_delete_group_finish:
+ * @manager: a #ActUserManager
+ * @result: a #GAsyncResult
+ * @error: a #GError
+ *
+ * Finishes an asynchronous group account deletion.
+ *
+ * See act_user_manager_delete_group_async().
+ *
+ * Returns: %TRUE if the group account was successfully deleted
+ *
+ * Since: 0.6.36
+ */
+gboolean
+act_user_manager_delete_group_finish (ActUserManager *manager,
+ GAsyncResult *result,
+ GError **error)
+{
+ GAsyncResult *inner_result;
+ gboolean success;
+ GSimpleAsyncResult *res;
+ GError *remote_error = NULL;
+
+ g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (manager), act_user_manager_delete_group_async), FALSE);
+ res = G_SIMPLE_ASYNC_RESULT (result);
+ inner_result = g_simple_async_result_get_op_res_gpointer (res);
+ g_assert (inner_result);
+
+ success = accounts_accounts_call_delete_group_finish (manager->priv->accounts_proxy,
+ inner_result, &remote_error);
+ if (remote_error) {
+ g_dbus_error_strip_remote_error (remote_error);
+ g_propagate_error (error, remote_error);
+ }
+
+ return success;
+}
+
+ActUser *
+_act_user_manager_get_user (ActUserManager *manager,
+ const gchar *object_path)
+{
+ return add_new_user_for_object_path (object_path, manager);
+}
+
+ActGroup *
+_act_user_manager_get_group (ActUserManager *manager,
+ const gchar *object_path)
+{
+ return add_new_group_for_object_path (object_path, manager);
+}
diff --git a/src/libaccountsservice/act-user-manager.h b/src/libaccountsservice/act-user-manager.h
index 3d91f83..856e313 100644
--- a/src/libaccountsservice/act-user-manager.h
+++ b/src/libaccountsservice/act-user-manager.h
@@ -24,7 +24,9 @@
#include <glib-object.h>
#include <gio/gio.h>
+#include "act-types.h"
#include "act-user.h"
+#include "act-group.h"
G_BEGIN_DECLS
@@ -58,7 +60,13 @@ struct _ActUserManagerClass
void (* user_is_logged_in_changed) (ActUserManager *user_manager,
ActUser *user);
void (* user_changed) (ActUserManager *user_manager,
- ActUser *user);
+ ActGroup *group);
+ void (* group_added) (ActUserManager *user_manager,
+ ActGroup *group);
+ void (* group_removed) (ActUserManager *user_manager,
+ ActGroup *group);
+ void (* group_changed) (ActUserManager *user_manager,
+ ActGroup *group);
};
typedef enum ActUserManagerError
@@ -67,7 +75,9 @@ typedef enum ActUserManagerError
ACT_USER_MANAGER_ERROR_USER_EXISTS,
ACT_USER_MANAGER_ERROR_USER_DOES_NOT_EXIST,
ACT_USER_MANAGER_ERROR_PERMISSION_DENIED,
- ACT_USER_MANAGER_ERROR_NOT_SUPPORTED
+ ACT_USER_MANAGER_ERROR_NOT_SUPPORTED,
+ ACT_USER_MANAGER_ERROR_GROUP_EXISTS,
+ ACT_USER_MANAGER_ERROR_GROUP_DOES_NOT_EXIST
} ActUserManagerError;
#define ACT_USER_MANAGER_ERROR act_user_manager_error_quark ()
@@ -84,6 +94,12 @@ ActUser * act_user_manager_get_user (ActUserManager *mana
ActUser * act_user_manager_get_user_by_id (ActUserManager *manager,
uid_t id);
+GSList * act_user_manager_list_groups (ActUserManager *manager);
+ActGroup * act_user_manager_get_group (ActUserManager *manager,
+ const char *groupname);
+ActGroup * act_user_manager_get_group_by_id (ActUserManager *manager,
+ gid_t id);
+
gboolean act_user_manager_activate_user_session (ActUserManager *manager,
ActUser *user);
@@ -137,6 +153,30 @@ gboolean act_user_manager_delete_user_finish (ActUserManager *
GAsyncResult *result,
GError **error);
+ActGroup* act_user_manager_create_group (ActUserManager *manager,
+ const char *groupname,
+ GError **error);
+void act_user_manager_create_group_async (ActUserManager *manager,
+ const gchar *groupname,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ActGroup* act_user_manager_create_group_finish (ActUserManager *manager,
+ GAsyncResult *result,
+ GError **error);
+
+gboolean act_user_manager_delete_group (ActUserManager *manager,
+ ActGroup *group,
+ GError **error);
+void act_user_manager_delete_group_async (ActUserManager *manager,
+ ActGroup *group,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean act_user_manager_delete_group_finish (ActUserManager *manager,
+ GAsyncResult *result,
+ GError **error);
+
G_END_DECLS
#endif /* __ACT_USER_MANAGER_H__ */
diff --git a/src/libaccountsservice/act-user-private.h b/src/libaccountsservice/act-user-private.h
index 582ccfb..0ec9cef 100644
--- a/src/libaccountsservice/act-user-private.h
+++ b/src/libaccountsservice/act-user-private.h
@@ -26,12 +26,14 @@
#include <pwd.h>
+#include "act-user-manager.h"
#include "act-user.h"
G_BEGIN_DECLS
-void _act_user_update_from_object_path (ActUser *user,
- const char *object_path);
+void _act_user_update_from_object_path (ActUserManager *manager,
+ ActUser *user,
+ const char *object_path);
void _act_user_update_as_nonexistent (ActUser *user);
void _act_user_update_login_frequency (ActUser *user,
int login_frequency);
diff --git a/src/libaccountsservice/act-user.c b/src/libaccountsservice/act-user.c
index 9de689e..d48117e 100644
--- a/src/libaccountsservice/act-user.c
+++ b/src/libaccountsservice/act-user.c
@@ -31,6 +31,7 @@
#include <gio/gio.h>
#include "act-user-private.h"
+#include "act-user-manager-private.h"
#include "accounts-user-generated.h"
/**
@@ -106,6 +107,7 @@ enum {
struct _ActUser {
GObject parent;
+ ActUserManager *manager;
GDBusConnection *connection;
AccountsUser *accounts_proxy;
GDBusProxy *object_proxy;
@@ -132,6 +134,8 @@ struct _ActUser {
ActUserAccountType account_type;
ActUserPasswordMode password_mode;
+ ActGroup **cached_groups;
+
guint uid_set : 1;
guint is_loaded : 1;
@@ -565,6 +569,17 @@ act_user_init (ActUser *user)
}
static void
+free_group_array (ActGroup **a)
+{
+ int i;
+ if (a) {
+ for (i = 0; a[i]; i++)
+ g_object_unref (a[i]);
+ g_free (a);
+ }
+}
+
+static void
act_user_finalize (GObject *object)
{
ActUser *user;
@@ -600,6 +615,8 @@ act_user_finalize (GObject *object)
g_object_unref (user->connection);
}
+ free_group_array (user->cached_groups);
+
if (G_OBJECT_CLASS (act_user_parent_class)->finalize)
(*G_OBJECT_CLASS (act_user_parent_class)->finalize) (object);
}
@@ -837,6 +854,23 @@ act_user_get_login_history (ActUser *user) {
}
/**
+ * act_user_get_cached_groups:
+ * @user: a #ActUser
+ *
+ * Returns the cached groups for @user.
+ *
+ * Returns: (transfer none): A %NULL-terminated array of pointers to #ActGroup objects.
+ *
+ * Since: 0.6.36
+ */
+ActGroup **
+act_user_get_cached_groups (ActUser *user) {
+ g_return_val_if_fail (ACT_IS_USER (user), NULL);
+
+ return user->cached_groups;
+}
+
+/**
* act_user_collate:
* @user1: a user
* @user2: a user
@@ -1069,7 +1103,7 @@ act_user_get_x_session (ActUser *user)
/**
* act_user_get_object_path:
- * @user: a #ActUser
+ * @user: a user
*
* Returns the user accounts service object path of @user,
* or %NULL if @user doesn't have an object path associated
@@ -1086,6 +1120,20 @@ act_user_get_object_path (ActUser *user)
}
/**
+ * act_user_get_manager:
+ * @user: a #ActUser
+ *
+ * Returns the #ActUserManager that this #ActUser object belongs to.
+ *
+ * Returns: (transfer none): the #ActUserManager
+ */
+ActUserManager *
+act_user_get_manager (ActUser *user)
+{
+ return user->manager;
+}
+
+/**
* act_user_get_primary_session_id:
* @user: a #ActUser
*
@@ -1288,6 +1336,41 @@ collect_props (const gchar *key,
user->x_session = g_strdup (new_x_session);
g_object_notify (G_OBJECT (user), "x-session");
}
+ } else if (strcmp (key, "CachedGroups") == 0) {
+ gboolean changed;
+ GVariantIter iter;
+ int i;
+ const gchar *group_path;
+
+ if (user->cached_groups == NULL)
+ changed = TRUE;
+ else {
+ changed = FALSE;
+ i = 0;
+ g_variant_iter_init (&iter, value);
+ while (user->cached_groups[i] && g_variant_iter_next (&iter, "&o", &group_path)) {
+ if (g_strcmp0 (act_group_get_object_path (user->cached_groups[i]), group_path) != 0) {
+ changed = TRUE;
+ break;
+ }
+ i++;
+ }
+ if (user->cached_groups[i] != NULL || i != g_variant_n_children (value))
+ changed = TRUE;
+ }
+
+ if (changed) {
+ free_group_array (user->cached_groups);
+ user->cached_groups = g_new0 (ActGroup*, g_variant_n_children (value)+1);
+ i = 0;
+ g_variant_iter_init (&iter, value);
+ while (g_variant_iter_next (&iter, "&o", &group_path)) {
+ user->cached_groups[i] = g_object_ref(_act_user_manager_get_group (user->manager,
+ group_path));
+ i++;
+ }
+ // g_object_notify (G_OBJECT (user), "cached-groups");
+ }
} else {
handled = FALSE;
}
@@ -1405,15 +1488,18 @@ _act_user_update_as_nonexistent (ActUser *user)
* the object path in @object_path.
**/
void
-_act_user_update_from_object_path (ActUser *user,
- const char *object_path)
+_act_user_update_from_object_path (ActUserManager *manager,
+ ActUser *user,
+ const char *object_path)
{
GError *error = NULL;
+ g_return_if_fail (ACT_IS_USER_MANAGER (manager));
g_return_if_fail (ACT_IS_USER (user));
g_return_if_fail (object_path != NULL);
g_return_if_fail (user->object_path == NULL);
+ user->manager = manager;
user->object_path = g_strdup (object_path);
user->accounts_proxy = accounts_user_proxy_new_sync (user->connection,
@@ -1965,3 +2051,152 @@ act_user_set_automatic_login (ActUser *user,
g_error_free (error);
}
}
+
+static ActGroup **
+convert_group_paths (ActUserManager *manager,
+ gchar **paths)
+{
+ int i, n;
+ ActGroup **res;
+
+ if (paths == NULL)
+ return NULL;
+
+ n = g_strv_length (paths);
+ res = g_new0 (ActGroup*, n+1);
+ for (i = 0; i < n; i++)
+ res[i] = g_object_ref (_act_user_manager_get_group (manager, paths[i]));
+
+ return res;
+}
+
+/**
+ * act_user_find_groups:
+ * @user: an #ActUser object
+ * @indirect: %TRUE to also list groups with indirect membership
+ * @error: a #GError
+ *
+ * Finds the groups of a user. This operation might query remote
+ * databases and thus be slow.
+ *
+ * Returns: (transfer full): A %NULL-terminated array of pointers to
+ * #ActGroup objects. Free it by unreffing all #ActGroup objects and
+ * then calling #g_free on the array. Returns %NULL in case of error.
+ */
+ActGroup **
+act_user_find_groups (ActUser *user,
+ gboolean indirect,
+ GError **error)
+{
+ GError *local_error;
+ gchar **res = NULL;
+
+ g_return_val_if_fail (ACT_IS_USER (user), FALSE);
+ g_return_val_if_fail (user->accounts_proxy != NULL, FALSE);
+
+ g_debug ("ActUser: finding groups of '%s' (uid %ld)",
+ act_user_get_user_name (user), (long) act_user_get_uid (user));
+
+ local_error = NULL;
+ if (!accounts_user_call_find_groups_sync (user->accounts_proxy,
+ indirect,
+ &res,
+ NULL,
+ &local_error)) {
+ g_propagate_error (error, local_error);
+ }
+
+ return convert_group_paths (user->manager, res);
+}
+
+static void
+act_user_async_complete_handler (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *res = user_data;
+
+ g_simple_async_result_set_op_res_gpointer (res, g_object_ref (result), g_object_unref);
+ g_simple_async_result_complete (res);
+ g_object_unref (res);
+}
+
+/**
+ * act_user_find_groups_async:
+ * @user: an #ActUser object
+ * @indirect: %TRUE to also list groups with indirect membership
+ * @cancellable: (allow-none): optional #GCancellable object,
+ * %NULL to ignore
+ * @callback: (scope async): a #GAsyncReadyCallback to call
+ * when the request is satisfied
+ * @user_data: (closure): the data to pass to @callback
+ *
+ * Asynchronously finds groups.
+ *
+ * For more details, see act_user_find_groups(), which is the
+ * synchronous version of this call.
+ */
+void
+act_user_find_groups_async (ActUser *user,
+ gboolean indirect,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *res;
+
+ g_return_if_fail (ACT_IS_USER (user));
+ g_return_if_fail (user->accounts_proxy != NULL);
+
+ res = g_simple_async_result_new (G_OBJECT (user),
+ callback, user_data,
+ act_user_find_groups_async);
+ g_simple_async_result_set_check_cancellable (res, cancellable);
+
+ g_debug ("ActUser: Finding groups (async) of '%s' (uid %ld)",
+ act_user_get_user_name (user), (long) act_user_get_uid (user));
+
+ accounts_user_call_find_groups (user->accounts_proxy,
+ indirect,
+ cancellable,
+ act_user_async_complete_handler, res);
+}
+
+/**
+ * act_user_find_groups_finish:
+ * @user: a #ActUser
+ * @result: a #GAsyncResult
+ * @error: a #GError
+ *
+ * Finishes an asynchronous group finding.
+ *
+ * See act_user_find_groups_async().
+ *
+ * Returns: (transfer full): A %NULL-terminated array of pointers to
+ * #ActGroup objects. Free it by unreffing all #ActGroup objects and
+ * then calling #g_free on the array. Returns %NULL in case of error.
+ */
+ActGroup **
+act_user_find_groups_finish (ActUser *user,
+ GAsyncResult *result,
+ GError **error)
+{
+ GAsyncResult *inner_result;
+ gchar **group_paths = NULL;
+ GSimpleAsyncResult *res;
+ GError *remote_error = NULL;
+
+ g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (user), act_user_find_groups_async), FALSE);
+ res = G_SIMPLE_ASYNC_RESULT (result);
+ inner_result = g_simple_async_result_get_op_res_gpointer (res);
+ g_assert (inner_result);
+
+ if (!accounts_user_call_find_groups_finish (user->accounts_proxy,
+ &group_paths,
+ inner_result, &remote_error)) {
+ g_dbus_error_strip_remote_error (remote_error);
+ g_propagate_error (error, remote_error);
+ }
+
+ return convert_group_paths (user->manager, group_paths);
+}
diff --git a/src/libaccountsservice/act-user.h b/src/libaccountsservice/act-user.h
index d188db4..8234fdf 100644
--- a/src/libaccountsservice/act-user.h
+++ b/src/libaccountsservice/act-user.h
@@ -28,6 +28,9 @@
#include <sys/types.h>
#include <glib.h>
#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "act-types.h"
G_BEGIN_DECLS
@@ -46,12 +49,12 @@ typedef enum {
ACT_USER_PASSWORD_MODE_NONE,
} ActUserPasswordMode;
-typedef struct _ActUser ActUser;
typedef struct _ActUserClass ActUserClass;
GType act_user_get_type (void) G_GNUC_CONST;
const char *act_user_get_object_path (ActUser *user);
+ActUserManager *act_user_get_manager (ActUser *user);
uid_t act_user_get_uid (ActUser *user);
const char *act_user_get_user_name (ActUser *user);
@@ -79,6 +82,7 @@ const char *act_user_get_icon_file (ActUser *user);
const char *act_user_get_language (ActUser *user);
const char *act_user_get_x_session (ActUser *user);
const char *act_user_get_primary_session_id (ActUser *user);
+ActGroup **act_user_get_cached_groups (ActUser *user);
gint act_user_collate (ActUser *user1,
ActUser *user2);
@@ -110,6 +114,18 @@ void act_user_set_locked (ActUser *user,
void act_user_set_automatic_login (ActUser *user,
gboolean enabled);
+ActGroup** act_user_find_groups (ActUser *user,
+ gboolean indirect,
+ GError **error);
+void act_user_find_groups_async (ActUser *user,
+ gboolean indirect,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ActGroup** act_user_find_groups_finish (ActUser *user,
+ GAsyncResult *result,
+ GError **error);
+
G_END_DECLS
#endif /* __ACT_USER_H__ */
diff --git a/src/libaccountsservice/act.h b/src/libaccountsservice/act.h
index ccb9b9e..62c8385 100644
--- a/src/libaccountsservice/act.h
+++ b/src/libaccountsservice/act.h
@@ -23,6 +23,7 @@
#include <act/act-user-enum-types.h>
#include <act/act-user.h>
+#include <act/act-group.h>
#include <act/act-user-manager.h>
#endif /* __ACT_H__ */