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,@cc,@xh,$envelope_sender, 173$initial_in_reply_to,$reply_to,$initial_subject,@files, 174$author,$sender,$smtp_authpass,$annotate,$compose,$time); 175# Things we either get from config, *or* are overridden on the 176# command-line. 177my($no_cc,$no_to,$no_bcc,$no_identity); 178my(@config_to,@getopt_to); 179my(@config_cc,@getopt_cc); 180my(@config_bcc,@getopt_bcc); 181 182# Example reply to: 183#$initial_in_reply_to = ''; #<20050203173208.GA23964@foobar.com>'; 184 185my$repo=eval{ Git->repository() }; 186my@repo=$repo? ($repo) : (); 187my$term=eval{ 188$ENV{"GIT_SEND_EMAIL_NOTTY"} 189? new Term::ReadLine 'git-send-email', \*STDIN, \*STDOUT 190: new Term::ReadLine 'git-send-email'; 191}; 192if($@) { 193$term= new FakeTerm "$@: going non-interactive"; 194} 195 196# Behavior modification variables 197my($quiet,$dry_run) = (0,0); 198my$format_patch; 199my$compose_filename; 200my$force=0; 201my$dump_aliases=0; 202 203# Handle interactive edition of files. 204my$multiedit; 205my$editor; 206 207sub do_edit { 208if(!defined($editor)) { 209$editor= Git::command_oneline('var','GIT_EDITOR'); 210} 211if(defined($multiedit) && !$multiedit) { 212map{ 213system('sh','-c',$editor.' "$@"',$editor,$_); 214if(($?&127) || ($?>>8)) { 215die(__("the editor exited uncleanly, aborting everything")); 216} 217}@_; 218}else{ 219system('sh','-c',$editor.' "$@"',$editor,@_); 220if(($?&127) || ($?>>8)) { 221die(__("the editor exited uncleanly, aborting everything")); 222} 223} 224} 225 226# Variables with corresponding config settings 227my($suppress_from,$signed_off_by_cc); 228my($cover_cc,$cover_to); 229my($to_cmd,$cc_cmd); 230my($smtp_server,$smtp_server_port,@smtp_server_options); 231my($smtp_authuser,$smtp_encryption,$smtp_ssl_cert_path); 232my($batch_size,$relogin_delay); 233my($identity,$aliasfiletype,@alias_files,$smtp_domain,$smtp_auth); 234my($confirm); 235my(@suppress_cc); 236my($auto_8bit_encoding); 237my($compose_encoding); 238# Variables with corresponding config settings & hardcoded defaults 239my($debug_net_smtp) =0;# Net::SMTP, see send_message() 240my$thread=1; 241my$chain_reply_to=0; 242my$use_xmailer=1; 243my$validate=1; 244my$target_xfer_encoding='auto'; 245 246my%config_bool_settings= ( 247"thread"=> \$thread, 248"chainreplyto"=> \$chain_reply_to, 249"suppressfrom"=> \$suppress_from, 250"signedoffbycc"=> \$signed_off_by_cc, 251"cccover"=> \$cover_cc, 252"tocover"=> \$cover_to, 253"signedoffcc"=> \$signed_off_by_cc, 254"validate"=> \$validate, 255"multiedit"=> \$multiedit, 256"annotate"=> \$annotate, 257"xmailer"=> \$use_xmailer, 258); 259 260my%config_settings= ( 261"smtpserver"=> \$smtp_server, 262"smtpserverport"=> \$smtp_server_port, 263"smtpserveroption"=> \@smtp_server_options, 264"smtpuser"=> \$smtp_authuser, 265"smtppass"=> \$smtp_authpass, 266"smtpdomain"=> \$smtp_domain, 267"smtpauth"=> \$smtp_auth, 268"smtpbatchsize"=> \$batch_size, 269"smtprelogindelay"=> \$relogin_delay, 270"to"=> \@config_to, 271"tocmd"=> \$to_cmd, 272"cc"=> \@config_cc, 273"cccmd"=> \$cc_cmd, 274"aliasfiletype"=> \$aliasfiletype, 275"bcc"=> \@config_bcc, 276"suppresscc"=> \@suppress_cc, 277"envelopesender"=> \$envelope_sender, 278"confirm"=> \$confirm, 279"from"=> \$sender, 280"assume8bitencoding"=> \$auto_8bit_encoding, 281"composeencoding"=> \$compose_encoding, 282"transferencoding"=> \$target_xfer_encoding, 283); 284 285my%config_path_settings= ( 286"aliasesfile"=> \@alias_files, 287"smtpsslcertpath"=> \$smtp_ssl_cert_path, 288); 289 290# Handle Uncouth Termination 291sub signal_handler { 292 293# Make text normal 294print color("reset"),"\n"; 295 296# SMTP password masked 297system"stty echo"; 298 299# tmp files from --compose 300if(defined$compose_filename) { 301if(-e $compose_filename) { 302printf __("'%s' contains an intermediate version ". 303"of the email you were composing.\n"), 304$compose_filename; 305} 306if(-e ($compose_filename.".final")) { 307printf __("'%s.final' contains the composed email.\n"), 308$compose_filename; 309} 310} 311 312exit; 313}; 314 315$SIG{TERM} = \&signal_handler; 316$SIG{INT} = \&signal_handler; 317 318# Read our sendemail.* config 319sub read_config { 320my($configured,$prefix) =@_; 321 322foreachmy$setting(keys%config_bool_settings) { 323my$target=$config_bool_settings{$setting}; 324my$v= Git::config_bool(@repo,"$prefix.$setting"); 325next unlessdefined$v; 326next if$configured->{$setting}++; 327$$target=$v; 328} 329 330foreachmy$setting(keys%config_path_settings) { 331my$target=$config_path_settings{$setting}; 332if(ref($target)eq"ARRAY") { 333my@values= Git::config_path(@repo,"$prefix.$setting"); 334next unless@values; 335next if$configured->{$setting}++; 336@$target=@values; 337} 338else{ 339my$v= Git::config_path(@repo,"$prefix.$setting"); 340next unlessdefined$v; 341next if$configured->{$setting}++; 342$$target=$v; 343} 344} 345 346foreachmy$setting(keys%config_settings) { 347my$target=$config_settings{$setting}; 348if(ref($target)eq"ARRAY") { 349my@values= Git::config(@repo,"$prefix.$setting"); 350next unless@values; 351next if$configured->{$setting}++; 352@$target=@values; 353} 354else{ 355my$v= Git::config(@repo,"$prefix.$setting"); 356next unlessdefined$v; 357next if$configured->{$setting}++; 358$$target=$v; 359} 360} 361 362if(!defined$smtp_encryption) { 363my$setting="$prefix.smtpencryption"; 364my$enc= Git::config(@repo,$setting); 365return unlessdefined$enc; 366return if$configured->{$setting}++; 367if(defined$enc) { 368$smtp_encryption=$enc; 369}elsif(Git::config_bool(@repo,"$prefix.smtpssl")) { 370$smtp_encryption='ssl'; 371} 372} 373} 374 375# sendemail.identity yields to --identity. We must parse this 376# special-case first before the rest of the config is read. 377$identity= Git::config(@repo,"sendemail.identity"); 378my$rc= GetOptions( 379"identity=s"=> \$identity, 380"no-identity"=> \$no_identity, 381); 382usage()unless$rc; 383undef$identityif$no_identity; 384 385# Now we know enough to read the config 386{ 387my%configured; 388 read_config(\%configured,"sendemail.$identity")ifdefined$identity; 389 read_config(\%configured,"sendemail"); 390} 391 392# Begin by accumulating all the variables (defined above), that we will end up 393# needing, first, from the command line: 394 395my$help; 396$rc= GetOptions("h"=> \$help, 397"dump-aliases"=> \$dump_aliases); 398usage()unless$rc; 399die __("--dump-aliases incompatible with other options\n") 400if!$helpand$dump_aliasesand@ARGV; 401$rc= GetOptions( 402"sender|from=s"=> \$sender, 403"in-reply-to=s"=> \$initial_in_reply_to, 404"reply-to=s"=> \$reply_to, 405"subject=s"=> \$initial_subject, 406"to=s"=> \@getopt_to, 407"to-cmd=s"=> \$to_cmd, 408"no-to"=> \$no_to, 409"cc=s"=> \@getopt_cc, 410"no-cc"=> \$no_cc, 411"bcc=s"=> \@getopt_bcc, 412"no-bcc"=> \$no_bcc, 413"chain-reply-to!"=> \$chain_reply_to, 414"no-chain-reply-to"=>sub{$chain_reply_to=0}, 415"smtp-server=s"=> \$smtp_server, 416"smtp-server-option=s"=> \@smtp_server_options, 417"smtp-server-port=s"=> \$smtp_server_port, 418"smtp-user=s"=> \$smtp_authuser, 419"smtp-pass:s"=> \$smtp_authpass, 420"smtp-ssl"=>sub{$smtp_encryption='ssl'}, 421"smtp-encryption=s"=> \$smtp_encryption, 422"smtp-ssl-cert-path=s"=> \$smtp_ssl_cert_path, 423"smtp-debug:i"=> \$debug_net_smtp, 424"smtp-domain:s"=> \$smtp_domain, 425"smtp-auth=s"=> \$smtp_auth, 426"annotate!"=> \$annotate, 427"no-annotate"=>sub{$annotate=0}, 428"compose"=> \$compose, 429"quiet"=> \$quiet, 430"cc-cmd=s"=> \$cc_cmd, 431"suppress-from!"=> \$suppress_from, 432"no-suppress-from"=>sub{$suppress_from=0}, 433"suppress-cc=s"=> \@suppress_cc, 434"signed-off-cc|signed-off-by-cc!"=> \$signed_off_by_cc, 435"no-signed-off-cc|no-signed-off-by-cc"=>sub{$signed_off_by_cc=0}, 436"cc-cover|cc-cover!"=> \$cover_cc, 437"no-cc-cover"=>sub{$cover_cc=0}, 438"to-cover|to-cover!"=> \$cover_to, 439"no-to-cover"=>sub{$cover_to=0}, 440"confirm=s"=> \$confirm, 441"dry-run"=> \$dry_run, 442"envelope-sender=s"=> \$envelope_sender, 443"thread!"=> \$thread, 444"no-thread"=>sub{$thread=0}, 445"validate!"=> \$validate, 446"no-validate"=>sub{$validate=0}, 447"transfer-encoding=s"=> \$target_xfer_encoding, 448"format-patch!"=> \$format_patch, 449"no-format-patch"=>sub{$format_patch=0}, 450"8bit-encoding=s"=> \$auto_8bit_encoding, 451"compose-encoding=s"=> \$compose_encoding, 452"force"=> \$force, 453"xmailer!"=> \$use_xmailer, 454"no-xmailer"=>sub{$use_xmailer=0}, 455"batch-size=i"=> \$batch_size, 456"relogin-delay=i"=> \$relogin_delay, 457); 458 459# Munge any "either config or getopt, not both" variables 460my@initial_to=@getopt_to?@getopt_to: ($no_to? () :@config_to); 461my@initial_cc=@getopt_cc?@getopt_cc: ($no_cc? () :@config_cc); 462my@initial_bcc=@getopt_bcc?@getopt_bcc: ($no_bcc? () :@config_bcc); 463 464usage()if$help; 465unless($rc) { 466 usage(); 467} 468 469die __("Cannot run git format-patch from outside a repository\n") 470if$format_patchand not$repo; 471 472die __("`batch-size` and `relogin` must be specified together ". 473"(via command-line or configuration option)\n") 474ifdefined$relogin_delayand not defined$batch_size; 475 476# 'default' encryption is none -- this only prevents a warning 477$smtp_encryption=''unless(defined$smtp_encryption); 478 479# Set CC suppressions 480my(%suppress_cc); 481if(@suppress_cc) { 482foreachmy$entry(@suppress_cc) { 483die sprintf(__("Unknown --suppress-cc field: '%s'\n"),$entry) 484unless$entry=~/^(?:all|cccmd|cc|author|self|sob|body|bodycc)$/; 485$suppress_cc{$entry} =1; 486} 487} 488 489if($suppress_cc{'all'}) { 490foreachmy$entry(qw (cccmd cc author self sob body bodycc)) { 491$suppress_cc{$entry} =1; 492} 493delete$suppress_cc{'all'}; 494} 495 496# If explicit old-style ones are specified, they trump --suppress-cc. 497$suppress_cc{'self'} =$suppress_fromifdefined$suppress_from; 498$suppress_cc{'sob'} = !$signed_off_by_ccifdefined$signed_off_by_cc; 499 500if($suppress_cc{'body'}) { 501foreachmy$entry(qw (sob bodycc)) { 502$suppress_cc{$entry} =1; 503} 504delete$suppress_cc{'body'}; 505} 506 507# Set confirm's default value 508my$confirm_unconfigured= !defined$confirm; 509if($confirm_unconfigured) { 510$confirm=scalar%suppress_cc?'compose':'auto'; 511}; 512die sprintf(__("Unknown --confirm setting: '%s'\n"),$confirm) 513unless$confirm=~/^(?:auto|cc|compose|always|never)/; 514 515# Debugging, print out the suppressions. 516if(0) { 517print"suppressions:\n"; 518foreachmy$entry(keys%suppress_cc) { 519printf" %-5s ->$suppress_cc{$entry}\n",$entry; 520} 521} 522 523my($repoauthor,$repocommitter); 524($repoauthor) = Git::ident_person(@repo,'author'); 525($repocommitter) = Git::ident_person(@repo,'committer'); 526 527sub parse_address_line { 528returnmap{$_->format} Mail::Address->parse($_[0]); 529} 530 531sub split_addrs { 532return quotewords('\s*,\s*',1,@_); 533} 534 535my%aliases; 536 537sub parse_sendmail_alias { 538local$_=shift; 539if(/"/) { 540printf STDERR __("warning: sendmail alias with quotes is not supported:%s\n"),$_; 541}elsif(/:include:/) { 542printf STDERR __("warning: `:include:` not supported:%s\n"),$_; 543}elsif(/[\/|]/) { 544printf STDERR __("warning: `/file` or `|pipe` redirection not supported:%s\n"),$_; 545}elsif(/^(\S+?)\s*:\s*(.+)$/) { 546my($alias,$addr) = ($1,$2); 547$aliases{$alias} = [ split_addrs($addr) ]; 548}else{ 549printf STDERR __("warning: sendmail line is not recognized:%s\n"),$_; 550} 551} 552 553sub parse_sendmail_aliases { 554my$fh=shift; 555my$s=''; 556while(<$fh>) { 557chomp; 558next if/^\s*$/||/^\s*#/; 559$s.=$_,next if$s=~s/\\$//||s/^\s+//; 560 parse_sendmail_alias($s)if$s; 561$s=$_; 562} 563$s=~s/\\$//;# silently tolerate stray '\' on last line 564 parse_sendmail_alias($s)if$s; 565} 566 567my%parse_alias= ( 568# multiline formats can be supported in the future 569 mutt =>sub{my$fh=shift;while(<$fh>) { 570if(/^\s*alias\s+(?:-group\s+\S+\s+)*(\S+)\s+(.*)$/) { 571my($alias,$addr) = ($1,$2); 572$addr=~s/#.*$//;# mutt allows # comments 573# commas delimit multiple addresses 574my@addr= split_addrs($addr); 575 576# quotes may be escaped in the file, 577# unescape them so we do not double-escape them later. 578s/\\"/"/gforeach@addr; 579$aliases{$alias} = \@addr 580}}}, 581 mailrc =>sub{my$fh=shift;while(<$fh>) { 582if(/^alias\s+(\S+)\s+(.*?)\s*$/) { 583# spaces delimit multiple addresses 584$aliases{$1} = [ quotewords('\s+',0,$2) ]; 585}}}, 586 pine =>sub{my$fh=shift;my$f='\t[^\t]*'; 587for(my$x='';defined($x);$x=$_) { 588chomp$x; 589$x.=$1while(defined($_= <$fh>) &&/^ +(.*)$/); 590$x=~/^(\S+)$f\t\(?([^\t]+?)\)?(:?$f){0,2}$/ornext; 591$aliases{$1} = [ split_addrs($2) ]; 592}}, 593 elm =>sub{my$fh=shift; 594while(<$fh>) { 595if(/^(\S+)\s+=\s+[^=]+=\s(\S+)/) { 596my($alias,$addr) = ($1,$2); 597$aliases{$alias} = [ split_addrs($addr) ]; 598} 599} }, 600 sendmail => \&parse_sendmail_aliases, 601 gnus =>sub{my$fh=shift;while(<$fh>) { 602if(/\(define-mail-alias\s+"(\S+?)"\s+"(\S+?)"\)/) { 603$aliases{$1} = [$2]; 604}}} 605); 606 607if(@alias_filesand$aliasfiletypeand defined$parse_alias{$aliasfiletype}) { 608foreachmy$file(@alias_files) { 609open my$fh,'<',$fileor die"opening$file:$!\n"; 610$parse_alias{$aliasfiletype}->($fh); 611close$fh; 612} 613} 614 615if($dump_aliases) { 616print"$_\n"for(sort keys%aliases); 617exit(0); 618} 619 620# is_format_patch_arg($f) returns 0 if $f names a patch, or 1 if 621# $f is a revision list specification to be passed to format-patch. 622sub is_format_patch_arg { 623return unless$repo; 624my$f=shift; 625try{ 626$repo->command('rev-parse','--verify','--quiet',$f); 627if(defined($format_patch)) { 628return$format_patch; 629} 630die sprintf(__ <<EOF,$f,$f); 631File '%s' exists but it could also be the range of commits 632to produce patches for. Please disambiguate by... 633 634 * Saying "./%s" if you mean a file; or 635 * Giving --format-patch option if you mean a range. 636EOF 637} catch Git::Error::Command with { 638# Not a valid revision. Treat it as a filename. 639return0; 640} 641} 642 643# Now that all the defaults are set, process the rest of the command line 644# arguments and collect up the files that need to be processed. 645my@rev_list_opts; 646while(defined(my$f=shift@ARGV)) { 647if($feq"--") { 648push@rev_list_opts,"--",@ARGV; 649@ARGV= (); 650}elsif(-d $fand!is_format_patch_arg($f)) { 651opendir my$dh,$f 652or die sprintf(__("Failed to opendir%s:%s"),$f,$!); 653 654push@files,grep{ -f $_}map{ catfile($f,$_) } 655sort readdir$dh; 656closedir$dh; 657}elsif((-f $for-p $f)and!is_format_patch_arg($f)) { 658push@files,$f; 659}else{ 660push@rev_list_opts,$f; 661} 662} 663 664if(@rev_list_opts) { 665die __("Cannot run git format-patch from outside a repository\n") 666unless$repo; 667push@files,$repo->command('format-patch','-o', tempdir(CLEANUP =>1),@rev_list_opts); 668} 669 670@files= handle_backup_files(@files); 671 672if($validate) { 673foreachmy$f(@files) { 674unless(-p $f) { 675my$error= validate_patch($f,$target_xfer_encoding); 676$errorand die sprintf(__("fatal:%s:%s\nwarning: no patches were sent\n"), 677$f,$error); 678} 679} 680} 681 682if(@files) { 683unless($quiet) { 684print$_,"\n"for(@files); 685} 686}else{ 687print STDERR __("\nNo patch files specified!\n\n"); 688 usage(); 689} 690 691sub get_patch_subject { 692my$fn=shift; 693open(my$fh,'<',$fn); 694while(my$line= <$fh>) { 695next unless($line=~/^Subject: (.*)$/); 696close$fh; 697return"GIT:$1\n"; 698} 699close$fh; 700die sprintf(__("No subject line in%s?"),$fn); 701} 702 703if($compose) { 704# Note that this does not need to be secure, but we will make a small 705# effort to have it be unique 706$compose_filename= ($repo? 707 tempfile(".gitsendemail.msg.XXXXXX", DIR =>$repo->repo_path()) : 708 tempfile(".gitsendemail.msg.XXXXXX", DIR =>"."))[1]; 709open my$c,">",$compose_filename 710or die sprintf(__("Failed to open for writing%s:%s"),$compose_filename,$!); 711 712 713my$tpl_sender=$sender||$repoauthor||$repocommitter||''; 714my$tpl_subject=$initial_subject||''; 715my$tpl_in_reply_to=$initial_in_reply_to||''; 716my$tpl_reply_to=$reply_to||''; 717 718print$c<<EOT1, Git::prefix_lines("GIT: ", __ <<EOT2), <<EOT3; 719From$tpl_sender# This line is ignored. 720EOT1 721Lines beginning in "GIT:" will be removed. 722Consider including an overall diffstat or table of contents 723for the patch you are writing. 724 725Clear the body content if you don't wish to send a summary. 726EOT2 727From:$tpl_sender 728Reply-To:$tpl_reply_to 729Subject:$tpl_subject 730In-Reply-To:$tpl_in_reply_to 731 732EOT3 733 for my$f(@files) { 734 print$cget_patch_subject($f); 735 } 736 close$c; 737 738 if ($annotate) { 739 do_edit($compose_filename,@files); 740 } else { 741 do_edit($compose_filename); 742 } 743 744 open$c, "<",$compose_filename 745 or die sprintf(__("Failed to open%s:%s"),$compose_filename,$!); 746 747 if (!defined$compose_encoding) { 748$compose_encoding= "UTF-8"; 749 } 750 751 my%parsed_email; 752 while (my$line= <$c>) { 753 next if$line=~ m/^GIT:/; 754 parse_header_line($line, \%parsed_email); 755 if ($line=~ /^$/) { 756$parsed_email{'body'} = filter_body($c); 757 } 758 } 759 close$c; 760 761 open my$c2, ">",$compose_filename. ".final" 762 or die sprintf(__("Failed to open%s.final:%s"),$compose_filename,$!); 763 764 765 if ($parsed_email{'From'}) { 766$sender= delete($parsed_email{'From'}); 767 } 768 if ($parsed_email{'In-Reply-To'}) { 769$initial_in_reply_to= delete($parsed_email{'In-Reply-To'}); 770 } 771 if ($parsed_email{'Reply-To'}) { 772$reply_to= delete($parsed_email{'Reply-To'}); 773 } 774 if ($parsed_email{'Subject'}) { 775$initial_subject= delete($parsed_email{'Subject'}); 776 print$c2"Subject: " . 777 quote_subject($initial_subject,$compose_encoding) . 778 "\n"; 779 } 780 781 if ($parsed_email{'MIME-Version'}) { 782 print$c2"MIME-Version:$parsed_email{'MIME-Version'}\n", 783 "Content-Type:$parsed_email{'Content-Type'};\n", 784 "Content-Transfer-Encoding:$parsed_email{'Content-Transfer-Encoding'}\n"; 785 delete($parsed_email{'MIME-Version'}); 786 delete($parsed_email{'Content-Type'}); 787 delete($parsed_email{'Content-Transfer-Encoding'}); 788 } elsif (file_has_nonascii($compose_filename)) { 789 my$content_type= (delete($parsed_email{'Content-Type'}) or 790 "text/plain; charset=$compose_encoding"); 791 print$c2"MIME-Version: 1.0\n", 792 "Content-Type:$content_type\n", 793 "Content-Transfer-Encoding: 8bit\n"; 794 } 795 # Preserve unknown headers 796 foreach my$key(keys%parsed_email) { 797 next if$keyeq 'body'; 798 print$c2"$key:$parsed_email{$key}"; 799 } 800 801 if ($parsed_email{'body'}) { 802 print$c2"\n$parsed_email{'body'}\n"; 803 delete($parsed_email{'body'}); 804 } else { 805 print __("Summary email is empty, skipping it\n"); 806$compose= -1; 807 } 808 809 close$c2; 810 811} elsif ($annotate) { 812 do_edit(@files); 813} 814 815sub ask { 816 my ($prompt,%arg) =@_; 817 my$valid_re=$arg{valid_re}; 818 my$default=$arg{default}; 819 my$confirm_only=$arg{confirm_only}; 820 my$resp; 821 my$i= 0; 822 return defined$default?$default: undef 823 unless defined$term->IN and defined fileno($term->IN) and 824 defined$term->OUT and defined fileno($term->OUT); 825 while ($i++< 10) { 826$resp=$term->readline($prompt); 827 if (!defined$resp) { # EOF 828 print "\n"; 829 return defined$default?$default: undef; 830 } 831 if ($respeq '' and defined$default) { 832 return$default; 833 } 834 if (!defined$valid_reor$resp=~ /$valid_re/) { 835 return$resp; 836 } 837 if ($confirm_only) { 838 my$yesno=$term->readline( 839 # TRANSLATORS: please keep [y/N] as is. 840 sprintf(__("Are you sure you want to use <%s> [y/N]? "),$resp)); 841 if (defined$yesno&&$yesno=~ /y/i) { 842 return$resp; 843 } 844 } 845 } 846 return; 847} 848 849sub parse_header_line { 850 my$lines= shift; 851 my$parsed_line= shift; 852 my$addr_pat= join "|", qw(To Cc Bcc); 853 854foreach(split(/\n/,$lines)) { 855if(/^($addr_pat):\s*(.+)$/i) { 856$parsed_line->{$1} = [ parse_address_line($2) ]; 857}elsif(/^([^:]*):\s*(.+)\s*$/i) { 858$parsed_line->{$1} =$2; 859} 860} 861} 862 863sub filter_body { 864my$c=shift; 865my$body=""; 866while(my$body_line= <$c>) { 867if($body_line!~m/^GIT:/) { 868$body.=$body_line; 869} 870} 871return$body; 872} 873 874 875my%broken_encoding; 876 877sub file_declares_8bit_cte { 878my$fn=shift; 879open(my$fh,'<',$fn); 880while(my$line= <$fh>) { 881last if($line=~/^$/); 882return1if($line=~/^Content-Transfer-Encoding: .*8bit.*$/); 883} 884close$fh; 885return0; 886} 887 888foreachmy$f(@files) { 889next unless(body_or_subject_has_nonascii($f) 890&& !file_declares_8bit_cte($f)); 891$broken_encoding{$f} =1; 892} 893 894if(!defined$auto_8bit_encoding&&scalar%broken_encoding) { 895print __("The following files are 8bit, but do not declare ". 896"a Content-Transfer-Encoding.\n"); 897foreachmy$f(sort keys%broken_encoding) { 898print"$f\n"; 899} 900$auto_8bit_encoding= ask(__("Which 8bit encoding should I declare [UTF-8]? "), 901 valid_re =>qr/.{4}/, confirm_only =>1, 902default=>"UTF-8"); 903} 904 905if(!$force) { 906formy$f(@files) { 907if(get_patch_subject($f) =~/\Q*** SUBJECT HERE ***\E/) { 908die sprintf(__("Refusing to send because the patch\n\t%s\n" 909."has the template subject '*** SUBJECT HERE ***'. " 910."Pass --force if you really want to send.\n"),$f); 911} 912} 913} 914 915if(defined$sender) { 916$sender=~s/^\s+|\s+$//g; 917($sender) = expand_aliases($sender); 918}else{ 919$sender=$repoauthor||$repocommitter||''; 920} 921 922# $sender could be an already sanitized address 923# (e.g. sendemail.from could be manually sanitized by user). 924# But it's a no-op to run sanitize_address on an already sanitized address. 925$sender= sanitize_address($sender); 926 927my$to_whom= __("To whom should the emails be sent (if anyone)?"); 928my$prompting=0; 929if(!@initial_to&& !defined$to_cmd) { 930my$to= ask("$to_whom", 931default=>"", 932 valid_re =>qr/\@.*\./, confirm_only =>1); 933push@initial_to, parse_address_line($to)ifdefined$to;# sanitized/validated later 934$prompting++; 935} 936 937sub expand_aliases { 938returnmap{ expand_one_alias($_) }@_; 939} 940 941my%EXPANDED_ALIASES; 942sub expand_one_alias { 943my$alias=shift; 944if($EXPANDED_ALIASES{$alias}) { 945die sprintf(__("fatal: alias '%s' expands to itself\n"),$alias); 946} 947local$EXPANDED_ALIASES{$alias} =1; 948return$aliases{$alias} ? expand_aliases(@{$aliases{$alias}}) :$alias; 949} 950 951@initial_to= process_address_list(@initial_to); 952@initial_cc= process_address_list(@initial_cc); 953@initial_bcc= process_address_list(@initial_bcc); 954 955if($thread&& !defined$initial_in_reply_to&&$prompting) { 956$initial_in_reply_to= ask( 957 __("Message-ID to be used as In-Reply-To for the first email (if any)? "), 958default=>"", 959 valid_re =>qr/\@.*\./, confirm_only =>1); 960} 961if(defined$initial_in_reply_to) { 962$initial_in_reply_to=~s/^\s*<?//; 963$initial_in_reply_to=~s/>?\s*$//; 964$initial_in_reply_to="<$initial_in_reply_to>"if$initial_in_reply_tone''; 965} 966 967if(defined$reply_to) { 968$reply_to=~s/^\s+|\s+$//g; 969($reply_to) = expand_aliases($reply_to); 970$reply_to= sanitize_address($reply_to); 971} 972 973if(!defined$smtp_server) { 974my@sendmail_paths=qw( /usr/sbin/sendmail /usr/lib/sendmail ); 975push@sendmail_paths,map{"$_/sendmail"}split/:/,$ENV{PATH}; 976foreach(@sendmail_paths) { 977if(-x $_) { 978$smtp_server=$_; 979last; 980} 981} 982$smtp_server||='localhost';# could be 127.0.0.1, too... *shrug* 983} 984 985if($compose&&$compose>0) { 986@files= ($compose_filename.".final",@files); 987} 988 989# Variables we set as part of the loop over files 990our($message_id,%mail,$subject,$in_reply_to,$references,$message, 991$needs_confirm,$message_num,$ask_default); 992 993sub extract_valid_address { 994my$address=shift; 995my$local_part_regexp=qr/[^<>"\s@]+/; 996my$domain_regexp=qr/[^.<>"\s@]+(?:\.[^.<>"\s@]+)+/; 997 998# check for a local address: 999return$addressif($address=~/^($local_part_regexp)$/);10001001$address=~s/^\s*<(.*)>\s*$/$1/;1002if($have_email_valid) {1003returnscalar Email::Valid->address($address);1004}10051006# less robust/correct than the monster regexp in Email::Valid,1007# but still does a 99% job, and one less dependency1008return$1if$address=~/($local_part_regexp\@$domain_regexp)/;1009return;1010}10111012sub extract_valid_address_or_die {1013my$address=shift;1014$address= extract_valid_address($address);1015die sprintf(__("error: unable to extract a valid address from:%s\n"),$address)1016if!$address;1017return$address;1018}10191020sub validate_address {1021my$address=shift;1022while(!extract_valid_address($address)) {1023printf STDERR __("error: unable to extract a valid address from:%s\n"),$address;1024# TRANSLATORS: Make sure to include [q] [d] [e] in your1025# translation. The program will only accept English input1026# at this point.1027$_= ask(__("What to do with this address? ([q]uit|[d]rop|[e]dit): "),1028 valid_re =>qr/^(?:quit|q|drop|d|edit|e)/i,1029default=>'q');1030if(/^d/i) {1031returnundef;1032}elsif(/^q/i) {1033 cleanup_compose_files();1034exit(0);1035}1036$address= ask("$to_whom",1037default=>"",1038 valid_re =>qr/\@.*\./, confirm_only =>1);1039}1040return$address;1041}10421043sub validate_address_list {1044return(grep{defined$_}1045map{ validate_address($_) }@_);1046}10471048# Usually don't need to change anything below here.10491050# we make a "fake" message id by taking the current number1051# of seconds since the beginning of Unix time and tacking on1052# a random number to the end, in case we are called quicker than1053# 1 second since the last time we were called.10541055# We'll setup a template for the message id, using the "from" address:10561057my($message_id_stamp,$message_id_serial);1058sub make_message_id {1059my$uniq;1060if(!defined$message_id_stamp) {1061$message_id_stamp= strftime("%Y%m%d%H%M%S.$$",gmtime(time));1062$message_id_serial=0;1063}1064$message_id_serial++;1065$uniq="$message_id_stamp-$message_id_serial";10661067my$du_part;1068for($sender,$repocommitter,$repoauthor) {1069$du_part= extract_valid_address(sanitize_address($_));1070last if(defined$du_partand$du_partne'');1071}1072if(not defined$du_partor$du_parteq'') {1073require Sys::Hostname;1074$du_part='user@'. Sys::Hostname::hostname();1075}1076my$message_id_template="<%s-%s>";1077$message_id=sprintf($message_id_template,$uniq,$du_part);1078#print "new message id = $message_id\n"; # Was useful for debugging1079}1080108110821083$time=time-scalar$#files;10841085sub unquote_rfc2047 {1086local($_) =@_;1087my$charset;1088my$sep=qr/[ \t]+/;1089 s{$re_encoded_word(?:$sep$re_encoded_word)*}{1090my@words=split$sep,$&;1091foreach(@words) {1092m/$re_encoded_word/;1093$charset=$1;1094my$encoding=$2;1095my$text=$3;1096if($encodingeq'q'||$encodingeq'Q') {1097$_=$text;1098s/_/ /g;1099s/=([0-9A-F]{2})/chr(hex($1))/egi;1100}else{1101# other encodings not supported yet1102}1103}1104join'',@words;1105}eg;1106returnwantarray? ($_,$charset) :$_;1107}11081109sub quote_rfc2047 {1110local$_=shift;1111my$encoding=shift||'UTF-8';1112s/([^-a-zA-Z0-9!*+\/])/sprintf("=%02X",ord($1))/eg;1113s/(.*)/=\?$encoding\?q\?$1\?=/;1114return$_;1115}11161117sub is_rfc2047_quoted {1118my$s=shift;1119length($s) <=75&&1120$s=~m/^(?:"[[:ascii:]]*"|$re_encoded_word)$/o;1121}11221123sub subject_needs_rfc2047_quoting {1124my$s=shift;11251126return($s=~/[^[:ascii:]]/) || ($s=~/=\?/);1127}11281129sub quote_subject {1130local$subject=shift;1131my$encoding=shift||'UTF-8';11321133if(subject_needs_rfc2047_quoting($subject)) {1134return quote_rfc2047($subject,$encoding);1135}1136return$subject;1137}11381139# use the simplest quoting being able to handle the recipient1140sub sanitize_address {1141my($recipient) =@_;11421143# remove garbage after email address1144$recipient=~s/(.*>).*$/$1/;11451146my($recipient_name,$recipient_addr) = ($recipient=~/^(.*?)\s*(<.*)/);11471148if(not$recipient_name) {1149return$recipient;1150}11511152# if recipient_name is already quoted, do nothing1153if(is_rfc2047_quoted($recipient_name)) {1154return$recipient;1155}11561157# remove non-escaped quotes1158$recipient_name=~s/(^|[^\\])"/$1/g;11591160# rfc2047 is needed if a non-ascii char is included1161if($recipient_name=~/[^[:ascii:]]/) {1162$recipient_name= quote_rfc2047($recipient_name);1163}11641165# double quotes are needed if specials or CTLs are included1166elsif($recipient_name=~/[][()<>@,;:\\".\000-\037\177]/) {1167$recipient_name=~s/([\\\r])/\\$1/g;1168$recipient_name=qq["$recipient_name"];1169}11701171return"$recipient_name$recipient_addr";11721173}11741175sub strip_garbage_one_address {1176my($addr) =@_;1177chomp$addr;1178if($addr=~/^(("[^"]*"|[^"<]*)? *<[^>]*>).*/) {1179# "Foo Bar" <foobar@example.com> [possibly garbage here]1180# Foo Bar <foobar@example.com> [possibly garbage here]1181return$1;1182}1183if($addr=~/^(<[^>]*>).*/) {1184# <foo@example.com> [possibly garbage here]1185# if garbage contains other addresses, they are ignored.1186return$1;1187}1188if($addr=~/^([^"#,\s]*)/) {1189# address without quoting: remove anything after the address1190return$1;1191}1192return$addr;1193}11941195sub sanitize_address_list {1196return(map{ sanitize_address($_) }@_);1197}11981199sub process_address_list {1200my@addr_list=map{ parse_address_line($_) }@_;1201@addr_list= expand_aliases(@addr_list);1202@addr_list= sanitize_address_list(@addr_list);1203@addr_list= validate_address_list(@addr_list);1204return@addr_list;1205}12061207# Returns the local Fully Qualified Domain Name (FQDN) if available.1208#1209# Tightly configured MTAa require that a caller sends a real DNS1210# domain name that corresponds the IP address in the HELO/EHLO1211# handshake. This is used to verify the connection and prevent1212# spammers from trying to hide their identity. If the DNS and IP don't1213# match, the receiveing MTA may deny the connection.1214#1215# Here is a deny example of Net::SMTP with the default "localhost.localdomain"1216#1217# Net::SMTP=GLOB(0x267ec28)>>> EHLO localhost.localdomain1218# Net::SMTP=GLOB(0x267ec28)<<< 550 EHLO argument does not match calling host1219#1220# This maildomain*() code is based on ideas in Perl library Test::Reporter1221# /usr/share/perl5/Test/Reporter/Mail/Util.pm ==> sub _maildomain ()12221223sub valid_fqdn {1224my$domain=shift;1225returndefined$domain&& !($^Oeq'darwin'&&$domain=~/\.local$/) &&$domain=~/\./;1226}12271228sub maildomain_net {1229my$maildomain;12301231my$domain= Net::Domain::domainname();1232$maildomain=$domainif valid_fqdn($domain);12331234return$maildomain;1235}12361237sub maildomain_mta {1238my$maildomain;12391240formy$host(qw(mailhost localhost)) {1241my$smtp= Net::SMTP->new($host);1242if(defined$smtp) {1243my$domain=$smtp->domain;1244$smtp->quit;12451246$maildomain=$domainif valid_fqdn($domain);12471248last if$maildomain;1249}1250}12511252return$maildomain;1253}12541255sub maildomain {1256return maildomain_net() || maildomain_mta() ||'localhost.localdomain';1257}12581259sub smtp_host_string {1260if(defined$smtp_server_port) {1261return"$smtp_server:$smtp_server_port";1262}else{1263return$smtp_server;1264}1265}12661267# Returns 1 if authentication succeeded or was not necessary1268# (smtp_user was not specified), and 0 otherwise.12691270sub smtp_auth_maybe {1271if(!defined$smtp_authuser||$auth) {1272return1;1273}12741275# Workaround AUTH PLAIN/LOGIN interaction defect1276# with Authen::SASL::Cyrus1277eval{1278require Authen::SASL;1279 Authen::SASL->import(qw(Perl));1280};12811282# Check mechanism naming as defined in:1283# https://tools.ietf.org/html/rfc4422#page-81284if($smtp_auth&&$smtp_auth!~/^(\b[A-Z0-9-_]{1,20}\s*)*$/) {1285die"invalid smtp auth: '${smtp_auth}'";1286}12871288# TODO: Authentication may fail not because credentials were1289# invalid but due to other reasons, in which we should not1290# reject credentials.1291$auth= Git::credential({1292'protocol'=>'smtp',1293'host'=> smtp_host_string(),1294'username'=>$smtp_authuser,1295# if there's no password, "git credential fill" will1296# give us one, otherwise it'll just pass this one.1297'password'=>$smtp_authpass1298},sub{1299my$cred=shift;13001301if($smtp_auth) {1302my$sasl= Authen::SASL->new(1303 mechanism =>$smtp_auth,1304 callback => {1305 user =>$cred->{'username'},1306 pass =>$cred->{'password'},1307 authname =>$cred->{'username'},1308}1309);13101311return!!$smtp->auth($sasl);1312}13131314return!!$smtp->auth($cred->{'username'},$cred->{'password'});1315});13161317return$auth;1318}13191320sub ssl_verify_params {1321eval{1322require IO::Socket::SSL;1323 IO::Socket::SSL->import(qw/SSL_VERIFY_PEER SSL_VERIFY_NONE/);1324};1325if($@) {1326print STDERR "Not using SSL_VERIFY_PEER due to out-of-date IO::Socket::SSL.\n";1327return;1328}13291330if(!defined$smtp_ssl_cert_path) {1331# use the OpenSSL defaults1332return(SSL_verify_mode => SSL_VERIFY_PEER());1333}13341335if($smtp_ssl_cert_patheq"") {1336return(SSL_verify_mode => SSL_VERIFY_NONE());1337}elsif(-d $smtp_ssl_cert_path) {1338return(SSL_verify_mode => SSL_VERIFY_PEER(),1339 SSL_ca_path =>$smtp_ssl_cert_path);1340}elsif(-f $smtp_ssl_cert_path) {1341return(SSL_verify_mode => SSL_VERIFY_PEER(),1342 SSL_ca_file =>$smtp_ssl_cert_path);1343}else{1344die sprintf(__("CA path\"%s\"does not exist"),$smtp_ssl_cert_path);1345}1346}13471348sub file_name_is_absolute {1349my($path) =@_;13501351# msys does not grok DOS drive-prefixes1352if($^Oeq'msys') {1353return($path=~ m#^/# || $path =~ m#^[a-zA-Z]\:#)1354}13551356require File::Spec::Functions;1357return File::Spec::Functions::file_name_is_absolute($path);1358}13591360# Prepares the email, then asks the user what to do.1361#1362# If the user chooses to send the email, it's sent and 1 is returned.1363# If the user chooses not to send the email, 0 is returned.1364# If the user decides they want to make further edits, -1 is returned and the1365# caller is expected to call send_message again after the edits are performed.1366#1367# If an error occurs sending the email, this just dies.13681369sub send_message {1370my@recipients= unique_email_list(@to);1371@cc= (grep{my$cc= extract_valid_address_or_die($_);1372not grep{$cceq$_||$_=~/<\Q${cc}\E>$/}@recipients1373}1374@cc);1375my$to=join(",\n\t",@recipients);1376@recipients= unique_email_list(@recipients,@cc,@initial_bcc);1377@recipients= (map{ extract_valid_address_or_die($_) }@recipients);1378my$date= format_2822_time($time++);1379my$gitversion='@@GIT_VERSION@@';1380if($gitversion=~m/..GIT_VERSION../) {1381$gitversion= Git::version();1382}13831384my$cc=join(",\n\t", unique_email_list(@cc));1385my$ccline="";1386if($ccne'') {1387$ccline="\nCc:$cc";1388}1389 make_message_id()unlessdefined($message_id);13901391my$header="From:$sender1392To:$to${ccline}1393Subject:$subject1394Date:$date1395Message-Id:$message_id1396";1397if($use_xmailer) {1398$header.="X-Mailer: git-send-email$gitversion\n";1399}1400if($in_reply_to) {14011402$header.="In-Reply-To:$in_reply_to\n";1403$header.="References:$references\n";1404}1405if($reply_to) {1406$header.="Reply-To:$reply_to\n";1407}1408if(@xh) {1409$header.=join("\n",@xh) ."\n";1410}14111412my@sendmail_parameters= ('-i',@recipients);1413my$raw_from=$sender;1414if(defined$envelope_sender&&$envelope_senderne"auto") {1415$raw_from=$envelope_sender;1416}1417$raw_from= extract_valid_address($raw_from);1418unshift(@sendmail_parameters,1419'-f',$raw_from)if(defined$envelope_sender);14201421if($needs_confirm&& !$dry_run) {1422print"\n$header\n";1423if($needs_confirmeq"inform") {1424$confirm_unconfigured=0;# squelch this message for the rest of this run1425$ask_default="y";# assume yes on EOF since user hasn't explicitly asked for confirmation1426print __ <<EOF ;1427 The Cc list above has been expanded by additional1428 addresses found in the patch commit message. By default1429 send-email prompts before sending whenever this occurs.1430 This behavior is controlled by the sendemail.confirm1431 configuration setting.14321433 For additional information, run 'git send-email --help'.1434 To retain the current behavior, but squelch this message,1435 run 'git config --global sendemail.confirm auto'.14361437EOF1438}1439# TRANSLATORS: Make sure to include [y] [n] [e] [q] [a] in your1440# translation. The program will only accept English input1441# at this point.1442$_= ask(__("Send this email? ([y]es|[n]o|[e]dit|[q]uit|[a]ll): "),1443 valid_re =>qr/^(?:yes|y|no|n|edit|e|quit|q|all|a)/i,1444default=>$ask_default);1445die __("Send this email reply required")unlessdefined$_;1446if(/^n/i) {1447return0;1448}elsif(/^e/i) {1449return-1;1450}elsif(/^q/i) {1451 cleanup_compose_files();1452exit(0);1453}elsif(/^a/i) {1454$confirm='never';1455}1456}14571458unshift(@sendmail_parameters,@smtp_server_options);14591460if($dry_run) {1461# We don't want to send the email.1462}elsif(file_name_is_absolute($smtp_server)) {1463my$pid=open my$sm,'|-';1464defined$pidor die$!;1465if(!$pid) {1466exec($smtp_server,@sendmail_parameters)or die$!;1467}1468print$sm"$header\n$message";1469close$smor die$!;1470}else{14711472if(!defined$smtp_server) {1473die __("The required SMTP server is not properly defined.")1474}14751476require Net::SMTP;1477my$use_net_smtp_ssl= version->parse($Net::SMTP::VERSION) < version->parse("2.34");1478$smtp_domain||= maildomain();14791480if($smtp_encryptioneq'ssl') {1481$smtp_server_port||=465;# ssmtp1482require IO::Socket::SSL;14831484# Suppress "variable accessed once" warning.1485{1486no warnings 'once';1487$IO::Socket::SSL::DEBUG =1;1488}14891490# Net::SMTP::SSL->new() does not forward any SSL options1491 IO::Socket::SSL::set_client_defaults(1492 ssl_verify_params());14931494if($use_net_smtp_ssl) {1495require Net::SMTP::SSL;1496$smtp||= Net::SMTP::SSL->new($smtp_server,1497 Hello =>$smtp_domain,1498 Port =>$smtp_server_port,1499 Debug =>$debug_net_smtp);1500}1501else{1502$smtp||= Net::SMTP->new($smtp_server,1503 Hello =>$smtp_domain,1504 Port =>$smtp_server_port,1505 Debug =>$debug_net_smtp,1506 SSL =>1);1507}1508}1509else{1510$smtp_server_port||=25;1511$smtp||= Net::SMTP->new($smtp_server,1512 Hello =>$smtp_domain,1513 Debug =>$debug_net_smtp,1514 Port =>$smtp_server_port);1515if($smtp_encryptioneq'tls'&&$smtp) {1516if($use_net_smtp_ssl) {1517$smtp->command('STARTTLS');1518$smtp->response();1519if($smtp->code!=220) {1520die sprintf(__("Server does not support STARTTLS!%s"),$smtp->message);1521}1522require Net::SMTP::SSL;1523$smtp= Net::SMTP::SSL->start_SSL($smtp,1524 ssl_verify_params())1525or die sprintf(__("STARTTLS failed!%s"), IO::Socket::SSL::errstr());1526}1527else{1528$smtp->starttls(ssl_verify_params())1529or die sprintf(__("STARTTLS failed!%s"), IO::Socket::SSL::errstr());1530}1531$smtp_encryption='';1532# Send EHLO again to receive fresh1533# supported commands1534$smtp->hello($smtp_domain);1535}1536}15371538if(!$smtp) {1539die __("Unable to initialize SMTP properly. Check config and use --smtp-debug."),1540" VALUES: server=$smtp_server",1541"encryption=$smtp_encryption",1542"hello=$smtp_domain",1543defined$smtp_server_port?" port=$smtp_server_port":"";1544}15451546 smtp_auth_maybe or die$smtp->message;15471548$smtp->mail($raw_from)or die$smtp->message;1549$smtp->to(@recipients)or die$smtp->message;1550$smtp->dataor die$smtp->message;1551$smtp->datasend("$header\n")or die$smtp->message;1552my@lines=split/^/,$message;1553foreachmy$line(@lines) {1554$smtp->datasend("$line")or die$smtp->message;1555}1556$smtp->dataend()or die$smtp->message;1557$smtp->code=~/250|200/or die sprintf(__("Failed to send%s\n"),$subject).$smtp->message;1558}1559if($quiet) {1560printf($dry_run? __("Dry-Sent%s\n") : __("Sent%s\n"),$subject);1561}else{1562print($dry_run? __("Dry-OK. Log says:\n") : __("OK. Log says:\n"));1563if(!file_name_is_absolute($smtp_server)) {1564print"Server:$smtp_server\n";1565print"MAIL FROM:<$raw_from>\n";1566foreachmy$entry(@recipients) {1567print"RCPT TO:<$entry>\n";1568}1569}else{1570print"Sendmail:$smtp_server".join(' ',@sendmail_parameters)."\n";1571}1572print$header,"\n";1573if($smtp) {1574print __("Result: "),$smtp->code,' ',1575($smtp->message=~/\n([^\n]+\n)$/s),"\n";1576}else{1577print __("Result: OK\n");1578}1579}15801581return1;1582}15831584$in_reply_to=$initial_in_reply_to;1585$references=$initial_in_reply_to||'';1586$subject=$initial_subject;1587$message_num=0;15881589# Prepares the email, prompts the user, sends it out1590# Returns 0 if an edit was done and the function should be called again, or 11591# otherwise.1592sub process_file {1593my($t) =@_;15941595open my$fh,"<",$tor die sprintf(__("can't open file%s"),$t);15961597my$author=undef;1598my$sauthor=undef;1599my$author_encoding;1600my$has_content_type;1601my$body_encoding;1602my$xfer_encoding;1603my$has_mime_version;1604@to= ();1605@cc= ();1606@xh= ();1607my$input_format=undef;1608my@header= ();1609$message="";1610$message_num++;1611# First unfold multiline header fields1612while(<$fh>) {1613last if/^\s*$/;1614if(/^\s+\S/and@header) {1615chomp($header[$#header]);1616s/^\s+/ /;1617$header[$#header] .=$_;1618}else{1619push(@header,$_);1620}1621}1622# Now parse the header1623foreach(@header) {1624if(/^From /) {1625$input_format='mbox';1626next;1627}1628chomp;1629if(!defined$input_format&&/^[-A-Za-z]+:\s/) {1630$input_format='mbox';1631}16321633if(defined$input_format&&$input_formateq'mbox') {1634if(/^Subject:\s+(.*)$/i) {1635$subject=$1;1636}1637elsif(/^From:\s+(.*)$/i) {1638($author,$author_encoding) = unquote_rfc2047($1);1639$sauthor= sanitize_address($author);1640next if$suppress_cc{'author'};1641next if$suppress_cc{'self'}and$sauthoreq$sender;1642printf(__("(mbox) Adding cc:%sfrom line '%s'\n"),1643$1,$_)unless$quiet;1644push@cc,$1;1645}1646elsif(/^To:\s+(.*)$/i) {1647foreachmy$addr(parse_address_line($1)) {1648printf(__("(mbox) Adding to:%sfrom line '%s'\n"),1649$addr,$_)unless$quiet;1650push@to,$addr;1651}1652}1653elsif(/^Cc:\s+(.*)$/i) {1654foreachmy$addr(parse_address_line($1)) {1655my$qaddr= unquote_rfc2047($addr);1656my$saddr= sanitize_address($qaddr);1657if($saddreq$sender) {1658next if($suppress_cc{'self'});1659}else{1660next if($suppress_cc{'cc'});1661}1662printf(__("(mbox) Adding cc:%sfrom line '%s'\n"),1663$addr,$_)unless$quiet;1664push@cc,$addr;1665}1666}1667elsif(/^Content-type:/i) {1668$has_content_type=1;1669if(/charset="?([^ "]+)/) {1670$body_encoding=$1;1671}1672push@xh,$_;1673}1674elsif(/^MIME-Version/i) {1675$has_mime_version=1;1676push@xh,$_;1677}1678elsif(/^Message-Id: (.*)/i) {1679$message_id=$1;1680}1681elsif(/^Content-Transfer-Encoding: (.*)/i) {1682$xfer_encoding=$1ifnot defined$xfer_encoding;1683}1684elsif(/^In-Reply-To: (.*)/i) {1685$in_reply_to=$1;1686}1687elsif(/^References: (.*)/i) {1688$references=$1;1689}1690elsif(!/^Date:\s/i&&/^[-A-Za-z]+:\s+\S/) {1691push@xh,$_;1692}1693}else{1694# In the traditional1695# "send lots of email" format,1696# line 1 = cc1697# line 2 = subject1698# So let's support that, too.1699$input_format='lots';1700if(@cc==0&& !$suppress_cc{'cc'}) {1701printf(__("(non-mbox) Adding cc:%sfrom line '%s'\n"),1702$_,$_)unless$quiet;1703push@cc,$_;1704}elsif(!defined$subject) {1705$subject=$_;1706}1707}1708}1709# Now parse the message body1710while(<$fh>) {1711$message.=$_;1712if(/^(Signed-off-by|Cc): (.*)/i) {1713chomp;1714my($what,$c) = ($1,$2);1715# strip garbage for the address we'll use:1716$c= strip_garbage_one_address($c);1717# sanitize a bit more to decide whether to suppress the address:1718my$sc= sanitize_address($c);1719if($sceq$sender) {1720next if($suppress_cc{'self'});1721}else{1722next if$suppress_cc{'sob'}and$what=~/Signed-off-by/i;1723next if$suppress_cc{'bodycc'}and$what=~/Cc/i;1724}1725push@cc,$c;1726printf(__("(body) Adding cc:%sfrom line '%s'\n"),1727$c,$_)unless$quiet;1728}1729}1730close$fh;17311732push@to, recipients_cmd("to-cmd","to",$to_cmd,$t)1733ifdefined$to_cmd;1734push@cc, recipients_cmd("cc-cmd","cc",$cc_cmd,$t)1735ifdefined$cc_cmd&& !$suppress_cc{'cccmd'};17361737if($broken_encoding{$t} && !$has_content_type) {1738$xfer_encoding='8bit'ifnot defined$xfer_encoding;1739$has_content_type=1;1740push@xh,"Content-Type: text/plain; charset=$auto_8bit_encoding";1741$body_encoding=$auto_8bit_encoding;1742}17431744if($broken_encoding{$t} && !is_rfc2047_quoted($subject)) {1745$subject= quote_subject($subject,$auto_8bit_encoding);1746}17471748if(defined$sauthorand$sauthorne$sender) {1749$message="From:$author\n\n$message";1750if(defined$author_encoding) {1751if($has_content_type) {1752if($body_encodingeq$author_encoding) {1753# ok, we already have the right encoding1754}1755else{1756# uh oh, we should re-encode1757}1758}1759else{1760$xfer_encoding='8bit'ifnot defined$xfer_encoding;1761$has_content_type=1;1762push@xh,1763"Content-Type: text/plain; charset=$author_encoding";1764}1765}1766}1767$xfer_encoding='8bit'ifnot defined$xfer_encoding;1768($message,$xfer_encoding) = apply_transfer_encoding(1769$message,$xfer_encoding,$target_xfer_encoding);1770push@xh,"Content-Transfer-Encoding:$xfer_encoding";1771unshift@xh,'MIME-Version: 1.0'unless$has_mime_version;17721773$needs_confirm= (1774$confirmeq"always"or1775($confirm=~/^(?:auto|cc)$/&&@cc)or1776($confirm=~/^(?:auto|compose)$/&&$compose&&$message_num==1));1777$needs_confirm="inform"if($needs_confirm&&$confirm_unconfigured&&@cc);17781779@to= process_address_list(@to);1780@cc= process_address_list(@cc);17811782@to= (@initial_to,@to);1783@cc= (@initial_cc,@cc);17841785if($message_num==1) {1786if(defined$cover_ccand$cover_cc) {1787@initial_cc=@cc;1788}1789if(defined$cover_toand$cover_to) {1790@initial_to=@to;1791}1792}17931794my$message_was_sent= send_message();1795if($message_was_sent== -1) {1796 do_edit($t);1797return0;1798}17991800# set up for the next message1801if($thread&&$message_was_sent&&1802($chain_reply_to|| !defined$in_reply_to||length($in_reply_to) ==0||1803$message_num==1)) {1804$in_reply_to=$message_id;1805if(length$references>0) {1806$references.="\n$message_id";1807}else{1808$references="$message_id";1809}1810}1811$message_id=undef;1812$num_sent++;1813if(defined$batch_size&&$num_sent==$batch_size) {1814$num_sent=0;1815$smtp->quitifdefined$smtp;1816undef$smtp;1817undef$auth;1818sleep($relogin_delay)ifdefined$relogin_delay;1819}18201821return1;1822}18231824foreachmy$t(@files) {1825while(!process_file($t)) {1826# user edited the file1827}1828}18291830# Execute a command (e.g. $to_cmd) to get a list of email addresses1831# and return a results array1832sub recipients_cmd {1833my($prefix,$what,$cmd,$file) =@_;18341835my@addresses= ();1836open my$fh,"-|","$cmd\Q$file\E"1837or die sprintf(__("(%s) Could not execute '%s'"),$prefix,$cmd);1838while(my$address= <$fh>) {1839$address=~s/^\s*//g;1840$address=~s/\s*$//g;1841$address= sanitize_address($address);1842next if($addresseq$senderand$suppress_cc{'self'});1843push@addresses,$address;1844printf(__("(%s) Adding%s:%sfrom: '%s'\n"),1845$prefix,$what,$address,$cmd)unless$quiet;1846}1847close$fh1848or die sprintf(__("(%s) failed to close pipe to '%s'"),$prefix,$cmd);1849return@addresses;1850}18511852cleanup_compose_files();18531854sub cleanup_compose_files {1855unlink($compose_filename,$compose_filename.".final")if$compose;1856}18571858$smtp->quitif$smtp;18591860sub apply_transfer_encoding {1861my$message=shift;1862my$from=shift;1863my$to=shift;18641865return$messageif($fromeq$toand$fromne'7bit');18661867require MIME::QuotedPrint;1868require MIME::Base64;18691870$message= MIME::QuotedPrint::decode($message)1871if($fromeq'quoted-printable');1872$message= MIME::Base64::decode($message)1873if($fromeq'base64');18741875$to= ($message=~/.{999,}/) ?'quoted-printable':'8bit'1876if$toeq'auto';18771878die __("cannot send message as 7bit")1879if($toeq'7bit'and$message=~/[^[:ascii:]]/);1880return($message,$to)1881if($toeq'7bit'or$toeq'8bit');1882return(MIME::QuotedPrint::encode($message,"\n",0),$to)1883if($toeq'quoted-printable');1884return(MIME::Base64::encode($message,"\n"),$to)1885if($toeq'base64');1886die __("invalid transfer encoding");1887}18881889sub unique_email_list {1890my%seen;1891my@emails;18921893foreachmy$entry(@_) {1894my$clean= extract_valid_address_or_die($entry);1895$seen{$clean} ||=0;1896next if$seen{$clean}++;1897push@emails,$entry;1898}1899return@emails;1900}19011902sub validate_patch {1903my($fn,$xfer_encoding) =@_;19041905if($repo) {1906my$validate_hook= catfile(catdir($repo->repo_path(),'hooks'),1907'sendemail-validate');1908my$hook_error;1909if(-x $validate_hook) {1910my$target= abs_path($fn);1911# The hook needs a correct cwd and GIT_DIR.1912my$cwd_save= cwd();1913chdir($repo->wc_path()or$repo->repo_path())1914or die("chdir:$!");1915local$ENV{"GIT_DIR"} =$repo->repo_path();1916$hook_error="rejected by sendemail-validate hook"1917ifsystem($validate_hook,$target);1918chdir($cwd_save)or die("chdir:$!");1919}1920return$hook_errorif$hook_error;1921}19221923# Any long lines will be automatically fixed if we use a suitable transfer1924# encoding.1925unless($xfer_encoding=~/^(?:auto|quoted-printable|base64)$/) {1926open(my$fh,'<',$fn)1927or die sprintf(__("unable to open%s:%s\n"),$fn,$!);1928while(my$line= <$fh>) {1929if(length($line) >998) {1930returnsprintf(__("%s: patch contains a line longer than 998 characters"), $.);1931}1932}1933}1934return;1935}19361937sub handle_backup {1938my($last,$lastlen,$file,$known_suffix) =@_;1939my($suffix,$skip);19401941$skip=0;1942if(defined$last&&1943($lastlen<length($file)) &&1944(substr($file,0,$lastlen)eq$last) &&1945($suffix=substr($file,$lastlen)) !~/^[a-z0-9]/i) {1946if(defined$known_suffix&&$suffixeq$known_suffix) {1947printf(__("Skipping%swith backup suffix '%s'.\n"),$file,$known_suffix);1948$skip=1;1949}else{1950# TRANSLATORS: please keep "[y|N]" as is.1951my$answer= ask(sprintf(__("Do you really want to send%s?[y|N]: "),$file),1952 valid_re =>qr/^(?:y|n)/i,1953default=>'n');1954$skip= ($answerne'y');1955if($skip) {1956$known_suffix=$suffix;1957}1958}1959}1960return($skip,$known_suffix);1961}19621963sub handle_backup_files {1964my@file=@_;1965my($last,$lastlen,$known_suffix,$skip,@result);1966formy$file(@file) {1967($skip,$known_suffix) = handle_backup($last,$lastlen,1968$file,$known_suffix);1969push@result,$fileunless$skip;1970$last=$file;1971$lastlen=length($file);1972}1973return@result;1974}19751976sub file_has_nonascii {1977my$fn=shift;1978open(my$fh,'<',$fn)1979or die sprintf(__("unable to open%s:%s\n"),$fn,$!);1980while(my$line= <$fh>) {1981return1if$line=~/[^[:ascii:]]/;1982}1983return0;1984}19851986sub body_or_subject_has_nonascii {1987my$fn=shift;1988open(my$fh,'<',$fn)1989or die sprintf(__("unable to open%s:%s\n"),$fn,$!);1990while(my$line= <$fh>) {1991last if$line=~/^$/;1992return1if$line=~/^Subject.*[^[:ascii:]]/;1993}1994while(my$line= <$fh>) {1995return1if$line=~/[^[:ascii:]]/;1996}1997return0;1998}