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