[gnome-control-center/wip/add-account: 15/15] user-accounts: Implement enterprise logins in add dialog



commit 5913dfb107875aa31946a9522ac2338a3ff54c3c
Author: Stef Walter <stefw gnome org>
Date:   Thu Jun 7 00:58:27 2012 +0200

    user-accounts: Implement enterprise logins in add dialog
    
     * Use realmd for domain joining and lookup, runtime dependency
     * 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. Prompt for admin creds.
     * Register the user's login with the AccountsService
     * This depends on the CacheUser() method of AccountsService
    
    https://bugzilla.gnome.org/show_bug.cgi?id=677548

 configure.ac                                       |   17 +
 panels/user-accounts/Makefile.am                   |   19 +-
 panels/user-accounts/data/Makefile.am              |    1 +
 panels/user-accounts/data/account-dialog.ui        |  359 ++++++++++-
 .../user-accounts/data/org.freedesktop.realmd.xml  |  145 +++++
 panels/user-accounts/um-account-dialog.c           |  588 +++++++++++++++++-
 panels/user-accounts/um-realm-manager.c            |  678 ++++++++++++++++++++
 panels/user-accounts/um-realm-manager.h            |   96 +++
 8 files changed, 1884 insertions(+), 19 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index a97dfcd..7c90be6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -225,6 +225,23 @@ PKG_CHECK_MODULES(ISOCODES, iso-codes)
 AC_DEFINE_UNQUOTED([ISO_CODES_PREFIX],["`$PKG_CONFIG --variable=prefix iso-codes`"],[ISO codes prefix])
 ISO_CODES=iso-codes
 
+# Kerberos kerberos support
+AC_PATH_PROG(KRB5_CONFIG, krb5-config)
+AC_MSG_CHECKING(for working krb5-config)
+if test -x "$KRB5_CONFIG"; then
+	KRB5_CFLAGS="`$KRB5_CONFIG --cflags`"
+	KRB5_LIBS="`$KRB5_CONFIG --libs`"
+	AC_MSG_RESULT(yes)
+elif test x$KRB5_PASSED_LIBS = x; then
+	AC_MSG_ERROR(no. Please install MIT kerberos devel package)
+fi
+
+AC_SUBST(KRB5_CFLAGS)
+AC_SUBST(KRB5_LIBS)
+
+USER_ACCOUNTS_PANEL_CFLAGS="$USER_ACCOUNTS_PANEL_CFLAGS $KRB5_CFLAGS"
+USER_ACCOUNTS_PANEL_LIBS="$USER_ACCOUNTS_PANEL_LIBS $KRB5_LIBS"
+
 dnl ==============================================
 dnl End: Check that we meet the  dependencies
 dnl ==============================================
diff --git a/panels/user-accounts/Makefile.am b/panels/user-accounts/Makefile.am
index 83d1a07..8a7da5d 100644
--- a/panels/user-accounts/Makefile.am
+++ b/panels/user-accounts/Makefile.am
@@ -23,6 +23,10 @@ if BUILD_CHEESE
 AM_CPPFLAGS += $(CHEESE_CFLAGS)
 endif
 
+BUILT_SOURCES = \
+       um-realm-generated.c \
+       um-realm-generated.h
+
 libuser_accounts_la_SOURCES =		\
 	um-account-type.h		\
 	um-account-type.c 		\
@@ -53,7 +57,10 @@ libuser_accounts_la_SOURCES =		\
 	um-editable-combo.c		\
 	um-user-panel.h 		\
 	um-user-panel.c			\
-	um-user-module.c
+	um-user-module.c		\
+	um-realm-manager.c		\
+	um-realm-manager.h		\
+	$(BUILT_SOURCES)
 
 libuser_accounts_la_LIBADD = 		\
 	$(PANEL_LIBS)			\
@@ -69,18 +76,26 @@ endif
 
 libuser_accounts_la_LDFLAGS = $(PANEL_LDFLAGS)
 
+um-realm-generated.c: $(srcdir)/data/org.freedesktop.realmd.xml
+	$(AM_V_GEN) gdbus-codegen --interface-prefix org.freedesktop.realmd. \
+		--generate-c-code um-realm-generated --c-namespace UmRealm $<
+um-realm-generated.h: um-realm-generated.c
+
 noinst_PROGRAMS = frob-account-dialog
 
 frob_account_dialog_SOURCES = \
 	frob-account-dialog.c \
 	um-account-dialog.h \
 	um-account-dialog.c \
+	um-realm-manager.c \
+	um-realm-manager.h \
 	um-user.h \
 	um-user.c \
 	um-user-manager.c \
 	um-user-manager.h \
 	um-utils.h \
-	um-utils.c
+	um-utils.c \
+	$(BUILT_SOURCES)
 
 frob_account_dialog_LDADD = \
 	$(libuser_accounts_la_LIBADD)
diff --git a/panels/user-accounts/data/Makefile.am b/panels/user-accounts/data/Makefile.am
index dbc17c6..d03d14b 100644
--- a/panels/user-accounts/data/Makefile.am
+++ b/panels/user-accounts/data/Makefile.am
@@ -16,6 +16,7 @@ desktop_DATA = $(Desktop_in_files:.desktop.in=.desktop)
 
 EXTRA_DIST = 					\
 	gnome-user-accounts-panel.desktop.in.in	\
+	org.freedesktop.realmd.xml		\
 	$(ui_DATA)
 
 CLEANFILES =					\
diff --git a/panels/user-accounts/data/account-dialog.ui b/panels/user-accounts/data/account-dialog.ui
index 089b32e..2c47908 100644
--- a/panels/user-accounts/data/account-dialog.ui
+++ b/panels/user-accounts/data/account-dialog.ui
@@ -205,29 +205,380 @@
             <child>
               <object class="GtkGrid" id="enterprise-area">
                 <property name="visible">True</property>
+                <property name="can_focus">False</property>
                 <property name="hexpand">True</property>
-                <property name="column_spacing">10</property>
                 <property name="row_spacing">10</property>
+                <property name="column_spacing">10</property>
+                <child>
+                  <object class="GtkLabel" id="label4">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="margin_top">5</property>
+                    <property name="xalign">1</property>
+                    <property name="yalign">0</property>
+                    <property name="label" translatable="yes">_Domain</property>
+                    <property name="use_underline">True</property>
+                    <property name="mnemonic_widget">enterprise-domain</property>
+                    <style>
+                      <class name="dim-label"/>
+                    </style>
+                  </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="label8">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="xalign">1</property>
+                    <property name="label" translatable="yes">_Login Name</property>
+                    <property name="use_underline">True</property>
+                    <property name="mnemonic_widget">enterprise-login</property>
+                    <style>
+                      <class name="dim-label"/>
+                    </style>
+                  </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="GtkLabel" id="label61">
+                  <object class="GtkLabel" id="label9">
                     <property name="visible">True</property>
+                    <property name="can_focus">False</property>
                     <property name="xalign">1</property>
-                    <property name="label" translatable="yes">Enterprise Widgets</property>
+                    <property name="label" translatable="yes">_Password</property>
                     <property name="use_underline">True</property>
+                    <property name="mnemonic_widget">enterprise-password</property>
                     <style>
                       <class name="dim-label"/>
                     </style>
                   </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="GtkBox" id="box1">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="orientation">vertical</property>
+                    <property name="spacing">3</property>
+                    <child>
+                      <object class="GtkComboBox" id="enterprise-domain">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="hexpand">True</property>
+                        <property name="has_entry">True</property>
+                        <property name="entry_text_column">0</property>
+                        <child internal-child="entry">
+                          <object class="GtkEntry" id="combobox-entry">
+                            <property name="can_focus">True</property>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label10">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">Tip: Enterprise domain or realm name</property>
+                        <attributes>
+                          <attribute name="scale" value="0.8"/>
+                        </attributes>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                      </packing>
+                    </child>
+                  </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="GtkEntry" id="enterprise-login">
+                    <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="GtkEntry" id="enterprise-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>
+            </child>
+          </object>
+  <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">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="label71">
+                <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>
+                    <style>
+                      <class name="dim-label"/>
+                    </style>
+                  </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>
+                    <style>
+                      <class name="dim-label"/>
+                    </style>
+                  </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>
+                    <style>
+                      <class name="dim-label"/>
+                    </style>
+                  </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>
 </interface>
diff --git a/panels/user-accounts/data/org.freedesktop.realmd.xml b/panels/user-accounts/data/org.freedesktop.realmd.xml
new file mode 100644
index 0000000..e508489
--- /dev/null
+++ b/panels/user-accounts/data/org.freedesktop.realmd.xml
@@ -0,0 +1,145 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd";>
+
+<node name="/">
+	<!--
+	 * Global interface implemented by realmd. Allows listing of providers
+	 * and discovering which one is relevant for a given domain.
+	 *
+	 * This is implemented by individual providers, but is aggregated
+	 * globally at the system bus name 'org.freedesktop.realmd' with the
+	 * object path '/org/freedesktop/realmd'
+	-->
+	<interface name="org.freedesktop.realmd.Provider">
+
+		<!--
+		 * A list of known, enrolled or discovered realms.
+		 * Each realm is a DBus object and is represeted by a:
+		 *   s: DBus bus name of the realm
+		 *   o: DBus object path of the realm
+		 *   s: DBus interface name, like 'ofr.Kerberos' (below)
+		-->
+		<property name="Realms" type="a(sos)" access="read"/>
+
+		<!--
+		 * Discover whether a string represents a realm that a provider
+		 * can enroll or otherwise use.
+		-->
+		<method name="Discover">
+			<!-- The input string -->
+			<arg name="string" type="s" direction="in"/>
+
+			<!-- Returned match relevance -->
+			<arg name="relevance" type="i" direction="out"/>
+
+			<!-- The realm objects: bus name, object path, interface -->
+			<arg name="realm" type="a(sos)" direction="out"/>
+		</method>
+	</interface>
+
+	<!--
+	 * This interface is implemented by Providers and Realms to provide
+	 * additional information when an long running operation is happening
+	 *
+	 * In particular you can connect to the "Diagnostics" signal when
+	 * during an enroll or unenroll to get details.
+	-->
+	<interface name="org.freedesktop.realmd.Diagnostics">
+		<signal name="Diagnostics">
+			<arg name="data" type="s"/>
+		</signal>
+	</interface>
+
+	<!--
+	 * This interface is implemented by Kerberos realms.
+	-->
+	<interface name="org.freedesktop.realmd.Kerberos">
+
+		<!--
+		 * The kerberos realm name. Usually capitalized.
+		-->
+		<property name="Name" type="s" access="read"/>
+
+		<!--
+		 * The DNS domain name for this realm.
+		-->
+		<property name="Domain" type="s" access="read"/>
+
+		<!--
+		 * The suggested Administrator login name for this realm
+		-->
+		<property name="SuggestedAdministrator" type="s" access="read"/>
+
+		<!--
+		 * Whether the machine is enrolled in this realm or not.
+		-->
+		<property name="Enrolled" type="b" access="read"/>
+
+		<!--
+		 * Enroll the machine in this realm using an administrative
+		 * account and a password.
+		-->
+		<method name="EnrollWithPassword">
+			<arg name="principal" type="s" direction="in"/>
+			<arg name="password" type="s" direction="in"/>
+			<arg name="options" type="a{sv}" direction="in"/>
+		</method>
+
+		<!--
+		 * Enroll the machine in this realm using kerberos cached
+		 * administrative credentials.
+		-->
+		<method name="EnrollWithCredentialCache">
+			<!-- The contents of a kerberos cache file containing administrative credentials -->
+			<arg name="kerberos_cache" type="ay" direction="in">
+				<annotation name="org.gtk.GDBus.C.ForceGVariant" value="yup"/>
+			</arg>
+			<arg name="options" type="a{sv}" direction="in"/>
+		</method>
+
+		<!--
+		 * Unenroll the machine from this realm using an administrative
+		 * account and a password.
+		-->
+		<method name="UnenrollWithPassword">
+			<arg name="principal" type="s" direction="in"/>
+			<arg name="password" type="s" direction="in"/>
+			<arg name="options" type="a{sv}" direction="in"/>
+		</method>
+
+		<!--
+		 * Unenroll the machine from this realm using a kerberos cached
+		 * administrative credentials.
+		-->
+		<method name="UnenrollWithCredentialCache">
+			<!-- The contents of a kerberos cache file containing administrative credentials -->
+			<arg name="kerberos_cache" type="ay" direction="in">
+				<annotation name="org.gtk.GDBus.C.ForceGVariant" value="yup"/>
+			</arg>
+			<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 65ee1a3..84b53ea 100644
--- a/panels/user-accounts/um-account-dialog.c
+++ b/panels/user-accounts/um-account-dialog.c
@@ -26,23 +26,35 @@
 #include <gtk/gtk.h>
 
 #include "um-account-dialog.h"
+#include "um-realm-manager.h"
 #include "um-user-manager.h"
 #include "um-utils.h"
 
+typedef enum {
+        UM_LOCAL,
+        UM_ENTERPRISE,
+        NUM_MODES
+} UmAccountMode;
+
+static void   switcher_switch     (UmAccountDialog *self,
+                                   UmAccountMode mode);
+
 static void   dialog_validate     (UmAccountDialog *self);
 
+static void   on_join_login       (GObject *source,
+                                   GAsyncResult *result,
+                                   gpointer user_data);
+
+static void   on_realm_joined     (GObject *source,
+                                   GAsyncResult *result,
+                                   gpointer user_data);
+
 #define UM_ACCOUNT_DIALOG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), UM_TYPE_ACCOUNT_DIALOG, \
                                                                     UmAccountDialogClass))
 #define UM_IS_ACCOUNT_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), UM_TYPE_ACCOUNT_DIALOG))
 #define UM_ACCOUNT_DIALOG_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), UM_TYPE_ACCOUNT_DIALOG, \
                                                                       UmAccountDialogClass))
 
-typedef enum {
-        UM_LOCAL,
-        UM_ENTERPRISE,
-        NUM_MODES
-} UmAccountMode;
-
 struct _UmAccountDialog {
         GtkDialog parent;
         GtkWidget *widgets;
@@ -63,6 +75,22 @@ struct _UmAccountDialog {
         GtkWidget *local_account_type;
 
         /* Enterprise widgets */
+        guint realmd_watch;
+        GtkWidget *enterprise_button;
+        GtkListStore *enterprise_realms;
+        GtkComboBox *enterprise_domain;
+        GtkEntry *enterprise_domain_entry;
+        gboolean enterprise_domain_chosen;
+        GtkEntry *enterprise_login;
+        GtkEntry *enterprise_password;
+        UmRealmManager *realm_manager;
+        UmRealmKerberos *selected_realm;
+
+        /* Join credential dialog */
+        GtkDialog *join_dialog;
+        GtkLabel *join_domain;
+        GtkEntry *join_name;
+        GtkEntry *join_password;
 };
 
 typedef struct {
@@ -230,8 +258,8 @@ on_name_changed (GtkEditable *editable,
 }
 
 static void
-local_area_init (UmAccountDialog *self,
-                 GtkBuilder *builder)
+local_init (UmAccountDialog *self,
+            GtkBuilder *builder)
 {
         GtkWidget *widget;
 
@@ -260,6 +288,526 @@ local_prepare (UmAccountDialog *self)
         gtk_combo_box_set_active (GTK_COMBO_BOX (self->local_account_type), 0);
 }
 
+static gboolean
+enterprise_validate (UmAccountDialog *self)
+{
+        const gchar *name;
+        gboolean valid_name;
+        gboolean valid_domain;
+        GtkTreeIter iter;
+
+        name = gtk_entry_get_text (GTK_ENTRY (self->enterprise_login));
+        valid_name = is_valid_name (name);
+
+        if (gtk_combo_box_get_active_iter (self->enterprise_domain, &iter)) {
+                gtk_tree_model_get (gtk_combo_box_get_model (self->enterprise_domain),
+                                    &iter, 0, &name, -1);
+        } else {
+                name = gtk_entry_get_text (self->enterprise_domain_entry);
+        }
+
+        valid_domain = is_valid_name (name);
+        return valid_name && valid_domain;
+}
+
+static void
+enterprise_add_realm (UmAccountDialog *self,
+                      UmRealmKerberos *realm)
+{
+        GtkTreeIter iter;
+
+        gtk_list_store_append (self->enterprise_realms, &iter);
+        gtk_list_store_set (self->enterprise_realms, &iter,
+                            0, um_realm_kerberos_get_domain (realm),
+                            1, realm,
+                            -1);
+
+        /* Select the domain if appropriate */
+        if (!self->enterprise_domain_chosen && um_realm_kerberos_get_enrolled (realm))
+                gtk_combo_box_set_active_iter (self->enterprise_domain, &iter);
+}
+
+static void
+on_manager_realm_added (UmRealmManager  *manager,
+                        UmRealmKerberos *realm,
+                        gpointer         user_data)
+{
+        UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data);
+        enterprise_add_realm (self, realm);
+}
+
+
+static void
+on_register_user (GObject *source,
+                  GAsyncResult *result,
+                  gpointer user_data)
+{
+        UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data);
+        GError *error = NULL;
+        UmUser *user = NULL;
+
+        um_user_manager_cache_user_finish (UM_USER_MANAGER (source),
+                                           result, &user, &error);
+
+        /* 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, self->cancellable,
+                                            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
+on_join_response (GtkDialog *dialog,
+                  gint response,
+                  gpointer user_data)
+{
+        UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data);
+
+        gtk_widget_hide (GTK_WIDGET (dialog));
+        if (response != GTK_RESPONSE_OK) {
+                finish_action (self);
+                return;
+        }
+
+        /* Prompted for some admin credentials, try to use them to log in */
+        um_realm_login (um_realm_kerberos_get_name (self->selected_realm),
+                        um_realm_kerberos_get_domain (self->selected_realm),
+                        gtk_entry_get_text (self->join_name),
+                        gtk_entry_get_text (self->join_password),
+                        UM_REALM_LOGIN_TO_CREDENTIALS,
+                        self->cancellable,
+                        on_join_login,
+                        g_object_ref (self));
+}
+
+static void
+join_show_prompt (UmAccountDialog *self,
+                  GError *error)
+{
+        const gchar *name;
+
+        gtk_entry_set_text (self->join_password, "");
+        gtk_widget_grab_focus (GTK_WIDGET (self->join_password));
+
+        gtk_label_set_text (self->join_domain,
+                            um_realm_kerberos_get_domain (self->selected_realm));
+
+        clear_entry_validation_error (self->join_name);
+        clear_entry_validation_error (self->join_password);
+
+        if (error == NULL) {
+                name = um_realm_kerberos_get_suggested_administrator (self->selected_realm);
+                if (name && !g_str_equal (name, "")) {
+                        gtk_entry_set_text (self->join_name, name);
+                } else {
+                        gtk_widget_grab_focus (GTK_WIDGET (self->join_name));
+                }
+
+        } else if (g_error_matches (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_PASSWORD)) {
+                set_entry_validation_error (self->join_password, error->message);
+
+        } else {
+                set_entry_validation_error (self->join_name, error->message);
+        }
+
+        gtk_window_set_transient_for (GTK_WINDOW (self->join_dialog), GTK_WINDOW (self));
+        gtk_window_set_modal (GTK_WINDOW (self->join_dialog), TRUE);
+        gtk_window_present (GTK_WINDOW (self->join_dialog));
+
+        /* And now we wait for on_join_response() */
+}
+
+static void
+on_join_login (GObject *source,
+               GAsyncResult *result,
+               gpointer user_data)
+{
+        UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data);
+        GError *error = NULL;
+        GBytes *creds;
+
+        um_realm_login_finish (result, &creds, &error);
+
+        /* Logged in as admin successfully, use creds to join domain */
+        if (error == NULL) {
+                um_realm_join (self->selected_realm,
+                               creds, self->cancellable, on_realm_joined,
+                               g_object_ref (self));
+                g_bytes_unref (creds);
+
+        /* 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);
+        }
+
+        g_object_unref (self);
+}
+
+static void
+join_init (UmAccountDialog *self,
+           GtkBuilder *builder)
+{
+        self->join_dialog = GTK_DIALOG (gtk_builder_get_object (builder, "join-dialog"));
+        self->join_domain = GTK_LABEL (gtk_builder_get_object (builder, "join-domain"));
+        self->join_name = GTK_ENTRY (gtk_builder_get_object (builder, "join-name"));
+        self->join_password = GTK_ENTRY (gtk_builder_get_object (builder, "join-password"));
+
+        g_signal_connect (self->join_dialog, "response",
+                          G_CALLBACK (on_join_response), self);
+}
+
+static void
+on_realm_joined (GObject *source,
+                 GAsyncResult *result,
+                 gpointer user_data)
+{
+        UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data);
+        GError *error = NULL;
+
+        um_realm_join_finish (self->selected_realm,
+                              result, &error);
+
+        /* Yay, joined the domain, register the user locally */
+        if (error == NULL) {
+                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, error);
+
+        /* Other failure */
+        } else {
+                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);
+}
+
+static void
+on_realm_login (GObject *source,
+                GAsyncResult *result,
+                gpointer user_data)
+{
+        UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data);
+        GError *error = NULL;
+        GBytes *creds;
+
+        um_realm_login_finish (result, &creds, &error);
+        if (error == NULL) {
+
+                /* Already joined to the domain, just register this user */
+                if (um_realm_kerberos_get_enrolled (self->selected_realm)) {
+                        enterprise_permit_user_login (self);
+
+                /* Join the domain, try using the user's creds */
+                } else {
+                        um_realm_join (self->selected_realm,
+                                       creds,
+                                       self->cancellable,
+                                       on_realm_joined,
+                                       g_object_ref (self));
+                }
+
+                g_bytes_unref (creds);
+
+        /* 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, _("Failed to log into domain"), error);
+                g_message ("Couldn't log in as user: %s", error->message);
+                finish_action (self);
+        }
+
+        g_clear_error (&error);
+        g_object_unref (self);
+}
+
+static void
+enterprise_check_login (UmAccountDialog *self)
+{
+        g_assert (self->selected_realm);
+
+        um_realm_login (um_realm_kerberos_get_name (self->selected_realm),
+                        um_realm_kerberos_get_domain (self->selected_realm),
+                        gtk_entry_get_text (self->enterprise_login),
+                        gtk_entry_get_text (self->enterprise_password),
+                        UM_REALM_LOGIN_TO_CREDENTIALS,
+                        self->cancellable,
+                        on_realm_login,
+                        g_object_ref (self));
+}
+
+static void
+on_realm_discover_input (GObject *source,
+                         GAsyncResult *result,
+                         gpointer user_data)
+{
+        UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data);
+        GError *error = NULL;
+        GList *realms;
+
+        realms = um_realm_manager_discover_finish (self->realm_manager,
+                                                   result, &error);
+
+        /* Found a realm, log user into domain */
+        if (error == NULL) {
+                g_assert (realms != NULL);
+                self->selected_realm = g_object_ref (realms->data);
+                enterprise_check_login (self);
+                g_list_free_full (realms, g_object_unref);
+
+        /* 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);
+                g_error_free (error);
+        }
+
+        g_object_unref (self);
+}
+
+static void
+enterprise_add_user (UmAccountDialog *self)
+{
+        GtkTreeIter iter;
+
+        begin_action (self);
+        g_clear_object (&self->selected_realm);
+
+        /* Already know about this realm, try to login as user */
+        if (gtk_combo_box_get_active_iter (self->enterprise_domain, &iter)) {
+                gtk_tree_model_get (gtk_combo_box_get_model (self->enterprise_domain),
+                                    &iter, 1, &self->selected_realm, -1);
+                enterprise_check_login (self);
+
+        /* Something the user typed, we need to discover realm */
+        } else {
+                um_realm_manager_discover (self->realm_manager,
+                                           gtk_entry_get_text (self->enterprise_domain_entry),
+                                           self->cancellable,
+                                           on_realm_discover_input,
+                                           g_object_ref (self));
+        }
+}
+
+static void
+on_realm_manager_created (GObject *source,
+                           GAsyncResult *result,
+                           gpointer user_data)
+{
+        UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data);
+        GError *error = NULL;
+        GList *realms, *l;
+
+        g_clear_object (&self->realm_manager);
+
+        self->realm_manager = um_realm_manager_new_finish (result, &error);
+        if (error != NULL) {
+                g_warning ("Couldn't contact realmd service: %s", error->message);
+                g_error_free (error);
+                return;
+        }
+
+        /* Lookup all the realm objects */
+        realms = um_realm_manager_get_realms (self->realm_manager);
+        for (l = realms; l != NULL; l = g_list_next (l))
+                enterprise_add_realm (self, l->data);
+        g_list_free (realms);
+        g_signal_connect (self->realm_manager, "realm-added",
+                          G_CALLBACK (on_manager_realm_added), self);
+
+        /* When no realms try to discover a sensible default, triggers realm-added signal */
+        um_realm_manager_discover (self->realm_manager, "", self->cancellable,
+                                   NULL, NULL);
+
+        /* Show the 'Enterprise Login' stuff, and update switcher */
+        gtk_widget_show (self->enterprise_button);
+        switcher_switch (self, self->mode);
+}
+
+static void
+on_realmd_appeared (GDBusConnection *connection,
+                    const gchar *name,
+                    const gchar *name_owner,
+                    gpointer user_data)
+{
+        UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data);
+        um_realm_manager_new (self->cancellable, on_realm_manager_created, self);
+}
+
+static void
+on_realmd_disappeared (GDBusConnection *unused1,
+                       const gchar *unused2,
+                       gpointer user_data)
+{
+        UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data);
+
+        if (self->realm_manager) {
+                g_signal_handlers_disconnect_by_func (self->realm_manager,
+                                                      on_manager_realm_added,
+                                                      self);
+                g_object_unref (self->realm_manager);
+                self->realm_manager = NULL;
+        }
+
+        gtk_list_store_clear (self->enterprise_realms);
+        gtk_widget_hide (self->enterprise_button);
+        switcher_switch (self, UM_LOCAL);
+}
+
+static void
+on_domain_changed (GtkComboBox *widget,
+                   gpointer user_data)
+{
+        UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data);
+
+        dialog_validate (self);
+        self->enterprise_domain_chosen = TRUE;
+        clear_entry_validation_error (self->enterprise_domain_entry);
+}
+
+static void
+on_entry_changed (GtkEditable *editable,
+                  gpointer user_data)
+{
+        UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data);
+        dialog_validate (self);
+        clear_entry_validation_error (GTK_ENTRY (editable));
+}
+
+static void
+enterprise_init (UmAccountDialog *self,
+                 GtkBuilder *builder)
+{
+        GtkWidget *widget;
+
+        self->enterprise_realms = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_OBJECT);
+
+        widget = (GtkWidget *) gtk_builder_get_object (builder, "enterprise-domain");
+        g_signal_connect (widget, "changed", G_CALLBACK (on_domain_changed), self);
+        self->enterprise_domain = GTK_COMBO_BOX (widget);
+        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);
+        self->enterprise_login = GTK_ENTRY (widget);
+
+        widget = (GtkWidget *) gtk_builder_get_object (builder, "enterprise-password");
+        g_signal_connect (widget, "changed", G_CALLBACK (on_entry_changed), self);
+        self->enterprise_password = GTK_ENTRY (widget);
+
+        /* Initially we hide the 'Enterprise Login' stuff */
+        widget = (GtkWidget *) gtk_builder_get_object (builder, "enterprise-button");
+        self->enterprise_button = widget;
+        gtk_widget_hide (widget);
+
+        self->realmd_watch = g_bus_watch_name (G_BUS_TYPE_SYSTEM, "org.freedesktop.realmd",
+                                               G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
+                                               on_realmd_appeared, on_realmd_disappeared,
+                                               self, NULL);
+}
+
+static void
+enterprise_prepare (UmAccountDialog *self)
+{
+        gtk_entry_set_text (GTK_ENTRY (self->enterprise_login), "");
+        gtk_entry_set_text (GTK_ENTRY (self->enterprise_password), "");
+}
+
 static void
 dialog_validate (UmAccountDialog *self)
 {
@@ -270,8 +818,7 @@ dialog_validate (UmAccountDialog *self)
                 valid = local_validate (self);
                 break;
         case UM_ENTERPRISE:
-                /* TODO: Implement */
-                valid = FALSE;
+                valid = enterprise_validate (self);
                 break;
         default:
                 valid = FALSE;
@@ -442,7 +989,9 @@ um_account_dialog_init (UmAccountDialog *self)
         gtk_container_add (GTK_CONTAINER (content), widget);
         self->widgets = widget;
 
-        local_area_init (self, builder);
+        local_init (self, builder);
+        enterprise_init (self, builder);
+        join_init (self, builder);
         switcher_init (self, builder);
 
         g_object_unref (builder);
@@ -461,8 +1010,7 @@ um_account_dialog_response (GtkDialog *dialog,
                         local_create_user (self);
                         break;
                 case UM_ENTERPRISE:
-                        /* TODO: */
-                        g_assert_not_reached ();
+                        enterprise_add_user (self);
                         break;
                 default:
                         g_assert_not_reached ();
@@ -487,6 +1035,18 @@ um_account_dialog_dispose (GObject *obj)
         if (self->cancellable)
                 g_cancellable_cancel (self->cancellable);
 
+        if (self->realmd_watch)
+                g_bus_unwatch_name (self->realmd_watch);
+        self->realmd_watch = 0;
+
+        if (self->realm_manager) {
+                g_signal_handlers_disconnect_by_func (self->realm_manager,
+                                                      on_manager_realm_added,
+                                                      self);
+                g_object_unref (self->realm_manager);
+                self->realm_manager = NULL;
+        }
+
         G_OBJECT_CLASS (um_account_dialog_parent_class)->dispose (obj);
 }
 
@@ -497,6 +1057,7 @@ um_account_dialog_finalize (GObject *obj)
 
         if (self->cancellable)
                 g_object_unref (self->cancellable);
+        g_object_unref (self->enterprise_realms);
 
         G_OBJECT_CLASS (um_account_dialog_parent_class)->finalize (obj);
 }
@@ -538,6 +1099,7 @@ um_account_dialog_perform (UmAccountDialog     *self,
         self->cancellable = g_cancellable_new ();
 
         local_prepare (self);
+        enterprise_prepare (self);
         switcher_switch (self, UM_LOCAL);
         dialog_validate (self);
 
diff --git a/panels/user-accounts/um-realm-manager.c b/panels/user-accounts/um-realm-manager.c
new file mode 100644
index 0000000..05aedce
--- /dev/null
+++ b/panels/user-accounts/um-realm-manager.c
@@ -0,0 +1,678 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright 2009-2012  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 3 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.
+ *
+ * Written by: Matthias Clasen <mclasen redhat com>
+ *             Stef Walter <stefw gnome org>
+ */
+
+#include "config.h"
+
+#include "um-realm-manager.h"
+
+#include <krb5/krb5.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+
+
+struct _UmRealmManager {
+        UmRealmProviderProxy parent;
+        GHashTable *realms;
+};
+
+typedef struct {
+        UmRealmProviderProxyClass parent_class;
+} UmRealmManagerClass;
+
+enum {
+        REALM_ADDED,
+        NUM_SIGNALS,
+};
+
+static gint signals[NUM_SIGNALS] = { 0, };
+
+G_DEFINE_TYPE (UmRealmManager, um_realm_manager, UM_REALM_TYPE_PROVIDER_PROXY);
+
+GQuark
+um_realm_error_get_quark (void)
+{
+        static GQuark quark = 0;
+        if (quark == 0)
+                quark = g_quark_from_static_string ("um-realm-error");
+        return quark;
+}
+
+typedef struct {
+        UmRealmManager *manager;
+        GList *realms;
+        gint outstanding;
+} LoadClosure;
+
+static void
+load_closure_free (gpointer data)
+{
+        LoadClosure *load = data;
+        g_list_free_full (load->realms, g_object_unref);
+        g_object_unref (load->manager);
+        g_slice_free (LoadClosure, load);
+}
+
+static void
+on_realm_proxy_created (GObject *source,
+                        GAsyncResult *result,
+                        gpointer user_data)
+{
+        GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
+        LoadClosure *load = g_simple_async_result_get_op_res_gpointer (async);
+        UmRealmManager *self = load->manager;
+        UmRealmKerberos *realm;
+        UmRealmKerberos *have;
+        GError *error = NULL;
+        GDBusProxy *proxy;
+        GVariant *info;
+
+        realm = um_realm_kerberos_proxy_new_finish (result, &error);
+        if (error == NULL) {
+                proxy = G_DBUS_PROXY (realm);
+                info = g_variant_new ("(sos)",
+                                      g_dbus_proxy_get_name (proxy),
+                                      g_dbus_proxy_get_object_path (proxy),
+                                      g_dbus_proxy_get_interface_name (proxy));
+
+                /* Add it to the manager, unless race */
+                have = g_hash_table_lookup (self->realms, info);
+                if (have == NULL) {
+                        g_hash_table_insert (self->realms,
+                                             g_variant_ref_sink (info), realm);
+                        g_signal_emit (self, signals[REALM_ADDED], 0, realm);
+
+                } else {
+                        g_object_unref (realm);
+                        g_variant_unref (info);
+                        realm = have;
+                }
+
+                load->realms = g_list_prepend (load->realms, g_object_ref (realm));
+
+        } else {
+                g_simple_async_result_take_error (async, error);
+        }
+
+        if (load->outstanding-- == 1)
+                g_simple_async_result_complete (async);
+
+        g_object_unref (async);
+}
+
+static void
+um_realm_manager_load (UmRealmManager *self,
+                       GVariant *realms,
+                       GCancellable *cancellable,
+                       GAsyncReadyCallback callback,
+                       gpointer user_data)
+{
+        GSimpleAsyncResult *async;
+        GDBusConnection *connection;
+        LoadClosure *load;
+        GVariantIter iter;
+        GVariant *info;
+        UmRealmKerberos *realm;
+        const gchar *path;
+        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_new0 (LoadClosure);
+        load->manager = g_object_ref (self);
+        g_simple_async_result_set_op_res_gpointer (async, load, load_closure_free);
+
+        connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (self));
+
+        g_variant_iter_init (&iter, realms);
+        for (;;) {
+                info = g_variant_iter_next_value (&iter);
+                if (info == NULL)
+                        break;
+                realm = g_hash_table_lookup (self->realms, info);
+                if (realm == NULL) {
+                        g_variant_get (info, "(&s&o&s)", &name, &path, &iface);
+                        if (g_str_equal (iface, "org.freedesktop.realmd.Kerberos")) {
+                                um_realm_kerberos_proxy_new (connection,
+                                                             G_DBUS_PROXY_FLAGS_NONE,
+                                                             name, path, cancellable,
+                                                             on_realm_proxy_created,
+                                                             g_object_ref (async));
+                                load->outstanding++;
+                        }
+                } else {
+                        load->realms = g_list_prepend (load->realms, g_object_ref (realm));
+                }
+                g_variant_unref (info);
+        }
+
+        if (load->outstanding == 0)
+                g_simple_async_result_complete_in_idle (async);
+}
+
+static GList *
+um_realm_manager_load_finish (UmRealmManager *self,
+                              GAsyncResult *result,
+                              GError **error)
+{
+        GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (result);
+        LoadClosure *load;
+        GList *realms;
+
+        if (g_simple_async_result_propagate_error (async, error))
+                return NULL;
+
+        load = g_simple_async_result_get_op_res_gpointer (async);
+        realms = g_list_reverse (load->realms);
+        load->realms = NULL;
+        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 (hash_realm_info, g_variant_equal,
+                                              (GDestroyNotify)g_variant_unref,
+                                              g_object_unref);
+}
+
+static void
+um_realm_manager_notify (GObject *obj,
+                         GParamSpec *spec)
+{
+        UmRealmManager *self = UM_REALM_MANAGER (obj);
+        GVariant *realms;
+
+        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));
+                if (realms != NULL)
+                        um_realm_manager_load (self, realms, NULL, NULL, NULL);
+        }
+}
+
+static void
+um_realm_manager_finalize (GObject *obj)
+{
+        UmRealmManager *self = UM_REALM_MANAGER (obj);
+
+        g_hash_table_destroy (self->realms);
+
+        G_OBJECT_CLASS (um_realm_manager_parent_class)->finalize (obj);
+}
+
+static void
+um_realm_manager_class_init (UmRealmManagerClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->notify = um_realm_manager_notify;
+        object_class->finalize = um_realm_manager_finalize;
+
+        signals[REALM_ADDED] = g_signal_new ("realm-added", UM_TYPE_REALM_MANAGER,
+                                             G_SIGNAL_RUN_FIRST, 0, NULL, NULL,
+                                             g_cclosure_marshal_generic,
+                                             G_TYPE_NONE, 1, UM_REALM_TYPE_KERBEROS);
+}
+
+void
+um_realm_manager_new (GCancellable *cancellable,
+                      GAsyncReadyCallback callback,
+                      gpointer user_data)
+{
+        g_async_initable_new_async (UM_TYPE_REALM_MANAGER,
+                                    G_PRIORITY_DEFAULT,
+                                    cancellable, callback, user_data,
+                                    "g-name", "org.freedesktop.realmd",
+                                    "g-bus-type", G_BUS_TYPE_SYSTEM,
+                                    "g-object-path", "/org/freedesktop/realmd",
+                                    "g-interface-name", "org.freedesktop.realmd.Provider",
+                                    NULL);
+}
+
+UmRealmManager *
+um_realm_manager_new_finish (GAsyncResult *result,
+                             GError **error)
+{
+        GObject *self;
+        GObject *source_object;
+
+        source_object = g_async_result_get_source_object (result);
+        self = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), result, error);
+        g_object_unref (source_object);
+
+        return UM_REALM_MANAGER (self);
+}
+
+typedef struct {
+        GCancellable *cancellable;
+        GList *realms;
+} DiscoverClosure;
+
+static void
+discover_closure_free (gpointer data)
+{
+        DiscoverClosure *discover = data;
+        g_clear_object (&discover->cancellable);
+        g_list_free_full (discover->realms, g_object_unref);
+        g_slice_free (DiscoverClosure, discover);
+}
+
+static void
+on_manager_load (GObject *source,
+                 GAsyncResult *result,
+                 gpointer user_data)
+{
+        GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
+        DiscoverClosure *discover = g_simple_async_result_get_op_res_gpointer (async);
+        GError *error = NULL;
+
+        discover->realms = um_realm_manager_load_finish (UM_REALM_MANAGER (source),
+                                                         result, &error);
+        if (error != NULL)
+                g_simple_async_result_take_error (async, error);
+        g_simple_async_result_complete (async);
+
+        g_object_unref (async);
+}
+
+static void
+on_provider_discover (GObject *source,
+                      GAsyncResult *result,
+                      gpointer user_data)
+{
+        GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
+        DiscoverClosure *discover = g_simple_async_result_get_op_res_gpointer (async);
+        UmRealmManager *self = UM_REALM_MANAGER (source);
+        GError *error = NULL;
+        GVariant *realms;
+        gint relevance;
+
+        um_realm_provider_call_discover_finish (UM_REALM_PROVIDER (self),
+                                                &relevance, &realms, result, &error);
+        if (error == NULL) {
+                um_realm_manager_load (self, realms, discover->cancellable,
+                                       on_manager_load, g_object_ref (async));
+
+        } else {
+                g_simple_async_result_take_error (async, error);
+                g_simple_async_result_complete (async);
+        }
+
+        g_object_unref (async);
+}
+
+void
+um_realm_manager_discover (UmRealmManager *self,
+                           const gchar *input,
+                           GCancellable *cancellable,
+                           GAsyncReadyCallback callback,
+                           gpointer user_data)
+{
+        GSimpleAsyncResult *res;
+        DiscoverClosure *discover;
+
+        g_return_if_fail (UM_IS_REALM_MANAGER (self));
+        g_return_if_fail (input != NULL);
+        g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+        res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
+                                         um_realm_manager_discover);
+        discover = g_slice_new0 (DiscoverClosure);
+        discover->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+        g_simple_async_result_set_op_res_gpointer (res, discover, discover_closure_free);
+
+        um_realm_provider_call_discover (UM_REALM_PROVIDER (self), input, cancellable,
+                                         on_provider_discover, g_object_ref (res));
+
+        g_object_unref (res);
+}
+
+GList *
+um_realm_manager_discover_finish (UmRealmManager *self,
+                                  GAsyncResult *result,
+                                  GError **error)
+{
+        GSimpleAsyncResult *async;
+        DiscoverClosure *discover;
+        GList *realms;
+
+        g_return_val_if_fail (UM_IS_REALM_MANAGER (self), NULL);
+        g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
+                              um_realm_manager_discover), NULL);
+        g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+        async = G_SIMPLE_ASYNC_RESULT (result);
+        if (g_simple_async_result_propagate_error (async, error))
+                return NULL;
+
+        discover = g_simple_async_result_get_op_res_gpointer (async);
+        if (!discover->realms) {
+                g_set_error (error, UM_REALM_ERROR, UM_REALM_ERROR_FAIL,
+                             _("No such domain or realm found"));
+                return NULL;
+        }
+
+        realms = discover->realms;
+        discover->realms = NULL;
+        return realms;
+}
+
+GList *
+um_realm_manager_get_realms (UmRealmManager *self)
+{
+        g_return_val_if_fail (UM_IS_REALM_MANAGER (self), NULL);
+        return g_hash_table_get_values (self->realms);
+}
+
+void
+um_realm_join (UmRealmKerberos *realm,
+               GBytes *credentials,
+               GCancellable *cancellable,
+               GAsyncReadyCallback callback,
+               gpointer user_data)
+{
+        GVariant *options;
+        GVariant *creds;
+
+        g_return_if_fail (UM_REALM_IS_KERBEROS (realm));
+        g_return_if_fail (credentials != NULL);
+        g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+        creds = g_variant_new_from_data (G_VARIANT_TYPE ("ay"),
+                                         g_bytes_get_data (credentials, NULL),
+                                         g_bytes_get_size (credentials),
+                                         TRUE, (GDestroyNotify)g_bytes_unref, credentials);
+        options = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0);
+
+        um_realm_kerberos_call_enroll_with_credential_cache (realm,
+                                                             g_variant_ref_sink (creds),
+                                                             g_variant_ref_sink (options),
+                                                             cancellable,
+                                                             callback,
+                                                             user_data);
+
+        g_variant_unref (options);
+        g_variant_unref (creds);
+}
+
+gboolean
+um_realm_join_finish (UmRealmKerberos *self,
+                      GAsyncResult *result,
+                      GError **error)
+{
+        GError *call_error = NULL;
+        gchar *dbus_error;
+
+        g_return_val_if_fail (UM_REALM_IS_KERBEROS (self), FALSE);
+        g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+        um_realm_kerberos_call_enroll_with_credential_cache_finish (self,
+                                                                    result,
+                                                                    &call_error);
+        if (call_error == NULL)
+                return TRUE;
+
+        dbus_error = g_dbus_error_get_remote_error (call_error);
+        if (dbus_error == NULL) {
+                g_propagate_error (error, call_error);
+                return FALSE;
+        }
+
+        g_dbus_error_strip_remote_error (call_error);
+
+        if (g_str_equal (dbus_error, "org.freedesktop.realmd.Error.AuthFailed")) {
+                g_set_error (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_LOGIN,
+                             call_error->message);
+                g_error_free (call_error);
+        } else {
+                g_propagate_error (error, call_error);
+        }
+
+        g_free (dbus_error);
+        return FALSE;
+}
+
+typedef struct {
+        gchar *domain;
+        gchar *realm;
+        gchar *user;
+        gchar *password;
+        gboolean unique;
+        GBytes *credentials;
+} LoginClosure;
+
+static void
+login_closure_free (gpointer data)
+{
+        LoginClosure *login = data;
+        g_free (login->domain);
+        g_free (login->realm);
+        g_free (login->user);
+        g_free (login->password);
+        g_bytes_unref (login->credentials);
+        g_slice_free (LoginClosure, login);
+}
+
+static krb5_error_code
+login_perform_kinit (krb5_context k5,
+                     const gchar *realm,
+                     const gchar *login,
+                     const gchar *password,
+                     const gchar *filename)
+{
+        krb5_get_init_creds_opt *opts;
+        krb5_error_code code;
+        krb5_principal principal;
+        krb5_ccache ccache;
+        krb5_creds creds;
+        gchar *name;
+
+        name = g_strdup_printf ("%s %s", login, realm);
+        code = krb5_parse_name (k5, name, &principal);
+        g_free (name);
+
+        if (code != 0)
+                return code;
+
+        if (filename == NULL)
+                code = krb5_cc_default (k5, &ccache);
+        else
+                code = krb5_cc_resolve (k5, filename, &ccache);
+
+        if (code != 0) {
+                krb5_free_principal (k5, principal);
+                return code;
+        }
+
+        code = krb5_get_init_creds_opt_alloc (k5, &opts);
+        g_return_val_if_fail (code == 0, code);
+
+        code = krb5_get_init_creds_opt_set_out_ccache (k5, opts, ccache);
+        g_return_val_if_fail (code == 0, code);
+
+        code = krb5_get_init_creds_password (k5, &creds, principal,
+                                             (char *)password,
+                                             NULL, 0, 0, NULL, opts);
+
+        krb5_get_init_creds_opt_free (k5, opts);
+        krb5_cc_close (k5, ccache);
+        krb5_free_principal (k5, principal);
+
+        if (code == 0)
+                krb5_free_cred_contents (k5, &creds);
+
+        return code;
+}
+
+static void
+kinit_thread_func (GSimpleAsyncResult *async,
+                   GObject *object,
+                   GCancellable *cancellable)
+{
+        LoginClosure *login = g_simple_async_result_get_op_res_gpointer (async);
+        krb5_context k5 = NULL;
+        krb5_error_code code;
+        GError *error = NULL;
+        gchar *filename = NULL;
+        gchar *contents;
+        gsize length;
+        gint temp_fd;
+
+        if (login->unique) {
+                filename = g_build_filename (g_get_user_runtime_dir (),
+                                             "um-krb5-creds.XXXXXX", NULL);
+                temp_fd = g_mkstemp_full (filename, O_RDWR, S_IRUSR | S_IWUSR);
+                if (temp_fd == -1) {
+                        g_warning ("Couldn't create credential cache file: %s: %s",
+                                   filename, g_strerror (errno));
+                        g_free (filename);
+                        filename = NULL;
+                } else {
+                        close (temp_fd);
+                }
+        }
+
+        code = krb5_init_context (&k5);
+        if (code == 0) {
+                code = login_perform_kinit (k5, login->realm, login->user,
+                                            login->password, filename);
+        }
+
+        switch (code) {
+        case 0:
+                if (filename != NULL) {
+                        g_file_get_contents (filename, &contents, &length, &error);
+                        if (error == NULL) {
+                                login->credentials = g_bytes_new_take (contents, length);
+                        } else {
+                                g_warning ("Couldn't read credential cache: %s", error->message);
+                                g_error_free (error);
+                        }
+                }
+                break;
+
+        case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
+        case KRB5KDC_ERR_CLIENT_REVOKED:
+        case KRB5KDC_ERR_KEY_EXP:
+        case KRB5KDC_ERR_POLICY:
+        case KRB5KDC_ERR_ETYPE_NOSUPP:
+                g_simple_async_result_set_error (async, UM_REALM_ERROR, UM_REALM_ERROR_BAD_LOGIN,
+                                                 _("Cannot log in as %s at the %s domain"),
+                                                 login->user, login->domain);
+                break;
+        case KRB5KDC_ERR_PREAUTH_FAILED:
+                g_simple_async_result_set_error (async, UM_REALM_ERROR, UM_REALM_ERROR_BAD_PASSWORD,
+                                                 _("Invalid password, please try again"));
+                break;
+        default:
+                g_simple_async_result_set_error (async, UM_REALM_ERROR, UM_REALM_ERROR_FAIL,
+                                                 _("Couldn't connect to the %s domain: %s"),
+                                                 login->domain, krb5_get_error_message (k5, code));
+                break;
+        }
+
+        if (filename) {
+                g_unlink (filename);
+                g_free (filename);
+        }
+
+        if (k5)
+                krb5_free_context (k5);
+}
+
+void
+um_realm_login (const gchar *realm,
+                const gchar *domain,
+                const gchar *user,
+                const gchar *password,
+                UmRealmLoginFlags flags,
+                GCancellable *cancellable,
+                GAsyncReadyCallback callback,
+                gpointer user_data)
+{
+        GSimpleAsyncResult *async;
+        LoginClosure *login;
+
+        g_return_if_fail (realm != NULL);
+        g_return_if_fail (user != NULL);
+        g_return_if_fail (password != NULL);
+        g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+        async = g_simple_async_result_new (NULL, callback, user_data,
+                                           um_realm_login);
+        login = g_slice_new0 (LoginClosure);
+        login->domain = g_strdup (domain ? domain : realm);
+        login->realm = g_strdup (realm);
+        login->user = g_strdup (user);
+        login->password = g_strdup (password);
+        login->unique = (flags & UM_REALM_LOGIN_TO_CREDENTIALS);
+        g_simple_async_result_set_op_res_gpointer (async, login, login_closure_free);
+
+        g_simple_async_result_set_handle_cancellation (async, TRUE);
+        g_simple_async_result_run_in_thread (async, kinit_thread_func,
+                                             G_PRIORITY_DEFAULT, cancellable);
+
+        g_object_unref (async);
+}
+
+gboolean
+um_realm_login_finish (GAsyncResult *result,
+                       GBytes **credentials,
+                       GError **error)
+{
+        GSimpleAsyncResult *async;
+        LoginClosure *login;
+
+        g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL,
+                              um_realm_login), FALSE);
+        g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+        async = G_SIMPLE_ASYNC_RESULT (result);
+        if (g_simple_async_result_propagate_error (async, error))
+                return FALSE;
+
+        login = g_simple_async_result_get_op_res_gpointer (async);
+        if (credentials && login->credentials)
+                *credentials = g_bytes_ref (login->credentials);
+
+        return TRUE;
+}
diff --git a/panels/user-accounts/um-realm-manager.h b/panels/user-accounts/um-realm-manager.h
new file mode 100644
index 0000000..8fe2b0c
--- /dev/null
+++ b/panels/user-accounts/um-realm-manager.h
@@ -0,0 +1,96 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright 2012  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 3 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.
+ *
+ * Written by: Stef Walter <stefw gnome org>
+ */
+
+#ifndef __UM_REALM_MANAGER_H__
+#define __UM_REALM_MANAGER_H__
+
+#include "um-realm-generated.h"
+
+G_BEGIN_DECLS
+
+typedef enum {
+	UM_REALM_ERROR_BAD_LOGIN,
+	UM_REALM_ERROR_BAD_PASSWORD,
+	UM_REALM_ERROR_FAIL,
+} UmRealmErrors;
+
+#define UM_REALM_ERROR             (um_realm_error_get_quark ())
+
+GQuark           um_realm_error_get_quark         (void) G_GNUC_CONST;
+
+#define UM_TYPE_REALM_MANAGER      (um_realm_manager_get_type ())
+#define UM_REALM_MANAGER(obj)      (G_TYPE_CHECK_INSTANCE_CAST ((obj), UM_TYPE_REALM_MANAGER, UmRealmManager))
+#define UM_IS_REALM_MANAGER(obj)   (G_TYPE_CHECK_INSTANCE_TYPE ((obj), UM_TYPE_REALM_MANAGER))
+
+typedef struct _UmRealmManager UmRealmManager;
+
+GType            um_realm_manager_get_type        (void) G_GNUC_CONST;
+
+void             um_realm_manager_new             (GCancellable *cancellable,
+                                                   GAsyncReadyCallback callback,
+                                                   gpointer user_data);
+
+UmRealmManager * um_realm_manager_new_finish      (GAsyncResult *result,
+                                                   GError **error);
+
+void             um_realm_manager_discover        (UmRealmManager *self,
+                                                   const gchar *input,
+                                                   GCancellable *cancellable,
+                                                   GAsyncReadyCallback callback,
+                                                   gpointer user_data);
+
+GList *          um_realm_manager_discover_finish (UmRealmManager *self,
+                                                   GAsyncResult *result,
+                                                   GError **error);
+
+GList *          um_realm_manager_get_realms      (UmRealmManager *self);
+
+typedef enum {
+	UM_REALM_LOGIN_TO_CREDENTIALS = 1 << 0,
+} UmRealmLoginFlags;
+
+void             um_realm_login                   (const gchar *realm_name,
+                                                   const gchar *domain,
+                                                   const gchar *login,
+                                                   const gchar *password,
+                                                   UmRealmLoginFlags flags,
+                                                   GCancellable *cancellable,
+                                                   GAsyncReadyCallback callback,
+                                                   gpointer user_data);
+
+gboolean         um_realm_login_finish            (GAsyncResult *result,
+                                                   GBytes **credentials,
+                                                   GError **error);
+
+void             um_realm_join                    (UmRealmKerberos *realm,
+                                                   GBytes *credentials,
+                                                   GCancellable *cancellable,
+                                                   GAsyncReadyCallback callback,
+                                                   gpointer user_data);
+
+gboolean         um_realm_join_finish             (UmRealmKerberos *self,
+                                                   GAsyncResult *result,
+                                                   GError **error);
+
+
+G_END_DECLS
+
+#endif /* __UM_REALM_H__ */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]