X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=git-send-email.perl;h=a83c7e90948fc3fe1b1ac82335704d66d060edab;hb=498fe00201401b766a200cf423a8ec42b5d5643e;hp=312a4ea2aa10189eeb470a185993bb7f8af45d0e;hpb=47e68cd803d9dace40984ea965afb54a72609a91;p=git.git diff --git a/git-send-email.perl b/git-send-email.perl index 312a4ea2a..a83c7e909 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -21,11 +21,56 @@ use warnings; use Term::ReadLine; use Getopt::Long; use Data::Dumper; -use Net::SMTP; + +package FakeTerm; +sub new { + my ($class, $reason) = @_; + return bless \$reason, shift; +} +sub readline { + my $self = shift; + die "Cannot use readline on FakeTerm: $$self"; +} +package main; # most mail servers generate the Date: header, but not all... -$ENV{LC_ALL} = 'C'; -use POSIX qw/strftime/; +sub format_2822_time { + my ($time) = @_; + my @localtm = localtime($time); + my @gmttm = gmtime($time); + my $localmin = $localtm[1] + $localtm[2] * 60; + my $gmtmin = $gmttm[1] + $gmttm[2] * 60; + if ($localtm[0] != $gmttm[0]) { + die "local zone differs from GMT by a non-minute interval\n"; + } + if ((($gmttm[6] + 1) % 7) == $localtm[6]) { + $localmin += 1440; + } elsif ((($gmttm[6] - 1) % 7) == $localtm[6]) { + $localmin -= 1440; + } elsif ($gmttm[6] != $localtm[6]) { + die "local time offset greater than or equal to 24 hours\n"; + } + my $offset = $localmin - $gmtmin; + my $offhour = $offset / 60; + my $offmin = abs($offset % 60); + if (abs($offhour) >= 24) { + die ("local time offset greater than or equal to 24 hours\n"); + } + + return sprintf("%s, %2d %s %d %02d:%02d:%02d %s%02d%02d", + qw(Sun Mon Tue Wed Thu Fri Sat)[$localtm[6]], + $localtm[3], + qw(Jan Feb Mar Apr May Jun + Jul Aug Sep Oct Nov Dec)[$localtm[4]], + $localtm[5]+1900, + $localtm[2], + $localtm[1], + $localtm[0], + ($offset >= 0) ? '+' : '-', + abs($offhour), + $offmin, + ); +} my $have_email_valid = eval { require Email::Valid; 1 }; my $smtp; @@ -37,7 +82,8 @@ sub cleanup_compose_files(); my $compose_filename = ".msg.$$"; # Variables we fill in automatically, or via prompting: -my (@to,@cc,@initial_cc,$initial_reply_to,$initial_subject,@files,$from,$compose,$time); +my (@to,@cc,@initial_cc,@bcclist, + $initial_reply_to,$initial_subject,@files,$from,$compose,$time); # Behavior modification variables my ($chain_reply_to, $quiet, $suppress_from, $no_signed_off_cc) = (1, 0, 0, 0); @@ -46,7 +92,12 @@ my $smtp_server; # Example reply to: #$initial_reply_to = ''; #<20050203173208.GA23964@foobar.com>'; -my $term = new Term::ReadLine 'git-send-email'; +my $term = eval { + new Term::ReadLine 'git-send-email'; +}; +if ($@) { + $term = new FakeTerm "$@: going non-interactive"; +} # Begin by accumulating all the variables (defined above), that we will end up # needing, first, from the command line: @@ -56,6 +107,7 @@ my $rc = GetOptions("from=s" => \$from, "subject=s" => \$initial_subject, "to=s" => \@to, "cc=s" => \@initial_cc, + "bcc=s" => \@bcclist, "chain-reply-to!" => \$chain_reply_to, "smtp-server=s" => \$smtp_server, "compose" => \$compose, @@ -64,6 +116,20 @@ my $rc = GetOptions("from=s" => \$from, "no-signed-off-cc|no-signed-off-by-cc" => \$no_signed_off_cc, ); +# Verify the user input + +foreach my $entry (@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/,/; +} + # Now, let's fill any that aren't set in with defaults: sub gitvar { @@ -160,6 +226,7 @@ sub expand_aliases { @to = expand_aliases(@to); @initial_cc = expand_aliases(@initial_cc); +@bcclist = expand_aliases(@bcclist); if (!defined $initial_subject && $compose) { do { @@ -269,6 +336,9 @@ Options: --cc Specify an initial "Cc:" list for the entire series of emails. + --bcc Specify a list of email addresses that should be Bcc: + on all the emails. + --compose Use \$EDITOR to edit an introductory message for the patch series. @@ -291,7 +361,7 @@ Options: --smtp-server If set, specifies the outgoing SMTP server to use. Defaults to localhost. - --suppress-from Supress sending emails to yourself if your address + --suppress-from Suppress sending emails to yourself if your address appears in a From: line. --quiet Make git-send-email less verbose. One line per email should be @@ -303,20 +373,23 @@ EOT } # Variables we set as part of the loop over files -our ($message_id, $cc, %mail, $subject, $reply_to, $message); +our ($message_id, $cc, %mail, $subject, $reply_to, $references, $message); sub extract_valid_address { my $address = shift; + my $local_part_regexp = '[^<>"\s@]+'; + my $domain_regexp = '[^.<>"\s@]+(?:\.[^.<>"\s@]+)+'; # check for a local address: - return $address if ($address =~ /^([\w\-]+)$/); + return $address if ($address =~ /^($local_part_regexp)$/); if ($have_email_valid) { - return Email::Valid->address($address); + return scalar Email::Valid->address($address); } else { # less robust/correct than the monster regexp in Email::Valid, # but still does a 99% job, and one less dependency - return ($address =~ /([^\"<>\s]+@[^<>\s]+)/); + $address =~ /($local_part_regexp\@$domain_regexp)/; + return $1; } } @@ -348,8 +421,8 @@ sub send_message { my @recipients = unique_email_list(@to); my $to = join (",\n\t", @recipients); - @recipients = unique_email_list(@recipients,@cc); - my $date = strftime('%a, %d %b %Y %H:%M:%S %z', localtime($time++)); + @recipients = unique_email_list(@recipients,@cc,@bcclist); + my $date = format_2822_time($time++); my $gitversion = '@@GIT_VERSION@@'; if ($gitversion =~ m/..GIT_VERSION../) { $gitversion = `git --version`; @@ -362,22 +435,28 @@ sub send_message To: $to Cc: $cc Subject: $subject -Reply-To: $from Date: $date Message-Id: $message_id X-Mailer: git-send-email $gitversion "; - $header .= "In-Reply-To: $reply_to\n" if $reply_to; + if ($reply_to) { + + $header .= "In-Reply-To: $reply_to\n"; + $header .= "References: $references\n"; + } if ($smtp_server =~ m#^/#) { my $pid = open my $sm, '|-'; defined $pid or die $!; if (!$pid) { - exec($smtp_server,'-i',@recipients) or die $!; + exec($smtp_server,'-i', + map { extract_valid_address($_) } + @recipients) or die $!; } print $sm "$header\n$message"; close $sm or die $?; } else { + require Net::SMTP; $smtp ||= Net::SMTP->new( $smtp_server ); $smtp->mail( $from ) or die $smtp->message; $smtp->to( @recipients ) or die $smtp->message; @@ -406,6 +485,7 @@ X-Mailer: git-send-email $gitversion } $reply_to = $initial_reply_to; +$references = $initial_reply_to || ''; make_message_id(); $subject = $initial_subject; @@ -482,6 +562,11 @@ foreach my $t (@files) { # set up for the next message if ($chain_reply_to || length($reply_to) == 0) { $reply_to = $message_id; + if (length $references > 0) { + $references .= " $message_id"; + } else { + $references = "$message_id"; + } } make_message_id(); }