send-email: add --header-cmd, --no-header-cmd options
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>
This commit is contained in:

committed by
Junio C Hamano

parent
03056ce796
commit
ba92106e93
@ -61,6 +61,7 @@ sendemail.ccCmd::
|
|||||||
sendemail.chainReplyTo::
|
sendemail.chainReplyTo::
|
||||||
sendemail.envelopeSender::
|
sendemail.envelopeSender::
|
||||||
sendemail.from::
|
sendemail.from::
|
||||||
|
sendemail.headerCmd::
|
||||||
sendemail.signedoffbycc::
|
sendemail.signedoffbycc::
|
||||||
sendemail.smtpPass::
|
sendemail.smtpPass::
|
||||||
sendemail.suppresscc::
|
sendemail.suppresscc::
|
||||||
|
@ -320,6 +320,17 @@ Automating
|
|||||||
Output of this command must be single email address per line.
|
Output of this command must be single email address per line.
|
||||||
Default is the value of `sendemail.ccCmd` configuration value.
|
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::
|
--[no-]chain-reply-to::
|
||||||
If this is set, each email will be sent as a reply to the previous
|
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
|
email sent. If disabled with "--no-chain-reply-to", all emails after
|
||||||
|
@ -87,8 +87,10 @@ git send-email --dump-aliases
|
|||||||
|
|
||||||
Automating:
|
Automating:
|
||||||
--identity <str> * Use the sendemail.<id> options.
|
--identity <str> * Use the sendemail.<id> options.
|
||||||
--to-cmd <str> * Email To: via `<str> \$patch_path`
|
--to-cmd <str> * Email To: via `<str> \$patch_path`.
|
||||||
--cc-cmd <str> * Email Cc: 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.
|
--suppress-cc <str> * author, self, sob, cc, cccmd, body, bodycc, misc-by, all.
|
||||||
--[no-]cc-cover * Email Cc: addresses in the cover letter.
|
--[no-]cc-cover * Email Cc: addresses in the cover letter.
|
||||||
--[no-]to-cover * Email To: 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);
|
$author,$sender,$smtp_authpass,$annotate,$compose,$time);
|
||||||
# Things we either get from config, *or* are overridden on the
|
# Things we either get from config, *or* are overridden on the
|
||||||
# command-line.
|
# 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_to, @getopt_to);
|
||||||
my (@config_cc, @getopt_cc);
|
my (@config_cc, @getopt_cc);
|
||||||
my (@config_bcc, @getopt_bcc);
|
my (@config_bcc, @getopt_bcc);
|
||||||
@ -269,7 +271,7 @@ sub do_edit {
|
|||||||
# Variables with corresponding config settings
|
# Variables with corresponding config settings
|
||||||
my ($suppress_from, $signed_off_by_cc);
|
my ($suppress_from, $signed_off_by_cc);
|
||||||
my ($cover_cc, $cover_to);
|
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_server, $smtp_server_port, @smtp_server_options);
|
||||||
my ($smtp_authuser, $smtp_encryption, $smtp_ssl_cert_path);
|
my ($smtp_authuser, $smtp_encryption, $smtp_ssl_cert_path);
|
||||||
my ($batch_size, $relogin_delay);
|
my ($batch_size, $relogin_delay);
|
||||||
@ -318,6 +320,7 @@ my %config_settings = (
|
|||||||
"tocmd" => \$to_cmd,
|
"tocmd" => \$to_cmd,
|
||||||
"cc" => \@config_cc,
|
"cc" => \@config_cc,
|
||||||
"cccmd" => \$cc_cmd,
|
"cccmd" => \$cc_cmd,
|
||||||
|
"headercmd" => \$header_cmd,
|
||||||
"aliasfiletype" => \$aliasfiletype,
|
"aliasfiletype" => \$aliasfiletype,
|
||||||
"bcc" => \@config_bcc,
|
"bcc" => \@config_bcc,
|
||||||
"suppresscc" => \@suppress_cc,
|
"suppresscc" => \@suppress_cc,
|
||||||
@ -519,6 +522,8 @@ my %options = (
|
|||||||
"compose" => \$compose,
|
"compose" => \$compose,
|
||||||
"quiet" => \$quiet,
|
"quiet" => \$quiet,
|
||||||
"cc-cmd=s" => \$cc_cmd,
|
"cc-cmd=s" => \$cc_cmd,
|
||||||
|
"header-cmd=s" => \$header_cmd,
|
||||||
|
"no-header-cmd" => \$no_header_cmd,
|
||||||
"suppress-from!" => \$suppress_from,
|
"suppress-from!" => \$suppress_from,
|
||||||
"no-suppress-from" => sub {$suppress_from = 0},
|
"no-suppress-from" => sub {$suppress_from = 0},
|
||||||
"suppress-cc=s" => \@suppress_cc,
|
"suppress-cc=s" => \@suppress_cc,
|
||||||
@ -1780,16 +1785,16 @@ sub process_file {
|
|||||||
$subject = $initial_subject;
|
$subject = $initial_subject;
|
||||||
$message = "";
|
$message = "";
|
||||||
$message_num++;
|
$message_num++;
|
||||||
# First unfold multiline header fields
|
# Retrieve and unfold header fields.
|
||||||
|
my @header_lines = ();
|
||||||
while(<$fh>) {
|
while(<$fh>) {
|
||||||
last if /^\s*$/;
|
last if /^\s*$/;
|
||||||
if (/^\s+\S/ and @header) {
|
push(@header_lines, $_);
|
||||||
chomp($header[$#header]);
|
}
|
||||||
s/^\s+/ /;
|
@header = unfold_headers(@header_lines);
|
||||||
$header[$#header] .= $_;
|
# Add computed headers, if applicable.
|
||||||
} else {
|
unless ($no_header_cmd || ! $header_cmd) {
|
||||||
push(@header, $_);
|
push @header, invoke_header_cmd($header_cmd, $t);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
# Now parse the header
|
# Now parse the header
|
||||||
foreach(@header) {
|
foreach(@header) {
|
||||||
@ -2036,6 +2041,32 @@ sub execute_cmd {
|
|||||||
return @lines;
|
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
|
# Execute a command (e.g. $to_cmd) to get a list of email addresses
|
||||||
# and return a results array
|
# and return a results array
|
||||||
sub recipients_cmd {
|
sub recipients_cmd {
|
||||||
|
@ -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 &&
|
write_script tocmd-sed <<-\EOF &&
|
||||||
sed -n -e "s/^tocmd--//p" "$1"
|
sed -n -e "s/^tocmd--//p" "$1"
|
||||||
EOF
|
EOF
|
||||||
write_script cccmd-sed <<-\EOF
|
write_script cccmd-sed <<-\EOF &&
|
||||||
sed -n -e "s/^cccmd--//p" "$1"
|
sed -n -e "s/^cccmd--//p" "$1"
|
||||||
EOF
|
EOF
|
||||||
|
write_script headercmd-sed <<-\EOF
|
||||||
|
sed -n -e "s/^headercmd--//p" "$1"
|
||||||
|
EOF
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success $PREREQ 'tocmd works' '
|
test_expect_success $PREREQ 'tocmd works' '
|
||||||
@ -410,6 +413,53 @@ test_expect_success $PREREQ 'cccmd works' '
|
|||||||
grep "^ cccmd@example.com" msgtxt1
|
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' '
|
test_expect_success $PREREQ 'reject long lines' '
|
||||||
z8=zzzzzzzz &&
|
z8=zzzzzzzz &&
|
||||||
z64=$z8$z8$z8$z8$z8$z8$z8$z8 &&
|
z64=$z8$z8$z8$z8$z8$z8$z8$z8 &&
|
||||||
|
Reference in New Issue
Block a user