From: Junio C Hamano Date: Fri, 22 Apr 2016 22:45:06 +0000 (-0700) Subject: Merge branch 'ew/send-email-drop-data-dumper' X-Git-Tag: v2.9.0-rc0~110 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/56b5a915e9ec5bc27c05dc7babfc601f46803d1c?hp=-c Merge branch 'ew/send-email-drop-data-dumper' Code clean-up. * ew/send-email-drop-data-dumper: send-email: do not load Data::Dumper --- 56b5a915e9ec5bc27c05dc7babfc601f46803d1c diff --combined git-send-email.perl index bc74ec979e,d76914755f..1406f64d78 --- a/git-send-email.perl +++ b/git-send-email.perl @@@ -19,11 -19,9 +19,10 @@@ use 5.008; use strict; use warnings; +use POSIX qw/strftime/; use Term::ReadLine; use Getopt::Long; use Text::ParseWords; - use Data::Dumper; use Term::ANSIColor; use File::Temp qw/ tempdir tempfile /; use File::Spec::Functions qw(catfile); @@@ -47,7 -45,6 +46,7 @@@ package main sub usage { print < +git send-email --dump-aliases Composing: --from * Email From: @@@ -77,8 -74,6 +76,8 @@@ Pass an empty string to disable certificate verification. --smtp-domain * The domain name sent to HELO/EHLO handshake + --smtp-auth * Space-separated list of allowed AUTH mechanisms. + This setting forces to use one of the listed mechanisms. --smtp-debug <0|1> * Disable, enable Net::SMTP debug. Automating: @@@ -103,9 -98,6 +102,9 @@@ `git format-patch` ones. --force * Send even if safety checks would prevent it. + Information: + --dump-aliases * Dump configured aliases and exit. + EOT exit(1); } @@@ -185,7 -177,6 +184,7 @@@ my ($quiet, $dry_run) = (0, 0) my $format_patch; my $compose_filename; my $force = 0; +my $dump_aliases = 0; # Handle interactive edition of files. my $multiedit; @@@ -216,7 -207,7 +215,7 @@@ my ($cover_cc, $cover_to) my ($to_cmd, $cc_cmd); my ($smtp_server, $smtp_server_port, @smtp_server_options); my ($smtp_authuser, $smtp_encryption, $smtp_ssl_cert_path); -my ($identity, $aliasfiletype, @alias_files, $smtp_domain); +my ($identity, $aliasfiletype, @alias_files, $smtp_domain, $smtp_auth); my ($validate, $confirm); my (@suppress_cc); my ($auto_8bit_encoding); @@@ -245,8 -236,8 +244,8 @@@ my %config_settings = "smtpserveroption" => \@smtp_server_options, "smtpuser" => \$smtp_authuser, "smtppass" => \$smtp_authpass, - "smtpsslcertpath" => \$smtp_ssl_cert_path, "smtpdomain" => \$smtp_domain, + "smtpauth" => \$smtp_auth, "to" => \@initial_to, "tocmd" => \$to_cmd, "cc" => \@initial_cc, @@@ -264,7 -255,6 +263,7 @@@ my %config_path_settings = ( "aliasesfile" => \@alias_files, + "smtpsslcertpath" => \$smtp_ssl_cert_path, ); # Handle Uncouth Termination @@@ -297,11 -287,6 +296,11 @@@ $SIG{INT} = \&signal_handler my $help; my $rc = GetOptions("h" => \$help, + "dump-aliases" => \$dump_aliases); +usage() unless $rc; +die "--dump-aliases incompatible with other options\n" + if !$help and $dump_aliases and @ARGV; +$rc = GetOptions( "sender|from=s" => \$sender, "in-reply-to=s" => \$initial_reply_to, "subject=s" => \$initial_subject, @@@ -324,7 -309,6 +323,7 @@@ "smtp-ssl-cert-path=s" => \$smtp_ssl_cert_path, "smtp-debug:i" => \$debug_net_smtp, "smtp-domain:s" => \$smtp_domain, + "smtp-auth=s" => \$smtp_auth, "identity=s" => \$identity, "annotate!" => \$annotate, "no-annotate" => sub {$annotate = 0}, @@@ -475,11 -459,25 +474,11 @@@ my ($repoauthor, $repocommitter) ($repoauthor) = Git::ident_person(@repo, 'author'); ($repocommitter) = Git::ident_person(@repo, 'committer'); -# Verify the user input - -foreach my $entry (@initial_to) { - die "Comma in --to entry: $entry'\n" unless $entry !~ m/,/; -} - -foreach my $entry (@initial_cc) { - die "Comma in --cc entry: $entry'\n" unless $entry !~ m/,/; -} - -foreach my $entry (@bcclist) { - die "Comma in --bcclist entry: $entry'\n" unless $entry !~ m/,/; -} - sub parse_address_line { if ($have_mail_address) { return map { $_->format } Mail::Address->parse($_[0]); } else { - return split_addrs($_[0]); + return Git::parse_mailboxes($_[0]); } } @@@ -488,53 -486,17 +487,53 @@@ sub split_addrs } my %aliases; + +sub parse_sendmail_alias { + local $_ = shift; + if (/"/) { + print STDERR "warning: sendmail alias with quotes is not supported: $_\n"; + } elsif (/:include:/) { + print STDERR "warning: `:include:` not supported: $_\n"; + } elsif (/[\/|]/) { + print STDERR "warning: `/file` or `|pipe` redirection not supported: $_\n"; + } elsif (/^(\S+?)\s*:\s*(.+)$/) { + my ($alias, $addr) = ($1, $2); + $aliases{$alias} = [ split_addrs($addr) ]; + } else { + print STDERR "warning: sendmail line is not recognized: $_\n"; + } +} + +sub parse_sendmail_aliases { + my $fh = shift; + my $s = ''; + while (<$fh>) { + chomp; + next if /^\s*$/ || /^\s*#/; + $s .= $_, next if $s =~ s/\\$// || s/^\s+//; + parse_sendmail_alias($s) if $s; + $s = $_; + } + $s =~ s/\\$//; # silently tolerate stray '\' on last line + parse_sendmail_alias($s) if $s; +} + my %parse_alias = ( # multiline formats can be supported in the future mutt => sub { my $fh = shift; while (<$fh>) { if (/^\s*alias\s+(?:-group\s+\S+\s+)*(\S+)\s+(.*)$/) { my ($alias, $addr) = ($1, $2); $addr =~ s/#.*$//; # mutt allows # comments - # commas delimit multiple addresses - $aliases{$alias} = [ split_addrs($addr) ]; + # commas delimit multiple addresses + my @addr = split_addrs($addr); + + # quotes may be escaped in the file, + # unescape them so we do not double-escape them later. + s/\\"/"/g foreach @addr; + $aliases{$alias} = \@addr }}}, mailrc => sub { my $fh = shift; while (<$fh>) { - if (/^alias\s+(\S+)\s+(.*)$/) { + if (/^alias\s+(\S+)\s+(.*?)\s*$/) { # spaces delimit multiple addresses $aliases{$1} = [ quotewords('\s+', 0, $2) ]; }}}, @@@ -552,7 -514,7 +551,7 @@@ $aliases{$alias} = [ split_addrs($addr) ]; } } }, - + sendmail => \&parse_sendmail_aliases, gnus => sub { my $fh = shift; while (<$fh>) { if (/\(define-mail-alias\s+"(\S+?)"\s+"(\S+?)"\)/) { $aliases{$1} = [ $2 ]; @@@ -567,10 -529,7 +566,10 @@@ if (@alias_files and $aliasfiletype an } } -($sender) = expand_aliases($sender) if defined $sender; +if ($dump_aliases) { + print "$_\n" for (sort keys %aliases); + exit(0); +} # is_format_patch_arg($f) returns 0 if $f names a patch, or 1 if # $f is a revision list specification to be passed to format-patch. @@@ -816,10 -775,7 +815,10 @@@ if (!$force) } } -if (!defined $sender) { +if (defined $sender) { + $sender =~ s/^\s+|\s+$//g; + ($sender) = expand_aliases($sender); +} else { $sender = $repoauthor || $repocommitter || ''; } @@@ -851,9 -807,12 +850,9 @@@ sub expand_one_alias return $aliases{$alias} ? expand_aliases(@{$aliases{$alias}}) : $alias; } -@initial_to = expand_aliases(@initial_to); -@initial_to = validate_address_list(sanitize_address_list(@initial_to)); -@initial_cc = expand_aliases(@initial_cc); -@initial_cc = validate_address_list(sanitize_address_list(@initial_cc)); -@bcclist = expand_aliases(@bcclist); -@bcclist = validate_address_list(sanitize_address_list(@bcclist)); +@initial_to = process_address_list(@initial_to); +@initial_cc = process_address_list(@initial_cc); +@bcclist = process_address_list(@bcclist); if ($thread && !defined $initial_reply_to && $prompting) { $initial_reply_to = ask( @@@ -950,7 -909,7 +949,7 @@@ my ($message_id_stamp, $message_id_seri sub make_message_id { my $uniq; if (!defined $message_id_stamp) { - $message_id_stamp = sprintf("%s-%s", time, $$); + $message_id_stamp = strftime("%Y%m%d%H%M%S.$$", gmtime(time)); $message_id_serial = 0; } $message_id_serial++; @@@ -965,7 -924,7 +964,7 @@@ require Sys::Hostname; $du_part = 'user@' . Sys::Hostname::hostname(); } - my $message_id_template = "<%s-git-send-email-%s>"; + my $message_id_template = "<%s-%s>"; $message_id = sprintf($message_id_template, $uniq, $du_part); #print "new message id = $message_id\n"; # Was useful for debugging } @@@ -1046,17 -1005,15 +1045,17 @@@ sub sanitize_address return $recipient; } + # remove non-escaped quotes + $recipient_name =~ s/(^|[^\\])"/$1/g; + # rfc2047 is needed if a non-ascii char is included if ($recipient_name =~ /[^[:ascii:]]/) { - $recipient_name =~ s/^"(.*)"$/$1/; $recipient_name = quote_rfc2047($recipient_name); } # double quotes are needed if specials or CTLs are included elsif ($recipient_name =~ /[][()<>@,;:\\".\000-\037\177]/) { - $recipient_name =~ s/(["\\\r])/\\$1/g; + $recipient_name =~ s/([\\\r])/\\$1/g; $recipient_name = qq["$recipient_name"]; } @@@ -1068,14 -1025,6 +1067,14 @@@ sub sanitize_address_list return (map { sanitize_address($_) } @_); } +sub process_address_list { + my @addr_list = map { parse_address_line($_) } @_; + @addr_list = expand_aliases(@addr_list); + @addr_list = sanitize_address_list(@addr_list); + @addr_list = validate_address_list(@addr_list); + return @addr_list; +} + # Returns the local Fully Qualified Domain Name (FQDN) if available. # # Tightly configured MTAa require that a caller sends a real DNS @@@ -1155,12 -1104,6 +1154,12 @@@ sub smtp_auth_maybe Authen::SASL->import(qw(Perl)); }; + # Check mechanism naming as defined in: + # https://tools.ietf.org/html/rfc4422#page-8 + if ($smtp_auth && $smtp_auth !~ /^(\b[A-Z0-9-_]{1,20}\s*)*$/) { + die "invalid smtp auth: '${smtp_auth}'"; + } + # TODO: Authentication may fail not because credentials were # invalid but due to other reasons, in which we should not # reject credentials. @@@ -1173,20 -1116,6 +1172,20 @@@ 'password' => $smtp_authpass }, sub { my $cred = shift; + + if ($smtp_auth) { + my $sasl = Authen::SASL->new( + mechanism => $smtp_auth, + callback => { + user => $cred->{'username'}, + pass => $cred->{'password'}, + authname => $cred->{'username'}, + } + ); + + return !!$smtp->auth($sasl); + } + return !!$smtp->auth($cred->{'username'}, $cred->{'password'}); }); @@@ -1217,7 -1146,8 +1216,7 @@@ sub ssl_verify_params return (SSL_verify_mode => SSL_VERIFY_PEER(), SSL_ca_file => $smtp_ssl_cert_path); } else { - print STDERR "Not using SSL_VERIFY_PEER because the CA path does not exist.\n"; - return (SSL_verify_mode => SSL_VERIFY_NONE()); + die "CA path \"$smtp_ssl_cert_path\" does not exist"; } } @@@ -1338,13 -1268,6 +1337,13 @@@ Message-Id: $message_i require Net::SMTP::SSL; $smtp_domain ||= maildomain(); require IO::Socket::SSL; + + # Suppress "variable accessed once" warning. + { + no warnings 'once'; + $IO::Socket::SSL::DEBUG = 1; + } + # Net::SMTP::SSL->new() does not forward any SSL options IO::Socket::SSL::set_client_defaults( ssl_verify_params()); @@@ -1392,11 -1315,7 +1391,11 @@@ $smtp->mail( $raw_from ) or die $smtp->message; $smtp->to( @recipients ) or die $smtp->message; $smtp->data or die $smtp->message; - $smtp->datasend("$header\n$message") or die $smtp->message; + $smtp->datasend("$header\n") or die $smtp->message; + my @lines = split /^/, $message; + foreach my $line (@lines) { + $smtp->datasend("$line") or die $smtp->message; + } $smtp->dataend() or die $smtp->message; $smtp->code =~ /250|200/ or die "Failed to send $subject\n".$smtp->message; } @@@ -1615,8 -1534,8 +1614,8 @@@ foreach my $t (@files) ($confirm =~ /^(?:auto|compose)$/ && $compose && $message_num == 1)); $needs_confirm = "inform" if ($needs_confirm && $confirm_unconfigured && @cc); - @to = validate_address_list(sanitize_address_list(@to)); - @cc = validate_address_list(sanitize_address_list(@cc)); + @to = process_address_list(@to); + @cc = process_address_list(@cc); @to = (@initial_to, @to); @cc = (@initial_cc, @cc);