[gnome-online-accounts/wip/mail: 5/5] Add IMAP/SMTP



commit 7b252464246fdc3ca723068828c2412a612fa39b
Author: Debarshi Ray <debarshir gnome org>
Date:   Mon Jan 28 21:45:54 2013 +0100

    Add IMAP/SMTP
    
    Fixes: https://bugzilla.gnome.org/692736

 configure.ac                          |   11 +
 src/goabackend/Makefile.am            |    4 +
 src/goabackend/goabackendenums-priv.h |   16 +
 src/goabackend/goabackendtypes.h      |    7 +-
 src/goabackend/goaimapauth.c          |  111 ++++
 src/goabackend/goaimapauth.h          |   96 ++++
 src/goabackend/goaimapauthlogin.c     |  372 +++++++++++++
 src/goabackend/goaimapauthlogin.h     |   47 ++
 src/goabackend/goaimapclient.c        |  281 ++++++++++
 src/goabackend/goaimapclient.h        |   61 +++
 src/goabackend/goaimapsmtpprovider.c  |  961 +++++++++++++++++++++++++++++++++
 src/goabackend/goaimapsmtpprovider.h  |   43 ++
 src/goabackend/goaprovider.c          |    6 +-
 13 files changed, 2011 insertions(+), 5 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 1e1fa95..0a316dd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -172,6 +172,16 @@ if test "$enable_google" != "no"; then
   AC_DEFINE(GOA_GOOGLE_ENABLED, 1, [Enable Google data provider])
 fi
 
+# IMAP/SMTP
+AC_ARG_ENABLE([imap-smtp],
+              [AS_HELP_STRING([--enable-imap-smtp],
+              [Enable IMAP/SMTP provider])],
+              [],
+              [enable_imap_smtp=no])
+if test "$enable_imap_smtp" != "no"; then
+  AC_DEFINE(GOA_IMAP_SMTP_ENABLED, 1, [Enable IMAP/SMTP data provider])
+fi
+
 # ownCloud
 AC_ARG_ENABLE([owncloud],
               [AS_HELP_STRING([--enable-owncloud],
@@ -450,6 +460,7 @@ echo "
 
 	Flickr provider:                ${enable_flickr} (OAuth 1.0, key:${with_flickr_consumer_key} secret:${with_flickr_consumer_secret})
 	Google provider:                ${enable_google} (OAuth 2.0, id:${with_google_client_id} secret:${with_google_client_secret})
+	IMAP/SMTP provider:             ${enable_imap_smtp}
 	Microsoft Exchange provider:    ${enable_exchange}
 	ownCloud provider:              ${enable_owncloud}
 	Kerberos provider:              ${enable_kerberos}
diff --git a/src/goabackend/Makefile.am b/src/goabackend/Makefile.am
index 666e175..6fe731f 100644
--- a/src/goabackend/Makefile.am
+++ b/src/goabackend/Makefile.am
@@ -60,12 +60,16 @@ libgoa_backend_1_0_la_SOURCES =						\
 	goaewsclient.h			goaewsclient.c			\
 	goahttpclient.h			goahttpclient.c			\
 	goaprovider-priv.h		goaprovider.c			\
+	goaimapauth.h			goaimapauth.c			\
+	goaimapauthlogin.h		goaimapauthlogin.c		\
+	goaimapclient.h			goaimapclient.c			\
 	goaexchangeprovider.h		goaexchangeprovider.c		\
 	goalogging.h			goalogging.c			\
 	goaoauthprovider.h		goaoauthprovider.c		\
 	goaoauth2provider.h		goaoauth2provider.c		\
 	goagoogleprovider.h		goagoogleprovider.c		\
 	goafacebookprovider.h		goafacebookprovider.c		\
+	goaimapsmtpprovider.h		goaimapsmtpprovider.c		\
 	goaowncloudprovider.h		goaowncloudprovider.c		\
 	goayahooprovider.h		goayahooprovider.c		\
 	goatwitterprovider.h		goatwitterprovider.c		\
diff --git a/src/goabackend/goabackendenums-priv.h b/src/goabackend/goabackendenums-priv.h
index 5c8eab3..9d87fbd 100644
--- a/src/goabackend/goabackendenums-priv.h
+++ b/src/goabackend/goabackendenums-priv.h
@@ -49,6 +49,22 @@ typedef enum
   GOA_LOG_LEVEL_ERROR
 } GoaLogLevel;
 
+/**
+ * GoaTlsType:
+ * @GOA_TLS_TYPE_NONE: No encryption.
+ * @GOA_TLS_TYPE_STARTTLS: STARTTLS should be used on a standard port
+ * after the connection has been established to obtain a secure channel.
+ * @GOA_TLS_TYPE_SSL: SSL should be used on a dedicated port.
+ *
+ * Type of SSL/TLS used to connect to a server.
+ */
+typedef enum
+{
+  GOA_TLS_TYPE_NONE,
+  GOA_TLS_TYPE_STARTTLS,
+  GOA_TLS_TYPE_SSL
+} GoaTlsType;
+
 G_END_DECLS
 
 #endif /* __GOA_BACKEND_ENUMS_PRIV_H__ */
diff --git a/src/goabackend/goabackendtypes.h b/src/goabackend/goabackendtypes.h
index 94a2fea..2187625 100644
--- a/src/goabackend/goabackendtypes.h
+++ b/src/goabackend/goabackendtypes.h
@@ -1,6 +1,6 @@
 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
 /*
- * Copyright (C) 2011, 2012 Red Hat, Inc.
+ * Copyright (C) 2011, 2012, 2013 Red Hat, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -17,7 +17,8 @@
  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  * Boston, MA 02111-1307, USA.
  *
- * Author: David Zeuthen <davidz redhat com>
+ * Authors: David Zeuthen <davidz redhat com>
+ *          Debarshi Ray <debarshir gnome org>
  */
 
 #if !defined (__GOA_BACKEND_INSIDE_GOA_BACKEND_H__) && !defined (GOA_BACKEND_COMPILATION)
@@ -30,8 +31,6 @@
 #include <goa/goa.h>
 #include <goabackend/goabackendenums.h>
 
-G_BEGIN_DECLS
-
 struct _GoaProvider;
 typedef struct _GoaProvider GoaProvider;
 
diff --git a/src/goabackend/goaimapauth.c b/src/goabackend/goaimapauth.c
new file mode 100644
index 0000000..3d32c50
--- /dev/null
+++ b/src/goabackend/goaimapauth.c
@@ -0,0 +1,111 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2011, 2013 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: David Zeuthen <davidz redhat com>
+ *          Debarshi Ray <debarshir gnome org>
+ */
+
+#include "config.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "goaimapauth.h"
+
+/**
+ * SECTION:goaimapauth
+ * @title: GoaImapAuth
+ * @short_description: Helper type for authenticating IMAP connections
+ *
+ * #GoaImapAuth is an abstract type used for authenticating
+ * IMAP connections. See #GoaImapAuthOAuth for a concrete
+ * implementation.
+ */
+
+G_DEFINE_ABSTRACT_TYPE (GoaImapAuth, goa_imap_auth, G_TYPE_OBJECT);
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+goa_imap_auth_init (GoaImapAuth *client)
+{
+}
+
+static void
+goa_imap_auth_class_init (GoaImapAuthClass *klass)
+{
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * goa_imap_auth_run_sync:
+ * @auth: A #GoaImapAuth.
+ * @input: A valid #GDataInputStream.
+ * @output: A valid #GDataOutputStream.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Authenticates the IMAP connection represented by @input and
+ * @output. This method blocks the calling thread until authentication
+ * is done.
+ *
+ * Returns: %TRUE if authentication succeeded, %FALSE if @error is
+ * set.
+ */
+gboolean
+goa_imap_auth_run_sync (GoaImapAuth         *auth,
+                        GDataInputStream    *input,
+                        GDataOutputStream   *output,
+                        GCancellable        *cancellable,
+                        GError             **error)
+{
+  g_return_val_if_fail (GOA_IS_IMAP_AUTH (auth), FALSE);
+  g_return_val_if_fail (G_IS_DATA_INPUT_STREAM (input), FALSE);
+  g_return_val_if_fail (G_IS_DATA_OUTPUT_STREAM (output), FALSE);
+  g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
+  return GOA_IMAP_AUTH_GET_CLASS (auth)->run_sync (auth, input, output, cancellable, error);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+void
+goa_imap_auth_run (GoaImapAuth         *auth,
+                   GDataInputStream    *input,
+                   GDataOutputStream   *output,
+                   GCancellable        *cancellable,
+                   GAsyncReadyCallback  callback,
+                   gpointer             user_data)
+{
+  GSimpleAsyncResult *simple;
+
+  g_return_if_fail (GOA_IS_IMAP_AUTH (auth));
+  g_return_if_fail (G_IS_DATA_INPUT_STREAM (input));
+  g_return_if_fail (G_IS_DATA_OUTPUT_STREAM (output));
+  g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+  simple = g_simple_async_result_new (G_OBJECT (auth), callback, user_data, goa_imap_auth_run);
+  g_object_unref (simple);
+}
+
+gboolean
+goa_imap_auth_run_finish (GoaImapAuth         *auth,
+                          GAsyncResult        *res,
+                          GError             **error)
+{
+}
diff --git a/src/goabackend/goaimapauth.h b/src/goabackend/goaimapauth.h
new file mode 100644
index 0000000..d1ff958
--- /dev/null
+++ b/src/goabackend/goaimapauth.h
@@ -0,0 +1,96 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2011, 2013 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: David Zeuthen <davidz redhat com>
+ *          Debarshi Ray <debarshir gnome org>
+ */
+
+#if !defined (__GOA_BACKEND_INSIDE_GOA_BACKEND_H__) && !defined (GOA_BACKEND_COMPILATION)
+#error "Only <goabackend/goabackend.h> can be included directly."
+#endif
+
+#ifndef __GOA_IMAP_AUTH_H__
+#define __GOA_IMAP_AUTH_H__
+
+#include <gio/gio.h>
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GOA_TYPE_IMAP_AUTH         (goa_imap_auth_get_type ())
+#define GOA_IMAP_AUTH(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GOA_TYPE_IMAP_AUTH, GoaImapAuth))
+#define GOA_IMAP_AUTH_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), GOA_TYPE_IMAP_AUTH, GoaImapAuthClass))
+#define GOA_IMAP_AUTH_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GOA_TYPE_IMAP_AUTH, GoaImapAuthClass))
+#define GOA_IS_IMAP_AUTH(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOA_TYPE_IMAP_AUTH))
+#define GOA_IS_IMAP_AUTH_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GOA_TYPE_IMAP_AUTH))
+
+typedef struct _GoaImapAuth GoaImapAuth;
+typedef struct _GoaImapAuthClass GoaImapAuthClass;
+typedef struct _GoaImapAuthPrivate GoaImapAuthPrivate;
+
+/**
+ * GoaImapAuth:
+ *
+ * The #GoaImapAuth structure contains only private data and
+ * should only be accessed using the provided API.
+ */
+struct _GoaImapAuth
+{
+  /*< private >*/
+  GObject parent_instance;
+  GoaImapAuthPrivate *priv;
+};
+
+/**
+ * GoaImapAuthClass:
+ * @parent_class: The parent class
+ * @run_sync: Virtual function for the goa_imap_auth_run_sync() method.
+ *
+ * Class structure for #GoaImapAuth.
+ */
+struct _GoaImapAuthClass
+{
+  GObjectClass parent_class;
+  gboolean (*run_sync) (GoaImapAuth         *auth,
+                        GDataInputStream    *input,
+                        GDataOutputStream   *output,
+                        GCancellable        *cancellable,
+                        GError             **error);
+};
+
+GType     goa_imap_auth_get_type     (void) G_GNUC_CONST;
+void      goa_imap_auth_run          (GoaImapAuth         *auth,
+                                      GDataInputStream    *input,
+                                      GDataOutputStream   *output,
+                                      GCancellable        *cancellable,
+                                      GAsyncReadyCallback  callback,
+                                      gpointer             user_data);
+gboolean  goa_imap_auth_run_finish   (GoaImapAuth         *auth,
+                                      GAsyncResult        *res,
+                                      GError             **error);
+gboolean  goa_imap_auth_run_sync     (GoaImapAuth         *auth,
+                                      GDataInputStream    *input,
+                                      GDataOutputStream   *output,
+                                      GCancellable        *cancellable,
+                                      GError             **error);
+
+G_END_DECLS
+
+#endif /* __GOA_IMAP_AUTH_H__ */
diff --git a/src/goabackend/goaimapauthlogin.c b/src/goabackend/goaimapauthlogin.c
new file mode 100644
index 0000000..eb2f98f
--- /dev/null
+++ b/src/goabackend/goaimapauthlogin.c
@@ -0,0 +1,372 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+#include <stdlib.h>
+
+#include "goaimapauth.h"
+#include "goaimapauthlogin.h"
+#include "goaprovider.h"
+#include "goautils.h"
+
+/**
+ * SECTION:goaimapauthlogin
+ * @title: GoaImapAuthLogin
+ * @short_description: LOGIN authentication method for IMAP
+ *
+ * #GoaImapAuthLogin implements the standard <ulink
+ * url="http://tools.ietf.org/html/rfc3501#section-6.2.3";>LOGIN</ulink>
+ * authentication method (e.g. using usernames / passwords) for IMAP.
+ */
+
+/**
+ * GoaImapAuthLogin:
+ *
+ * The #GoaImapAuthLogin structure contains only private data
+ * and should only be accessed using the provided API.
+ */
+struct _GoaImapAuthLogin
+{
+  GoaImapAuth parent_instance;
+
+  GoaProvider *provider;
+  GoaObject *object;
+  gchar *user_name;
+  gchar *password;
+};
+
+typedef struct
+{
+  GoaImapAuthClass parent_class;
+
+} GoaImapAuthLoginClass;
+
+enum
+{
+  PROP_0,
+  PROP_PROVIDER,
+  PROP_OBJECT,
+  PROP_USER_NAME,
+  PROP_PASSWORD
+};
+
+static gboolean goa_imap_auth_login_run_sync (GoaImapAuth         *_auth,
+                                              GDataInputStream    *input,
+                                              GDataOutputStream   *output,
+                                              GCancellable        *cancellable,
+                                              GError             **error);
+
+G_DEFINE_TYPE (GoaImapAuthLogin, goa_imap_auth_login, GOA_TYPE_IMAP_AUTH);
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+goa_imap_auth_login_finalize (GObject *object)
+{
+  GoaImapAuthLogin *auth = GOA_IMAP_AUTH_LOGIN (object);
+
+  g_clear_object (&auth->provider);
+  g_clear_object (&auth->object);
+  g_free (auth->user_name);
+  g_free (auth->password);
+
+  G_OBJECT_CLASS (goa_imap_auth_login_parent_class)->finalize (object);
+}
+
+static void
+goa_imap_auth_login_get_property (GObject      *object,
+                                  guint         prop_id,
+                                  GValue       *value,
+                                  GParamSpec   *pspec)
+{
+  GoaImapAuthLogin *auth = GOA_IMAP_AUTH_LOGIN (object);
+
+  switch (prop_id)
+    {
+    case PROP_PROVIDER:
+      g_value_set_object (value, auth->provider);
+      break;
+
+    case PROP_OBJECT:
+      g_value_set_object (value, auth->object);
+      break;
+
+    case PROP_USER_NAME:
+      g_value_set_string (value, auth->user_name);
+      break;
+
+    case PROP_PASSWORD:
+      g_value_set_string (value, auth->password);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+goa_imap_auth_login_set_property (GObject      *object,
+                                  guint         prop_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+  GoaImapAuthLogin *auth = GOA_IMAP_AUTH_LOGIN (object);
+
+  switch (prop_id)
+    {
+    case PROP_PROVIDER:
+      auth->provider = g_value_dup_object (value);
+      break;
+
+    case PROP_OBJECT:
+      auth->object = g_value_dup_object (value);
+      break;
+
+    case PROP_USER_NAME:
+      auth->user_name = g_value_dup_string (value);
+      break;
+
+    case PROP_PASSWORD:
+      auth->password = g_value_dup_string (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+
+static void
+goa_imap_auth_login_init (GoaImapAuthLogin *client)
+{
+}
+
+static void
+goa_imap_auth_login_class_init (GoaImapAuthLoginClass *klass)
+{
+  GObjectClass *gobject_class;
+  GoaImapAuthClass *auth_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->finalize     = goa_imap_auth_login_finalize;
+  gobject_class->get_property = goa_imap_auth_login_get_property;
+  gobject_class->set_property = goa_imap_auth_login_set_property;
+
+  auth_class = GOA_IMAP_AUTH_CLASS (klass);
+  auth_class->run_sync = goa_imap_auth_login_run_sync;
+
+  /**
+   * GoaImapAuthLogin:provider:
+   *
+   * The #GoaProvider object for the account or %NULL.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_PROVIDER,
+                                   g_param_spec_object ("provider",
+                                                        "provider",
+                                                        "provider",
+                                                        GOA_TYPE_PROVIDER,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_WRITABLE |
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GoaImapAuthLogin:object:
+   *
+   * The #GoaObject object for the account.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_OBJECT,
+                                   g_param_spec_object ("object",
+                                                        "object",
+                                                        "object",
+                                                        GOA_TYPE_OBJECT,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_WRITABLE |
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GoaImapAuthLogin:user-name:
+   *
+   * The user name.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_USER_NAME,
+                                   g_param_spec_string ("user-name",
+                                                        "user-name",
+                                                        "user-name",
+                                                        NULL,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_WRITABLE |
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GoaImapAuthLogin:password:
+   *
+   * The password or %NULL.
+   *
+   * If this is %NULL, the credentials are looked up using
+   * goa_utils_lookup_credentials_sync() using the
+   * #GoaImapAuthLogin:provider and #GoaImapAuthLogin:object for
+   * @provider and @object. The credentials are expected to be a
+   * %G_VARIANT_VARDICT and the key <literal>imap-password</literal>
+   * is used to look up the password.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_PASSWORD,
+                                   g_param_spec_string ("password",
+                                                        "password",
+                                                        "password",
+                                                        NULL,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_WRITABLE |
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_STRINGS));
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * goa_imap_auth_login_new:
+ * @provider: (allow-none): A #GoaLoginProvider or %NULL.
+ * @object: (allow-none): An account object or %NULL.
+ * @user_name: The user name to use.
+ * @password: (allow-none): The password to use or %NULL to look it up (see the #GoaImapAuthLogin:password property).
+ *
+ * Creates a new #GoaImapAuth to be used for username/password authentication.
+ *
+ * Returns: (type GoaImapAuthLogin): A #GoaImapAuthLogin. Free with g_object_unref().
+ */
+GoaImapAuth *
+goa_imap_auth_login_new (GoaProvider       *provider,
+                         GoaObject         *object,
+                         const gchar       *user_name,
+                         const gchar       *password)
+{
+  g_return_val_if_fail (provider == NULL || GOA_IS_PROVIDER (provider), NULL);
+  g_return_val_if_fail (object == NULL || GOA_IS_OBJECT (object), NULL);
+  g_return_val_if_fail (user_name != NULL, NULL);
+  return GOA_IMAP_AUTH (g_object_new (GOA_TYPE_IMAP_AUTH_LOGIN,
+                                      "provider", provider,
+                                      "object", object,
+                                      "user-name", user_name,
+                                      "password", password,
+                                      NULL));
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+goa_imap_auth_login_run_sync (GoaImapAuth         *_auth,
+                              GDataInputStream    *input,
+                              GDataOutputStream   *output,
+                              GCancellable        *cancellable,
+                              GError             **error)
+{
+  GoaImapAuthLogin *auth = GOA_IMAP_AUTH_LOGIN (_auth);
+  gchar *request;
+  gchar *response;
+  gboolean ret;
+  gchar *password;
+
+  request = NULL;
+  response = NULL;
+  password = NULL;
+  ret = FALSE;
+
+  /* TODO: support looking up the password from the keyring */
+  if (auth->password != NULL)
+    {
+      password = g_strdup (auth->password);
+    }
+  else if (auth->provider != NULL && auth->object != NULL)
+    {
+      GVariant *credentials;
+      credentials = goa_utils_lookup_credentials_sync (auth->provider,
+                                                       auth->object,
+                                                       cancellable,
+                                                       error);
+      if (credentials == NULL)
+        {
+          g_prefix_error (error, "Error looking up credentials for IMAP LOGIN in keyring: ");
+          goto out;
+        }
+      if (!g_variant_lookup (credentials, "imap-password", "s", &password))
+        {
+          g_set_error (error,
+                       GOA_ERROR,
+                       GOA_ERROR_FAILED,
+                       "Did not find imap-password in credentials");
+          g_variant_unref (credentials);
+          goto out;
+        }
+      g_variant_unref (credentials);
+    }
+  else
+    {
+      g_set_error (error,
+                   GOA_ERROR,
+                   GOA_ERROR_FAILED,
+                   "Cannot do IMAP LOGIN without a password");
+      goto out;
+    }
+
+  request = g_strdup_printf ("A001 LOGIN %s %s\r\n", auth->user_name, password);
+  if (!g_data_output_stream_put_string (output, request, cancellable, error))
+    goto out;
+
+ again:
+  response = g_data_input_stream_read_line (input, NULL, cancellable, error);
+  if (response == NULL)
+    goto out;
+  /* ignore untagged responses */
+  if (g_str_has_prefix (response, "*"))
+    {
+      g_free (response);
+      goto again;
+    }
+  if (!g_str_has_prefix (response, "A001 OK"))
+    {
+      g_set_error (error,
+                   GOA_ERROR,
+                   GOA_ERROR_FAILED,
+                   "Unexpected response `%s' while doing LOGIN authentication",
+                   response);
+      goto out;
+    }
+
+  ret = TRUE;
+
+ out:
+  g_free (response);
+  g_free (request);
+  g_free (password);
+  return ret;
+}
diff --git a/src/goabackend/goaimapauthlogin.h b/src/goabackend/goaimapauthlogin.h
new file mode 100644
index 0000000..5d2a1ba
--- /dev/null
+++ b/src/goabackend/goaimapauthlogin.h
@@ -0,0 +1,47 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2011 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#if !defined (__GOA_BACKEND_INSIDE_GOA_BACKEND_H__) && !defined (GOA_BACKEND_COMPILATION)
+#error "Only <goabackend/goabackend.h> can be included directly."
+#endif
+
+#ifndef __GOA_IMAP_AUTH_LOGIN_H__
+#define __GOA_IMAP_AUTH_LOGIN_H__
+
+#include <goabackend/goabackendtypes.h>
+
+G_BEGIN_DECLS
+
+#define GOA_TYPE_IMAP_AUTH_LOGIN         (goa_imap_auth_login_get_type ())
+#define GOA_IMAP_AUTH_LOGIN(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GOA_TYPE_IMAP_AUTH_LOGIN, GoaImapAuthLogin))
+#define GOA_IS_IMAP_AUTH_LOGIN(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOA_TYPE_IMAP_AUTH_LOGIN))
+
+
+GType        goa_imap_auth_login_get_type (void) G_GNUC_CONST;
+GoaImapAuth *goa_imap_auth_login_new (GoaProvider       *provider,
+                                      GoaObject         *object,
+                                      const gchar       *user_name,
+                                      const gchar       *password);
+
+G_END_DECLS
+
+#endif /* __GOA_IMAP_AUTH_LOGIN_H__ */
diff --git a/src/goabackend/goaimapclient.c b/src/goabackend/goaimapclient.c
new file mode 100644
index 0000000..680d9c4
--- /dev/null
+++ b/src/goabackend/goaimapclient.c
@@ -0,0 +1,281 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2011, 2013 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: David Zeuthen <davidz redhat com>
+ *          Debarshi Ray <debarshir gnome org>
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <glib/gi18n-lib.h>
+
+#include "goalogging.h"
+#include "goaimapclient.h"
+
+/* The timeout used for non-IDLE commands */
+#define COMMAND_TIMEOUT_SEC 30
+
+struct _GoaImapClient
+{
+  /*< private >*/
+  GObject parent_instance;
+};
+
+typedef struct _GoaImapClientClass GoaImapClientClass;
+
+struct _GoaImapClientClass
+{
+  GObjectClass parent_class;
+};
+
+G_DEFINE_TYPE (GoaImapClient, goa_imap_client, G_TYPE_OBJECT);
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+goa_imap_client_init (GoaImapClient *client)
+{
+}
+
+static void
+goa_imap_client_class_init (GoaImapClientClass *klass)
+{
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+GoaImapClient *
+goa_imap_client_new (void)
+{
+  return GOA_IMAP_CLIENT (g_object_new (GOA_TYPE_IMAP_CLIENT, NULL));
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct
+{
+  GCancellable *cancellable;
+  GSimpleAsyncResult *res;
+  GSocketClient *sc;
+  GSocketConnection *conn;
+  GTlsCertificateFlags cert_flags;
+  GTlsClientConnection *tls_conn;
+  GoaTlsType tls_type;
+  gboolean accept_ssl_errors;
+  gulong cancellable_id;
+} CheckData;
+
+static gboolean
+imap_client_check_data_free (gpointer user_data)
+{
+  CheckData *data = user_data;
+
+  if (data->cancellable_id > 0)
+    {
+      g_cancellable_disconnect (data->cancellable, data->cancellable_id);
+      g_object_unref (data->cancellable);
+    }
+
+  g_object_unref (data->res);
+  g_object_unref (data->sc);
+  g_clear_object (&data->conn);
+  g_clear_object (&data->tls_conn);
+  g_slice_free (CheckData, data);
+
+  return G_SOURCE_REMOVE;
+}
+
+static gboolean
+imap_client_check_accept_certificate_cb (GTlsConnection *conn,
+                                         GTlsCertificate *peer_cert,
+                                         GTlsCertificateFlags errors,
+                                         gpointer user_data)
+{
+  CheckData *data = user_data;
+
+  /* Fail the connection if the certificate is invalid. */
+  data->cert_flags = errors;
+  return FALSE;
+}
+
+static void
+imap_client_check_event_cb (GSocketClient *sc,
+                            GSocketClientEvent *event,
+                            GSocketConnectable *connectable,
+                            GIOStream *connection,
+                            gpointer user_data)
+{
+  CheckData *data = user_data;
+
+  if (event != G_SOCKET_CLIENT_TLS_HANDSHAKING)
+    return;
+
+  data->tls_conn = G_TLS_CLIENT_CONNECTION (g_object_ref (connection));
+  if (data->tls_type == GOA_TLS_TYPE_SSL)
+    g_tls_client_connection_set_use_ssl3 (data->tls_conn, TRUE);
+
+  g_signal_connect (data->tls_conn,
+                    "accept-certificate",
+                    G_CALLBACK (imap_client_check_accept_certificate_cb),
+                    data);
+}
+
+static void
+imap_client_check_connect_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  CheckData *data = user_data;
+  GError *error;
+  GSocket *socket;
+
+  error = NULL;
+  data->conn = g_socket_client_connect_to_host_finish (data->sc, res, &error);
+  if (data->conn == NULL)
+    {
+      if (error->code == G_TLS_ERROR_BAD_CERTIFICATE)
+        {
+          GError *tls_error;
+
+          tls_error = NULL;
+          goa_utils_set_error_ssl (&tls_error, data->cert_flags);
+          g_simple_async_result_take_error (data->res, tls_error);
+        }
+      else if (error->code != G_IO_ERROR_CANCELLED)
+        {
+          g_simple_async_result_set_error (data->res,
+                                           GOA_ERROR,
+                                           GOA_ERROR_FAILED, /* TODO: more specific */
+                                           _("Connection failed: %s"),
+                                           error->message);
+        }
+      g_error_free (error);
+      goto error;
+    }
+
+  /* fail quickly */
+  socket = g_socket_connection_get_socket (data->conn);
+  g_socket_set_timeout (socket, COMMAND_TIMEOUT_SEC);
+
+  client->dis = g_data_input_stream_new (g_io_stream_get_input_stream (G_IO_STREAM (client->c)));
+  client->dos = g_data_output_stream_new (g_io_stream_get_output_stream (G_IO_STREAM (client->c)));
+  g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (client->dis), FALSE);
+  g_filter_output_stream_set_close_base_stream (G_FILTER_OUTPUT_STREAM (client->dos), FALSE);
+  g_data_input_stream_set_newline_type (client->dis, G_DATA_STREAM_NEWLINE_TYPE_CR_LF);
+
+  /* Authenticate via the passed in auth helper */
+  if (!goa_imap_auth_run_sync (auth,
+                               client->dis,
+                               client->dos,
+                               cancellable,
+                               error))
+    goto out;
+
+
+  ret = TRUE;
+
+ error:
+  g_simple_async_result_set_op_res_gboolean (data->res, FALSE);
+  g_simple_async_result_complete_in_idle (data->res);
+  g_idle_add (imap_client_check_data_free, data);
+}
+
+void
+goa_imap_client_check (GoaImapClient       *client,
+                       const gchar         *host_and_port,
+                       GoaTlsType           tls_type,
+                       gboolean             accept_ssl_errors,
+                       GoaImapAuth         *auth,
+                       GCancellable        *cancellable,
+                       GAsyncReadyCallback *callback,
+                       gpointer             user_data)
+{
+  CheckData *data;
+  GSocketClient *sc;
+
+  g_return_if_fail (GOA_IS_IMAP_CLIENT (client));
+  g_return_if_fail (host_and_port != NULL || host_and_port[0] != '\0');
+  g_return_if_fail (GOA_IS_IMAP_AUTH (auth));
+  g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+  g_return_if_fail (error == NULL || *error == NULL);
+
+  data = g_slice_new0 (CheckData);
+  data->res = g_simple_async_result_new (G_OBJECT (client), callback, user_data, goa_imap_client_check);
+
+  data->sc = g_socket_client_new ();
+  if (tls_type != GOA_TLS_TYPE_NONE)
+    g_socket_client_set_tls (data->sc, TRUE);
+  g_signal_connect (data->sc, "event", G_CALLBACK (imap_client_check_event_cb), data);
+
+  data->tls_type = tls_type;
+  data->accept_ssl_errors = accept_ssl_errors;
+
+  if (accept_ssl_errors)
+    g_socket_client_set_tls_validation_flags (client->sc, 0);
+
+  if (cancellable != NULL)
+    {
+      data->cancellable = g_object_ref (cancellable);
+      data->cancellable_id = g_cancellable_connect (data->cancellable,
+                                                    G_CALLBACK (imap_client_check_cancelled_cb),
+                                                    data,
+                                                    NULL);
+      g_simple_async_result_set_check_cancellable (data->res, data->cancellable);
+    }
+
+  g_socket_client_connect_to_host (data->sc,
+                                   host_and_port,
+                                   (data->tls_type == GOA_TLS_TYPE_SSL) ? 993 : 143,
+                                   imap_client_check_connect_cb,
+                                   data);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * goa_imap_client_disconnect_sync:
+ * @client: A #GoaImapClient.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Closes the connection used by @client, if any. The calling thread
+ * is blocked while the operation is pending.
+ *
+ * Returns: %TRUE if the connection was closed, %FALSE if @error is set.
+ */
+gboolean
+goa_imap_client_disconnect_sync (GoaImapClient  *client,
+                                 GCancellable   *cancellable,
+                                 GError        **error)
+{
+  gboolean ret;
+
+  g_return_val_if_fail (GOA_IS_IMAP_CLIENT (client), FALSE);
+  g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+  g_mutex_lock (client->lock);
+  if (client->c == NULL)
+    ret = TRUE;
+  else
+    ret = g_io_stream_close (G_IO_STREAM (client->c), cancellable, error);
+  g_mutex_unlock (client->lock);
+  return ret;
+}
+
diff --git a/src/goabackend/goaimapclient.h b/src/goabackend/goaimapclient.h
new file mode 100644
index 0000000..4359c52
--- /dev/null
+++ b/src/goabackend/goaimapclient.h
@@ -0,0 +1,61 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2011, 2013 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: David Zeuthen <davidz redhat com>
+ *          Debarshi Ray <debarshir gnome org>
+ */
+
+#if !defined (__GOA_BACKEND_INSIDE_GOA_BACKEND_H__) && !defined (GOA_BACKEND_COMPILATION)
+#error "Only <goabackend/goabackend.h> can be included directly."
+#endif
+
+#ifndef __GOA_IMAP_CLIENT_H__
+#define __GOA_IMAP_CLIENT_H__
+
+#include <gio/gio.h>
+#include <glib.h>
+#include <glib-object.h>
+
+#include "goabackendenums-priv.h"
+#include "goaimapauth.h"
+
+G_BEGIN_DECLS
+
+#define GOA_TYPE_IMAP_CLIENT         (goa_imap_client_get_type ())
+#define GOA_IMAP_CLIENT(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GOA_TYPE_IMAP_CLIENT, GoaImapClient))
+#define GOA_IS_IMAP_CLIENT(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOA_TYPE_IMAP_CLIENT))
+
+typedef struct _GoaImapClient GoaImapClient;
+
+GType           goa_imap_client_get_type          (void) G_GNUC_CONST;
+GoaImapClient  *goa_imap_client_new               (void);
+gboolean        goa_imap_client_check_sync        (GoaImapClient  *client,
+                                                   const gchar    *host_and_port,
+                                                   GoaTlsType      tls_type,
+                                                   gboolean        accept_ssl_errors,
+                                                   GoaImapAuth    *auth,
+                                                   GCancellable   *cancellable,
+                                                   GError        **error);
+gboolean        goa_imap_client_disconnect_sync   (GoaImapClient  *client,
+                                                   GCancellable   *cancellable,
+                                                   GError        **error);
+
+G_END_DECLS
+
+#endif /* __GOA_IMAP_CLIENT_H__ */
diff --git a/src/goabackend/goaimapsmtpprovider.c b/src/goabackend/goaimapsmtpprovider.c
new file mode 100644
index 0000000..a67b932
--- /dev/null
+++ b/src/goabackend/goaimapsmtpprovider.c
@@ -0,0 +1,961 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2011, 2013 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: David Zeuthen <davidz redhat com>
+ *          Debarshi Ray <debarshir gnome org>
+ */
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+
+#include "goaimapauthlogin.h"
+#include "goaimapclient.h"
+#include "goaimapsmtpprovider.h"
+#include "goalogging.h"
+#include "goaprovider.h"
+#include "goautils.h"
+
+/**
+ * GoaImapSmtpProvider:
+ *
+ * The #GoaImapSmtpProvider structure contains only private data and should
+ * only be accessed using the provided API.
+ */
+struct _GoaImapSmtpProvider
+{
+  /*< private >*/
+  GoaProvider parent_instance;
+};
+
+typedef struct _GoaImapSmtpProviderClass GoaImapSmtpProviderClass;
+
+struct _GoaImapSmtpProviderClass
+{
+  GoaProviderClass parent_class;
+};
+
+/**
+ * SECTION:goaimapsmtpprovider
+ * @title: GoaImapSmtpProvider
+ * @short_description: A provider for IMAP and SMTP servers
+ *
+ * #GoaImapSmtpProvider is used to access IMAP and SMTP mail servers.
+ */
+
+G_DEFINE_TYPE_WITH_CODE (GoaImapSmtpProvider, goa_imap_smtp_provider, GOA_TYPE_PROVIDER,
+                         g_io_extension_point_implement (GOA_PROVIDER_EXTENSION_POINT_NAME,
+							 g_define_type_id,
+							 "imap_smtp",
+							 0));
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static const gchar *
+get_provider_type (GoaProvider *_provider)
+{
+  return "imap_smtp";
+}
+
+static const gchar *
+get_provider_name (GoaProvider *_provider)
+{
+  return g_strdup (_("IMAP/SMTP Account"));
+}
+
+static GoaProviderGroup
+get_provider_group (GoaProvider *_provider)
+{
+  return GOA_PROVIDER_GROUP_MAIL;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean on_handle_get_password (GoaPasswordBased      *interface,
+                                        GDBusMethodInvocation *invocation,
+                                        const gchar           *id,
+                                        gpointer               user_data);
+
+static gboolean
+build_object (GoaProvider         *provider,
+              GoaObjectSkeleton   *object,
+              GKeyFile            *key_file,
+              const gchar         *group,
+              GDBusConnection     *connection,
+              gboolean             just_added,
+              GError             **error)
+{
+  GoaAccount *account;
+  GoaMail *mail;
+  GoaPasswordBased *password_based;
+  gboolean enabled;
+  gboolean imap_use_ssl;
+  gboolean imap_use_tls;
+  gboolean imap_ignore_bad_tls;
+  gboolean ret;
+  gboolean smtp_use_ssl;
+  gboolean smtp_use_tls;
+  gboolean smtp_ignore_bad_tls;
+  gchar *email_address;
+  gchar *imap_host;
+  gchar *imap_user_name;
+  gchar *smtp_host;
+  gchar *smtp_user_name;
+
+  account = NULL;
+  mail = NULL;
+  password_based = NULL;
+  email_address = NULL;
+  imap_host = NULL;
+  imap_user_name = NULL;
+  smtp_host = NULL;
+  smtp_user_name = NULL;
+
+  ret = FALSE;
+
+  /* Chain up */
+  if (!GOA_PROVIDER_CLASS (goa_imap_smtp_provider_parent_class)->build_object (provider,
+                                                                               object,
+                                                                               key_file,
+                                                                               group,
+                                                                               connection,
+                                                                               just_added,
+                                                                               error))
+    goto out;
+
+  password_based = goa_object_get_password_based (GOA_OBJECT (object));
+  if (password_based == NULL)
+    {
+      password_based = goa_password_based_skeleton_new ();
+      /* Ensure D-Bus method invocations run in their own thread */
+      g_dbus_interface_skeleton_set_flags (G_DBUS_INTERFACE_SKELETON (password_based),
+                                           G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD);
+      goa_object_skeleton_set_password_based (object, password_based);
+      g_signal_connect (password_based,
+                        "handle-get-password",
+                        G_CALLBACK (on_handle_get_password),
+                        NULL);
+    }
+
+  account = goa_object_get_account (GOA_OBJECT (object));
+
+  /* Email */
+  mail = goa_object_get_mail (GOA_OBJECT (object));
+  enabled = g_key_file_get_boolean (key_file, group, "Enabled", NULL);
+  if (enabled)
+    {
+      if (mail == NULL)
+        {
+          email_address = g_key_file_get_string (key_file, group, "EmailAddress", NULL);
+
+          imap_host = g_key_file_get_string (key_file, group, "ImapHost", NULL);
+          imap_user_name = g_key_file_get_string (key_file, group, "ImapUserName", NULL);
+          if (imap_user_name == NULL)
+            imap_user_name = g_strdup (g_get_user_name ());
+          imap_use_ssl = g_key_file_get_boolean (key_file, group, "ImapUseSsl", NULL);
+          imap_use_tls = g_key_file_get_boolean (key_file, group, "ImapUseTls", NULL);
+          imap_ignore_bad_tls = g_key_file_get_boolean (key_file, group, "ImapIgnoreBadTls", NULL);
+
+          smtp_host = g_key_file_get_string (key_file, group, "SmtpHost", NULL);
+          smtp_user_name = g_key_file_get_string (key_file, group, "SmtpUserName", NULL);
+          if (smtp_user_name == NULL)
+            smtp_user_name = g_strdup (g_get_user_name ());
+          smtp_use_ssl = g_key_file_get_boolean (key_file, group, "SmtpUseSsl", NULL);
+          smtp_use_tls = g_key_file_get_boolean (key_file, group, "SmtpUseTls", NULL);
+          smtp_ignore_bad_tls = g_key_file_get_boolean (key_file, group, "SmtpIgnoreBadTls", NULL);
+
+          mail = goa_mail_skeleton_new ();
+          g_object_set (G_OBJECT (mail),
+                        "email-address", email_address,
+                        "imap-supported", TRUE,
+                        "imap-host", imap_host,
+                        "imap-user-name", imap_user_name,
+                        "imap-use-ssl", imap_use_ssl,
+                        "imap-use-tls", imap_use_tls,
+                        "smtp-supported", TRUE,
+                        "smtp-host", smtp_host,
+                        "smtp-user-name", smtp_user_name,
+                        "smtp-use-ssl", smtp_use_ssl,
+                        "smtp-use-tls", smtp_use_tls,
+                        NULL);
+          goa_object_skeleton_set_mail (object, mail);
+        }
+    }
+  else
+    {
+      if (mail != NULL)
+        goa_object_skeleton_set_mail (object, NULL);
+    }
+
+  ret = TRUE;
+
+ out:
+  g_clear_object (&account);
+  g_clear_object (&mail);
+  g_clear_object (&password_based);
+  g_free (email_address);
+  g_free (imap_host);
+  g_free (imap_user_name);
+  g_free (smtp_host);
+  g_free (smtp_user_name);
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+ensure_credentials_sync (GoaProvider         *provider,
+                         GoaObject           *object,
+                         gint                *out_expires_in,
+                         GCancellable        *cancellable,
+                         GError             **error)
+{
+  /* TODO: we could try and log into the mail server etc. but for now we don't */
+  if (out_expires_in != NULL)
+    *out_expires_in = 0;
+  return TRUE;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+add_combo_box (GtkWidget     *grid1,
+               GtkWidget     *grid2,
+               const gchar   *text,
+               GtkWidget    **out_combo_box)
+{
+  GtkStyleContext *context;
+  GtkWidget *label;
+  GtkWidget *combo_box;
+
+  label = gtk_label_new_with_mnemonic (text);
+  context = gtk_widget_get_style_context (label);
+  gtk_style_context_add_class (context, GTK_STYLE_CLASS_DIM_LABEL);
+  gtk_widget_set_vexpand (label, TRUE);
+  gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
+  gtk_container_add (GTK_CONTAINER (grid1), label);
+
+  combo_box = gtk_combo_box_text_new ();
+  gtk_widget_set_hexpand (entry, TRUE);
+  gtk_widget_set_vexpand (entry, TRUE);
+  gtk_container_add (GTK_CONTAINER (grid2), entry);
+
+  gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo_box);
+  if (out_combo_box != NULL)
+    *out_combo_box = combo_box;
+}
+
+static void
+add_entry (GtkWidget     *grid1,
+           GtkWidget     *grid2,
+           const gchar   *text,
+           GtkWidget    **out_entry)
+{
+  GtkStyleContext *context;
+  GtkWidget *label;
+  GtkWidget *entry;
+
+  label = gtk_label_new_with_mnemonic (text);
+  context = gtk_widget_get_style_context (label);
+  gtk_style_context_add_class (context, GTK_STYLE_CLASS_DIM_LABEL);
+  gtk_widget_set_vexpand (label, TRUE);
+  gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
+  gtk_container_add (GTK_CONTAINER (grid1), label);
+
+  entry = gtk_entry_new ();
+  gtk_widget_set_hexpand (entry, TRUE);
+  gtk_widget_set_vexpand (entry, TRUE);
+  gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
+  gtk_entry_set_max_length (GTK_ENTRY (entry), 132);
+  gtk_container_add (GTK_CONTAINER (grid2), entry);
+
+  gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
+  if (out_entry != NULL)
+    *out_entry = entry;
+}
+
+static void
+add_check_button (GtkWidget     *table,
+                  guint          row,
+                  const gchar   *text,
+                  GtkWidget    **out_check_button)
+{
+  GtkWidget *button;
+  button = gtk_check_button_new_with_mnemonic (text);
+  gtk_table_attach (GTK_TABLE (table), button,
+                    1, 2,
+                    row, row + 1,
+                    GTK_FILL, GTK_FILL, 0, 0);
+  if (out_check_button != NULL)
+    *out_check_button = button;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct
+{
+  GCancellable *cancellable;
+
+  GtkDialog *dialog;
+  GMainLoop *loop;
+
+  GtkWidget *cluebar;
+  GtkWidget *cluebar_label;
+  GtkWidget *notebook;
+  GtkWidget *forward_button;
+  GtkWidget *progress_grid;
+
+  GtkWidget *email_address;
+  GtkWidget *name;
+
+  GtkWidget *imap_server;
+  GtkWidget *imap_username;
+  GtkWidget *imap_password;
+  GtkWidget *imap_use_tls;
+
+  GtkWidget *smtp_server;
+  GtkWidget *smtp_username;
+  GtkWidget *smtp_password;
+  GtkWidget *smtp_use_tls;
+
+  gchar *account_object_path;
+
+  GError *error;
+} AddAccountData;
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+is_valid_email_address (const gchar *email, gchar **out_username, gchar **out_domain)
+{
+  gchar *at;
+  gchar *dot;
+
+  if (email == NULL || email[0] == '\0')
+    return FALSE;
+
+  at = strchr (email, '@');
+  if (at == NULL || *(at + 1) == '\0')
+    return FALSE;
+
+  dot = strchr (at + 1, '.');
+  if (dot == NULL || *(dot + 1) == '\0')
+    return FALSE;
+
+  if (out_username != NULL)
+    {
+      *out_username = g_strdup (email);
+      (*out_username)[at - email] = '\0';
+    }
+
+  if (out_domain != NULL)
+    *out_domain = g_strdup (at + 1);
+
+  return TRUE;
+}
+
+static void
+on_email_address_changed (GtkEditable *editable, gpointer user_data)
+{
+  AddAccountData *data = user_data;
+  gboolean can_add;
+  const gchar *email;
+
+  email = gtk_entry_get_text (GTK_ENTRY (data->email_address));
+  can_add = is_valid_email_address (email, NULL, NULL);
+  gtk_dialog_set_response_sensitive (data->dialog, GTK_RESPONSE_OK, can_add);
+}
+
+static void
+on_imap_changed (GtkEditable *editable, gpointer user_data)
+{
+  AddAccountData *data = user_data;
+  gboolean can_add;
+
+  can_add = gtk_entry_get_text_length (GTK_ENTRY (data->imap_password)) != 0
+            && gtk_entry_get_text_length (GTK_ENTRY (data->imap_server)) != 0
+            && gtk_entry_get_text_length (GTK_ENTRY (data->imap_username)) != 0;
+  gtk_dialog_set_response_sensitive (data->dialog, GTK_RESPONSE_OK, can_add);
+}
+
+static void
+on_smtp_changed (GtkEditable *editable, gpointer user_data)
+{
+  AddAccountData *data = user_data;
+  gboolean can_add;
+
+  can_add = gtk_entry_get_text_length (GTK_ENTRY (data->smtp_password)) != 0
+            && gtk_entry_get_text_length (GTK_ENTRY (data->smtp__server)) != 0
+            && gtk_entry_get_text_length (GTK_ENTRY (data->smtp_username)) != 0;
+  gtk_dialog_set_response_sensitive (data->dialog, GTK_RESPONSE_OK, can_add);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+check_imap_settings (AddAccountData  *data,
+                     GError         **error)
+{
+  GoaImapClient *client;
+  GoaImapAuth *auth;
+  gboolean ret;
+  GError *local_error;
+
+  ret = FALSE;
+
+  auth = goa_imap_auth_login_new (NULL,
+                                  NULL,
+                                  gtk_entry_get_text (GTK_ENTRY (data->imap_user_entry)),
+                                  gtk_entry_get_text (GTK_ENTRY (data->imap_password_entry)));
+
+  /* TODO: run in thread and show spinner while connecting */
+  client = goa_imap_client_new ();
+  if (!goa_imap_client_connect_sync (client,
+                                     gtk_entry_get_text (GTK_ENTRY (data->imap_server_entry)),
+                                     gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->imap_use_tls)),
+                                     gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->imap_ignore_bad_tls)),
+                                     auth,
+                                     NULL, /* cancellable */
+                                     error))
+    goto out;
+
+  ret = TRUE;
+
+ out:
+  local_error = NULL;
+  if (!goa_imap_client_disconnect_sync (client,
+                                        NULL, /* GCancellable */
+                                        &local_error))
+    {
+      goa_warning ("Error closing connection: %s (%s, %d)",
+                   local_error->message, g_quark_to_string (local_error->domain), local_error->code);
+      g_error_free (local_error);
+    }
+  g_object_unref (client);
+  g_object_unref (auth);
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+create_account_details_ui (GoaProvider    *provider,
+                           GtkDialog      *dialog,
+                           GtkBox         *vbox,
+                           gboolean        new_account,
+                           AddAccountData *data)
+{
+  GtkWidget *action_area;
+  GtkWidget *grid0;
+  GtkWidget *grid1;
+  GtkWidget *grid2;
+  GtkWidget *hbox;
+
+  goa_utils_set_dialog_title (provider, dialog, new_account);
+
+  grid0 = gtk_grid_new ();
+  gtk_container_set_border_width (GTK_CONTAINER (grid0), 5);
+  gtk_widget_set_margin_bottom (grid0, 6);
+  gtk_orientable_set_orientation (GTK_ORIENTABLE (grid0), GTK_ORIENTATION_VERTICAL);
+  gtk_grid_set_row_spacing (GTK_GRID (grid0), 12);
+  gtk_container_add (GTK_CONTAINER (vbox), grid0);
+
+  data->cluebar = gtk_info_bar_new ();
+  gtk_info_bar_set_message_type (GTK_INFO_BAR (data->cluebar), GTK_MESSAGE_ERROR);
+  gtk_widget_set_hexpand (data->cluebar, TRUE);
+  gtk_widget_set_no_show_all (data->cluebar, TRUE);
+  gtk_container_add (GTK_CONTAINER (grid0), data->cluebar);
+
+  data->cluebar_label = gtk_label_new ("");
+  gtk_label_set_line_wrap (GTK_LABEL (data->cluebar_label), TRUE);
+  gtk_container_add (GTK_CONTAINER (gtk_info_bar_get_content_area (GTK_INFO_BAR (data->cluebar))),
+                     data->cluebar_label);
+
+  data->notebook = gtk_notebook_new ();
+  gtk_notebook_set_show_border (GTK_NOTEBOOK (data->notebook), FALSE);
+  gtk_notebook_set_show_tabs (GTK_NOTEBOOK (data->notebook), FALSE);
+  gtk_container_add (GTK_CONTAINER (grid0), data->notebook);
+
+  /* Introduction*/
+
+  grid1 = gtk_grid_new ();
+  gtk_orientable_set_orientation (GTK_ORIENTABLE (grid1), GTK_ORIENTATION_VERTICAL);
+  gtk_grid_set_row_spacing (GTK_GRID (grid1), 12);
+
+  grid2 = gtk_grid_new ();
+  gtk_orientable_set_orientation (GTK_ORIENTABLE (grid2), GTK_ORIENTATION_VERTICAL);
+  gtk_grid_set_row_spacing (GTK_GRID (grid2), 12);
+
+  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+  gtk_box_set_homogeneous (GTK_BOX (hbox), FALSE);
+  gtk_box_pack_start (GTK_BOX (hbox), grid1, FALSE, FALSE, 0);
+  gtk_box_pack_start (GTK_BOX (hbox), grid2, TRUE, TRUE, 0);
+  gtk_notebook_append_page (data->notebook, hbox, NULL);
+
+  add_entry (grid1, grid2, _("_E-mail"), &data->email_address);
+  add_entry (grid1, grid2, _("_Name"), &data->name);
+
+  g_signal_connect (data->email_address, "changed", G_CALLBACK (on_email_address_changed), data);
+
+  /* IMAP */
+
+  grid1 = gtk_grid_new ();
+  gtk_orientable_set_orientation (GTK_ORIENTABLE (grid1), GTK_ORIENTATION_VERTICAL);
+  gtk_grid_set_row_spacing (GTK_GRID (grid1), 12);
+
+  grid2 = gtk_grid_new ();
+  gtk_orientable_set_orientation (GTK_ORIENTABLE (grid2), GTK_ORIENTATION_VERTICAL);
+  gtk_grid_set_row_spacing (GTK_GRID (grid2), 12);
+
+  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+  gtk_box_set_homogeneous (GTK_BOX (hbox), FALSE);
+  gtk_box_pack_start (GTK_BOX (hbox), grid1, FALSE, FALSE, 0);
+  gtk_box_pack_start (GTK_BOX (hbox), grid2, TRUE, TRUE, 0);
+  gtk_notebook_append_page (data->notebook, hbox, NULL);
+
+  add_entry (grid1, grid2, _("IMAP _Server"), &data->imap_server);
+  add_entry (grid1, grid2, _("User_name"), &data->imap_username);
+  add_entry (grid1, grid2, _("_Password"), &data->imap_password);
+  add_combo_box (grid1, grid2, _("_Encryption"), &data->imap_encryption);
+
+  gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (data->imap_encryption),
+                             "none",
+                             _("None"));
+  gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (data->imap_encryption),
+                             "starttls",
+                             _("STARTTLS after connecting"));
+  gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (data->imap_encryption),
+                             "ssl",
+                             _("SSL on a dedicated port"));
+
+  gtk_entry_set_visibility (GTK_ENTRY (data->imap_password), FALSE);
+
+  g_signal_connect (data->imap_server, "changed", G_CALLBACK (on_imap_changed), data);
+  g_signal_connect (data->imap_username, "changed", G_CALLBACK (on_imap_changed), data);
+  g_signal_connect (data->imap_password, "changed", G_CALLBACK (on_imap_changed), data);
+
+  /* SMTP */
+
+  grid1 = gtk_grid_new ();
+  gtk_orientable_set_orientation (GTK_ORIENTABLE (grid1), GTK_ORIENTATION_VERTICAL);
+  gtk_grid_set_row_spacing (GTK_GRID (grid1), 12);
+
+  grid2 = gtk_grid_new ();
+  gtk_orientable_set_orientation (GTK_ORIENTABLE (grid2), GTK_ORIENTATION_VERTICAL);
+  gtk_grid_set_row_spacing (GTK_GRID (grid2), 12);
+
+  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
+  gtk_box_set_homogeneous (GTK_BOX (hbox), FALSE);
+  gtk_box_pack_start (GTK_BOX (hbox), grid1, FALSE, FALSE, 0);
+  gtk_box_pack_start (GTK_BOX (hbox), grid2, TRUE, TRUE, 0);
+  gtk_notebook_append_page (data->notebook, hbox, NULL);
+
+  add_entry (grid1, grid2, _("SMTP _Server"), &data->smtp_server);
+  add_entry (grid1, grid2, _("User_name"), &data->smtp_username);
+  add_entry (grid1, grid2, _("_Password"), &data->smtp_password);
+  gtk_entry_set_visibility (GTK_ENTRY (data->smtp_password), FALSE);
+
+  /* -- */
+
+  data->forward_button = gtk_dialog_add_button (data->dialog, GTK_STOCK_GO_FORWARD, GTK_RESPONSE_OK);
+  gtk_dialog_set_default_response (data->dialog, GTK_RESPONSE_OK);
+  gtk_dialog_set_response_sensitive (data->dialog, GTK_RESPONSE_OK, FALSE);
+
+  action_area = gtk_dialog_get_action_area (data->dialog);
+
+  data->progress_grid = gtk_grid_new ();
+  gtk_widget_set_no_show_all (data->progress_grid, TRUE);
+  gtk_orientable_set_orientation (GTK_ORIENTABLE (data->progress_grid), GTK_ORIENTATION_HORIZONTAL);
+  gtk_grid_set_column_spacing (GTK_GRID (data->progress_grid), 3);
+  gtk_box_pack_end (GTK_BOX (action_area), data->progress_grid, FALSE, FALSE, 0);
+  gtk_box_reorder_child (GTK_BOX (action_area), data->progress_grid, 0);
+  gtk_button_box_set_child_non_homogeneous (GTK_BUTTON_BOX (action_area), data->progress_grid, TRUE);
+
+  spinner = gtk_spinner_new ();
+  gtk_widget_set_size_request (spinner, 20, 20);
+  gtk_widget_show (spinner);
+  gtk_spinner_start (GTK_SPINNER (spinner));
+  gtk_container_add (GTK_CONTAINER (data->progress_grid), spinner);
+
+  label = gtk_label_new (_("Connectingâ"));
+  gtk_widget_show (label);
+  gtk_container_add (GTK_CONTAINER (data->progress_grid), label);
+
+  gtk_window_get_size (GTK_WINDOW (data->dialog), &width, NULL);
+  gtk_widget_set_size_request (GTK_WIDGET (data->dialog), width, -1);
+}
+
+static void
+guess_imap_smtp (AddAccountData *data)
+{
+  const gchar *email_address;
+  gchar *imap_server;
+  gchar *smtp_server;
+  gchar *username;
+  gchar *domain;
+
+  domain = NULL;
+  imap_server = NULL;
+  smtp_server = NULL;
+  username = NULL;
+
+  email_address = gtk_entry_get_text (GTK_ENTRY (data->email_address));
+  if (!is_valid_email_address (email_address, &username, &domain))
+    goto out;
+
+  /* TODO: Consult http://api.gnome.org/evolution/autoconfig/1.1/<domain> */
+
+  imap_server = g_strconcat ("imap.", domain, NULL);
+  smtp_server = g_strconcat ("smtp.", domain, NULL);
+
+  gtk_entry_set_text (GTK_ENTRY (data->imap_username), username);
+  gtk_entry_set_text (GTK_ENTRY (data->smtp_username), username);
+  gtk_entry_set_text (GTK_ENTRY (data->imap_server), imap_server);
+  gtk_entry_set_text (GTK_ENTRY (data->smtp_server), smtp_server);
+
+ out:
+  g_free (imap_server);
+  g_free (smtp_server);
+  g_free (username);
+  g_free (domain);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+add_account_cb (GoaManager *manager, GAsyncResult *res, gpointer user_data)
+{
+  AddAccountData *data = user_data;
+  goa_manager_call_add_account_finish (manager,
+                                       &data->account_object_path,
+                                       res,
+                                       &data->error);
+  g_main_loop_quit (data->loop);
+}
+
+static void
+dialog_response_cb (GtkDialog *dialog, gint response_id, gpointer user_data)
+{
+  AddAccountData *data = user_data;
+
+  if (response_id == GTK_RESPONSE_CANCEL)
+    g_cancellable_cancel (data->cancellable);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static GoaObject *
+add_account (GoaProvider    *provider,
+             GoaClient      *client,
+             GtkDialog      *dialog,
+             GtkBox         *vbox,
+             GError        **error)
+{
+  AddAccountData data;
+  GVariantBuilder credentials;
+  GVariantBuilder details;
+  GoaImapAuth *imap_auth;
+  GoaImapClient *imap_client;
+  GoaObject *ret;
+  GError *local_error;
+  gboolean accept_ssl_errors;
+  const gchar *password;
+  const gchar *server;
+  const gchar *username;
+  gint response;
+
+  imap_client = NULL;
+  accept_ssl_errors = FALSE;
+
+  ret = NULL;
+
+  memset (&data, 0, sizeof (AddAccountData));
+  data.cancellable = g_cancellable_new ();
+  data.loop = g_main_loop_new (NULL, FALSE);
+  data.dialog = dialog;
+  data.error = NULL;
+
+  create_account_details_ui (provider, dialog, vbox, TRUE, &data);
+  gtk_widget_show_all (GTK_WIDGET (vbox));
+  g_signal_connect (dialog, "response", G_CALLBACK (dialog_response_cb), &data);
+
+  imap_client = goa_imap_client_new ();
+
+  gtk_notebook_set_current_page (GTK_NOTEBOOK (data.notebook), 0);
+  gtk_widget_grab_focus (data.email_address);
+
+  response = gtk_dialog_run (GTK_DIALOG (dialog));
+  if (response != GTK_RESPONSE_OK)
+    {
+      g_set_error (error,
+                   GOA_ERROR,
+                   GOA_ERROR_DIALOG_DISMISSED,
+                   _("Dialog was dismissed"));
+      goto out;
+    }
+
+  guess_imap_smtp (&data);
+
+  gtk_notebook_next_page (GTK_NOTEBOOK (data.notebook));
+  gtk_widget_grab_focus (data.imap_password);
+
+ imap_again:
+  response = gtk_dialog_run (GTK_DIALOG (dialog));
+  if (response != GTK_RESPONSE_OK)
+    {
+      g_set_error (error,
+                   GOA_ERROR,
+                   GOA_ERROR_DIALOG_DISMISSED,
+                   _("Dialog was dismissed"));
+      goto out;
+    }
+
+  password = gtk_entry_get_text (GTK_ENTRY (data.imap_password));
+  server = gtk_entry_get_text (GTK_ENTRY (data.imap_server));
+  username = gtk_entry_get_text (GTK_ENTRY (data.imap_username));
+
+  g_cancellable_reset (data.cancellable);
+  imap_auth = goa_imap_auth_login_new (NULL, NULL, username, password);
+
+  gtk_notebook_next_page (GTK_NOTEBOOK (data.notebook));
+  gtk_widget_grab_focus (data.smtp_password);
+
+  response = gtk_dialog_run (GTK_DIALOG (dialog));
+  if (response != GTK_RESPONSE_OK)
+    {
+      g_set_error (error,
+                   GOA_ERROR,
+                   GOA_ERROR_DIALOG_DISMISSED,
+                   _("Dialog was dismissed"));
+      goto out;
+    }
+
+  /* Check that IMAP settings work */
+  local_error = NULL;
+  if (!check_imap_settings (&data, &local_error))
+    {
+      gchar *markup;
+      markup = g_strdup_printf ("<b>%s:</b> %s",
+                                _("Error connecting to IMAP server"),
+                                local_error->message);
+      gtk_label_set_markup (GTK_LABEL (data.cluebar_label), markup);
+      gtk_widget_set_no_show_all (data.cluebar, FALSE);
+      gtk_widget_show_all (data.cluebar);
+      g_free (markup);
+      g_error_free (local_error);
+      goto imap_again;
+    }
+
+  /* hide cluebar errors */
+  gtk_widget_hide (data.cluebar);
+
+  /* ------------------------------------------------------------ */
+  /* Then show the SMTP page */
+
+  gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), 2);
+  gtk_widget_grab_focus (data.smtp_server_entry);
+
+  /* Re-use the password from the IMAP page */
+  gtk_entry_set_text (GTK_ENTRY (data.smtp_password_entry),
+                      gtk_entry_get_text (GTK_ENTRY (data.imap_password_entry)));
+
+ smtp_again:
+  response = gtk_dialog_run (GTK_DIALOG (dialog));
+  if (response != GTK_RESPONSE_OK)
+    {
+      g_set_error (error,
+                   GOA_ERROR,
+                   GOA_ERROR_DIALOG_DISMISSED,
+                   "dismissed");
+      goto out;
+    }
+
+  /* Check that SMTP settings work */
+  local_error = NULL;
+  if (!check_smtp_settings (&data, &local_error))
+    {
+      gchar *markup;
+      markup = g_strdup_printf ("<b>%s:</b> %s",
+                                _("Error connecting to SMTP server"),
+                                local_error->message);
+      gtk_label_set_markup (GTK_LABEL (data.cluebar_label), markup);
+      gtk_widget_set_no_show_all (data.cluebar, FALSE);
+      gtk_widget_show_all (data.cluebar);
+      g_free (markup);
+      g_error_free (local_error);
+      goto smtp_again;
+    }
+
+  g_variant_builder_init (&credentials, G_VARIANT_TYPE_VARDICT);
+  g_variant_builder_add (&credentials, "{sv}", "imap-password",
+                         g_variant_new_string (gtk_entry_get_text (GTK_ENTRY (data.imap_password_entry))));
+  g_variant_builder_add (&credentials, "{sv}", "smtp-password",
+                         g_variant_new_string (gtk_entry_get_text (GTK_ENTRY (data.smtp_password_entry))));
+
+  g_variant_builder_init (&details, G_VARIANT_TYPE ("a{ss}"));
+  g_variant_builder_add (&details, "{ss}", "Enabled", "true");
+  g_variant_builder_add (&details, "{ss}", "EmailAddress",
+                         gtk_entry_get_text (GTK_ENTRY (data.intro_address_entry)));
+  g_variant_builder_add (&details, "{ss}", "ImapHost",
+                         gtk_entry_get_text (GTK_ENTRY (data.imap_server_entry)));
+  g_variant_builder_add (&details, "{ss}", "ImapUserName",
+                         gtk_entry_get_text (GTK_ENTRY (data.imap_user_entry)));
+  g_variant_builder_add (&details, "{ss}", "ImapUseTls",
+                         gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data.imap_use_tls))  ? "true" : "false");
+  g_variant_builder_add (&details, "{ss}", "ImapIgnoreBadTls",
+                         gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data.imap_ignore_bad_tls)) ? "true" : "false");
+  g_variant_builder_add (&details, "{ss}", "SmtpHost",
+                         gtk_entry_get_text (GTK_ENTRY (data.smtp_server_entry)));
+  g_variant_builder_add (&details, "{ss}", "SmtpUserName",
+                         gtk_entry_get_text (GTK_ENTRY (data.smtp_user_entry)));
+  g_variant_builder_add (&details, "{ss}", "SmtpUseTls",
+                         gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data.smtp_use_tls))  ? "true" : "false");
+  g_variant_builder_add (&details, "{ss}", "SmtpIgnoreBadTls",
+                         gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data.smtp_ignore_bad_tls)) ? "true" : "false");
+
+  /* OK, everything is dandy, add the account */
+  /* we want the GoaClient to update before this method returns (so it
+   * can create a proxy for the new object) so run the mainloop while
+   * waiting for this to complete
+   */
+  goa_manager_call_add_account (goa_client_get_manager (client),
+                                goa_provider_get_provider_type (GOA_PROVIDER (provider)),
+                                gtk_entry_get_text (GTK_ENTRY (data.intro_address_entry)),
+                                gtk_entry_get_text (GTK_ENTRY (data.intro_address_entry)),
+                                g_variant_builder_end (&credentials),
+                                g_variant_builder_end (&details),
+                                NULL, /* GCancellable* */
+                                (GAsyncReadyCallback) add_account_cb,
+                                &data);
+  g_main_loop_run (data.loop);
+  if (data.error != NULL)
+    goto out;
+
+  ret = GOA_OBJECT (g_dbus_object_manager_get_object (goa_client_get_object_manager (client),
+                                                      data.account_object_path));
+ out:
+  /* We might have an object even when data.error is set.
+   * eg., if we failed to store the credentials in the keyring.
+   */
+  if (data.error != NULL)
+    g_propagate_error (error, data.error);
+  else
+    g_assert (ret != NULL);
+
+  g_free (data.account_object_path);
+  if (data.loop != NULL)
+    g_main_loop_unref (data.loop);
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+show_account (GoaProvider         *provider,
+              GoaClient           *client,
+              GoaObject           *object,
+              GtkBox              *vbox,
+              GtkGrid             *left,
+              GtkGrid             *right)
+{
+  /* Chain up */
+  GOA_PROVIDER_CLASS (goa_imap_smtp_provider_parent_class)->show_account (provider,
+                                                                          client,
+                                                                          object,
+                                                                          vbox,
+                                                                          left,
+                                                                          right);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+goa_imap_smtp_provider_init (GoaImapSmtpProvider *provider)
+{
+}
+
+static void
+goa_imap_smtp_provider_class_init (GoaImapSmtpProviderClass *klass)
+{
+  GoaProviderClass *provider_class;
+
+  provider_class = GOA_PROVIDER_CLASS (klass);
+  provider_class->get_provider_type          = get_provider_type;
+  provider_class->get_provider_name          = get_provider_name;
+  provider_class->get_provider_group         = get_provider_group;
+  provider_class->add_account                = add_account;
+  provider_class->build_object               = build_object;
+  provider_class->show_account               = show_account;
+  provider_class->ensure_credentials_sync    = ensure_credentials_sync;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+on_handle_get_password (GoaPasswordBased      *interface,
+                        GDBusMethodInvocation *invocation,
+                        const gchar           *id,
+                        gpointer               user_data)
+{
+  GoaObject *object;
+  GoaAccount *account;
+  GoaProvider *provider;
+  GError *error;
+  GVariant *credentials;
+  gchar *password;
+
+  /* TODO: maybe log what app is requesting access */
+
+  password = NULL;
+  credentials = NULL;
+
+  object = GOA_OBJECT (g_dbus_interface_get_object (G_DBUS_INTERFACE (interface)));
+  account = goa_object_peek_account (object);
+  provider = goa_provider_get_for_provider_type (goa_account_get_provider_type (account));
+
+  error = NULL;
+  credentials = goa_utils_lookup_credentials_sync (provider,
+                                                   object,
+                                                   NULL, /* GCancellable* */
+                                                   &error);
+  if (credentials == NULL)
+    {
+      g_dbus_method_invocation_take_error (invocation, error);
+      goto out;
+    }
+
+  if (!g_variant_lookup (credentials, id, "s", &password))
+    {
+      g_dbus_method_invocation_return_error (invocation,
+                                             GOA_ERROR,
+                                             GOA_ERROR_FAILED, /* TODO: more specific */
+                                             "Did not find password with id `%s' in credentials",
+                                             id);
+      goto out;
+    }
+
+  goa_password_based_complete_get_password (interface, invocation, password);
+
+ out:
+  g_free (password);
+  if (credentials != NULL)
+    g_variant_unref (credentials);
+  g_object_unref (provider);
+  return TRUE; /* invocation was handled */
+}
+
diff --git a/src/goabackend/goaimapsmtpprovider.h b/src/goabackend/goaimapsmtpprovider.h
new file mode 100644
index 0000000..e18f6fb
--- /dev/null
+++ b/src/goabackend/goaimapsmtpprovider.h
@@ -0,0 +1,43 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2011, 2013 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: David Zeuthen <davidz redhat com>
+ *          Debarshi Ray <debarshir gnome org>
+ */
+
+#if !defined (__GOA_BACKEND_INSIDE_GOA_BACKEND_H__) && !defined (GOA_BACKEND_COMPILATION)
+#error "Only <goabackend/goabackend.h> can be included directly."
+#endif
+
+#ifndef __GOA_IMAP_SMTP_PROVIDER_H__
+#define __GOA_IMAP_SMTP_PROVIDER_H__
+
+#include <goabackend/goabackendtypes.h>
+
+G_BEGIN_DECLS
+
+#define GOA_TYPE_IMAP_SMTP_PROVIDER   (goa_imap_smtp_provider_get_type ())
+#define GOA_IMAP_SMTP_PROVIDER(o)     (G_TYPE_CHECK_INSTANCE_CAST ((o), GOA_TYPE_IMAP_SMTP_PROVIDER, GoaImapSmtpProvider))
+#define GOA_IS_IMAP_SMTP_PROVIDER(o)  (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOA_TYPE_IMAP_SMTP_PROVIDER))
+
+GType goa_imap_smtp_provider_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __GOA_IMAP_SMTP_PROVIDER_H__ */
diff --git a/src/goabackend/goaprovider.c b/src/goabackend/goaprovider.c
index b768fb3..c49cbff 100644
--- a/src/goabackend/goaprovider.c
+++ b/src/goabackend/goaprovider.c
@@ -1,6 +1,6 @@
 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
 /*
- * Copyright (C) 2011, 2012 Red Hat, Inc.
+ * Copyright (C) 2011, 2012, 2013 Red Hat, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -31,6 +31,7 @@
 #include "goaexchangeprovider.h"
 #include "goagoogleprovider.h"
 #include "goafacebookprovider.h"
+#include "goaimapsmtpprovider.h"
 #include "goaowncloudprovider.h"
 #include "goayahooprovider.h"
 #include "goatwitterprovider.h"
@@ -678,6 +679,9 @@ ensure_builtins_loaded (void)
 #ifdef GOA_EXCHANGE_ENABLED
       type = GOA_TYPE_EXCHANGE_PROVIDER;
 #endif
+#ifdef GOA_IMAP_SMTP_ENABLED
+      type = GOA_TYPE_IMAP_SMTP_PROVIDER;
+#endif
 #ifdef GOA_KERBEROS_ENABLED
       type = GOA_TYPE_KERBEROS_PROVIDER;
 #endif


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