[evolution-data-server] I#319 - SMTP: Extra empty line added at the end of the message
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] I#319 - SMTP: Extra empty line added at the end of the message
- Date: Tue, 30 Mar 2021 10:17:03 +0000 (UTC)
commit bd644b708188f9c5f9735ac92066a83e23cedd33
Author: Milan Crha <mcrha redhat com>
Date: Tue Mar 30 11:42:57 2021 +0200
I#319 - SMTP: Extra empty line added at the end of the message
Closes https://gitlab.gnome.org/GNOME/evolution-data-server/-/issues/319
docs/reference/camel/camel-docs.sgml.in | 4 ++
src/camel/camel-filter-input-stream.c | 2 +-
src/camel/camel-mime-filter-crlf.c | 69 ++++++++++++++++++
src/camel/camel-mime-filter-crlf.h | 5 ++
src/camel/providers/smtp/camel-smtp-transport.c | 6 +-
src/camel/tests/mime-filter/test-crlf.c | 96 +++++++++++++++++++++++--
6 files changed, 175 insertions(+), 7 deletions(-)
---
diff --git a/docs/reference/camel/camel-docs.sgml.in b/docs/reference/camel/camel-docs.sgml.in
index 360e5c9a1..4f54beb54 100644
--- a/docs/reference/camel/camel-docs.sgml.in
+++ b/docs/reference/camel/camel-docs.sgml.in
@@ -298,6 +298,10 @@
<title>Index of deprecated symbols</title>
<xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
</index>
+ <index id="api-index-3-42" role="3.42">
+ <title>Index of new symbols in 3.42</title>
+ <xi:include href="xml/api-index-3.42.xml"><xi:fallback /></xi:include>
+ </index>
<index id="api-index-3-40" role="3.40">
<title>Index of new symbols in 3.40</title>
<xi:include href="xml/api-index-3.40.xml"><xi:fallback /></xi:include>
diff --git a/src/camel/camel-filter-input-stream.c b/src/camel/camel-filter-input-stream.c
index a7511d7b0..437ba1a00 100644
--- a/src/camel/camel-filter-input-stream.c
+++ b/src/camel/camel-filter-input-stream.c
@@ -144,7 +144,7 @@ filter_input_stream_read (GInputStream *stream,
if (n_bytes_read == 0) {
camel_mime_filter_complete (
- filter, priv->filtered, priv->filtered_length, presize,
+ filter, priv->filtered ? priv->filtered : "", priv->filtered ? priv->filtered_length
: 0, presize,
&priv->filtered, &priv->filtered_length, &presize);
n_bytes_read = priv->filtered_length;
diff --git a/src/camel/camel-mime-filter-crlf.c b/src/camel/camel-mime-filter-crlf.c
index 5f942d7f2..d8f93bde3 100644
--- a/src/camel/camel-mime-filter-crlf.c
+++ b/src/camel/camel-mime-filter-crlf.c
@@ -26,6 +26,8 @@ struct _CamelMimeFilterCRLFPrivate {
gboolean saw_cr;
gboolean saw_lf;
gboolean saw_dot;
+ gboolean ends_with_lf;
+ gboolean ensure_crlf_end;
};
G_DEFINE_TYPE_WITH_PRIVATE (CamelMimeFilterCRLF, camel_mime_filter_crlf, CAMEL_TYPE_MIME_FILTER)
@@ -74,6 +76,9 @@ mime_filter_crlf_filter (CamelMimeFilter *mime_filter,
*outptr++ = *inptr++;
}
+
+ if (len > 0)
+ priv->ends_with_lf = in[len - 1] == '\n';
} else {
/* Output can "grow" by one byte if priv->saw_cr was set as
* a carry-over from the previous invocation. This will happen
@@ -130,10 +135,34 @@ mime_filter_crlf_complete (CamelMimeFilter *mime_filter,
gsize *outlen,
gsize *outprespace)
{
+ CamelMimeFilterCRLF *crlf_filter;
+
if (len)
mime_filter_crlf_filter (
mime_filter, in, len, prespace,
out, outlen, outprespace);
+ else
+ *outlen = 0;
+
+ crlf_filter = CAMEL_MIME_FILTER_CRLF (mime_filter);
+
+ if (crlf_filter->priv->direction == CAMEL_MIME_FILTER_CRLF_ENCODE &&
+ crlf_filter->priv->ensure_crlf_end &&
+ !crlf_filter->priv->ends_with_lf) {
+ gchar *outptr;
+
+ camel_mime_filter_set_size (mime_filter, *outlen + 2, FALSE);
+
+ outptr = mime_filter->outbuf + *outlen;
+ *outptr++ = '\r';
+ *outptr++ = '\n';
+
+ crlf_filter->priv->ends_with_lf = TRUE;
+
+ *out = mime_filter->outbuf;
+ *outlen = outptr - mime_filter->outbuf;
+ *outprespace = mime_filter->outpre;
+ }
}
static void
@@ -146,6 +175,7 @@ mime_filter_crlf_reset (CamelMimeFilter *mime_filter)
priv->saw_cr = FALSE;
priv->saw_lf = TRUE;
priv->saw_dot = FALSE;
+ priv->ends_with_lf = FALSE;
}
static void
@@ -167,6 +197,8 @@ camel_mime_filter_crlf_init (CamelMimeFilterCRLF *filter)
filter->priv->saw_cr = FALSE;
filter->priv->saw_lf = TRUE;
filter->priv->saw_dot = FALSE;
+ filter->priv->ends_with_lf = FALSE;
+ filter->priv->ensure_crlf_end = FALSE;
}
/**
@@ -193,3 +225,40 @@ camel_mime_filter_crlf_new (CamelMimeFilterCRLFDirection direction,
return filter;
}
+
+/**
+ * camel_mime_filter_crlf_set_ensure_crlf_end:
+ * @filter: a #CamelMimeFilterCRLF
+ * @ensure_crlf_end: value to set
+ *
+ * When set to true, the filter will ensure that the output stream will
+ * end with CRLF, in case it does not. The default is to not do that.
+ * The option is used only when encoding the stream.
+ *
+ * Since: 3.42
+ **/
+void
+camel_mime_filter_crlf_set_ensure_crlf_end (CamelMimeFilterCRLF *filter,
+ gboolean ensure_crlf_end)
+{
+ g_return_if_fail (CAMEL_IS_MIME_FILTER_CRLF (filter));
+
+ filter->priv->ensure_crlf_end = ensure_crlf_end;
+}
+
+/**
+ * camel_mime_filter_crlf_get_ensure_crlf_end:
+ * @filter: a #CamelMimeFilterCRLF
+ *
+ * Returns: whether the filter will ensure that the output stream will
+ * end with CRLF
+ *
+ * Since: 3.42
+ **/
+gboolean
+camel_mime_filter_crlf_get_ensure_crlf_end (CamelMimeFilterCRLF *filter)
+{
+ g_return_val_if_fail (CAMEL_IS_MIME_FILTER_CRLF (filter), FALSE);
+
+ return filter->priv->ensure_crlf_end;
+}
diff --git a/src/camel/camel-mime-filter-crlf.h b/src/camel/camel-mime-filter-crlf.h
index 47dc50266..65671417b 100644
--- a/src/camel/camel-mime-filter-crlf.h
+++ b/src/camel/camel-mime-filter-crlf.h
@@ -69,6 +69,11 @@ GType camel_mime_filter_crlf_get_type (void);
CamelMimeFilter *
camel_mime_filter_crlf_new (CamelMimeFilterCRLFDirection direction,
CamelMimeFilterCRLFMode mode);
+void camel_mime_filter_crlf_set_ensure_crlf_end
+ (CamelMimeFilterCRLF *filter,
+ gboolean ensure_crlf_end);
+gboolean camel_mime_filter_crlf_get_ensure_crlf_end
+ (CamelMimeFilterCRLF *filter);
G_END_DECLS
diff --git a/src/camel/providers/smtp/camel-smtp-transport.c b/src/camel/providers/smtp/camel-smtp-transport.c
index c6b5aebee..117811977 100644
--- a/src/camel/providers/smtp/camel-smtp-transport.c
+++ b/src/camel/providers/smtp/camel-smtp-transport.c
@@ -1824,6 +1824,7 @@ smtp_data (CamelSmtpTransport *transport,
filter = camel_mime_filter_crlf_new (
CAMEL_MIME_FILTER_CRLF_ENCODE,
CAMEL_MIME_FILTER_CRLF_MODE_CRLF_DOTS);
+ camel_mime_filter_crlf_set_ensure_crlf_end (CAMEL_MIME_FILTER_CRLF (filter), TRUE);
camel_stream_filter_add (
CAMEL_STREAM_FILTER (filtered_stream), filter);
g_object_unref (filter);
@@ -1843,6 +1844,7 @@ smtp_data (CamelSmtpTransport *transport,
filter = camel_mime_filter_crlf_new (
CAMEL_MIME_FILTER_CRLF_ENCODE,
CAMEL_MIME_FILTER_CRLF_MODE_CRLF_DOTS);
+ camel_mime_filter_crlf_set_ensure_crlf_end (CAMEL_MIME_FILTER_CRLF (filter), TRUE);
camel_stream_filter_add (CAMEL_STREAM_FILTER (sec_filtered_stream), filter);
g_object_unref (filter);
@@ -1892,9 +1894,9 @@ smtp_data (CamelSmtpTransport *transport,
/* terminate the message body */
- d (fprintf (stderr, "[SMTP] sending: \\r\\n.\\r\\n\n"));
+ d (fprintf (stderr, "[SMTP] sending: .\\r\\n\n"));
- if (camel_stream_write (ostream, "\r\n.\r\n", 5, cancellable, error) == -1) {
+ if (camel_stream_write (ostream, ".\r\n", 3, cancellable, error) == -1) {
g_prefix_error (error, _("DATA command failed: "));
camel_service_disconnect_sync (
CAMEL_SERVICE (transport),
diff --git a/src/camel/tests/mime-filter/test-crlf.c b/src/camel/tests/mime-filter/test-crlf.c
index cff46ddf1..77cc7f083 100644
--- a/src/camel/tests/mime-filter/test-crlf.c
+++ b/src/camel/tests/mime-filter/test-crlf.c
@@ -176,23 +176,111 @@ test_case (gint test_num)
camel_test_pull ();
}
+static gboolean
+test_case_ensure_crlf_end_run (const gchar *in,
+ const gchar *expected,
+ gboolean ensure_crlf_end)
+{
+ CamelMimeFilter *filter;
+ GInputStream *input_stream;
+ GInputStream *filter_stream;
+ gchar bytes[64];
+ gsize bytes_read = 0;
+ gboolean success = FALSE;
+
+ if (strlen (expected) >= sizeof (bytes) - 1) {
+ camel_test_fail ("Local buffer too small (%u bytes) to cover %u bytes", sizeof (bytes),
strlen (expected));
+ return FALSE;
+ }
+
+ input_stream = g_memory_input_stream_new_from_data (in, strlen (in), NULL);
+
+ filter = camel_mime_filter_crlf_new (CAMEL_MIME_FILTER_CRLF_ENCODE,
CAMEL_MIME_FILTER_CRLF_MODE_CRLF_DOTS);
+ camel_mime_filter_crlf_set_ensure_crlf_end (CAMEL_MIME_FILTER_CRLF (filter), ensure_crlf_end);
+ filter_stream = camel_filter_input_stream_new (input_stream, filter);
+ g_object_unref (filter);
+
+ if (g_input_stream_read_all (filter_stream, bytes, sizeof (bytes) - 1, &bytes_read, NULL, NULL)) {
+ bytes[bytes_read] = '\0';
+
+ if (bytes_read == strlen (expected)) {
+ success = memcmp (bytes, expected, bytes_read) == 0;
+
+ if (!success)
+ camel_test_fail ("Returned text '%s' and expected text '%s' do not match",
bytes, expected);
+ } else {
+ camel_test_fail ("Read %u bytes, but expected %u bytes", bytes_read, strlen
(expected));
+ }
+ } else {
+ camel_test_fail ("Failed to read up to %u bytes from the input stream", sizeof (bytes));
+ }
+
+ g_object_unref (filter_stream);
+ g_object_unref (input_stream);
+
+ return success;
+}
+
+static void
+test_case_ensure_crlf_end (void)
+{
+ struct _data {
+ const gchar *in;
+ const gchar *out_without;
+ const gchar *out_with;
+ } data[] = {
+ { "", "", "\r\n" },
+ { "a", "a", "a\r\n" },
+ { "a\n", "a\r\n", "a\r\n" },
+ { "a\r\n", "a\r\n", "a\r\n" },
+ { "a\r\nb", "a\r\nb", "a\r\nb\r\n" },
+ { "a\nb", "a\r\nb", "a\r\nb\r\n" },
+ { "a\r\nb\n", "a\r\nb\r\n", "a\r\nb\r\n" },
+ { "a\n\nb", "a\r\n\r\nb", "a\r\n\r\nb\r\n" }
+ };
+ guint ii;
+
+ camel_test_push ("Test encode with used ensure-crlf-end");
+
+ for (ii = 0; ii < G_N_ELEMENTS (data); ii++) {
+ camel_test_push ("case %d/a (without set option)", ii);
+ if (!test_case_ensure_crlf_end_run (data[ii].in, data[ii].out_without, FALSE)) {
+ camel_test_pull ();
+ break;
+ }
+
+ camel_test_pull ();
+ camel_test_push ("case %d/b (with set option)", ii);
+
+ if (!test_case_ensure_crlf_end_run (data[ii].in, data[ii].out_with, TRUE)) {
+ camel_test_pull ();
+ break;
+ }
+
+ camel_test_pull ();
+ }
+
+ camel_test_pull ();
+}
+
gint
main (gint argc,
gchar **argv)
{
- gchar *work;
gint ii;
camel_test_init (argc, argv);
- work = g_strdup_printf ("CRLF/DOT filter, test case %d", 0);
- camel_test_start (work);
- g_free (work);
+ camel_test_start ("CRLF/DOT filter, test case 0");
for (ii = CRLF_ENCODE; ii < CRLF_DONE; ii++)
test_case (ii);
camel_test_end ();
+ camel_test_start ("CRLF/DOT filter, test case 1");
+ test_case_ensure_crlf_end ();
+ camel_test_end ();
+
return 0;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]