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, misc-by, all. 98 --[no-]cc-cover * Email Cc: addresses in the cover letter. 99 --[no-]to-cover * Email To: addresses in the cover letter. 100 --[no-]signed-off-by-cc * Send to Signed-off-by: addresses. Default on. 101 --[no-]suppress-from * Send to self. Default off. 102 --[no-]chain-reply-to * Chain In-Reply-To: fields. Default off. 103 --[no-]thread * Use In-Reply-To: field. Default on. 104 105 Administering: 106 --confirm <str> * Confirm recipients before sending; 107 auto, cc, compose, always, or never. 108 --quiet * Output one line of info per email. 109 --dry-run * Don't actually send the emails. 110 --[no-]validate * Perform patch sanity checks. Default on. 111 --[no-]format-patch * understand any non optional arguments as 112 `git format-patch` ones. 113 --force * Send even if safety checks would prevent it. 114 115 Information: 116 --dump-aliases * Dump configured aliases and exit. 117 118EOT 119exit(1); 120} 121 122# most mail servers generate the Date: header, but not all... 123sub format_2822_time { 124my($time) =@_; 125my@localtm=localtime($time); 126my@gmttm=gmtime($time); 127my$localmin=$localtm[1] +$localtm[2] *60; 128my$gmtmin=$gmttm[1] +$gmttm[2] *60; 129if($localtm[0] !=$gmttm[0]) { 130die __("local zone differs from GMT by a non-minute interval\n"); 131} 132if((($gmttm[6] +1) %7) ==$localtm[6]) { 133$localmin+=1440; 134}elsif((($gmttm[6] -1) %7) ==$localtm[6]) { 135$localmin-=1440; 136}elsif($gmttm[6] !=$localtm[6]) { 137die __("local time offset greater than or equal to 24 hours\n"); 138} 139my$offset=$localmin-$gmtmin; 140my$offhour=$offset/60; 141my$offmin=abs($offset%60); 142if(abs($offhour) >=24) { 143die __("local time offset greater than or equal to 24 hours\n"); 144} 145 146returnsprintf("%s,%2d%s%d%02d:%02d:%02d%s%02d%02d", 147qw(Sun Mon Tue Wed Thu Fri Sat)[$localtm[6]], 148$localtm[3], 149qw(Jan Feb Mar Apr May Jun 150 Jul Aug Sep Oct Nov Dec)[$localtm[4]], 151$localtm[5]+1900, 152$localtm[2], 153$localtm[1], 154$localtm[0], 155($offset>=0) ?'+':'-', 156abs($offhour), 157$offmin, 158); 159} 160 161my$have_email_valid=eval{require Email::Valid;1}; 162my$smtp; 163my$auth; 164my$num_sent=0; 165 166# Regexes for RFC 2047 productions. 167my$re_token=qr/[^][()<>@,;:\\"\/?.=\000-\037\177-\377]+/; 168my$re_encoded_text=qr/[^? \000-\037\177-\377]+/; 169my$re_encoded_word=qr/=\?($re_token)\?($re_token)\?($re_encoded_text)\?=/; 170 171# Variables we fill in automatically, or via prompting: 172my(@to,$no_to,@initial_to,@cc,$no_cc,@initial_cc,@bcclist,$no_bcc,@xh, 173$initial_in_reply_to,$reply_to,$initial_subject,@files, 174$author,$sender,$smtp_authpass,$annotate,$use_xmailer,$compose,$time); 175 176my$envelope_sender; 177 178# Example reply to: 179#$initial_in_reply_to = ''; #<20050203173208.GA23964@foobar.com>'; 180 181my$repo=eval{ Git->repository() }; 182my@repo=$repo? ($repo) : (); 183my$term=eval{ 184$ENV{"GIT_SEND_EMAIL_NOTTY"} 185? new Term::ReadLine 'git-send-email', \*STDIN, \*STDOUT 186: new Term::ReadLine 'git-send-email'; 187}; 188if($@) { 189$term= new FakeTerm "$@: going non-interactive"; 190} 191 192# Behavior modification variables 193my($quiet,$dry_run) = (0,0); 194my$format_patch; 195my$compose_filename; 196my$force=0; 197my$dump_aliases=0; 198 199# Handle interactive edition of files. 200my$multiedit; 201my$editor; 202 203sub do_edit { 204if(!defined($editor)) { 205$editor= Git::command_oneline('var','GIT_EDITOR'); 206} 207if(defined($multiedit) && !$multiedit) { 208map{ 209system('sh','-c',$editor.' "$@"',$editor,$_); 210if(($?&127) || ($?>>8)) { 211die(__("the editor exited uncleanly, aborting everything")); 212} 213}@_; 214}else{ 215system('sh','-c',$editor.' "$@"',$editor,@_); 216if(($?&127) || ($?>>8)) { 217die(__("the editor exited uncleanly, aborting everything")); 218} 219} 220} 221 222# Variables with corresponding config settings 223my($thread,$chain_reply_to,$suppress_from,$signed_off_by_cc); 224my($cover_cc,$cover_to); 225my($to_cmd,$cc_cmd); 226my($smtp_server,$smtp_server_port,@smtp_server_options); 227my($smtp_authuser,$smtp_encryption,$smtp_ssl_cert_path); 228my($batch_size,$relogin_delay); 229my($identity,$aliasfiletype,@alias_files,$smtp_domain,$smtp_auth); 230my($validate,$confirm); 231my(@suppress_cc); 232my($auto_8bit_encoding); 233my($compose_encoding); 234my$target_xfer_encoding='auto'; 235 236my($debug_net_smtp) =0;# Net::SMTP, see send_message() 237 238my%config_bool_settings= ( 239"thread"=> [\$thread,1], 240"chainreplyto"=> [\$chain_reply_to,0], 241"suppressfrom"=> [\$suppress_from,undef], 242"signedoffbycc"=> [\$signed_off_by_cc,undef], 243"cccover"=> [\$cover_cc,undef], 244"tocover"=> [\$cover_to,undef], 245"signedoffcc"=> [\$signed_off_by_cc,undef],# Deprecated 246"validate"=> [\$validate,1], 247"multiedit"=> [\$multiedit,undef], 248"annotate"=> [\$annotate,undef], 249"xmailer"=> [\$use_xmailer,1] 250); 251 252my%config_settings= ( 253"smtpserver"=> \$smtp_server, 254"smtpserverport"=> \$smtp_server_port, 255"smtpserveroption"=> \@smtp_server_options, 256"smtpuser"=> \$smtp_authuser, 257"smtppass"=> \$smtp_authpass, 258"smtpdomain"=> \$smtp_domain, 259"smtpauth"=> \$smtp_auth, 260"smtpbatchsize"=> \$batch_size, 261"smtprelogindelay"=> \$relogin_delay, 262"to"=> \@initial_to, 263"tocmd"=> \$to_cmd, 264"cc"=> \@initial_cc, 265"cccmd"=> \$cc_cmd, 266"aliasfiletype"=> \$aliasfiletype, 267"bcc"=> \@bcclist, 268"suppresscc"=> \@suppress_cc, 269"envelopesender"=> \$envelope_sender, 270"confirm"=> \$confirm, 271"from"=> \$sender, 272"assume8bitencoding"=> \$auto_8bit_encoding, 273"composeencoding"=> \$compose_encoding, 274"transferencoding"=> \$target_xfer_encoding, 275); 276 277my%config_path_settings= ( 278"aliasesfile"=> \@alias_files, 279"smtpsslcertpath"=> \$smtp_ssl_cert_path, 280); 281 282# Handle Uncouth Termination 283sub signal_handler { 284 285# Make text normal 286print color("reset"),"\n"; 287 288# SMTP password masked 289system"stty echo"; 290 291# tmp files from --compose 292if(defined$compose_filename) { 293if(-e $compose_filename) { 294printf __("'%s' contains an intermediate version ". 295"of the email you were composing.\n"), 296$compose_filename; 297} 298if(-e ($compose_filename.".final")) { 299printf __("'%s.final' contains the composed email.\n"), 300$compose_filename; 301} 302} 303 304exit; 305}; 306 307$SIG{TERM} = \&signal_handler; 308$SIG{INT} = \&signal_handler; 309 310# Begin by accumulating all the variables (defined above), that we will end up 311# needing, first, from the command line: 312 313my$help; 314my$rc= GetOptions("h"=> \$help, 315"dump-aliases"=> \$dump_aliases); 316usage()unless$rc; 317die __("--dump-aliases incompatible with other options\n") 318if!$helpand$dump_aliasesand@ARGV; 319$rc= GetOptions( 320"sender|from=s"=> \$sender, 321"in-reply-to=s"=> \$initial_in_reply_to, 322"reply-to=s"=> \$reply_to, 323"subject=s"=> \$initial_subject, 324"to=s"=> \@initial_to, 325"to-cmd=s"=> \$to_cmd, 326"no-to"=> \$no_to, 327"cc=s"=> \@initial_cc, 328"no-cc"=> \$no_cc, 329"bcc=s"=> \@bcclist, 330"no-bcc"=> \$no_bcc, 331"chain-reply-to!"=> \$chain_reply_to, 332"no-chain-reply-to"=>sub{$chain_reply_to=0}, 333"smtp-server=s"=> \$smtp_server, 334"smtp-server-option=s"=> \@smtp_server_options, 335"smtp-server-port=s"=> \$smtp_server_port, 336"smtp-user=s"=> \$smtp_authuser, 337"smtp-pass:s"=> \$smtp_authpass, 338"smtp-ssl"=>sub{$smtp_encryption='ssl'}, 339"smtp-encryption=s"=> \$smtp_encryption, 340"smtp-ssl-cert-path=s"=> \$smtp_ssl_cert_path, 341"smtp-debug:i"=> \$debug_net_smtp, 342"smtp-domain:s"=> \$smtp_domain, 343"smtp-auth=s"=> \$smtp_auth, 344"identity=s"=> \$identity, 345"annotate!"=> \$annotate, 346"no-annotate"=>sub{$annotate=0}, 347"compose"=> \$compose, 348"quiet"=> \$quiet, 349"cc-cmd=s"=> \$cc_cmd, 350"suppress-from!"=> \$suppress_from, 351"no-suppress-from"=>sub{$suppress_from=0}, 352"suppress-cc=s"=> \@suppress_cc, 353"signed-off-cc|signed-off-by-cc!"=> \$signed_off_by_cc, 354"no-signed-off-cc|no-signed-off-by-cc"=>sub{$signed_off_by_cc=0}, 355"cc-cover|cc-cover!"=> \$cover_cc, 356"no-cc-cover"=>sub{$cover_cc=0}, 357"to-cover|to-cover!"=> \$cover_to, 358"no-to-cover"=>sub{$cover_to=0}, 359"confirm=s"=> \$confirm, 360"dry-run"=> \$dry_run, 361"envelope-sender=s"=> \$envelope_sender, 362"thread!"=> \$thread, 363"no-thread"=>sub{$thread=0}, 364"validate!"=> \$validate, 365"no-validate"=>sub{$validate=0}, 366"transfer-encoding=s"=> \$target_xfer_encoding, 367"format-patch!"=> \$format_patch, 368"no-format-patch"=>sub{$format_patch=0}, 369"8bit-encoding=s"=> \$auto_8bit_encoding, 370"compose-encoding=s"=> \$compose_encoding, 371"force"=> \$force, 372"xmailer!"=> \$use_xmailer, 373"no-xmailer"=>sub{$use_xmailer=0}, 374"batch-size=i"=> \$batch_size, 375"relogin-delay=i"=> \$relogin_delay, 376); 377 378usage()if$help; 379unless($rc) { 380 usage(); 381} 382 383die __("Cannot run git format-patch from outside a repository\n") 384if$format_patchand not$repo; 385 386die __("`batch-size` and `relogin` must be specified together ". 387"(via command-line or configuration option)\n") 388ifdefined$relogin_delayand not defined$batch_size; 389 390# Now, let's fill any that aren't set in with defaults: 391 392sub read_config { 393my($prefix) =@_; 394 395foreachmy$setting(keys%config_bool_settings) { 396my$target=$config_bool_settings{$setting}->[0]; 397$$target= Git::config_bool(@repo,"$prefix.$setting")unless(defined$$target); 398} 399 400foreachmy$setting(keys%config_path_settings) { 401my$target=$config_path_settings{$setting}; 402if(ref($target)eq"ARRAY") { 403unless(@$target) { 404my@values= Git::config_path(@repo,"$prefix.$setting"); 405@$target=@valuesif(@values&&defined$values[0]); 406} 407} 408else{ 409$$target= Git::config_path(@repo,"$prefix.$setting")unless(defined$$target); 410} 411} 412 413foreachmy$setting(keys%config_settings) { 414my$target=$config_settings{$setting}; 415next if$settingeq"to"and defined$no_to; 416next if$settingeq"cc"and defined$no_cc; 417next if$settingeq"bcc"and defined$no_bcc; 418if(ref($target)eq"ARRAY") { 419unless(@$target) { 420my@values= Git::config(@repo,"$prefix.$setting"); 421@$target=@valuesif(@values&&defined$values[0]); 422} 423} 424else{ 425$$target= Git::config(@repo,"$prefix.$setting")unless(defined$$target); 426} 427} 428 429if(!defined$smtp_encryption) { 430my$enc= Git::config(@repo,"$prefix.smtpencryption"); 431if(defined$enc) { 432$smtp_encryption=$enc; 433}elsif(Git::config_bool(@repo,"$prefix.smtpssl")) { 434$smtp_encryption='ssl'; 435} 436} 437} 438 439# read configuration from [sendemail "$identity"], fall back on [sendemail] 440$identity= Git::config(@repo,"sendemail.identity")unless(defined$identity); 441read_config("sendemail.$identity")if(defined$identity); 442read_config("sendemail"); 443 444# fall back on builtin bool defaults 445foreachmy$setting(values%config_bool_settings) { 446${$setting->[0]} =$setting->[1]unless(defined(${$setting->[0]})); 447} 448 449# 'default' encryption is none -- this only prevents a warning 450$smtp_encryption=''unless(defined$smtp_encryption); 451 452# Set CC suppressions 453my(%suppress_cc); 454if(@suppress_cc) { 455foreachmy$entry(@suppress_cc) { 456die sprintf(__("Unknown --suppress-cc field: '%s'\n"),$entry) 457unless$entry=~/^(?:all|cccmd|cc|author|self|sob|body|bodycc|misc-by)$/; 458$suppress_cc{$entry} =1; 459} 460} 461 462if($suppress_cc{'all'}) { 463foreachmy$entry(qw (cccmd cc author self sob body bodycc misc-by)) { 464$suppress_cc{$entry} =1; 465} 466delete$suppress_cc{'all'}; 467} 468 469# If explicit old-style ones are specified, they trump --suppress-cc. 470$suppress_cc{'self'} =$suppress_fromifdefined$suppress_from; 471$suppress_cc{'sob'} = !$signed_off_by_ccifdefined$signed_off_by_cc; 472 473if($suppress_cc{'body'}) { 474foreachmy$entry(qw (sob bodycc misc-by)) { 475$suppress_cc{$entry} =1; 476} 477delete$suppress_cc{'body'}; 478} 479 480# Set confirm's default value 481my$confirm_unconfigured= !defined$confirm; 482if($confirm_unconfigured) { 483$confirm=scalar%suppress_cc?'compose':'auto'; 484}; 485die sprintf(__("Unknown --confirm setting: '%s'\n"),$confirm) 486unless$confirm=~/^(?:auto|cc|compose|always|never)/; 487 488# Debugging, print out the suppressions. 489if(0) { 490print"suppressions:\n"; 491foreachmy$entry(keys%suppress_cc) { 492printf" %-5s ->$suppress_cc{$entry}\n",$entry; 493} 494} 495 496my($repoauthor,$repocommitter); 497($repoauthor) = Git::ident_person(@repo,'author'); 498($repocommitter) = Git::ident_person(@repo,'committer'); 499 500sub parse_address_line { 501returnmap{$_->format} Mail::Address->parse($_[0]); 502} 503 504sub split_addrs { 505return quotewords('\s*,\s*',1,@_); 506} 507 508my%aliases; 509 510sub parse_sendmail_alias { 511local$_=shift; 512if(/"/) { 513printf STDERR __("warning: sendmail alias with quotes is not supported:%s\n"),$_; 514}elsif(/:include:/) { 515printf STDERR __("warning: `:include:` not supported:%s\n"),$_; 516}elsif(/[\/|]/) { 517printf STDERR __("warning: `/file` or `|pipe` redirection not supported:%s\n"),$_; 518}elsif(/^(\S+?)\s*:\s*(.+)$/) { 519my($alias,$addr) = ($1,$2); 520$aliases{$alias} = [ split_addrs($addr) ]; 521}else{ 522printf STDERR __("warning: sendmail line is not recognized:%s\n"),$_; 523} 524} 525 526sub parse_sendmail_aliases { 527my$fh=shift; 528my$s=''; 529while(<$fh>) { 530chomp; 531next if/^\s*$/||/^\s*#/; 532$s.=$_,next if$s=~s/\\$//||s/^\s+//; 533 parse_sendmail_alias($s)if$s; 534$s=$_; 535} 536$s=~s/\\$//;# silently tolerate stray '\' on last line 537 parse_sendmail_alias($s)if$s; 538} 539 540my%parse_alias= ( 541# multiline formats can be supported in the future 542 mutt =>sub{my$fh=shift;while(<$fh>) { 543if(/^\s*alias\s+(?:-group\s+\S+\s+)*(\S+)\s+(.*)$/) { 544my($alias,$addr) = ($1,$2); 545$addr=~s/#.*$//;# mutt allows # comments 546# commas delimit multiple addresses 547my@addr= split_addrs($addr); 548 549# quotes may be escaped in the file, 550# unescape them so we do not double-escape them later. 551s/\\"/"/gforeach@addr; 552$aliases{$alias} = \@addr 553}}}, 554 mailrc =>sub{my$fh=shift;while(<$fh>) { 555if(/^alias\s+(\S+)\s+(.*?)\s*$/) { 556# spaces delimit multiple addresses 557$aliases{$1} = [ quotewords('\s+',0,$2) ]; 558}}}, 559 pine =>sub{my$fh=shift;my$f='\t[^\t]*'; 560for(my$x='';defined($x);$x=$_) { 561chomp$x; 562$x.=$1while(defined($_= <$fh>) &&/^ +(.*)$/); 563$x=~/^(\S+)$f\t\(?([^\t]+?)\)?(:?$f){0,2}$/ornext; 564$aliases{$1} = [ split_addrs($2) ]; 565}}, 566 elm =>sub{my$fh=shift; 567while(<$fh>) { 568if(/^(\S+)\s+=\s+[^=]+=\s(\S+)/) { 569my($alias,$addr) = ($1,$2); 570$aliases{$alias} = [ split_addrs($addr) ]; 571} 572} }, 573 sendmail => \&parse_sendmail_aliases, 574 gnus =>sub{my$fh=shift;while(<$fh>) { 575if(/\(define-mail-alias\s+"(\S+?)"\s+"(\S+?)"\)/) { 576$aliases{$1} = [$2]; 577}}} 578); 579 580if(@alias_filesand$aliasfiletypeand defined$parse_alias{$aliasfiletype}) { 581foreachmy$file(@alias_files) { 582open my$fh,'<',$fileor die"opening$file:$!\n"; 583$parse_alias{$aliasfiletype}->($fh); 584close$fh; 585} 586} 587 588if($dump_aliases) { 589print"$_\n"for(sort keys%aliases); 590exit(0); 591} 592 593# is_format_patch_arg($f) returns 0 if $f names a patch, or 1 if 594# $f is a revision list specification to be passed to format-patch. 595sub is_format_patch_arg { 596return unless$repo; 597my$f=shift; 598try{ 599$repo->command('rev-parse','--verify','--quiet',$f); 600if(defined($format_patch)) { 601return$format_patch; 602} 603die sprintf(__ <<EOF,$f,$f); 604File '%s' exists but it could also be the range of commits 605to produce patches for. Please disambiguate by... 606 607 * Saying "./%s" if you mean a file; or 608 * Giving --format-patch option if you mean a range. 609EOF 610} catch Git::Error::Command with { 611# Not a valid revision. Treat it as a filename. 612return0; 613} 614} 615 616# Now that all the defaults are set, process the rest of the command line 617# arguments and collect up the files that need to be processed. 618my@rev_list_opts; 619while(defined(my$f=shift@ARGV)) { 620if($feq"--") { 621push@rev_list_opts,"--",@ARGV; 622@ARGV= (); 623}elsif(-d $fand!is_format_patch_arg($f)) { 624opendir my$dh,$f 625or die sprintf(__("Failed to opendir%s:%s"),$f,$!); 626 627push@files,grep{ -f $_}map{ catfile($f,$_) } 628sort readdir$dh; 629closedir$dh; 630}elsif((-f $for-p $f)and!is_format_patch_arg($f)) { 631push@files,$f; 632}else{ 633push@rev_list_opts,$f; 634} 635} 636 637if(@rev_list_opts) { 638die __("Cannot run git format-patch from outside a repository\n") 639unless$repo; 640push@files,$repo->command('format-patch','-o', tempdir(CLEANUP =>1),@rev_list_opts); 641} 642 643@files= handle_backup_files(@files); 644 645if($validate) { 646foreachmy$f(@files) { 647unless(-p $f) { 648my$error= validate_patch($f,$target_xfer_encoding); 649$errorand die sprintf(__("fatal:%s:%s\nwarning: no patches were sent\n"), 650$f,$error); 651} 652} 653} 654 655if(@files) { 656unless($quiet) { 657print$_,"\n"for(@files); 658} 659}else{ 660print STDERR __("\nNo patch files specified!\n\n"); 661 usage(); 662} 663 664sub get_patch_subject { 665my$fn=shift; 666open(my$fh,'<',$fn); 667while(my$line= <$fh>) { 668next unless($line=~/^Subject: (.*)$/); 669close$fh; 670return"GIT:$1\n"; 671} 672close$fh; 673die sprintf(__("No subject line in%s?"),$fn); 674} 675 676if($compose) { 677# Note that this does not need to be secure, but we will make a small 678# effort to have it be unique 679$compose_filename= ($repo? 680 tempfile(".gitsendemail.msg.XXXXXX", DIR =>$repo->repo_path()) : 681 tempfile(".gitsendemail.msg.XXXXXX", DIR =>"."))[1]; 682open my$c,">",$compose_filename 683or die sprintf(__("Failed to open for writing%s:%s"),$compose_filename,$!); 684 685 686my$tpl_sender=$sender||$repoauthor||$repocommitter||''; 687my$tpl_subject=$initial_subject||''; 688my$tpl_in_reply_to=$initial_in_reply_to||''; 689my$tpl_reply_to=$reply_to||''; 690 691print$c<<EOT1, Git::prefix_lines("GIT: ", __ <<EOT2), <<EOT3; 692From$tpl_sender# This line is ignored. 693EOT1 694Lines beginning in "GIT:" will be removed. 695Consider including an overall diffstat or table of contents 696for the patch you are writing. 697 698Clear the body content if you don't wish to send a summary. 699EOT2 700From:$tpl_sender 701Reply-To:$tpl_reply_to 702Subject:$tpl_subject 703In-Reply-To:$tpl_in_reply_to 704 705EOT3 706 for my$f(@files) { 707 print$cget_patch_subject($f); 708 } 709 close$c; 710 711 if ($annotate) { 712 do_edit($compose_filename,@files); 713 } else { 714 do_edit($compose_filename); 715 } 716 717 open$c, "<",$compose_filename 718 or die sprintf(__("Failed to open%s:%s"),$compose_filename,$!); 719 720 if (!defined$compose_encoding) { 721$compose_encoding= "UTF-8"; 722 } 723 724 my%parsed_email; 725 while (my$line= <$c>) { 726 next if$line=~ m/^GIT:/; 727 parse_header_line($line, \%parsed_email); 728 if ($line=~ /^$/) { 729$parsed_email{'body'} = filter_body($c); 730 } 731 } 732 close$c; 733 734 open my$c2, ">",$compose_filename. ".final" 735 or die sprintf(__("Failed to open%s.final:%s"),$compose_filename,$!); 736 737 738 if ($parsed_email{'From'}) { 739$sender= delete($parsed_email{'From'}); 740 } 741 if ($parsed_email{'In-Reply-To'}) { 742$initial_in_reply_to= delete($parsed_email{'In-Reply-To'}); 743 } 744 if ($parsed_email{'Reply-To'}) { 745$reply_to= delete($parsed_email{'Reply-To'}); 746 } 747 if ($parsed_email{'Subject'}) { 748$initial_subject= delete($parsed_email{'Subject'}); 749 print$c2"Subject: " . 750 quote_subject($initial_subject,$compose_encoding) . 751 "\n"; 752 } 753 754 if ($parsed_email{'MIME-Version'}) { 755 print$c2"MIME-Version:$parsed_email{'MIME-Version'}\n", 756 "Content-Type:$parsed_email{'Content-Type'};\n", 757 "Content-Transfer-Encoding:$parsed_email{'Content-Transfer-Encoding'}\n"; 758 delete($parsed_email{'MIME-Version'}); 759 delete($parsed_email{'Content-Type'}); 760 delete($parsed_email{'Content-Transfer-Encoding'}); 761 } elsif (file_has_nonascii($compose_filename)) { 762 my$content_type= (delete($parsed_email{'Content-Type'}) or 763 "text/plain; charset=$compose_encoding"); 764 print$c2"MIME-Version: 1.0\n", 765 "Content-Type:$content_type\n", 766 "Content-Transfer-Encoding: 8bit\n"; 767 } 768 # Preserve unknown headers 769 foreach my$key(keys%parsed_email) { 770 next if$keyeq 'body'; 771 print$c2"$key:$parsed_email{$key}"; 772 } 773 774 if ($parsed_email{'body'}) { 775 print$c2"\n$parsed_email{'body'}\n"; 776 delete($parsed_email{'body'}); 777 } else { 778 print __("Summary email is empty, skipping it\n"); 779$compose= -1; 780 } 781 782 close$c2; 783 784} elsif ($annotate) { 785 do_edit(@files); 786} 787 788sub ask { 789 my ($prompt,%arg) =@_; 790 my$valid_re=$arg{valid_re}; 791 my$default=$arg{default}; 792 my$confirm_only=$arg{confirm_only}; 793 my$resp; 794 my$i= 0; 795 return defined$default?$default: undef 796 unless defined$term->IN and defined fileno($term->IN) and 797 defined$term->OUT and defined fileno($term->OUT); 798 while ($i++< 10) { 799$resp=$term->readline($prompt); 800 if (!defined$resp) { # EOF 801 print "\n"; 802 return defined$default?$default: undef; 803 } 804 if ($respeq '' and defined$default) { 805 return$default; 806 } 807 if (!defined$valid_reor$resp=~ /$valid_re/) { 808 return$resp; 809 } 810 if ($confirm_only) { 811 my$yesno=$term->readline( 812 # TRANSLATORS: please keep [y/N] as is. 813 sprintf(__("Are you sure you want to use <%s> [y/N]? "),$resp)); 814 if (defined$yesno&&$yesno=~ /y/i) { 815 return$resp; 816 } 817 } 818 } 819 return; 820} 821 822sub parse_header_line { 823 my$lines= shift; 824 my$parsed_line= shift; 825 my$addr_pat= join "|", qw(To Cc Bcc); 826 827foreach(split(/\n/,$lines)) { 828if(/^($addr_pat):\s*(.+)$/i) { 829$parsed_line->{$1} = [ parse_address_line($2) ]; 830}elsif(/^([^:]*):\s*(.+)\s*$/i) { 831$parsed_line->{$1} =$2; 832} 833} 834} 835 836sub filter_body { 837my$c=shift; 838my$body=""; 839while(my$body_line= <$c>) { 840if($body_line!~m/^GIT:/) { 841$body.=$body_line; 842} 843} 844return$body; 845} 846 847 848my%broken_encoding; 849 850sub file_declares_8bit_cte { 851my$fn=shift; 852open(my$fh,'<',$fn); 853while(my$line= <$fh>) { 854last if($line=~/^$/); 855return1if($line=~/^Content-Transfer-Encoding: .*8bit.*$/); 856} 857close$fh; 858return0; 859} 860 861foreachmy$f(@files) { 862next unless(body_or_subject_has_nonascii($f) 863&& !file_declares_8bit_cte($f)); 864$broken_encoding{$f} =1; 865} 866 867if(!defined$auto_8bit_encoding&&scalar%broken_encoding) { 868print __("The following files are 8bit, but do not declare ". 869"a Content-Transfer-Encoding.\n"); 870foreachmy$f(sort keys%broken_encoding) { 871print"$f\n"; 872} 873$auto_8bit_encoding= ask(__("Which 8bit encoding should I declare [UTF-8]? "), 874 valid_re =>qr/.{4}/, confirm_only =>1, 875default=>"UTF-8"); 876} 877 878if(!$force) { 879formy$f(@files) { 880if(get_patch_subject($f) =~/\Q*** SUBJECT HERE ***\E/) { 881die sprintf(__("Refusing to send because the patch\n\t%s\n" 882."has the template subject '*** SUBJECT HERE ***'. " 883."Pass --force if you really want to send.\n"),$f); 884} 885} 886} 887 888if(defined$sender) { 889$sender=~s/^\s+|\s+$//g; 890($sender) = expand_aliases($sender); 891}else{ 892$sender=$repoauthor||$repocommitter||''; 893} 894 895# $sender could be an already sanitized address 896# (e.g. sendemail.from could be manually sanitized by user). 897# But it's a no-op to run sanitize_address on an already sanitized address. 898$sender= sanitize_address($sender); 899 900my$to_whom= __("To whom should the emails be sent (if anyone)?"); 901my$prompting=0; 902if(!@initial_to&& !defined$to_cmd) { 903my$to= ask("$to_whom", 904default=>"", 905 valid_re =>qr/\@.*\./, confirm_only =>1); 906push@initial_to, parse_address_line($to)ifdefined$to;# sanitized/validated later 907$prompting++; 908} 909 910sub expand_aliases { 911returnmap{ expand_one_alias($_) }@_; 912} 913 914my%EXPANDED_ALIASES; 915sub expand_one_alias { 916my$alias=shift; 917if($EXPANDED_ALIASES{$alias}) { 918die sprintf(__("fatal: alias '%s' expands to itself\n"),$alias); 919} 920local$EXPANDED_ALIASES{$alias} =1; 921return$aliases{$alias} ? expand_aliases(@{$aliases{$alias}}) :$alias; 922} 923 924@initial_to= process_address_list(@initial_to); 925@initial_cc= process_address_list(@initial_cc); 926@bcclist= process_address_list(@bcclist); 927 928if($thread&& !defined$initial_in_reply_to&&$prompting) { 929$initial_in_reply_to= ask( 930 __("Message-ID to be used as In-Reply-To for the first email (if any)? "), 931default=>"", 932 valid_re =>qr/\@.*\./, confirm_only =>1); 933} 934if(defined$initial_in_reply_to) { 935$initial_in_reply_to=~s/^\s*<?//; 936$initial_in_reply_to=~s/>?\s*$//; 937$initial_in_reply_to="<$initial_in_reply_to>"if$initial_in_reply_tone''; 938} 939 940if(defined$reply_to) { 941$reply_to=~s/^\s+|\s+$//g; 942($reply_to) = expand_aliases($reply_to); 943$reply_to= sanitize_address($reply_to); 944} 945 946if(!defined$smtp_server) { 947my@sendmail_paths=qw( /usr/sbin/sendmail /usr/lib/sendmail ); 948push@sendmail_paths,map{"$_/sendmail"}split/:/,$ENV{PATH}; 949foreach(@sendmail_paths) { 950if(-x $_) { 951$smtp_server=$_; 952last; 953} 954} 955$smtp_server||='localhost';# could be 127.0.0.1, too... *shrug* 956} 957 958if($compose&&$compose>0) { 959@files= ($compose_filename.".final",@files); 960} 961 962# Variables we set as part of the loop over files 963our($message_id,%mail,$subject,$in_reply_to,$references,$message, 964$needs_confirm,$message_num,$ask_default); 965 966sub extract_valid_address { 967my$address=shift; 968my$local_part_regexp=qr/[^<>"\s@]+/; 969my$domain_regexp=qr/[^.<>"\s@]+(?:\.[^.<>"\s@]+)+/; 970 971# check for a local address: 972return$addressif($address=~/^($local_part_regexp)$/); 973 974$address=~s/^\s*<(.*)>\s*$/$1/; 975if($have_email_valid) { 976returnscalar Email::Valid->address($address); 977} 978 979# less robust/correct than the monster regexp in Email::Valid, 980# but still does a 99% job, and one less dependency 981return$1if$address=~/($local_part_regexp\@$domain_regexp)/; 982return; 983} 984 985sub extract_valid_address_or_die { 986my$address=shift; 987$address= extract_valid_address($address); 988die sprintf(__("error: unable to extract a valid address from:%s\n"),$address) 989if!$address; 990return$address; 991} 992 993sub validate_address { 994my$address=shift; 995while(!extract_valid_address($address)) { 996printf STDERR __("error: unable to extract a valid address from:%s\n"),$address; 997# TRANSLATORS: Make sure to include [q] [d] [e] in your 998# translation. The program will only accept English input 999# at this point.1000$_= ask(__("What to do with this address? ([q]uit|[d]rop|[e]dit): "),1001 valid_re =>qr/^(?:quit|q|drop|d|edit|e)/i,1002default=>'q');1003if(/^d/i) {1004returnundef;1005}elsif(/^q/i) {1006 cleanup_compose_files();1007exit(0);1008}1009$address= ask("$to_whom",1010default=>"",1011 valid_re =>qr/\@.*\./, confirm_only =>1);1012}1013return$address;1014}10151016sub validate_address_list {1017return(grep{defined$_}1018map{ validate_address($_) }@_);1019}10201021# Usually don't need to change anything below here.10221023# we make a "fake" message id by taking the current number1024# of seconds since the beginning of Unix time and tacking on1025# a random number to the end, in case we are called quicker than1026# 1 second since the last time we were called.10271028# We'll setup a template for the message id, using the "from" address:10291030my($message_id_stamp,$message_id_serial);1031sub make_message_id {1032my$uniq;1033if(!defined$message_id_stamp) {1034$message_id_stamp= strftime("%Y%m%d%H%M%S.$$",gmtime(time));1035$message_id_serial=0;1036}1037$message_id_serial++;1038$uniq="$message_id_stamp-$message_id_serial";10391040my$du_part;1041for($sender,$repocommitter,$repoauthor) {1042$du_part= extract_valid_address(sanitize_address($_));1043last if(defined$du_partand$du_partne'');1044}1045if(not defined$du_partor$du_parteq'') {1046require Sys::Hostname;1047$du_part='user@'. Sys::Hostname::hostname();1048}1049my$message_id_template="<%s-%s>";1050$message_id=sprintf($message_id_template,$uniq,$du_part);1051#print "new message id = $message_id\n"; # Was useful for debugging1052}1053105410551056$time=time-scalar$#files;10571058sub unquote_rfc2047 {1059local($_) =@_;1060my$charset;1061my$sep=qr/[ \t]+/;1062 s{$re_encoded_word(?:$sep$re_encoded_word)*}{1063my@words=split$sep,$&;1064foreach(@words) {1065m/$re_encoded_word/;1066$charset=$1;1067my$encoding=$2;1068my$text=$3;1069if($encodingeq'q'||$encodingeq'Q') {1070$_=$text;1071s/_/ /g;1072s/=([0-9A-F]{2})/chr(hex($1))/egi;1073}else{1074# other encodings not supported yet1075}1076}1077join'',@words;1078}eg;1079returnwantarray? ($_,$charset) :$_;1080}10811082sub quote_rfc2047 {1083local$_=shift;1084my$encoding=shift||'UTF-8';1085s/([^-a-zA-Z0-9!*+\/])/sprintf("=%02X",ord($1))/eg;1086s/(.*)/=\?$encoding\?q\?$1\?=/;1087return$_;1088}10891090sub is_rfc2047_quoted {1091my$s=shift;1092length($s) <=75&&1093$s=~m/^(?:"[[:ascii:]]*"|$re_encoded_word)$/o;1094}10951096sub subject_needs_rfc2047_quoting {1097my$s=shift;10981099return($s=~/[^[:ascii:]]/) || ($s=~/=\?/);1100}11011102sub quote_subject {1103local$subject=shift;1104my$encoding=shift||'UTF-8';11051106if(subject_needs_rfc2047_quoting($subject)) {1107return quote_rfc2047($subject,$encoding);1108}1109return$subject;1110}11111112# use the simplest quoting being able to handle the recipient1113sub sanitize_address {1114my($recipient) =@_;11151116# remove garbage after email address1117$recipient=~s/(.*>).*$/$1/;11181119my($recipient_name,$recipient_addr) = ($recipient=~/^(.*?)\s*(<.*)/);11201121if(not$recipient_name) {1122return$recipient;1123}11241125# if recipient_name is already quoted, do nothing1126if(is_rfc2047_quoted($recipient_name)) {1127return$recipient;1128}11291130# remove non-escaped quotes1131$recipient_name=~s/(^|[^\\])"/$1/g;11321133# rfc2047 is needed if a non-ascii char is included1134if($recipient_name=~/[^[:ascii:]]/) {1135$recipient_name= quote_rfc2047($recipient_name);1136}11371138# double quotes are needed if specials or CTLs are included1139elsif($recipient_name=~/[][()<>@,;:\\".\000-\037\177]/) {1140$recipient_name=~s/([\\\r])/\\$1/g;1141$recipient_name=qq["$recipient_name"];1142}11431144return"$recipient_name$recipient_addr";11451146}11471148sub strip_garbage_one_address {1149my($addr) =@_;1150chomp$addr;1151if($addr=~/^(("[^"]*"|[^"<]*)? *<[^>]*>).*/) {1152# "Foo Bar" <foobar@example.com> [possibly garbage here]1153# Foo Bar <foobar@example.com> [possibly garbage here]1154return$1;1155}1156if($addr=~/^(<[^>]*>).*/) {1157# <foo@example.com> [possibly garbage here]1158# if garbage contains other addresses, they are ignored.1159return$1;1160}1161if($addr=~/^([^"#,\s]*)/) {1162# address without quoting: remove anything after the address1163return$1;1164}1165return$addr;1166}11671168sub sanitize_address_list {1169return(map{ sanitize_address($_) }@_);1170}11711172sub process_address_list {1173my@addr_list=map{ parse_address_line($_) }@_;1174@addr_list= expand_aliases(@addr_list);1175@addr_list= sanitize_address_list(@addr_list);1176@addr_list= validate_address_list(@addr_list);1177return@addr_list;1178}11791180# Returns the local Fully Qualified Domain Name (FQDN) if available.1181#1182# Tightly configured MTAa require that a caller sends a real DNS1183# domain name that corresponds the IP address in the HELO/EHLO1184# handshake. This is used to verify the connection and prevent1185# spammers from trying to hide their identity. If the DNS and IP don't1186# match, the receiveing MTA may deny the connection.1187#1188# Here is a deny example of Net::SMTP with the default "localhost.localdomain"1189#1190# Net::SMTP=GLOB(0x267ec28)>>> EHLO localhost.localdomain1191# Net::SMTP=GLOB(0x267ec28)<<< 550 EHLO argument does not match calling host1192#1193# This maildomain*() code is based on ideas in Perl library Test::Reporter1194# /usr/share/perl5/Test/Reporter/Mail/Util.pm ==> sub _maildomain ()11951196sub valid_fqdn {1197my$domain=shift;1198returndefined$domain&& !($^Oeq'darwin'&&$domain=~/\.local$/) &&$domain=~/\./;1199}12001201sub maildomain_net {1202my$maildomain;12031204my$domain= Net::Domain::domainname();1205$maildomain=$domainif valid_fqdn($domain);12061207return$maildomain;1208}12091210sub maildomain_mta {1211my$maildomain;12121213formy$host(qw(mailhost localhost)) {1214my$smtp= Net::SMTP->new($host);1215if(defined$smtp) {1216my$domain=$smtp->domain;1217$smtp->quit;12181219$maildomain=$domainif valid_fqdn($domain);12201221last if$maildomain;1222}1223}12241225return$maildomain;1226}12271228sub maildomain {1229return maildomain_net() || maildomain_mta() ||'localhost.localdomain';1230}12311232sub smtp_host_string {1233if(defined$smtp_server_port) {1234return"$smtp_server:$smtp_server_port";1235}else{1236return$smtp_server;1237}1238}12391240# Returns 1 if authentication succeeded or was not necessary1241# (smtp_user was not specified), and 0 otherwise.12421243sub smtp_auth_maybe {1244if(!defined$smtp_authuser||$auth) {1245return1;1246}12471248# Workaround AUTH PLAIN/LOGIN interaction defect1249# with Authen::SASL::Cyrus1250eval{1251require Authen::SASL;1252 Authen::SASL->import(qw(Perl));1253};12541255# Check mechanism naming as defined in:1256# https://tools.ietf.org/html/rfc4422#page-81257if($smtp_auth&&$smtp_auth!~/^(\b[A-Z0-9-_]{1,20}\s*)*$/) {1258die"invalid smtp auth: '${smtp_auth}'";1259}12601261# TODO: Authentication may fail not because credentials were1262# invalid but due to other reasons, in which we should not1263# reject credentials.1264$auth= Git::credential({1265'protocol'=>'smtp',1266'host'=> smtp_host_string(),1267'username'=>$smtp_authuser,1268# if there's no password, "git credential fill" will1269# give us one, otherwise it'll just pass this one.1270'password'=>$smtp_authpass1271},sub{1272my$cred=shift;12731274if($smtp_auth) {1275my$sasl= Authen::SASL->new(1276 mechanism =>$smtp_auth,1277 callback => {1278 user =>$cred->{'username'},1279 pass =>$cred->{'password'},1280 authname =>$cred->{'username'},1281}1282);12831284return!!$smtp->auth($sasl);1285}12861287return!!$smtp->auth($cred->{'username'},$cred->{'password'});1288});12891290return$auth;1291}12921293sub ssl_verify_params {1294eval{1295require IO::Socket::SSL;1296 IO::Socket::SSL->import(qw/SSL_VERIFY_PEER SSL_VERIFY_NONE/);1297};1298if($@) {1299print STDERR "Not using SSL_VERIFY_PEER due to out-of-date IO::Socket::SSL.\n";1300return;1301}13021303if(!defined$smtp_ssl_cert_path) {1304# use the OpenSSL defaults1305return(SSL_verify_mode => SSL_VERIFY_PEER());1306}13071308if($smtp_ssl_cert_patheq"") {1309return(SSL_verify_mode => SSL_VERIFY_NONE());1310}elsif(-d $smtp_ssl_cert_path) {1311return(SSL_verify_mode => SSL_VERIFY_PEER(),1312 SSL_ca_path =>$smtp_ssl_cert_path);1313}elsif(-f $smtp_ssl_cert_path) {1314return(SSL_verify_mode => SSL_VERIFY_PEER(),1315 SSL_ca_file =>$smtp_ssl_cert_path);1316}else{1317die sprintf(__("CA path\"%s\"does not exist"),$smtp_ssl_cert_path);1318}1319}13201321sub file_name_is_absolute {1322my($path) =@_;13231324# msys does not grok DOS drive-prefixes1325if($^Oeq'msys') {1326return($path=~ m#^/# || $path =~ m#^[a-zA-Z]\:#)1327}13281329require File::Spec::Functions;1330return File::Spec::Functions::file_name_is_absolute($path);1331}13321333# Prepares the email, then asks the user what to do.1334#1335# If the user chooses to send the email, it's sent and 1 is returned.1336# If the user chooses not to send the email, 0 is returned.1337# If the user decides they want to make further edits, -1 is returned and the1338# caller is expected to call send_message again after the edits are performed.1339#1340# If an error occurs sending the email, this just dies.13411342sub send_message {1343my@recipients= unique_email_list(@to);1344@cc= (grep{my$cc= extract_valid_address_or_die($_);1345not grep{$cceq$_||$_=~/<\Q${cc}\E>$/}@recipients1346}1347@cc);1348my$to=join(",\n\t",@recipients);1349@recipients= unique_email_list(@recipients,@cc,@bcclist);1350@recipients= (map{ extract_valid_address_or_die($_) }@recipients);1351my$date= format_2822_time($time++);1352my$gitversion='@@GIT_VERSION@@';1353if($gitversion=~m/..GIT_VERSION../) {1354$gitversion= Git::version();1355}13561357my$cc=join(",\n\t", unique_email_list(@cc));1358my$ccline="";1359if($ccne'') {1360$ccline="\nCc:$cc";1361}1362 make_message_id()unlessdefined($message_id);13631364my$header="From:$sender1365To:$to${ccline}1366Subject:$subject1367Date:$date1368Message-Id:$message_id1369";1370if($use_xmailer) {1371$header.="X-Mailer: git-send-email$gitversion\n";1372}1373if($in_reply_to) {13741375$header.="In-Reply-To:$in_reply_to\n";1376$header.="References:$references\n";1377}1378if($reply_to) {1379$header.="Reply-To:$reply_to\n";1380}1381if(@xh) {1382$header.=join("\n",@xh) ."\n";1383}13841385my@sendmail_parameters= ('-i',@recipients);1386my$raw_from=$sender;1387if(defined$envelope_sender&&$envelope_senderne"auto") {1388$raw_from=$envelope_sender;1389}1390$raw_from= extract_valid_address($raw_from);1391unshift(@sendmail_parameters,1392'-f',$raw_from)if(defined$envelope_sender);13931394if($needs_confirm&& !$dry_run) {1395print"\n$header\n";1396if($needs_confirmeq"inform") {1397$confirm_unconfigured=0;# squelch this message for the rest of this run1398$ask_default="y";# assume yes on EOF since user hasn't explicitly asked for confirmation1399print __ <<EOF ;1400 The Cc list above has been expanded by additional1401 addresses found in the patch commit message. By default1402 send-email prompts before sending whenever this occurs.1403 This behavior is controlled by the sendemail.confirm1404 configuration setting.14051406 For additional information, run 'git send-email --help'.1407 To retain the current behavior, but squelch this message,1408 run 'git config --global sendemail.confirm auto'.14091410EOF1411}1412# TRANSLATORS: Make sure to include [y] [n] [e] [q] [a] in your1413# translation. The program will only accept English input1414# at this point.1415$_= ask(__("Send this email? ([y]es|[n]o|[e]dit|[q]uit|[a]ll): "),1416 valid_re =>qr/^(?:yes|y|no|n|edit|e|quit|q|all|a)/i,1417default=>$ask_default);1418die __("Send this email reply required")unlessdefined$_;1419if(/^n/i) {1420return0;1421}elsif(/^e/i) {1422return-1;1423}elsif(/^q/i) {1424 cleanup_compose_files();1425exit(0);1426}elsif(/^a/i) {1427$confirm='never';1428}1429}14301431unshift(@sendmail_parameters,@smtp_server_options);14321433if($dry_run) {1434# We don't want to send the email.1435}elsif(file_name_is_absolute($smtp_server)) {1436my$pid=open my$sm,'|-';1437defined$pidor die$!;1438if(!$pid) {1439exec($smtp_server,@sendmail_parameters)or die$!;1440}1441print$sm"$header\n$message";1442close$smor die$!;1443}else{14441445if(!defined$smtp_server) {1446die __("The required SMTP server is not properly defined.")1447}14481449require Net::SMTP;1450my$use_net_smtp_ssl= version->parse($Net::SMTP::VERSION) < version->parse("2.34");1451$smtp_domain||= maildomain();14521453if($smtp_encryptioneq'ssl') {1454$smtp_server_port||=465;# ssmtp1455require IO::Socket::SSL;14561457# Suppress "variable accessed once" warning.1458{1459no warnings 'once';1460$IO::Socket::SSL::DEBUG =1;1461}14621463# Net::SMTP::SSL->new() does not forward any SSL options1464 IO::Socket::SSL::set_client_defaults(1465 ssl_verify_params());14661467if($use_net_smtp_ssl) {1468require Net::SMTP::SSL;1469$smtp||= Net::SMTP::SSL->new($smtp_server,1470 Hello =>$smtp_domain,1471 Port =>$smtp_server_port,1472 Debug =>$debug_net_smtp);1473}1474else{1475$smtp||= Net::SMTP->new($smtp_server,1476 Hello =>$smtp_domain,1477 Port =>$smtp_server_port,1478 Debug =>$debug_net_smtp,1479 SSL =>1);1480}1481}1482elsif(!$smtp) {1483$smtp_server_port||=25;1484$smtp||= Net::SMTP->new($smtp_server,1485 Hello =>$smtp_domain,1486 Debug =>$debug_net_smtp,1487 Port =>$smtp_server_port);1488if($smtp_encryptioneq'tls'&&$smtp) {1489if($use_net_smtp_ssl) {1490$smtp->command('STARTTLS');1491$smtp->response();1492if($smtp->code!=220) {1493die sprintf(__("Server does not support STARTTLS!%s"),$smtp->message);1494}1495require Net::SMTP::SSL;1496$smtp= Net::SMTP::SSL->start_SSL($smtp,1497 ssl_verify_params())1498or die sprintf(__("STARTTLS failed!%s"), IO::Socket::SSL::errstr());1499}1500else{1501$smtp->starttls(ssl_verify_params())1502or die sprintf(__("STARTTLS failed!%s"), IO::Socket::SSL::errstr());1503}1504# Send EHLO again to receive fresh1505# supported commands1506$smtp->hello($smtp_domain);1507}1508}15091510if(!$smtp) {1511die __("Unable to initialize SMTP properly. Check config and use --smtp-debug."),1512" VALUES: server=$smtp_server",1513"encryption=$smtp_encryption",1514"hello=$smtp_domain",1515defined$smtp_server_port?" port=$smtp_server_port":"";1516}15171518 smtp_auth_maybe or die$smtp->message;15191520$smtp->mail($raw_from)or die$smtp->message;1521$smtp->to(@recipients)or die$smtp->message;1522$smtp->dataor die$smtp->message;1523$smtp->datasend("$header\n")or die$smtp->message;1524my@lines=split/^/,$message;1525foreachmy$line(@lines) {1526$smtp->datasend("$line")or die$smtp->message;1527}1528$smtp->dataend()or die$smtp->message;1529$smtp->code=~/250|200/or die sprintf(__("Failed to send%s\n"),$subject).$smtp->message;1530}1531if($quiet) {1532printf($dry_run? __("Dry-Sent%s\n") : __("Sent%s\n"),$subject);1533}else{1534print($dry_run? __("Dry-OK. Log says:\n") : __("OK. Log says:\n"));1535if(!file_name_is_absolute($smtp_server)) {1536print"Server:$smtp_server\n";1537print"MAIL FROM:<$raw_from>\n";1538foreachmy$entry(@recipients) {1539print"RCPT TO:<$entry>\n";1540}1541}else{1542print"Sendmail:$smtp_server".join(' ',@sendmail_parameters)."\n";1543}1544print$header,"\n";1545if($smtp) {1546print __("Result: "),$smtp->code,' ',1547($smtp->message=~/\n([^\n]+\n)$/s),"\n";1548}else{1549print __("Result: OK\n");1550}1551}15521553return1;1554}15551556$in_reply_to=$initial_in_reply_to;1557$references=$initial_in_reply_to||'';1558$subject=$initial_subject;1559$message_num=0;15601561# Prepares the email, prompts the user, sends it out1562# Returns 0 if an edit was done and the function should be called again, or 11563# otherwise.1564sub process_file {1565my($t) =@_;15661567open my$fh,"<",$tor die sprintf(__("can't open file%s"),$t);15681569my$author=undef;1570my$sauthor=undef;1571my$author_encoding;1572my$has_content_type;1573my$body_encoding;1574my$xfer_encoding;1575my$has_mime_version;1576@to= ();1577@cc= ();1578@xh= ();1579my$input_format=undef;1580my@header= ();1581$message="";1582$message_num++;1583# First unfold multiline header fields1584while(<$fh>) {1585last if/^\s*$/;1586if(/^\s+\S/and@header) {1587chomp($header[$#header]);1588s/^\s+/ /;1589$header[$#header] .=$_;1590}else{1591push(@header,$_);1592}1593}1594# Now parse the header1595foreach(@header) {1596if(/^From /) {1597$input_format='mbox';1598next;1599}1600chomp;1601if(!defined$input_format&&/^[-A-Za-z]+:\s/) {1602$input_format='mbox';1603}16041605if(defined$input_format&&$input_formateq'mbox') {1606if(/^Subject:\s+(.*)$/i) {1607$subject=$1;1608}1609elsif(/^From:\s+(.*)$/i) {1610($author,$author_encoding) = unquote_rfc2047($1);1611$sauthor= sanitize_address($author);1612next if$suppress_cc{'author'};1613next if$suppress_cc{'self'}and$sauthoreq$sender;1614printf(__("(mbox) Adding cc:%sfrom line '%s'\n"),1615$1,$_)unless$quiet;1616push@cc,$1;1617}1618elsif(/^To:\s+(.*)$/i) {1619foreachmy$addr(parse_address_line($1)) {1620printf(__("(mbox) Adding to:%sfrom line '%s'\n"),1621$addr,$_)unless$quiet;1622push@to,$addr;1623}1624}1625elsif(/^Cc:\s+(.*)$/i) {1626foreachmy$addr(parse_address_line($1)) {1627my$qaddr= unquote_rfc2047($addr);1628my$saddr= sanitize_address($qaddr);1629if($saddreq$sender) {1630next if($suppress_cc{'self'});1631}else{1632next if($suppress_cc{'cc'});1633}1634printf(__("(mbox) Adding cc:%sfrom line '%s'\n"),1635$addr,$_)unless$quiet;1636push@cc,$addr;1637}1638}1639elsif(/^Content-type:/i) {1640$has_content_type=1;1641if(/charset="?([^ "]+)/) {1642$body_encoding=$1;1643}1644push@xh,$_;1645}1646elsif(/^MIME-Version/i) {1647$has_mime_version=1;1648push@xh,$_;1649}1650elsif(/^Message-Id: (.*)/i) {1651$message_id=$1;1652}1653elsif(/^Content-Transfer-Encoding: (.*)/i) {1654$xfer_encoding=$1ifnot defined$xfer_encoding;1655}1656elsif(/^In-Reply-To: (.*)/i) {1657$in_reply_to=$1;1658}1659elsif(/^References: (.*)/i) {1660$references=$1;1661}1662elsif(!/^Date:\s/i&&/^[-A-Za-z]+:\s+\S/) {1663push@xh,$_;1664}1665}else{1666# In the traditional1667# "send lots of email" format,1668# line 1 = cc1669# line 2 = subject1670# So let's support that, too.1671$input_format='lots';1672if(@cc==0&& !$suppress_cc{'cc'}) {1673printf(__("(non-mbox) Adding cc:%sfrom line '%s'\n"),1674$_,$_)unless$quiet;1675push@cc,$_;1676}elsif(!defined$subject) {1677$subject=$_;1678}1679}1680}1681# Now parse the message body1682while(<$fh>) {1683$message.=$_;1684if(/^([a-z-]*-by|Cc): (.*)/i) {1685chomp;1686my($what,$c) = ($1,$2);1687# strip garbage for the address we'll use:1688$c= strip_garbage_one_address($c);1689# sanitize a bit more to decide whether to suppress the address:1690my$sc= sanitize_address($c);1691if($sceq$sender) {1692next if($suppress_cc{'self'});1693}else{1694if($what=~/^Signed-off-by$/i) {1695next if$suppress_cc{'sob'};1696}elsif($what=~/-by$/i) {1697next if$suppress_cc{'misc-by'};1698}elsif($what=~/Cc/i) {1699next if$suppress_cc{'bodycc'};1700}1701}1702if($c!~/.+@.+|<.+>/) {1703printf("(body) Ignoring%sfrom line '%s'\n",1704$what,$_)unless$quiet;1705next;1706}1707push@cc,$c;1708printf(__("(body) Adding cc:%sfrom line '%s'\n"),1709$c,$_)unless$quiet;1710}1711}1712close$fh;17131714push@to, recipients_cmd("to-cmd","to",$to_cmd,$t)1715ifdefined$to_cmd;1716push@cc, recipients_cmd("cc-cmd","cc",$cc_cmd,$t)1717ifdefined$cc_cmd&& !$suppress_cc{'cccmd'};17181719if($broken_encoding{$t} && !$has_content_type) {1720$xfer_encoding='8bit'ifnot defined$xfer_encoding;1721$has_content_type=1;1722push@xh,"Content-Type: text/plain; charset=$auto_8bit_encoding";1723$body_encoding=$auto_8bit_encoding;1724}17251726if($broken_encoding{$t} && !is_rfc2047_quoted($subject)) {1727$subject= quote_subject($subject,$auto_8bit_encoding);1728}17291730if(defined$sauthorand$sauthorne$sender) {1731$message="From:$author\n\n$message";1732if(defined$author_encoding) {1733if($has_content_type) {1734if($body_encodingeq$author_encoding) {1735# ok, we already have the right encoding1736}1737else{1738# uh oh, we should re-encode1739}1740}1741else{1742$xfer_encoding='8bit'ifnot defined$xfer_encoding;1743$has_content_type=1;1744push@xh,1745"Content-Type: text/plain; charset=$author_encoding";1746}1747}1748}1749$xfer_encoding='8bit'ifnot defined$xfer_encoding;1750($message,$xfer_encoding) = apply_transfer_encoding(1751$message,$xfer_encoding,$target_xfer_encoding);1752push@xh,"Content-Transfer-Encoding:$xfer_encoding";1753unshift@xh,'MIME-Version: 1.0'unless$has_mime_version;17541755$needs_confirm= (1756$confirmeq"always"or1757($confirm=~/^(?:auto|cc)$/&&@cc)or1758($confirm=~/^(?:auto|compose)$/&&$compose&&$message_num==1));1759$needs_confirm="inform"if($needs_confirm&&$confirm_unconfigured&&@cc);17601761@to= process_address_list(@to);1762@cc= process_address_list(@cc);17631764@to= (@initial_to,@to);1765@cc= (@initial_cc,@cc);17661767if($message_num==1) {1768if(defined$cover_ccand$cover_cc) {1769@initial_cc=@cc;1770}1771if(defined$cover_toand$cover_to) {1772@initial_to=@to;1773}1774}17751776my$message_was_sent= send_message();1777if($message_was_sent== -1) {1778 do_edit($t);1779return0;1780}17811782# set up for the next message1783if($thread&&$message_was_sent&&1784($chain_reply_to|| !defined$in_reply_to||length($in_reply_to) ==0||1785$message_num==1)) {1786$in_reply_to=$message_id;1787if(length$references>0) {1788$references.="\n$message_id";1789}else{1790$references="$message_id";1791}1792}1793$message_id=undef;1794$num_sent++;1795if(defined$batch_size&&$num_sent==$batch_size) {1796$num_sent=0;1797$smtp->quitifdefined$smtp;1798undef$smtp;1799undef$auth;1800sleep($relogin_delay)ifdefined$relogin_delay;1801}18021803return1;1804}18051806foreachmy$t(@files) {1807while(!process_file($t)) {1808# user edited the file1809}1810}18111812# Execute a command (e.g. $to_cmd) to get a list of email addresses1813# and return a results array1814sub recipients_cmd {1815my($prefix,$what,$cmd,$file) =@_;18161817my@addresses= ();1818open my$fh,"-|","$cmd\Q$file\E"1819or die sprintf(__("(%s) Could not execute '%s'"),$prefix,$cmd);1820while(my$address= <$fh>) {1821$address=~s/^\s*//g;1822$address=~s/\s*$//g;1823$address= sanitize_address($address);1824next if($addresseq$senderand$suppress_cc{'self'});1825push@addresses,$address;1826printf(__("(%s) Adding%s:%sfrom: '%s'\n"),1827$prefix,$what,$address,$cmd)unless$quiet;1828}1829close$fh1830or die sprintf(__("(%s) failed to close pipe to '%s'"),$prefix,$cmd);1831return@addresses;1832}18331834cleanup_compose_files();18351836sub cleanup_compose_files {1837unlink($compose_filename,$compose_filename.".final")if$compose;1838}18391840$smtp->quitif$smtp;18411842sub apply_transfer_encoding {1843my$message=shift;1844my$from=shift;1845my$to=shift;18461847return$messageif($fromeq$toand$fromne'7bit');18481849require MIME::QuotedPrint;1850require MIME::Base64;18511852$message= MIME::QuotedPrint::decode($message)1853if($fromeq'quoted-printable');1854$message= MIME::Base64::decode($message)1855if($fromeq'base64');18561857$to= ($message=~/.{999,}/) ?'quoted-printable':'8bit'1858if$toeq'auto';18591860die __("cannot send message as 7bit")1861if($toeq'7bit'and$message=~/[^[:ascii:]]/);1862return($message,$to)1863if($toeq'7bit'or$toeq'8bit');1864return(MIME::QuotedPrint::encode($message,"\n",0),$to)1865if($toeq'quoted-printable');1866return(MIME::Base64::encode($message,"\n"),$to)1867if($toeq'base64');1868die __("invalid transfer encoding");1869}18701871sub unique_email_list {1872my%seen;1873my@emails;18741875foreachmy$entry(@_) {1876my$clean= extract_valid_address_or_die($entry);1877$seen{$clean} ||=0;1878next if$seen{$clean}++;1879push@emails,$entry;1880}1881return@emails;1882}18831884sub validate_patch {1885my($fn,$xfer_encoding) =@_;18861887if($repo) {1888my$validate_hook= catfile(catdir($repo->repo_path(),'hooks'),1889'sendemail-validate');1890my$hook_error;1891if(-x $validate_hook) {1892my$target= abs_path($fn);1893# The hook needs a correct cwd and GIT_DIR.1894my$cwd_save= cwd();1895chdir($repo->wc_path()or$repo->repo_path())1896or die("chdir:$!");1897local$ENV{"GIT_DIR"} =$repo->repo_path();1898$hook_error="rejected by sendemail-validate hook"1899ifsystem($validate_hook,$target);1900chdir($cwd_save)or die("chdir:$!");1901}1902return$hook_errorif$hook_error;1903}19041905# Any long lines will be automatically fixed if we use a suitable transfer1906# encoding.1907unless($xfer_encoding=~/^(?:auto|quoted-printable|base64)$/) {1908open(my$fh,'<',$fn)1909or die sprintf(__("unable to open%s:%s\n"),$fn,$!);1910while(my$line= <$fh>) {1911if(length($line) >998) {1912returnsprintf(__("%s: patch contains a line longer than 998 characters"), $.);1913}1914}1915}1916return;1917}19181919sub handle_backup {1920my($last,$lastlen,$file,$known_suffix) =@_;1921my($suffix,$skip);19221923$skip=0;1924if(defined$last&&1925($lastlen<length($file)) &&1926(substr($file,0,$lastlen)eq$last) &&1927($suffix=substr($file,$lastlen)) !~/^[a-z0-9]/i) {1928if(defined$known_suffix&&$suffixeq$known_suffix) {1929printf(__("Skipping%swith backup suffix '%s'.\n"),$file,$known_suffix);1930$skip=1;1931}else{1932# TRANSLATORS: please keep "[y|N]" as is.1933my$answer= ask(sprintf(__("Do you really want to send%s?[y|N]: "),$file),1934 valid_re =>qr/^(?:y|n)/i,1935default=>'n');1936$skip= ($answerne'y');1937if($skip) {1938$known_suffix=$suffix;1939}1940}1941}1942return($skip,$known_suffix);1943}19441945sub handle_backup_files {1946my@file=@_;1947my($last,$lastlen,$known_suffix,$skip,@result);1948formy$file(@file) {1949($skip,$known_suffix) = handle_backup($last,$lastlen,1950$file,$known_suffix);1951push@result,$fileunless$skip;1952$last=$file;1953$lastlen=length($file);1954}1955return@result;1956}19571958sub file_has_nonascii {1959my$fn=shift;1960open(my$fh,'<',$fn)1961or die sprintf(__("unable to open%s:%s\n"),$fn,$!);1962while(my$line= <$fh>) {1963return1if$line=~/[^[:ascii:]]/;1964}1965return0;1966}19671968sub body_or_subject_has_nonascii {1969my$fn=shift;1970open(my$fh,'<',$fn)1971or die sprintf(__("unable to open%s:%s\n"),$fn,$!);1972while(my$line= <$fh>) {1973last if$line=~/^$/;1974return1if$line=~/^Subject.*[^[:ascii:]]/;1975}1976while(my$line= <$fh>) {1977return1if$line=~/[^[:ascii:]]/;1978}1979return0;1980}