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, or 86 "none" to disable authentication. 87 This setting forces to use one of the listed mechanisms. 88 --no-smtp-auth Disable SMTP authentication. Shorthand for 89 `--smtp-auth=none` 90 --smtp-debug <0|1> * Disable, enable Net::SMTP debug. 91 92 --batch-size <int> * send max <int> message per connection. 93 --relogin-delay <int> * delay <int> seconds between two successive login. 94 This option can only be used with --batch-size 95 96 Automating: 97 --identity <str> * Use the sendemail.<id> options. 98 --to-cmd <str> * Email To: via `<str> \$patch_path` 99 --cc-cmd <str> * Email Cc: via `<str> \$patch_path` 100 --suppress-cc <str> * author, self, sob, cc, cccmd, body, bodycc, misc-by, all. 101 --[no-]cc-cover * Email Cc: addresses in the cover letter. 102 --[no-]to-cover * Email To: addresses in the cover letter. 103 --[no-]signed-off-by-cc * Send to Signed-off-by: addresses. Default on. 104 --[no-]suppress-from * Send to self. Default off. 105 --[no-]chain-reply-to * Chain In-Reply-To: fields. Default off. 106 --[no-]thread * Use In-Reply-To: field. Default on. 107 108 Administering: 109 --confirm <str> * Confirm recipients before sending; 110 auto, cc, compose, always, or never. 111 --quiet * Output one line of info per email. 112 --dry-run * Don't actually send the emails. 113 --[no-]validate * Perform patch sanity checks. Default on. 114 --[no-]format-patch * understand any non optional arguments as 115 `git format-patch` ones. 116 --force * Send even if safety checks would prevent it. 117 118 Information: 119 --dump-aliases * Dump configured aliases and exit. 120 121EOT 122exit(1); 123} 124 125# most mail servers generate the Date: header, but not all... 126sub format_2822_time { 127my($time) =@_; 128my@localtm=localtime($time); 129my@gmttm=gmtime($time); 130my$localmin=$localtm[1] +$localtm[2] *60; 131my$gmtmin=$gmttm[1] +$gmttm[2] *60; 132if($localtm[0] !=$gmttm[0]) { 133die __("local zone differs from GMT by a non-minute interval\n"); 134} 135if((($gmttm[6] +1) %7) ==$localtm[6]) { 136$localmin+=1440; 137}elsif((($gmttm[6] -1) %7) ==$localtm[6]) { 138$localmin-=1440; 139}elsif($gmttm[6] !=$localtm[6]) { 140die __("local time offset greater than or equal to 24 hours\n"); 141} 142my$offset=$localmin-$gmtmin; 143my$offhour=$offset/60; 144my$offmin=abs($offset%60); 145if(abs($offhour) >=24) { 146die __("local time offset greater than or equal to 24 hours\n"); 147} 148 149returnsprintf("%s,%2d%s%d%02d:%02d:%02d%s%02d%02d", 150qw(Sun Mon Tue Wed Thu Fri Sat)[$localtm[6]], 151$localtm[3], 152qw(Jan Feb Mar Apr May Jun 153 Jul Aug Sep Oct Nov Dec)[$localtm[4]], 154$localtm[5]+1900, 155$localtm[2], 156$localtm[1], 157$localtm[0], 158($offset>=0) ?'+':'-', 159abs($offhour), 160$offmin, 161); 162} 163 164my$have_email_valid=eval{require Email::Valid;1}; 165my$smtp; 166my$auth; 167my$num_sent=0; 168 169# Regexes for RFC 2047 productions. 170my$re_token=qr/[^][()<>@,;:\\"\/?.=\000-\037\177-\377]+/; 171my$re_encoded_text=qr/[^? \000-\037\177-\377]+/; 172my$re_encoded_word=qr/=\?($re_token)\?($re_token)\?($re_encoded_text)\?=/; 173 174# Variables we fill in automatically, or via prompting: 175my(@to,$no_to,@initial_to,@cc,$no_cc,@initial_cc,@bcclist,$no_bcc,@xh, 176$initial_in_reply_to,$reply_to,$initial_subject,@files, 177$author,$sender,$smtp_authpass,$annotate,$use_xmailer,$compose,$time); 178 179my$envelope_sender; 180 181# Example reply to: 182#$initial_in_reply_to = ''; #<20050203173208.GA23964@foobar.com>'; 183 184my$repo=eval{ Git->repository() }; 185my@repo=$repo? ($repo) : (); 186my$term=eval{ 187$ENV{"GIT_SEND_EMAIL_NOTTY"} 188? new Term::ReadLine 'git-send-email', \*STDIN, \*STDOUT 189: new Term::ReadLine 'git-send-email'; 190}; 191if($@) { 192$term= new FakeTerm "$@: going non-interactive"; 193} 194 195# Behavior modification variables 196my($quiet,$dry_run) = (0,0); 197my$format_patch; 198my$compose_filename; 199my$force=0; 200my$dump_aliases=0; 201 202# Handle interactive edition of files. 203my$multiedit; 204my$editor; 205 206sub do_edit { 207if(!defined($editor)) { 208$editor= Git::command_oneline('var','GIT_EDITOR'); 209} 210if(defined($multiedit) && !$multiedit) { 211map{ 212system('sh','-c',$editor.' "$@"',$editor,$_); 213if(($?&127) || ($?>>8)) { 214die(__("the editor exited uncleanly, aborting everything")); 215} 216}@_; 217}else{ 218system('sh','-c',$editor.' "$@"',$editor,@_); 219if(($?&127) || ($?>>8)) { 220die(__("the editor exited uncleanly, aborting everything")); 221} 222} 223} 224 225# Variables with corresponding config settings 226my($thread,$chain_reply_to,$suppress_from,$signed_off_by_cc); 227my($cover_cc,$cover_to); 228my($to_cmd,$cc_cmd); 229my($smtp_server,$smtp_server_port,@smtp_server_options); 230my($smtp_authuser,$smtp_encryption,$smtp_ssl_cert_path); 231my($batch_size,$relogin_delay); 232my($identity,$aliasfiletype,@alias_files,$smtp_domain,$smtp_auth); 233my($validate,$confirm); 234my(@suppress_cc); 235my($auto_8bit_encoding); 236my($compose_encoding); 237my$target_xfer_encoding='auto'; 238 239my($debug_net_smtp) =0;# Net::SMTP, see send_message() 240 241my%config_bool_settings= ( 242"thread"=> [\$thread,1], 243"chainreplyto"=> [\$chain_reply_to,0], 244"suppressfrom"=> [\$suppress_from,undef], 245"signedoffbycc"=> [\$signed_off_by_cc,undef], 246"cccover"=> [\$cover_cc,undef], 247"tocover"=> [\$cover_to,undef], 248"signedoffcc"=> [\$signed_off_by_cc,undef],# Deprecated 249"validate"=> [\$validate,1], 250"multiedit"=> [\$multiedit,undef], 251"annotate"=> [\$annotate,undef], 252"xmailer"=> [\$use_xmailer,1] 253); 254 255my%config_settings= ( 256"smtpserver"=> \$smtp_server, 257"smtpserverport"=> \$smtp_server_port, 258"smtpserveroption"=> \@smtp_server_options, 259"smtpuser"=> \$smtp_authuser, 260"smtppass"=> \$smtp_authpass, 261"smtpdomain"=> \$smtp_domain, 262"smtpauth"=> \$smtp_auth, 263"smtpbatchsize"=> \$batch_size, 264"smtprelogindelay"=> \$relogin_delay, 265"to"=> \@initial_to, 266"tocmd"=> \$to_cmd, 267"cc"=> \@initial_cc, 268"cccmd"=> \$cc_cmd, 269"aliasfiletype"=> \$aliasfiletype, 270"bcc"=> \@bcclist, 271"suppresscc"=> \@suppress_cc, 272"envelopesender"=> \$envelope_sender, 273"confirm"=> \$confirm, 274"from"=> \$sender, 275"assume8bitencoding"=> \$auto_8bit_encoding, 276"composeencoding"=> \$compose_encoding, 277"transferencoding"=> \$target_xfer_encoding, 278); 279 280my%config_path_settings= ( 281"aliasesfile"=> \@alias_files, 282"smtpsslcertpath"=> \$smtp_ssl_cert_path, 283); 284 285# Handle Uncouth Termination 286sub signal_handler { 287 288# Make text normal 289print color("reset"),"\n"; 290 291# SMTP password masked 292system"stty echo"; 293 294# tmp files from --compose 295if(defined$compose_filename) { 296if(-e $compose_filename) { 297printf __("'%s' contains an intermediate version ". 298"of the email you were composing.\n"), 299$compose_filename; 300} 301if(-e ($compose_filename.".final")) { 302printf __("'%s.final' contains the composed email.\n"), 303$compose_filename; 304} 305} 306 307exit; 308}; 309 310$SIG{TERM} = \&signal_handler; 311$SIG{INT} = \&signal_handler; 312 313# Begin by accumulating all the variables (defined above), that we will end up 314# needing, first, from the command line: 315 316my$help; 317my$rc= GetOptions("h"=> \$help, 318"dump-aliases"=> \$dump_aliases); 319usage()unless$rc; 320die __("--dump-aliases incompatible with other options\n") 321if!$helpand$dump_aliasesand@ARGV; 322$rc= GetOptions( 323"sender|from=s"=> \$sender, 324"in-reply-to=s"=> \$initial_in_reply_to, 325"reply-to=s"=> \$reply_to, 326"subject=s"=> \$initial_subject, 327"to=s"=> \@initial_to, 328"to-cmd=s"=> \$to_cmd, 329"no-to"=> \$no_to, 330"cc=s"=> \@initial_cc, 331"no-cc"=> \$no_cc, 332"bcc=s"=> \@bcclist, 333"no-bcc"=> \$no_bcc, 334"chain-reply-to!"=> \$chain_reply_to, 335"no-chain-reply-to"=>sub{$chain_reply_to=0}, 336"smtp-server=s"=> \$smtp_server, 337"smtp-server-option=s"=> \@smtp_server_options, 338"smtp-server-port=s"=> \$smtp_server_port, 339"smtp-user=s"=> \$smtp_authuser, 340"smtp-pass:s"=> \$smtp_authpass, 341"smtp-ssl"=>sub{$smtp_encryption='ssl'}, 342"smtp-encryption=s"=> \$smtp_encryption, 343"smtp-ssl-cert-path=s"=> \$smtp_ssl_cert_path, 344"smtp-debug:i"=> \$debug_net_smtp, 345"smtp-domain:s"=> \$smtp_domain, 346"smtp-auth=s"=> \$smtp_auth, 347"no-smtp-auth"=>sub{$smtp_auth='none'}, 348"identity=s"=> \$identity, 349"annotate!"=> \$annotate, 350"no-annotate"=>sub{$annotate=0}, 351"compose"=> \$compose, 352"quiet"=> \$quiet, 353"cc-cmd=s"=> \$cc_cmd, 354"suppress-from!"=> \$suppress_from, 355"no-suppress-from"=>sub{$suppress_from=0}, 356"suppress-cc=s"=> \@suppress_cc, 357"signed-off-cc|signed-off-by-cc!"=> \$signed_off_by_cc, 358"no-signed-off-cc|no-signed-off-by-cc"=>sub{$signed_off_by_cc=0}, 359"cc-cover|cc-cover!"=> \$cover_cc, 360"no-cc-cover"=>sub{$cover_cc=0}, 361"to-cover|to-cover!"=> \$cover_to, 362"no-to-cover"=>sub{$cover_to=0}, 363"confirm=s"=> \$confirm, 364"dry-run"=> \$dry_run, 365"envelope-sender=s"=> \$envelope_sender, 366"thread!"=> \$thread, 367"no-thread"=>sub{$thread=0}, 368"validate!"=> \$validate, 369"no-validate"=>sub{$validate=0}, 370"transfer-encoding=s"=> \$target_xfer_encoding, 371"format-patch!"=> \$format_patch, 372"no-format-patch"=>sub{$format_patch=0}, 373"8bit-encoding=s"=> \$auto_8bit_encoding, 374"compose-encoding=s"=> \$compose_encoding, 375"force"=> \$force, 376"xmailer!"=> \$use_xmailer, 377"no-xmailer"=>sub{$use_xmailer=0}, 378"batch-size=i"=> \$batch_size, 379"relogin-delay=i"=> \$relogin_delay, 380); 381 382usage()if$help; 383unless($rc) { 384 usage(); 385} 386 387die __("Cannot run git format-patch from outside a repository\n") 388if$format_patchand not$repo; 389 390die __("`batch-size` and `relogin` must be specified together ". 391"(via command-line or configuration option)\n") 392ifdefined$relogin_delayand not defined$batch_size; 393 394# Now, let's fill any that aren't set in with defaults: 395 396sub read_config { 397my($prefix) =@_; 398 399foreachmy$setting(keys%config_bool_settings) { 400my$target=$config_bool_settings{$setting}->[0]; 401$$target= Git::config_bool(@repo,"$prefix.$setting")unless(defined$$target); 402} 403 404foreachmy$setting(keys%config_path_settings) { 405my$target=$config_path_settings{$setting}; 406if(ref($target)eq"ARRAY") { 407unless(@$target) { 408my@values= Git::config_path(@repo,"$prefix.$setting"); 409@$target=@valuesif(@values&&defined$values[0]); 410} 411} 412else{ 413$$target= Git::config_path(@repo,"$prefix.$setting")unless(defined$$target); 414} 415} 416 417foreachmy$setting(keys%config_settings) { 418my$target=$config_settings{$setting}; 419next if$settingeq"to"and defined$no_to; 420next if$settingeq"cc"and defined$no_cc; 421next if$settingeq"bcc"and defined$no_bcc; 422if(ref($target)eq"ARRAY") { 423unless(@$target) { 424my@values= Git::config(@repo,"$prefix.$setting"); 425@$target=@valuesif(@values&&defined$values[0]); 426} 427} 428else{ 429$$target= Git::config(@repo,"$prefix.$setting")unless(defined$$target); 430} 431} 432 433if(!defined$smtp_encryption) { 434my$enc= Git::config(@repo,"$prefix.smtpencryption"); 435if(defined$enc) { 436$smtp_encryption=$enc; 437}elsif(Git::config_bool(@repo,"$prefix.smtpssl")) { 438$smtp_encryption='ssl'; 439} 440} 441} 442 443# read configuration from [sendemail "$identity"], fall back on [sendemail] 444$identity= Git::config(@repo,"sendemail.identity")unless(defined$identity); 445read_config("sendemail.$identity")if(defined$identity); 446read_config("sendemail"); 447 448# fall back on builtin bool defaults 449foreachmy$setting(values%config_bool_settings) { 450${$setting->[0]} =$setting->[1]unless(defined(${$setting->[0]})); 451} 452 453# 'default' encryption is none -- this only prevents a warning 454$smtp_encryption=''unless(defined$smtp_encryption); 455 456# Set CC suppressions 457my(%suppress_cc); 458if(@suppress_cc) { 459foreachmy$entry(@suppress_cc) { 460die sprintf(__("Unknown --suppress-cc field: '%s'\n"),$entry) 461unless$entry=~/^(?:all|cccmd|cc|author|self|sob|body|bodycc|misc-by)$/; 462$suppress_cc{$entry} =1; 463} 464} 465 466if($suppress_cc{'all'}) { 467foreachmy$entry(qw (cccmd cc author self sob body bodycc misc-by)) { 468$suppress_cc{$entry} =1; 469} 470delete$suppress_cc{'all'}; 471} 472 473# If explicit old-style ones are specified, they trump --suppress-cc. 474$suppress_cc{'self'} =$suppress_fromifdefined$suppress_from; 475$suppress_cc{'sob'} = !$signed_off_by_ccifdefined$signed_off_by_cc; 476 477if($suppress_cc{'body'}) { 478foreachmy$entry(qw (sob bodycc misc-by)) { 479$suppress_cc{$entry} =1; 480} 481delete$suppress_cc{'body'}; 482} 483 484# Set confirm's default value 485my$confirm_unconfigured= !defined$confirm; 486if($confirm_unconfigured) { 487$confirm=scalar%suppress_cc?'compose':'auto'; 488}; 489die sprintf(__("Unknown --confirm setting: '%s'\n"),$confirm) 490unless$confirm=~/^(?:auto|cc|compose|always|never)/; 491 492# Debugging, print out the suppressions. 493if(0) { 494print"suppressions:\n"; 495foreachmy$entry(keys%suppress_cc) { 496printf" %-5s ->$suppress_cc{$entry}\n",$entry; 497} 498} 499 500my($repoauthor,$repocommitter); 501($repoauthor) = Git::ident_person(@repo,'author'); 502($repocommitter) = Git::ident_person(@repo,'committer'); 503 504sub parse_address_line { 505returnmap{$_->format} Mail::Address->parse($_[0]); 506} 507 508sub split_addrs { 509return quotewords('\s*,\s*',1,@_); 510} 511 512my%aliases; 513 514sub parse_sendmail_alias { 515local$_=shift; 516if(/"/) { 517printf STDERR __("warning: sendmail alias with quotes is not supported:%s\n"),$_; 518}elsif(/:include:/) { 519printf STDERR __("warning: `:include:` not supported:%s\n"),$_; 520}elsif(/[\/|]/) { 521printf STDERR __("warning: `/file` or `|pipe` redirection not supported:%s\n"),$_; 522}elsif(/^(\S+?)\s*:\s*(.+)$/) { 523my($alias,$addr) = ($1,$2); 524$aliases{$alias} = [ split_addrs($addr) ]; 525}else{ 526printf STDERR __("warning: sendmail line is not recognized:%s\n"),$_; 527} 528} 529 530sub parse_sendmail_aliases { 531my$fh=shift; 532my$s=''; 533while(<$fh>) { 534chomp; 535next if/^\s*$/||/^\s*#/; 536$s.=$_,next if$s=~s/\\$//||s/^\s+//; 537 parse_sendmail_alias($s)if$s; 538$s=$_; 539} 540$s=~s/\\$//;# silently tolerate stray '\' on last line 541 parse_sendmail_alias($s)if$s; 542} 543 544my%parse_alias= ( 545# multiline formats can be supported in the future 546 mutt =>sub{my$fh=shift;while(<$fh>) { 547if(/^\s*alias\s+(?:-group\s+\S+\s+)*(\S+)\s+(.*)$/) { 548my($alias,$addr) = ($1,$2); 549$addr=~s/#.*$//;# mutt allows # comments 550# commas delimit multiple addresses 551my@addr= split_addrs($addr); 552 553# quotes may be escaped in the file, 554# unescape them so we do not double-escape them later. 555s/\\"/"/gforeach@addr; 556$aliases{$alias} = \@addr 557}}}, 558 mailrc =>sub{my$fh=shift;while(<$fh>) { 559if(/^alias\s+(\S+)\s+(.*?)\s*$/) { 560# spaces delimit multiple addresses 561$aliases{$1} = [ quotewords('\s+',0,$2) ]; 562}}}, 563 pine =>sub{my$fh=shift;my$f='\t[^\t]*'; 564for(my$x='';defined($x);$x=$_) { 565chomp$x; 566$x.=$1while(defined($_= <$fh>) &&/^ +(.*)$/); 567$x=~/^(\S+)$f\t\(?([^\t]+?)\)?(:?$f){0,2}$/ornext; 568$aliases{$1} = [ split_addrs($2) ]; 569}}, 570 elm =>sub{my$fh=shift; 571while(<$fh>) { 572if(/^(\S+)\s+=\s+[^=]+=\s(\S+)/) { 573my($alias,$addr) = ($1,$2); 574$aliases{$alias} = [ split_addrs($addr) ]; 575} 576} }, 577 sendmail => \&parse_sendmail_aliases, 578 gnus =>sub{my$fh=shift;while(<$fh>) { 579if(/\(define-mail-alias\s+"(\S+?)"\s+"(\S+?)"\)/) { 580$aliases{$1} = [$2]; 581}}} 582); 583 584if(@alias_filesand$aliasfiletypeand defined$parse_alias{$aliasfiletype}) { 585foreachmy$file(@alias_files) { 586open my$fh,'<',$fileor die"opening$file:$!\n"; 587$parse_alias{$aliasfiletype}->($fh); 588close$fh; 589} 590} 591 592if($dump_aliases) { 593print"$_\n"for(sort keys%aliases); 594exit(0); 595} 596 597# is_format_patch_arg($f) returns 0 if $f names a patch, or 1 if 598# $f is a revision list specification to be passed to format-patch. 599sub is_format_patch_arg { 600return unless$repo; 601my$f=shift; 602try{ 603$repo->command('rev-parse','--verify','--quiet',$f); 604if(defined($format_patch)) { 605return$format_patch; 606} 607die sprintf(__ <<EOF,$f,$f); 608File '%s' exists but it could also be the range of commits 609to produce patches for. Please disambiguate by... 610 611 * Saying "./%s" if you mean a file; or 612 * Giving --format-patch option if you mean a range. 613EOF 614} catch Git::Error::Command with { 615# Not a valid revision. Treat it as a filename. 616return0; 617} 618} 619 620# Now that all the defaults are set, process the rest of the command line 621# arguments and collect up the files that need to be processed. 622my@rev_list_opts; 623while(defined(my$f=shift@ARGV)) { 624if($feq"--") { 625push@rev_list_opts,"--",@ARGV; 626@ARGV= (); 627}elsif(-d $fand!is_format_patch_arg($f)) { 628opendir my$dh,$f 629or die sprintf(__("Failed to opendir%s:%s"),$f,$!); 630 631push@files,grep{ -f $_}map{ catfile($f,$_) } 632sort readdir$dh; 633closedir$dh; 634}elsif((-f $for-p $f)and!is_format_patch_arg($f)) { 635push@files,$f; 636}else{ 637push@rev_list_opts,$f; 638} 639} 640 641if(@rev_list_opts) { 642die __("Cannot run git format-patch from outside a repository\n") 643unless$repo; 644push@files,$repo->command('format-patch','-o', tempdir(CLEANUP =>1),@rev_list_opts); 645} 646 647@files= handle_backup_files(@files); 648 649if($validate) { 650foreachmy$f(@files) { 651unless(-p $f) { 652my$error= validate_patch($f,$target_xfer_encoding); 653$errorand die sprintf(__("fatal:%s:%s\nwarning: no patches were sent\n"), 654$f,$error); 655} 656} 657} 658 659if(@files) { 660unless($quiet) { 661print$_,"\n"for(@files); 662} 663}else{ 664print STDERR __("\nNo patch files specified!\n\n"); 665 usage(); 666} 667 668sub get_patch_subject { 669my$fn=shift; 670open(my$fh,'<',$fn); 671while(my$line= <$fh>) { 672next unless($line=~/^Subject: (.*)$/); 673close$fh; 674return"GIT:$1\n"; 675} 676close$fh; 677die sprintf(__("No subject line in%s?"),$fn); 678} 679 680if($compose) { 681# Note that this does not need to be secure, but we will make a small 682# effort to have it be unique 683$compose_filename= ($repo? 684 tempfile(".gitsendemail.msg.XXXXXX", DIR =>$repo->repo_path()) : 685 tempfile(".gitsendemail.msg.XXXXXX", DIR =>"."))[1]; 686open my$c,">",$compose_filename 687or die sprintf(__("Failed to open for writing%s:%s"),$compose_filename,$!); 688 689 690my$tpl_sender=$sender||$repoauthor||$repocommitter||''; 691my$tpl_subject=$initial_subject||''; 692my$tpl_in_reply_to=$initial_in_reply_to||''; 693my$tpl_reply_to=$reply_to||''; 694 695print$c<<EOT1, Git::prefix_lines("GIT: ", __ <<EOT2), <<EOT3; 696From$tpl_sender# This line is ignored. 697EOT1 698Lines beginning in "GIT:" will be removed. 699Consider including an overall diffstat or table of contents 700for the patch you are writing. 701 702Clear the body content if you don't wish to send a summary. 703EOT2 704From:$tpl_sender 705Reply-To:$tpl_reply_to 706Subject:$tpl_subject 707In-Reply-To:$tpl_in_reply_to 708 709EOT3 710 for my$f(@files) { 711 print$cget_patch_subject($f); 712 } 713 close$c; 714 715 if ($annotate) { 716 do_edit($compose_filename,@files); 717 } else { 718 do_edit($compose_filename); 719 } 720 721 open$c, "<",$compose_filename 722 or die sprintf(__("Failed to open%s:%s"),$compose_filename,$!); 723 724 if (!defined$compose_encoding) { 725$compose_encoding= "UTF-8"; 726 } 727 728 my%parsed_email; 729 while (my$line= <$c>) { 730 next if$line=~ m/^GIT:/; 731 parse_header_line($line, \%parsed_email); 732 if ($line=~ /^$/) { 733$parsed_email{'body'} = filter_body($c); 734 } 735 } 736 close$c; 737 738 open my$c2, ">",$compose_filename. ".final" 739 or die sprintf(__("Failed to open%s.final:%s"),$compose_filename,$!); 740 741 742 if ($parsed_email{'From'}) { 743$sender= delete($parsed_email{'From'}); 744 } 745 if ($parsed_email{'In-Reply-To'}) { 746$initial_in_reply_to= delete($parsed_email{'In-Reply-To'}); 747 } 748 if ($parsed_email{'Reply-To'}) { 749$reply_to= delete($parsed_email{'Reply-To'}); 750 } 751 if ($parsed_email{'Subject'}) { 752$initial_subject= delete($parsed_email{'Subject'}); 753 print$c2"Subject: " . 754 quote_subject($initial_subject,$compose_encoding) . 755 "\n"; 756 } 757 758 if ($parsed_email{'MIME-Version'}) { 759 print$c2"MIME-Version:$parsed_email{'MIME-Version'}\n", 760 "Content-Type:$parsed_email{'Content-Type'};\n", 761 "Content-Transfer-Encoding:$parsed_email{'Content-Transfer-Encoding'}\n"; 762 delete($parsed_email{'MIME-Version'}); 763 delete($parsed_email{'Content-Type'}); 764 delete($parsed_email{'Content-Transfer-Encoding'}); 765 } elsif (file_has_nonascii($compose_filename)) { 766 my$content_type= (delete($parsed_email{'Content-Type'}) or 767 "text/plain; charset=$compose_encoding"); 768 print$c2"MIME-Version: 1.0\n", 769 "Content-Type:$content_type\n", 770 "Content-Transfer-Encoding: 8bit\n"; 771 } 772 # Preserve unknown headers 773 foreach my$key(keys%parsed_email) { 774 next if$keyeq 'body'; 775 print$c2"$key:$parsed_email{$key}"; 776 } 777 778 if ($parsed_email{'body'}) { 779 print$c2"\n$parsed_email{'body'}\n"; 780 delete($parsed_email{'body'}); 781 } else { 782 print __("Summary email is empty, skipping it\n"); 783$compose= -1; 784 } 785 786 close$c2; 787 788} elsif ($annotate) { 789 do_edit(@files); 790} 791 792sub ask { 793 my ($prompt,%arg) =@_; 794 my$valid_re=$arg{valid_re}; 795 my$default=$arg{default}; 796 my$confirm_only=$arg{confirm_only}; 797 my$resp; 798 my$i= 0; 799 return defined$default?$default: undef 800 unless defined$term->IN and defined fileno($term->IN) and 801 defined$term->OUT and defined fileno($term->OUT); 802 while ($i++< 10) { 803$resp=$term->readline($prompt); 804 if (!defined$resp) { # EOF 805 print "\n"; 806 return defined$default?$default: undef; 807 } 808 if ($respeq '' and defined$default) { 809 return$default; 810 } 811 if (!defined$valid_reor$resp=~ /$valid_re/) { 812 return$resp; 813 } 814 if ($confirm_only) { 815 my$yesno=$term->readline( 816 # TRANSLATORS: please keep [y/N] as is. 817 sprintf(__("Are you sure you want to use <%s> [y/N]? "),$resp)); 818 if (defined$yesno&&$yesno=~ /y/i) { 819 return$resp; 820 } 821 } 822 } 823 return; 824} 825 826sub parse_header_line { 827 my$lines= shift; 828 my$parsed_line= shift; 829 my$addr_pat= join "|", qw(To Cc Bcc); 830 831foreach(split(/\n/,$lines)) { 832if(/^($addr_pat):\s*(.+)$/i) { 833$parsed_line->{$1} = [ parse_address_line($2) ]; 834}elsif(/^([^:]*):\s*(.+)\s*$/i) { 835$parsed_line->{$1} =$2; 836} 837} 838} 839 840sub filter_body { 841my$c=shift; 842my$body=""; 843while(my$body_line= <$c>) { 844if($body_line!~m/^GIT:/) { 845$body.=$body_line; 846} 847} 848return$body; 849} 850 851 852my%broken_encoding; 853 854sub file_declares_8bit_cte { 855my$fn=shift; 856open(my$fh,'<',$fn); 857while(my$line= <$fh>) { 858last if($line=~/^$/); 859return1if($line=~/^Content-Transfer-Encoding: .*8bit.*$/); 860} 861close$fh; 862return0; 863} 864 865foreachmy$f(@files) { 866next unless(body_or_subject_has_nonascii($f) 867&& !file_declares_8bit_cte($f)); 868$broken_encoding{$f} =1; 869} 870 871if(!defined$auto_8bit_encoding&&scalar%broken_encoding) { 872print __("The following files are 8bit, but do not declare ". 873"a Content-Transfer-Encoding.\n"); 874foreachmy$f(sort keys%broken_encoding) { 875print"$f\n"; 876} 877$auto_8bit_encoding= ask(__("Which 8bit encoding should I declare [UTF-8]? "), 878 valid_re =>qr/.{4}/, confirm_only =>1, 879default=>"UTF-8"); 880} 881 882if(!$force) { 883formy$f(@files) { 884if(get_patch_subject($f) =~/\Q*** SUBJECT HERE ***\E/) { 885die sprintf(__("Refusing to send because the patch\n\t%s\n" 886."has the template subject '*** SUBJECT HERE ***'. " 887."Pass --force if you really want to send.\n"),$f); 888} 889} 890} 891 892if(defined$sender) { 893$sender=~s/^\s+|\s+$//g; 894($sender) = expand_aliases($sender); 895}else{ 896$sender=$repoauthor||$repocommitter||''; 897} 898 899# $sender could be an already sanitized address 900# (e.g. sendemail.from could be manually sanitized by user). 901# But it's a no-op to run sanitize_address on an already sanitized address. 902$sender= sanitize_address($sender); 903 904my$to_whom= __("To whom should the emails be sent (if anyone)?"); 905my$prompting=0; 906if(!@initial_to&& !defined$to_cmd) { 907my$to= ask("$to_whom", 908default=>"", 909 valid_re =>qr/\@.*\./, confirm_only =>1); 910push@initial_to, parse_address_line($to)ifdefined$to;# sanitized/validated later 911$prompting++; 912} 913 914sub expand_aliases { 915returnmap{ expand_one_alias($_) }@_; 916} 917 918my%EXPANDED_ALIASES; 919sub expand_one_alias { 920my$alias=shift; 921if($EXPANDED_ALIASES{$alias}) { 922die sprintf(__("fatal: alias '%s' expands to itself\n"),$alias); 923} 924local$EXPANDED_ALIASES{$alias} =1; 925return$aliases{$alias} ? expand_aliases(@{$aliases{$alias}}) :$alias; 926} 927 928@initial_to= process_address_list(@initial_to); 929@initial_cc= process_address_list(@initial_cc); 930@bcclist= process_address_list(@bcclist); 931 932if($thread&& !defined$initial_in_reply_to&&$prompting) { 933$initial_in_reply_to= ask( 934 __("Message-ID to be used as In-Reply-To for the first email (if any)? "), 935default=>"", 936 valid_re =>qr/\@.*\./, confirm_only =>1); 937} 938if(defined$initial_in_reply_to) { 939$initial_in_reply_to=~s/^\s*<?//; 940$initial_in_reply_to=~s/>?\s*$//; 941$initial_in_reply_to="<$initial_in_reply_to>"if$initial_in_reply_tone''; 942} 943 944if(defined$reply_to) { 945$reply_to=~s/^\s+|\s+$//g; 946($reply_to) = expand_aliases($reply_to); 947$reply_to= sanitize_address($reply_to); 948} 949 950if(!defined$smtp_server) { 951my@sendmail_paths=qw( /usr/sbin/sendmail /usr/lib/sendmail ); 952push@sendmail_paths,map{"$_/sendmail"}split/:/,$ENV{PATH}; 953foreach(@sendmail_paths) { 954if(-x $_) { 955$smtp_server=$_; 956last; 957} 958} 959$smtp_server||='localhost';# could be 127.0.0.1, too... *shrug* 960} 961 962if($compose&&$compose>0) { 963@files= ($compose_filename.".final",@files); 964} 965 966# Variables we set as part of the loop over files 967our($message_id,%mail,$subject,$in_reply_to,$references,$message, 968$needs_confirm,$message_num,$ask_default); 969 970sub extract_valid_address { 971my$address=shift; 972my$local_part_regexp=qr/[^<>"\s@]+/; 973my$domain_regexp=qr/[^.<>"\s@]+(?:\.[^.<>"\s@]+)+/; 974 975# check for a local address: 976return$addressif($address=~/^($local_part_regexp)$/); 977 978$address=~s/^\s*<(.*)>\s*$/$1/; 979if($have_email_valid) { 980returnscalar Email::Valid->address($address); 981} 982 983# less robust/correct than the monster regexp in Email::Valid, 984# but still does a 99% job, and one less dependency 985return$1if$address=~/($local_part_regexp\@$domain_regexp)/; 986return; 987} 988 989sub extract_valid_address_or_die { 990my$address=shift; 991$address= extract_valid_address($address); 992die sprintf(__("error: unable to extract a valid address from:%s\n"),$address) 993if!$address; 994return$address; 995} 996 997sub validate_address { 998my$address=shift; 999while(!extract_valid_address($address)) {1000printf STDERR __("error: unable to extract a valid address from:%s\n"),$address;1001# TRANSLATORS: Make sure to include [q] [d] [e] in your1002# translation. The program will only accept English input1003# at this point.1004$_= ask(__("What to do with this address? ([q]uit|[d]rop|[e]dit): "),1005 valid_re =>qr/^(?:quit|q|drop|d|edit|e)/i,1006default=>'q');1007if(/^d/i) {1008returnundef;1009}elsif(/^q/i) {1010 cleanup_compose_files();1011exit(0);1012}1013$address= ask("$to_whom",1014default=>"",1015 valid_re =>qr/\@.*\./, confirm_only =>1);1016}1017return$address;1018}10191020sub validate_address_list {1021return(grep{defined$_}1022map{ validate_address($_) }@_);1023}10241025# Usually don't need to change anything below here.10261027# we make a "fake" message id by taking the current number1028# of seconds since the beginning of Unix time and tacking on1029# a random number to the end, in case we are called quicker than1030# 1 second since the last time we were called.10311032# We'll setup a template for the message id, using the "from" address:10331034my($message_id_stamp,$message_id_serial);1035sub make_message_id {1036my$uniq;1037if(!defined$message_id_stamp) {1038$message_id_stamp= strftime("%Y%m%d%H%M%S.$$",gmtime(time));1039$message_id_serial=0;1040}1041$message_id_serial++;1042$uniq="$message_id_stamp-$message_id_serial";10431044my$du_part;1045for($sender,$repocommitter,$repoauthor) {1046$du_part= extract_valid_address(sanitize_address($_));1047last if(defined$du_partand$du_partne'');1048}1049if(not defined$du_partor$du_parteq'') {1050require Sys::Hostname;1051$du_part='user@'. Sys::Hostname::hostname();1052}1053my$message_id_template="<%s-%s>";1054$message_id=sprintf($message_id_template,$uniq,$du_part);1055#print "new message id = $message_id\n"; # Was useful for debugging1056}1057105810591060$time=time-scalar$#files;10611062sub unquote_rfc2047 {1063local($_) =@_;1064my$charset;1065my$sep=qr/[ \t]+/;1066 s{$re_encoded_word(?:$sep$re_encoded_word)*}{1067my@words=split$sep,$&;1068foreach(@words) {1069m/$re_encoded_word/;1070$charset=$1;1071my$encoding=$2;1072my$text=$3;1073if($encodingeq'q'||$encodingeq'Q') {1074$_=$text;1075s/_/ /g;1076s/=([0-9A-F]{2})/chr(hex($1))/egi;1077}else{1078# other encodings not supported yet1079}1080}1081join'',@words;1082}eg;1083returnwantarray? ($_,$charset) :$_;1084}10851086sub quote_rfc2047 {1087local$_=shift;1088my$encoding=shift||'UTF-8';1089s/([^-a-zA-Z0-9!*+\/])/sprintf("=%02X",ord($1))/eg;1090s/(.*)/=\?$encoding\?q\?$1\?=/;1091return$_;1092}10931094sub is_rfc2047_quoted {1095my$s=shift;1096length($s) <=75&&1097$s=~m/^(?:"[[:ascii:]]*"|$re_encoded_word)$/o;1098}10991100sub subject_needs_rfc2047_quoting {1101my$s=shift;11021103return($s=~/[^[:ascii:]]/) || ($s=~/=\?/);1104}11051106sub quote_subject {1107local$subject=shift;1108my$encoding=shift||'UTF-8';11091110if(subject_needs_rfc2047_quoting($subject)) {1111return quote_rfc2047($subject,$encoding);1112}1113return$subject;1114}11151116# use the simplest quoting being able to handle the recipient1117sub sanitize_address {1118my($recipient) =@_;11191120# remove garbage after email address1121$recipient=~s/(.*>).*$/$1/;11221123my($recipient_name,$recipient_addr) = ($recipient=~/^(.*?)\s*(<.*)/);11241125if(not$recipient_name) {1126return$recipient;1127}11281129# if recipient_name is already quoted, do nothing1130if(is_rfc2047_quoted($recipient_name)) {1131return$recipient;1132}11331134# remove non-escaped quotes1135$recipient_name=~s/(^|[^\\])"/$1/g;11361137# rfc2047 is needed if a non-ascii char is included1138if($recipient_name=~/[^[:ascii:]]/) {1139$recipient_name= quote_rfc2047($recipient_name);1140}11411142# double quotes are needed if specials or CTLs are included1143elsif($recipient_name=~/[][()<>@,;:\\".\000-\037\177]/) {1144$recipient_name=~s/([\\\r])/\\$1/g;1145$recipient_name=qq["$recipient_name"];1146}11471148return"$recipient_name$recipient_addr";11491150}11511152sub strip_garbage_one_address {1153my($addr) =@_;1154chomp$addr;1155if($addr=~/^(("[^"]*"|[^"<]*)? *<[^>]*>).*/) {1156# "Foo Bar" <foobar@example.com> [possibly garbage here]1157# Foo Bar <foobar@example.com> [possibly garbage here]1158return$1;1159}1160if($addr=~/^(<[^>]*>).*/) {1161# <foo@example.com> [possibly garbage here]1162# if garbage contains other addresses, they are ignored.1163return$1;1164}1165if($addr=~/^([^"#,\s]*)/) {1166# address without quoting: remove anything after the address1167return$1;1168}1169return$addr;1170}11711172sub sanitize_address_list {1173return(map{ sanitize_address($_) }@_);1174}11751176sub process_address_list {1177my@addr_list=map{ parse_address_line($_) }@_;1178@addr_list= expand_aliases(@addr_list);1179@addr_list= sanitize_address_list(@addr_list);1180@addr_list= validate_address_list(@addr_list);1181return@addr_list;1182}11831184# Returns the local Fully Qualified Domain Name (FQDN) if available.1185#1186# Tightly configured MTAa require that a caller sends a real DNS1187# domain name that corresponds the IP address in the HELO/EHLO1188# handshake. This is used to verify the connection and prevent1189# spammers from trying to hide their identity. If the DNS and IP don't1190# match, the receiveing MTA may deny the connection.1191#1192# Here is a deny example of Net::SMTP with the default "localhost.localdomain"1193#1194# Net::SMTP=GLOB(0x267ec28)>>> EHLO localhost.localdomain1195# Net::SMTP=GLOB(0x267ec28)<<< 550 EHLO argument does not match calling host1196#1197# This maildomain*() code is based on ideas in Perl library Test::Reporter1198# /usr/share/perl5/Test/Reporter/Mail/Util.pm ==> sub _maildomain ()11991200sub valid_fqdn {1201my$domain=shift;1202returndefined$domain&& !($^Oeq'darwin'&&$domain=~/\.local$/) &&$domain=~/\./;1203}12041205sub maildomain_net {1206my$maildomain;12071208my$domain= Net::Domain::domainname();1209$maildomain=$domainif valid_fqdn($domain);12101211return$maildomain;1212}12131214sub maildomain_mta {1215my$maildomain;12161217formy$host(qw(mailhost localhost)) {1218my$smtp= Net::SMTP->new($host);1219if(defined$smtp) {1220my$domain=$smtp->domain;1221$smtp->quit;12221223$maildomain=$domainif valid_fqdn($domain);12241225last if$maildomain;1226}1227}12281229return$maildomain;1230}12311232sub maildomain {1233return maildomain_net() || maildomain_mta() ||'localhost.localdomain';1234}12351236sub smtp_host_string {1237if(defined$smtp_server_port) {1238return"$smtp_server:$smtp_server_port";1239}else{1240return$smtp_server;1241}1242}12431244# Returns 1 if authentication succeeded or was not necessary1245# (smtp_user was not specified), and 0 otherwise.12461247sub smtp_auth_maybe {1248if(!defined$smtp_authuser||$auth|| (defined$smtp_auth&&$smtp_autheq"none")) {1249return1;1250}12511252# Workaround AUTH PLAIN/LOGIN interaction defect1253# with Authen::SASL::Cyrus1254eval{1255require Authen::SASL;1256 Authen::SASL->import(qw(Perl));1257};12581259# Check mechanism naming as defined in:1260# https://tools.ietf.org/html/rfc4422#page-81261if($smtp_auth&&$smtp_auth!~/^(\b[A-Z0-9-_]{1,20}\s*)*$/) {1262die"invalid smtp auth: '${smtp_auth}'";1263}12641265# TODO: Authentication may fail not because credentials were1266# invalid but due to other reasons, in which we should not1267# reject credentials.1268$auth= Git::credential({1269'protocol'=>'smtp',1270'host'=> smtp_host_string(),1271'username'=>$smtp_authuser,1272# if there's no password, "git credential fill" will1273# give us one, otherwise it'll just pass this one.1274'password'=>$smtp_authpass1275},sub{1276my$cred=shift;12771278if($smtp_auth) {1279my$sasl= Authen::SASL->new(1280 mechanism =>$smtp_auth,1281 callback => {1282 user =>$cred->{'username'},1283 pass =>$cred->{'password'},1284 authname =>$cred->{'username'},1285}1286);12871288return!!$smtp->auth($sasl);1289}12901291return!!$smtp->auth($cred->{'username'},$cred->{'password'});1292});12931294return$auth;1295}12961297sub ssl_verify_params {1298eval{1299require IO::Socket::SSL;1300 IO::Socket::SSL->import(qw/SSL_VERIFY_PEER SSL_VERIFY_NONE/);1301};1302if($@) {1303print STDERR "Not using SSL_VERIFY_PEER due to out-of-date IO::Socket::SSL.\n";1304return;1305}13061307if(!defined$smtp_ssl_cert_path) {1308# use the OpenSSL defaults1309return(SSL_verify_mode => SSL_VERIFY_PEER());1310}13111312if($smtp_ssl_cert_patheq"") {1313return(SSL_verify_mode => SSL_VERIFY_NONE());1314}elsif(-d $smtp_ssl_cert_path) {1315return(SSL_verify_mode => SSL_VERIFY_PEER(),1316 SSL_ca_path =>$smtp_ssl_cert_path);1317}elsif(-f $smtp_ssl_cert_path) {1318return(SSL_verify_mode => SSL_VERIFY_PEER(),1319 SSL_ca_file =>$smtp_ssl_cert_path);1320}else{1321die sprintf(__("CA path\"%s\"does not exist"),$smtp_ssl_cert_path);1322}1323}13241325sub file_name_is_absolute {1326my($path) =@_;13271328# msys does not grok DOS drive-prefixes1329if($^Oeq'msys') {1330return($path=~ m#^/# || $path =~ m#^[a-zA-Z]\:#)1331}13321333require File::Spec::Functions;1334return File::Spec::Functions::file_name_is_absolute($path);1335}13361337# Prepares the email, then asks the user what to do.1338#1339# If the user chooses to send the email, it's sent and 1 is returned.1340# If the user chooses not to send the email, 0 is returned.1341# If the user decides they want to make further edits, -1 is returned and the1342# caller is expected to call send_message again after the edits are performed.1343#1344# If an error occurs sending the email, this just dies.13451346sub send_message {1347my@recipients= unique_email_list(@to);1348@cc= (grep{my$cc= extract_valid_address_or_die($_);1349not grep{$cceq$_||$_=~/<\Q${cc}\E>$/}@recipients1350}1351@cc);1352my$to=join(",\n\t",@recipients);1353@recipients= unique_email_list(@recipients,@cc,@bcclist);1354@recipients= (map{ extract_valid_address_or_die($_) }@recipients);1355my$date= format_2822_time($time++);1356my$gitversion='@@GIT_VERSION@@';1357if($gitversion=~m/..GIT_VERSION../) {1358$gitversion= Git::version();1359}13601361my$cc=join(",\n\t", unique_email_list(@cc));1362my$ccline="";1363if($ccne'') {1364$ccline="\nCc:$cc";1365}1366 make_message_id()unlessdefined($message_id);13671368my$header="From:$sender1369To:$to${ccline}1370Subject:$subject1371Date:$date1372Message-Id:$message_id1373";1374if($use_xmailer) {1375$header.="X-Mailer: git-send-email$gitversion\n";1376}1377if($in_reply_to) {13781379$header.="In-Reply-To:$in_reply_to\n";1380$header.="References:$references\n";1381}1382if($reply_to) {1383$header.="Reply-To:$reply_to\n";1384}1385if(@xh) {1386$header.=join("\n",@xh) ."\n";1387}13881389my@sendmail_parameters= ('-i',@recipients);1390my$raw_from=$sender;1391if(defined$envelope_sender&&$envelope_senderne"auto") {1392$raw_from=$envelope_sender;1393}1394$raw_from= extract_valid_address($raw_from);1395unshift(@sendmail_parameters,1396'-f',$raw_from)if(defined$envelope_sender);13971398if($needs_confirm&& !$dry_run) {1399print"\n$header\n";1400if($needs_confirmeq"inform") {1401$confirm_unconfigured=0;# squelch this message for the rest of this run1402$ask_default="y";# assume yes on EOF since user hasn't explicitly asked for confirmation1403print __ <<EOF ;1404 The Cc list above has been expanded by additional1405 addresses found in the patch commit message. By default1406 send-email prompts before sending whenever this occurs.1407 This behavior is controlled by the sendemail.confirm1408 configuration setting.14091410 For additional information, run 'git send-email --help'.1411 To retain the current behavior, but squelch this message,1412 run 'git config --global sendemail.confirm auto'.14131414EOF1415}1416# TRANSLATORS: Make sure to include [y] [n] [e] [q] [a] in your1417# translation. The program will only accept English input1418# at this point.1419$_= ask(__("Send this email? ([y]es|[n]o|[e]dit|[q]uit|[a]ll): "),1420 valid_re =>qr/^(?:yes|y|no|n|edit|e|quit|q|all|a)/i,1421default=>$ask_default);1422die __("Send this email reply required")unlessdefined$_;1423if(/^n/i) {1424return0;1425}elsif(/^e/i) {1426return-1;1427}elsif(/^q/i) {1428 cleanup_compose_files();1429exit(0);1430}elsif(/^a/i) {1431$confirm='never';1432}1433}14341435unshift(@sendmail_parameters,@smtp_server_options);14361437if($dry_run) {1438# We don't want to send the email.1439}elsif(file_name_is_absolute($smtp_server)) {1440my$pid=open my$sm,'|-';1441defined$pidor die$!;1442if(!$pid) {1443exec($smtp_server,@sendmail_parameters)or die$!;1444}1445print$sm"$header\n$message";1446close$smor die$!;1447}else{14481449if(!defined$smtp_server) {1450die __("The required SMTP server is not properly defined.")1451}14521453require Net::SMTP;1454my$use_net_smtp_ssl= version->parse($Net::SMTP::VERSION) < version->parse("2.34");1455$smtp_domain||= maildomain();14561457if($smtp_encryptioneq'ssl') {1458$smtp_server_port||=465;# ssmtp1459require IO::Socket::SSL;14601461# Suppress "variable accessed once" warning.1462{1463no warnings 'once';1464$IO::Socket::SSL::DEBUG =1;1465}14661467# Net::SMTP::SSL->new() does not forward any SSL options1468 IO::Socket::SSL::set_client_defaults(1469 ssl_verify_params());14701471if($use_net_smtp_ssl) {1472require Net::SMTP::SSL;1473$smtp||= Net::SMTP::SSL->new($smtp_server,1474 Hello =>$smtp_domain,1475 Port =>$smtp_server_port,1476 Debug =>$debug_net_smtp);1477}1478else{1479$smtp||= Net::SMTP->new($smtp_server,1480 Hello =>$smtp_domain,1481 Port =>$smtp_server_port,1482 Debug =>$debug_net_smtp,1483 SSL =>1);1484}1485}1486elsif(!$smtp) {1487$smtp_server_port||=25;1488$smtp||= Net::SMTP->new($smtp_server,1489 Hello =>$smtp_domain,1490 Debug =>$debug_net_smtp,1491 Port =>$smtp_server_port);1492if($smtp_encryptioneq'tls'&&$smtp) {1493if($use_net_smtp_ssl) {1494$smtp->command('STARTTLS');1495$smtp->response();1496if($smtp->code!=220) {1497die sprintf(__("Server does not support STARTTLS!%s"),$smtp->message);1498}1499require Net::SMTP::SSL;1500$smtp= Net::SMTP::SSL->start_SSL($smtp,1501 ssl_verify_params())1502or die sprintf(__("STARTTLS failed!%s"), IO::Socket::SSL::errstr());1503}1504else{1505$smtp->starttls(ssl_verify_params())1506or die sprintf(__("STARTTLS failed!%s"), IO::Socket::SSL::errstr());1507}1508# Send EHLO again to receive fresh1509# supported commands1510$smtp->hello($smtp_domain);1511}1512}15131514if(!$smtp) {1515die __("Unable to initialize SMTP properly. Check config and use --smtp-debug."),1516" VALUES: server=$smtp_server",1517"encryption=$smtp_encryption",1518"hello=$smtp_domain",1519defined$smtp_server_port?" port=$smtp_server_port":"";1520}15211522 smtp_auth_maybe or die$smtp->message;15231524$smtp->mail($raw_from)or die$smtp->message;1525$smtp->to(@recipients)or die$smtp->message;1526$smtp->dataor die$smtp->message;1527$smtp->datasend("$header\n")or die$smtp->message;1528my@lines=split/^/,$message;1529foreachmy$line(@lines) {1530$smtp->datasend("$line")or die$smtp->message;1531}1532$smtp->dataend()or die$smtp->message;1533$smtp->code=~/250|200/or die sprintf(__("Failed to send%s\n"),$subject).$smtp->message;1534}1535if($quiet) {1536printf($dry_run? __("Dry-Sent%s\n") : __("Sent%s\n"),$subject);1537}else{1538print($dry_run? __("Dry-OK. Log says:\n") : __("OK. Log says:\n"));1539if(!file_name_is_absolute($smtp_server)) {1540print"Server:$smtp_server\n";1541print"MAIL FROM:<$raw_from>\n";1542foreachmy$entry(@recipients) {1543print"RCPT TO:<$entry>\n";1544}1545}else{1546print"Sendmail:$smtp_server".join(' ',@sendmail_parameters)."\n";1547}1548print$header,"\n";1549if($smtp) {1550print __("Result: "),$smtp->code,' ',1551($smtp->message=~/\n([^\n]+\n)$/s),"\n";1552}else{1553print __("Result: OK\n");1554}1555}15561557return1;1558}15591560$in_reply_to=$initial_in_reply_to;1561$references=$initial_in_reply_to||'';1562$subject=$initial_subject;1563$message_num=0;15641565# Prepares the email, prompts the user, sends it out1566# Returns 0 if an edit was done and the function should be called again, or 11567# otherwise.1568sub process_file {1569my($t) =@_;15701571open my$fh,"<",$tor die sprintf(__("can't open file%s"),$t);15721573my$author=undef;1574my$sauthor=undef;1575my$author_encoding;1576my$has_content_type;1577my$body_encoding;1578my$xfer_encoding;1579my$has_mime_version;1580@to= ();1581@cc= ();1582@xh= ();1583my$input_format=undef;1584my@header= ();1585$message="";1586$message_num++;1587# First unfold multiline header fields1588while(<$fh>) {1589last if/^\s*$/;1590if(/^\s+\S/and@header) {1591chomp($header[$#header]);1592s/^\s+/ /;1593$header[$#header] .=$_;1594}else{1595push(@header,$_);1596}1597}1598# Now parse the header1599foreach(@header) {1600if(/^From /) {1601$input_format='mbox';1602next;1603}1604chomp;1605if(!defined$input_format&&/^[-A-Za-z]+:\s/) {1606$input_format='mbox';1607}16081609if(defined$input_format&&$input_formateq'mbox') {1610if(/^Subject:\s+(.*)$/i) {1611$subject=$1;1612}1613elsif(/^From:\s+(.*)$/i) {1614($author,$author_encoding) = unquote_rfc2047($1);1615$sauthor= sanitize_address($author);1616next if$suppress_cc{'author'};1617next if$suppress_cc{'self'}and$sauthoreq$sender;1618printf(__("(mbox) Adding cc:%sfrom line '%s'\n"),1619$1,$_)unless$quiet;1620push@cc,$1;1621}1622elsif(/^To:\s+(.*)$/i) {1623foreachmy$addr(parse_address_line($1)) {1624printf(__("(mbox) Adding to:%sfrom line '%s'\n"),1625$addr,$_)unless$quiet;1626push@to,$addr;1627}1628}1629elsif(/^Cc:\s+(.*)$/i) {1630foreachmy$addr(parse_address_line($1)) {1631my$qaddr= unquote_rfc2047($addr);1632my$saddr= sanitize_address($qaddr);1633if($saddreq$sender) {1634next if($suppress_cc{'self'});1635}else{1636next if($suppress_cc{'cc'});1637}1638printf(__("(mbox) Adding cc:%sfrom line '%s'\n"),1639$addr,$_)unless$quiet;1640push@cc,$addr;1641}1642}1643elsif(/^Content-type:/i) {1644$has_content_type=1;1645if(/charset="?([^ "]+)/) {1646$body_encoding=$1;1647}1648push@xh,$_;1649}1650elsif(/^MIME-Version/i) {1651$has_mime_version=1;1652push@xh,$_;1653}1654elsif(/^Message-Id: (.*)/i) {1655$message_id=$1;1656}1657elsif(/^Content-Transfer-Encoding: (.*)/i) {1658$xfer_encoding=$1ifnot defined$xfer_encoding;1659}1660elsif(/^In-Reply-To: (.*)/i) {1661$in_reply_to=$1;1662}1663elsif(/^References: (.*)/i) {1664$references=$1;1665}1666elsif(!/^Date:\s/i&&/^[-A-Za-z]+:\s+\S/) {1667push@xh,$_;1668}1669}else{1670# In the traditional1671# "send lots of email" format,1672# line 1 = cc1673# line 2 = subject1674# So let's support that, too.1675$input_format='lots';1676if(@cc==0&& !$suppress_cc{'cc'}) {1677printf(__("(non-mbox) Adding cc:%sfrom line '%s'\n"),1678$_,$_)unless$quiet;1679push@cc,$_;1680}elsif(!defined$subject) {1681$subject=$_;1682}1683}1684}1685# Now parse the message body1686while(<$fh>) {1687$message.=$_;1688if(/^([a-z-]*-by|Cc): (.*)/i) {1689chomp;1690my($what,$c) = ($1,$2);1691# strip garbage for the address we'll use:1692$c= strip_garbage_one_address($c);1693# sanitize a bit more to decide whether to suppress the address:1694my$sc= sanitize_address($c);1695if($sceq$sender) {1696next if($suppress_cc{'self'});1697}else{1698if($what=~/^Signed-off-by$/i) {1699next if$suppress_cc{'sob'};1700}elsif($what=~/-by$/i) {1701next if$suppress_cc{'misc-by'};1702}elsif($what=~/Cc/i) {1703next if$suppress_cc{'bodycc'};1704}1705}1706if($c!~/.+@.+|<.+>/) {1707printf("(body) Ignoring%sfrom line '%s'\n",1708$what,$_)unless$quiet;1709next;1710}1711push@cc,$c;1712printf(__("(body) Adding cc:%sfrom line '%s'\n"),1713$c,$_)unless$quiet;1714}1715}1716close$fh;17171718push@to, recipients_cmd("to-cmd","to",$to_cmd,$t)1719ifdefined$to_cmd;1720push@cc, recipients_cmd("cc-cmd","cc",$cc_cmd,$t)1721ifdefined$cc_cmd&& !$suppress_cc{'cccmd'};17221723if($broken_encoding{$t} && !$has_content_type) {1724$xfer_encoding='8bit'ifnot defined$xfer_encoding;1725$has_content_type=1;1726push@xh,"Content-Type: text/plain; charset=$auto_8bit_encoding";1727$body_encoding=$auto_8bit_encoding;1728}17291730if($broken_encoding{$t} && !is_rfc2047_quoted($subject)) {1731$subject= quote_subject($subject,$auto_8bit_encoding);1732}17331734if(defined$sauthorand$sauthorne$sender) {1735$message="From:$author\n\n$message";1736if(defined$author_encoding) {1737if($has_content_type) {1738if($body_encodingeq$author_encoding) {1739# ok, we already have the right encoding1740}1741else{1742# uh oh, we should re-encode1743}1744}1745else{1746$xfer_encoding='8bit'ifnot defined$xfer_encoding;1747$has_content_type=1;1748push@xh,1749"Content-Type: text/plain; charset=$author_encoding";1750}1751}1752}1753$xfer_encoding='8bit'ifnot defined$xfer_encoding;1754($message,$xfer_encoding) = apply_transfer_encoding(1755$message,$xfer_encoding,$target_xfer_encoding);1756push@xh,"Content-Transfer-Encoding:$xfer_encoding";1757unshift@xh,'MIME-Version: 1.0'unless$has_mime_version;17581759$needs_confirm= (1760$confirmeq"always"or1761($confirm=~/^(?:auto|cc)$/&&@cc)or1762($confirm=~/^(?:auto|compose)$/&&$compose&&$message_num==1));1763$needs_confirm="inform"if($needs_confirm&&$confirm_unconfigured&&@cc);17641765@to= process_address_list(@to);1766@cc= process_address_list(@cc);17671768@to= (@initial_to,@to);1769@cc= (@initial_cc,@cc);17701771if($message_num==1) {1772if(defined$cover_ccand$cover_cc) {1773@initial_cc=@cc;1774}1775if(defined$cover_toand$cover_to) {1776@initial_to=@to;1777}1778}17791780my$message_was_sent= send_message();1781if($message_was_sent== -1) {1782 do_edit($t);1783return0;1784}17851786# set up for the next message1787if($thread&&$message_was_sent&&1788($chain_reply_to|| !defined$in_reply_to||length($in_reply_to) ==0||1789$message_num==1)) {1790$in_reply_to=$message_id;1791if(length$references>0) {1792$references.="\n$message_id";1793}else{1794$references="$message_id";1795}1796}1797$message_id=undef;1798$num_sent++;1799if(defined$batch_size&&$num_sent==$batch_size) {1800$num_sent=0;1801$smtp->quitifdefined$smtp;1802undef$smtp;1803undef$auth;1804sleep($relogin_delay)ifdefined$relogin_delay;1805}18061807return1;1808}18091810foreachmy$t(@files) {1811while(!process_file($t)) {1812# user edited the file1813}1814}18151816# Execute a command (e.g. $to_cmd) to get a list of email addresses1817# and return a results array1818sub recipients_cmd {1819my($prefix,$what,$cmd,$file) =@_;18201821my@addresses= ();1822open my$fh,"-|","$cmd\Q$file\E"1823or die sprintf(__("(%s) Could not execute '%s'"),$prefix,$cmd);1824while(my$address= <$fh>) {1825$address=~s/^\s*//g;1826$address=~s/\s*$//g;1827$address= sanitize_address($address);1828next if($addresseq$senderand$suppress_cc{'self'});1829push@addresses,$address;1830printf(__("(%s) Adding%s:%sfrom: '%s'\n"),1831$prefix,$what,$address,$cmd)unless$quiet;1832}1833close$fh1834or die sprintf(__("(%s) failed to close pipe to '%s'"),$prefix,$cmd);1835return@addresses;1836}18371838cleanup_compose_files();18391840sub cleanup_compose_files {1841unlink($compose_filename,$compose_filename.".final")if$compose;1842}18431844$smtp->quitif$smtp;18451846sub apply_transfer_encoding {1847my$message=shift;1848my$from=shift;1849my$to=shift;18501851return$messageif($fromeq$toand$fromne'7bit');18521853require MIME::QuotedPrint;1854require MIME::Base64;18551856$message= MIME::QuotedPrint::decode($message)1857if($fromeq'quoted-printable');1858$message= MIME::Base64::decode($message)1859if($fromeq'base64');18601861$to= ($message=~/.{999,}/) ?'quoted-printable':'8bit'1862if$toeq'auto';18631864die __("cannot send message as 7bit")1865if($toeq'7bit'and$message=~/[^[:ascii:]]/);1866return($message,$to)1867if($toeq'7bit'or$toeq'8bit');1868return(MIME::QuotedPrint::encode($message,"\n",0),$to)1869if($toeq'quoted-printable');1870return(MIME::Base64::encode($message,"\n"),$to)1871if($toeq'base64');1872die __("invalid transfer encoding");1873}18741875sub unique_email_list {1876my%seen;1877my@emails;18781879foreachmy$entry(@_) {1880my$clean= extract_valid_address_or_die($entry);1881$seen{$clean} ||=0;1882next if$seen{$clean}++;1883push@emails,$entry;1884}1885return@emails;1886}18871888sub validate_patch {1889my($fn,$xfer_encoding) =@_;18901891if($repo) {1892my$validate_hook= catfile(catdir($repo->repo_path(),'hooks'),1893'sendemail-validate');1894my$hook_error;1895if(-x $validate_hook) {1896my$target= abs_path($fn);1897# The hook needs a correct cwd and GIT_DIR.1898my$cwd_save= cwd();1899chdir($repo->wc_path()or$repo->repo_path())1900or die("chdir:$!");1901local$ENV{"GIT_DIR"} =$repo->repo_path();1902$hook_error="rejected by sendemail-validate hook"1903ifsystem($validate_hook,$target);1904chdir($cwd_save)or die("chdir:$!");1905}1906return$hook_errorif$hook_error;1907}19081909# Any long lines will be automatically fixed if we use a suitable transfer1910# encoding.1911unless($xfer_encoding=~/^(?:auto|quoted-printable|base64)$/) {1912open(my$fh,'<',$fn)1913or die sprintf(__("unable to open%s:%s\n"),$fn,$!);1914while(my$line= <$fh>) {1915if(length($line) >998) {1916returnsprintf(__("%s: patch contains a line longer than 998 characters"), $.);1917}1918}1919}1920return;1921}19221923sub handle_backup {1924my($last,$lastlen,$file,$known_suffix) =@_;1925my($suffix,$skip);19261927$skip=0;1928if(defined$last&&1929($lastlen<length($file)) &&1930(substr($file,0,$lastlen)eq$last) &&1931($suffix=substr($file,$lastlen)) !~/^[a-z0-9]/i) {1932if(defined$known_suffix&&$suffixeq$known_suffix) {1933printf(__("Skipping%swith backup suffix '%s'.\n"),$file,$known_suffix);1934$skip=1;1935}else{1936# TRANSLATORS: please keep "[y|N]" as is.1937my$answer= ask(sprintf(__("Do you really want to send%s?[y|N]: "),$file),1938 valid_re =>qr/^(?:y|n)/i,1939default=>'n');1940$skip= ($answerne'y');1941if($skip) {1942$known_suffix=$suffix;1943}1944}1945}1946return($skip,$known_suffix);1947}19481949sub handle_backup_files {1950my@file=@_;1951my($last,$lastlen,$known_suffix,$skip,@result);1952formy$file(@file) {1953($skip,$known_suffix) = handle_backup($last,$lastlen,1954$file,$known_suffix);1955push@result,$fileunless$skip;1956$last=$file;1957$lastlen=length($file);1958}1959return@result;1960}19611962sub file_has_nonascii {1963my$fn=shift;1964open(my$fh,'<',$fn)1965or die sprintf(__("unable to open%s:%s\n"),$fn,$!);1966while(my$line= <$fh>) {1967return1if$line=~/[^[:ascii:]]/;1968}1969return0;1970}19711972sub body_or_subject_has_nonascii {1973my$fn=shift;1974open(my$fh,'<',$fn)1975or die sprintf(__("unable to open%s:%s\n"),$fn,$!);1976while(my$line= <$fh>) {1977last if$line=~/^$/;1978return1if$line=~/^Subject.*[^[:ascii:]]/;1979}1980while(my$line= <$fh>) {1981return1if$line=~/[^[:ascii:]]/;1982}1983return0;1984}