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") 60or die"Failed to open pipe from git-var:$!"; 61 62my($author,$committer); 63while(<GITVAR>) { 64chomp; 65my($var,$data) =split/=/,$_,2; 66my@fields=split/\s+/,$data; 67 68my$ident=join(" ",@fields[0...(@fields-3)]); 69 70if($vareq'GIT_AUTHOR_IDENT') { 71$author=$ident; 72}elsif($vareq'GIT_COMMITTER_IDENT') { 73$committer=$ident; 74} 75} 76close(GITVAR); 77 78 79if(!defined$from) { 80$from=$author||$committer; 811while(!defined($_=$term->readline("Who should the emails appear to be from? ", 82$from))); 83$from=$_; 84print"Emails will be sent from: ",$from,"\n"; 85} 86 87if(!@to) { 881while(!defined($_=$term->readline("Who should the emails be sent to? ", 89""))); 90my$to=$_; 91push@to,split/,/,$to; 92} 93 94if(!defined$initial_subject) { 951while(!defined($_= 96$term->readline("What subject should the emails start with? ", 97$initial_subject))); 98$initial_subject=$_; 99} 100 101if(!defined$initial_reply_to) { 1021while(!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. 110formy$f(@ARGV) { 111if(-d $f) { 112opendir(DH,$f) 113or die"Failed to opendir$f:$!"; 114 115push@files,map{ +$f."/".$_}grep!/^\.{1,2}$/, 116sort readdir(DH); 117}elsif(-f $f) { 118push@files,$f; 119 120}else{ 121print STDERR "Skipping$f- not found.\n"; 122} 123} 124 125if(@files) { 126print$_,"\n"for@files; 127}else{ 128print<<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 138exit(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{ 153my$date=`date "+\%s"`; 154chomp($date); 155my$pseudo_rand=int(rand(4200)); 156$message_id="<$date$pseudo_rand\@foobar.com>"; 157print"new message id =$message_id\n"; 158} 159 160 161 162$cc=""; 163 164sub send_message 165{ 166my%to; 167$to{lc(Email::Valid->address($_))}++for(@to); 168 169my$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 188print"OK. Log says:\n",$Mail::Sendmail::log; 189print"\n\n" 190} 191 192 193$reply_to=$initial_reply_to; 194make_message_id(); 195$subject=$initial_subject; 196 197foreachmy$t(@files) { 198my$F=$t; 199open(F,"<",$t)or die"can't open file$t"; 200 201@cc= (); 202my$found_mbox=0; 203my$header_done=0; 204$message=""; 205while(<F>) { 206if(!$header_done) { 207$found_mbox=1,next if(/^From /); 208chomp; 209 210if($found_mbox) { 211if(/^Subject:\s+(.*)$/) { 212$subject=$1; 213 214}elsif(/^(Cc|From):\s+(.*)$/) { 215printf("(mbox) Adding cc:%sfrom line '%s'\n", 216$2,$_); 217push@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. 226if(@cc==0) { 227printf("(non-mbox) Adding cc:%sfrom line '%s'\n", 228$_,$_); 229 230push@cc,$_; 231 232}elsif(!defined$subject) { 233$subject=$_; 234} 235} 236 237# A whitespace line will terminate the headers 238if(m/^\s*$/) { 239$header_done=1; 240} 241}else{ 242$message.=$_; 243if(/^Signed-off-by: (.*)$/i) { 244my$c=$1; 245chomp$c; 246push@cc,$c; 247printf("(sob) Adding cc:%sfrom line '%s'\n", 248$c,$_); 249} 250} 251} 252close F; 253 254my%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}