]> git.ipfire.org Git - thirdparty/git.git/commitdiff
send-email: add --header-cmd, --no-header-cmd options
authorMaxim Cournoyer <maxim.cournoyer@gmail.com>
Mon, 1 May 2023 14:38:47 +0000 (10:38 -0400)
committerJunio C Hamano <gitster@pobox.com>
Mon, 1 May 2023 15:55:52 +0000 (08:55 -0700)
Sometimes, adding a header different than CC or TO is desirable; for
example, when using Debbugs, it is best to use 'X-Debbugs-Cc' headers
to keep people in CC; this is an example use case enabled by the new
'--header-cmd' option.

The header unfolding logic is extracted to a subroutine so that it can
be reused; a test is added for coverage.

Signed-off-by: Maxim Cournoyer <maxim.cournoyer@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/config/sendemail.txt
Documentation/git-send-email.txt
git-send-email.perl
t/t9001-send-email.sh

index 51da7088a844d3f148d78317e792efd485a2c0af..92a9ebe98c63e5fe75eb98237543d41d9f15c502 100644 (file)
@@ -61,6 +61,7 @@ sendemail.ccCmd::
 sendemail.chainReplyTo::
 sendemail.envelopeSender::
 sendemail.from::
+sendemail.headerCmd::
 sendemail.signedoffbycc::
 sendemail.smtpPass::
 sendemail.suppresscc::
index b0f438ec990b7e3ac2d37730273ec2fab8088553..4d2ae061f932bffd799a2d7b9de2679c257a7e21 100644 (file)
@@ -320,6 +320,17 @@ Automating
        Output of this command must be single email address per line.
        Default is the value of `sendemail.ccCmd` configuration value.
 
+--header-cmd=<command>::
+       Specify a command that is executed once per outgoing message
+       and output RFC 2822 style header lines to be inserted into
+       them. When the `sendemail.headerCmd` configuration variable is
+       set, its value is always used. When --header-cmd is provided
+       at the command line, its value takes precedence over the
+       `sendemail.headerCmd` configuration variable.
+
+--no-header-cmd::
+       Disable any header command in use.
+
 --[no-]chain-reply-to::
        If this is set, each email will be sent as a reply to the previous
        email sent.  If disabled with "--no-chain-reply-to", all emails after
index 04503e3c3ce856566a5fd4d2085317c22737edaf..32febe9af368b124f61fd5d05bde6c223b12fa6b 100755 (executable)
@@ -87,8 +87,10 @@ git send-email --dump-aliases
 
   Automating:
     --identity              <str>  * Use the sendemail.<id> options.
-    --to-cmd                <str>  * Email To: via `<str> \$patch_path`
-    --cc-cmd                <str>  * Email Cc: via `<str> \$patch_path`
+    --to-cmd                <str>  * Email To: via `<str> \$patch_path`.
+    --cc-cmd                <str>  * Email Cc: via `<str> \$patch_path`.
+    --header-cmd            <str>  * Add headers via `<str> \$patch_path`.
+    --no-header-cmd                * Disable any header command in use.
     --suppress-cc           <str>  * author, self, sob, cc, cccmd, body, bodycc, misc-by, all.
     --[no-]cc-cover                * Email Cc: addresses in the cover letter.
     --[no-]to-cover                * Email To: addresses in the cover letter.
@@ -202,7 +204,7 @@ my (@to,@cc,@xh,$envelope_sender,
        $author,$sender,$smtp_authpass,$annotate,$compose,$time);
 # Things we either get from config, *or* are overridden on the
 # command-line.
-my ($no_cc, $no_to, $no_bcc, $no_identity);
+my ($no_cc, $no_to, $no_bcc, $no_identity, $no_header_cmd);
 my (@config_to, @getopt_to);
 my (@config_cc, @getopt_cc);
 my (@config_bcc, @getopt_bcc);
@@ -269,7 +271,7 @@ sub do_edit {
 # Variables with corresponding config settings
 my ($suppress_from, $signed_off_by_cc);
 my ($cover_cc, $cover_to);
-my ($to_cmd, $cc_cmd);
+my ($to_cmd, $cc_cmd, $header_cmd);
 my ($smtp_server, $smtp_server_port, @smtp_server_options);
 my ($smtp_authuser, $smtp_encryption, $smtp_ssl_cert_path);
 my ($batch_size, $relogin_delay);
@@ -318,6 +320,7 @@ my %config_settings = (
     "tocmd" => \$to_cmd,
     "cc" => \@config_cc,
     "cccmd" => \$cc_cmd,
+    "headercmd" => \$header_cmd,
     "aliasfiletype" => \$aliasfiletype,
     "bcc" => \@config_bcc,
     "suppresscc" => \@suppress_cc,
@@ -519,6 +522,8 @@ my %options = (
                    "compose" => \$compose,
                    "quiet" => \$quiet,
                    "cc-cmd=s" => \$cc_cmd,
+                   "header-cmd=s" => \$header_cmd,
+                   "no-header-cmd" => \$no_header_cmd,
                    "suppress-from!" => \$suppress_from,
                    "no-suppress-from" => sub {$suppress_from = 0},
                    "suppress-cc=s" => \@suppress_cc,
@@ -1780,16 +1785,16 @@ sub process_file {
        $subject = $initial_subject;
        $message = "";
        $message_num++;
-       # First unfold multiline header fields
+       # Retrieve and unfold header fields.
+       my @header_lines = ();
        while(<$fh>) {
                last if /^\s*$/;
-               if (/^\s+\S/ and @header) {
-                       chomp($header[$#header]);
-                       s/^\s+/ /;
-                       $header[$#header] .= $_;
-           } else {
-                       push(@header, $_);
-               }
+               push(@header_lines, $_);
+       }
+       @header = unfold_headers(@header_lines);
+       # Add computed headers, if applicable.
+       unless ($no_header_cmd || ! $header_cmd) {
+               push @header, invoke_header_cmd($header_cmd, $t);
        }
        # Now parse the header
        foreach(@header) {
@@ -2036,6 +2041,32 @@ sub execute_cmd {
        return @lines;
 }
 
+# Process headers lines, unfolding multiline headers as defined by RFC
+# 2822.
+sub unfold_headers {
+       my @headers;
+       foreach(@_) {
+               last if /^\s*$/;
+               if (/^\s+\S/ and @headers) {
+                       chomp($headers[$#headers]);
+                       s/^\s+/ /;
+                       $headers[$#headers] .= $_;
+               } else {
+                       push(@headers, $_);
+               }
+       }
+       return @headers;
+}
+
+# Invoke the provided CMD with FILE as an argument, which should
+# output RFC 2822 email headers. Fold multiline headers and return the
+# headers as an array.
+sub invoke_header_cmd {
+       my ($cmd, $file) = @_;
+       my @lines = execute_cmd("header-cmd", $header_cmd, $file);
+       return unfold_headers(@lines);
+}
+
 # Execute a command (e.g. $to_cmd) to get a list of email addresses
 # and return a results array
 sub recipients_cmd {
index 65203462466ed024d8200a76a91cef6d388a0d7a..f10546acba79407624da409406384aa15e7e1b89 100755 (executable)
@@ -374,13 +374,16 @@ test_expect_success $PREREQ,!AUTOIDENT 'broken implicit ident aborts send-email'
        )
 '
 
-test_expect_success $PREREQ 'setup tocmd and cccmd scripts' '
+test_expect_success $PREREQ 'setup cmd scripts' '
        write_script tocmd-sed <<-\EOF &&
        sed -n -e "s/^tocmd--//p" "$1"
        EOF
-       write_script cccmd-sed <<-\EOF
+       write_script cccmd-sed <<-\EOF &&
        sed -n -e "s/^cccmd--//p" "$1"
        EOF
+       write_script headercmd-sed <<-\EOF
+       sed -n -e "s/^headercmd--//p" "$1"
+       EOF
 '
 
 test_expect_success $PREREQ 'tocmd works' '
@@ -410,6 +413,53 @@ test_expect_success $PREREQ 'cccmd works' '
        grep "^ cccmd@example.com" msgtxt1
 '
 
+test_expect_success $PREREQ 'headercmd works' '
+       clean_fake_sendmail &&
+       cp $patches headercmd.patch &&
+       echo "headercmd--X-Debbugs-CC: dummy@example.com" >>headercmd.patch &&
+       git send-email \
+               --from="Example <nobody@example.com>" \
+               --to=nobody@example.com \
+               --header-cmd=./headercmd-sed \
+               --smtp-server="$(pwd)/fake.sendmail" \
+               headercmd.patch \
+               &&
+       grep "^X-Debbugs-CC: dummy@example.com" msgtxt1
+'
+
+test_expect_success $PREREQ '--no-header-cmd works' '
+       clean_fake_sendmail &&
+       cp $patches headercmd.patch &&
+       echo "headercmd--X-Debbugs-CC: dummy@example.com" >>headercmd.patch &&
+       git send-email \
+               --from="Example <nobody@example.com>" \
+               --to=nobody@example.com \
+               --header-cmd=./headercmd-sed \
+               --no-header-cmd \
+               --smtp-server="$(pwd)/fake.sendmail" \
+               headercmd.patch \
+               &&
+       ! grep "^X-Debbugs-CC: dummy@example.com" msgtxt1
+'
+
+test_expect_success $PREREQ 'multiline fields are correctly unfolded' '
+       clean_fake_sendmail &&
+       cp $patches headercmd.patch &&
+       write_script headercmd-multiline <<-\EOF &&
+       echo "X-Debbugs-CC: someone@example.com
+FoldedField: This is a tale
+ best told using
+ multiple lines."
+       EOF
+       git send-email \
+               --from="Example <nobody@example.com>" \
+               --to=nobody@example.com \
+               --header-cmd=./headercmd-multiline \
+               --smtp-server="$(pwd)/fake.sendmail" \
+               headercmd.patch &&
+       grep "^FoldedField: This is a tale best told using multiple lines.$" msgtxt1
+'
+
 test_expect_success $PREREQ 'reject long lines' '
        z8=zzzzzzzz &&
        z64=$z8$z8$z8$z8$z8$z8$z8$z8 &&