From a64c446fed34ccddecaab53a2d836088a970d628 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 13 Aug 2013 14:40:39 +2000 Subject: [PATCH] [PATCH 6/6] reply: Use RFC 2822/MIME wholly for text format template --- 0c/8dbdf40dd7fc432916b1ee19491fd9d6e73320 | 567 ++++++++++++++++++++++ 1 file changed, 567 insertions(+) create mode 100644 0c/8dbdf40dd7fc432916b1ee19491fd9d6e73320 diff --git a/0c/8dbdf40dd7fc432916b1ee19491fd9d6e73320 b/0c/8dbdf40dd7fc432916b1ee19491fd9d6e73320 new file mode 100644 index 000000000..659c876d9 --- /dev/null +++ b/0c/8dbdf40dd7fc432916b1ee19491fd9d6e73320 @@ -0,0 +1,567 @@ +Return-Path: +X-Original-To: notmuch@notmuchmail.org +Delivered-To: notmuch@notmuchmail.org +Received: from localhost (localhost [127.0.0.1]) + by olra.theworths.org (Postfix) with ESMTP id 53947431FBF + for ; Mon, 12 Aug 2013 11:41:08 -0700 (PDT) +X-Virus-Scanned: Debian amavisd-new at olra.theworths.org +X-Spam-Flag: NO +X-Spam-Score: -0.7 +X-Spam-Level: +X-Spam-Status: No, score=-0.7 tagged_above=-999 required=5 + tests=[RCVD_IN_DNSWL_LOW=-0.7] autolearn=disabled +Received: from olra.theworths.org ([127.0.0.1]) + by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024) + with ESMTP id kJcCDtCiQTCL for ; + Mon, 12 Aug 2013 11:41:04 -0700 (PDT) +Received: from dmz-mailsec-scanner-4.mit.edu (dmz-mailsec-scanner-4.mit.edu + [18.9.25.15]) + by olra.theworths.org (Postfix) with ESMTP id EDF75431FC0 + for ; Mon, 12 Aug 2013 11:40:55 -0700 (PDT) +X-AuditID: 1209190f-b7fa58e000000953-9c-52092c37d165 +Received: from mailhub-auth-2.mit.edu ( [18.7.62.36]) + by dmz-mailsec-scanner-4.mit.edu (Symantec Messaging Gateway) with SMTP + id 0E.F0.02387.73C29025; Mon, 12 Aug 2013 14:40:55 -0400 (EDT) +Received: from outgoing.mit.edu (outgoing-auth-1.mit.edu [18.9.28.11]) + by mailhub-auth-2.mit.edu (8.13.8/8.9.2) with ESMTP id r7CIeqPe011680; + Mon, 12 Aug 2013 14:40:52 -0400 +Received: from drake.dyndns.org (26-4-172.dynamic.csail.mit.edu [18.26.4.172]) + (authenticated bits=0) + (User authenticated as amdragon@ATHENA.MIT.EDU) + by outgoing.mit.edu (8.13.8/8.12.4) with ESMTP id r7CIemcs003752 + (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NOT); + Mon, 12 Aug 2013 14:40:50 -0400 +Received: from amthrax by drake.dyndns.org with local (Exim 4.77) + (envelope-from ) + id 1V8x2u-0005yj-Oa; Mon, 12 Aug 2013 14:40:48 -0400 +From: Austin Clements +To: notmuch@notmuchmail.org +Subject: [PATCH 6/6] reply: Use RFC 2822/MIME wholly for text format template +Date: Mon, 12 Aug 2013 14:40:39 -0400 +Message-Id: <1376332839-22825-7-git-send-email-amdragon@mit.edu> +X-Mailer: git-send-email 1.7.10.4 +In-Reply-To: <1376332839-22825-1-git-send-email-amdragon@mit.edu> +References: <1376332839-22825-1-git-send-email-amdragon@mit.edu> +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit +X-Brightmail-Tracker: + H4sIAAAAAAAAA+NgFmphleLIzCtJLcpLzFFi42IRYrdT0TXX4Qwy+HVS2OL6zZnMDowez1bd + Yg5gjOKySUnNySxLLdK3S+DKaJ/TwVSwpJmxYk7bTpYGxkXZXYycHBICJhJn7lxghLDFJC7c + W8/WxcjFISSwj1Hi8JZPLCAJIYGNjBKbF1hAJI4wSTR1/GCCSMxllLj6yxHEZhPQkNi2fznY + JBEBaYmdd2ezdjFycDALqEn86VIBCQsLFEvMXHQJrIRFQFXiVsNKNhCbV8BB4vq11+wQRyhK + dD+bABbnFHCUuL/8HNQNDhIbd/UwQtQLSpyc+YQFYry6xPp5QiBhZgF5ieats5knMArNQlI1 + C6FqFpKqBYzMqxhlU3KrdHMTM3OKU5N1i5MT8/JSi3RN9HIzS/RSU0o3MYICmFOSfwfjt4NK + hxgFOBiVeHgzPnIECbEmlhVX5h5ilORgUhLlnaTJGSTEl5SfUpmRWJwRX1Sak1p8iFGCg1lJ + hHcLK1CONyWxsiq1KB8mJc3BoiTO++zp2UAhgfTEktTs1NSC1CKYrAwHh5IEb782UKNgUWp6 + akVaZk4JQpqJgxNkOA/Q8GaQGt7igsTc4sx0iPwpRl2OC70LPjEKseTl56VKifOmgBQJgBRl + lObBzYElnleM4kBvCfN2glTxAJMW3KRXQEuYgJYYNXOALClJREhJNTDOnabTaTKjq7LKctsr + ublTXyRJv3QKdej6zJ9jsuifs+COkrX/AitNNwV+sDeqkCg56cV/0nXtgr8HZmtXFvWaxbwO + CdZS67sY5np9F/PhHZo8T9ne1PE+7cxadOTcu7DDKqd+X9GfaTv5KUu57YegSCner5L32TYr + 6Ff/OXsxdqen16P7Ag5KLMUZiYZazEXFiQBDGZQTFwMAAA== +X-BeenThere: notmuch@notmuchmail.org +X-Mailman-Version: 2.1.13 +Precedence: list +List-Id: "Use and development of the notmuch mail system." + +List-Unsubscribe: , + +List-Archive: +List-Post: +List-Help: +List-Subscribe: , + +X-List-Received-Date: Mon, 12 Aug 2013 18:41:08 -0000 + +Previously, reply's default text format used an odd mix of RFC 2045 +MIME encoding for the reply template's body and some made-up RFC +2822-like UTF-8 format for the headers. The intent was to present the +headers to the user in a nice, un-encoded format, but this assumed +that whatever ultimately sent the email would RFC 2047-encode the +headers, while at the same time the body was already RFC 2045 encoded, +so it assumed that whatever sent the email would *not* re-encode the +body. + +This can be fixed by either producing a fully decoded UTF-8 reply +template, or a fully encoded MIME-compliant RFC 2822 message. This +patch does the latter because it is + +a) Well-defined by RFC 2822 and MIME (while any UTF-8 format would be + ad hoc). + +b) Ready to be piped to sendmail. The point of the text format is to + be minimal, so a user should be able to pop up the tmeplate in + whatever editor they want, edit it, and push it to sendmail. + +c) Consistent with how frontend capabilities. If a frontend has the + smarts to RFC 2047 encode the headers before sending the mail, it + probably has the smarts to RFC 2047 decode them before presenting + the template to a user for editing. + +Also, as far as I know, nothing automated consumes the reply text +format, so changing this should not cause serious problems. (And if +anything does still consume this format, it probably gets these +encoding issues wrong anyway.) +--- + Makefile.local | 1 - + gmime-filter-headers.c | 263 ---------------------------------------------- + gmime-filter-headers.h | 69 ------------ + man/man1/notmuch-reply.1 | 2 +- + notmuch-reply.c | 15 +-- + test/reply | 9 +- + test/reply-to-sender | 4 +- + 7 files changed, 12 insertions(+), 351 deletions(-) + delete mode 100644 gmime-filter-headers.c + delete mode 100644 gmime-filter-headers.h + +diff --git a/Makefile.local b/Makefile.local +index 84043fe..b7cd266 100644 +--- a/Makefile.local ++++ b/Makefile.local +@@ -255,7 +255,6 @@ notmuch_client_srcs = \ + command-line-arguments.c\ + debugger.c \ + gmime-filter-reply.c \ +- gmime-filter-headers.c \ + hooks.c \ + notmuch.c \ + notmuch-config.c \ +diff --git a/gmime-filter-headers.c b/gmime-filter-headers.c +deleted file mode 100644 +index 7db3779..0000000 +--- a/gmime-filter-headers.c ++++ /dev/null +@@ -1,263 +0,0 @@ +-/* +- * Copyright © 2009 Keith Packard +- * Copyright © 2010 Michal Sojka +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation, either version 3 of the License, or +- * (at your option) any later version. +- * +- * This program 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 +- * General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License along +- * with this program; if not, write to the Free Software Foundation, Inc., +- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +- */ +- +-#include "gmime-filter-headers.h" +-#include +-#include +-#include +-#include +-#include +- +-/** +- * SECTION: gmime-filter-headers +- * @title: GMimeFilterHeaders +- * @short_description: Add/remove headers markers +- * +- * A #GMimeFilter for decoding rfc2047 encoded headers to UTF-8 +- **/ +- +- +-static void g_mime_filter_headers_class_init (GMimeFilterHeadersClass *klass); +-static void g_mime_filter_headers_init (GMimeFilterHeaders *filter, GMimeFilterHeadersClass *klass); +-static void g_mime_filter_headers_finalize (GObject *object); +- +-static GMimeFilter *filter_copy (GMimeFilter *filter); +-static void filter_filter (GMimeFilter *filter, char *in, size_t len, size_t prespace, +- char **out, size_t *outlen, size_t *outprespace); +-static void filter_complete (GMimeFilter *filter, char *in, size_t len, size_t prespace, +- char **out, size_t *outlen, size_t *outprespace); +-static void filter_reset (GMimeFilter *filter); +- +- +-static GMimeFilterClass *parent_class = NULL; +- +-GType +-g_mime_filter_headers_get_type (void) +-{ +- static GType type = 0; +- +- if (!type) { +- static const GTypeInfo info = { +- sizeof (GMimeFilterHeadersClass), +- NULL, /* base_class_init */ +- NULL, /* base_class_finalize */ +- (GClassInitFunc) g_mime_filter_headers_class_init, +- NULL, /* class_finalize */ +- NULL, /* class_data */ +- sizeof (GMimeFilterHeaders), +- 0, /* n_preallocs */ +- (GInstanceInitFunc) g_mime_filter_headers_init, +- NULL /* value_table */ +- }; +- +- type = g_type_register_static (GMIME_TYPE_FILTER, "GMimeFilterHeaders", &info, (GTypeFlags) 0); +- } +- +- return type; +-} +- +- +-static void +-g_mime_filter_headers_class_init (GMimeFilterHeadersClass *klass) +-{ +- GObjectClass *object_class = G_OBJECT_CLASS (klass); +- GMimeFilterClass *filter_class = GMIME_FILTER_CLASS (klass); +- +- parent_class = (GMimeFilterClass *) g_type_class_ref (GMIME_TYPE_FILTER); +- +- object_class->finalize = g_mime_filter_headers_finalize; +- +- filter_class->copy = filter_copy; +- filter_class->filter = filter_filter; +- filter_class->complete = filter_complete; +- filter_class->reset = filter_reset; +-} +- +-static void +-g_mime_filter_headers_init (GMimeFilterHeaders *filter, GMimeFilterHeadersClass *klass) +-{ +- (void) klass; +- filter->saw_nl = TRUE; +- filter->line = NULL; +- filter->line_size = 0; +- filter->lineptr = NULL; +-} +- +-static void +-g_mime_filter_headers_finalize (GObject *object) +-{ +- free (GMIME_FILTER_HEADERS (object)->line); +- G_OBJECT_CLASS (parent_class)->finalize (object); +-} +- +- +-static GMimeFilter * +-filter_copy (GMimeFilter *filter) +-{ +- (void) filter; +- return g_mime_filter_headers_new (); +-} +- +-static void +-output_decoded_header (GMimeFilterHeaders *headers, char **outptr) +-{ +- char *colon, *name, *s, *decoded_value; +- size_t offset; +- gint ret; +- +- colon = strchr (headers->line, ':'); +- if (colon == NULL) +- return; +- +- name = headers->line; +- *colon = '\0'; +- s = colon + 1; +- while (*s == ' ' || *s == '\t') +- s++; +- decoded_value = g_mime_utils_header_decode_text(s); +- if (decoded_value == NULL) +- return; +- offset = *outptr - GMIME_FILTER (headers)->outbuf; +- g_mime_filter_set_size (GMIME_FILTER (headers), strlen(name) + 2 + +- strlen(decoded_value) + 2, TRUE); +- *outptr = GMIME_FILTER (headers)->outbuf + offset; +- ret = g_sprintf (*outptr, "%s: %s\n", name, decoded_value); +- if (ret > 0) +- *outptr += ret; +- free (decoded_value); +-} +- +-static void +-output_final_newline (GMimeFilterHeaders *headers, char **outptr) +-{ +- size_t offset; +- +- offset = *outptr - GMIME_FILTER (headers)->outbuf; +- g_mime_filter_set_size (GMIME_FILTER (headers), 1, TRUE); +- *outptr = GMIME_FILTER (headers)->outbuf + offset; +- *(*outptr)++ = '\n'; +-} +- +-static void +-filter_filter (GMimeFilter *filter, char *inbuf, size_t inlen, size_t prespace, +- char **outbuf, size_t *outlen, size_t *outprespace) +-{ +- GMimeFilterHeaders *headers = (GMimeFilterHeaders *) filter; +- register const char *inptr = inbuf; +- const char *inend = inbuf + inlen; +- char *lineptr, *lineend, *outptr; +- +- (void) prespace; +- if (headers->line == NULL) { +- headers->line_size = 200; +- headers->lineptr = headers->line = malloc (headers->line_size); +- } +- lineptr = headers->lineptr; +- lineend = headers->line + headers->line_size - 1; +- if (lineptr == NULL) +- return; +- outptr = filter->outbuf; +- while (inptr < inend) { +- if (*inptr == '\n') { +- if (headers->saw_nl) +- output_final_newline(headers, &outptr); +- headers->saw_nl = TRUE; +- inptr++; +- continue; +- } +- +- if (lineptr == lineend) { +- headers->line_size *= 2; +- headers->line = xrealloc (headers->line, headers->line_size); +- lineptr = headers->line + (headers->line_size / 2) - 1; +- lineend = headers->line + headers->line_size - 1; +- } +- +- if (headers->saw_nl && *inptr != ' ' && *inptr != '\t') { +- *lineptr = '\0'; +- output_decoded_header (headers, &outptr); +- lineptr = headers->line; +- } +- if (headers->saw_nl && (*inptr == ' ' || *inptr == '\t')) { +- *lineptr = ' '; +- lineptr++; +- while (inptr < inend && (*inptr == ' ' || *inptr == '\t')) +- inptr++; +- headers->saw_nl = FALSE; +- continue; +- } +- headers->saw_nl = FALSE; +- +- if (*inptr != '\r') +- *lineptr++ = *inptr; +- inptr++; +- } +- if (headers->saw_nl) { +- *lineptr = '\0'; +- output_decoded_header (headers, &outptr); +- lineptr = headers->line; +- } +- headers->lineptr = lineptr; +- *outlen = outptr - filter->outbuf; +- *outprespace = filter->outpre; +- *outbuf = filter->outbuf; +-} +- +-static void +-filter_complete (GMimeFilter *filter, char *inbuf, size_t inlen, size_t prespace, +- char **outbuf, size_t *outlen, size_t *outprespace) +-{ +- if (inbuf && inlen) +- filter_filter (filter, inbuf, inlen, prespace, outbuf, outlen, outprespace); +-} +- +-static void +-filter_reset (GMimeFilter *filter) +-{ +- GMimeFilterHeaders *headers = (GMimeFilterHeaders *) filter; +- +- headers->saw_nl = TRUE; +- free(headers->line); +- headers->line = NULL; +- headers->line_size = 0; +-} +- +- +-/** +- * g_mime_filter_headers_new: +- * @encode: %TRUE if the filter should encode or %FALSE otherwise +- * @dots: encode/decode dots (as for SMTP) +- * +- * Creates a new #GMimeFilterHeaders filter. +- * +- * If @encode is %TRUE, then all lines will be prefixed by "> ", +- * otherwise any lines starting with "> " will have that removed +- * +- * Returns: a new #GMimeFilterHeaders filter. +- **/ +-GMimeFilter * +-g_mime_filter_headers_new (void) +-{ +- GMimeFilterHeaders *new_headers; +- +- new_headers = (GMimeFilterHeaders *) g_object_newv (GMIME_TYPE_FILTER_HEADERS, 0, NULL); +- +- return (GMimeFilter *) new_headers; +-} +- +diff --git a/gmime-filter-headers.h b/gmime-filter-headers.h +deleted file mode 100644 +index 1d1a3eb..0000000 +--- a/gmime-filter-headers.h ++++ /dev/null +@@ -1,69 +0,0 @@ +-/* +- * Copyright © 2009 Keith Packard +- * Copyright © 2010 Michal Sojka +- * +- * This program is free software; you can redistribute it and/or modify +- * it under the terms of the GNU General Public License as published by +- * the Free Software Foundation, either version 3 of the License, or +- * (at your option) any later version. +- * +- * This program 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 +- * General Public License for more details. +- * +- * You should have received a copy of the GNU General Public License along +- * with this program; if not, write to the Free Software Foundation, Inc., +- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +- */ +- +-#ifndef _GMIME_FILTER_HEADERS_H_ +-#define _GMIME_FILTER_HEADERS_H_ +- +-#include +- +-G_BEGIN_DECLS +- +-#define GMIME_TYPE_FILTER_HEADERS (g_mime_filter_headers_get_type ()) +-#define GMIME_FILTER_HEADERS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GMIME_TYPE_FILTER_HEADERS, GMimeFilterHeaders)) +-#define GMIME_FILTER_HEADERS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GMIME_TYPE_FILTER_HEADERS, GMimeFilterHeadersClass)) +-#define GMIME_IS_FILTER_HEADERS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GMIME_TYPE_FILTER_HEADERS)) +-#define GMIME_IS_FILTER_HEADERS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GMIME_TYPE_FILTER_HEADERS)) +-#define GMIME_FILTER_HEADERS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GMIME_TYPE_FILTER_HEADERS, GMimeFilterHeadersClass)) +- +-typedef struct _GMimeFilterHeaders GMimeFilterHeaders; +-typedef struct _GMimeFilterHeadersClass GMimeFilterHeadersClass; +- +-/** +- * GMimeFilterHeaders: +- * @parent_object: parent #GMimeFilter +- * @saw_nl: previous char was a \n +- * @line: temporary buffer for line unfolding +- * @line_size: size of currently allocated memory for @line +- * @lineptr: pointer to the first unused character in @line +- * +- * A filter to decode rfc2047 encoded headers +- **/ +-struct _GMimeFilterHeaders { +- GMimeFilter parent_object; +- +- gboolean saw_nl; +- char *line; +- size_t line_size; +- char *lineptr; +-}; +- +-struct _GMimeFilterHeadersClass { +- GMimeFilterClass parent_class; +- +-}; +- +- +-GType g_mime_filter_headers_get_type (void); +- +-GMimeFilter *g_mime_filter_headers_new (void); +- +-G_END_DECLS +- +- +-#endif /* _GMIME_FILTER_HEADERS_H_ */ +diff --git a/man/man1/notmuch-reply.1 b/man/man1/notmuch-reply.1 +index ac76b07..e553145 100644 +--- a/man/man1/notmuch-reply.1 ++++ b/man/man1/notmuch-reply.1 +@@ -41,7 +41,7 @@ include + .RS + .TP 4 + .BR default +-Includes subject and quoted message body. ++Includes subject and quoted message body as an RFC 2822 message. + .TP + .BR json + Produces JSON output containing headers for a reply message and the +diff --git a/notmuch-reply.c b/notmuch-reply.c +index 0f3b9cd..bfd0f51 100644 +--- a/notmuch-reply.c ++++ b/notmuch-reply.c +@@ -21,28 +21,17 @@ + */ + + #include "notmuch-client.h" +-#include "gmime-filter-headers.h" + #include "sprinter.h" + + static void + show_reply_headers (GMimeMessage *message) + { +- GMimeStream *stream_stdout = NULL, *stream_filter = NULL; ++ GMimeStream *stream_stdout = NULL; + + stream_stdout = g_mime_stream_file_new (stdout); + if (stream_stdout) { + g_mime_stream_file_set_owner (GMIME_STREAM_FILE (stream_stdout), FALSE); +- stream_filter = g_mime_stream_filter_new(stream_stdout); +- if (stream_filter) { +- // g_mime_object_write_to_stream will produce +- // RFC2047-encoded headers, but we want to present the +- // user with decoded headers and let whatever +- // ultimately sends the mail do the RFC2047 encoding. +- g_mime_stream_filter_add(GMIME_STREAM_FILTER(stream_filter), +- g_mime_filter_headers_new()); +- g_mime_object_write_to_stream(GMIME_OBJECT(message), stream_filter); +- g_object_unref(stream_filter); +- } ++ g_mime_object_write_to_stream (GMIME_OBJECT(message), stream_stdout); + g_object_unref(stream_stdout); + } + } +diff --git a/test/reply b/test/reply +index a85ebe5..d4389cf 100755 +--- a/test/reply ++++ b/test/reply +@@ -132,7 +132,9 @@ add_message '[subject]="This subject is exactly 200 bytes in length. Other than + '[body]="200-byte header"' + output=$(notmuch reply id:${gen_msg_id}) + test_expect_equal "$output" "From: Notmuch Test Suite +-Subject: Re: This subject is exactly 200 bytes in length. Other than its length there is not much of note here. Note that the length of 200 bytes includes the Subject: and Re: prefixes with two spaces ++Subject: Re: This subject is exactly 200 bytes in length. Other than its ++ length there is not much of note here. Note that the length of 200 bytes ++ includes the Subject: and Re: prefixes with two spaces + In-Reply-To: <${gen_msg_id}> + References: <${gen_msg_id}> + +@@ -200,10 +202,11 @@ add_message '[subject]="=?iso-8859-1?q?=e0=df=e7?="' \ + '[body]="Encoding"' + + output=$(notmuch reply id:${gen_msg_id}) ++# Note that GMime changes from Q- to B-encoding + test_expect_equal "$output" "\ + From: Notmuch Test Suite +-Subject: Re: àßç +-To: ☃ ++Subject: Re: =?iso-8859-1?b?4N/n?= ++To: =?UTF-8?b?4piD?= + In-Reply-To: <${gen_msg_id}> + References: <${gen_msg_id}> + +diff --git a/test/reply-to-sender b/test/reply-to-sender +index c7d15bb..30e5e38 100755 +--- a/test/reply-to-sender ++++ b/test/reply-to-sender +@@ -200,7 +200,9 @@ add_message '[subject]="This subject is exactly 200 bytes in length. Other than + '[body]="200-byte header"' + output=$(notmuch reply --reply-to=sender id:${gen_msg_id}) + test_expect_equal "$output" "From: Notmuch Test Suite +-Subject: Re: This subject is exactly 200 bytes in length. Other than its length there is not much of note here. Note that the length of 200 bytes includes the Subject: and Re: prefixes with two spaces ++Subject: Re: This subject is exactly 200 bytes in length. Other than its ++ length there is not much of note here. Note that the length of 200 bytes ++ includes the Subject: and Re: prefixes with two spaces + In-Reply-To: <${gen_msg_id}> + References: <${gen_msg_id}> + +-- +1.7.10.4 + -- 2.26.2