Skip to content

Commit

Permalink
git-send-email: add option to specify sendmail command
Browse files Browse the repository at this point in the history
The sendemail.smtpServer configuration option and --smtp-server command
line option both support using a sendmail-like program to send emails by
specifying an absolute file path. However, this is not ideal for the
following reasons:

1. It overloads the meaning of smtpServer (now a program is being used
   for the server?)
2. It doesn't allow for non-absolute paths, arguments, or arbitrary
   scripting

Requiring an absolute path is bad for portability, as the same program
may be in different locations on different systems. If a user wishes to
pass arguments to their program, they have to use the smtpServerOption
option, which is cumbersome (as it must be repeated for each option) and
doesn't adhere to normal git conventions.

Introduce a new configuration option sendemail.sendmailCmd as well as a
command line option --sendmail-cmd that can be used to specify a command
(with or without arguments) or shell expression to run to send email.
The name of this option is consistent with --to-cmd and --cc-cmd. This
invocation honors the user's $PATH so that absolute paths are not
necessary. Arbitrary shell expressions are also supported, allowing
users to do basic scripting.

Give this option a higher precedence over --smtp-server and
sendemail.smtpServer, as the new interface is more flexible. For
backward compatibility, continue to support absolute paths in
--smtp-server and sendemail.smtpServer.

Signed-off-by: Gregory Anders <greg@gpanders.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
gpanders authored and gitster committed May 16, 2021
1 parent 48bf2fa commit cd5b33f
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 14 deletions.
25 changes: 18 additions & 7 deletions Documentation/git-send-email.txt
Expand Up @@ -167,6 +167,14 @@ Sending
`sendemail.envelopeSender` configuration variable; if that is
unspecified, choosing the envelope sender is left to your MTA.

--sendmail-cmd=<command>::
Specify a command to run to send the email. The command should
be sendmail-like; specifically, it must support the `-i` option.
The command will be executed in the shell if necessary. Default
is the value of `sendemail.sendmailcmd`. If unspecified, and if
--smtp-server is also unspecified, git-send-email will search
for `sendmail` in `/usr/sbin`, `/usr/lib` and $PATH.

--smtp-encryption=<encryption>::
Specify the encryption to use, either 'ssl' or 'tls'. Any other
value reverts to plain SMTP. Default is the value of
Expand Down Expand Up @@ -211,13 +219,16 @@ a password is obtained using 'git-credential'.

--smtp-server=<host>::
If set, specifies the outgoing SMTP server to use (e.g.
`smtp.example.com` or a raw IP address). Alternatively it can
specify a full pathname of a sendmail-like program instead;
the program must support the `-i` option. Default value can
be specified by the `sendemail.smtpServer` configuration
option; the built-in default is to search for `sendmail` in
`/usr/sbin`, `/usr/lib` and $PATH if such program is
available, falling back to `localhost` otherwise.
`smtp.example.com` or a raw IP address). If unspecified, and if
`--sendmail-cmd` is also unspecified, the default is to search
for `sendmail` in `/usr/sbin`, `/usr/lib` and $PATH if such a
program is available, falling back to `localhost` otherwise.
+
For backward compatibility, this option can also specify a full pathname
of a sendmail-like program instead; the program must support the `-i`
option. This method does not support passing arguments or using plain
command names. For those use cases, consider using `--sendmail-cmd`
instead.

--smtp-server-port=<port>::
Specifies a port different from the default port (SMTP
Expand Down
34 changes: 27 additions & 7 deletions git-send-email.perl
Expand Up @@ -70,6 +70,7 @@ sub usage {
Sending:
--envelope-sender <str> * Email envelope sender.
--sendmail-cmd <str> * Command to run to send email.
--smtp-server <str:int> * Outgoing SMTP server to use. The port
is optional. Default 'localhost'.
--smtp-server-option <str> * Outgoing SMTP server option to use.
Expand Down Expand Up @@ -243,6 +244,7 @@ sub do_edit {
my (@suppress_cc);
my ($auto_8bit_encoding);
my ($compose_encoding);
my ($sendmail_cmd);
# Variables with corresponding config settings & hardcoded defaults
my ($debug_net_smtp) = 0; # Net::SMTP, see send_message()
my $thread = 1;
Expand Down Expand Up @@ -290,6 +292,7 @@ sub do_edit {
"assume8bitencoding" => \$auto_8bit_encoding,
"composeencoding" => \$compose_encoding,
"transferencoding" => \$target_xfer_encoding,
"sendmailcmd" => \$sendmail_cmd,
);

my %config_path_settings = (
Expand Down Expand Up @@ -423,6 +426,7 @@ sub read_config {
"no-bcc" => \$no_bcc,
"chain-reply-to!" => \$chain_reply_to,
"no-chain-reply-to" => sub {$chain_reply_to = 0},
"sendmail-cmd=s" => \$sendmail_cmd,
"smtp-server=s" => \$smtp_server,
"smtp-server-option=s" => \@smtp_server_options,
"smtp-server-port=s" => \$smtp_server_port,
Expand Down Expand Up @@ -996,16 +1000,19 @@ sub expand_one_alias {
$reply_to = sanitize_address($reply_to);
}

if (!defined $smtp_server) {
if (!defined $sendmail_cmd && !defined $smtp_server) {
my @sendmail_paths = qw( /usr/sbin/sendmail /usr/lib/sendmail );
push @sendmail_paths, map {"$_/sendmail"} split /:/, $ENV{PATH};
foreach (@sendmail_paths) {
if (-x $_) {
$smtp_server = $_;
$sendmail_cmd = $_;
last;
}
}
$smtp_server ||= 'localhost'; # could be 127.0.0.1, too... *shrug*

if (!defined $sendmail_cmd) {
$smtp_server = 'localhost'; # could be 127.0.0.1, too... *shrug*
}
}

if ($compose && $compose > 0) {
Expand Down Expand Up @@ -1485,11 +1492,17 @@ sub send_message {

if ($dry_run) {
# We don't want to send the email.
} elsif (file_name_is_absolute($smtp_server)) {
} elsif (defined $sendmail_cmd || file_name_is_absolute($smtp_server)) {
my $pid = open my $sm, '|-';
defined $pid or die $!;
if (!$pid) {
exec($smtp_server, @sendmail_parameters) or die $!;
if (defined $sendmail_cmd) {
exec ("sh", "-c", "$sendmail_cmd \"\$@\"", "-", @sendmail_parameters)
or die $!;
} else {
exec ($smtp_server, @sendmail_parameters)
or die $!;
}
}
print $sm "$header\n$message";
close $sm or die $!;
Expand Down Expand Up @@ -1585,14 +1598,21 @@ sub send_message {
printf($dry_run ? __("Dry-Sent %s\n") : __("Sent %s\n"), $subject);
} else {
print($dry_run ? __("Dry-OK. Log says:\n") : __("OK. Log says:\n"));
if (!file_name_is_absolute($smtp_server)) {
if (!defined $sendmail_cmd && !file_name_is_absolute($smtp_server)) {
print "Server: $smtp_server\n";
print "MAIL FROM:<$raw_from>\n";
foreach my $entry (@recipients) {
print "RCPT TO:<$entry>\n";
}
} else {
print "Sendmail: $smtp_server ".join(' ',@sendmail_parameters)."\n";
my $sm;
if (defined $sendmail_cmd) {
$sm = $sendmail_cmd;
} else {
$sm = $smtp_server;
}

print "Sendmail: $sm ".join(' ',@sendmail_parameters)."\n";
}
print $header, "\n";
if ($smtp) {
Expand Down
31 changes: 31 additions & 0 deletions t/t9001-send-email.sh
Expand Up @@ -2097,6 +2097,37 @@ test_expect_success $PREREQ 'leading and trailing whitespaces are removed' '
test_cmp expected-list actual-list
'

test_expect_success $PREREQ 'test using command name with --sendmail-cmd' '
clean_fake_sendmail &&
PATH="$(pwd):$PATH" \
git send-email \
--from="Example <nobody@example.com>" \
--to=nobody@example.com \
--sendmail-cmd="fake.sendmail" \
HEAD^ &&
test_path_is_file commandline1
'

test_expect_success $PREREQ 'test using arguments with --sendmail-cmd' '
clean_fake_sendmail &&
git send-email \
--from="Example <nobody@example.com>" \
--to=nobody@example.com \
--sendmail-cmd='\''"$(pwd)/fake.sendmail" -f nobody@example.com'\'' \
HEAD^ &&
test_path_is_file commandline1
'

test_expect_success $PREREQ 'test shell expression with --sendmail-cmd' '
clean_fake_sendmail &&
git send-email \
--from="Example <nobody@example.com>" \
--to=nobody@example.com \
--sendmail-cmd='\''f() { "$(pwd)/fake.sendmail" "$@"; };f'\'' \
HEAD^ &&
test_path_is_file commandline1
'

test_expect_success $PREREQ 'invoke hook' '
mkdir -p .git/hooks &&
Expand Down

0 comments on commit cd5b33f

Please sign in to comment.