use Term::ReadLine;
use Getopt::Long;
use Data::Dumper;
+use Term::ANSIColor;
use Git;
+$SIG{INT} = sub { print color("reset"), "\n"; exit };
+
package FakeTerm;
sub new {
my ($class, $reason) = @_;
--cc Specify an initial "Cc:" list for the entire series
of emails.
+ --cc-cmd Specify a command to execute per file which adds
+ per file specific cc address entries
+
--bcc Specify a list of email addresses that should be Bcc:
on all the emails.
my ($quiet, $dry_run) = (0, 0);
# Variables with corresponding config settings
-my ($thread, $chain_reply_to, $suppress_from, $signed_off_cc);
+my ($thread, $chain_reply_to, $suppress_from, $signed_off_cc, $cc_cmd);
my %config_settings = (
"thread" => [\$thread, 1],
"chainreplyto" => [\$chain_reply_to, 1],
"suppressfrom" => [\$suppress_from, 0],
"signedoffcc" => [\$signed_off_cc, 1],
+ "cccmd" => [\$cc_cmd, ""],
);
foreach my $setting (keys %config_settings) {
"smtp-server=s" => \$smtp_server,
"compose" => \$compose,
"quiet" => \$quiet,
+ "cc-cmd=s" => \$cc_cmd,
"suppress-from!" => \$suppress_from,
"signed-off-cc|signed-off-by-cc!" => \$signed_off_cc,
"dry-run" => \$dry_run,
if (!defined $initial_subject && $compose) {
do {
- $_ = $term->readline("What subject should the emails start with? ",
+ $_ = $term->readline("What subject should the initial email start with? ",
$initial_subject);
} while (!defined $_);
$initial_subject = $_;
} while (!defined $_);
$initial_reply_to = $_;
- $initial_reply_to =~ s/(^\s+|\s+$)//g;
+ $initial_reply_to =~ s/^\s+<?/</;
+ $initial_reply_to =~ s/>?\s+$/>/;
}
if (!$smtp_server) {
# We'll setup a template for the message id, using the "from" address:
+my ($message_id_stamp, $message_id_serial);
sub make_message_id
{
- my $date = time;
- my $pseudo_rand = int (rand(4200));
+ my $uniq;
+ if (!defined $message_id_stamp) {
+ $message_id_stamp = sprintf("%s-%s", time, $$);
+ $message_id_serial = 0;
+ }
+ $message_id_serial++;
+ $uniq = "$message_id_stamp-$message_id_serial";
+
my $du_part;
for ($sender, $repocommitter, $repoauthor) {
$du_part = extract_valid_address(sanitize_address($_));
use Sys::Hostname qw();
$du_part = 'user@' . Sys::Hostname::hostname();
}
- my $message_id_template = "<%s-git-send-email-$du_part>";
- $message_id = sprintf $message_id_template, "$date$pseudo_rand";
+ my $message_id_template = "<%s-git-send-email-%s>";
+ $message_id = sprintf($message_id_template, $uniq, $du_part);
#print "new message id = $message_id\n"; # Was useful for debugging
}
sub unquote_rfc2047 {
local ($_) = @_;
- if (s/=\?utf-8\?q\?(.*)\?=/$1/g) {
+ my $encoding;
+ if (s/=\?([^?]+)\?q\?(.*)\?=/$2/g) {
+ $encoding = $1;
s/_/ /g;
s/=([0-9A-F]{2})/chr(hex($1))/eg;
}
- return "$_";
+ return wantarray ? ($_, $encoding) : $_;
}
# use the simplest quoting being able to handle the recipient
$ccline = "\nCc: $cc";
}
my $sanitized_sender = sanitize_address($sender);
- make_message_id();
+ make_message_id() unless defined($message_id);
my $header = "From: $sanitized_sender
To: $to${ccline}
open(F,"<",$t) or die "can't open file $t";
my $author = undef;
+ my $author_encoding;
+ my $has_content_type;
+ my $body_encoding;
@cc = @initial_cc;
@xh = ();
my $input_format = undef;
next if ($suppress_from);
}
elsif ($1 eq 'From') {
- $author = unquote_rfc2047($2);
+ ($author, $author_encoding)
+ = unquote_rfc2047($2);
}
printf("(mbox) Adding cc: %s from line '%s'\n",
$2, $_) unless $quiet;
push @cc, $2;
}
+ elsif (/^Content-type:/i) {
+ $has_content_type = 1;
+ if (/charset="?[^ "]+/) {
+ $body_encoding = $1;
+ }
+ push @xh, $_;
+ }
+ elsif (/^Message-Id: (.*)/i) {
+ $message_id = $1;
+ }
elsif (!/^Date:\s/ && /^[-A-Za-z]+:\s+\S/) {
push @xh, $_;
}
}
}
close F;
+
+ if ($cc_cmd ne "") {
+ open(F, "$cc_cmd $t |")
+ or die "(cc-cmd) Could not execute '$cc_cmd'";
+ while(<F>) {
+ my $c = $_;
+ $c =~ s/^\s*//g;
+ $c =~ s/\n$//g;
+ push @cc, $c;
+ printf("(cc-cmd) Adding cc: %s from: '%s'\n",
+ $c, $cc_cmd) unless $quiet;
+ }
+ close F
+ or die "(cc-cmd) failed to close pipe to '$cc_cmd'";
+ }
+
if (defined $author) {
$message = "From: $author\n\n$message";
+ if (defined $author_encoding) {
+ if ($has_content_type) {
+ if ($body_encoding eq $author_encoding) {
+ # ok, we already have the right encoding
+ }
+ else {
+ # uh oh, we should re-encode
+ }
+ }
+ else {
+ push @xh,
+ 'MIME-Version: 1.0',
+ "Content-Type: text/plain; charset=$author_encoding",
+ 'Content-Transfer-Encoding: 8bit';
+ }
+ }
}
-
send_message();
# set up for the next message
$references = "$message_id";
}
}
+ $message_id = undef;
}
if ($compose) {