Subject: [PATCH] mailinfo: support format=flowed
From: A <[email-masked]>
Date: Sat, 25 Aug 2018 22:04:50 +0200

---
  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