[evolution-data-server] I#168 - Camel: Can fail to connect when password contains non-ASCII letters



commit 3b59c0c3fd990c14a265e18d570eda1d99744442
Author: Milan Crha <mcrha redhat com>
Date:   Fri Feb 7 08:54:06 2020 +0100

    I#168 - Camel: Can fail to connect when password contains non-ASCII letters
    
    Closes https://gitlab.gnome.org/GNOME/evolution-data-server/issues/168

 src/camel/providers/imapx/camel-imapx-command.c |  9 +++-
 src/camel/providers/imapx/camel-imapx-server.c  | 47 +++++++++++++++++++-
 src/camel/providers/smtp/camel-smtp-transport.c | 57 ++++++++++++++++++++++++-
 3 files changed, 108 insertions(+), 5 deletions(-)
---
diff --git a/src/camel/providers/imapx/camel-imapx-command.c b/src/camel/providers/imapx/camel-imapx-command.c
index acd2b1084..90beaf70b 100644
--- a/src/camel/providers/imapx/camel-imapx-command.c
+++ b/src/camel/providers/imapx/camel-imapx-command.c
@@ -199,6 +199,7 @@ camel_imapx_command_addv (CamelIMAPXCommand *ic,
        gchar *utf7_name = NULL;
        const gchar *name;
        gboolean use_utf8_string = FALSE;
+       guchar force_str_mask = 0;
 
        g_return_if_fail (CAMEL_IS_IMAPX_COMMAND (ic));
 
@@ -264,13 +265,19 @@ camel_imapx_command_addv (CamelIMAPXCommand *ic,
                                s = va_arg (ap, gchar *);
                                g_string_append (buffer, s);
                                break;
+                       case 'S': /* escaped and quoted string */
+                               s = va_arg (ap, gchar *);
+                               use_utf8_string = FALSE;
+                               force_str_mask = IMAPX_TYPE_TEXT_CHAR;
+                               c (camel_imapx_server_get_tagprefix (ic->is), "got string '%s'\n", 
g_str_has_prefix (format, "LOGIN") ? "***" : s);
+                               goto output_string;
                        case 's': /* simple string */
                                s = va_arg (ap, gchar *);
                                use_utf8_string = FALSE;
                                c (camel_imapx_server_get_tagprefix (ic->is), "got string '%s'\n", 
g_str_has_prefix (format, "LOGIN") ? "***" : s);
                        output_string:
                                if (s && *s) {
-                                       guchar mask = imapx_is_mask (s);
+                                       guchar mask = force_str_mask ? force_str_mask : imapx_is_mask (s);
 
                                        if (use_utf8_string && !(mask & IMAPX_TYPE_ATOM_CHAR)) {
                                                g_string_append_c (buffer, '"');
diff --git a/src/camel/providers/imapx/camel-imapx-server.c b/src/camel/providers/imapx/camel-imapx-server.c
index cf341a917..3c38fb1e9 100644
--- a/src/camel/providers/imapx/camel-imapx-server.c
+++ b/src/camel/providers/imapx/camel-imapx-server.c
@@ -3140,6 +3140,25 @@ camel_imapx_server_is_connected (CamelIMAPXServer *imapx_server)
        return imapx_server->priv->state >= IMAPX_CONNECTED;
 }
 
+static gboolean
+imapx_password_contains_nonascii (CamelService *service)
+{
+       const gchar *password;
+
+       g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
+
+       password = camel_service_get_password (service);
+
+       while (password && *password) {
+               if (*password < 0)
+                       return TRUE;
+
+               password++;
+       }
+
+       return FALSE;
+}
+
 CamelAuthenticationResult
 camel_imapx_server_authenticate_sync (CamelIMAPXServer *is,
                                      const gchar *mechanism,
@@ -3155,6 +3174,8 @@ camel_imapx_server_authenticate_sync (CamelIMAPXServer *is,
        CamelSasl *sasl = NULL;
        gchar *host;
        gchar *user;
+       gboolean can_retry_login = FALSE;
+       gboolean success;
 
        g_return_val_if_fail (
                CAMEL_IS_IMAPX_SERVER (is),
@@ -3235,10 +3256,32 @@ camel_imapx_server_authenticate_sync (CamelIMAPXServer *is,
                }
 
                ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_LOGIN, "LOGIN %s %s", user, password);
+
+               can_retry_login = TRUE;
+       }
+
+       success = camel_imapx_server_process_command_sync (is, ic, _("Failed to authenticate"), cancellable, 
error);
+       if (!success && can_retry_login && imapx_password_contains_nonascii (service)) {
+               const gchar *password;
+               gchar *password_latin1;
+
+               can_retry_login = -1;
+
+               password = camel_service_get_password (service);
+               password_latin1 = g_convert_with_fallback (password, -1, "ISO-8859-1", "UTF-8", "", NULL, 
NULL, NULL);
+
+               if (password_latin1 && g_strcmp0 (password, password_latin1) != 0) {
+                       camel_imapx_command_unref (ic);
+                       ic = camel_imapx_command_new (is, CAMEL_IMAPX_JOB_LOGIN, "LOGIN %S %S", user, 
password_latin1);
+                       g_free (password_latin1);
+
+                       success = camel_imapx_server_process_command_sync (is, ic, _("Failed to 
authenticate"), cancellable, NULL);
+               } else {
+                       g_free (password_latin1);
+               }
        }
 
-       if (!camel_imapx_server_process_command_sync (is, ic, _("Failed to authenticate"), cancellable, 
error) && (
-           !ic->status || ic->status->result != IMAPX_NO))
+       if (!success && (!ic->status || ic->status->result != IMAPX_NO))
                result = CAMEL_AUTHENTICATION_ERROR;
        else if (ic->status->result == IMAPX_OK)
                result = CAMEL_AUTHENTICATION_ACCEPTED;
diff --git a/src/camel/providers/smtp/camel-smtp-transport.c b/src/camel/providers/smtp/camel-smtp-transport.c
index b459b14fa..035baf367 100644
--- a/src/camel/providers/smtp/camel-smtp-transport.c
+++ b/src/camel/providers/smtp/camel-smtp-transport.c
@@ -643,6 +643,25 @@ smtp_transport_disconnect_sync (CamelService *service,
        return TRUE;
 }
 
+static gboolean
+smtp_password_contains_nonascii (CamelService *service)
+{
+       const gchar *password;
+
+       g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
+
+       password = camel_service_get_password (service);
+
+       while (password && *password) {
+               if (*password < 0)
+                       return TRUE;
+
+               password++;
+       }
+
+       return FALSE;
+}
+
 static CamelAuthenticationResult
 smtp_transport_authenticate_sync (CamelService *service,
                                   const gchar *mechanism,
@@ -654,8 +673,8 @@ smtp_transport_authenticate_sync (CamelService *service,
        CamelSasl *sasl;
        CamelStreamBuffer *istream;
        CamelStream *ostream;
-       gchar *cmdbuf, *respbuf = NULL, *challenge;
-       gboolean auth_challenge = FALSE;
+       gchar *cmdbuf, *respbuf = NULL, *challenge, *restore_password = NULL;
+       gboolean auth_challenge = FALSE, can_try_again = TRUE;
        GError *local_error = NULL;
 
        if (mechanism == NULL) {
@@ -666,6 +685,7 @@ smtp_transport_authenticate_sync (CamelService *service,
                return CAMEL_AUTHENTICATION_ERROR;
        }
 
+ try_again:
        sasl = camel_sasl_new ("smtp", mechanism, service);
        if (sasl == NULL) {
                g_set_error (
@@ -796,6 +816,34 @@ smtp_transport_authenticate_sync (CamelService *service,
                        respbuf = camel_stream_buffer_read_line (istream, cancellable, error);
                        d (fprintf (stderr, "[SMTP] received: %s\n", respbuf ? respbuf : "(null)"));
                }
+
+               if (can_try_again) {
+                       can_try_again = FALSE;
+
+                       if (smtp_password_contains_nonascii (service)) {
+                               const gchar *password;
+                               gchar *password_latin1;
+
+                               password = camel_service_get_password (service);
+                               password_latin1 = g_convert_with_fallback (password, -1, "ISO-8859-1", 
"UTF-8", "", NULL, NULL, NULL);
+
+                               if (password_latin1 && g_strcmp0 (password, password_latin1) != 0) {
+                                       restore_password = g_strdup (password);
+
+                                       camel_service_set_password (service, password_latin1);
+
+                                       g_clear_object (&istream);
+                                       g_clear_object (&ostream);
+                                       g_object_unref (sasl);
+                                       g_free (password_latin1);
+                                       g_free (respbuf);
+
+                                       goto try_again;
+                               } else {
+                                       g_free (password_latin1);
+                               }
+                       }
+               }
        } else if (strncmp (respbuf, "235", 3) == 0)
                result = CAMEL_AUTHENTICATION_ACCEPTED;
        /* Catch any other errors. */
@@ -820,6 +868,11 @@ lose:
        result = CAMEL_AUTHENTICATION_ERROR;
 
 exit:
+       if (restore_password) {
+               camel_service_set_password (service, restore_password);
+               g_free (restore_password);
+       }
+
        g_clear_object (&istream);
        g_clear_object (&ostream);
        g_object_unref (sasl);


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