1#!/usr/bin/perl 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 19use5.008; 20use strict; 21use warnings; 22use POSIX qw/strftime/; 23use Term::ReadLine; 24use Getopt::Long; 25use Text::ParseWords; 26use Term::ANSIColor; 27use File::Temp qw/ tempdir tempfile /; 28use File::Spec::Functions qw(catdir catfile); 29use Git::LoadCPAN::Error qw(:try); 30use Cwd qw(abs_path cwd); 31use Git; 32use Git::I18N; 33use Net::Domain (); 34use Net::SMTP (); 35use Git::LoadCPAN::Mail::Address; 36 37Getopt::Long::Configure qw/ pass_through /; 38 39package FakeTerm; 40sub new { 41my($class,$reason) =@_; 42returnbless \$reason,shift; 43} 44subreadline{ 45my$self=shift; 46die"Cannot use readline on FakeTerm:$$self"; 47} 48package main; 49 50 51sub usage { 52print<<EOT; 53git send-email [options] <file | directory | rev-list options > 54git send-email --dump-aliases 55 56 Composing: 57 --from <str> * Email From: 58 --[no-]to <str> * Email To: 59 --[no-]cc <str> * Email Cc: 60 --[no-]bcc <str> * Email Bcc: 61 --subject <str> * Email "Subject:" 62 --reply-to <str> * Email "Reply-To:" 63 --in-reply-to <str> * Email "In-Reply-To:" 64 --[no-]xmailer * Add "X-Mailer:" header (default). 65 --[no-]annotate * Review each patch that will be sent in an editor. 66 --compose * Open an editor for introduction. 67 --compose-encoding <str> * Encoding to assume for introduction. 68 --8bit-encoding <str> * Encoding to assume 8bit mails if undeclared 69 --transfer-encoding <str> * Transfer encoding to use (quoted-printable, 8bit, base64) 70 71 Sending: 72 --envelope-sender <str> * Email envelope sender. 73 --smtp-server <str:int> * Outgoing SMTP server to use. The port 74 is optional. Default 'localhost'. 75 --smtp-server-option <str> * Outgoing SMTP server option to use. 76 --smtp-server-port <int> * Outgoing SMTP server port. 77 --smtp-user <str> * Username for SMTP-AUTH. 78 --smtp-pass <str> * Password for SMTP-AUTH; not necessary. 79 --smtp-encryption <str> * tls or ssl; anything else disables. 80 --smtp-ssl * Deprecated. Use '--smtp-encryption ssl'. 81 --smtp-ssl-cert-path <str> * Path to ca-certificates (either directory or file). 82 Pass an empty string to disable certificate 83 verification. 84 --smtp-domain <str> * The domain name sent to HELO/EHLO handshake 85 --smtp-auth <str> * Space-separated list of allowed AUTH mechanisms. 86 This setting forces to use one of the listed mechanisms. 87 --smtp-debug <0|1> * Disable, enable Net::SMTP debug. 88 89 --batch-size <int> * send max <int> message per connection. 90 --relogin-delay <int> * delay <int> seconds between two successive login. 91 This option can only be used with --batch-size 92 93 Automating: 94 --identity <str> * Use the sendemail.<id> options. 95 --to-cmd <str> * Email To: via `<str> \$patch_path` 96 --cc-cmd <str> * Email Cc: via `<str> \$patch_path` 97 --suppress-cc <str> * author, self, sob, cc, cccmd, body, bodycc, all. 98 --[no-]cc-cover * Email Cc: addresses in the cover letter. 99 --[no-]to-cover * Email To: addresses in the cover letter. 100 --[no-]signed-off-by-cc * Send to Signed-off-by: addresses. Default on. 101 --[no-]suppress-from * Send to self. Default off. 102 --[no-]chain-reply-to * Chain In-Reply-To: fields. Default off. 103 --[no-]thread * Use In-Reply-To: field. Default on. 104 105 Administering: 106 --confirm <str> * Confirm recipients before sending; 107 auto, cc, compose, always, or never. 108 --quiet * Output one line of info per email. 109 --dry-run * Don't actually send the emails. 110 --[no-]validate * Perform patch sanity checks. Default on. 111 --[no-]format-patch * understand any non optional arguments as 112 `git format-patch` ones. 113 --force * Send even if safety checks would prevent it. 114 115 Information: 116 --dump-aliases * Dump configured aliases and exit. 117 118EOT 119exit(1); 120} 121 122# most mail servers generate the Date: header, but not all... 123sub format_2822_time { 124my($time) =@_; 125my@localtm=localtime($time); 126my@gmttm=gmtime($time); 127my$localmin=$localtm[1] +$localtm[2] *60; 128my$gmtmin=$gmttm[1] +$gmttm[2] *60; 129if($localtm[0] !=$gmttm[0]) { 130die __("local zone differs from GMT by a non-minute interval\n"); 131} 132if((($gmttm[6] +1) %7) ==$localtm[6]) { 133$localmin+=1440; 134}elsif((($gmttm[6] -1) %7) ==$localtm[6]) { 135$localmin-=1440; 136}elsif($gmttm[6] !=$localtm[6]) { 137die __("local time offset greater than or equal to 24 hours\n"); 138} 139my$offset=$localmin-$gmtmin; 140my$offhour=$offset/60; 141my$offmin=abs($offset%60); 142if(abs($offhour) >=24) { 143die __("local time offset greater than or equal to 24 hours\n"); 144} 145 146returnsprintf("%s,%2d%s%d%02d:%02d:%02d%s%02d%02d", 147qw(Sun Mon Tue Wed Thu Fri Sat)[$localtm[6]], 148$localtm[3], 149qw(Jan Feb Mar Apr May Jun 150 Jul Aug Sep Oct Nov Dec)[$localtm[4]], 151$localtm[5]+1900, 152$localtm[2], 153$localtm[1], 154$localtm[0], 155($offset>=0) ?'+':'-', 156abs($offhour), 157$offmin, 158); 159} 160 161my$have_email_valid=eval{require Email::Valid;1}; 162my$smtp; 163my$auth; 164my$num_sent=0; 165 166# Regexes for RFC 2047 productions. 167my$re_token=qr/[^][()<>@,;:\\"\/?.=\000-\037\177-\377]+/; 168my$re_encoded_text=qr/[^? \000-\037\177-\377]+/; 169my$re_encoded_word=qr/=\?($re_token)\?($re_token)\?($re_encoded_text)\?=/; 170 171# Variables we fill in automatically, or via prompting: 172my(@to,$no_to,@initial_to,@cc,$no_cc,@initial_cc,@initial_bcc,$no_bcc,@xh, 173$initial_in_reply_to,$reply_to,$initial_subject,@files, 174$author,$sender,$smtp_authpass,$annotate,$use_xmailer,$compose,$time); 175 176my$envelope_sender; 177 178# Example reply to: 179#$initial_in_reply_to = ''; #<20050203173208.GA23964@foobar.com>'; 180 181my$repo=eval{ Git->repository() }; 182my@repo=$repo? ($repo) : (); 183my$term=eval{ 184$ENV{"GIT_SEND_EMAIL_NOTTY"} 185? new Term::ReadLine 'git-send-email', \*STDIN, \*STDOUT 186: new Term::ReadLine 'git-send-email'; 187}; 188if($@) { 189$term= new FakeTerm "$@: going non-interactive"; 190} 191 192# Behavior modification variables 193my($quiet,$dry_run) = (0,0); 194my$format_patch; 195my$compose_filename; 196my$force=0; 197my$dump_aliases=0; 198 199# Handle interactive edition of files. 200my$multiedit; 201my$editor; 202 203sub do_edit { 204if(!defined($editor)) { 205$editor= Git::command_oneline('var','GIT_EDITOR'); 206} 207if(defined($multiedit) && !$multiedit) { 208map{ 209system('sh','-c',$editor.' "$@"',$editor,$_); 210if(($?&127) || ($?>>8)) { 211die(__("the editor exited uncleanly, aborting everything")); 212} 213}@_; 214}else{ 215system('sh','-c',$editor.' "$@"',$editor,@_); 216if(($?&127) || ($?>>8)) { 217die(__("the editor exited uncleanly, aborting everything")); 218} 219} 220} 221 222# Variables with corresponding config settings 223my($thread,$chain_reply_to,$suppress_from,$signed_off_by_cc); 224my($cover_cc,$cover_to); 225my($to_cmd,$cc_cmd); 226my($smtp_server,$smtp_server_port,@smtp_server_options); 227my($smtp_authuser,$smtp_encryption,$smtp_ssl_cert_path); 228my($batch_size,$relogin_delay); 229my($identity,$aliasfiletype,@alias_files,$smtp_domain,$smtp_auth); 230my($validate,$confirm); 231my(@suppress_cc); 232my($auto_8bit_encoding); 233my($compose_encoding); 234my$target_xfer_encoding='auto'; 235 236my($debug_net_smtp) =0;# Net::SMTP, see send_message() 237 238my%config_bool_settings= ( 239"thread"=> [\$thread,1], 240"chainreplyto"=> [\$chain_reply_to,0], 241"suppressfrom"=> [\$suppress_from,undef], 242"signedoffbycc"=> [\$signed_off_by_cc,undef], 243"cccover"=> [\$cover_cc,undef], 244"tocover"=> [\$cover_to,undef], 245"signedoffcc"=> [\$signed_off_by_cc,undef],# Deprecated 246"validate"=> [\$validate,1], 247"multiedit"=> [\$multiedit,undef], 248"annotate"=> [\$annotate,undef], 249"xmailer"=> [\$use_xmailer,1] 250); 251 252my%config_settings= ( 253"smtpserver"=> \$smtp_server, 254"smtpserverport"=> \$smtp_server_port, 255"smtpserveroption"=> \@smtp_server_options, 256"smtpuser"=> \$smtp_authuser, 257"smtppass"=> \$smtp_authpass, 258"smtpdomain"=> \$smtp_domain, 259"smtpauth"=> \$smtp_auth, 260"smtpbatchsize"=> \$batch_size, 261"smtprelogindelay"=> \$relogin_delay, 262"to"=> \@initial_to, 263"tocmd"=> \$to_cmd, 264"cc"=> \@initial_cc, 265"cccmd"=> \$cc_cmd, 266"aliasfiletype"=> \$aliasfiletype, 267"bcc"=> \@initial_bcc, 268"suppresscc"=> \@suppress_cc, 269"envelopesender"=> \$envelope_sender, 270"confirm"=> \$confirm, 271"from"=> \$sender, 272"assume8bitencoding"=> \$auto_8bit_encoding, 273"composeencoding"=> \$compose_encoding, 274"transferencoding"=> \$target_xfer_encoding, 275); 276 277my%config_path_settings= ( 278"aliasesfile"=> \@alias_files, 279"smtpsslcertpath"=> \$smtp_ssl_cert_path, 280); 281 282# Handle Uncouth Termination 283sub signal_handler { 284 285# Make text normal 286print color("reset"),"\n"; 287 288# SMTP password masked 289system"stty echo"; 290 291# tmp files from --compose 292if(defined$compose_filename) { 293if(-e $compose_filename) { 294printf __("'%s' contains an intermediate version ". 295"of the email you were composing.\n"), 296$compose_filename; 297} 298if(-e ($compose_filename.".final")) { 299printf __("'%s.final' contains the composed email.\n"), 300$compose_filename; 301} 302} 303 304exit; 305}; 306 307$SIG{TERM} = \&signal_handler; 308$SIG{INT} = \&signal_handler; 309 310# Read our sendemail.* config 311sub read_config { 312my($prefix) =@_; 313 314foreachmy$setting(keys%config_bool_settings) { 315my$target=$config_bool_settings{$setting}->[0]; 316$$target= Git::config_bool(@repo,"$prefix.$setting")unless(defined$$target); 317} 318 319foreachmy$setting(keys%config_path_settings) { 320my$target=$config_path_settings{$setting}; 321if(ref($target)eq"ARRAY") { 322unless(@$target) { 323my@values= Git::config_path(@repo,"$prefix.$setting"); 324@$target=@valuesif(@values&&defined$values[0]); 325} 326} 327else{ 328$$target= Git::config_path(@repo,"$prefix.$setting")unless(defined$$target); 329} 330} 331 332foreachmy$setting(keys%config_settings) { 333my$target=$config_settings{$setting}; 334next if$settingeq"to"and defined$no_to; 335next if$settingeq"cc"and defined$no_cc; 336next if$settingeq"bcc"and defined$no_bcc; 337if(ref($target)eq"ARRAY") { 338unless(@$target) { 339my@values= Git::config(@repo,"$prefix.$setting"); 340@$target=@valuesif(@values&&defined$values[0]); 341} 342} 343else{ 344$$target= Git::config(@repo,"$prefix.$setting")unless(defined$$target); 345} 346} 347 348if(!defined$smtp_encryption) { 349my$enc= Git::config(@repo,"$prefix.smtpencryption"); 350if(defined$enc) { 351$smtp_encryption=$enc; 352}elsif(Git::config_bool(@repo,"$prefix.smtpssl")) { 353$smtp_encryption='ssl'; 354} 355} 356} 357 358# Begin by accumulating all the variables (defined above), that we will end up 359# needing, first, from the command line: 360 361my$help; 362my$rc= GetOptions("h"=> \$help, 363"dump-aliases"=> \$dump_aliases); 364usage()unless$rc; 365die __("--dump-aliases incompatible with other options\n") 366if!$helpand$dump_aliasesand@ARGV; 367$rc= GetOptions( 368"sender|from=s"=> \$sender, 369"in-reply-to=s"=> \$initial_in_reply_to, 370"reply-to=s"=> \$reply_to, 371"subject=s"=> \$initial_subject, 372"to=s"=> \@initial_to, 373"to-cmd=s"=> \$to_cmd, 374"no-to"=> \$no_to, 375"cc=s"=> \@initial_cc, 376"no-cc"=> \$no_cc, 377"bcc=s"=> \@initial_bcc, 378"no-bcc"=> \$no_bcc, 379"chain-reply-to!"=> \$chain_reply_to, 380"no-chain-reply-to"=>sub{$chain_reply_to=0}, 381"smtp-server=s"=> \$smtp_server, 382"smtp-server-option=s"=> \@smtp_server_options, 383"smtp-server-port=s"=> \$smtp_server_port, 384"smtp-user=s"=> \$smtp_authuser, 385"smtp-pass:s"=> \$smtp_authpass, 386"smtp-ssl"=>sub{$smtp_encryption='ssl'}, 387"smtp-encryption=s"=> \$smtp_encryption, 388"smtp-ssl-cert-path=s"=> \$smtp_ssl_cert_path, 389"smtp-debug:i"=> \$debug_net_smtp, 390"smtp-domain:s"=> \$smtp_domain, 391"smtp-auth=s"=> \$smtp_auth, 392"identity=s"=> \$identity, 393"annotate!"=> \$annotate, 394"no-annotate"=>sub{$annotate=0}, 395"compose"=> \$compose, 396"quiet"=> \$quiet, 397"cc-cmd=s"=> \$cc_cmd, 398"suppress-from!"=> \$suppress_from, 399"no-suppress-from"=>sub{$suppress_from=0}, 400"suppress-cc=s"=> \@suppress_cc, 401"signed-off-cc|signed-off-by-cc!"=> \$signed_off_by_cc, 402"no-signed-off-cc|no-signed-off-by-cc"=>sub{$signed_off_by_cc=0}, 403"cc-cover|cc-cover!"=> \$cover_cc, 404"no-cc-cover"=>sub{$cover_cc=0}, 405"to-cover|to-cover!"=> \$cover_to, 406"no-to-cover"=>sub{$cover_to=0}, 407"confirm=s"=> \$confirm, 408"dry-run"=> \$dry_run, 409"envelope-sender=s"=> \$envelope_sender, 410"thread!"=> \$thread, 411"no-thread"=>sub{$thread=0}, 412"validate!"=> \$validate, 413"no-validate"=>sub{$validate=0}, 414"transfer-encoding=s"=> \$target_xfer_encoding, 415"format-patch!"=> \$format_patch, 416"no-format-patch"=>sub{$format_patch=0}, 417"8bit-encoding=s"=> \$auto_8bit_encoding, 418"compose-encoding=s"=> \$compose_encoding, 419"force"=> \$force, 420"xmailer!"=> \$use_xmailer, 421"no-xmailer"=>sub{$use_xmailer=0}, 422"batch-size=i"=> \$batch_size, 423"relogin-delay=i"=> \$relogin_delay, 424); 425 426usage()if$help; 427unless($rc) { 428 usage(); 429} 430 431die __("Cannot run git format-patch from outside a repository\n") 432if$format_patchand not$repo; 433 434die __("`batch-size` and `relogin` must be specified together ". 435"(via command-line or configuration option)\n") 436ifdefined$relogin_delayand not defined$batch_size; 437 438# read configuration from [sendemail "$identity"], fall back on [sendemail] 439$identity= Git::config(@repo,"sendemail.identity")unless(defined$identity); 440read_config("sendemail.$identity")if(defined$identity); 441read_config("sendemail"); 442 443# fall back on builtin bool defaults 444foreachmy$setting(values%config_bool_settings) { 445${$setting->[0]} =$setting->[1]unless(defined(${$setting->[0]})); 446} 447 448# 'default' encryption is none -- this only prevents a warning 449$smtp_encryption=''unless(defined$smtp_encryption); 450 451# Set CC suppressions 452my(%suppress_cc); 453if(@suppress_cc) { 454foreachmy$entry(@suppress_cc) { 455die sprintf(__("Unknown --suppress-cc field: '%s'\n"),$entry) 456unless$entry=~/^(?:all|cccmd|cc|author|self|sob|body|bodycc)$/; 457$suppress_cc{$entry} =1; 458} 459} 460 461if($suppress_cc{'all'}) { 462foreachmy$entry(qw (cccmd cc author self sob body bodycc)) { 463$suppress_cc{$entry} =1; 464} 465delete$suppress_cc{'all'}; 466} 467 468# If explicit old-style ones are specified, they trump --suppress-cc. 469$suppress_cc{'self'} =$suppress_fromifdefined$suppress_from; 470$suppress_cc{'sob'} = !$signed_off_by_ccifdefined$signed_off_by_cc; 471 472if($suppress_cc{'body'}) { 473foreachmy$entry(qw (sob bodycc)) { 474$suppress_cc{$entry} =1; 475} 476delete$suppress_cc{'body'}; 477} 478 479# Set confirm's default value 480my$confirm_unconfigured= !defined$confirm; 481if($confirm_unconfigured) { 482$confirm=scalar%suppress_cc?'compose':'auto'; 483}; 484die sprintf(__("Unknown --confirm setting: '%s'\n"),$confirm) 485unless$confirm=~/^(?:auto|cc|compose|always|never)/; 486 487# Debugging, print out the suppressions. 488if(0) { 489print"suppressions:\n"; 490foreachmy$entry(keys%suppress_cc) { 491printf" %-5s ->$suppress_cc{$entry}\n",$entry; 492} 493} 494 495my($repoauthor,$repocommitter); 496($repoauthor) = Git::ident_person(@repo,'author'); 497($repocommitter) = Git::ident_person(@repo,'committer'); 498 499sub parse_address_line { 500returnmap{$_->format} Mail::Address->parse($_[0]); 501} 502 503sub split_addrs { 504return quotewords('\s*,\s*',1,@_); 505} 506 507my%aliases; 508 509sub parse_sendmail_alias { 510local$_=shift; 511if(/"/) { 512printf STDERR __("warning: sendmail alias with quotes is not supported:%s\n"),$_; 513}elsif(/:include:/) { 514printf STDERR __("warning: `:include:` not supported:%s\n"),$_; 515}elsif(/[\/|]/) { 516printf STDERR __("warning: `/file` or `|pipe` redirection not supported:%s\n"),$_; 517}elsif(/^(\S+?)\s*:\s*(.+)$/) { 518my($alias,$addr) = ($1,$2); 519$aliases{$alias} = [ split_addrs($addr) ]; 520}else{ 521printf STDERR __("warning: sendmail line is not recognized:%s\n"),$_; 522} 523} 524 525sub parse_sendmail_aliases { 526my$fh=shift; 527my$s=''; 528while(<$fh>) { 529chomp; 530next if/^\s*$/||/^\s*#/; 531$s.=$_,next if$s=~s/\\$//||s/^\s+//; 532 parse_sendmail_alias($s)if$s; 533$s=$_; 534} 535$s=~s/\\$//;# silently tolerate stray '\' on last line 536 parse_sendmail_alias($s)if$s; 537} 538 539my%parse_alias= ( 540# multiline formats can be supported in the future 541 mutt =>sub{my$fh=shift;while(<$fh>) { 542if(/^\s*alias\s+(?:-group\s+\S+\s+)*(\S+)\s+(.*)$/) { 543my($alias,$addr) = ($1,$2); 544$addr=~s/#.*$//;# mutt allows # comments 545# commas delimit multiple addresses 546my@addr= split_addrs($addr); 547 548# quotes may be escaped in the file, 549# unescape them so we do not double-escape them later. 550s/\\"/"/gforeach@addr; 551$aliases{$alias} = \@addr 552}}}, 553 mailrc =>sub{my$fh=shift;while(<$fh>) { 554if(/^alias\s+(\S+)\s+(.*?)\s*$/) { 555# spaces delimit multiple addresses 556$aliases{$1} = [ quotewords('\s+',0,$2) ]; 557}}}, 558 pine =>sub{my$fh=shift;my$f='\t[^\t]*'; 559for(my$x='';defined($x);$x=$_) { 560chomp$x; 561$x.=$1while(defined($_= <$fh>) &&/^ +(.*)$/); 562$x=~/^(\S+)$f\t\(?([^\t]+?)\)?(:?$f){0,2}$/ornext; 563$aliases{$1} = [ split_addrs($2) ]; 564}}, 565 elm =>sub{my$fh=shift; 566while(<$fh>) { 567if(/^(\S+)\s+=\s+[^=]+=\s(\S+)/) { 568my($alias,$addr) = ($1,$2); 569$aliases{$alias} = [ split_addrs($addr) ]; 570} 571} }, 572 sendmail => \&parse_sendmail_aliases, 573 gnus =>sub{my$fh=shift;while(<$fh>) { 574if(/\(define-mail-alias\s+"(\S+?)"\s+"(\S+?)"\)/) { 575$aliases{$1} = [$2]; 576}}} 577); 578 579if(@alias_filesand$aliasfiletypeand defined$parse_alias{$aliasfiletype}) { 580foreachmy$file(@alias_files) { 581open my$fh,'<',$fileor die"opening$file:$!\n"; 582$parse_alias{$aliasfiletype}->($fh); 583close$fh; 584} 585} 586 587if($dump_aliases) { 588print"$_\n"for(sort keys%aliases); 589exit(0); 590} 591 592# is_format_patch_arg($f) returns 0 if $f names a patch, or 1 if 593# $f is a revision list specification to be passed to format-patch. 594sub is_format_patch_arg { 595return unless$repo; 596my$f=shift; 597try{ 598$repo->command('rev-parse','--verify','--quiet',$f); 599if(defined($format_patch)) { 600return$format_patch; 601} 602die sprintf(__ <<EOF,$f,$f); 603File '%s' exists but it could also be the range of commits 604to produce patches for. Please disambiguate by... 605 606 * Saying "./%s" if you mean a file; or 607 * Giving --format-patch option if you mean a range. 608EOF 609} catch Git::Error::Command with { 610# Not a valid revision. Treat it as a filename. 611return0; 612} 613} 614 615# Now that all the defaults are set, process the rest of the command line 616# arguments and collect up the files that need to be processed. 617my@rev_list_opts; 618while(defined(my$f=shift@ARGV)) { 619if($feq"--") { 620push@rev_list_opts,"--",@ARGV; 621@ARGV= (); 622}elsif(-d $fand!is_format_patch_arg($f)) { 623opendir my$dh,$f 624or die sprintf(__("Failed to opendir%s:%s"),$f,$!); 625 626push@files,grep{ -f $_}map{ catfile($f,$_) } 627sort readdir$dh; 628closedir$dh; 629}elsif((-f $for-p $f)and!is_format_patch_arg($f)) { 630push@files,$f; 631}else{ 632push@rev_list_opts,$f; 633} 634} 635 636if(@rev_list_opts) { 637die __("Cannot run git format-patch from outside a repository\n") 638unless$repo; 639push@files,$repo->command('format-patch','-o', tempdir(CLEANUP =>1),@rev_list_opts); 640} 641 642@files= handle_backup_files(@files); 643 644if($validate) { 645foreachmy$f(@files) { 646unless(-p $f) { 647my$error= validate_patch($f,$target_xfer_encoding); 648$errorand die sprintf(__("fatal:%s:%s\nwarning: no patches were sent\n"), 649$f,$error); 650} 651} 652} 653 654if(@files) { 655unless($quiet) { 656print$_,"\n"for(@files); 657} 658}else{ 659print STDERR __("\nNo patch files specified!\n\n"); 660 usage(); 661} 662 663sub get_patch_subject { 664my$fn=shift; 665open(my$fh,'<',$fn); 666while(my$line= <$fh>) { 667next unless($line=~/^Subject: (.*)$/); 668close$fh; 669return"GIT:$1\n"; 670} 671close$fh; 672die sprintf(__("No subject line in%s?"),$fn); 673} 674 675if($compose) { 676# Note that this does not need to be secure, but we will make a small 677# effort to have it be unique 678$compose_filename= ($repo? 679 tempfile(".gitsendemail.msg.XXXXXX", DIR =>$repo->repo_path()) : 680 tempfile(".gitsendemail.msg.XXXXXX", DIR =>"."))[1]; 681open my$c,">",$compose_filename 682or die sprintf(__("Failed to open for writing%s:%s"),$compose_filename,$!); 683 684 685my$tpl_sender=$sender||$repoauthor||$repocommitter||''; 686my$tpl_subject=$initial_subject||''; 687my$tpl_in_reply_to=$initial_in_reply_to||''; 688my$tpl_reply_to=$reply_to||''; 689 690print$c<<EOT1, Git::prefix_lines("GIT: ", __ <<EOT2), <<EOT3; 691From$tpl_sender# This line is ignored. 692EOT1 693Lines beginning in "GIT:" will be removed. 694Consider including an overall diffstat or table of contents 695for the patch you are writing. 696 697Clear the body content if you don't wish to send a summary. 698EOT2 699From:$tpl_sender 700Reply-To:$tpl_reply_to 701Subject:$tpl_subject 702In-Reply-To:$tpl_in_reply_to 703 704EOT3 705 for my$f(@files) { 706 print$cget_patch_subject($f); 707 } 708 close$c; 709 710 if ($annotate) { 711 do_edit($compose_filename,@files); 712 } else { 713 do_edit($compose_filename); 714 } 715 716 open$c, "<",$compose_filename 717 or die sprintf(__("Failed to open%s:%s"),$compose_filename,$!); 718 719 if (!defined$compose_encoding) { 720$compose_encoding= "UTF-8"; 721 } 722 723 my%parsed_email; 724 while (my$line= <$c>) { 725 next if$line=~ m/^GIT:/; 726 parse_header_line($line, \%parsed_email); 727 if ($line=~ /^$/) { 728$parsed_email{'body'} = filter_body($c); 729 } 730 } 731 close$c; 732 733 open my$c2, ">",$compose_filename. ".final" 734 or die sprintf(__("Failed to open%s.final:%s"),$compose_filename,$!); 735 736 737 if ($parsed_email{'From'}) { 738$sender= delete($parsed_email{'From'}); 739 } 740 if ($parsed_email{'In-Reply-To'}) { 741$initial_in_reply_to= delete($parsed_email{'In-Reply-To'}); 742 } 743 if ($parsed_email{'Reply-To'}) { 744$reply_to= delete($parsed_email{'Reply-To'}); 745 } 746 if ($parsed_email{'Subject'}) { 747$initial_subject= delete($parsed_email{'Subject'}); 748 print$c2"Subject: " . 749 quote_subject($initial_subject,$compose_encoding) . 750 "\n"; 751 } 752 753 if ($parsed_email{'MIME-Version'}) { 754 print$c2"MIME-Version:$parsed_email{'MIME-Version'}\n", 755 "Content-Type:$parsed_email{'Content-Type'};\n", 756 "Content-Transfer-Encoding:$parsed_email{'Content-Transfer-Encoding'}\n"; 757 delete($parsed_email{'MIME-Version'}); 758 delete($parsed_email{'Content-Type'}); 759 delete($parsed_email{'Content-Transfer-Encoding'}); 760 } elsif (file_has_nonascii($compose_filename)) { 761 my$content_type= (delete($parsed_email{'Content-Type'}) or 762 "text/plain; charset=$compose_encoding"); 763 print$c2"MIME-Version: 1.0\n", 764 "Content-Type:$content_type\n", 765 "Content-Transfer-Encoding: 8bit\n"; 766 } 767 # Preserve unknown headers 768 foreach my$key(keys%parsed_email) { 769 next if$keyeq 'body'; 770 print$c2"$key:$parsed_email{$key}"; 771 } 772 773 if ($parsed_email{'body'}) { 774 print$c2"\n$parsed_email{'body'}\n"; 775 delete($parsed_email{'body'}); 776 } else { 777 print __("Summary email is empty, skipping it\n"); 778$compose= -1; 779 } 780 781 close$c2; 782 783} elsif ($annotate) { 784 do_edit(@files); 785} 786 787sub ask { 788 my ($prompt,%arg) =@_; 789 my$valid_re=$arg{valid_re}; 790 my$default=$arg{default}; 791 my$confirm_only=$arg{confirm_only}; 792 my$resp; 793 my$i= 0; 794 return defined$default?$default: undef 795 unless defined$term->IN and defined fileno($term->IN) and 796 defined$term->OUT and defined fileno($term->OUT); 797 while ($i++< 10) { 798$resp=$term->readline($prompt); 799 if (!defined$resp) { # EOF 800 print "\n"; 801 return defined$default?$default: undef; 802 } 803 if ($respeq '' and defined$default) { 804 return$default; 805 } 806 if (!defined$valid_reor$resp=~ /$valid_re/) { 807 return$resp; 808 } 809 if ($confirm_only) { 810 my$yesno=$term->readline( 811 # TRANSLATORS: please keep [y/N] as is. 812 sprintf(__("Are you sure you want to use <%s> [y/N]? "),$resp)); 813 if (defined$yesno&&$yesno=~ /y/i) { 814 return$resp; 815 } 816 } 817 } 818 return; 819} 820 821sub parse_header_line { 822 my$lines= shift; 823 my$parsed_line= shift; 824 my$addr_pat= join "|", qw(To Cc Bcc); 825 826foreach(split(/\n/,$lines)) { 827if(/^($addr_pat):\s*(.+)$/i) { 828$parsed_line->{$1} = [ parse_address_line($2) ]; 829}elsif(/^([^:]*):\s*(.+)\s*$/i) { 830$parsed_line->{$1} =$2; 831} 832} 833} 834 835sub filter_body { 836my$c=shift; 837my$body=""; 838while(my$body_line= <$c>) { 839if($body_line!~m/^GIT:/) { 840$body.=$body_line; 841} 842} 843return$body; 844} 845 846 847my%broken_encoding; 848 849sub file_declares_8bit_cte { 850my$fn=shift; 851open(my$fh,'<',$fn); 852while(my$line= <$fh>) { 853last if($line=~/^$/); 854return1if($line=~/^Content-Transfer-Encoding: .*8bit.*$/); 855} 856close$fh; 857return0; 858} 859 860foreachmy$f(@files) { 861next unless(body_or_subject_has_nonascii($f) 862&& !file_declares_8bit_cte($f)); 863$broken_encoding{$f} =1; 864} 865 866if(!defined$auto_8bit_encoding&&scalar%broken_encoding) { 867print __("The following files are 8bit, but do not declare ". 868"a Content-Transfer-Encoding.\n"); 869foreachmy$f(sort keys%broken_encoding) { 870print"$f\n"; 871} 872$auto_8bit_encoding= ask(__("Which 8bit encoding should I declare [UTF-8]? "), 873 valid_re =>qr/.{4}/, confirm_only =>1, 874default=>"UTF-8"); 875} 876 877if(!$force) { 878formy$f(@files) { 879if(get_patch_subject($f) =~/\Q*** SUBJECT HERE ***\E/) { 880die sprintf(__("Refusing to send because the patch\n\t%s\n" 881."has the template subject '*** SUBJECT HERE ***'. " 882."Pass --force if you really want to send.\n"),$f); 883} 884} 885} 886 887if(defined$sender) { 888$sender=~s/^\s+|\s+$//g; 889($sender) = expand_aliases($sender); 890}else{ 891$sender=$repoauthor||$repocommitter||''; 892} 893 894# $sender could be an already sanitized address 895# (e.g. sendemail.from could be manually sanitized by user). 896# But it's a no-op to run sanitize_address on an already sanitized address. 897$sender= sanitize_address($sender); 898 899my$to_whom= __("To whom should the emails be sent (if anyone)?"); 900my$prompting=0; 901if(!@initial_to&& !defined$to_cmd) { 902my$to= ask("$to_whom", 903default=>"", 904 valid_re =>qr/\@.*\./, confirm_only =>1); 905push@initial_to, parse_address_line($to)ifdefined$to;# sanitized/validated later 906$prompting++; 907} 908 909sub expand_aliases { 910returnmap{ expand_one_alias($_) }@_; 911} 912 913my%EXPANDED_ALIASES; 914sub expand_one_alias { 915my$alias=shift; 916if($EXPANDED_ALIASES{$alias}) { 917die sprintf(__("fatal: alias '%s' expands to itself\n"),$alias); 918} 919local$EXPANDED_ALIASES{$alias} =1; 920return$aliases{$alias} ? expand_aliases(@{$aliases{$alias}}) :$alias; 921} 922 923@initial_to= process_address_list(@initial_to); 924@initial_cc= process_address_list(@initial_cc); 925@initial_bcc= process_address_list(@initial_bcc); 926 927if($thread&& !defined$initial_in_reply_to&&$prompting) { 928$initial_in_reply_to= ask( 929 __("Message-ID to be used as In-Reply-To for the first email (if any)? "), 930default=>"", 931 valid_re =>qr/\@.*\./, confirm_only =>1); 932} 933if(defined$initial_in_reply_to) { 934$initial_in_reply_to=~s/^\s*<?//; 935$initial_in_reply_to=~s/>?\s*$//; 936$initial_in_reply_to="<$initial_in_reply_to>"if$initial_in_reply_tone''; 937} 938 939if(defined$reply_to) { 940$reply_to=~s/^\s+|\s+$//g; 941($reply_to) = expand_aliases($reply_to); 942$reply_to= sanitize_address($reply_to); 943} 944 945if(!defined$smtp_server) { 946my@sendmail_paths=qw( /usr/sbin/sendmail /usr/lib/sendmail ); 947push@sendmail_paths,map{"$_/sendmail"}split/:/,$ENV{PATH}; 948foreach(@sendmail_paths) { 949if(-x $_) { 950$smtp_server=$_; 951last; 952} 953} 954$smtp_server||='localhost';# could be 127.0.0.1, too... *shrug* 955} 956 957if($compose&&$compose>0) { 958@files= ($compose_filename.".final",@files); 959} 960 961# Variables we set as part of the loop over files 962our($message_id,%mail,$subject,$in_reply_to,$references,$message, 963$needs_confirm,$message_num,$ask_default); 964 965sub extract_valid_address { 966my$address=shift; 967my$local_part_regexp=qr/[^<>"\s@]+/; 968my$domain_regexp=qr/[^.<>"\s@]+(?:\.[^.<>"\s@]+)+/; 969 970# check for a local address: 971return$addressif($address=~/^($local_part_regexp)$/); 972 973$address=~s/^\s*<(.*)>\s*$/$1/; 974if($have_email_valid) { 975returnscalar Email::Valid->address($address); 976} 977 978# less robust/correct than the monster regexp in Email::Valid, 979# but still does a 99% job, and one less dependency 980return$1if$address=~/($local_part_regexp\@$domain_regexp)/; 981return; 982} 983 984sub extract_valid_address_or_die { 985my$address=shift; 986$address= extract_valid_address($address); 987die sprintf(__("error: unable to extract a valid address from:%s\n"),$address) 988if!$address; 989return$address; 990} 991 992sub validate_address { 993my$address=shift; 994while(!extract_valid_address($address)) { 995printf STDERR __("error: unable to extract a valid address from:%s\n"),$address; 996# TRANSLATORS: Make sure to include [q] [d] [e] in your 997# translation. The program will only accept English input 998# at this point. 999$_= ask(__("What to do with this address? ([q]uit|[d]rop|[e]dit): "),1000 valid_re =>qr/^(?:quit|q|drop|d|edit|e)/i,1001default=>'q');1002if(/^d/i) {1003returnundef;1004}elsif(/^q/i) {1005 cleanup_compose_files();1006exit(0);1007}1008$address= ask("$to_whom",1009default=>"",1010 valid_re =>qr/\@.*\./, confirm_only =>1);1011}1012return$address;1013}10141015sub validate_address_list {1016return(grep{defined$_}1017map{ validate_address($_) }@_);1018}10191020# Usually don't need to change anything below here.10211022# we make a "fake" message id by taking the current number1023# of seconds since the beginning of Unix time and tacking on1024# a random number to the end, in case we are called quicker than1025# 1 second since the last time we were called.10261027# We'll setup a template for the message id, using the "from" address:10281029my($message_id_stamp,$message_id_serial);1030sub make_message_id {1031my$uniq;1032if(!defined$message_id_stamp) {1033$message_id_stamp= strftime("%Y%m%d%H%M%S.$$",gmtime(time));1034$message_id_serial=0;1035}1036$message_id_serial++;1037$uniq="$message_id_stamp-$message_id_serial";10381039my$du_part;1040for($sender,$repocommitter,$repoauthor) {1041$du_part= extract_valid_address(sanitize_address($_));1042last if(defined$du_partand$du_partne'');1043}1044if(not defined$du_partor$du_parteq'') {1045require Sys::Hostname;1046$du_part='user@'. Sys::Hostname::hostname();1047}1048my$message_id_template="<%s-%s>";1049$message_id=sprintf($message_id_template,$uniq,$du_part);1050#print "new message id = $message_id\n"; # Was useful for debugging1051}1052105310541055$time=time-scalar$#files;10561057sub unquote_rfc2047 {1058local($_) =@_;1059my$charset;1060my$sep=qr/[ \t]+/;1061 s{$re_encoded_word(?:$sep$re_encoded_word)*}{1062my@words=split$sep,$&;1063foreach(@words) {1064m/$re_encoded_word/;1065$charset=$1;1066my$encoding=$2;1067my$text=$3;1068if($encodingeq'q'||$encodingeq'Q') {1069$_=$text;1070s/_/ /g;1071s/=([0-9A-F]{2})/chr(hex($1))/egi;1072}else{1073# other encodings not supported yet1074}1075}1076join'',@words;1077}eg;1078returnwantarray? ($_,$charset) :$_;1079}10801081sub quote_rfc2047 {1082local$_=shift;1083my$encoding=shift||'UTF-8';1084s/([^-a-zA-Z0-9!*+\/])/sprintf("=%02X",ord($1))/eg;1085s/(.*)/=\?$encoding\?q\?$1\?=/;1086return$_;1087}10881089sub is_rfc2047_quoted {1090my$s=shift;1091length($s) <=75&&1092$s=~m/^(?:"[[:ascii:]]*"|$re_encoded_word)$/o;1093}10941095sub subject_needs_rfc2047_quoting {1096my$s=shift;10971098return($s=~/[^[:ascii:]]/) || ($s=~/=\?/);1099}11001101sub quote_subject {1102local$subject=shift;1103my$encoding=shift||'UTF-8';11041105if(subject_needs_rfc2047_quoting($subject)) {1106return quote_rfc2047($subject,$encoding);1107}1108return$subject;1109}11101111# use the simplest quoting being able to handle the recipient1112sub sanitize_address {1113my($recipient) =@_;11141115# remove garbage after email address1116$recipient=~s/(.*>).*$/$1/;11171118my($recipient_name,$recipient_addr) = ($recipient=~/^(.*?)\s*(<.*)/);11191120if(not$recipient_name) {1121return$recipient;1122}11231124# if recipient_name is already quoted, do nothing1125if(is_rfc2047_quoted($recipient_name)) {1126return$recipient;1127}11281129# remove non-escaped quotes1130$recipient_name=~s/(^|[^\\])"/$1/g;11311132# rfc2047 is needed if a non-ascii char is included1133if($recipient_name=~/[^[:ascii:]]/) {1134$recipient_name= quote_rfc2047($recipient_name);1135}11361137# double quotes are needed if specials or CTLs are included1138elsif($recipient_name=~/[][()<>@,;:\\".\000-\037\177]/) {1139$recipient_name=~s/([\\\r])/\\$1/g;1140$recipient_name=qq["$recipient_name"];1141}11421143return"$recipient_name$recipient_addr";11441145}11461147sub strip_garbage_one_address {1148my($addr) =@_;1149chomp$addr;1150if($addr=~/^(("[^"]*"|[^"<]*)? *<[^>]*>).*/) {1151# "Foo Bar" <foobar@example.com> [possibly garbage here]1152# Foo Bar <foobar@example.com> [possibly garbage here]1153return$1;1154}1155if($addr=~/^(<[^>]*>).*/) {1156# <foo@example.com> [possibly garbage here]1157# if garbage contains other addresses, they are ignored.1158return$1;1159}1160if($addr=~/^([^"#,\s]*)/) {1161# address without quoting: remove anything after the address1162return$1;1163}1164return$addr;1165}11661167sub sanitize_address_list {1168return(map{ sanitize_address($_) }@_);1169}11701171sub process_address_list {1172my@addr_list=map{ parse_address_line($_) }@_;1173@addr_list= expand_aliases(@addr_list);1174@addr_list= sanitize_address_list(@addr_list);1175@addr_list= validate_address_list(@addr_list);1176return@addr_list;1177}11781179# Returns the local Fully Qualified Domain Name (FQDN) if available.1180#1181# Tightly configured MTAa require that a caller sends a real DNS1182# domain name that corresponds the IP address in the HELO/EHLO1183# handshake. This is used to verify the connection and prevent1184# spammers from trying to hide their identity. If the DNS and IP don't1185# match, the receiveing MTA may deny the connection.1186#1187# Here is a deny example of Net::SMTP with the default "localhost.localdomain"1188#1189# Net::SMTP=GLOB(0x267ec28)>>> EHLO localhost.localdomain1190# Net::SMTP=GLOB(0x267ec28)<<< 550 EHLO argument does not match calling host1191#1192# This maildomain*() code is based on ideas in Perl library Test::Reporter1193# /usr/share/perl5/Test/Reporter/Mail/Util.pm ==> sub _maildomain ()11941195sub valid_fqdn {1196my$domain=shift;1197returndefined$domain&& !($^Oeq'darwin'&&$domain=~/\.local$/) &&$domain=~/\./;1198}11991200sub maildomain_net {1201my$maildomain;12021203my$domain= Net::Domain::domainname();1204$maildomain=$domainif valid_fqdn($domain);12051206return$maildomain;1207}12081209sub maildomain_mta {1210my$maildomain;12111212formy$host(qw(mailhost localhost)) {1213my$smtp= Net::SMTP->new($host);1214if(defined$smtp) {1215my$domain=$smtp->domain;1216$smtp->quit;12171218$maildomain=$domainif valid_fqdn($domain);12191220last if$maildomain;1221}1222}12231224return$maildomain;1225}12261227sub maildomain {1228return maildomain_net() || maildomain_mta() ||'localhost.localdomain';1229}12301231sub smtp_host_string {1232if(defined$smtp_server_port) {1233return"$smtp_server:$smtp_server_port";1234}else{1235return$smtp_server;1236}1237}12381239# Returns 1 if authentication succeeded or was not necessary1240# (smtp_user was not specified), and 0 otherwise.12411242sub smtp_auth_maybe {1243if(!defined$smtp_authuser||$auth) {1244return1;1245}12461247# Workaround AUTH PLAIN/LOGIN interaction defect1248# with Authen::SASL::Cyrus1249eval{1250require Authen::SASL;1251 Authen::SASL->import(qw(Perl));1252};12531254# Check mechanism naming as defined in:1255# https://tools.ietf.org/html/rfc4422#page-81256if($smtp_auth&&$smtp_auth!~/^(\b[A-Z0-9-_]{1,20}\s*)*$/) {1257die"invalid smtp auth: '${smtp_auth}'";1258}12591260# TODO: Authentication may fail not because credentials were1261# invalid but due to other reasons, in which we should not1262# reject credentials.1263$auth= Git::credential({1264'protocol'=>'smtp',1265'host'=> smtp_host_string(),1266'username'=>$smtp_authuser,1267# if there's no password, "git credential fill" will1268# give us one, otherwise it'll just pass this one.1269'password'=>$smtp_authpass1270},sub{1271my$cred=shift;12721273if($smtp_auth) {1274my$sasl= Authen::SASL->new(1275 mechanism =>$smtp_auth,1276 callback => {1277 user =>$cred->{'username'},1278 pass =>$cred->{'password'},1279 authname =>$cred->{'username'},1280}1281);12821283return!!$smtp->auth($sasl);1284}12851286return!!$smtp->auth($cred->{'username'},$cred->{'password'});1287});12881289return$auth;1290}12911292sub ssl_verify_params {1293eval{1294require IO::Socket::SSL;1295 IO::Socket::SSL->import(qw/SSL_VERIFY_PEER SSL_VERIFY_NONE/);1296};1297if($@) {1298print STDERR "Not using SSL_VERIFY_PEER due to out-of-date IO::Socket::SSL.\n";1299return;1300}13011302if(!defined$smtp_ssl_cert_path) {1303# use the OpenSSL defaults1304return(SSL_verify_mode => SSL_VERIFY_PEER());1305}13061307if($smtp_ssl_cert_patheq"") {1308return(SSL_verify_mode => SSL_VERIFY_NONE());1309}elsif(-d $smtp_ssl_cert_path) {1310return(SSL_verify_mode => SSL_VERIFY_PEER(),1311 SSL_ca_path =>$smtp_ssl_cert_path);1312}elsif(-f $smtp_ssl_cert_path) {1313return(SSL_verify_mode => SSL_VERIFY_PEER(),1314 SSL_ca_file =>$smtp_ssl_cert_path);1315}else{1316die sprintf(__("CA path\"%s\"does not exist"),$smtp_ssl_cert_path);1317}1318}13191320sub file_name_is_absolute {1321my($path) =@_;13221323# msys does not grok DOS drive-prefixes1324if($^Oeq'msys') {1325return($path=~ m#^/# || $path =~ m#^[a-zA-Z]\:#)1326}13271328require File::Spec::Functions;1329return File::Spec::Functions::file_name_is_absolute($path);1330}13311332# Prepares the email, then asks the user what to do.1333#1334# If the user chooses to send the email, it's sent and 1 is returned.1335# If the user chooses not to send the email, 0 is returned.1336# If the user decides they want to make further edits, -1 is returned and the1337# caller is expected to call send_message again after the edits are performed.1338#1339# If an error occurs sending the email, this just dies.13401341sub send_message {1342my@recipients= unique_email_list(@to);1343@cc= (grep{my$cc= extract_valid_address_or_die($_);1344not grep{$cceq$_||$_=~/<\Q${cc}\E>$/}@recipients1345}1346@cc);1347my$to=join(",\n\t",@recipients);1348@recipients= unique_email_list(@recipients,@cc,@initial_bcc);1349@recipients= (map{ extract_valid_address_or_die($_) }@recipients);1350my$date= format_2822_time($time++);1351my$gitversion='@@GIT_VERSION@@';1352if($gitversion=~m/..GIT_VERSION../) {1353$gitversion= Git::version();1354}13551356my$cc=join(",\n\t", unique_email_list(@cc));1357my$ccline="";1358if($ccne'') {1359$ccline="\nCc:$cc";1360}1361 make_message_id()unlessdefined($message_id);13621363my$header="From:$sender1364To:$to${ccline}1365Subject:$subject1366Date:$date1367Message-Id:$message_id1368";1369if($use_xmailer) {1370$header.="X-Mailer: git-send-email$gitversion\n";1371}1372if($in_reply_to) {13731374$header.="In-Reply-To:$in_reply_to\n";1375$header.="References:$references\n";1376}1377if($reply_to) {1378$header.="Reply-To:$reply_to\n";1379}1380if(@xh) {1381$header.=join("\n",@xh) ."\n";1382}13831384my@sendmail_parameters= ('-i',@recipients);1385my$raw_from=$sender;1386if(defined$envelope_sender&&$envelope_senderne"auto") {1387$raw_from=$envelope_sender;1388}1389$raw_from= extract_valid_address($raw_from);1390unshift(@sendmail_parameters,1391'-f',$raw_from)if(defined$envelope_sender);13921393if($needs_confirm&& !$dry_run) {1394print"\n$header\n";1395if($needs_confirmeq"inform") {1396$confirm_unconfigured=0;# squelch this message for the rest of this run1397$ask_default="y";# assume yes on EOF since user hasn't explicitly asked for confirmation1398print __ <<EOF ;1399 The Cc list above has been expanded by additional1400 addresses found in the patch commit message. By default1401 send-email prompts before sending whenever this occurs.1402 This behavior is controlled by the sendemail.confirm1403 configuration setting.14041405 For additional information, run 'git send-email --help'.1406 To retain the current behavior, but squelch this message,1407 run 'git config --global sendemail.confirm auto'.14081409EOF1410}1411# TRANSLATORS: Make sure to include [y] [n] [e] [q] [a] in your1412# translation. The program will only accept English input1413# at this point.1414$_= ask(__("Send this email? ([y]es|[n]o|[e]dit|[q]uit|[a]ll): "),1415 valid_re =>qr/^(?:yes|y|no|n|edit|e|quit|q|all|a)/i,1416default=>$ask_default);1417die __("Send this email reply required")unlessdefined$_;1418if(/^n/i) {1419return0;1420}elsif(/^e/i) {1421return-1;1422}elsif(/^q/i) {1423 cleanup_compose_files();1424exit(0);1425}elsif(/^a/i) {1426$confirm='never';1427}1428}14291430unshift(@sendmail_parameters,@smtp_server_options);14311432if($dry_run) {1433# We don't want to send the email.1434}elsif(file_name_is_absolute($smtp_server)) {1435my$pid=open my$sm,'|-';1436defined$pidor die$!;1437if(!$pid) {1438exec($smtp_server,@sendmail_parameters)or die$!;1439}1440print$sm"$header\n$message";1441close$smor die$!;1442}else{14431444if(!defined$smtp_server) {1445die __("The required SMTP server is not properly defined.")1446}14471448require Net::SMTP;1449my$use_net_smtp_ssl= version->parse($Net::SMTP::VERSION) < version->parse("2.34");1450$smtp_domain||= maildomain();14511452if($smtp_encryptioneq'ssl') {1453$smtp_server_port||=465;# ssmtp1454require IO::Socket::SSL;14551456# Suppress "variable accessed once" warning.1457{1458no warnings 'once';1459$IO::Socket::SSL::DEBUG =1;1460}14611462# Net::SMTP::SSL->new() does not forward any SSL options1463 IO::Socket::SSL::set_client_defaults(1464 ssl_verify_params());14651466if($use_net_smtp_ssl) {1467require Net::SMTP::SSL;1468$smtp||= Net::SMTP::SSL->new($smtp_server,1469 Hello =>$smtp_domain,1470 Port =>$smtp_server_port,1471 Debug =>$debug_net_smtp);1472}1473else{1474$smtp||= Net::SMTP->new($smtp_server,1475 Hello =>$smtp_domain,1476 Port =>$smtp_server_port,1477 Debug =>$debug_net_smtp,1478 SSL =>1);1479}1480}1481else{1482$smtp_server_port||=25;1483$smtp||= Net::SMTP->new($smtp_server,1484 Hello =>$smtp_domain,1485 Debug =>$debug_net_smtp,1486 Port =>$smtp_server_port);1487if($smtp_encryptioneq'tls'&&$smtp) {1488if($use_net_smtp_ssl) {1489$smtp->command('STARTTLS');1490$smtp->response();1491if($smtp->code!=220) {1492die sprintf(__("Server does not support STARTTLS!%s"),$smtp->message);1493}1494require Net::SMTP::SSL;1495$smtp= Net::SMTP::SSL->start_SSL($smtp,1496 ssl_verify_params())1497or die sprintf(__("STARTTLS failed!%s"), IO::Socket::SSL::errstr());1498}1499else{1500$smtp->starttls(ssl_verify_params())1501or die sprintf(__("STARTTLS failed!%s"), IO::Socket::SSL::errstr());1502}1503$smtp_encryption='';1504# Send EHLO again to receive fresh1505# supported commands1506$smtp->hello($smtp_domain);1507}1508}15091510if(!$smtp) {1511die __("Unable to initialize SMTP properly. Check config and use --smtp-debug."),1512" VALUES: server=$smtp_server",1513"encryption=$smtp_encryption",1514"hello=$smtp_domain",1515defined$smtp_server_port?" port=$smtp_server_port":"";1516}15171518 smtp_auth_maybe or die$smtp->message;15191520$smtp->mail($raw_from)or die$smtp->message;1521$smtp->to(@recipients)or die$smtp->message;1522$smtp->dataor die$smtp->message;1523$smtp->datasend("$header\n")or die$smtp->message;1524my@lines=split/^/,$message;1525foreachmy$line(@lines) {1526$smtp->datasend("$line")or die$smtp->message;1527}1528$smtp->dataend()or die$smtp->message;1529$smtp->code=~/250|200/or die sprintf(__("Failed to send%s\n"),$subject).$smtp->message;1530}1531if($quiet) {1532printf($dry_run? __("Dry-Sent%s\n") : __("Sent%s\n"),$subject);1533}else{1534print($dry_run? __("Dry-OK. Log says:\n") : __("OK. Log says:\n"));1535if(!file_name_is_absolute($smtp_server)) {1536print"Server:$smtp_server\n";1537print"MAIL FROM:<$raw_from>\n";1538foreachmy$entry(@recipients) {1539print"RCPT TO:<$entry>\n";1540}1541}else{1542print"Sendmail:$smtp_server".join(' ',@sendmail_parameters)."\n";1543}1544print$header,"\n";1545if($smtp) {1546print __("Result: "),$smtp->code,' ',1547($smtp->message=~/\n([^\n]+\n)$/s),"\n";1548}else{1549print __("Result: OK\n");1550}1551}15521553return1;1554}15551556$in_reply_to=$initial_in_reply_to;1557$references=$initial_in_reply_to||'';1558$subject=$initial_subject;1559$message_num=0;15601561# Prepares the email, prompts the user, sends it out1562# Returns 0 if an edit was done and the function should be called again, or 11563# otherwise.1564sub process_file {1565my($t) =@_;15661567open my$fh,"<",$tor die sprintf(__("can't open file%s"),$t);15681569my$author=undef;1570my$sauthor=undef;1571my$author_encoding;1572my$has_content_type;1573my$body_encoding;1574my$xfer_encoding;1575my$has_mime_version;1576@to= ();1577@cc= ();1578@xh= ();1579my$input_format=undef;1580my@header= ();1581$message="";1582$message_num++;1583# First unfold multiline header fields1584while(<$fh>) {1585last if/^\s*$/;1586if(/^\s+\S/and@header) {1587chomp($header[$#header]);1588s/^\s+/ /;1589$header[$#header] .=$_;1590}else{1591push(@header,$_);1592}1593}1594# Now parse the header1595foreach(@header) {1596if(/^From /) {1597$input_format='mbox';1598next;1599}1600chomp;1601if(!defined$input_format&&/^[-A-Za-z]+:\s/) {1602$input_format='mbox';1603}16041605if(defined$input_format&&$input_formateq'mbox') {1606if(/^Subject:\s+(.*)$/i) {1607$subject=$1;1608}1609elsif(/^From:\s+(.*)$/i) {1610($author,$author_encoding) = unquote_rfc2047($1);1611$sauthor= sanitize_address($author);1612next if$suppress_cc{'author'};1613next if$suppress_cc{'self'}and$sauthoreq$sender;1614printf(__("(mbox) Adding cc:%sfrom line '%s'\n"),1615$1,$_)unless$quiet;1616push@cc,$1;1617}1618elsif(/^To:\s+(.*)$/i) {1619foreachmy$addr(parse_address_line($1)) {1620printf(__("(mbox) Adding to:%sfrom line '%s'\n"),1621$addr,$_)unless$quiet;1622push@to,$addr;1623}1624}1625elsif(/^Cc:\s+(.*)$/i) {1626foreachmy$addr(parse_address_line($1)) {1627my$qaddr= unquote_rfc2047($addr);1628my$saddr= sanitize_address($qaddr);1629if($saddreq$sender) {1630next if($suppress_cc{'self'});1631}else{1632next if($suppress_cc{'cc'});1633}1634printf(__("(mbox) Adding cc:%sfrom line '%s'\n"),1635$addr,$_)unless$quiet;1636push@cc,$addr;1637}1638}1639elsif(/^Content-type:/i) {1640$has_content_type=1;1641if(/charset="?([^ "]+)/) {1642$body_encoding=$1;1643}1644push@xh,$_;1645}1646elsif(/^MIME-Version/i) {1647$has_mime_version=1;1648push@xh,$_;1649}1650elsif(/^Message-Id: (.*)/i) {1651$message_id=$1;1652}1653elsif(/^Content-Transfer-Encoding: (.*)/i) {1654$xfer_encoding=$1ifnot defined$xfer_encoding;1655}1656elsif(/^In-Reply-To: (.*)/i) {1657$in_reply_to=$1;1658}1659elsif(/^References: (.*)/i) {1660$references=$1;1661}1662elsif(!/^Date:\s/i&&/^[-A-Za-z]+:\s+\S/) {1663push@xh,$_;1664}1665}else{1666# In the traditional1667# "send lots of email" format,1668# line 1 = cc1669# line 2 = subject1670# So let's support that, too.1671$input_format='lots';1672if(@cc==0&& !$suppress_cc{'cc'}) {1673printf(__("(non-mbox) Adding cc:%sfrom line '%s'\n"),1674$_,$_)unless$quiet;1675push@cc,$_;1676}elsif(!defined$subject) {1677$subject=$_;1678}1679}1680}1681# Now parse the message body1682while(<$fh>) {1683$message.=$_;1684if(/^(Signed-off-by|Cc): (.*)/i) {1685chomp;1686my($what,$c) = ($1,$2);1687# strip garbage for the address we'll use:1688$c= strip_garbage_one_address($c);1689# sanitize a bit more to decide whether to suppress the address:1690my$sc= sanitize_address($c);1691if($sceq$sender) {1692next if($suppress_cc{'self'});1693}else{1694next if$suppress_cc{'sob'}and$what=~/Signed-off-by/i;1695next if$suppress_cc{'bodycc'}and$what=~/Cc/i;1696}1697push@cc,$c;1698printf(__("(body) Adding cc:%sfrom line '%s'\n"),1699$c,$_)unless$quiet;1700}1701}1702close$fh;17031704push@to, recipients_cmd("to-cmd","to",$to_cmd,$t)1705ifdefined$to_cmd;1706push@cc, recipients_cmd("cc-cmd","cc",$cc_cmd,$t)1707ifdefined$cc_cmd&& !$suppress_cc{'cccmd'};17081709if($broken_encoding{$t} && !$has_content_type) {1710$xfer_encoding='8bit'ifnot defined$xfer_encoding;1711$has_content_type=1;1712push@xh,"Content-Type: text/plain; charset=$auto_8bit_encoding";1713$body_encoding=$auto_8bit_encoding;1714}17151716if($broken_encoding{$t} && !is_rfc2047_quoted($subject)) {1717$subject= quote_subject($subject,$auto_8bit_encoding);1718}17191720if(defined$sauthorand$sauthorne$sender) {1721$message="From:$author\n\n$message";1722if(defined$author_encoding) {1723if($has_content_type) {1724if($body_encodingeq$author_encoding) {1725# ok, we already have the right encoding1726}1727else{1728# uh oh, we should re-encode1729}1730}1731else{1732$xfer_encoding='8bit'ifnot defined$xfer_encoding;1733$has_content_type=1;1734push@xh,1735"Content-Type: text/plain; charset=$author_encoding";1736}1737}1738}1739$xfer_encoding='8bit'ifnot defined$xfer_encoding;1740($message,$xfer_encoding) = apply_transfer_encoding(1741$message,$xfer_encoding,$target_xfer_encoding);1742push@xh,"Content-Transfer-Encoding:$xfer_encoding";1743unshift@xh,'MIME-Version: 1.0'unless$has_mime_version;17441745$needs_confirm= (1746$confirmeq"always"or1747($confirm=~/^(?:auto|cc)$/&&@cc)or1748($confirm=~/^(?:auto|compose)$/&&$compose&&$message_num==1));1749$needs_confirm="inform"if($needs_confirm&&$confirm_unconfigured&&@cc);17501751@to= process_address_list(@to);1752@cc= process_address_list(@cc);17531754@to= (@initial_to,@to);1755@cc= (@initial_cc,@cc);17561757if($message_num==1) {1758if(defined$cover_ccand$cover_cc) {1759@initial_cc=@cc;1760}1761if(defined$cover_toand$cover_to) {1762@initial_to=@to;1763}1764}17651766my$message_was_sent= send_message();1767if($message_was_sent== -1) {1768 do_edit($t);1769return0;1770}17711772# set up for the next message1773if($thread&&$message_was_sent&&1774($chain_reply_to|| !defined$in_reply_to||length($in_reply_to) ==0||1775$message_num==1)) {1776$in_reply_to=$message_id;1777if(length$references>0) {1778$references.="\n$message_id";1779}else{1780$references="$message_id";1781}1782}1783$message_id=undef;1784$num_sent++;1785if(defined$batch_size&&$num_sent==$batch_size) {1786$num_sent=0;1787$smtp->quitifdefined$smtp;1788undef$smtp;1789undef$auth;1790sleep($relogin_delay)ifdefined$relogin_delay;1791}17921793return1;1794}17951796foreachmy$t(@files) {1797while(!process_file($t)) {1798# user edited the file1799}1800}18011802# Execute a command (e.g. $to_cmd) to get a list of email addresses1803# and return a results array1804sub recipients_cmd {1805my($prefix,$what,$cmd,$file) =@_;18061807my@addresses= ();1808open my$fh,"-|","$cmd\Q$file\E"1809or die sprintf(__("(%s) Could not execute '%s'"),$prefix,$cmd);1810while(my$address= <$fh>) {1811$address=~s/^\s*//g;1812$address=~s/\s*$//g;1813$address= sanitize_address($address);1814next if($addresseq$senderand$suppress_cc{'self'});1815push@addresses,$address;1816printf(__("(%s) Adding%s:%sfrom: '%s'\n"),1817$prefix,$what,$address,$cmd)unless$quiet;1818}1819close$fh1820or die sprintf(__("(%s) failed to close pipe to '%s'"),$prefix,$cmd);1821return@addresses;1822}18231824cleanup_compose_files();18251826sub cleanup_compose_files {1827unlink($compose_filename,$compose_filename.".final")if$compose;1828}18291830$smtp->quitif$smtp;18311832sub apply_transfer_encoding {1833my$message=shift;1834my$from=shift;1835my$to=shift;18361837return$messageif($fromeq$toand$fromne'7bit');18381839require MIME::QuotedPrint;1840require MIME::Base64;18411842$message= MIME::QuotedPrint::decode($message)1843if($fromeq'quoted-printable');1844$message= MIME::Base64::decode($message)1845if($fromeq'base64');18461847$to= ($message=~/.{999,}/) ?'quoted-printable':'8bit'1848if$toeq'auto';18491850die __("cannot send message as 7bit")1851if($toeq'7bit'and$message=~/[^[:ascii:]]/);1852return($message,$to)1853if($toeq'7bit'or$toeq'8bit');1854return(MIME::QuotedPrint::encode($message,"\n",0),$to)1855if($toeq'quoted-printable');1856return(MIME::Base64::encode($message,"\n"),$to)1857if($toeq'base64');1858die __("invalid transfer encoding");1859}18601861sub unique_email_list {1862my%seen;1863my@emails;18641865foreachmy$entry(@_) {1866my$clean= extract_valid_address_or_die($entry);1867$seen{$clean} ||=0;1868next if$seen{$clean}++;1869push@emails,$entry;1870}1871return@emails;1872}18731874sub validate_patch {1875my($fn,$xfer_encoding) =@_;18761877if($repo) {1878my$validate_hook= catfile(catdir($repo->repo_path(),'hooks'),1879'sendemail-validate');1880my$hook_error;1881if(-x $validate_hook) {1882my$target= abs_path($fn);1883# The hook needs a correct cwd and GIT_DIR.1884my$cwd_save= cwd();1885chdir($repo->wc_path()or$repo->repo_path())1886or die("chdir:$!");1887local$ENV{"GIT_DIR"} =$repo->repo_path();1888$hook_error="rejected by sendemail-validate hook"1889ifsystem($validate_hook,$target);1890chdir($cwd_save)or die("chdir:$!");1891}1892return$hook_errorif$hook_error;1893}18941895# Any long lines will be automatically fixed if we use a suitable transfer1896# encoding.1897unless($xfer_encoding=~/^(?:auto|quoted-printable|base64)$/) {1898open(my$fh,'<',$fn)1899or die sprintf(__("unable to open%s:%s\n"),$fn,$!);1900while(my$line= <$fh>) {1901if(length($line) >998) {1902returnsprintf(__("%s: patch contains a line longer than 998 characters"), $.);1903}1904}1905}1906return;1907}19081909sub handle_backup {1910my($last,$lastlen,$file,$known_suffix) =@_;1911my($suffix,$skip);19121913$skip=0;1914if(defined$last&&1915($lastlen<length($file)) &&1916(substr($file,0,$lastlen)eq$last) &&1917($suffix=substr($file,$lastlen)) !~/^[a-z0-9]/i) {1918if(defined$known_suffix&&$suffixeq$known_suffix) {1919printf(__("Skipping%swith backup suffix '%s'.\n"),$file,$known_suffix);1920$skip=1;1921}else{1922# TRANSLATORS: please keep "[y|N]" as is.1923my$answer= ask(sprintf(__("Do you really want to send%s?[y|N]: "),$file),1924 valid_re =>qr/^(?:y|n)/i,1925default=>'n');1926$skip= ($answerne'y');1927if($skip) {1928$known_suffix=$suffix;1929}1930}1931}1932return($skip,$known_suffix);1933}19341935sub handle_backup_files {1936my@file=@_;1937my($last,$lastlen,$known_suffix,$skip,@result);1938formy$file(@file) {1939($skip,$known_suffix) = handle_backup($last,$lastlen,1940$file,$known_suffix);1941push@result,$fileunless$skip;1942$last=$file;1943$lastlen=length($file);1944}1945return@result;1946}19471948sub file_has_nonascii {1949my$fn=shift;1950open(my$fh,'<',$fn)1951or die sprintf(__("unable to open%s:%s\n"),$fn,$!);1952while(my$line= <$fh>) {1953return1if$line=~/[^[:ascii:]]/;1954}1955return0;1956}19571958sub body_or_subject_has_nonascii {1959my$fn=shift;1960open(my$fh,'<',$fn)1961or die sprintf(__("unable to open%s:%s\n"),$fn,$!);1962while(my$line= <$fh>) {1963last if$line=~/^$/;1964return1if$line=~/^Subject.*[^[:ascii:]]/;1965}1966while(my$line= <$fh>) {1967return1if$line=~/[^[:ascii:]]/;1968}1969return0;1970}