[gnome-control-center/wip/add-account: 7/7] user-accounts: Implement registering of enterprise logins, bugs



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]