[gnome-control-center/wip/add-account: 7/7] user-accounts: Implement registering of enterprise logins, bugs
- From: Stefan Walter <stefw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-control-center/wip/add-account: 7/7] user-accounts: Implement registering of enterprise logins, bugs
- Date: Wed, 6 Jun 2012 12:10:06 +0000 (UTC)
commit 4444ed03f4ac5765ee9456bf136c7a69ebfdcf81
Author: Stef Walter <stefw gnome org>
Date: Fri Jun 1 16:36:43 2012 +0200
user-accounts: Implement registering of enterprise logins, bugs
* Validate join domain correctly
* Add UmRealmManager for handling some stuff above the autogenerated
realmd dbus code
* Show a dialog if the user's credentials cannot be used to join
the domain.
* Register the user's login with the AccountsService
* This depends on the CacheUser() method of AccountsService
panels/user-accounts/data/account-dialog.ui | 225 ++++++++++++++++++++
.../user-accounts/data/org.freedesktop.realmd.xml | 30 ++-
panels/user-accounts/um-account-dialog.c | 113 +++++++++-
panels/user-accounts/um-realm-manager.c | 22 ++-
panels/user-accounts/um-user-manager.c | 68 ++++++-
panels/user-accounts/um-user-manager.h | 9 +
6 files changed, 437 insertions(+), 30 deletions(-)
---
diff --git a/panels/user-accounts/data/account-dialog.ui b/panels/user-accounts/data/account-dialog.ui
index 14557fa..7680949 100644
--- a/panels/user-accounts/data/account-dialog.ui
+++ b/panels/user-accounts/data/account-dialog.ui
@@ -1,6 +1,228 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkDialog" id="join-dialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">10</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="type_hint">dialog</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="button1">
+ <property name="label">gtk-cancel</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="button2">
+ <property name="label" translatable="yes">C_ontinue</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">5</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">10</property>
+ <child>
+ <object class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Domain Administrator Login</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ <attribute name="scale" value="1.2"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label12">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="label" translatable="yes">In order to use enterpise logins, this computer needs to be
+enrolled in the domain. Please have your network administrator
+type their domain password here.</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="hexpand">True</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label13">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">_Domain</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">join-domain</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="join-domain">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">5</property>
+ <property name="margin_bottom">5</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label14">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">Administrator _Name</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">join-name</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="join-name">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="invisible_char">â</property>
+ <property name="invisible_char_set">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label15">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">Administrator Password</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">join-password</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="join-password">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="visibility">False</property>
+ <property name="invisible_char">â</property>
+ <property name="activates_default">True</property>
+ <property name="invisible_char_set">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-6">button1</action-widget>
+ <action-widget response="-5">button2</action-widget>
+ </action-widgets>
+ </object>
<object class="GtkBox" id="account-dialog">
<property name="visible">True</property>
<property name="can_focus">False</property>
@@ -181,6 +403,7 @@
<child internal-child="entry">
<object class="GtkEntry" id="local-login-entry">
<property name="can_focus">True</property>
+ <property name="activates_default">True</property>
</object>
</child>
</object>
@@ -333,7 +556,9 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
+ <property name="visibility">False</property>
<property name="invisible_char">â</property>
+ <property name="activates_default">True</property>
<property name="invisible_char_set">True</property>
</object>
<packing>
diff --git a/panels/user-accounts/data/org.freedesktop.realmd.xml b/panels/user-accounts/data/org.freedesktop.realmd.xml
index 364609a..e508489 100644
--- a/panels/user-accounts/data/org.freedesktop.realmd.xml
+++ b/panels/user-accounts/data/org.freedesktop.realmd.xml
@@ -71,14 +71,6 @@
<property name="SuggestedAdministrator" type="s" access="read"/>
<!--
- * The format for user logins when this realm is enrolled.
- * This property may not be valid unless machine is enrolled
- * in this realm. The format contains a %s where the user name
- * goes eg: "DOMAIN\%s"
- -->
- <property name="UserFormat" type="s" access="read"/>
-
- <!--
* Whether the machine is enrolled in this realm or not.
-->
<property name="Enrolled" type="b" access="read"/>
@@ -127,5 +119,27 @@
<arg name="options" type="a{sv}" direction="in"/>
</method>
+ <!--
+ * The format for user logins when this realm is enrolled.
+ * This property may not be valid unless machine is enrolled
+ * in this realm. The format contains a %s where the user name
+ * goes eg: "DOMAIN\%s"
+ -->
+ <property name="LoginFormat" type="s" access="read"/>
+
+ <!--
+ * The list of permitted logins in the LoginFormat style
+ -->
+ <property name="PermittedLogins" type="as" access="read"/>
+
+ <!--
+ * Change the PermittedLogins property. Should take effect
+ * immediately. Some providers may not enforce this :S
+ -->
+ <method name="ChangePermittedLogins">
+ <arg name="add" type="as" direction="in"/>
+ <arg name="remove" type="as" direction="in"/>
+ </method>
+
</interface>
</node>
diff --git a/panels/user-accounts/um-account-dialog.c b/panels/user-accounts/um-account-dialog.c
index a6174e9..8f16a0c 100644
--- a/panels/user-accounts/um-account-dialog.c
+++ b/panels/user-accounts/um-account-dialog.c
@@ -99,6 +99,7 @@ get_account_mode (UmAccountDialog *self)
static void
show_error_dialog (UmAccountDialog *self,
+ const gchar *message,
GError *error)
{
GtkWidget *dialog;
@@ -107,7 +108,7 @@ show_error_dialog (UmAccountDialog *self,
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE,
- _("Failed to join domain"));
+ message);
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
"%s", error->message);
@@ -415,7 +416,7 @@ on_create_local_user (GObject *object,
if (!um_user_manager_create_user_finish (UM_USER_MANAGER (object), res, &user, &error)) {
if (!g_error_matches (error, UM_USER_MANAGER_ERROR, UM_USER_MANAGER_ERROR_PERMISSION_DENIED))
- show_error_dialog (self, error);
+ show_error_dialog (self, _("Failed to create account"), error);
g_error_free (error);
gtk_widget_grab_focus (self->local_name);
@@ -480,14 +481,93 @@ on_manager_realm_added (UmRealmManager *manager,
}
static void
-enterprise_register_user (UmAccountDialog *self)
+on_register_user (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
{
+ UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data);
+ GError *error = NULL;
UmUser *user = NULL;
- /* TODO: And here is where we register/create the user */
+ um_user_manager_cache_user_finish (UM_USER_MANAGER (source),
+ result, &user, &error);
- finish_action (self);
- complete_dialog (self, user);
+ /* This is where we're finally done */
+ if (error == NULL) {
+ finish_action (self);
+ complete_dialog (self, user);
+
+ } else {
+ show_error_dialog (self, _("Failed to register account"), error);
+ g_message ("Couldn't cache user account: %s", error->message);
+ finish_action (self);
+ g_error_free (error);
+ }
+}
+
+static gchar *
+enterprise_calculate_login (UmAccountDialog *self)
+{
+ const gchar *format = um_realm_kerberos_get_login_format (self->selected_realm);
+ return g_strdup_printf (format, gtk_entry_get_text (self->enterprise_login));
+}
+
+static void
+on_permit_user_login (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data);
+ UmUserManager *manager;
+ GError *error = NULL;
+ gchar *login;
+
+ um_realm_kerberos_call_change_permitted_logins_finish (UM_REALM_KERBEROS (source),
+ result, &error);
+ if (error == NULL) {
+
+ /*
+ * Now tell the account service about this user. The account service
+ * should also lookup information about this via the realm and make
+ * sure all that is functional.
+ */
+ manager = um_user_manager_ref_default ();
+ login = enterprise_calculate_login (self);
+ um_user_manager_cache_user (manager, login, on_register_user,
+ g_object_ref (self), g_object_unref);
+
+ g_free (login);
+ g_object_unref (manager);
+
+ } else {
+ show_error_dialog (self, _("Failed to register account"), error);
+ g_message ("Couldn't permit logins on account: %s", error->message);
+ finish_action (self);
+ }
+
+ g_object_unref (self);
+}
+
+static void
+enterprise_permit_user_login (UmAccountDialog *self)
+{
+ gchar *login;
+ const gchar *add[2];
+ const gchar *remove[1];
+
+ login = enterprise_calculate_login (self);
+
+ add[0] = login;
+ add[1] = NULL;
+ remove[0] = NULL;
+
+ um_realm_kerberos_call_change_permitted_logins (self->selected_realm,
+ add, remove,
+ self->cancellable,
+ on_permit_user_login,
+ g_object_ref (self));
+
+ g_free (login);
}
static void
@@ -572,6 +652,7 @@ on_join_login (GObject *source,
/* Couldn't login as admin, show prompt again */
} else {
join_show_prompt (self, error);
+ g_message ("Couldn't log in as admin to join domain: %s", error->message);
g_error_free (error);
}
@@ -604,17 +685,19 @@ on_realm_joined (GObject *source,
/* Yay, joined the domain, register the user locally */
if (error == NULL) {
- enterprise_register_user (self);
+ enterprise_permit_user_login (self);
/* Credential failure while joining domain, prompt for admin creds */
} else if (g_error_matches (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_LOGIN) ||
g_error_matches (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_PASSWORD)) {
- join_show_prompt (self, NULL);
+ join_show_prompt (self, error);
/* Other failure */
} else {
- show_error_dialog (self, error);
+ show_error_dialog (self, _("Failed to join domain"), error);
+ g_message ("Failed to join the domain: %s", error->message);
finish_action (self);
+ g_error_free (error);
}
g_object_unref (self);
@@ -634,7 +717,7 @@ on_realm_login (GObject *source,
/* Already joined to the domain, just register this user */
if (um_realm_kerberos_get_enrolled (self->selected_realm)) {
- enterprise_register_user (self);
+ enterprise_permit_user_login (self);
/* Join the domain, try using the user's creds */
} else {
@@ -650,12 +733,18 @@ on_realm_login (GObject *source,
/* A problem with the user's login name or password */
} else if (g_error_matches (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_LOGIN)) {
set_entry_validation_error (self->enterprise_login, error->message);
+ finish_action (self);
+ gtk_widget_grab_focus (GTK_WIDGET (self->enterprise_login));
+
} else if (g_error_matches (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_PASSWORD)) {
set_entry_validation_error (self->enterprise_password, error->message);
+ finish_action (self);
+ gtk_widget_grab_focus (GTK_WIDGET (self->enterprise_password));
/* Other login failure */
} else {
- show_error_dialog (self, error);
+ show_error_dialog (self, _("Failed to log into domain"), error);
+ g_message ("Couldn't log in as user: %s", error->message);
finish_action (self);
}
@@ -700,6 +789,7 @@ on_realm_discover_input (GObject *source,
/* The domain is likely invalid*/
} else {
finish_action (self);
+ g_message ("Couldn't discover domain: %s", error->message);
gtk_widget_grab_focus (GTK_WIDGET (self->enterprise_domain_entry));
set_entry_validation_error (self->enterprise_domain_entry,
error->message);
@@ -834,6 +924,7 @@ enterprise_construct (UmAccountDialog *self,
gtk_combo_box_set_model (self->enterprise_domain,
GTK_TREE_MODEL (self->enterprise_realms));
gtk_combo_box_set_entry_text_column (self->enterprise_domain, 0);
+ self->enterprise_domain_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (widget)));
widget = (GtkWidget *) gtk_builder_get_object (builder, "enterprise-login");
g_signal_connect (widget, "changed", G_CALLBACK (on_entry_changed), self);
diff --git a/panels/user-accounts/um-realm-manager.c b/panels/user-accounts/um-realm-manager.c
index 9f6f949..79daa74 100644
--- a/panels/user-accounts/um-realm-manager.c
+++ b/panels/user-accounts/um-realm-manager.c
@@ -142,9 +142,11 @@ um_realm_manager_load (UmRealmManager *self,
const gchar *iface;
const gchar *name;
+ g_return_if_fail (realms != NULL);
+
async = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
um_realm_manager_load);
- load = g_slice_new (LoadClosure);
+ load = g_slice_new0 (LoadClosure);
load->manager = g_object_ref (self);
g_simple_async_result_set_op_res_gpointer (async, load, load_closure_free);
@@ -194,10 +196,18 @@ um_realm_manager_load_finish (UmRealmManager *self,
return realms;
}
+static guint
+hash_realm_info (gconstpointer value)
+{
+ const gchar *name, *path, *iface;
+ g_variant_get ((GVariant *)value, "(&s&o&s)", &name, &path, &iface);
+ return g_str_hash (name) ^ g_str_hash (path) ^ g_str_hash (iface);
+}
+
static void
um_realm_manager_init (UmRealmManager *self)
{
- self->realms = g_hash_table_new_full (g_variant_hash, g_variant_equal,
+ self->realms = g_hash_table_new_full (hash_realm_info, g_variant_equal,
(GDestroyNotify)g_variant_unref,
g_object_unref);
}
@@ -209,11 +219,13 @@ um_realm_manager_notify (GObject *obj,
UmRealmManager *self = UM_REALM_MANAGER (obj);
GVariant *realms;
- G_OBJECT_CLASS (um_realm_manager_parent_class)->notify (obj, spec);
+ if (G_OBJECT_CLASS (um_realm_manager_parent_class)->notify)
+ G_OBJECT_CLASS (um_realm_manager_parent_class)->notify (obj, spec);
if (g_str_equal (spec->name, "realms")) {
realms = um_realm_provider_get_realms (UM_REALM_PROVIDER (self));
- um_realm_manager_load (self, realms, NULL, NULL, NULL);
+ if (realms != NULL)
+ um_realm_manager_load (self, realms, NULL, NULL, NULL);
}
}
@@ -605,8 +617,6 @@ kinit_thread_func (GSimpleAsyncResult *async,
if (k5)
krb5_free_context (k5);
-
- g_simple_async_result_complete_in_idle (async);
}
void
diff --git a/panels/user-accounts/um-user-manager.c b/panels/user-accounts/um-user-manager.c
index d59b263..4ba9fbd 100644
--- a/panels/user-accounts/um-user-manager.c
+++ b/panels/user-accounts/um-user-manager.c
@@ -427,10 +427,11 @@ async_user_op_data_free (gpointer d)
g_free (data);
}
+/* Used for both create_user and cache_user */
static void
-create_user_done (GObject *proxy,
- GAsyncResult *r,
- gpointer user_data)
+user_call_done (GObject *proxy,
+ GAsyncResult *r,
+ gpointer user_data)
{
AsyncUserOpData *data = user_data;
GSimpleAsyncResult *res;
@@ -455,7 +456,7 @@ create_user_done (GObject *proxy,
UM_USER_MANAGER_ERROR_PERMISSION_DENIED,
"Not authorized");
}
- if (g_dbus_error_is_remote_error (error) &&
+ else if (g_dbus_error_is_remote_error (error) &&
strcmp (g_dbus_error_get_remote_error(error), "org.freedesktop.Accounts.Error.UserExists") == 0) {
g_simple_async_result_set_error (res,
UM_USER_MANAGER_ERROR,
@@ -463,6 +464,14 @@ create_user_done (GObject *proxy,
_("A user with name '%s' already exists."),
data->user_name);
}
+ else if (g_dbus_error_is_remote_error (error) &&
+ strcmp (g_dbus_error_get_remote_error(error), "org.freedesktop.Accounts.Error.UserDoesNotExist") == 0) {
+ g_simple_async_result_set_error (res,
+ UM_USER_MANAGER_ERROR,
+ UM_USER_MANAGER_ERROR_USER_DOES_NOT_EXIST,
+ _("No user with the name '%s' exists."),
+ data->user_name);
+ }
else {
g_simple_async_result_set_from_error (res, error);
}
@@ -534,7 +543,56 @@ um_user_manager_create_user (UmUserManager *manager,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
- create_user_done,
+ user_call_done,
+ data);
+}
+
+gboolean
+um_user_manager_cache_user_finish (UmUserManager *manager,
+ GAsyncResult *result,
+ UmUser **user,
+ GError **error)
+{
+ gchar *path;
+ GSimpleAsyncResult *res;
+
+ res = G_SIMPLE_ASYNC_RESULT (result);
+
+ *user = NULL;
+
+ if (g_simple_async_result_propagate_error (res, error)) {
+ return FALSE;
+ }
+
+ path = g_simple_async_result_get_op_res_gpointer (res);
+ *user = g_hash_table_lookup (manager->user_by_object_path, path);
+
+ return TRUE;
+}
+
+void
+um_user_manager_cache_user (UmUserManager *manager,
+ const char *user_name,
+ GAsyncReadyCallback done,
+ gpointer done_data,
+ GDestroyNotify destroy)
+{
+ AsyncUserOpData *data;
+
+ data = g_new0 (AsyncUserOpData, 1);
+ data->manager = g_object_ref (manager);
+ data->user_name = g_strdup (user_name);
+ data->callback = done;
+ data->data = done_data;
+ data->destroy = destroy;
+
+ g_dbus_proxy_call (manager->proxy,
+ "CacheUser",
+ g_variant_new ("(s)", user_name),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ user_call_done,
data);
}
diff --git a/panels/user-accounts/um-user-manager.h b/panels/user-accounts/um-user-manager.h
index abf6e42..ce27023 100644
--- a/panels/user-accounts/um-user-manager.h
+++ b/panels/user-accounts/um-user-manager.h
@@ -95,6 +95,15 @@ gboolean um_user_manager_create_user_finish (UmUserManager *m
GAsyncResult *result,
UmUser **user,
GError **error);
+void um_user_manager_cache_user (UmUserManager *manager,
+ const char *user_name,
+ GAsyncReadyCallback done,
+ gpointer user_data,
+ GDestroyNotify destroy);
+gboolean um_user_manager_cache_user_finish (UmUserManager *manager,
+ GAsyncResult *result,
+ UmUser **user,
+ GError **error);
void um_user_manager_delete_user (UmUserManager *manager,
UmUser *user,
gboolean remove_files,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]