From: Harald Nordgren via GitGitGadget Date: Sun, 01 Nov 2026 08:21:33 GMT Subject: [PATCH v5 0/4] imap-send: fix with reordered message headers Message-ID: In-Reply-To: git imap-send rejected patches sent with git send-email, such as ones fetched from lore with b4, because it expected Date before Subject. Changes in v5: * End the header block at an empty line in a CRLF mbox as well, so headers in the body or in later messages no longer count. Add a test. Changes in v4: * Store the tunnel as an allocated pointer, so it is only finished when it was started, and drop the local tunnel variable * Add a test for lowercase from:, date: and subject: headers * Explain in the commit message why dropping the From git-send-email special case is fine for mboxes written by other scripts Changes in v3: * Split concerns into different commits, commit message explains what they do. * Handle tunnel leak in the proper place. Changes in v2: * Case insensitive header match according to RFC 822/2822/5322. Harald Nordgren (4): imap-send: wait for the tunnel command to finish imap-send: accept messages with Subject before Date imap-send: check git send-email copies like other messages imap-send: match header names case insensitively imap-send.c | 61 ++++++++++++++++++++------------------- t/t9001-send-email.sh | 66 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 29 deletions(-) base-commit: 50fe5cc63f9cd6e89b87a2c9ccf96a444f4cfdb7 Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2456%2FHaraldNordgren%2Ffix%2Fimap-send-header-order-v5 Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2456/HaraldNordgren/fix/imap-send-header-order-v5 Pull-Request: https://github.com/git/git/pull/2456 Range-diff vs v4: 1: 8941ae12bb = 1: cb27ac3a27 imap-send: wait for the tunnel command to finish 2: ad3158b336 ! 2: fa90c64a5c imap-send: accept messages with Subject before Date @@ Commit message and git send-email writes Subject before Date. Accept the three headers in any order within the header block of each - message. + message, which ends at the first empty line whether the mbox uses LF or + CRLF line endings. Signed-off-by: Harald Nordgren @@ imap-send.c: static void wrap_in_html(struct strbuf *msg) +{ + bool from = false, date = false, subject = false; + -+ while ((line = strchr(line, '\n')) && *++line != '\n') { ++ while ((line = strchr(line, '\n')) && ++ *++line != '\n' && !starts_with(line, "\r\n")) { + if (starts_with(line, "From: ")) + from = true; + else if (starts_with(line, "Date: ")) @@ t/t9001-send-email.sh: test_expect_success $PREREQ '--compose handles to headers + test_grep "^Sending 1 message to INBOX folder" err && + test_grep "(1/1) done" err +' ++ ++test_expect_success $PREREQ 'imap-send ignores headers in the body of a CRLF mbox' ' ++ test_config imap.tunnel ./fake-imap-server && ++ q_to_cr >crlf.mbox <<-\EOF && ++ From mboxrd@z Thu Jan 1 00:00:00 1970Q ++ From: Example Q ++ Subject: Fwd: originalQ ++ Q ++ ---------- Forwarded message ---------Q ++ From: Someone Q ++ Date: Thu, 1 Jan 1970 00:00:00 +0000Q ++ Subject: originalQ ++ EOF ++ test_must_fail git imap-send --folder=INBOX err && ++ test_grep "no messages found to send" err ++' + test_done 3: 919e38c31d ! 3: 8646185486 imap-send: check git send-email copies like other messages @@ imap-send.c: static int count_messages(struct strbuf *all_msgs) break; ## t/t9001-send-email.sh ## -@@ t/t9001-send-email.sh: test_expect_success $PREREQ 'imap-send accepts a sent patch from the list archiv - test_grep "(1/1) done" err +@@ t/t9001-send-email.sh: test_expect_success $PREREQ 'imap-send ignores headers in the body of a CRLF mbo + test_grep "no messages found to send" err ' +test_expect_success $PREREQ '--imap-sent-folder stores a copy of the sent patch' ' 4: 5b4a08bbda ! 4: e7d1d7cd72 imap-send: match header names case insensitively @@ Commit message ## imap-send.c ## @@ imap-send.c: static bool has_required_headers(const char *line) - bool from = false, date = false, subject = false; - while ((line = strchr(line, '\n')) && *++line != '\n') { + while ((line = strchr(line, '\n')) && + *++line != '\n' && !starts_with(line, "\r\n")) { - if (starts_with(line, "From: ")) + if (istarts_with(line, "From: ")) from = true; -- gitgitgadget