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



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

    Add IMAP/SMTP
    
    Currently it assumes IMAP LOGIN and SMTP PLAIN as authentication
    methods for checking the credentials. Other authentication mechanisms
    can be added by extending GoaMailAuth.
    
    We would also like to use the
    http://api.gnome.org/evolution/autoconfig/1.1/ database for guessing
    some of the configuration items for well-known services.
    
    Based on code written by David Zeuthen that was earlier removed in
    d8f3227b1f5f0e62713a697c2a0d34671d0edcbe
    
    Fixes: https://bugzilla.gnome.org/692736

 configure.ac                          |   11 +
 src/goabackend/Makefile.am            |    5 +
 src/goabackend/goabackendenums-priv.h |   16 +
 src/goabackend/goabackendtypes.h      |    7 +-
 src/goabackend/goaimapauthlogin.c     |  374 ++++++++++
 src/goabackend/goaimapauthlogin.h     |   55 ++
 src/goabackend/goaimapsmtpprovider.c  | 1241 +++++++++++++++++++++++++++++++++
 src/goabackend/goaimapsmtpprovider.h  |   45 ++
 src/goabackend/goamailauth.c          |  175 +++++
 src/goabackend/goamailauth.h          |   83 +++
 src/goabackend/goamailclient.c        |  330 +++++++++
 src/goabackend/goamailclient.h        |   71 ++
 src/goabackend/goaprovider.c          |    6 +-
 src/goabackend/goasmtpauthplain.c     |  520 ++++++++++++++
 src/goabackend/goasmtpauthplain.h     |   56 ++
 15 files changed, 2990 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..93185c0 100644
--- a/src/goabackend/Makefile.am
+++ b/src/goabackend/Makefile.am
@@ -60,12 +60,17 @@ libgoa_backend_1_0_la_SOURCES =						\
 	goaewsclient.h			goaewsclient.c			\
 	goahttpclient.h			goahttpclient.c			\
 	goaprovider-priv.h		goaprovider.c			\
+	goamailauth.h			goamailauth.c			\
+	goaimapauthlogin.h		goaimapauthlogin.c		\
+	goasmtpauthplain.h		goasmtpauthplain.c		\
+	goamailclient.h			goamailclient.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/goaimapauthlogin.c b/src/goabackend/goaimapauthlogin.c
new file mode 100644
index 0000000..ad46340
--- /dev/null
+++ b/src/goabackend/goaimapauthlogin.c
@@ -0,0 +1,374 @@
+/* -*- 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 <stdlib.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
+{
+  GoaMailAuth parent_instance;
+
+  GoaProvider *provider;
+  GoaObject *object;
+  gchar *username;
+  gchar *password;
+};
+
+typedef struct
+{
+  GoaMailAuthClass parent_class;
+
+} GoaImapAuthLoginClass;
+
+enum
+{
+  PROP_0,
+  PROP_PROVIDER,
+  PROP_OBJECT,
+  PROP_USERNAME,
+  PROP_PASSWORD
+};
+
+static gboolean goa_imap_auth_login_run_sync (GoaMailAuth         *_auth,
+                                              GDataInputStream    *input,
+                                              GDataOutputStream   *output,
+                                              GCancellable        *cancellable,
+                                              GError             **error);
+
+G_DEFINE_TYPE (GoaImapAuthLogin, goa_imap_auth_login, GOA_TYPE_MAIL_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->username);
+  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_USERNAME:
+      g_value_set_string (value, auth->username);
+      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_USERNAME:
+      auth->username = 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;
+  GoaMailAuthClass *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_MAIL_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_USERNAME,
+                                   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.
+ * @username: 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 #GoaMailAuth to be used for username/password
+ * authentication using LOGIN over IMAP.
+ *
+ * Returns: (type GoaImapAuthLogin): A #GoaImapAuthLogin. Free with
+ * g_object_unref().
+ */
+GoaMailAuth *
+goa_imap_auth_login_new (GoaProvider       *provider,
+                         GoaObject         *object,
+                         const gchar       *username,
+                         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 (username != NULL, NULL);
+  return GOA_MAIL_AUTH (g_object_new (GOA_TYPE_IMAP_AUTH_LOGIN,
+                                      "provider", provider,
+                                      "object", object,
+                                      "user-name", username,
+                                      "password", password,
+                                      NULL));
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+goa_imap_auth_login_run_sync (GoaMailAuth         *_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;
+
+  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->username, 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..6c66e48
--- /dev/null
+++ b/src/goabackend/goaimapauthlogin.h
@@ -0,0 +1,55 @@
+/* -*- 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_LOGIN_H__
+#define __GOA_IMAP_AUTH_LOGIN_H__
+
+#include <gio/gio.h>
+#include <glib.h>
+#include <glib-object.h>
+
+#include <goabackend/goabackendtypes.h>
+
+#include "goamailauth.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))
+
+typedef struct _GoaImapAuthLogin GoaImapAuthLogin;
+
+GType        goa_imap_auth_login_get_type  (void) G_GNUC_CONST;
+GoaMailAuth *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/goaimapsmtpprovider.c b/src/goabackend/goaimapsmtpprovider.c
new file mode 100644
index 0000000..5a5457b
--- /dev/null
+++ b/src/goabackend/goaimapsmtpprovider.c
@@ -0,0 +1,1241 @@
+/* -*- 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 "goamailclient.h"
+#include "goaimapsmtpprovider.h"
+#include "goalogging.h"
+#include "goaprovider.h"
+#include "goaprovider-priv.h"
+#include "goasmtpauthplain.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,
+                         goa_provider_ensure_extension_points_registered ();
+                         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 gchar *
+get_provider_name (GoaProvider *provider, GoaObject *object)
+{
+  return g_strdup (_("IMAP and SMTP"));
+}
+
+static GoaProviderGroup
+get_provider_group (GoaProvider *provider)
+{
+  return GOA_PROVIDER_GROUP_MAIL;
+}
+
+static GIcon *
+get_provider_icon (GoaProvider *provider, GoaObject *object)
+{
+  return g_themed_icon_new_with_default_fallbacks ("mail-unread-symbolic");
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+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_accept_ssl_errors;
+  gboolean imap_use_ssl;
+  gboolean imap_use_tls;
+  gboolean ret;
+  gboolean smtp_accept_ssl_errors;
+  gboolean smtp_use_ssl;
+  gboolean smtp_use_tls;
+  gchar *email_address;
+  gchar *imap_host;
+  gchar *imap_username;
+  gchar *name;
+  gchar *smtp_host;
+  gchar *smtp_username;
+
+  account = NULL;
+  mail = NULL;
+  password_based = NULL;
+  email_address = NULL;
+  imap_host = NULL;
+  imap_username = NULL;
+  name = NULL;
+  smtp_host = NULL;
+  smtp_username = 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);
+          name = g_key_file_get_string (key_file, group, "Name", NULL);
+
+          imap_host = g_key_file_get_string (key_file, group, "ImapHost", NULL);
+          imap_username = g_key_file_get_string (key_file, group, "ImapUserName", NULL);
+          if (imap_username == NULL)
+            imap_username = 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_accept_ssl_errors = g_key_file_get_boolean (key_file, group, "ImapAcceptSslErrors", NULL);
+
+          smtp_host = g_key_file_get_string (key_file, group, "SmtpHost", NULL);
+          smtp_username = g_key_file_get_string (key_file, group, "SmtpUserName", NULL);
+          if (smtp_username == NULL)
+            smtp_username = 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_accept_ssl_errors = g_key_file_get_boolean (key_file, group, "SmtpAcceptSslErrors", NULL);
+
+          mail = goa_mail_skeleton_new ();
+          g_object_set (G_OBJECT (mail),
+                        "email-address", email_address,
+                        "name", name,
+                        "imap-supported", TRUE,
+                        "imap-host", imap_host,
+                        "imap-user-name", imap_username,
+                        "imap-use-ssl", imap_use_ssl,
+                        "imap-use-tls", imap_use_tls,
+                        "imap-accept-ssl-errors", imap_accept_ssl_errors,
+                        "smtp-supported", TRUE,
+                        "smtp-host", smtp_host,
+                        "smtp-user-name", smtp_username,
+                        "smtp-use-ssl", smtp_use_ssl,
+                        "smtp-use-tls", smtp_use_tls,
+                        "smtp-accept-ssl-errors", smtp_accept_ssl_errors,
+                        NULL);
+          goa_object_skeleton_set_mail (object, mail);
+        }
+    }
+  else
+    {
+      if (mail != NULL)
+        goa_object_skeleton_set_mail (object, NULL);
+    }
+
+  if (just_added)
+    {
+      goa_account_set_mail_disabled (account, !enabled);
+      g_signal_connect (account,
+                        "notify::mail-disabled",
+                        G_CALLBACK (goa_util_account_notify_property_cb),
+                        "Enabled");
+    }
+
+  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_username);
+  g_free (name);
+  g_free (smtp_host);
+  g_free (smtp_username);
+  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     *grid,
+               gint           row,
+               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_halign (label, GTK_ALIGN_END);
+  gtk_widget_set_hexpand (label, TRUE);
+  gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1);
+
+  combo_box = gtk_combo_box_text_new ();
+  gtk_widget_set_hexpand (combo_box, TRUE);
+  gtk_grid_attach (GTK_GRID (grid), combo_box, 1, row, 3, 1);
+
+  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     *grid,
+           gint           row,
+           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_halign (label, GTK_ALIGN_END);
+  gtk_widget_set_hexpand (label, TRUE);
+  gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1);
+
+  entry = gtk_entry_new ();
+  gtk_widget_set_hexpand (entry, TRUE);
+  gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
+  gtk_grid_attach (GTK_GRID (grid), entry, 1, row, 3, 1);
+
+  gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
+  if (out_entry != NULL)
+    *out_entry = entry;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+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_encryption;
+
+  GtkWidget *smtp_server;
+  GtkWidget *smtp_username;
+  GtkWidget *smtp_password;
+  GtkWidget *smtp_encryption;
+
+  gchar *account_object_path;
+
+  GError *error;
+} AddAccountData;
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+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 = goa_utils_parse_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 void
+create_account_details_ui (GoaProvider    *provider,
+                           GtkDialog      *dialog,
+                           GtkBox         *vbox,
+                           gboolean        new_account,
+                           AddAccountData *data)
+{
+  GtkWidget *action_area;
+  GtkWidget *grid0;
+  GtkWidget *grid1;
+  GtkWidget *label;
+  GtkWidget *spinner;
+  gint row;
+  gint width;
+  const gchar *real_name;
+
+  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_grid_set_column_spacing (GTK_GRID (grid1), 12);
+  gtk_grid_set_row_spacing (GTK_GRID (grid1), 12);
+  gtk_notebook_append_page (GTK_NOTEBOOK (data->notebook), grid1, NULL);
+
+  row = 0;
+  add_entry (grid1, row++, _("_E-mail"), &data->email_address);
+  add_entry (grid1, row++, _("_Name"), &data->name);
+
+  real_name = g_get_real_name ();
+  if (g_strcmp0 (real_name, "Unknown") != 0)
+    gtk_entry_set_text (GTK_ENTRY (data->name), real_name);
+
+  g_signal_connect (data->email_address, "changed", G_CALLBACK (on_email_address_changed), data);
+
+  /* IMAP */
+
+  grid1 = gtk_grid_new ();
+  gtk_grid_set_column_spacing (GTK_GRID (grid1), 12);
+  gtk_grid_set_row_spacing (GTK_GRID (grid1), 12);
+  gtk_notebook_append_page (GTK_NOTEBOOK (data->notebook), grid1, NULL);
+
+  row = 0;
+  add_entry (grid1, row++, _("IMAP _Server"), &data->imap_server);
+  add_entry (grid1, row++, _("User_name"), &data->imap_username);
+  add_entry (grid1, row++, _("_Password"), &data->imap_password);
+  add_combo_box (grid1, row++, _("_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_combo_box_set_active_id (GTK_COMBO_BOX (data->imap_encryption), "starttls");
+
+  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_grid_set_column_spacing (GTK_GRID (grid1), 12);
+  gtk_grid_set_row_spacing (GTK_GRID (grid1), 12);
+  gtk_notebook_append_page (GTK_NOTEBOOK (data->notebook), grid1, NULL);
+
+  row = 0;
+  add_entry (grid1, row++, _("SMTP _Server"), &data->smtp_server);
+  add_entry (grid1, row++, _("User_name"), &data->smtp_username);
+  add_entry (grid1, row++, _("_Password"), &data->smtp_password);
+  add_combo_box (grid1, row++, _("_Encryption"), &data->smtp_encryption);
+
+  gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (data->smtp_encryption),
+                             "none",
+                             _("None"));
+  gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (data->smtp_encryption),
+                             "starttls",
+                             _("STARTTLS after connecting"));
+  gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (data->smtp_encryption),
+                             "ssl",
+                             _("SSL on a dedicated port"));
+  gtk_combo_box_set_active_id (GTK_COMBO_BOX (data->smtp_encryption), "starttls");
+
+  gtk_entry_set_visibility (GTK_ENTRY (data->smtp_password), FALSE);
+
+  g_signal_connect (data->smtp_server, "changed", G_CALLBACK (on_smtp_changed), data);
+  g_signal_connect (data->smtp_username, "changed", G_CALLBACK (on_smtp_changed), data);
+  g_signal_connect (data->smtp_password, "changed", G_CALLBACK (on_smtp_changed), data);
+
+  /* -- */
+
+  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 gchar *
+get_string_from_tls_type (GoaTlsType tls_type)
+{
+  gchar *str;
+
+  str = NULL;
+
+  switch (tls_type)
+    {
+    case GOA_TLS_TYPE_NONE:
+      str = g_strdup (_("None"));
+      break;
+    case GOA_TLS_TYPE_STARTTLS:
+      str = g_strdup (_("STARTTLS after connecting"));
+      break;
+    case GOA_TLS_TYPE_SSL:
+      str = g_strdup (_("SSL on a dedicated port"));
+      break;
+    default:
+      g_assert_not_reached ();
+    }
+
+  return str;
+}
+
+static GoaTlsType
+get_tls_type_from_string (const gchar *str)
+{
+  GoaTlsType tls_type;
+
+  if (g_strcmp0 (str, "none") == 0)
+    tls_type = GOA_TLS_TYPE_NONE;
+  else if (g_strcmp0 (str, "starttls") == 0)
+    tls_type = GOA_TLS_TYPE_STARTTLS;
+  else if (g_strcmp0 (str, "ssl") == 0)
+    tls_type = GOA_TLS_TYPE_SSL;
+  else
+    g_assert_not_reached ();
+
+  return tls_type;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+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 (!goa_utils_parse_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 void
+imap_check_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  GoaMailClient *client = GOA_MAIL_CLIENT (source_object);
+  AddAccountData *data = user_data;
+
+  goa_mail_client_check_finish (client, res, &data->error);
+  g_main_loop_quit (data->loop);
+  gtk_widget_set_sensitive (data->forward_button, TRUE);
+  gtk_widget_hide (data->progress_grid);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static GoaObject *
+add_account (GoaProvider    *provider,
+             GoaClient      *client,
+             GtkDialog      *dialog,
+             GtkBox         *vbox,
+             GError        **error)
+{
+  AddAccountData data;
+  GVariantBuilder credentials;
+  GVariantBuilder details;
+  GoaMailAuth *imap_auth;
+  GoaMailAuth *smtp_auth;
+  GoaMailClient *mail_client;
+  GoaObject *ret;
+  GoaTlsType imap_tls_type;
+  GoaTlsType smtp_tls_type;
+  gboolean imap_accept_ssl_errors;
+  gboolean smtp_accept_ssl_errors;
+  const gchar *email_address;
+  const gchar *encryption;
+  const gchar *imap_password;
+  const gchar *imap_server;
+  const gchar *imap_username;
+  const gchar *name;
+  const gchar *provider_type;
+  const gchar *smtp_password;
+  const gchar *smtp_server;
+  const gchar *smtp_username;
+  gchar *domain;
+  gint response;
+
+  imap_auth = NULL;
+  smtp_auth = NULL;
+  mail_client = NULL;
+  domain = NULL;
+
+  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);
+
+  mail_client = goa_mail_client_new ();
+
+  /* Introduction */
+
+  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 (&data.error,
+                   GOA_ERROR,
+                   GOA_ERROR_DIALOG_DISMISSED,
+                   _("Dialog was dismissed"));
+      goto out;
+    }
+
+  email_address = gtk_entry_get_text (GTK_ENTRY (data.email_address));
+  name = gtk_entry_get_text (GTK_ENTRY (data.name));
+
+  /* See if there's already an account of this type with the
+   * given identity
+   */
+  provider_type = goa_provider_get_provider_type (provider);
+  if (!goa_utils_check_duplicate (client,
+                                  email_address,
+                                  provider_type,
+                                  (GoaPeekInterfaceFunc) goa_object_peek_password_based,
+                                  &data.error))
+    goto out;
+
+  guess_imap_smtp (&data);
+
+  /* IMAP */
+
+  gtk_notebook_next_page (GTK_NOTEBOOK (data.notebook));
+  gtk_widget_grab_focus (data.imap_password);
+
+  imap_accept_ssl_errors = FALSE;
+
+ imap_again:
+  response = gtk_dialog_run (GTK_DIALOG (dialog));
+  if (response != GTK_RESPONSE_OK)
+    {
+      g_set_error (&data.error,
+                   GOA_ERROR,
+                   GOA_ERROR_DIALOG_DISMISSED,
+                   _("Dialog was dismissed"));
+      goto out;
+    }
+
+  encryption = gtk_combo_box_get_active_id (GTK_COMBO_BOX (data.imap_encryption));
+  imap_tls_type = get_tls_type_from_string (encryption);
+
+  imap_password = gtk_entry_get_text (GTK_ENTRY (data.imap_password));
+  imap_server = gtk_entry_get_text (GTK_ENTRY (data.imap_server));
+  imap_username = gtk_entry_get_text (GTK_ENTRY (data.imap_username));
+
+  g_cancellable_reset (data.cancellable);
+  imap_auth = goa_imap_auth_login_new (NULL, NULL, imap_username, imap_password);
+  goa_mail_client_check (mail_client,
+                         imap_server,
+                         imap_tls_type,
+                         imap_accept_ssl_errors,
+                         (imap_tls_type == GOA_TLS_TYPE_SSL) ? 993 : 143,
+                         imap_auth,
+                         data.cancellable,
+                         imap_check_cb,
+                         &data);
+
+  gtk_widget_set_sensitive (data.forward_button, FALSE);
+  gtk_widget_show (data.progress_grid);
+  g_main_loop_run (data.loop);
+
+  if (g_cancellable_is_cancelled (data.cancellable))
+    {
+      g_prefix_error (&data.error,
+                      _("Dialog was dismissed (%s, %d): "),
+                      g_quark_to_string (data.error->domain),
+                      data.error->code);
+      data.error->domain = GOA_ERROR;
+      data.error->code = GOA_ERROR_DIALOG_DISMISSED;
+      goto out;
+    }
+  else if (data.error != NULL)
+    {
+      gchar *markup;
+
+      if (data.error->code == GOA_ERROR_SSL)
+        {
+          gtk_button_set_label (GTK_BUTTON (data.forward_button), _("_Ignore"));
+          imap_accept_ssl_errors = TRUE;
+        }
+      else
+        {
+          gtk_button_set_label (GTK_BUTTON (data.forward_button), _("_Try Again"));
+          imap_accept_ssl_errors = FALSE;
+        }
+
+      markup = g_strdup_printf ("<b>%s</b>\n%s",
+                                _("Error connecting to IMAP server"),
+                                data.error->message);
+      g_clear_error (&data.error);
+
+      gtk_label_set_markup (GTK_LABEL (data.cluebar_label), markup);
+      g_free (markup);
+
+      gtk_widget_set_no_show_all (data.cluebar, FALSE);
+      gtk_widget_show_all (data.cluebar);
+
+      g_clear_object (&imap_auth);
+      goto imap_again;
+    }
+
+  gtk_widget_set_no_show_all (data.cluebar, TRUE);
+  gtk_widget_hide (data.cluebar);
+  gtk_button_set_label (GTK_BUTTON (data.forward_button), GTK_STOCK_GO_FORWARD);
+
+  /* SMTP */
+
+  /* Re-use the password from the IMAP page */
+  gtk_entry_set_text (GTK_ENTRY (data.smtp_password), imap_password);
+
+  gtk_notebook_next_page (GTK_NOTEBOOK (data.notebook));
+  gtk_widget_grab_focus (data.smtp_password);
+
+  smtp_accept_ssl_errors = FALSE;
+
+ smtp_again:
+  response = gtk_dialog_run (GTK_DIALOG (dialog));
+  if (response != GTK_RESPONSE_OK)
+    {
+      g_set_error (&data.error,
+                   GOA_ERROR,
+                   GOA_ERROR_DIALOG_DISMISSED,
+                   _("Dialog was dismissed"));
+      goto out;
+    }
+
+  encryption = gtk_combo_box_get_active_id (GTK_COMBO_BOX (data.smtp_encryption));
+  smtp_tls_type = get_tls_type_from_string (encryption);
+
+  smtp_password = gtk_entry_get_text (GTK_ENTRY (data.smtp_password));
+  smtp_server = gtk_entry_get_text (GTK_ENTRY (data.smtp_server));
+  smtp_username = gtk_entry_get_text (GTK_ENTRY (data.smtp_username));
+
+  g_cancellable_reset (data.cancellable);
+  goa_utils_parse_email_address (email_address, NULL, &domain);
+  smtp_auth = goa_smtp_auth_plain_new (NULL, NULL, domain, smtp_username, smtp_password);
+  goa_mail_client_check (mail_client,
+                         smtp_server,
+                         smtp_tls_type,
+                         smtp_accept_ssl_errors,
+                         (smtp_tls_type == GOA_TLS_TYPE_SSL) ? 465 : 587,
+                         smtp_auth,
+                         data.cancellable,
+                         imap_check_cb,
+                         &data);
+
+  gtk_widget_set_sensitive (data.forward_button, FALSE);
+  gtk_widget_show (data.progress_grid);
+  g_main_loop_run (data.loop);
+
+  if (g_cancellable_is_cancelled (data.cancellable))
+    {
+      g_prefix_error (&data.error,
+                      _("Dialog was dismissed (%s, %d): "),
+                      g_quark_to_string (data.error->domain),
+                      data.error->code);
+      data.error->domain = GOA_ERROR;
+      data.error->code = GOA_ERROR_DIALOG_DISMISSED;
+      goto out;
+    }
+  else if (data.error != NULL)
+    {
+      gchar *markup;
+
+      if (data.error->code == GOA_ERROR_SSL)
+        {
+          gtk_button_set_label (GTK_BUTTON (data.forward_button), _("_Ignore"));
+          smtp_accept_ssl_errors = TRUE;
+        }
+      else
+        {
+          gtk_button_set_label (GTK_BUTTON (data.forward_button), _("_Try Again"));
+          smtp_accept_ssl_errors = FALSE;
+        }
+
+      markup = g_strdup_printf ("<b>%s</b>\n%s",
+                                _("Error connecting to SMTP server"),
+                                data.error->message);
+      g_clear_error (&data.error);
+
+      gtk_label_set_markup (GTK_LABEL (data.cluebar_label), markup);
+      g_free (markup);
+
+      gtk_widget_set_no_show_all (data.cluebar, FALSE);
+      gtk_widget_show_all (data.cluebar);
+
+      g_clear_object (&smtp_auth);
+      goto smtp_again;
+    }
+
+  gtk_widget_hide (GTK_WIDGET (dialog));
+
+  g_variant_builder_init (&credentials, G_VARIANT_TYPE_VARDICT);
+  g_variant_builder_add (&credentials, "{sv}", "imap-password", g_variant_new_string (imap_password));
+  g_variant_builder_add (&credentials, "{sv}", "smtp-password", g_variant_new_string (smtp_password));
+
+  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", email_address);
+  g_variant_builder_add (&details, "{ss}", "Name", name);
+  g_variant_builder_add (&details, "{ss}", "ImapHost", imap_server);
+  g_variant_builder_add (&details, "{ss}", "ImapUserName", imap_username);
+  g_variant_builder_add (&details, "{ss}",
+                         "ImapUseSsl", (imap_tls_type == GOA_TLS_TYPE_SSL) ? "true" : "false");
+  g_variant_builder_add (&details, "{ss}",
+                         "ImapUseTls", (imap_tls_type == GOA_TLS_TYPE_STARTTLS) ? "true" : "false");
+  g_variant_builder_add (&details, "{ss}",
+                         "ImapAcceptSslErrors", (imap_accept_ssl_errors) ? "true" : "false");
+  g_variant_builder_add (&details, "{ss}", "SmtpHost", smtp_server);
+  g_variant_builder_add (&details, "{ss}", "SmtpUserName", smtp_username);
+  g_variant_builder_add (&details, "{ss}",
+                         "SmtpUseSsl", (smtp_tls_type == GOA_TLS_TYPE_SSL) ? "true" : "false");
+  g_variant_builder_add (&details, "{ss}",
+                         "SmtpUseTls", (smtp_tls_type == GOA_TLS_TYPE_STARTTLS) ? "true" : "false");
+  g_variant_builder_add (&details, "{ss}",
+                         "SmtpAcceptSslErrors", (smtp_accept_ssl_errors) ? "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 (provider),
+                                email_address,
+                                email_address,
+                                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 (domain);
+  g_free (data.account_object_path);
+  if (data.loop != NULL)
+    g_main_loop_unref (data.loop);
+  g_clear_object (&data.cancellable);
+  g_clear_object (&imap_auth);
+  g_clear_object (&smtp_auth);
+  g_clear_object (&mail_client);
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+show_account (GoaProvider         *provider,
+              GoaClient           *client,
+              GoaObject           *object,
+              GtkBox              *vbox,
+              GtkGrid             *left,
+              GtkGrid             *right)
+{
+  GoaTlsType tls_type;
+  GtkStyleContext *context;
+  GtkWidget *grid;
+  GtkWidget *label;
+  GtkWidget *sw;
+  const gchar *username;
+  gchar *value_str;
+  gint row;
+
+  sw = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+  gtk_box_pack_end (vbox, sw, TRUE, TRUE, 0);
+
+  grid = gtk_grid_new ();
+  gtk_widget_set_hexpand (grid, TRUE);
+  gtk_widget_set_vexpand (grid, TRUE);
+  gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
+  gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
+  gtk_container_add (GTK_CONTAINER (sw), grid);
+
+  username = g_get_user_name ();
+  row = 0;
+
+  goa_util_add_account_info_2 (GTK_GRID (grid), row, object);
+
+  row++;
+
+  label = gtk_label_new (_("E-mail"));
+  context = gtk_widget_get_style_context (label);
+  gtk_style_context_add_class (context, GTK_STYLE_CLASS_DIM_LABEL);
+  gtk_widget_set_halign (label, GTK_ALIGN_END);
+  gtk_widget_set_hexpand (label, TRUE);
+  gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1);
+
+  value_str = goa_util_lookup_keyfile_string (object, "EmailAddress");
+  label = gtk_label_new (value_str);
+  gtk_widget_set_halign (label, GTK_ALIGN_START);
+  gtk_widget_set_hexpand (label, TRUE);
+  gtk_grid_attach (GTK_GRID (grid), label, 1, row, 3, 1);
+  g_free (value_str);
+
+  row++;
+
+  label = gtk_label_new (_("Name"));
+  context = gtk_widget_get_style_context (label);
+  gtk_style_context_add_class (context, GTK_STYLE_CLASS_DIM_LABEL);
+  gtk_widget_set_halign (label, GTK_ALIGN_END);
+  gtk_widget_set_hexpand (label, TRUE);
+  gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1);
+
+  value_str = goa_util_lookup_keyfile_string (object, "Name");
+  label = gtk_label_new (value_str);
+  gtk_widget_set_halign (label, GTK_ALIGN_START);
+  gtk_widget_set_hexpand (label, TRUE);
+  gtk_grid_attach (GTK_GRID (grid), label, 1, row, 3, 1);
+  g_free (value_str);
+
+  row++;
+
+  label = gtk_label_new (_("IMAP Details"));
+  gtk_widget_set_halign (label, GTK_ALIGN_END);
+  gtk_widget_set_hexpand (label, TRUE);
+  gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1);
+
+  row++;
+
+  label = gtk_label_new (_("Server"));
+  context = gtk_widget_get_style_context (label);
+  gtk_style_context_add_class (context, GTK_STYLE_CLASS_DIM_LABEL);
+  gtk_widget_set_halign (label, GTK_ALIGN_END);
+  gtk_widget_set_hexpand (label, TRUE);
+  gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1);
+
+  value_str = goa_util_lookup_keyfile_string (object, "ImapHost");
+  label = gtk_label_new (value_str);
+  gtk_widget_set_halign (label, GTK_ALIGN_START);
+  gtk_widget_set_hexpand (label, TRUE);
+  gtk_grid_attach (GTK_GRID (grid), label, 1, row, 3, 1);
+  g_free (value_str);
+
+  value_str = goa_util_lookup_keyfile_string (object, "ImapUserName");
+  if (g_strcmp0 (username, value_str) != 0)
+    {
+      row++;
+
+      label = gtk_label_new (_("Username"));
+      context = gtk_widget_get_style_context (label);
+      gtk_style_context_add_class (context, GTK_STYLE_CLASS_DIM_LABEL);
+      gtk_widget_set_halign (label, GTK_ALIGN_END);
+      gtk_widget_set_hexpand (label, TRUE);
+      gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1);
+
+      label = gtk_label_new (value_str);
+      gtk_widget_set_halign (label, GTK_ALIGN_START);
+      gtk_widget_set_hexpand (label, TRUE);
+      gtk_grid_attach (GTK_GRID (grid), label, 1, row, 3, 1);
+    }
+  g_free (value_str);
+
+  row++;
+
+  label = gtk_label_new (_("Encryption"));
+  context = gtk_widget_get_style_context (label);
+  gtk_style_context_add_class (context, GTK_STYLE_CLASS_DIM_LABEL);
+  gtk_widget_set_halign (label, GTK_ALIGN_END);
+  gtk_widget_set_hexpand (label, TRUE);
+  gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1);
+
+  if (goa_util_lookup_keyfile_boolean (object, "ImapUseSsl"))
+    tls_type = GOA_TLS_TYPE_SSL;
+  else if (goa_util_lookup_keyfile_boolean (object, "ImapUseTls"))
+    tls_type = GOA_TLS_TYPE_STARTTLS;
+  else
+    tls_type = GOA_TLS_TYPE_NONE;
+  value_str = get_string_from_tls_type (tls_type);
+  label = gtk_label_new (value_str);
+  gtk_widget_set_halign (label, GTK_ALIGN_START);
+  gtk_widget_set_hexpand (label, TRUE);
+  gtk_grid_attach (GTK_GRID (grid), label, 1, row, 3, 1);
+  g_free (value_str);
+
+  row++;
+
+  label = gtk_label_new (_("SMTP Details"));
+  gtk_widget_set_halign (label, GTK_ALIGN_END);
+  gtk_widget_set_hexpand (label, TRUE);
+  gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1);
+
+  row++;
+
+  label = gtk_label_new (_("Server"));
+  context = gtk_widget_get_style_context (label);
+  gtk_style_context_add_class (context, GTK_STYLE_CLASS_DIM_LABEL);
+  gtk_widget_set_halign (label, GTK_ALIGN_END);
+  gtk_widget_set_hexpand (label, TRUE);
+  gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1);
+
+  value_str = goa_util_lookup_keyfile_string (object, "SmtpHost");
+  label = gtk_label_new (value_str);
+  gtk_widget_set_halign (label, GTK_ALIGN_START);
+  gtk_widget_set_hexpand (label, TRUE);
+  gtk_grid_attach (GTK_GRID (grid), label, 1, row, 3, 1);
+  g_free (value_str);
+
+  value_str = goa_util_lookup_keyfile_string (object, "SmtpUserName");
+  if (g_strcmp0 (username, value_str) != 0)
+    {
+      row++;
+
+      label = gtk_label_new (_("Username"));
+      context = gtk_widget_get_style_context (label);
+      gtk_style_context_add_class (context, GTK_STYLE_CLASS_DIM_LABEL);
+      gtk_widget_set_halign (label, GTK_ALIGN_END);
+      gtk_widget_set_hexpand (label, TRUE);
+      gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1);
+
+      label = gtk_label_new (value_str);
+      gtk_widget_set_halign (label, GTK_ALIGN_START);
+      gtk_widget_set_hexpand (label, TRUE);
+      gtk_grid_attach (GTK_GRID (grid), label, 1, row, 3, 1);
+    }
+  g_free (value_str);
+
+  row++;
+
+  label = gtk_label_new (_("Encryption"));
+  context = gtk_widget_get_style_context (label);
+  gtk_style_context_add_class (context, GTK_STYLE_CLASS_DIM_LABEL);
+  gtk_widget_set_halign (label, GTK_ALIGN_END);
+  gtk_widget_set_hexpand (label, TRUE);
+  gtk_grid_attach (GTK_GRID (grid), label, 0, row, 1, 1);
+
+  if (goa_util_lookup_keyfile_boolean (object, "SmtpUseSsl"))
+    tls_type = GOA_TLS_TYPE_SSL;
+  else if (goa_util_lookup_keyfile_boolean (object, "SmtpUseTls"))
+    tls_type = GOA_TLS_TYPE_STARTTLS;
+  else
+    tls_type = GOA_TLS_TYPE_NONE;
+  value_str = get_string_from_tls_type (tls_type);
+  label = gtk_label_new (value_str);
+  gtk_widget_set_halign (label, GTK_ALIGN_START);
+  gtk_widget_set_hexpand (label, TRUE);
+  gtk_grid_attach (GTK_GRID (grid), label, 1, row, 3, 1);
+  g_free (value_str);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+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->get_provider_icon          = get_provider_icon;
+  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..571e7f2
--- /dev/null
+++ b/src/goabackend/goaimapsmtpprovider.h
@@ -0,0 +1,45 @@
+/* -*- 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 <glib-object.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))
+
+typedef struct _GoaImapSmtpProvider GoaImapSmtpProvider;
+
+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/goamailauth.c b/src/goabackend/goamailauth.c
new file mode 100644
index 0000000..8c733c9
--- /dev/null
+++ b/src/goabackend/goamailauth.c
@@ -0,0 +1,175 @@
+/* -*- 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 "goamailauth.h"
+
+G_DEFINE_ABSTRACT_TYPE (GoaMailAuth, goa_mail_auth, G_TYPE_OBJECT);
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct
+{
+  GAsyncReadyCallback callback;
+  GDataInputStream *input;
+  GDataOutputStream *output;
+  gpointer user_data;
+} RunData;
+
+static void
+mail_auth_run_data_free (RunData *data)
+{
+  g_object_unref (data->input);
+  g_object_unref (data->output);
+  g_slice_free (RunData, data);
+}
+
+static RunData *
+mail_auth_run_data_new (GDataInputStream *input,
+                        GDataOutputStream *output,
+                        GAsyncReadyCallback callback,
+                        gpointer user_data)
+{
+  RunData *data;
+
+  data = g_slice_new0 (RunData);
+  data->input = g_object_ref (input);
+  data->output = g_object_ref (output);
+  data->callback = callback;
+  data->user_data = user_data;
+
+  return data;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+mail_auth_run_async_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  RunData *data = user_data;
+
+  if (data->callback != NULL)
+    data->callback (source_object, res, data->user_data);
+
+  mail_auth_run_data_free (data);
+}
+
+static void
+mail_auth_run_in_thread_func (GSimpleAsyncResult *res, GObject *object, GCancellable *cancellable)
+{
+  GError *error;
+  RunData *data;
+  gboolean op_res;
+
+  data = (RunData *) g_async_result_get_user_data (G_ASYNC_RESULT (res));
+  op_res = FALSE;
+
+  error = NULL;
+  if (!goa_mail_auth_run_sync (GOA_MAIL_AUTH (object), data->input, data->output, cancellable, &error))
+    {
+      g_simple_async_result_take_error (res, error);
+      goto out;
+    }
+
+  op_res = TRUE;
+
+ out:
+  g_simple_async_result_set_op_res_gboolean (res, op_res);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+goa_mail_auth_init (GoaMailAuth *client)
+{
+}
+
+static void
+goa_mail_auth_class_init (GoaMailAuthClass *klass)
+{
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+gboolean
+goa_mail_auth_run_sync (GoaMailAuth         *auth,
+                        GDataInputStream    *input,
+                        GDataOutputStream   *output,
+                        GCancellable        *cancellable,
+                        GError             **error)
+{
+  g_return_val_if_fail (GOA_IS_MAIL_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_MAIL_AUTH_GET_CLASS (auth)->run_sync (auth, input, output, cancellable, error);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+void
+goa_mail_auth_run (GoaMailAuth         *auth,
+                   GDataInputStream    *input,
+                   GDataOutputStream   *output,
+                   GCancellable        *cancellable,
+                   GAsyncReadyCallback  callback,
+                   gpointer             user_data)
+{
+  GSimpleAsyncResult *simple;
+  RunData *data;
+
+  g_return_if_fail (GOA_IS_MAIL_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));
+
+  data = mail_auth_run_data_new (input, output, callback, user_data);
+
+  simple = g_simple_async_result_new (G_OBJECT (auth), mail_auth_run_async_cb, data, goa_mail_auth_run);
+  g_simple_async_result_set_handle_cancellation (simple, TRUE);
+
+  g_simple_async_result_run_in_thread (simple, mail_auth_run_in_thread_func, G_PRIORITY_DEFAULT, cancellable);
+
+  g_object_unref (simple);
+}
+
+gboolean
+goa_mail_auth_run_finish (GoaMailAuth         *auth,
+                          GAsyncResult        *res,
+                          GError             **error)
+{
+  GSimpleAsyncResult *simple;
+
+  g_return_val_if_fail (g_simple_async_result_is_valid (res, G_OBJECT (auth), goa_mail_auth_run), FALSE);
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+  simple = G_SIMPLE_ASYNC_RESULT (res);
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    return FALSE;
+
+  return g_simple_async_result_get_op_res_gboolean (simple);
+}
diff --git a/src/goabackend/goamailauth.h b/src/goabackend/goamailauth.h
new file mode 100644
index 0000000..89df120
--- /dev/null
+++ b/src/goabackend/goamailauth.h
@@ -0,0 +1,83 @@
+/* -*- 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_MAIL_AUTH_H__
+#define __GOA_MAIL_AUTH_H__
+
+#include <gio/gio.h>
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GOA_TYPE_MAIL_AUTH         (goa_mail_auth_get_type ())
+#define GOA_MAIL_AUTH(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GOA_TYPE_MAIL_AUTH, GoaMailAuth))
+#define GOA_MAIL_AUTH_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), GOA_TYPE_MAIL_AUTH, GoaMailAuthClass))
+#define GOA_MAIL_AUTH_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GOA_TYPE_MAIL_AUTH, GoaMailAuthClass))
+#define GOA_IS_MAIL_AUTH(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOA_TYPE_MAIL_AUTH))
+#define GOA_IS_MAIL_AUTH_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GOA_TYPE_MAIL_AUTH))
+
+typedef struct _GoaMailAuth GoaMailAuth;
+typedef struct _GoaMailAuthClass GoaMailAuthClass;
+typedef struct _GoaMailAuthPrivate GoaMailAuthPrivate;
+
+struct _GoaMailAuth
+{
+  /*< private >*/
+  GObject parent_instance;
+  GoaMailAuthPrivate *priv;
+};
+
+struct _GoaMailAuthClass
+{
+  GObjectClass parent_class;
+  gboolean (*run_sync) (GoaMailAuth         *auth,
+                        GDataInputStream    *input,
+                        GDataOutputStream   *output,
+                        GCancellable        *cancellable,
+                        GError             **error);
+};
+
+GType     goa_mail_auth_get_type     (void) G_GNUC_CONST;
+void      goa_mail_auth_run          (GoaMailAuth         *auth,
+                                      GDataInputStream    *input,
+                                      GDataOutputStream   *output,
+                                      GCancellable        *cancellable,
+                                      GAsyncReadyCallback  callback,
+                                      gpointer             user_data);
+gboolean  goa_mail_auth_run_finish   (GoaMailAuth         *auth,
+                                      GAsyncResult        *res,
+                                      GError             **error);
+gboolean  goa_mail_auth_run_sync     (GoaMailAuth         *auth,
+                                      GDataInputStream    *input,
+                                      GDataOutputStream   *output,
+                                      GCancellable        *cancellable,
+                                      GError             **error);
+
+G_END_DECLS
+
+#endif /* __GOA_MAIL_AUTH_H__ */
diff --git a/src/goabackend/goamailclient.c b/src/goabackend/goamailclient.c
new file mode 100644
index 0000000..2b7ec65
--- /dev/null
+++ b/src/goabackend/goamailclient.c
@@ -0,0 +1,330 @@
+/* -*- 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 "goamailclient.h"
+#include "goalogging.h"
+#include "goautils.h"
+
+/* The timeout used for non-IDLE commands */
+#define COMMAND_TIMEOUT_SEC 30
+
+struct _GoaMailClient
+{
+  /*< private >*/
+  GObject parent_instance;
+};
+
+typedef struct _GoaMailClientClass GoaMailClientClass;
+
+struct _GoaMailClientClass
+{
+  GObjectClass parent_class;
+};
+
+G_DEFINE_TYPE (GoaMailClient, goa_mail_client, G_TYPE_OBJECT);
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+goa_mail_client_init (GoaMailClient *client)
+{
+}
+
+static void
+goa_mail_client_class_init (GoaMailClientClass *klass)
+{
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+GoaMailClient *
+goa_mail_client_new (void)
+{
+  return GOA_MAIL_CLIENT (g_object_new (GOA_TYPE_MAIL_CLIENT, NULL));
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct
+{
+  GCancellable *cancellable;
+  GDataInputStream *input;
+  GDataOutputStream *output;
+  GSimpleAsyncResult *res;
+  GSocketClient *sc;
+  GSocketConnection *conn;
+  GTlsCertificateFlags cert_flags;
+  GTlsClientConnection *tls_conn;
+  GoaMailAuth *auth;
+  GoaTlsType tls_type;
+} CheckData;
+
+static void
+mail_client_check_data_free (CheckData *data)
+{
+  g_object_unref (data->res);
+  g_object_unref (data->sc);
+  g_object_unref (data->auth);
+  g_clear_object (&data->cancellable);
+  g_clear_object (&data->input);
+  g_clear_object (&data->output);
+  g_clear_object (&data->conn);
+  g_clear_object (&data->tls_conn);
+  g_slice_free (CheckData, data);
+}
+
+static gboolean
+mail_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
+mail_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 (mail_client_check_accept_certificate_cb),
+                    data);
+}
+
+static void
+mail_client_check_auth_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  CheckData *data = user_data;
+  GError *error;
+  gboolean op_res;
+
+  op_res = FALSE;
+
+  error = NULL;
+  if (!goa_mail_auth_run_finish (data->auth, res, &error))
+    {
+      g_simple_async_result_take_error (data->res, error);
+      goto out;
+    }
+
+  op_res = TRUE;
+  g_io_stream_close (G_IO_STREAM (data->conn), NULL, NULL);
+
+ out:
+  g_simple_async_result_set_op_res_gboolean (data->res, op_res);
+  g_simple_async_result_complete_in_idle (data->res);
+  mail_client_check_data_free (data);
+}
+
+static void
+mail_client_check_connect_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  CheckData *data = user_data;
+  GInputStream *base_input;
+  GError *error;
+  GOutputStream *base_output;
+  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);
+
+  base_input = g_io_stream_get_input_stream (G_IO_STREAM (data->conn));
+  data->input = g_data_input_stream_new (base_input);
+  g_filter_input_stream_set_close_base_stream (G_FILTER_INPUT_STREAM (data->input), FALSE);
+  g_data_input_stream_set_newline_type (data->input, G_DATA_STREAM_NEWLINE_TYPE_CR_LF);
+
+  base_output = g_io_stream_get_output_stream (G_IO_STREAM (data->conn));
+  data->output = g_data_output_stream_new (base_output);
+  g_filter_output_stream_set_close_base_stream (G_FILTER_OUTPUT_STREAM (data->output), FALSE);
+
+  goa_mail_auth_run (data->auth, data->input, data->output, data->cancellable, mail_client_check_auth_cb, data);
+  return;
+
+ error:
+  g_simple_async_result_set_op_res_gboolean (data->res, FALSE);
+  g_simple_async_result_complete_in_idle (data->res);
+  mail_client_check_data_free (data);
+}
+
+void
+goa_mail_client_check (GoaMailClient       *client,
+                       const gchar         *host_and_port,
+                       GoaTlsType           tls_type,
+                       gboolean             accept_ssl_errors,
+                       guint16              default_port,
+                       GoaMailAuth         *auth,
+                       GCancellable        *cancellable,
+                       GAsyncReadyCallback  callback,
+                       gpointer             user_data)
+{
+  CheckData *data;
+
+  g_return_if_fail (GOA_IS_MAIL_CLIENT (client));
+  g_return_if_fail (host_and_port != NULL || host_and_port[0] != '\0');
+  g_return_if_fail (GOA_IS_MAIL_AUTH (auth));
+  g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+  data = g_slice_new0 (CheckData);
+  data->res = g_simple_async_result_new (G_OBJECT (client), callback, user_data, goa_mail_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 (mail_client_check_event_cb), data);
+
+  data->tls_type = tls_type;
+  data->auth = g_object_ref (auth);
+
+  if (accept_ssl_errors)
+    g_socket_client_set_tls_validation_flags (data->sc, 0);
+
+  if (cancellable != NULL)
+    {
+      data->cancellable = g_object_ref (cancellable);
+      g_simple_async_result_set_check_cancellable (data->res, data->cancellable);
+    }
+
+  g_socket_client_connect_to_host_async (data->sc,
+                                         host_and_port,
+                                         default_port,
+                                         data->cancellable,
+                                         mail_client_check_connect_cb,
+                                         data);
+}
+
+gboolean
+goa_mail_client_check_finish (GoaMailClient *client, GAsyncResult *res, GError **error)
+{
+  GSimpleAsyncResult *simple;
+
+  g_return_val_if_fail (g_simple_async_result_is_valid (res, G_OBJECT (client), goa_mail_client_check), FALSE);
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+  simple = G_SIMPLE_ASYNC_RESULT (res);
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    return FALSE;
+
+  return g_simple_async_result_get_op_res_gboolean (simple);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct
+{
+  GError **error;
+  GMainLoop *loop;
+  gboolean op_res;
+} CheckSyncData;
+
+static void
+mail_client_check_sync_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  CheckSyncData *data = user_data;
+
+  data->op_res = goa_mail_client_check_finish (GOA_MAIL_CLIENT (source_object), res, data->error);
+  g_main_loop_quit (data->loop);
+}
+
+gboolean
+goa_mail_client_check_sync (GoaMailClient  *client,
+                            const gchar    *host_and_port,
+                            GoaTlsType      tls_type,
+                            gboolean        accept_ssl_errors,
+                            guint16         default_port,
+                            GoaMailAuth    *auth,
+                            GCancellable   *cancellable,
+                            GError        **error)
+{
+  CheckSyncData data;
+  GMainContext *context = NULL;
+
+  data.error = error;
+
+  context = g_main_context_new ();
+  g_main_context_push_thread_default (context);
+  data.loop = g_main_loop_new (context, FALSE);
+
+  goa_mail_client_check (client,
+                         host_and_port,
+                         tls_type,
+                         accept_ssl_errors,
+                         default_port,
+                         auth,
+                         cancellable,
+                         mail_client_check_sync_cb,
+                         &data);
+  g_main_loop_run (data.loop);
+  g_main_loop_unref (data.loop);
+
+  g_main_context_pop_thread_default (context);
+  g_main_context_unref (context);
+
+  return data.op_res;
+}
diff --git a/src/goabackend/goamailclient.h b/src/goabackend/goamailclient.h
new file mode 100644
index 0000000..79b02d4
--- /dev/null
+++ b/src/goabackend/goamailclient.h
@@ -0,0 +1,71 @@
+/* -*- 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_MAIL_CLIENT_H__
+#define __GOA_MAIL_CLIENT_H__
+
+#include <gio/gio.h>
+#include <glib.h>
+#include <glib-object.h>
+
+#include "goabackendenums-priv.h"
+#include "goamailauth.h"
+
+G_BEGIN_DECLS
+
+#define GOA_TYPE_MAIL_CLIENT         (goa_mail_client_get_type ())
+#define GOA_MAIL_CLIENT(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GOA_TYPE_MAIL_CLIENT, GoaMailClient))
+#define GOA_IS_MAIL_CLIENT(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOA_TYPE_MAIL_CLIENT))
+
+typedef struct _GoaMailClient GoaMailClient;
+
+GType           goa_mail_client_get_type          (void) G_GNUC_CONST;
+GoaMailClient  *goa_mail_client_new               (void);
+void            goa_mail_client_check             (GoaMailClient        *client,
+                                                   const gchar          *host_and_port,
+                                                   GoaTlsType            tls_type,
+                                                   gboolean              accept_ssl_errors,
+                                                   guint16               default_port,
+                                                   GoaMailAuth          *auth,
+                                                   GCancellable         *cancellable,
+                                                   GAsyncReadyCallback   callback,
+                                                   gpointer              user_data);
+gboolean        goa_mail_client_check_finish      (GoaMailClient        *client,
+                                                   GAsyncResult         *res,
+                                                   GError              **error);
+gboolean        goa_mail_client_check_sync        (GoaMailClient        *client,
+                                                   const gchar          *host_and_port,
+                                                   GoaTlsType            tls_type,
+                                                   gboolean              accept_ssl_errors,
+                                                   guint16               default_port,
+                                                   GoaMailAuth          *auth,
+                                                   GCancellable         *cancellable,
+                                                   GError              **error);
+
+G_END_DECLS
+
+#endif /* __GOA_MAIL_CLIENT_H__ */
diff --git a/src/goabackend/goaprovider.c b/src/goabackend/goaprovider.c
index 56823d9..9bb3433 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
diff --git a/src/goabackend/goasmtpauthplain.c b/src/goabackend/goasmtpauthplain.c
new file mode 100644
index 0000000..000aaab
--- /dev/null
+++ b/src/goabackend/goasmtpauthplain.c
@@ -0,0 +1,520 @@
+/* -*- 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 <string.h>
+
+#include <glib/gi18n-lib.h>
+
+#include "goasmtpauthplain.h"
+#include "goaprovider.h"
+#include "goautils.h"
+
+/**
+ * SECTION:goasmtpauthplain
+ * @title: GoaSmtpAuthPlain
+ * @short_description: PLAIN authentication method for SMTP
+ *
+ * #GoaSmtpAuthPlain implements the <ulink
+ * url="http://tools.ietf.org/html/rfc4616";>PLAIN</ulink>
+ * SASL mechanism (e.g. using usernames / passwords) for SMTP.
+ */
+
+/**
+ * GoaSmtpAuthPlain:
+ *
+ * The #GoaSmtpAuthPlain structure contains only private data
+ * and should only be accessed using the provided API.
+ */
+struct _GoaSmtpAuthPlain
+{
+  GoaMailAuth parent_instance;
+
+  GoaProvider *provider;
+  GoaObject *object;
+  gchar *domain;
+  gchar *username;
+  gchar *password;
+};
+
+typedef struct
+{
+  GoaMailAuthClass parent_class;
+
+} GoaSmtpAuthPlainClass;
+
+enum
+{
+  PROP_0,
+  PROP_PROVIDER,
+  PROP_OBJECT,
+  PROP_DOMAIN,
+  PROP_USERNAME,
+  PROP_PASSWORD
+};
+
+static gboolean goa_smtp_auth_plain_run_sync (GoaMailAuth         *_auth,
+                                              GDataInputStream    *input,
+                                              GDataOutputStream   *output,
+                                              GCancellable        *cancellable,
+                                              GError             **error);
+
+G_DEFINE_TYPE (GoaSmtpAuthPlain, goa_smtp_auth_plain, GOA_TYPE_MAIL_AUTH);
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+goa_smtp_auth_plain_finalize (GObject *object)
+{
+  GoaSmtpAuthPlain *auth = GOA_SMTP_AUTH_PLAIN (object);
+
+  g_clear_object (&auth->provider);
+  g_clear_object (&auth->object);
+  g_free (auth->domain);
+  g_free (auth->username);
+  g_free (auth->password);
+
+  G_OBJECT_CLASS (goa_smtp_auth_plain_parent_class)->finalize (object);
+}
+
+static void
+goa_smtp_auth_plain_get_property (GObject      *object,
+                                  guint         prop_id,
+                                  GValue       *value,
+                                  GParamSpec   *pspec)
+{
+  GoaSmtpAuthPlain *auth = GOA_SMTP_AUTH_PLAIN (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_DOMAIN:
+      g_value_set_string (value, auth->domain);
+      break;
+
+    case PROP_USERNAME:
+      g_value_set_string (value, auth->username);
+      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_smtp_auth_plain_set_property (GObject      *object,
+                                  guint         prop_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+  GoaSmtpAuthPlain *auth = GOA_SMTP_AUTH_PLAIN (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_DOMAIN:
+      auth->domain = g_value_dup_string (value);
+      break;
+
+    case PROP_USERNAME:
+      auth->username = 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_smtp_auth_plain_init (GoaSmtpAuthPlain *client)
+{
+}
+
+static void
+goa_smtp_auth_plain_class_init (GoaSmtpAuthPlainClass *klass)
+{
+  GObjectClass *gobject_class;
+  GoaMailAuthClass *auth_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->finalize     = goa_smtp_auth_plain_finalize;
+  gobject_class->get_property = goa_smtp_auth_plain_get_property;
+  gobject_class->set_property = goa_smtp_auth_plain_set_property;
+
+  auth_class = GOA_MAIL_AUTH_CLASS (klass);
+  auth_class->run_sync = goa_smtp_auth_plain_run_sync;
+
+  /**
+   * GoaSmtpAuthPlain: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));
+
+  /**
+   * GoaSmtpAuthPlain: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));
+
+  /**
+   * GoaSmtpAuthPlain:domain:
+   *
+   * The domail or %NULL.
+   *
+   * If this is %NULL, the domain is obtained from the
+   * email address associated with the #GoaObject.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_DOMAIN,
+                                   g_param_spec_string ("domain",
+                                                        "domain",
+                                                        "domain",
+                                                        NULL,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_WRITABLE |
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GoaSmtpAuthPlain:user-name:
+   *
+   * The user name.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_USERNAME,
+                                   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));
+
+  /**
+   * GoaSmtpAuthPlain:password:
+   *
+   * The password or %NULL.
+   *
+   * If this is %NULL, the credentials are looked up using
+   * goa_utils_lookup_credentials_sync() using the
+   * #GoaSmtpAuthPlain:provider and #GoaSmtpAuthPlain:object for
+   * @provider and @object. The credentials are expected to be a
+   * %G_VARIANT_VARDICT and the key <literal>smtp-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_smtp_auth_plain_new:
+ * @provider: (allow-none): A #GoaPlainProvider or %NULL.
+ * @object: (allow-none): An account object or %NULL.
+ * @domain: (allow-none): The domain to use or %NULL to look it up
+ * (see the #GoaSmtpAuthPlain:domain property).
+ * @username: The user name to use.
+ * @password: (allow-none): The password to use or %NULL to look it up
+ * (see the #GoaSmtpAuthPlain:password property).
+ *
+ * Creates a new #GoaMailAuth to be used for username/password
+ * authentication using PLAIN over SMTP.
+ *
+ * Returns: (type GoaSmtpAuthPlain): A #GoaSmtpAuthPlain. Free with
+ * g_object_unref().
+ */
+GoaMailAuth *
+goa_smtp_auth_plain_new (GoaProvider       *provider,
+                         GoaObject         *object,
+                         const gchar       *domain,
+                         const gchar       *username,
+                         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 (username != NULL, NULL);
+  return GOA_MAIL_AUTH (g_object_new (GOA_TYPE_SMTP_AUTH_PLAIN,
+                                      "provider", provider,
+                                      "object", object,
+                                      "domain", domain,
+                                      "user-name", username,
+                                      "password", password,
+                                      NULL));
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+goa_smtp_auth_plain_run_sync (GoaMailAuth         *_auth,
+                              GDataInputStream    *input,
+                              GDataOutputStream   *output,
+                              GCancellable        *cancellable,
+                              GError             **error)
+{
+  GoaSmtpAuthPlain *auth = GOA_SMTP_AUTH_PLAIN (_auth);
+  gboolean ret;
+  gchar *auth_arg_base64;
+  gchar *auth_arg_plain;
+  gchar *domain;
+  gchar *password;
+  gchar *request;
+  gchar *response;
+  gsize auth_arg_plain_len;
+
+  auth_arg_base64 = NULL;
+  auth_arg_plain = NULL;
+  domain = NULL;
+  password = NULL;
+  request = NULL;
+  response = NULL;
+
+  ret = FALSE;
+
+  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 SMTP PLAIN in keyring: ");
+          goto out;
+        }
+      if (!g_variant_lookup (credentials, "smtp-password", "s", &password))
+        {
+          g_set_error (error,
+                       GOA_ERROR,
+                       GOA_ERROR_FAILED, /* TODO: more specific */
+                       _("Did not find smtp-password in credentials"));
+          g_variant_unref (credentials);
+          goto out;
+        }
+      g_variant_unref (credentials);
+    }
+  else
+    {
+      g_set_error (error,
+                   GOA_ERROR,
+                   GOA_ERROR_FAILED, /* TODO: more specific */
+                   _("Cannot do SMTP PLAIN without a password"));
+      goto out;
+    }
+
+  if (auth->domain != NULL)
+    {
+      domain = g_strdup (auth->domain);
+    }
+  else if (auth->object != NULL)
+    {
+      GoaMail *mail;
+      gchar *email_address;
+
+      mail = goa_object_get_mail (auth->object);
+      if (mail == NULL)
+        {
+          g_set_error (error,
+                       GOA_ERROR,
+                       GOA_ERROR_FAILED, /* TODO: more specific */
+                       _("org.gnome.OnlineAccounts.Mail is not available"));
+          goto out;
+        }
+
+      email_address = goa_mail_dup_email_address (mail);
+      if (!goa_utils_parse_email_address (email_address, NULL, &domain))
+        {
+          g_set_error (error,
+                       GOA_ERROR,
+                       GOA_ERROR_FAILED, /* TODO: more specific */
+                       _("Failed to parse email address"));
+          goto out;
+        }
+
+      g_free (email_address);
+      g_object_unref (mail);
+    }
+  else
+    {
+      g_set_error (error,
+                   GOA_ERROR,
+                   GOA_ERROR_FAILED, /* TODO: more specific */
+                   _("Cannot do SMTP PLAIN without a domain"));
+      goto out;
+    }
+
+  /* Check the greeting */
+
+  response = g_data_input_stream_read_line (input, NULL, cancellable, error);
+  if (response == NULL)
+    goto out;
+  if (g_str_has_prefix (response, "421"))
+    {
+      g_set_error (error,
+                   GOA_ERROR,
+                   GOA_ERROR_FAILED, /* TODO: more specific */
+                   _("Service not available"));
+      goto out;
+    }
+  if (!g_str_has_prefix (response, "220"))
+    {
+      g_set_error (error,
+                   GOA_ERROR,
+                   GOA_ERROR_FAILED, /* TODO: more specific */
+                   "Unexpected response `%s' while doing PLAIN authentication",
+                   response);
+      goto out;
+    }
+  g_clear_pointer (&response, g_free);
+
+  /* Send EHLO */
+
+  request = g_strdup_printf ("EHLO %s\r\n", domain);
+  if (!g_data_output_stream_put_string (output, request, cancellable, error))
+    goto out;
+  g_clear_pointer (&request, g_free);
+
+  /* Check if PLAIN is supported or not */
+
+ ehlo_again:
+  response = g_data_input_stream_read_line (input, NULL, cancellable, error);
+  if (response == NULL)
+    goto out;
+  if (!g_str_has_prefix (response, "250-AUTH"))
+    {
+      g_free (response);
+      goto ehlo_again;
+    }
+  if (strstr (response, "PLAIN") == NULL)
+    {
+      g_set_error (error,
+                   GOA_ERROR,
+                   GOA_ERROR_NOT_SUPPORTED,
+                   _("Server does not support PLAIN"));
+      goto out;
+    }
+  g_clear_pointer (&response, g_free);
+
+  /* Send AUTH PLAIN */
+
+  auth_arg_plain = g_strdup_printf ("%s%c%s%c%s", auth->username, '\0', auth->username, '\0', password);
+  auth_arg_plain_len = 2 * strlen (auth->username) + 2 + strlen (password);
+  auth_arg_base64 = g_base64_encode ((guchar *) auth_arg_plain, auth_arg_plain_len);
+
+  request = g_strdup_printf ("AUTH PLAIN %s\r\n", auth_arg_base64);
+  if (!g_data_output_stream_put_string (output, request, cancellable, error))
+    goto out;
+  g_clear_pointer (&request, g_free);
+
+ auth_again:
+  response = g_data_input_stream_read_line (input, NULL, cancellable, error);
+  if (response == NULL)
+    goto out;
+  if (g_str_has_prefix (response, "250"))
+    {
+      g_free (response);
+      goto auth_again;
+    }
+  if (!g_str_has_prefix (response, "235"))
+    {
+      g_set_error (error,
+                   GOA_ERROR,
+                   GOA_ERROR_FAILED, /* TODO: more specific */
+                   _("Authentication failed"));
+      goto out;
+    }
+  g_clear_pointer (&response, g_free);
+
+  ret = TRUE;
+
+ out:
+  g_free (auth_arg_base64);
+  g_free (auth_arg_plain);
+  g_free (domain);
+  g_free (password);
+  g_free (response);
+  g_free (request);
+  return ret;
+}
diff --git a/src/goabackend/goasmtpauthplain.h b/src/goabackend/goasmtpauthplain.h
new file mode 100644
index 0000000..9880dcb
--- /dev/null
+++ b/src/goabackend/goasmtpauthplain.h
@@ -0,0 +1,56 @@
+/* -*- 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_SMTP_AUTH_PLAIN_H__
+#define __GOA_SMTP_AUTH_PLAIN_H__
+
+#include <gio/gio.h>
+#include <glib.h>
+#include <glib-object.h>
+
+#include <goabackend/goabackendtypes.h>
+
+#include "goamailauth.h"
+
+G_BEGIN_DECLS
+
+#define GOA_TYPE_SMTP_AUTH_PLAIN         (goa_smtp_auth_plain_get_type ())
+#define GOA_SMTP_AUTH_PLAIN(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GOA_TYPE_SMTP_AUTH_PLAIN, GoaSmtpAuthPlain))
+#define GOA_IS_SMTP_AUTH_PLAIN(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOA_TYPE_SMTP_AUTH_PLAIN))
+
+typedef struct _GoaSmtpAuthPlain GoaSmtpAuthPlain;
+
+GType        goa_smtp_auth_plain_get_type  (void) G_GNUC_CONST;
+GoaMailAuth *goa_smtp_auth_plain_new       (GoaProvider       *provider,
+                                            GoaObject         *object,
+                                            const gchar       *domain,
+                                            const gchar       *user_name,
+                                            const gchar       *password);
+
+G_END_DECLS
+
+#endif /* __GOA_SMTP_AUTH_PLAIN_H__ */


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