git-send-email-scripton commit [PATCH] Add git-send-email-script - tool to send emails from git-format-patch-script (83b2443)
   1#!/usr/bin/perl -w
   2# horrible hack of a script to send off a large number of email messages, one after
   3# each other, all chained together.  This is useful for large numbers of patches.
   4#
   5# Use at your own risk!!!!
   6#
   7# greg kroah-hartman Jan 8, 2002
   8# <greg@kroah.com>
   9#
  10# GPL v2 (See COPYING)
  11# 
  12# Ported to support git "mbox" format files by Ryan Anderson <ryan@michonline.com>
  13#
  14# Sends emails to the email listed on the command line.
  15# 
  16# updated to give a valid subject and CC the owner of the patch - Jan 2005
  17# first line of the message is who to CC, 
  18# and second line is the subject of the message.
  19# 
  20
  21use strict;
  22use warnings;
  23use Term::ReadLine;
  24use Mail::Sendmail;
  25use Getopt::Long;
  26use Data::Dumper;
  27use Email::Valid;
  28
  29# Variables we fill in automatically, or via prompting:
  30my (@to,@cc,$initial_reply_to,$initial_subject,@files,$from);
  31
  32# Example of them
  33# modify these options each time you run the script
  34#$to = 'torvalds@osdl.org,git@vger.kernel.org';
  35#$initial_reply_to = ''; #<20050203173208.GA23964@foobar.com>';
  36#$initial_subject = "[PATCH] Deb package build fixes";
  37#@files = (qw(
  38#0001-Make-debian-rules-executable-and-correct-the-spelling-of-rsync-in.txt
  39#0002-Debian-packages-should-include-the-binaries.txt
  40#0003-The-deb-package-building-needs-these-two-new-files-to-work-correctly.txt
  41#));
  42
  43# change this to your email address.
  44#$from = "Ryan Anderson <ryan\@michonline.com>";
  45
  46my $term = new Term::ReadLine 'git-send-email';
  47
  48# Begin by accumulating all the variables (defined above), that we will end up
  49# needing, first, from the command line:
  50
  51my $rc = GetOptions("from=s" => \$from,
  52                    "in-reply-to=s" => \$initial_reply_to,
  53                    "subject=s" => \$initial_subject,
  54                    "to=s" => \@to,
  55         );
  56
  57# Now, let's fill any that aren't set in with defaults:
  58
  59open(GITVAR,"-|","git-var","-l")
  60        or die "Failed to open pipe from git-var: $!";
  61
  62my ($author,$committer);
  63while(<GITVAR>) {
  64        chomp;
  65        my ($var,$data) = split /=/,$_,2;
  66        my @fields = split /\s+/, $data;
  67
  68        my $ident = join(" ", @fields[0...(@fields-3)]);
  69
  70        if ($var eq 'GIT_AUTHOR_IDENT') {
  71                $author = $ident;
  72        } elsif ($var eq 'GIT_COMMITTER_IDENT') {
  73                $committer = $ident;
  74        }
  75}
  76close(GITVAR);
  77
  78
  79if (!defined $from) {
  80        $from = $author || $committer;
  81        1 while (!defined ($_ = $term->readline("Who should the emails appear to be from? ", 
  82                                $from)));
  83        $from = $_;
  84        print "Emails will be sent from: ", $from, "\n";
  85}
  86
  87if (!@to) {
  88        1 while (!defined ($_ = $term->readline("Who should the emails be sent to? ", 
  89                                "")));
  90        my $to = $_;
  91        push @to, split /,/, $to;
  92}
  93
  94if (!defined $initial_subject) {
  95        1 while (!defined ($_ = 
  96                $term->readline("What subject should the emails start with? ", 
  97                        $initial_subject)));
  98        $initial_subject = $_;
  99}
 100
 101if (!defined $initial_reply_to) {
 102        1 while (!defined ($_ = 
 103                $term->readline("Message-ID to be used as In-Reply-To? ", 
 104                        $initial_reply_to)));
 105        $initial_reply_to = $_;
 106}
 107
 108# Now that all the defaults are set, process the rest of the command line
 109# arguments and collect up the files that need to be processed.
 110for my $f (@ARGV) {
 111        if (-d $f) {
 112                opendir(DH,$f)
 113                        or die "Failed to opendir $f: $!";
 114
 115                push @files, map { +$f . "/" . $_ } grep !/^\.{1,2}$/,
 116                        sort readdir(DH);
 117        } elsif (-f $f) {
 118                push @files, $f;
 119
 120        } else {
 121                print STDERR "Skipping $f - not found.\n";
 122        }
 123}
 124
 125if (@files) {
 126        print $_,"\n" for @files;
 127} else {
 128        print <<EOT;
 129git-send-email-script [options] <file | directory> [... file | directory ]
 130Options:
 131   --from         Specify the "From:" line of the email to be sent.
 132   --to           Specify the primary "To:" line of the email.
 133   --subject      Specify the initial "Subject:" line.
 134   --in-reply-to  Specify the first "In-Reply-To:" header line.
 135
 136Error: Please specify a file or a directory on the command line.
 137EOT
 138        exit(1);
 139}
 140
 141# Variables we set as part of the loop over files
 142our ($message_id, $cc, %mail, $subject, $reply_to, $message);
 143
 144
 145# Usually don't need to change anything below here.
 146
 147# we make a "fake" message id by taking the current number
 148# of seconds since the beginning of Unix time and tacking on
 149# a random number to the end, in case we are called quicker than
 150# 1 second since the last time we were called.
 151sub make_message_id
 152{
 153        my $date = `date "+\%s"`;
 154        chomp($date);
 155        my $pseudo_rand = int (rand(4200));
 156        $message_id = "<$date$pseudo_rand\@foobar.com>";
 157        print "new message id = $message_id\n";
 158}
 159
 160
 161
 162$cc = "";
 163
 164sub send_message
 165{
 166        my %to;
 167        $to{lc(Email::Valid->address($_))}++ for (@to);
 168
 169        my $to = join(",", keys %to);
 170
 171        %mail = (       To      =>      $to,
 172                        From    =>      $from,
 173                        CC      =>      $cc,
 174                        Subject =>      $subject,
 175                        Message =>      $message,
 176                        'Reply-to'      =>      $from,
 177                        'In-Reply-To'   =>      $reply_to,
 178                        'Message-ID'    =>      $message_id,
 179                        'X-Mailer'      =>      "git-send-email-script",
 180                );
 181
 182        $mail{smtp} = 'localhost';
 183
 184        #print Data::Dumper->Dump([\%mail],[qw(*mail)]);
 185
 186        sendmail(%mail) or die $Mail::Sendmail::error;
 187
 188        print "OK. Log says:\n", $Mail::Sendmail::log;
 189        print "\n\n"
 190}
 191
 192
 193$reply_to = $initial_reply_to;
 194make_message_id();
 195$subject = $initial_subject;
 196
 197foreach my $t (@files) {
 198        my $F = $t;
 199        open(F,"<",$t) or die "can't open file $t";
 200
 201        @cc = ();
 202        my $found_mbox = 0;
 203        my $header_done = 0;
 204        $message = "";
 205        while(<F>) {
 206                if (!$header_done) {
 207                        $found_mbox = 1, next if (/^From /);
 208                        chomp;
 209
 210                        if ($found_mbox) {
 211                                if (/^Subject:\s+(.*)$/) {
 212                                        $subject = $1;
 213
 214                                } elsif (/^(Cc|From):\s+(.*)$/) {
 215                                        printf("(mbox) Adding cc: %s from line '%s'\n",
 216                                                $2, $_);
 217                                        push @cc, $2;
 218                                }
 219
 220                        } else {
 221                                # In the traditional
 222                                # "send lots of email" format,
 223                                # line 1 = cc
 224                                # line 2 = subject
 225                                # So let's support that, too.
 226                                if (@cc == 0) {
 227                                        printf("(non-mbox) Adding cc: %s from line '%s'\n",
 228                                                $_, $_);
 229
 230                                        push @cc, $_;
 231
 232                                } elsif (!defined $subject) {
 233                                        $subject = $_;
 234                                }
 235                        }
 236                        
 237                        # A whitespace line will terminate the headers
 238                        if (m/^\s*$/) {
 239                                $header_done = 1;
 240                        }
 241                } else {
 242                        $message .=  $_;
 243                        if (/^Signed-off-by: (.*)$/i) {
 244                                my $c = $1;
 245                                chomp $c;
 246                                push @cc, $c;
 247                                printf("(sob) Adding cc: %s from line '%s'\n",
 248                                        $c, $_);
 249                        }
 250                }
 251        }
 252        close F;
 253
 254        my %clean_ccs;
 255        $clean_ccs{lc(Email::Valid->address($_))}++ for @cc;
 256
 257        $cc = join(",", keys %clean_ccs);
 258
 259        send_message();
 260
 261        # set up for the next message
 262        $reply_to = $message_id;
 263        make_message_id();
 264#       $subject = "Re: ".$initial_subject;
 265}