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