THIS IS A TEST INSTANCE ONLY! REPOSITORIES CAN BE DELETED AT ANY TIME!

Browse Source

mailinfo: support format=flowed

Add best-effort support for patches sent using format=flowed (RFC 3676).
Remove leading spaces ("unstuff"), remove soft line breaks (indicated
by space + newline), but leave the signature separator (dash dash space
newline) alone.

Warn in git am when encountering a format=flowed patch, because any
trailing spaces would most probably be lost, as the sending MUA is
encouraged to remove them when preparing the email.

Provide a test patch formatted by Mozilla Thunderbird 60 using its
default configuration.  It reuses the contents of the file mailinfo.c
before and after this patch.

Signed-off-by: Rene Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
tags/v2.20.0-rc0
René Scharfe Junio C Hamano 1 year ago
parent
commit
3aa4d81f88
7 changed files with 2646 additions and 2 deletions
  1. +4
    -0
      builtin/am.c
  2. +62
    -2
      mailinfo.c
  3. +2
    -0
      mailinfo.h
  4. +19
    -0
      t/t4256-am-format-flowed.sh
  5. +1245
    -0
      t/t4256/1/mailinfo.c
  6. +1185
    -0
      t/t4256/1/mailinfo.c.orig
  7. +129
    -0
      t/t4256/1/patch

+ 4
- 0
builtin/am.c View File

@@ -1243,6 +1243,10 @@ static int parse_mail(struct am_state *state, const char *mail)
fclose(mi.input);
fclose(mi.output);

if (mi.format_flowed)
warning(_("Patch sent with format=flowed; "
"space at the end of lines might be lost."));

/* Extract message and author information */
fp = xfopen(am_path(state, "info"), "r");
while (!strbuf_getline_lf(&sb, fp)) {


+ 62
- 2
mailinfo.c View File

@@ -237,11 +237,22 @@ static int slurp_attr(const char *line, const char *name, struct strbuf *attr)
return 1;
}

static int has_attr_value(const char *line, const char *name, const char *value)
{
struct strbuf sb = STRBUF_INIT;
int rc = slurp_attr(line, name, &sb) && !strcasecmp(sb.buf, value);
strbuf_release(&sb);
return rc;
}

static void handle_content_type(struct mailinfo *mi, struct strbuf *line)
{
struct strbuf *boundary = xmalloc(sizeof(struct strbuf));
strbuf_init(boundary, line->len);

mi->format_flowed = has_attr_value(line->buf, "format=", "flowed");
mi->delsp = has_attr_value(line->buf, "delsp=", "yes");

if (slurp_attr(line->buf, "boundary=", boundary)) {
strbuf_insert(boundary, 0, "--", 2);
if (++mi->content_top >= &mi->content[MAX_BOUNDARIES]) {
@@ -964,6 +975,52 @@ again:
return 1;
}

static void handle_filter_flowed(struct mailinfo *mi, struct strbuf *line,
struct strbuf *prev)
{
size_t len = line->len;
const char *rest;

if (!mi->format_flowed) {
handle_filter(mi, line);
return;
}

if (line->buf[len - 1] == '\n') {
len--;
if (len && line->buf[len - 1] == '\r')
len--;
}

/* Keep signature separator as-is. */
if (skip_prefix(line->buf, "-- ", &rest) && rest - line->buf == len) {
if (prev->len) {
handle_filter(mi, prev);
strbuf_reset(prev);
}
handle_filter(mi, line);
return;
}

/* Unstuff space-stuffed line. */
if (len && line->buf[0] == ' ') {
strbuf_remove(line, 0, 1);
len--;
}

/* Save flowed line for later, but without the soft line break. */
if (len && line->buf[len - 1] == ' ') {
strbuf_add(prev, line->buf, len - !!mi->delsp);
return;
}

/* Prepend any previous partial lines */
strbuf_insert(line, 0, prev->buf, prev->len);
strbuf_reset(prev);

handle_filter(mi, line);
}

static void handle_body(struct mailinfo *mi, struct strbuf *line)
{
struct strbuf prev = STRBUF_INIT;
@@ -1012,7 +1069,7 @@ static void handle_body(struct mailinfo *mi, struct strbuf *line)
strbuf_addbuf(&prev, sb);
break;
}
handle_filter(mi, sb);
handle_filter_flowed(mi, sb, &prev);
}
/*
* The partial chunk is saved in "prev" and will be
@@ -1022,13 +1079,16 @@ static void handle_body(struct mailinfo *mi, struct strbuf *line)
break;
}
default:
handle_filter(mi, line);
handle_filter_flowed(mi, line, &prev);
}

if (mi->input_error)
break;
} while (!strbuf_getwholeline(line, mi->input, '\n'));

if (prev.len)
handle_filter(mi, &prev);

flush_inbody_header_accum(mi);

handle_body_out:


+ 2
- 0
mailinfo.h View File

@@ -20,6 +20,8 @@ struct mailinfo {
struct strbuf *content[MAX_BOUNDARIES];
struct strbuf **content_top;
struct strbuf charset;
unsigned int format_flowed:1;
unsigned int delsp:1;
char *message_id;
enum {
TE_DONTCARE, TE_QP, TE_BASE64


+ 19
- 0
t/t4256-am-format-flowed.sh View File

@@ -0,0 +1,19 @@
#!/bin/sh

test_description='test format=flowed support of git am'

. ./test-lib.sh

test_expect_success 'setup' '
cp "$TEST_DIRECTORY/t4256/1/mailinfo.c.orig" mailinfo.c &&
git add mailinfo.c &&
git commit -m initial
'

test_expect_success 'am with format=flowed' '
git am <"$TEST_DIRECTORY/t4256/1/patch" >stdout 2>stderr &&
test_i18ngrep "warning: Patch sent with format=flowed" stderr &&
test_cmp "$TEST_DIRECTORY/t4256/1/mailinfo.c" mailinfo.c
'

test_done

+ 1245
- 0
t/t4256/1/mailinfo.c
File diff suppressed because it is too large
View File


+ 1185
- 0
t/t4256/1/mailinfo.c.orig
File diff suppressed because it is too large
View File


+ 129
- 0
t/t4256/1/patch View File

@@ -0,0 +1,129 @@
From: A <author@example.com>
Subject: [PATCH] mailinfo: support format=flowed
Message-ID: <aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa@example.com>
Date: Sat, 25 Aug 2018 22:04:50 +0200
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:60.0) Gecko/20100101
Thunderbird/60.0
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8; format=flowed
Content-Language: en-US
Content-Transfer-Encoding: 7bit

---
mailinfo.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 62 insertions(+), 2 deletions(-)

diff --git a/mailinfo.c b/mailinfo.c
index 3281a37d51..b395adbdf2 100644
--- a/mailinfo.c
+++ b/mailinfo.c
@@ -237,11 +237,22 @@ static int slurp_attr(const char *line, const char
*name, struct strbuf *attr)
return 1;
}

+static int has_attr_value(const char *line, const char *name, const
char *value)
+{
+ struct strbuf sb = STRBUF_INIT;
+ int rc = slurp_attr(line, name, &sb) && !strcasecmp(sb.buf, value);
+ strbuf_release(&sb);
+ return rc;
+}
+
static void handle_content_type(struct mailinfo *mi, struct strbuf *line)
{
struct strbuf *boundary = xmalloc(sizeof(struct strbuf));
strbuf_init(boundary, line->len);

+ mi->format_flowed = has_attr_value(line->buf, "format=", "flowed");
+ mi->delsp = has_attr_value(line->buf, "delsp=", "yes");
+
if (slurp_attr(line->buf, "boundary=", boundary)) {
strbuf_insert(boundary, 0, "--", 2);
if (++mi->content_top >= &mi->content[MAX_BOUNDARIES]) {
@@ -964,6 +975,52 @@ static int handle_boundary(struct mailinfo *mi,
struct strbuf *line)
return 1;
}

+static void handle_filter_flowed(struct mailinfo *mi, struct strbuf *line,
+ struct strbuf *prev)
+{
+ size_t len = line->len;
+ const char *rest;
+
+ if (!mi->format_flowed) {
+ handle_filter(mi, line);
+ return;
+ }
+
+ if (line->buf[len - 1] == '\n') {
+ len--;
+ if (len && line->buf[len - 1] == '\r')
+ len--;
+ }
+
+ /* Keep signature separator as-is. */
+ if (skip_prefix(line->buf, "-- ", &rest) && rest - line->buf == len) {
+ if (prev->len) {
+ handle_filter(mi, prev);
+ strbuf_reset(prev);
+ }
+ handle_filter(mi, line);
+ return;
+ }
+
+ /* Unstuff space-stuffed line. */
+ if (len && line->buf[0] == ' ') {
+ strbuf_remove(line, 0, 1);
+ len--;
+ }
+
+ /* Save flowed line for later, but without the soft line break. */
+ if (len && line->buf[len - 1] == ' ') {
+ strbuf_add(prev, line->buf, len - !!mi->delsp);
+ return;
+ }
+
+ /* Prepend any previous partial lines */
+ strbuf_insert(line, 0, prev->buf, prev->len);
+ strbuf_reset(prev);
+
+ handle_filter(mi, line);
+}
+
static void handle_body(struct mailinfo *mi, struct strbuf *line)
{
struct strbuf prev = STRBUF_INIT;
@@ -1012,7 +1069,7 @@ static void handle_body(struct mailinfo *mi,
struct strbuf *line)
strbuf_addbuf(&prev, sb);
break;
}
- handle_filter(mi, sb);
+ handle_filter_flowed(mi, sb, &prev);
}
/*
* The partial chunk is saved in "prev" and will be
@@ -1022,13 +1079,16 @@ static void handle_body(struct mailinfo *mi,
struct strbuf *line)
break;
}
default:
- handle_filter(mi, line);
+ handle_filter_flowed(mi, line, &prev);
}

if (mi->input_error)
break;
} while (!strbuf_getwholeline(line, mi->input, '\n'));

+ if (prev.len)
+ handle_filter(mi, &prev);
+
flush_inbody_header_accum(mi);

handle_body_out:
--
2.18.0

Loading…
Cancel
Save