Merge branch 'jc/unresolve' into next
[gitweb.git] / git-send-email.perl
index 7c8d51223fd940d8e7ccfebe90046bf32f90e327..ecfa347b85bb6c3c5e6b1b3a1f83ac1acb069b66 100755 (executable)
 use strict;
 use warnings;
 use Term::ReadLine;
-use Mail::Sendmail qw(sendmail %mailcfg);
 use Getopt::Long;
 use Data::Dumper;
-use Email::Valid;
+use Net::SMTP;
+
+# most mail servers generate the Date: header, but not all...
+$ENV{LC_ALL} = 'C';
+use POSIX qw/strftime/;
+
+my $have_email_valid = eval { require Email::Valid; 1 };
+my $smtp;
 
 sub unique_email_list(@);
 sub cleanup_compose_files();
@@ -31,7 +37,7 @@
 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);
+my (@to,@cc,@initial_cc,$initial_reply_to,$initial_subject,@files,$from,$compose,$time);
 
 # Behavior modification variables
 my ($chain_reply_to, $smtp_server, $quiet, $suppress_from, $no_signed_off_cc) = (1, "localhost", 0, 0, 0);
@@ -244,6 +250,16 @@ sub gitvar_ident {
 # Variables we set as part of the loop over files
 our ($message_id, $cc, %mail, $subject, $reply_to, $message);
 
+sub extract_valid_address {
+       my $address = shift;
+       if ($have_email_valid) {
+               return 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]+)/);
+       }
+}
 
 # Usually don't need to change anything below here.
 
@@ -253,13 +269,12 @@ sub gitvar_ident {
 # 1 second since the last time we were called.
 
 # We'll setup a template for the message id, using the "from" address:
-my $message_id_from = Email::Valid->address($from);
+my $message_id_from = extract_valid_address($from);
 my $message_id_template = "<%s-git-send-email-$message_id_from>";
 
 sub make_message_id
 {
-       my $date = `date "+\%s"`;
-       chomp($date);
+       my $date = time;
        my $pseudo_rand = int (rand(4200));
        $message_id = sprintf $message_id_template, "$date$pseudo_rand";
        #print "new message id = $message_id\n"; # Was useful for debugging
@@ -268,38 +283,49 @@ sub make_message_id
 
 
 $cc = "";
+$time = time - scalar $#files;
 
 sub send_message
 {
-       my $to = join (", ", unique_email_list(@to));
-
-       %mail = (       To      =>      $to,
-                       From    =>      $from,
-                       CC      =>      $cc,
-                       Subject =>      $subject,
-                       Message =>      $message,
-                       'Reply-to'      =>      $from,
-                       'In-Reply-To'   =>      $reply_to,
-                       'Message-ID'    =>      $message_id,
-                       'X-Mailer'      =>      "git-send-email",
-               );
-
-       $mail{smtp} = $smtp_server;
-       $mailcfg{mime} = 0;
-
-       #print Data::Dumper->Dump([\%mail],[qw(*mail)]);
-
-       sendmail(%mail) or die $Mail::Sendmail::error;
+       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++));
+
+       my $header = "From: $from
+To: $to
+Cc: $cc
+Subject: $subject
+Reply-To: $from
+Date: $date
+Message-Id: $message_id
+X-Mailer: git-send-email @@GIT_VERSION@@
+";
+       $header .= "In-Reply-To: $reply_to\n" if $reply_to;
+
+       $smtp ||= Net::SMTP->new( $smtp_server );
+       $smtp->mail( $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->dataend() or die $smtp->message;
+       $smtp->ok or die "Failed to send $subject\n".$smtp->message;
 
        if ($quiet) {
                printf "Sent %s\n", $subject;
        } else {
-               print "OK. Log says:\n", $Mail::Sendmail::log;
-               print "\n\n"
+               print "OK. Log says:
+Date: $date
+Server: $smtp_server Port: 25
+From: $from
+Subject: $subject
+Cc: $cc
+To: $to
+
+Result: ", $smtp->code, ' ', ($smtp->message =~ /\n([^\n]+\n)$/s), "\n";
        }
 }
 
-
 $reply_to = $initial_reply_to;
 make_message_id();
 $subject = $initial_subject;
@@ -307,6 +333,7 @@ sub send_message
 foreach my $t (@files) {
        open(F,"<",$t) or die "can't open file $t";
 
+       my $author_not_sender = undef;
        @cc = @initial_cc;
        my $found_mbox = 0;
        my $header_done = 0;
@@ -321,7 +348,12 @@ sub send_message
                                        $subject = $1;
 
                                } elsif (/^(Cc|From):\s+(.*)$/) {
-                                       next if ($2 eq $from && $suppress_from);
+                                       if ($2 eq $from) {
+                                               next if ($suppress_from);
+                                       }
+                                       else {
+                                               $author_not_sender = $2;
+                                       }
                                        printf("(mbox) Adding cc: %s from line '%s'\n",
                                                $2, $_) unless $quiet;
                                        push @cc, $2;
@@ -360,6 +392,9 @@ sub send_message
                }
        }
        close F;
+       if (defined $author_not_sender) {
+               $message = "From: $author_not_sender\n\n$message";
+       }
 
        $cc = join(", ", unique_email_list(@cc));
 
@@ -381,14 +416,14 @@ ()
 
 }
 
-
+$smtp->quit if $smtp;
 
 sub unique_email_list(@) {
        my %seen;
        my @emails;
 
        foreach my $entry (@_) {
-               my $clean = Email::Valid->address($entry);
+               my $clean = extract_valid_address($entry);
                next if $seen{$clean}++;
                push @emails, $entry;
        }