But .netrc/.authinfo format separates its entries with SP, HT, or
LF. An entry begins with "machine <remote-hostname>" token pair.
split(/\s+/) will not work for an entry that span multiple lines but
CSV will not help, either.
Is it bad to use Net::Netrc instead? This looks like exactly the
use case that module was written for, no?
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index eeb561c..ac020d1 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -158,14 +158,49 @@ Sending
--smtp-pass[=<password>]::
Password for SMTP-AUTH. The argument is optional: If no
argument is specified, then the empty string is used as
- the password. Default is the value of 'sendemail.smtppass',
- however '--smtp-pass' always overrides this value.
+ the password. Default is the value of 'sendemail.smtppass'
+ or value read from ~/.authinfo file, however '--smtp-pass'
+ always overrides this value.
+
-Furthermore, passwords need not be specified in configuration files
-or on the command line. If a username has been specified (with
+Furthermore, passwords need not be specified in configuration files or
+on the command line. If a username has been specified (with
'--smtp-user' or a 'sendemail.smtpuser'), but no password has been
-specified (with '--smtp-pass' or 'sendemail.smtppass'), then the
-user is prompted for a password while the input is masked for privacy.
+specified (with '--smtp-pass', 'sendemail.smtppass' or via
+~/.authinfo file), then the user is prompted for a password while
+the input is masked for privacy.
++
+The ~/.authinfo file should contain a line with the following
+format:
++
+ machine <domain> port <port> login <user> password <pass>
++
+Instead of `machine <domain>` pair a `default` token can be used
+instead in which case all domains will match. Similarly, `port
+<port>` and `login <user>` pairs can be omitted in which case matching
+of the given value will be skipped. `<port>` can be either an integer
+or a symbolic name. Lines are interpreted in order and password from
+the first line that matches will be used. For instance, one may end
+up with:
++
+ machine example.com login jane port ssmtp password smtppassword
+ machine example.com login jane password janepassword
+ default login janedoe password doepassword
++
+if she wants to use `smtppassword` for authenticating as `jane` to
+a service at example.com:465 (SSMTP), `janepassword` for all other
+services at example.com; and `doepassword` when authonticating as
+`janedoe` to any service. If ~/.authinfo file is missing,
+'git-send-email' will also try ~/.netrc file (even though parsing is
+not fully compatible with ftp's .netrc file format).
++
+Note that you should never make ~/.authinfo file world-readable. To
+help guarantee that, you might want to create the file with the
+following command:
++
+ ( umask 077; cat >~/.authinfo <<EOF
+ ... file contents ...
+ EOF
+ )
--smtp-server=<host>::
If set, specifies the outgoing SMTP server to use (e.g.
diff --git a/git-send-email.perl b/git-send-email.perl
index be809e5..a62dfa4 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -1045,6 +1045,86 @@ sub maildomain {
return maildomain_net() || maildomain_mta() || 'localhost.localdomain';
}
+
+sub read_password_from_stdin {
+ my $line;
+
+ system "stty -echo";
+
+ do {
+ print "Password: ";
+ $line = <STDIN>;
+ print "\n";
+ } while (!defined $line);
+
+ system "stty echo";
+
+ chomp $line;
+ return $line;
+}
+
+sub authinfo_is_port_eq {
+ my ($from_file, $value, $filename) = @_;
+
+ if (!defined $from_file) {
+ return 1;
+ } elsif ($from_file =~ /^\d+$/) {
+ return $from_file == $value;
+ }
+
+ my $port = getservbyname $from_file, 'tcp';
+ if (!defined $port) {
+ print STDERR "$filename: invalid port name: $from_file\n";
+ return;
+ }
+
+ return $port == $value;
+}
+
+sub read_password_from_authinfo {
+ my $filename = join '/', $ENV{'HOME'}, $_[0] // '.authinfo';
+ my $fd;
+ if (!open $fd, '<', $filename) {
+ return;
+ }
+
+ my $password;
+ while (my $line = <$fd>) {
+ $line =~ s/^\s+|\s+$//g;
+ my @line = split /\s+/, $line;
+ my %line;
+ while (@line) {
+ my $token = shift @line;
+ if ($token eq 'default') {
+ $line{'machine'} = $smtp_server;
+ } elsif (@line) {
+ $line{$token} = shift @line;
+ }
+ }
+
+ if (defined $line{'password'} &&
+ defined $line{'machine'} &&
+ $line{'machine'} eq $smtp_server &&
+ (!defined $line{'login'} ||
+ $line{'login'} eq $smtp_authuser) &&
+ authinfo_is_port_eq($line{'port'}, $smtp_server_port, $filename)) {
+ $password = $line{'password'};
+ last;
+ }
+ }
+
+ close $fd;
+ return $password;
+}
+
+sub read_password {
+ return
+ read_password_from_authinfo '.authinfo' ||
+ read_password_from_authinfo '.netrc' ||
+ read_password_from_stdin;
+}
+
+
# Returns 1 if the message was sent, and 0 otherwise.
# In actuality, the whole program dies when there
# is an error sending a message.@@ -1194,18 +1274,7 @@ X-Mailer: git-send-email $gitversion
};
if (!defined $smtp_authpass) {
-
- system "stty -echo";
-
- do {
- print "Password: ";
- $_ = <STDIN>;
- print "\n";
- } while (!defined $_);
-
- chomp($smtp_authpass = $_);
-
- system "stty echo";
+ $smtp_authpass = read_password
}
$auth ||= $smtp->auth( $smtp_authuser, $smtp_authpass ) or die $smtp->message;--
1.8.1