[gnome-online-accounts/gnome-3-8] mail-auth: Support STARTTLS for IMAP and SMTP



commit ca44a61b925b74d18fd3d757639fd0d4280b3bad
Author: Debarshi Ray <debarshir gnome org>
Date:   Fri Mar 8 23:58:00 2013 +0100

    mail-auth: Support STARTTLS for IMAP and SMTP
    
    Add a starttls_sync virtual method which GoaImapAuthLogin and
    GoaSmtpAuthPlain should implement for STARTTLS.
    
    Fixes: https://bugzilla.gnome.org/695355

 src/goabackend/goaimapauthlogin.c |   57 ++++++++++++++
 src/goabackend/goamailauth.c      |   77 +++++++++++++++++++
 src/goabackend/goamailauth.h      |   13 +++
 src/goabackend/goasmtpauthplain.c |  153 ++++++++++++++++++++++++++++++++++---
 4 files changed, 290 insertions(+), 10 deletions(-)
---
diff --git a/src/goabackend/goaimapauthlogin.c b/src/goabackend/goaimapauthlogin.c
index c35a8a2..8b3ba72 100644
--- a/src/goabackend/goaimapauthlogin.c
+++ b/src/goabackend/goaimapauthlogin.c
@@ -74,6 +74,9 @@ static gboolean goa_imap_auth_login_is_needed (GoaMailAuth        *auth);
 static gboolean goa_imap_auth_login_run_sync (GoaMailAuth         *_auth,
                                               GCancellable        *cancellable,
                                               GError             **error);
+static gboolean goa_imap_auth_login_starttls_sync (GoaMailAuth    *_auth,
+                                                   GCancellable   *cancellable,
+                                                   GError        **error);
 
 G_DEFINE_TYPE (GoaImapAuthLogin, goa_imap_auth_login, GOA_TYPE_MAIL_AUTH);
 
@@ -178,6 +181,7 @@ goa_imap_auth_login_class_init (GoaImapAuthLoginClass *klass)
   auth_class = GOA_MAIL_AUTH_CLASS (klass);
   auth_class->is_needed = goa_imap_auth_login_is_needed;
   auth_class->run_sync = goa_imap_auth_login_run_sync;
+  auth_class->starttls_sync = goa_imap_auth_login_starttls_sync;
 
   /**
    * GoaImapAuthLogin:provider:
@@ -384,3 +388,56 @@ goa_imap_auth_login_run_sync (GoaMailAuth         *_auth,
   g_free (password);
   return ret;
 }
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+goa_imap_auth_login_starttls_sync (GoaMailAuth         *_auth,
+                                   GCancellable        *cancellable,
+                                   GError             **error)
+{
+  GDataInputStream *input;
+  GDataOutputStream *output;
+  gchar *request;
+  gchar *response;
+  gboolean ret;
+
+  request = NULL;
+  response = NULL;
+
+  ret = FALSE;
+
+  input = goa_mail_auth_get_input (_auth);
+  output = goa_mail_auth_get_output (_auth);
+
+  request = g_strdup ("A001 STARTTLS\r\n");
+  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);
+  return ret;
+}
diff --git a/src/goabackend/goamailauth.c b/src/goabackend/goamailauth.c
index 4984b03..6585f66 100644
--- a/src/goabackend/goamailauth.c
+++ b/src/goabackend/goamailauth.c
@@ -68,6 +68,29 @@ mail_auth_run_in_thread_func (GSimpleAsyncResult *res, GObject *object, GCancell
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
+mail_auth_starttls_in_thread_func (GSimpleAsyncResult *res, GObject *object, GCancellable *cancellable)
+{
+  GError *error;
+  gboolean op_res;
+
+  op_res = FALSE;
+
+  error = NULL;
+  if (!goa_mail_auth_starttls_sync (GOA_MAIL_AUTH (object), 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_dispose (GObject *object)
 {
   GoaMailAuth *auth = GOA_MAIL_AUTH (object);
@@ -229,6 +252,60 @@ goa_mail_auth_run_finish (GoaMailAuth         *auth,
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+gboolean
+goa_mail_auth_starttls_sync (GoaMailAuth         *auth,
+                             GCancellable        *cancellable,
+                             GError             **error)
+{
+  g_return_val_if_fail (GOA_IS_MAIL_AUTH (auth), FALSE);
+  g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
+  return GOA_MAIL_AUTH_GET_CLASS (auth)->starttls_sync (auth, cancellable, error);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+void
+goa_mail_auth_starttls (GoaMailAuth         *auth,
+                        GCancellable        *cancellable,
+                        GAsyncReadyCallback  callback,
+                        gpointer             user_data)
+{
+  GSimpleAsyncResult *simple;
+
+  g_return_if_fail (GOA_IS_MAIL_AUTH (auth));
+  g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+  simple = g_simple_async_result_new (G_OBJECT (auth), callback, user_data, goa_mail_auth_starttls);
+  g_simple_async_result_set_handle_cancellation (simple, TRUE);
+
+  g_simple_async_result_run_in_thread (simple,
+                                       mail_auth_starttls_in_thread_func,
+                                       G_PRIORITY_DEFAULT,
+                                       cancellable);
+
+  g_object_unref (simple);
+}
+
+gboolean
+goa_mail_auth_starttls_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_starttls), 
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);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
 GDataInputStream *
 goa_mail_auth_get_input (GoaMailAuth *auth)
 {
diff --git a/src/goabackend/goamailauth.h b/src/goabackend/goamailauth.h
index 6cdff1d..d5f9d81 100644
--- a/src/goabackend/goamailauth.h
+++ b/src/goabackend/goamailauth.h
@@ -59,6 +59,9 @@ struct _GoaMailAuthClass
   gboolean (*run_sync) (GoaMailAuth         *auth,
                         GCancellable        *cancellable,
                         GError             **error);
+  gboolean (*starttls_sync) (GoaMailAuth    *auth,
+                             GCancellable   *cancellable,
+                             GError        **error);
 };
 
 GType     goa_mail_auth_get_type     (void) G_GNUC_CONST;
@@ -73,6 +76,16 @@ gboolean  goa_mail_auth_run_finish   (GoaMailAuth         *auth,
 gboolean  goa_mail_auth_run_sync     (GoaMailAuth         *auth,
                                       GCancellable        *cancellable,
                                       GError             **error);
+void      goa_mail_auth_starttls     (GoaMailAuth         *auth,
+                                      GCancellable        *cancellable,
+                                      GAsyncReadyCallback  callback,
+                                      gpointer             user_data);
+gboolean  goa_mail_auth_starttls_finish (GoaMailAuth      *auth,
+                                         GAsyncResult     *res,
+                                         GError          **error);
+gboolean  goa_mail_auth_starttls_sync (GoaMailAuth        *auth,
+                                       GCancellable       *cancellable,
+                                       GError            **error);
 GDataInputStream *goa_mail_auth_get_input (GoaMailAuth    *auth);
 void      goa_mail_auth_set_input    (GoaMailAuth         *auth,
                                       GDataInputStream    *input);
diff --git a/src/goabackend/goasmtpauthplain.c b/src/goabackend/goasmtpauthplain.c
index fc34cbe..9a37d77 100644
--- a/src/goabackend/goasmtpauthplain.c
+++ b/src/goabackend/goasmtpauthplain.c
@@ -54,6 +54,7 @@ struct _GoaSmtpAuthPlain
   GoaProvider *provider;
   GoaObject *object;
   gboolean auth_supported;
+  gboolean greeting_absent;
   gchar *domain;
   gchar *username;
   gchar *password;
@@ -79,6 +80,9 @@ static gboolean goa_smtp_auth_plain_is_needed (GoaMailAuth        *_auth);
 static gboolean goa_smtp_auth_plain_run_sync (GoaMailAuth         *_auth,
                                               GCancellable        *cancellable,
                                               GError             **error);
+static gboolean goa_smtp_auth_plain_starttls_sync (GoaMailAuth    *_auth,
+                                                   GCancellable   *cancellable,
+                                                   GError        **error);
 
 G_DEFINE_TYPE (GoaSmtpAuthPlain, goa_smtp_auth_plain, GOA_TYPE_MAIL_AUTH);
 
@@ -146,6 +150,21 @@ smtp_auth_plain_check_421 (const gchar *response, GError **error)
   return FALSE;
 }
 
+static gboolean
+smtp_auth_plain_check_454 (const gchar *response, GError **error)
+{
+  if (g_str_has_prefix (response, "454"))
+    {
+      g_set_error (error,
+                   GOA_ERROR,
+                   GOA_ERROR_FAILED, /* TODO: more specific */
+                   _("TLS not available"));
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
 /* ---------------------------------------------------------------------------------------------------- */
 
 static gchar *
@@ -360,6 +379,7 @@ goa_smtp_auth_plain_class_init (GoaSmtpAuthPlainClass *klass)
   auth_class = GOA_MAIL_AUTH_CLASS (klass);
   auth_class->is_needed = goa_smtp_auth_plain_is_needed;
   auth_class->run_sync = goa_smtp_auth_plain_run_sync;
+  auth_class->starttls_sync = goa_smtp_auth_plain_starttls_sync;
 
   /**
    * GoaSmtpAuthPlain:provider:
@@ -539,17 +559,20 @@ goa_smtp_auth_plain_run_sync (GoaMailAuth         *_auth,
   input = goa_mail_auth_get_input (_auth);
   output = goa_mail_auth_get_output (_auth);
 
-  /* Check the greeting */
+  /* Check the greeting, if there is one */
 
-  response = g_data_input_stream_read_line (input, NULL, cancellable, error);
-  if (response == NULL)
-    goto out;
-  g_debug ("< %s", response);
-  if (smtp_auth_plain_check_421 (response, error))
-    goto out;
-  if (smtp_auth_plain_check_not_220 (response, error))
-    goto out;
-  g_clear_pointer (&response, g_free);
+  if (!auth->greeting_absent)
+    {
+      response = g_data_input_stream_read_line (input, NULL, cancellable, error);
+      if (response == NULL)
+        goto out;
+      g_debug ("< %s", response);
+      if (smtp_auth_plain_check_421 (response, error))
+        goto out;
+      if (smtp_auth_plain_check_not_220 (response, error))
+        goto out;
+      g_clear_pointer (&response, g_free);
+    }
 
   /* Send EHLO */
 
@@ -629,3 +652,113 @@ goa_smtp_auth_plain_run_sync (GoaMailAuth         *_auth,
   g_free (request);
   return ret;
 }
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+goa_smtp_auth_plain_starttls_sync (GoaMailAuth         *_auth,
+                                   GCancellable        *cancellable,
+                                   GError             **error)
+{
+  GoaSmtpAuthPlain *auth = GOA_SMTP_AUTH_PLAIN (_auth);
+  GDataInputStream *input;
+  GDataOutputStream *output;
+  gboolean ret;
+  gboolean starttls_supported;
+  gchar *domain;
+  gchar *request;
+  gchar *response;
+
+  starttls_supported = FALSE;
+  domain = NULL;
+  request = NULL;
+  response = NULL;
+
+  ret = FALSE;
+
+  domain = smtp_auth_plain_get_domain (auth, error);
+  if (domain == NULL)
+    goto out;
+
+  input = goa_mail_auth_get_input (_auth);
+  output = goa_mail_auth_get_output (_auth);
+
+  /* Check the greeting */
+
+  response = g_data_input_stream_read_line (input, NULL, cancellable, error);
+  if (response == NULL)
+    goto out;
+  g_debug ("< %s", response);
+  if (smtp_auth_plain_check_421 (response, error))
+    goto out;
+  if (smtp_auth_plain_check_not_220 (response, error))
+    goto out;
+  g_clear_pointer (&response, g_free);
+
+  /* Send EHLO */
+
+  request = g_strdup_printf ("EHLO %s\r\n", domain);
+  g_debug ("> %s", request);
+  if (!g_data_output_stream_put_string (output, request, cancellable, error))
+    goto out;
+  g_clear_pointer (&request, g_free);
+
+  /* Check if STARTTLS is supported or not */
+
+ ehlo_again:
+  response = g_data_input_stream_read_line (input, NULL, cancellable, error);
+  if (response == NULL)
+    goto out;
+  g_debug ("< %s", response);
+  if (smtp_auth_plain_check_421 (response, error))
+    goto out;
+  if (smtp_auth_plain_check_not_250 (response, error))
+    goto out;
+
+  if (g_str_has_prefix (response + 4, "STARTTLS"))
+    starttls_supported = TRUE;
+
+  if (response[3] == '-')
+    {
+      g_free (response);
+      goto ehlo_again;
+    }
+  else if (!starttls_supported)
+    {
+      g_set_error (error,
+                   GOA_ERROR,
+                   GOA_ERROR_NOT_SUPPORTED,
+                   _("Server does not support STARTTLS"));
+      goto out;
+    }
+  g_clear_pointer (&response, g_free);
+
+  /* Send STARTTLS */
+
+  request = g_strdup ("STARTTLS\r\n");
+  g_debug ("> %s", request);
+  if (!g_data_output_stream_put_string (output, request, cancellable, error))
+    goto out;
+  g_clear_pointer (&request, g_free);
+
+  response = g_data_input_stream_read_line (input, NULL, cancellable, error);
+  if (response == NULL)
+    goto out;
+  g_debug ("< %s", response);
+  if (smtp_auth_plain_check_454 (response, error))
+    goto out;
+  if (smtp_auth_plain_check_not_220 (response, error))
+    goto out;
+  g_clear_pointer (&response, g_free);
+
+  /* There won't be a greeting after this */
+  auth->greeting_absent = TRUE;
+
+  ret = TRUE;
+
+ out:
+  g_free (domain);
+  g_free (response);
+  g_free (request);
+  return ret;
+}


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