}
package main;
+
+sub usage {
+ print <<EOT;
+git-send-email [options] <file | directory>...
+Options:
+ --from Specify the "From:" line of the email to be sent.
+
+ --to Specify the primary "To:" line of the email.
+
+ --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.
+
+ --subject Specify the initial "Subject:" line.
+ Only necessary if --compose is also set. If --compose
+ is not set, this will be prompted for.
+
+ --in-reply-to Specify the first "In-Reply-To:" header line.
+ Only used if --compose is also set. If --compose is not
+ set, this will be prompted for.
+
+ --chain-reply-to If set, the replies will all be to the previous
+ email sent, rather than to the first email sent.
+ Defaults to on.
+
+ --no-signed-off-cc Suppress the automatic addition of email addresses
+ that appear in Signed-off-by: or Cc: lines to the cc:
+ list. Note: Using this option is not recommended.
+
+ --smtp-server If set, specifies the outgoing SMTP server to use.
+ Defaults to localhost.
+
+ --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 all that is output.
+
+ --dry-run Do everything except actually send the emails.
+
+ --envelope-sender Specify the envelope sender used to send the emails.
+
+EOT
+ exit(1);
+}
+
# most mail servers generate the Date: header, but not all...
sub format_2822_time {
my ($time) = @_;
my ($chain_reply_to, $quiet, $suppress_from, $no_signed_off_cc,
$dry_run) = (1, 0, 0, 0, 0);
my $smtp_server;
+my $envelope_sender;
# Example reply to:
#$initial_reply_to = ''; #<20050203173208.GA23964@foobar.com>';
$term = new FakeTerm "$@: going non-interactive";
}
+my $def_chain = $repo->config_boolean('sendemail.chainreplyto');
+if ($def_chain and $def_chain eq 'false') {
+ $chain_reply_to = 0;
+}
+
+@bcclist = $repo->config('sendemail.bcc');
+if (!@bcclist or !$bcclist[0]) {
+ @bcclist = ();
+}
+
# Begin by accumulating all the variables (defined above), that we will end up
# needing, first, from the command line:
"suppress-from" => \$suppress_from,
"no-signed-off-cc|no-signed-off-by-cc" => \$no_signed_off_cc,
"dry-run" => \$dry_run,
+ "envelope-sender=s" => \$envelope_sender,
);
+unless ($rc) {
+ usage();
+}
+
# Verify the user input
foreach my $entry (@to) {
}
@to = expand_aliases(@to);
+@to = (map { sanitize_address_rfc822($_) } @to);
@initial_cc = expand_aliases(@initial_cc);
@bcclist = expand_aliases(@bcclist);
print $_,"\n" for (@files);
}
} else {
- print <<EOT;
-git-send-email [options] <file | directory> [... file | directory ]
-Options:
- --from Specify the "From:" line of the email to be sent.
-
- --to Specify the primary "To:" line of the email.
-
- --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.
-
- --subject Specify the initial "Subject:" line.
- Only necessary if --compose is also set. If --compose
- is not set, this will be prompted for.
-
- --in-reply-to Specify the first "In-Reply-To:" header line.
- Only used if --compose is also set. If --compose is not
- set, this will be prompted for.
-
- --chain-reply-to If set, the replies will all be to the previous
- email sent, rather than to the first email sent.
- Defaults to on.
-
- --no-signed-off-cc Suppress the automatic addition of email addresses
- that appear in a Signed-off-by: line, to the cc: list.
- Note: Using this option is not recommended.
-
- --smtp-server If set, specifies the outgoing SMTP server to use.
- Defaults to localhost.
-
- --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
- all that is output.
-
-Error: Please specify a file or a directory on the command line.
-EOT
- exit(1);
+ print STDERR "\nNo patch files specified!\n\n";
+ usage();
}
# Variables we set as part of the loop over files
-our ($message_id, $cc, %mail, $subject, $reply_to, $references, $message);
+our ($message_id, %mail, $subject, $reply_to, $references, $message);
sub extract_valid_address {
my $address = shift;
-$cc = "";
$time = time - scalar $#files;
+sub unquote_rfc2047 {
+ local ($_) = @_;
+ if (s/=\?utf-8\?q\?(.*)\?=/$1/g) {
+ s/_/ /g;
+ s/=([0-9A-F]{2})/chr(hex($1))/eg;
+ }
+ return "$_";
+}
+
+# If an address contains a . in the name portion, the name must be quoted.
+sub sanitize_address_rfc822
+{
+ my ($recipient) = @_;
+ my ($recipient_name) = ($recipient =~ /^(.*?)\s+</);
+ if ($recipient_name && $recipient_name =~ /\./ && $recipient_name !~ /^".*"$/) {
+ my ($name, $addr) = ($recipient =~ /^(.*?)(\s+<.*)/);
+ $recipient = "\"$name\"$addr";
+ }
+ return $recipient;
+}
+
sub send_message
{
my @recipients = unique_email_list(@to);
+ @cc = (map { sanitize_address_rfc822($_) } @cc);
my $to = join (",\n\t", @recipients);
@recipients = unique_email_list(@recipients,@cc,@bcclist);
+ @recipients = (map { extract_valid_address($_) } @recipients);
my $date = format_2822_time($time++);
my $gitversion = '@@GIT_VERSION@@';
if ($gitversion =~ m/..GIT_VERSION../) {
$gitversion = Git::version();
}
- my ($author_name) = ($from =~ /^(.*?)\s+</);
- if ($author_name && $author_name =~ /\./ && $author_name !~ /^".*"$/) {
- my ($name, $addr) = ($from =~ /^(.*?)(\s+<.*)/);
- $from = "\"$name\"$addr";
- }
+ my $cc = join(", ", unique_email_list(@cc));
+ $from = sanitize_address_rfc822($from);
my $header = "From: $from
To: $to
Cc: $cc
$header .= join("\n", @xh) . "\n";
}
+ my @sendmail_parameters = ('-i', @recipients);
+ my $raw_from = $from;
+ $raw_from = $envelope_sender if (defined $envelope_sender);
+ $raw_from = extract_valid_address($raw_from);
+ unshift (@sendmail_parameters,
+ '-f', $raw_from) if(defined $envelope_sender);
+
if ($dry_run) {
# We don't want to send the email.
} elsif ($smtp_server =~ m#^/#) {
my $pid = open my $sm, '|-';
defined $pid or die $!;
if (!$pid) {
- exec($smtp_server,'-i',
- map { extract_valid_address($_) }
- @recipients) or die $!;
+ exec($smtp_server, @sendmail_parameters) 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->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->ok or die "Failed to send $subject\n".$smtp->message;
}
if ($quiet) {
- printf "Sent %s\n", $subject;
+ printf (($dry_run ? "Dry-" : "")."Sent %s\n", $subject);
} else {
- print "OK. Log says:\nDate: $date\n";
- if ($smtp) {
+ print (($dry_run ? "Dry-" : "")."OK. Log says:\nDate: $date\n");
+ if ($smtp_server !~ m#^/#) {
print "Server: $smtp_server\n";
+ print "MAIL FROM:<$raw_from>\n";
+ print "RCPT TO:".join(',',(map { "<$_>" } @recipients))."\n";
} else {
- print "Sendmail: $smtp_server\n";
+ print "Sendmail: $smtp_server ".join(' ',@sendmail_parameters)."\n";
}
print "From: $from\nSubject: $subject\nCc: $cc\nTo: $to\n\n";
if ($smtp) {
}
} else {
$message .= $_;
- if (/^Signed-off-by: (.*)$/i && !$no_signed_off_cc) {
- my $c = $1;
+ if (/^(Signed-off-by|Cc): (.*)$/i && !$no_signed_off_cc) {
+ my $c = $2;
chomp $c;
push @cc, $c;
printf("(sob) Adding cc: %s from line '%s'\n",
}
close F;
if (defined $author_not_sender) {
+ $author_not_sender = unquote_rfc2047($author_not_sender);
$message = "From: $author_not_sender\n\n$message";
}
- $cc = join(", ", unique_email_list(@cc));
send_message();
if ($chain_reply_to || !defined $reply_to || length($reply_to) == 0) {
$reply_to = $message_id;
if (length $references > 0) {
- $references .= " $message_id";
+ $references .= "\n $message_id";
} else {
$references = "$message_id";
}