[evolution-data-server/gnome-3-28] I#13 - [IMAPx] Fails to append message to Yahoo! with no CRLF at the end



commit 07f6a54f22f70f1037eb0bd6422eb51eea23b29e
Author: Milan Crha <mcrha redhat com>
Date:   Wed Jun 27 17:27:11 2018 +0200

    I#13 - [IMAPx] Fails to append message to Yahoo! with no CRLF at the end
    
    Closes https://gitlab.gnome.org/GNOME/evolution-data-server/issues/13

 src/camel/camel-null-output-stream.c            | 28 +++++++++++++++++++
 src/camel/camel-null-output-stream.h            |  2 ++
 src/camel/camel-stream-null.c                   | 32 +++++++++++++++++++++-
 src/camel/camel-stream-null.h                   |  2 ++
 src/camel/providers/imapx/camel-imapx-command.c | 36 ++++++++++++++++++++++++-
 src/camel/providers/imapx/camel-imapx-command.h |  1 +
 src/camel/providers/imapx/camel-imapx-server.c  |  9 +++++++
 7 files changed, 108 insertions(+), 2 deletions(-)
---
diff --git a/src/camel/camel-null-output-stream.c b/src/camel/camel-null-output-stream.c
index aff36a310..99d9ca9b1 100644
--- a/src/camel/camel-null-output-stream.c
+++ b/src/camel/camel-null-output-stream.c
@@ -35,6 +35,8 @@
 
 struct _CamelNullOutputStreamPrivate {
        gsize bytes_written;
+       gboolean ends_with_crlf;
+       gboolean ends_with_cr; /* Just for cases when the CRLF is split into two writes, CR and LF */
 };
 
 G_DEFINE_TYPE (
@@ -50,11 +52,20 @@ null_output_stream_write (GOutputStream *stream,
                           GError **error)
 {
        CamelNullOutputStreamPrivate *priv;
+       const guchar *data = buffer;
 
        priv = CAMEL_NULL_OUTPUT_STREAM_GET_PRIVATE (stream);
 
        priv->bytes_written += count;
 
+       if (count >= 2) {
+               priv->ends_with_crlf = data[count - 2] == '\r' && data[count - 1] == '\n';
+               priv->ends_with_cr = data[count - 1] == '\r';
+       } else if (count == 1) {
+               priv->ends_with_crlf = priv->ends_with_cr && data[count - 1] == '\n';
+               priv->ends_with_cr = data[count - 1] == '\r';
+       }
+
        return count;
 }
 
@@ -74,6 +85,8 @@ static void
 camel_null_output_stream_init (CamelNullOutputStream *null_stream)
 {
        null_stream->priv = CAMEL_NULL_OUTPUT_STREAM_GET_PRIVATE (null_stream);
+       null_stream->priv->ends_with_crlf = FALSE;
+       null_stream->priv->ends_with_cr = FALSE;
 }
 
 /**
@@ -109,3 +122,18 @@ camel_null_output_stream_get_bytes_written (CamelNullOutputStream *null_stream)
        return null_stream->priv->bytes_written;
 }
 
+/**
+ * camel_null_output_stream_get_ends_with_crlf:
+ * @null_stream: a #CamelNullOutputStream
+ *
+ * Returns: Whether the data being written to @null_stream ended with CRLF.
+ *
+ * Since: 3.28.4
+ **/
+gboolean
+camel_null_output_stream_get_ends_with_crlf (CamelNullOutputStream *null_stream)
+{
+       g_return_val_if_fail (CAMEL_IS_NULL_OUTPUT_STREAM (null_stream), FALSE);
+
+       return null_stream->priv->ends_with_crlf;
+}
diff --git a/src/camel/camel-null-output-stream.h b/src/camel/camel-null-output-stream.h
index 978763db9..99b44ab18 100644
--- a/src/camel/camel-null-output-stream.h
+++ b/src/camel/camel-null-output-stream.h
@@ -67,6 +67,8 @@ GOutputStream *       camel_null_output_stream_new
                                        (void);
 gsize          camel_null_output_stream_get_bytes_written
                                        (CamelNullOutputStream *null_stream);
+gboolean       camel_null_output_stream_get_ends_with_crlf
+                                       (CamelNullOutputStream *null_stream);
 
 G_END_DECLS
 
diff --git a/src/camel/camel-stream-null.c b/src/camel/camel-stream-null.c
index 72aa6176c..5183e9ef2 100644
--- a/src/camel/camel-stream-null.c
+++ b/src/camel/camel-stream-null.c
@@ -26,6 +26,8 @@
 
 struct _CamelStreamNullPrivate {
        gsize written;
+       gboolean ends_with_crlf;
+       gboolean ends_with_cr; /* Just for cases when the CRLF is split into two writes, CR and LF */
 };
 
 static void camel_stream_null_seekable_init (GSeekableIface *iface);
@@ -40,7 +42,17 @@ stream_null_write (CamelStream *stream,
                    GCancellable *cancellable,
                    GError **error)
 {
-       CAMEL_STREAM_NULL (stream)->priv->written += n;
+       CamelStreamNull *stream_null = CAMEL_STREAM_NULL (stream);
+
+       stream_null->priv->written += n;
+
+       if (n >= 2) {
+               stream_null->priv->ends_with_crlf = buffer[n - 2] == '\r' && buffer[n - 1] == '\n';
+               stream_null->priv->ends_with_cr = buffer[n - 1] == '\r';
+       } else if (n == 1) {
+               stream_null->priv->ends_with_crlf = stream_null->priv->ends_with_cr && buffer[n - 1] == '\n';
+               stream_null->priv->ends_with_cr = buffer[n - 1] == '\r';
+       }
 
        return n;
 }
@@ -128,6 +140,8 @@ static void
 camel_stream_null_init (CamelStreamNull *stream_null)
 {
        stream_null->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream_null, CAMEL_TYPE_STREAM_NULL, 
CamelStreamNullPrivate);
+       stream_null->priv->ends_with_crlf = FALSE;
+       stream_null->priv->ends_with_cr = FALSE;
 }
 
 /**
@@ -160,3 +174,19 @@ camel_stream_null_get_bytes_written (CamelStreamNull *stream_null)
 
        return stream_null->priv->written;
 }
+
+/**
+ * camel_stream_null_get_ends_with_crlf:
+ * @stream_null: a #CamelStreamNull
+ *
+ * Returns: Whether the data being written to @stream_null ended with CRLF.
+ *
+ * Since: 3.28.4
+ **/
+gboolean
+camel_stream_null_get_ends_with_crlf (CamelStreamNull *stream_null)
+{
+       g_return_val_if_fail (CAMEL_IS_STREAM_NULL (stream_null), FALSE);
+
+       return stream_null->priv->ends_with_crlf;
+}
diff --git a/src/camel/camel-stream-null.h b/src/camel/camel-stream-null.h
index 51b3da5a1..f7fc1b4cf 100644
--- a/src/camel/camel-stream-null.h
+++ b/src/camel/camel-stream-null.h
@@ -68,6 +68,8 @@ GType camel_stream_null_get_type (void);
 CamelStream *  camel_stream_null_new           (void);
 gsize          camel_stream_null_get_bytes_written
                                                (CamelStreamNull *stream_null);
+gboolean       camel_stream_null_get_ends_with_crlf
+                                               (CamelStreamNull *stream_null);
 
 G_END_DECLS
 
diff --git a/src/camel/providers/imapx/camel-imapx-command.c b/src/camel/providers/imapx/camel-imapx-command.c
index 5ef56a54f..dffa8f017 100644
--- a/src/camel/providers/imapx/camel-imapx-command.c
+++ b/src/camel/providers/imapx/camel-imapx-command.c
@@ -371,6 +371,30 @@ camel_imapx_command_addv (CamelIMAPXCommand *ic,
        g_string_append_len (buffer, ps, p - ps - 1);
 }
 
+static gboolean
+imapx_file_ends_with_crlf (const gchar *filename)
+{
+       CamelStream *null_stream, *input_stream;
+       gboolean ends_with_crlf;
+
+       g_return_val_if_fail (filename != NULL, FALSE);
+
+       input_stream = camel_stream_fs_new_with_name (filename, O_RDONLY, 0, NULL);
+       if (!input_stream)
+               return FALSE;
+
+       null_stream = camel_stream_null_new ();
+       camel_stream_write_to_stream (input_stream, null_stream, NULL, NULL);
+       camel_stream_flush (input_stream, NULL, NULL);
+       g_object_unref (input_stream);
+
+       ends_with_crlf = camel_stream_null_get_ends_with_crlf (CAMEL_STREAM_NULL (null_stream));
+
+       g_object_unref (null_stream);
+
+       return ends_with_crlf;
+}
+
 void
 camel_imapx_command_add_part (CamelIMAPXCommand *ic,
                               CamelIMAPXCommandPartType type,
@@ -379,6 +403,7 @@ camel_imapx_command_add_part (CamelIMAPXCommand *ic,
        CamelIMAPXCommandPart *cp;
        GString *buffer;
        guint ob_size = 0;
+       gboolean ends_with_crlf = TRUE;
 
        buffer = ((CamelIMAPXRealCommand *) ic)->buffer;
 
@@ -396,6 +421,7 @@ camel_imapx_command_add_part (CamelIMAPXCommand *ic,
                g_object_ref (ob);
                ob_size = camel_null_output_stream_get_bytes_written (
                        CAMEL_NULL_OUTPUT_STREAM (stream));
+               ends_with_crlf = camel_null_output_stream_get_ends_with_crlf (CAMEL_NULL_OUTPUT_STREAM 
(stream));
                g_object_unref (stream);
                break;
        }
@@ -420,6 +446,8 @@ camel_imapx_command_add_part (CamelIMAPXCommand *ic,
                if (g_stat (path, &st) == 0) {
                        data = g_strdup (data);
                        ob_size = st.st_size;
+
+                       ends_with_crlf = imapx_file_ends_with_crlf (data);
                } else
                        data = NULL;
 
@@ -436,8 +464,13 @@ camel_imapx_command_add_part (CamelIMAPXCommand *ic,
        }
 
        if (type & CAMEL_IMAPX_COMMAND_LITERAL_PLUS) {
+               guint total_size = ob_size;
+
+               if (ic->job_kind == CAMEL_IMAPX_JOB_APPEND_MESSAGE && !ends_with_crlf)
+                       total_size += 2;
+
                g_string_append_c (buffer, '{');
-               g_string_append_printf (buffer, "%u", ob_size);
+               g_string_append_printf (buffer, "%u", total_size);
                if (camel_imapx_server_have_capability (ic->is, IMAPX_CAPABILITY_LITERALPLUS)) {
                        g_string_append_c (buffer, '+');
                } else {
@@ -453,6 +486,7 @@ camel_imapx_command_add_part (CamelIMAPXCommand *ic,
        cp->ob = data;
        cp->data_size = buffer->len;
        cp->data = g_strdup (buffer->str);
+       cp->ends_with_crlf = ends_with_crlf;
 
        g_string_set_size (buffer, 0);
 
diff --git a/src/camel/providers/imapx/camel-imapx-command.h b/src/camel/providers/imapx/camel-imapx-command.h
index 1300c2531..b81a2a1f1 100644
--- a/src/camel/providers/imapx/camel-imapx-command.h
+++ b/src/camel/providers/imapx/camel-imapx-command.h
@@ -58,6 +58,7 @@ struct _CamelIMAPXCommandPart {
 
        gint ob_size;
        gpointer ob;
+       gboolean ends_with_crlf;
 };
 
 struct _CamelIMAPXCommand {
diff --git a/src/camel/providers/imapx/camel-imapx-server.c b/src/camel/providers/imapx/camel-imapx-server.c
index 633938253..11a0f7c0d 100644
--- a/src/camel/providers/imapx/camel-imapx-server.c
+++ b/src/camel/providers/imapx/camel-imapx-server.c
@@ -2267,6 +2267,15 @@ imapx_continuation (CamelIMAPXServer *is,
                return FALSE;
        }
 
+       if (ic->job_kind == CAMEL_IMAPX_JOB_APPEND_MESSAGE && !cp->ends_with_crlf) {
+               g_mutex_lock (&is->priv->stream_lock);
+               n_bytes_written = g_output_stream_write_all (
+                       output_stream, "\r\n", 2, NULL, cancellable, error);
+               g_mutex_unlock (&is->priv->stream_lock);
+               if (n_bytes_written < 0)
+                       return FALSE;
+       }
+
        if (!litplus) {
                success = camel_imapx_input_stream_skip (
                        CAMEL_IMAPX_INPUT_STREAM (input_stream),


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