1#!/usr/bin/perl 2# 3# Copyright 2002,2005 Greg Kroah-Hartman <greg@kroah.com> 4# Copyright 2005 Ryan Anderson <ryan@michonline.com> 5# 6# GPL v2 (See COPYING) 7# 8# Ported to support git "mbox" format files by Ryan Anderson <ryan@michonline.com> 9# 10# Sends a collection of emails to the given email addresses, disturbingly fast. 11# 12# Supports two formats: 13# 1. mbox format files (ignoring most headers and MIME formatting - this is designed for sending patches) 14# 2. The original format support by Greg's script: 15# first line of the message is who to CC, 16# and second line is the subject of the message. 17# 18 19use5.008; 20use strict; 21use warnings; 22use POSIX qw/strftime/; 23use Term::ReadLine; 24use Getopt::Long; 25use Text::ParseWords; 26use Term::ANSIColor; 27use File::Temp qw/ tempdir tempfile /; 28use File::Spec::Functions qw(catdir catfile); 29use Git::LoadCPAN::Error qw(:try); 30use Cwd qw(abs_path cwd); 31use Git; 32use Git::I18N; 33use Net::Domain (); 34use Net::SMTP (); 35use Git::LoadCPAN::Mail::Address; 36 37Getopt::Long::Configure qw/ pass_through /; 38 39package FakeTerm; 40sub new { 41my($class,$reason) =@_; 42returnbless \$reason,shift; 43} 44subreadline{ 45my$self=shift; 46die"Cannot use readline on FakeTerm:$$self"; 47} 48package main; 49 50 51sub usage { 52print<<EOT; 53git send-email [options] <file | directory | rev-list options > 54git send-email --dump-aliases 55 56 Composing: 57 --from <str> * Email From: 58 --[no-]to <str> * Email To: 59 --[no-]cc <str> * Email Cc: 60 --[no-]bcc <str> * Email Bcc: 61 --subject <str> * Email "Subject:" 62 --reply-to <str> * Email "Reply-To:" 63 --in-reply-to <str> * Email "In-Reply-To:" 64 --[no-]xmailer * Add "X-Mailer:" header (default). 65 --[no-]annotate * Review each patch that will be sent in an editor. 66 --compose * Open an editor for introduction. 67 --compose-encoding <str> * Encoding to assume for introduction. 68 --8bit-encoding <str> * Encoding to assume 8bit mails if undeclared 69 --transfer-encoding <str> * Transfer encoding to use (quoted-printable, 8bit, base64) 70 71 Sending: 72 --envelope-sender <str> * Email envelope sender. 73 --smtp-server <str:int> * Outgoing SMTP server to use. The port 74 is optional. Default 'localhost'. 75 --smtp-server-option <str> * Outgoing SMTP server option to use. 76 --smtp-server-port <int> * Outgoing SMTP server port. 77 --smtp-user <str> * Username for SMTP-AUTH. 78 --smtp-pass <str> * Password for SMTP-AUTH; not necessary. 79 --smtp-encryption <str> * tls or ssl; anything else disables. 80 --smtp-ssl * Deprecated. Use '--smtp-encryption ssl'. 81 --smtp-ssl-cert-path <str> * Path to ca-certificates (either directory or file). 82 Pass an empty string to disable certificate 83 verification. 84 --smtp-domain <str> * The domain name sent to HELO/EHLO handshake 85 --smtp-auth <str> * Space-separated list of allowed AUTH mechanisms. 86 This setting forces to use one of the listed mechanisms. 87 --smtp-debug <0|1> * Disable, enable Net::SMTP debug. 88 89 --batch-size <int> * send max <int> message per connection. 90 --relogin-delay <int> * delay <int> seconds between two successive login. 91 This option can only be used with --batch-size 92 93 Automating: 94 --identity <str> * Use the sendemail.<id> options. 95 --to-cmd <str> * Email To: via `<str> \$patch_path` 96 --cc-cmd <str> * Email Cc: via `<str> \$patch_path` 97 --suppress-cc <str> * author, self, sob, cc, cccmd, body, bodycc, all. 98 --[no-]cc-cover * Email Cc: addresses in the cover letter. 99 --[no-]to-cover * Email To: addresses in the cover letter. 100 --[no-]signed-off-by-cc * Send to Signed-off-by: addresses. Default on. 101 --[no-]suppress-from * Send to self. Default off. 102 --[no-]chain-reply-to * Chain In-Reply-To: fields. Default off. 103 --[no-]thread * Use In-Reply-To: field. Default on. 104 105 Administering: 106 --confirm <str> * Confirm recipients before sending; 107 auto, cc, compose, always, or never. 108 --quiet * Output one line of info per email. 109 --dry-run * Don't actually send the emails. 110 --[no-]validate * Perform patch sanity checks. Default on. 111 --[no-]format-patch * understand any non optional arguments as 112 `git format-patch` ones. 113 --force * Send even if safety checks would prevent it. 114 115 Information: 116 --dump-aliases * Dump configured aliases and exit. 117 118EOT 119exit(1); 120} 121 122# most mail servers generate the Date: header, but not all... 123sub format_2822_time { 124my($time) =@_; 125my@localtm=localtime($time); 126my@gmttm=gmtime($time); 127my$localmin=$localtm[1] +$localtm[2] *60; 128my$gmtmin=$gmttm[1] +$gmttm[2] *60; 129if($localtm[0] !=$gmttm[0]) { 130die __("local zone differs from GMT by a non-minute interval\n"); 131} 132if((($gmttm[6] +1) %7) ==$localtm[6]) { 133$localmin+=1440; 134}elsif((($gmttm[6] -1) %7) ==$localtm[6]) { 135$localmin-=1440; 136}elsif($gmttm[6] !=$localtm[6]) { 137die __("local time offset greater than or equal to 24 hours\n"); 138} 139my$offset=$localmin-$gmtmin; 140my$offhour=$offset/60; 141my$offmin=abs($offset%60); 142if(abs($offhour) >=24) { 143die __("local time offset greater than or equal to 24 hours\n"); 144} 145 146returnsprintf("%s,%2d%s%d%02d:%02d:%02d%s%02d%02d", 147qw(Sun Mon Tue Wed Thu Fri Sat)[$localtm[6]], 148$localtm[3], 149qw(Jan Feb Mar Apr May Jun 150 Jul Aug Sep Oct Nov Dec)[$localtm[4]], 151$localtm[5]+1900, 152$localtm[2], 153$localtm[1], 154$localtm[0], 155($offset>=0) ?'+':'-', 156abs($offhour), 157$offmin, 158); 159} 160 161my$have_email_valid=eval{require Email::Valid;1}; 162my$smtp; 163my$auth; 164my$num_sent=0; 165 166# Regexes for RFC 2047 productions. 167my$re_token=qr/[^][()<>@,;:\\"\/?.=\000-\037\177-\377]+/; 168my$re_encoded_text=qr/[^? \000-\037\177-\377]+/; 169my$re_encoded_word=qr/=\?($re_token)\?($re_token)\?($re_encoded_text)\?=/; 170 171# Variables we fill in automatically, or via prompting: 172my(@to,@cc,@xh,$envelope_sender, 173$initial_in_reply_to,$reply_to,$initial_subject,@files, 174$author,$sender,$smtp_authpass,$annotate,$compose,$time); 175# Things we either get from config, *or* are overridden on the 176# command-line. 177my($no_cc,$no_to,$no_bcc); 178my(@config_to,@getopt_to); 179my(@config_cc,@getopt_cc); 180my(@config_bcc,@getopt_bcc); 181 182# Example reply to: 183#$initial_in_reply_to = ''; #<20050203173208.GA23964@foobar.com>'; 184 185my$repo=eval{ Git->repository() }; 186my@repo=$repo? ($repo) : (); 187my$term=eval{ 188$ENV{"GIT_SEND_EMAIL_NOTTY"} 189? new Term::ReadLine 'git-send-email', \*STDIN, \*STDOUT 190: new Term::ReadLine 'git-send-email'; 191}; 192if($@) { 193$term= new FakeTerm "$@: going non-interactive"; 194} 195 196# Behavior modification variables 197my($quiet,$dry_run) = (0,0); 198my$format_patch; 199my$compose_filename; 200my$force=0; 201my$dump_aliases=0; 202 203# Handle interactive edition of files. 204my$multiedit; 205my$editor; 206 207sub do_edit { 208if(!defined($editor)) { 209$editor= Git::command_oneline('var','GIT_EDITOR'); 210} 211if(defined($multiedit) && !$multiedit) { 212map{ 213system('sh','-c',$editor.' "$@"',$editor,$_); 214if(($?&127) || ($?>>8)) { 215die(__("the editor exited uncleanly, aborting everything")); 216} 217}@_; 218}else{ 219system('sh','-c',$editor.' "$@"',$editor,@_); 220if(($?&127) || ($?>>8)) { 221die(__("the editor exited uncleanly, aborting everything")); 222} 223} 224} 225 226# Variables with corresponding config settings 227my($suppress_from,$signed_off_by_cc); 228my($cover_cc,$cover_to); 229my($to_cmd,$cc_cmd); 230my($smtp_server,$smtp_server_port,@smtp_server_options); 231my($smtp_authuser,$smtp_encryption,$smtp_ssl_cert_path); 232my($batch_size,$relogin_delay); 233my($identity,$aliasfiletype,@alias_files,$smtp_domain,$smtp_auth); 234my($confirm); 235my(@suppress_cc); 236my($auto_8bit_encoding); 237my($compose_encoding); 238# Variables with corresponding config settings & hardcoded defaults 239my($debug_net_smtp) =0;# Net::SMTP, see send_message() 240my$thread=1; 241my$chain_reply_to=0; 242my$use_xmailer=1; 243my$validate=1; 244my$target_xfer_encoding='auto'; 245 246my%config_bool_settings= ( 247"thread"=> \$thread, 248"chainreplyto"=> \$chain_reply_to, 249"suppressfrom"=> \$suppress_from, 250"signedoffbycc"=> \$signed_off_by_cc, 251"cccover"=> \$cover_cc, 252"tocover"=> \$cover_to, 253"signedoffcc"=> \$signed_off_by_cc, 254"validate"=> \$validate, 255"multiedit"=> \$multiedit, 256"annotate"=> \$annotate, 257"xmailer"=> \$use_xmailer, 258); 259 260my%config_settings= ( 261"smtpserver"=> \$smtp_server, 262"smtpserverport"=> \$smtp_server_port, 263"smtpserveroption"=> \@smtp_server_options, 264"smtpuser"=> \$smtp_authuser, 265"smtppass"=> \$smtp_authpass, 266"smtpdomain"=> \$smtp_domain, 267"smtpauth"=> \$smtp_auth, 268"smtpbatchsize"=> \$batch_size, 269"smtprelogindelay"=> \$relogin_delay, 270"to"=> \@config_to, 271"tocmd"=> \$to_cmd, 272"cc"=> \@config_cc, 273"cccmd"=> \$cc_cmd, 274"aliasfiletype"=> \$aliasfiletype, 275"bcc"=> \@config_bcc, 276"suppresscc"=> \@suppress_cc, 277"envelopesender"=> \$envelope_sender, 278"confirm"=> \$confirm, 279"from"=> \$sender, 280"assume8bitencoding"=> \$auto_8bit_encoding, 281"composeencoding"=> \$compose_encoding, 282"transferencoding"=> \$target_xfer_encoding, 283); 284 285my%config_path_settings= ( 286"aliasesfile"=> \@alias_files, 287"smtpsslcertpath"=> \$smtp_ssl_cert_path, 288); 289 290# Handle Uncouth Termination 291sub signal_handler { 292 293# Make text normal 294print color("reset"),"\n"; 295 296# SMTP password masked 297system"stty echo"; 298 299# tmp files from --compose 300if(defined$compose_filename) { 301if(-e $compose_filename) { 302printf __("'%s' contains an intermediate version ". 303"of the email you were composing.\n"), 304$compose_filename; 305} 306if(-e ($compose_filename.".final")) { 307printf __("'%s.final' contains the composed email.\n"), 308$compose_filename; 309} 310} 311 312exit; 313}; 314 315$SIG{TERM} = \&signal_handler; 316$SIG{INT} = \&signal_handler; 317 318# Read our sendemail.* config 319sub read_config { 320my($prefix) =@_; 321 322foreachmy$setting(keys%config_bool_settings) { 323my$target=$config_bool_settings{$setting}; 324my$v= Git::config_bool(@repo,"$prefix.$setting"); 325$$target=$vifdefined$v; 326} 327 328foreachmy$setting(keys%config_path_settings) { 329my$target=$config_path_settings{$setting}; 330if(ref($target)eq"ARRAY") { 331unless(@$target) { 332my@values= Git::config_path(@repo,"$prefix.$setting"); 333@$target=@valuesif(@values&&defined$values[0]); 334} 335} 336else{ 337my$v= Git::config_path(@repo,"$prefix.$setting"); 338$$target=$vifdefined$v; 339} 340} 341 342foreachmy$setting(keys%config_settings) { 343my$target=$config_settings{$setting}; 344if(ref($target)eq"ARRAY") { 345unless(@$target) { 346my@values= Git::config(@repo,"$prefix.$setting"); 347@$target=@valuesif(@values&&defined$values[0]); 348} 349} 350else{ 351my$v= Git::config(@repo,"$prefix.$setting"); 352$$target=$vifdefined$v; 353} 354} 355 356if(!defined$smtp_encryption) { 357my$enc= Git::config(@repo,"$prefix.smtpencryption"); 358if(defined$enc) { 359$smtp_encryption=$enc; 360}elsif(Git::config_bool(@repo,"$prefix.smtpssl")) { 361$smtp_encryption='ssl'; 362} 363} 364} 365 366$identity= Git::config(@repo,"sendemail.identity"); 367read_config("sendemail.$identity")ifdefined$identity; 368read_config("sendemail"); 369 370# Begin by accumulating all the variables (defined above), that we will end up 371# needing, first, from the command line: 372 373my$help; 374my$rc= GetOptions("h"=> \$help, 375"dump-aliases"=> \$dump_aliases); 376usage()unless$rc; 377die __("--dump-aliases incompatible with other options\n") 378if!$helpand$dump_aliasesand@ARGV; 379$rc= GetOptions( 380"sender|from=s"=> \$sender, 381"in-reply-to=s"=> \$initial_in_reply_to, 382"reply-to=s"=> \$reply_to, 383"subject=s"=> \$initial_subject, 384"to=s"=> \@getopt_to, 385"to-cmd=s"=> \$to_cmd, 386"no-to"=> \$no_to, 387"cc=s"=> \@getopt_cc, 388"no-cc"=> \$no_cc, 389"bcc=s"=> \@getopt_bcc, 390"no-bcc"=> \$no_bcc, 391"chain-reply-to!"=> \$chain_reply_to, 392"no-chain-reply-to"=>sub{$chain_reply_to=0}, 393"smtp-server=s"=> \$smtp_server, 394"smtp-server-option=s"=> \@smtp_server_options, 395"smtp-server-port=s"=> \$smtp_server_port, 396"smtp-user=s"=> \$smtp_authuser, 397"smtp-pass:s"=> \$smtp_authpass, 398"smtp-ssl"=>sub{$smtp_encryption='ssl'}, 399"smtp-encryption=s"=> \$smtp_encryption, 400"smtp-ssl-cert-path=s"=> \$smtp_ssl_cert_path, 401"smtp-debug:i"=> \$debug_net_smtp, 402"smtp-domain:s"=> \$smtp_domain, 403"smtp-auth=s"=> \$smtp_auth, 404"identity=s"=> \$identity, 405"annotate!"=> \$annotate, 406"no-annotate"=>sub{$annotate=0}, 407"compose"=> \$compose, 408"quiet"=> \$quiet, 409"cc-cmd=s"=> \$cc_cmd, 410"suppress-from!"=> \$suppress_from, 411"no-suppress-from"=>sub{$suppress_from=0}, 412"suppress-cc=s"=> \@suppress_cc, 413"signed-off-cc|signed-off-by-cc!"=> \$signed_off_by_cc, 414"no-signed-off-cc|no-signed-off-by-cc"=>sub{$signed_off_by_cc=0}, 415"cc-cover|cc-cover!"=> \$cover_cc, 416"no-cc-cover"=>sub{$cover_cc=0}, 417"to-cover|to-cover!"=> \$cover_to, 418"no-to-cover"=>sub{$cover_to=0}, 419"confirm=s"=> \$confirm, 420"dry-run"=> \$dry_run, 421"envelope-sender=s"=> \$envelope_sender, 422"thread!"=> \$thread, 423"no-thread"=>sub{$thread=0}, 424"validate!"=> \$validate, 425"no-validate"=>sub{$validate=0}, 426"transfer-encoding=s"=> \$target_xfer_encoding, 427"format-patch!"=> \$format_patch, 428"no-format-patch"=>sub{$format_patch=0}, 429"8bit-encoding=s"=> \$auto_8bit_encoding, 430"compose-encoding=s"=> \$compose_encoding, 431"force"=> \$force, 432"xmailer!"=> \$use_xmailer, 433"no-xmailer"=>sub{$use_xmailer=0}, 434"batch-size=i"=> \$batch_size, 435"relogin-delay=i"=> \$relogin_delay, 436); 437 438# Munge any "either config or getopt, not both" variables 439my@initial_to=@getopt_to?@getopt_to: ($no_to? () :@config_to); 440my@initial_cc=@getopt_cc?@getopt_cc: ($no_cc? () :@config_cc); 441my@initial_bcc=@getopt_bcc?@getopt_bcc: ($no_bcc? () :@config_bcc); 442 443usage()if$help; 444unless($rc) { 445 usage(); 446} 447 448die __("Cannot run git format-patch from outside a repository\n") 449if$format_patchand not$repo; 450 451die __("`batch-size` and `relogin` must be specified together ". 452"(via command-line or configuration option)\n") 453ifdefined$relogin_delayand not defined$batch_size; 454 455# 'default' encryption is none -- this only prevents a warning 456$smtp_encryption=''unless(defined$smtp_encryption); 457 458# Set CC suppressions 459my(%suppress_cc); 460if(@suppress_cc) { 461foreachmy$entry(@suppress_cc) { 462die sprintf(__("Unknown --suppress-cc field: '%s'\n"),$entry) 463unless$entry=~/^(?:all|cccmd|cc|author|self|sob|body|bodycc)$/; 464$suppress_cc{$entry} =1; 465} 466} 467 468if($suppress_cc{'all'}) { 469foreachmy$entry(qw (cccmd cc author self sob body bodycc)) { 470$suppress_cc{$entry} =1; 471} 472delete$suppress_cc{'all'}; 473} 474 475# If explicit old-style ones are specified, they trump --suppress-cc. 476$suppress_cc{'self'} =$suppress_fromifdefined$suppress_from; 477$suppress_cc{'sob'} = !$signed_off_by_ccifdefined$signed_off_by_cc; 478 479if($suppress_cc{'body'}) { 480foreachmy$entry(qw (sob bodycc)) { 481$suppress_cc{$entry} =1; 482} 483delete$suppress_cc{'body'}; 484} 485 486# Set confirm's default value 487my$confirm_unconfigured= !defined$confirm; 488if($confirm_unconfigured) { 489$confirm=scalar%suppress_cc?'compose':'auto'; 490}; 491die sprintf(__("Unknown --confirm setting: '%s'\n"),$confirm) 492unless$confirm=~/^(?:auto|cc|compose|always|never)/; 493 494# Debugging, print out the suppressions. 495if(0) { 496print"suppressions:\n"; 497foreachmy$entry(keys%suppress_cc) { 498printf" %-5s ->$suppress_cc{$entry}\n",$entry; 499} 500} 501 502my($repoauthor,$repocommitter); 503($repoauthor) = Git::ident_person(@repo,'author'); 504($repocommitter) = Git::ident_person(@repo,'committer'); 505 506sub parse_address_line { 507returnmap{$_->format} Mail::Address->parse($_[0]); 508} 509 510sub split_addrs { 511return quotewords('\s*,\s*',1,@_); 512} 513 514my%aliases; 515 516sub parse_sendmail_alias { 517local$_=shift; 518if(/"/) { 519printf STDERR __("warning: sendmail alias with quotes is not supported:%s\n"),$_; 520}elsif(/:include:/) { 521printf STDERR __("warning: `:include:` not supported:%s\n"),$_; 522}elsif(/[\/|]/) { 523printf STDERR __("warning: `/file` or `|pipe` redirection not supported:%s\n"),$_; 524}elsif(/^(\S+?)\s*:\s*(.+)$/) { 525my($alias,$addr) = ($1,$2); 526$aliases{$alias} = [ split_addrs($addr) ]; 527}else{ 528printf STDERR __("warning: sendmail line is not recognized:%s\n"),$_; 529} 530} 531 532sub parse_sendmail_aliases { 533my$fh=shift; 534my$s=''; 535while(<$fh>) { 536chomp; 537next if/^\s*$/||/^\s*#/; 538$s.=$_,next if$s=~s/\\$//||s/^\s+//; 539 parse_sendmail_alias($s)if$s; 540$s=$_; 541} 542$s=~s/\\$//;# silently tolerate stray '\' on last line 543 parse_sendmail_alias($s)if$s; 544} 545 546my%parse_alias= ( 547# multiline formats can be supported in the future 548 mutt =>sub{my$fh=shift;while(<$fh>) { 549if(/^\s*alias\s+(?:-group\s+\S+\s+)*(\S+)\s+(.*)$/) { 550my($alias,$addr) = ($1,$2); 551$addr=~s/#.*$//;# mutt allows # comments 552# commas delimit multiple addresses 553my@addr= split_addrs($addr); 554 555# quotes may be escaped in the file, 556# unescape them so we do not double-escape them later. 557s/\\"/"/gforeach@addr; 558$aliases{$alias} = \@addr 559}}}, 560 mailrc =>sub{my$fh=shift;while(<$fh>) { 561if(/^alias\s+(\S+)\s+(.*?)\s*$/) { 562# spaces delimit multiple addresses 563$aliases{$1} = [ quotewords('\s+',0,$2) ]; 564}}}, 565 pine =>sub{my$fh=shift;my$f='\t[^\t]*'; 566for(my$x='';defined($x);$x=$_) { 567chomp$x; 568$x.=$1while(defined($_= <$fh>) &&/^ +(.*)$/); 569$x=~/^(\S+)$f\t\(?([^\t]+?)\)?(:?$f){0,2}$/ornext; 570$aliases{$1} = [ split_addrs($2) ]; 571}}, 572 elm =>sub{my$fh=shift; 573while(<$fh>) { 574if(/^(\S+)\s+=\s+[^=]+=\s(\S+)/) { 575my($alias,$addr) = ($1,$2); 576$aliases{$alias} = [ split_addrs($addr) ]; 577} 578} }, 579 sendmail => \&parse_sendmail_aliases, 580 gnus =>sub{my$fh=shift;while(<$fh>) { 581if(/\(define-mail-alias\s+"(\S+?)"\s+"(\S+?)"\)/) { 582$aliases{$1} = [$2]; 583}}} 584); 585 586if(@alias_filesand$aliasfiletypeand defined$parse_alias{$aliasfiletype}) { 587foreachmy$file(@alias_files) { 588open my$fh,'<',$fileor die"opening$file:$!\n"; 589$parse_alias{$aliasfiletype}->($fh); 590close$fh; 591} 592} 593 594if($dump_aliases) { 595print"$_\n"for(sort keys%aliases); 596exit(0); 597} 598 599# is_format_patch_arg($f) returns 0 if $f names a patch, or 1 if 600# $f is a revision list specification to be passed to format-patch. 601sub is_format_patch_arg { 602return unless$repo; 603my$f=shift; 604try{ 605$repo->command('rev-parse','--verify','--quiet',$f); 606if(defined($format_patch)) { 607return$format_patch; 608} 609die sprintf(__ <<EOF,$f,$f); 610File '%s' exists but it could also be the range of commits 611to produce patches for. Please disambiguate by... 612 613 * Saying "./%s" if you mean a file; or 614 * Giving --format-patch option if you mean a range. 615EOF 616} catch Git::Error::Command with { 617# Not a valid revision. Treat it as a filename. 618return0; 619} 620} 621 622# Now that all the defaults are set, process the rest of the command line 623# arguments and collect up the files that need to be processed. 624my@rev_list_opts; 625while(defined(my$f=shift@ARGV)) { 626if($feq"--") { 627push@rev_list_opts,"--",@ARGV; 628@ARGV= (); 629}elsif(-d $fand!is_format_patch_arg($f)) { 630opendir my$dh,$f 631or die sprintf(__("Failed to opendir%s:%s"),$f,$!); 632 633push@files,grep{ -f $_}map{ catfile($f,$_) } 634sort readdir$dh; 635closedir$dh; 636}elsif((-f $for-p $f)and!is_format_patch_arg($f)) { 637push@files,$f; 638}else{ 639push@rev_list_opts,$f; 640} 641} 642 643if(@rev_list_opts) { 644die __("Cannot run git format-patch from outside a repository\n") 645unless$repo; 646push@files,$repo->command('format-patch','-o', tempdir(CLEANUP =>1),@rev_list_opts); 647} 648 649@files= handle_backup_files(@files); 650 651if($validate) { 652foreachmy$f(@files) { 653unless(-p $f) { 654my$error= validate_patch($f,$target_xfer_encoding); 655$errorand die sprintf(__("fatal:%s:%s\nwarning: no patches were sent\n"), 656$f,$error); 657} 658} 659} 660 661if(@files) { 662unless($quiet) { 663print$_,"\n"for(@files); 664} 665}else{ 666print STDERR __("\nNo patch files specified!\n\n"); 667 usage(); 668} 669 670sub get_patch_subject { 671my$fn=shift; 672open(my$fh,'<',$fn); 673while(my$line= <$fh>) { 674next unless($line=~/^Subject: (.*)$/); 675close$fh; 676return"GIT:$1\n"; 677} 678close$fh; 679die sprintf(__("No subject line in%s?"),$fn); 680} 681 682if($compose) { 683# Note that this does not need to be secure, but we will make a small 684# effort to have it be unique 685$compose_filename= ($repo? 686 tempfile(".gitsendemail.msg.XXXXXX", DIR =>$repo->repo_path()) : 687 tempfile(".gitsendemail.msg.XXXXXX", DIR =>"."))[1]; 688open my$c,">",$compose_filename 689or die sprintf(__("Failed to open for writing%s:%s"),$compose_filename,$!); 690 691 692my$tpl_sender=$sender||$repoauthor||$repocommitter||''; 693my$tpl_subject=$initial_subject||''; 694my$tpl_in_reply_to=$initial_in_reply_to||''; 695my$tpl_reply_to=$reply_to||''; 696 697print$c<<EOT1, Git::prefix_lines("GIT: ", __ <<EOT2), <<EOT3; 698From$tpl_sender# This line is ignored. 699EOT1 700Lines beginning in "GIT:" will be removed. 701Consider including an overall diffstat or table of contents 702for the patch you are writing. 703 704Clear the body content if you don't wish to send a summary. 705EOT2 706From:$tpl_sender 707Reply-To:$tpl_reply_to 708Subject:$tpl_subject 709In-Reply-To:$tpl_in_reply_to 710 711EOT3 712 for my$f(@files) { 713 print$cget_patch_subject($f); 714 } 715 close$c; 716 717 if ($annotate) { 718 do_edit($compose_filename,@files); 719 } else { 720 do_edit($compose_filename); 721 } 722 723 open$c, "<",$compose_filename 724 or die sprintf(__("Failed to open%s:%s"),$compose_filename,$!); 725 726 if (!defined$compose_encoding) { 727$compose_encoding= "UTF-8"; 728 } 729 730 my%parsed_email; 731 while (my$line= <$c>) { 732 next if$line=~ m/^GIT:/; 733 parse_header_line($line, \%parsed_email); 734 if ($line=~ /^$/) { 735$parsed_email{'body'} = filter_body($c); 736 } 737 } 738 close$c; 739 740 open my$c2, ">",$compose_filename. ".final" 741 or die sprintf(__("Failed to open%s.final:%s"),$compose_filename,$!); 742 743 744 if ($parsed_email{'From'}) { 745$sender= delete($parsed_email{'From'}); 746 } 747 if ($parsed_email{'In-Reply-To'}) { 748$initial_in_reply_to= delete($parsed_email{'In-Reply-To'}); 749 } 750 if ($parsed_email{'Reply-To'}) { 751$reply_to= delete($parsed_email{'Reply-To'}); 752 } 753 if ($parsed_email{'Subject'}) { 754$initial_subject= delete($parsed_email{'Subject'}); 755 print$c2"Subject: " . 756 quote_subject($initial_subject,$compose_encoding) . 757 "\n"; 758 } 759 760 if ($parsed_email{'MIME-Version'}) { 761 print$c2"MIME-Version:$parsed_email{'MIME-Version'}\n", 762 "Content-Type:$parsed_email{'Content-Type'};\n", 763 "Content-Transfer-Encoding:$parsed_email{'Content-Transfer-Encoding'}\n"; 764 delete($parsed_email{'MIME-Version'}); 765 delete($parsed_email{'Content-Type'}); 766 delete($parsed_email{'Content-Transfer-Encoding'}); 767 } elsif (file_has_nonascii($compose_filename)) { 768 my$content_type= (delete($parsed_email{'Content-Type'}) or 769 "text/plain; charset=$compose_encoding"); 770 print$c2"MIME-Version: 1.0\n", 771 "Content-Type:$content_type\n", 772 "Content-Transfer-Encoding: 8bit\n"; 773 } 774 # Preserve unknown headers 775 foreach my$key(keys%parsed_email) { 776 next if$keyeq 'body'; 777 print$c2"$key:$parsed_email{$key}"; 778 } 779 780 if ($parsed_email{'body'}) { 781 print$c2"\n$parsed_email{'body'}\n"; 782 delete($parsed_email{'body'}); 783 } else { 784 print __("Summary email is empty, skipping it\n"); 785$compose= -1; 786 } 787 788 close$c2; 789 790} elsif ($annotate) { 791 do_edit(@files); 792} 793 794sub ask { 795 my ($prompt,%arg) =@_; 796 my$valid_re=$arg{valid_re}; 797 my$default=$arg{default}; 798 my$confirm_only=$arg{confirm_only}; 799 my$resp; 800 my$i= 0; 801 return defined$default?$default: undef 802 unless defined$term->IN and defined fileno($term->IN) and 803 defined$term->OUT and defined fileno($term->OUT); 804 while ($i++< 10) { 805$resp=$term->readline($prompt); 806 if (!defined$resp) { # EOF 807 print "\n"; 808 return defined$default?$default: undef; 809 } 810 if ($respeq '' and defined$default) { 811 return$default; 812 } 813 if (!defined$valid_reor$resp=~ /$valid_re/) { 814 return$resp; 815 } 816 if ($confirm_only) { 817 my$yesno=$term->readline( 818 # TRANSLATORS: please keep [y/N] as is. 819 sprintf(__("Are you sure you want to use <%s> [y/N]? "),$resp)); 820 if (defined$yesno&&$yesno=~ /y/i) { 821 return$resp; 822 } 823 } 824 } 825 return; 826} 827 828sub parse_header_line { 829 my$lines= shift; 830 my$parsed_line= shift; 831 my$addr_pat= join "|", qw(To Cc Bcc); 832 833foreach(split(/\n/,$lines)) { 834if(/^($addr_pat):\s*(.+)$/i) { 835$parsed_line->{$1} = [ parse_address_line($2) ]; 836}elsif(/^([^:]*):\s*(.+)\s*$/i) { 837$parsed_line->{$1} =$2; 838} 839} 840} 841 842sub filter_body { 843my$c=shift; 844my$body=""; 845while(my$body_line= <$c>) { 846if($body_line!~m/^GIT:/) { 847$body.=$body_line; 848} 849} 850return$body; 851} 852 853 854my%broken_encoding; 855 856sub file_declares_8bit_cte { 857my$fn=shift; 858open(my$fh,'<',$fn); 859while(my$line= <$fh>) { 860last if($line=~/^$/); 861return1if($line=~/^Content-Transfer-Encoding: .*8bit.*$/); 862} 863close$fh; 864return0; 865} 866 867foreachmy$f(@files) { 868next unless(body_or_subject_has_nonascii($f) 869&& !file_declares_8bit_cte($f)); 870$broken_encoding{$f} =1; 871} 872 873if(!defined$auto_8bit_encoding&&scalar%broken_encoding) { 874print __("The following files are 8bit, but do not declare ". 875"a Content-Transfer-Encoding.\n"); 876foreachmy$f(sort keys%broken_encoding) { 877print"$f\n"; 878} 879$auto_8bit_encoding= ask(__("Which 8bit encoding should I declare [UTF-8]? "), 880 valid_re =>qr/.{4}/, confirm_only =>1, 881default=>"UTF-8"); 882} 883 884if(!$force) { 885formy$f(@files) { 886if(get_patch_subject($f) =~/\Q*** SUBJECT HERE ***\E/) { 887die sprintf(__("Refusing to send because the patch\n\t%s\n" 888."has the template subject '*** SUBJECT HERE ***'. " 889."Pass --force if you really want to send.\n"),$f); 890} 891} 892} 893 894if(defined$sender) { 895$sender=~s/^\s+|\s+$//g; 896($sender) = expand_aliases($sender); 897}else{ 898$sender=$repoauthor||$repocommitter||''; 899} 900 901# $sender could be an already sanitized address 902# (e.g. sendemail.from could be manually sanitized by user). 903# But it's a no-op to run sanitize_address on an already sanitized address. 904$sender= sanitize_address($sender); 905 906my$to_whom= __("To whom should the emails be sent (if anyone)?"); 907my$prompting=0; 908if(!@initial_to&& !defined$to_cmd) { 909my$to= ask("$to_whom", 910default=>"", 911 valid_re =>qr/\@.*\./, confirm_only =>1); 912push@initial_to, parse_address_line($to)ifdefined$to;# sanitized/validated later 913$prompting++; 914} 915 916sub expand_aliases { 917returnmap{ expand_one_alias($_) }@_; 918} 919 920my%EXPANDED_ALIASES; 921sub expand_one_alias { 922my$alias=shift; 923if($EXPANDED_ALIASES{$alias}) { 924die sprintf(__("fatal: alias '%s' expands to itself\n"),$alias); 925} 926local$EXPANDED_ALIASES{$alias} =1; 927return$aliases{$alias} ? expand_aliases(@{$aliases{$alias}}) :$alias; 928} 929 930@initial_to= process_address_list(@initial_to); 931@initial_cc= process_address_list(@initial_cc); 932@initial_bcc= process_address_list(@initial_bcc); 933 934if($thread&& !defined$initial_in_reply_to&&$prompting) { 935$initial_in_reply_to= ask( 936 __("Message-ID to be used as In-Reply-To for the first email (if any)? "), 937default=>"", 938 valid_re =>qr/\@.*\./, confirm_only =>1); 939} 940if(defined$initial_in_reply_to) { 941$initial_in_reply_to=~s/^\s*<?//; 942$initial_in_reply_to=~s/>?\s*$//; 943$initial_in_reply_to="<$initial_in_reply_to>"if$initial_in_reply_tone''; 944} 945 946if(defined$reply_to) { 947$reply_to=~s/^\s+|\s+$//g; 948($reply_to) = expand_aliases($reply_to); 949$reply_to= sanitize_address($reply_to); 950} 951 952if(!defined$smtp_server) { 953my@sendmail_paths=qw( /usr/sbin/sendmail /usr/lib/sendmail ); 954push@sendmail_paths,map{"$_/sendmail"}split/:/,$ENV{PATH}; 955foreach(@sendmail_paths) { 956if(-x $_) { 957$smtp_server=$_; 958last; 959} 960} 961$smtp_server||='localhost';# could be 127.0.0.1, too... *shrug* 962} 963 964if($compose&&$compose>0) { 965@files= ($compose_filename.".final",@files); 966} 967 968# Variables we set as part of the loop over files 969our($message_id,%mail,$subject,$in_reply_to,$references,$message, 970$needs_confirm,$message_num,$ask_default); 971 972sub extract_valid_address { 973my$address=shift; 974my$local_part_regexp=qr/[^<>"\s@]+/; 975my$domain_regexp=qr/[^.<>"\s@]+(?:\.[^.<>"\s@]+)+/; 976 977# check for a local address: 978return$addressif($address=~/^($local_part_regexp)$/); 979 980$address=~s/^\s*<(.*)>\s*$/$1/; 981if($have_email_valid) { 982returnscalar Email::Valid->address($address); 983} 984 985# less robust/correct than the monster regexp in Email::Valid, 986# but still does a 99% job, and one less dependency 987return$1if$address=~/($local_part_regexp\@$domain_regexp)/; 988return; 989} 990 991sub extract_valid_address_or_die { 992my$address=shift; 993$address= extract_valid_address($address); 994die sprintf(__("error: unable to extract a valid address from:%s\n"),$address) 995if!$address; 996return$address; 997} 998 999sub validate_address {1000my$address=shift;1001while(!extract_valid_address($address)) {1002printf STDERR __("error: unable to extract a valid address from:%s\n"),$address;1003# TRANSLATORS: Make sure to include [q] [d] [e] in your1004# translation. The program will only accept English input1005# at this point.1006$_= ask(__("What to do with this address? ([q]uit|[d]rop|[e]dit): "),1007 valid_re =>qr/^(?:quit|q|drop|d|edit|e)/i,1008default=>'q');1009if(/^d/i) {1010returnundef;1011}elsif(/^q/i) {1012 cleanup_compose_files();1013exit(0);1014}1015$address= ask("$to_whom",1016default=>"",1017 valid_re =>qr/\@.*\./, confirm_only =>1);1018}1019return$address;1020}10211022sub validate_address_list {1023return(grep{defined$_}1024map{ validate_address($_) }@_);1025}10261027# Usually don't need to change anything below here.10281029# we make a "fake" message id by taking the current number1030# of seconds since the beginning of Unix time and tacking on1031# a random number to the end, in case we are called quicker than1032# 1 second since the last time we were called.10331034# We'll setup a template for the message id, using the "from" address:10351036my($message_id_stamp,$message_id_serial);1037sub make_message_id {1038my$uniq;1039if(!defined$message_id_stamp) {1040$message_id_stamp= strftime("%Y%m%d%H%M%S.$$",gmtime(time));1041$message_id_serial=0;1042}1043$message_id_serial++;1044$uniq="$message_id_stamp-$message_id_serial";10451046my$du_part;1047for($sender,$repocommitter,$repoauthor) {1048$du_part= extract_valid_address(sanitize_address($_));1049last if(defined$du_partand$du_partne'');1050}1051if(not defined$du_partor$du_parteq'') {1052require Sys::Hostname;1053$du_part='user@'. Sys::Hostname::hostname();1054}1055my$message_id_template="<%s-%s>";1056$message_id=sprintf($message_id_template,$uniq,$du_part);1057#print "new message id = $message_id\n"; # Was useful for debugging1058}1059106010611062$time=time-scalar$#files;10631064sub unquote_rfc2047 {1065local($_) =@_;1066my$charset;1067my$sep=qr/[ \t]+/;1068 s{$re_encoded_word(?:$sep$re_encoded_word)*}{1069my@words=split$sep,$&;1070foreach(@words) {1071m/$re_encoded_word/;1072$charset=$1;1073my$encoding=$2;1074my$text=$3;1075if($encodingeq'q'||$encodingeq'Q') {1076$_=$text;1077s/_/ /g;1078s/=([0-9A-F]{2})/chr(hex($1))/egi;1079}else{1080# other encodings not supported yet1081}1082}1083join'',@words;1084}eg;1085returnwantarray? ($_,$charset) :$_;1086}10871088sub quote_rfc2047 {1089local$_=shift;1090my$encoding=shift||'UTF-8';1091s/([^-a-zA-Z0-9!*+\/])/sprintf("=%02X",ord($1))/eg;1092s/(.*)/=\?$encoding\?q\?$1\?=/;1093return$_;1094}10951096sub is_rfc2047_quoted {1097my$s=shift;1098length($s) <=75&&1099$s=~m/^(?:"[[:ascii:]]*"|$re_encoded_word)$/o;1100}11011102sub subject_needs_rfc2047_quoting {1103my$s=shift;11041105return($s=~/[^[:ascii:]]/) || ($s=~/=\?/);1106}11071108sub quote_subject {1109local$subject=shift;1110my$encoding=shift||'UTF-8';11111112if(subject_needs_rfc2047_quoting($subject)) {1113return quote_rfc2047($subject,$encoding);1114}1115return$subject;1116}11171118# use the simplest quoting being able to handle the recipient1119sub sanitize_address {1120my($recipient) =@_;11211122# remove garbage after email address1123$recipient=~s/(.*>).*$/$1/;11241125my($recipient_name,$recipient_addr) = ($recipient=~/^(.*?)\s*(<.*)/);11261127if(not$recipient_name) {1128return$recipient;1129}11301131# if recipient_name is already quoted, do nothing1132if(is_rfc2047_quoted($recipient_name)) {1133return$recipient;1134}11351136# remove non-escaped quotes1137$recipient_name=~s/(^|[^\\])"/$1/g;11381139# rfc2047 is needed if a non-ascii char is included1140if($recipient_name=~/[^[:ascii:]]/) {1141$recipient_name= quote_rfc2047($recipient_name);1142}11431144# double quotes are needed if specials or CTLs are included1145elsif($recipient_name=~/[][()<>@,;:\\".\000-\037\177]/) {1146$recipient_name=~s/([\\\r])/\\$1/g;1147$recipient_name=qq["$recipient_name"];1148}11491150return"$recipient_name$recipient_addr";11511152}11531154sub strip_garbage_one_address {1155my($addr) =@_;1156chomp$addr;1157if($addr=~/^(("[^"]*"|[^"<]*)? *<[^>]*>).*/) {1158# "Foo Bar" <foobar@example.com> [possibly garbage here]1159# Foo Bar <foobar@example.com> [possibly garbage here]1160return$1;1161}1162if($addr=~/^(<[^>]*>).*/) {1163# <foo@example.com> [possibly garbage here]1164# if garbage contains other addresses, they are ignored.1165return$1;1166}1167if($addr=~/^([^"#,\s]*)/) {1168# address without quoting: remove anything after the address1169return$1;1170}1171return$addr;1172}11731174sub sanitize_address_list {1175return(map{ sanitize_address($_) }@_);1176}11771178sub process_address_list {1179my@addr_list=map{ parse_address_line($_) }@_;1180@addr_list= expand_aliases(@addr_list);1181@addr_list= sanitize_address_list(@addr_list);1182@addr_list= validate_address_list(@addr_list);1183return@addr_list;1184}11851186# Returns the local Fully Qualified Domain Name (FQDN) if available.1187#1188# Tightly configured MTAa require that a caller sends a real DNS1189# domain name that corresponds the IP address in the HELO/EHLO1190# handshake. This is used to verify the connection and prevent1191# spammers from trying to hide their identity. If the DNS and IP don't1192# match, the receiveing MTA may deny the connection.1193#1194# Here is a deny example of Net::SMTP with the default "localhost.localdomain"1195#1196# Net::SMTP=GLOB(0x267ec28)>>> EHLO localhost.localdomain1197# Net::SMTP=GLOB(0x267ec28)<<< 550 EHLO argument does not match calling host1198#1199# This maildomain*() code is based on ideas in Perl library Test::Reporter1200# /usr/share/perl5/Test/Reporter/Mail/Util.pm ==> sub _maildomain ()12011202sub valid_fqdn {1203my$domain=shift;1204returndefined$domain&& !($^Oeq'darwin'&&$domain=~/\.local$/) &&$domain=~/\./;1205}12061207sub maildomain_net {1208my$maildomain;12091210my$domain= Net::Domain::domainname();1211$maildomain=$domainif valid_fqdn($domain);12121213return$maildomain;1214}12151216sub maildomain_mta {1217my$maildomain;12181219formy$host(qw(mailhost localhost)) {1220my$smtp= Net::SMTP->new($host);1221if(defined$smtp) {1222my$domain=$smtp->domain;1223$smtp->quit;12241225$maildomain=$domainif valid_fqdn($domain);12261227last if$maildomain;1228}1229}12301231return$maildomain;1232}12331234sub maildomain {1235return maildomain_net() || maildomain_mta() ||'localhost.localdomain';1236}12371238sub smtp_host_string {1239if(defined$smtp_server_port) {1240return"$smtp_server:$smtp_server_port";1241}else{1242return$smtp_server;1243}1244}12451246# Returns 1 if authentication succeeded or was not necessary1247# (smtp_user was not specified), and 0 otherwise.12481249sub smtp_auth_maybe {1250if(!defined$smtp_authuser||$auth) {1251return1;1252}12531254# Workaround AUTH PLAIN/LOGIN interaction defect1255# with Authen::SASL::Cyrus1256eval{1257require Authen::SASL;1258 Authen::SASL->import(qw(Perl));1259};12601261# Check mechanism naming as defined in:1262# https://tools.ietf.org/html/rfc4422#page-81263if($smtp_auth&&$smtp_auth!~/^(\b[A-Z0-9-_]{1,20}\s*)*$/) {1264die"invalid smtp auth: '${smtp_auth}'";1265}12661267# TODO: Authentication may fail not because credentials were1268# invalid but due to other reasons, in which we should not1269# reject credentials.1270$auth= Git::credential({1271'protocol'=>'smtp',1272'host'=> smtp_host_string(),1273'username'=>$smtp_authuser,1274# if there's no password, "git credential fill" will1275# give us one, otherwise it'll just pass this one.1276'password'=>$smtp_authpass1277},sub{1278my$cred=shift;12791280if($smtp_auth) {1281my$sasl= Authen::SASL->new(1282 mechanism =>$smtp_auth,1283 callback => {1284 user =>$cred->{'username'},1285 pass =>$cred->{'password'},1286 authname =>$cred->{'username'},1287}1288);12891290return!!$smtp->auth($sasl);1291}12921293return!!$smtp->auth($cred->{'username'},$cred->{'password'});1294});12951296return$auth;1297}12981299sub ssl_verify_params {1300eval{1301require IO::Socket::SSL;1302 IO::Socket::SSL->import(qw/SSL_VERIFY_PEER SSL_VERIFY_NONE/);1303};1304if($@) {1305print STDERR "Not using SSL_VERIFY_PEER due to out-of-date IO::Socket::SSL.\n";1306return;1307}13081309if(!defined$smtp_ssl_cert_path) {1310# use the OpenSSL defaults1311return(SSL_verify_mode => SSL_VERIFY_PEER());1312}13131314if($smtp_ssl_cert_patheq"") {1315return(SSL_verify_mode => SSL_VERIFY_NONE());1316}elsif(-d $smtp_ssl_cert_path) {1317return(SSL_verify_mode => SSL_VERIFY_PEER(),1318 SSL_ca_path =>$smtp_ssl_cert_path);1319}elsif(-f $smtp_ssl_cert_path) {1320return(SSL_verify_mode => SSL_VERIFY_PEER(),1321 SSL_ca_file =>$smtp_ssl_cert_path);1322}else{1323die sprintf(__("CA path\"%s\"does not exist"),$smtp_ssl_cert_path);1324}1325}13261327sub file_name_is_absolute {1328my($path) =@_;13291330# msys does not grok DOS drive-prefixes1331if($^Oeq'msys') {1332return($path=~ m#^/# || $path =~ m#^[a-zA-Z]\:#)1333}13341335require File::Spec::Functions;1336return File::Spec::Functions::file_name_is_absolute($path);1337}13381339# Prepares the email, then asks the user what to do.1340#1341# If the user chooses to send the email, it's sent and 1 is returned.1342# If the user chooses not to send the email, 0 is returned.1343# If the user decides they want to make further edits, -1 is returned and the1344# caller is expected to call send_message again after the edits are performed.1345#1346# If an error occurs sending the email, this just dies.13471348sub send_message {1349my@recipients= unique_email_list(@to);1350@cc= (grep{my$cc= extract_valid_address_or_die($_);1351not grep{$cceq$_||$_=~/<\Q${cc}\E>$/}@recipients1352}1353@cc);1354my$to=join(",\n\t",@recipients);1355@recipients= unique_email_list(@recipients,@cc,@initial_bcc);1356@recipients= (map{ extract_valid_address_or_die($_) }@recipients);1357my$date= format_2822_time($time++);1358my$gitversion='@@GIT_VERSION@@';1359if($gitversion=~m/..GIT_VERSION../) {1360$gitversion= Git::version();1361}13621363my$cc=join(",\n\t", unique_email_list(@cc));1364my$ccline="";1365if($ccne'') {1366$ccline="\nCc:$cc";1367}1368 make_message_id()unlessdefined($message_id);13691370my$header="From:$sender1371To:$to${ccline}1372Subject:$subject1373Date:$date1374Message-Id:$message_id1375";1376if($use_xmailer) {1377$header.="X-Mailer: git-send-email$gitversion\n";1378}1379if($in_reply_to) {13801381$header.="In-Reply-To:$in_reply_to\n";1382$header.="References:$references\n";1383}1384if($reply_to) {1385$header.="Reply-To:$reply_to\n";1386}1387if(@xh) {1388$header.=join("\n",@xh) ."\n";1389}13901391my@sendmail_parameters= ('-i',@recipients);1392my$raw_from=$sender;1393if(defined$envelope_sender&&$envelope_senderne"auto") {1394$raw_from=$envelope_sender;1395}1396$raw_from= extract_valid_address($raw_from);1397unshift(@sendmail_parameters,1398'-f',$raw_from)if(defined$envelope_sender);13991400if($needs_confirm&& !$dry_run) {1401print"\n$header\n";1402if($needs_confirmeq"inform") {1403$confirm_unconfigured=0;# squelch this message for the rest of this run1404$ask_default="y";# assume yes on EOF since user hasn't explicitly asked for confirmation1405print __ <<EOF ;1406 The Cc list above has been expanded by additional1407 addresses found in the patch commit message. By default1408 send-email prompts before sending whenever this occurs.1409 This behavior is controlled by the sendemail.confirm1410 configuration setting.14111412 For additional information, run 'git send-email --help'.1413 To retain the current behavior, but squelch this message,1414 run 'git config --global sendemail.confirm auto'.14151416EOF1417}1418# TRANSLATORS: Make sure to include [y] [n] [e] [q] [a] in your1419# translation. The program will only accept English input1420# at this point.1421$_= ask(__("Send this email? ([y]es|[n]o|[e]dit|[q]uit|[a]ll): "),1422 valid_re =>qr/^(?:yes|y|no|n|edit|e|quit|q|all|a)/i,1423default=>$ask_default);1424die __("Send this email reply required")unlessdefined$_;1425if(/^n/i) {1426return0;1427}elsif(/^e/i) {1428return-1;1429}elsif(/^q/i) {1430 cleanup_compose_files();1431exit(0);1432}elsif(/^a/i) {1433$confirm='never';1434}1435}14361437unshift(@sendmail_parameters,@smtp_server_options);14381439if($dry_run) {1440# We don't want to send the email.1441}elsif(file_name_is_absolute($smtp_server)) {1442my$pid=open my$sm,'|-';1443defined$pidor die$!;1444if(!$pid) {1445exec($smtp_server,@sendmail_parameters)or die$!;1446}1447print$sm"$header\n$message";1448close$smor die$!;1449}else{14501451if(!defined$smtp_server) {1452die __("The required SMTP server is not properly defined.")1453}14541455require Net::SMTP;1456my$use_net_smtp_ssl= version->parse($Net::SMTP::VERSION) < version->parse("2.34");1457$smtp_domain||= maildomain();14581459if($smtp_encryptioneq'ssl') {1460$smtp_server_port||=465;# ssmtp1461require IO::Socket::SSL;14621463# Suppress "variable accessed once" warning.1464{1465no warnings 'once';1466$IO::Socket::SSL::DEBUG =1;1467}14681469# Net::SMTP::SSL->new() does not forward any SSL options1470 IO::Socket::SSL::set_client_defaults(1471 ssl_verify_params());14721473if($use_net_smtp_ssl) {1474require Net::SMTP::SSL;1475$smtp||= Net::SMTP::SSL->new($smtp_server,1476 Hello =>$smtp_domain,1477 Port =>$smtp_server_port,1478 Debug =>$debug_net_smtp);1479}1480else{1481$smtp||= Net::SMTP->new($smtp_server,1482 Hello =>$smtp_domain,1483 Port =>$smtp_server_port,1484 Debug =>$debug_net_smtp,1485 SSL =>1);1486}1487}1488else{1489$smtp_server_port||=25;1490$smtp||= Net::SMTP->new($smtp_server,1491 Hello =>$smtp_domain,1492 Debug =>$debug_net_smtp,1493 Port =>$smtp_server_port);1494if($smtp_encryptioneq'tls'&&$smtp) {1495if($use_net_smtp_ssl) {1496$smtp->command('STARTTLS');1497$smtp->response();1498if($smtp->code!=220) {1499die sprintf(__("Server does not support STARTTLS!%s"),$smtp->message);1500}1501require Net::SMTP::SSL;1502$smtp= Net::SMTP::SSL->start_SSL($smtp,1503 ssl_verify_params())1504or die sprintf(__("STARTTLS failed!%s"), IO::Socket::SSL::errstr());1505}1506else{1507$smtp->starttls(ssl_verify_params())1508or die sprintf(__("STARTTLS failed!%s"), IO::Socket::SSL::errstr());1509}1510$smtp_encryption='';1511# Send EHLO again to receive fresh1512# supported commands1513$smtp->hello($smtp_domain);1514}1515}15161517if(!$smtp) {1518die __("Unable to initialize SMTP properly. Check config and use --smtp-debug."),1519" VALUES: server=$smtp_server",1520"encryption=$smtp_encryption",1521"hello=$smtp_domain",1522defined$smtp_server_port?" port=$smtp_server_port":"";1523}15241525 smtp_auth_maybe or die$smtp->message;15261527$smtp->mail($raw_from)or die$smtp->message;1528$smtp->to(@recipients)or die$smtp->message;1529$smtp->dataor die$smtp->message;1530$smtp->datasend("$header\n")or die$smtp->message;1531my@lines=split/^/,$message;1532foreachmy$line(@lines) {1533$smtp->datasend("$line")or die$smtp->message;1534}1535$smtp->dataend()or die$smtp->message;1536$smtp->code=~/250|200/or die sprintf(__("Failed to send%s\n"),$subject).$smtp->message;1537}1538if($quiet) {1539printf($dry_run? __("Dry-Sent%s\n") : __("Sent%s\n"),$subject);1540}else{1541print($dry_run? __("Dry-OK. Log says:\n") : __("OK. Log says:\n"));1542if(!file_name_is_absolute($smtp_server)) {1543print"Server:$smtp_server\n";1544print"MAIL FROM:<$raw_from>\n";1545foreachmy$entry(@recipients) {1546print"RCPT TO:<$entry>\n";1547}1548}else{1549print"Sendmail:$smtp_server".join(' ',@sendmail_parameters)."\n";1550}1551print$header,"\n";1552if($smtp) {1553print __("Result: "),$smtp->code,' ',1554($smtp->message=~/\n([^\n]+\n)$/s),"\n";1555}else{1556print __("Result: OK\n");1557}1558}15591560return1;1561}15621563$in_reply_to=$initial_in_reply_to;1564$references=$initial_in_reply_to||'';1565$subject=$initial_subject;1566$message_num=0;15671568# Prepares the email, prompts the user, sends it out1569# Returns 0 if an edit was done and the function should be called again, or 11570# otherwise.1571sub process_file {1572my($t) =@_;15731574open my$fh,"<",$tor die sprintf(__("can't open file%s"),$t);15751576my$author=undef;1577my$sauthor=undef;1578my$author_encoding;1579my$has_content_type;1580my$body_encoding;1581my$xfer_encoding;1582my$has_mime_version;1583@to= ();1584@cc= ();1585@xh= ();1586my$input_format=undef;1587my@header= ();1588$message="";1589$message_num++;1590# First unfold multiline header fields1591while(<$fh>) {1592last if/^\s*$/;1593if(/^\s+\S/and@header) {1594chomp($header[$#header]);1595s/^\s+/ /;1596$header[$#header] .=$_;1597}else{1598push(@header,$_);1599}1600}1601# Now parse the header1602foreach(@header) {1603if(/^From /) {1604$input_format='mbox';1605next;1606}1607chomp;1608if(!defined$input_format&&/^[-A-Za-z]+:\s/) {1609$input_format='mbox';1610}16111612if(defined$input_format&&$input_formateq'mbox') {1613if(/^Subject:\s+(.*)$/i) {1614$subject=$1;1615}1616elsif(/^From:\s+(.*)$/i) {1617($author,$author_encoding) = unquote_rfc2047($1);1618$sauthor= sanitize_address($author);1619next if$suppress_cc{'author'};1620next if$suppress_cc{'self'}and$sauthoreq$sender;1621printf(__("(mbox) Adding cc:%sfrom line '%s'\n"),1622$1,$_)unless$quiet;1623push@cc,$1;1624}1625elsif(/^To:\s+(.*)$/i) {1626foreachmy$addr(parse_address_line($1)) {1627printf(__("(mbox) Adding to:%sfrom line '%s'\n"),1628$addr,$_)unless$quiet;1629push@to,$addr;1630}1631}1632elsif(/^Cc:\s+(.*)$/i) {1633foreachmy$addr(parse_address_line($1)) {1634my$qaddr= unquote_rfc2047($addr);1635my$saddr= sanitize_address($qaddr);1636if($saddreq$sender) {1637next if($suppress_cc{'self'});1638}else{1639next if($suppress_cc{'cc'});1640}1641printf(__("(mbox) Adding cc:%sfrom line '%s'\n"),1642$addr,$_)unless$quiet;1643push@cc,$addr;1644}1645}1646elsif(/^Content-type:/i) {1647$has_content_type=1;1648if(/charset="?([^ "]+)/) {1649$body_encoding=$1;1650}1651push@xh,$_;1652}1653elsif(/^MIME-Version/i) {1654$has_mime_version=1;1655push@xh,$_;1656}1657elsif(/^Message-Id: (.*)/i) {1658$message_id=$1;1659}1660elsif(/^Content-Transfer-Encoding: (.*)/i) {1661$xfer_encoding=$1ifnot defined$xfer_encoding;1662}1663elsif(/^In-Reply-To: (.*)/i) {1664$in_reply_to=$1;1665}1666elsif(/^References: (.*)/i) {1667$references=$1;1668}1669elsif(!/^Date:\s/i&&/^[-A-Za-z]+:\s+\S/) {1670push@xh,$_;1671}1672}else{1673# In the traditional1674# "send lots of email" format,1675# line 1 = cc1676# line 2 = subject1677# So let's support that, too.1678$input_format='lots';1679if(@cc==0&& !$suppress_cc{'cc'}) {1680printf(__("(non-mbox) Adding cc:%sfrom line '%s'\n"),1681$_,$_)unless$quiet;1682push@cc,$_;1683}elsif(!defined$subject) {1684$subject=$_;1685}1686}1687}1688# Now parse the message body1689while(<$fh>) {1690$message.=$_;1691if(/^(Signed-off-by|Cc): (.*)/i) {1692chomp;1693my($what,$c) = ($1,$2);1694# strip garbage for the address we'll use:1695$c= strip_garbage_one_address($c);1696# sanitize a bit more to decide whether to suppress the address:1697my$sc= sanitize_address($c);1698if($sceq$sender) {1699next if($suppress_cc{'self'});1700}else{1701next if$suppress_cc{'sob'}and$what=~/Signed-off-by/i;1702next if$suppress_cc{'bodycc'}and$what=~/Cc/i;1703}1704push@cc,$c;1705printf(__("(body) Adding cc:%sfrom line '%s'\n"),1706$c,$_)unless$quiet;1707}1708}1709close$fh;17101711push@to, recipients_cmd("to-cmd","to",$to_cmd,$t)1712ifdefined$to_cmd;1713push@cc, recipients_cmd("cc-cmd","cc",$cc_cmd,$t)1714ifdefined$cc_cmd&& !$suppress_cc{'cccmd'};17151716if($broken_encoding{$t} && !$has_content_type) {1717$xfer_encoding='8bit'ifnot defined$xfer_encoding;1718$has_content_type=1;1719push@xh,"Content-Type: text/plain; charset=$auto_8bit_encoding";1720$body_encoding=$auto_8bit_encoding;1721}17221723if($broken_encoding{$t} && !is_rfc2047_quoted($subject)) {1724$subject= quote_subject($subject,$auto_8bit_encoding);1725}17261727if(defined$sauthorand$sauthorne$sender) {1728$message="From:$author\n\n$message";1729if(defined$author_encoding) {1730if($has_content_type) {1731if($body_encodingeq$author_encoding) {1732# ok, we already have the right encoding1733}1734else{1735# uh oh, we should re-encode1736}1737}1738else{1739$xfer_encoding='8bit'ifnot defined$xfer_encoding;1740$has_content_type=1;1741push@xh,1742"Content-Type: text/plain; charset=$author_encoding";1743}1744}1745}1746$xfer_encoding='8bit'ifnot defined$xfer_encoding;1747($message,$xfer_encoding) = apply_transfer_encoding(1748$message,$xfer_encoding,$target_xfer_encoding);1749push@xh,"Content-Transfer-Encoding:$xfer_encoding";1750unshift@xh,'MIME-Version: 1.0'unless$has_mime_version;17511752$needs_confirm= (1753$confirmeq"always"or1754($confirm=~/^(?:auto|cc)$/&&@cc)or1755($confirm=~/^(?:auto|compose)$/&&$compose&&$message_num==1));1756$needs_confirm="inform"if($needs_confirm&&$confirm_unconfigured&&@cc);17571758@to= process_address_list(@to);1759@cc= process_address_list(@cc);17601761@to= (@initial_to,@to);1762@cc= (@initial_cc,@cc);17631764if($message_num==1) {1765if(defined$cover_ccand$cover_cc) {1766@initial_cc=@cc;1767}1768if(defined$cover_toand$cover_to) {1769@initial_to=@to;1770}1771}17721773my$message_was_sent= send_message();1774if($message_was_sent== -1) {1775 do_edit($t);1776return0;1777}17781779# set up for the next message1780if($thread&&$message_was_sent&&1781($chain_reply_to|| !defined$in_reply_to||length($in_reply_to) ==0||1782$message_num==1)) {1783$in_reply_to=$message_id;1784if(length$references>0) {1785$references.="\n$message_id";1786}else{1787$references="$message_id";1788}1789}1790$message_id=undef;1791$num_sent++;1792if(defined$batch_size&&$num_sent==$batch_size) {1793$num_sent=0;1794$smtp->quitifdefined$smtp;1795undef$smtp;1796undef$auth;1797sleep($relogin_delay)ifdefined$relogin_delay;1798}17991800return1;1801}18021803foreachmy$t(@files) {1804while(!process_file($t)) {1805# user edited the file1806}1807}18081809# Execute a command (e.g. $to_cmd) to get a list of email addresses1810# and return a results array1811sub recipients_cmd {1812my($prefix,$what,$cmd,$file) =@_;18131814my@addresses= ();1815open my$fh,"-|","$cmd\Q$file\E"1816or die sprintf(__("(%s) Could not execute '%s'"),$prefix,$cmd);1817while(my$address= <$fh>) {1818$address=~s/^\s*//g;1819$address=~s/\s*$//g;1820$address= sanitize_address($address);1821next if($addresseq$senderand$suppress_cc{'self'});1822push@addresses,$address;1823printf(__("(%s) Adding%s:%sfrom: '%s'\n"),1824$prefix,$what,$address,$cmd)unless$quiet;1825}1826close$fh1827or die sprintf(__("(%s) failed to close pipe to '%s'"),$prefix,$cmd);1828return@addresses;1829}18301831cleanup_compose_files();18321833sub cleanup_compose_files {1834unlink($compose_filename,$compose_filename.".final")if$compose;1835}18361837$smtp->quitif$smtp;18381839sub apply_transfer_encoding {1840my$message=shift;1841my$from=shift;1842my$to=shift;18431844return$messageif($fromeq$toand$fromne'7bit');18451846require MIME::QuotedPrint;1847require MIME::Base64;18481849$message= MIME::QuotedPrint::decode($message)1850if($fromeq'quoted-printable');1851$message= MIME::Base64::decode($message)1852if($fromeq'base64');18531854$to= ($message=~/.{999,}/) ?'quoted-printable':'8bit'1855if$toeq'auto';18561857die __("cannot send message as 7bit")1858if($toeq'7bit'and$message=~/[^[:ascii:]]/);1859return($message,$to)1860if($toeq'7bit'or$toeq'8bit');1861return(MIME::QuotedPrint::encode($message,"\n",0),$to)1862if($toeq'quoted-printable');1863return(MIME::Base64::encode($message,"\n"),$to)1864if($toeq'base64');1865die __("invalid transfer encoding");1866}18671868sub unique_email_list {1869my%seen;1870my@emails;18711872foreachmy$entry(@_) {1873my$clean= extract_valid_address_or_die($entry);1874$seen{$clean} ||=0;1875next if$seen{$clean}++;1876push@emails,$entry;1877}1878return@emails;1879}18801881sub validate_patch {1882my($fn,$xfer_encoding) =@_;18831884if($repo) {1885my$validate_hook= catfile(catdir($repo->repo_path(),'hooks'),1886'sendemail-validate');1887my$hook_error;1888if(-x $validate_hook) {1889my$target= abs_path($fn);1890# The hook needs a correct cwd and GIT_DIR.1891my$cwd_save= cwd();1892chdir($repo->wc_path()or$repo->repo_path())1893or die("chdir:$!");1894local$ENV{"GIT_DIR"} =$repo->repo_path();1895$hook_error="rejected by sendemail-validate hook"1896ifsystem($validate_hook,$target);1897chdir($cwd_save)or die("chdir:$!");1898}1899return$hook_errorif$hook_error;1900}19011902# Any long lines will be automatically fixed if we use a suitable transfer1903# encoding.1904unless($xfer_encoding=~/^(?:auto|quoted-printable|base64)$/) {1905open(my$fh,'<',$fn)1906or die sprintf(__("unable to open%s:%s\n"),$fn,$!);1907while(my$line= <$fh>) {1908if(length($line) >998) {1909returnsprintf(__("%s: patch contains a line longer than 998 characters"), $.);1910}1911}1912}1913return;1914}19151916sub handle_backup {1917my($last,$lastlen,$file,$known_suffix) =@_;1918my($suffix,$skip);19191920$skip=0;1921if(defined$last&&1922($lastlen<length($file)) &&1923(substr($file,0,$lastlen)eq$last) &&1924($suffix=substr($file,$lastlen)) !~/^[a-z0-9]/i) {1925if(defined$known_suffix&&$suffixeq$known_suffix) {1926printf(__("Skipping%swith backup suffix '%s'.\n"),$file,$known_suffix);1927$skip=1;1928}else{1929# TRANSLATORS: please keep "[y|N]" as is.1930my$answer= ask(sprintf(__("Do you really want to send%s?[y|N]: "),$file),1931 valid_re =>qr/^(?:y|n)/i,1932default=>'n');1933$skip= ($answerne'y');1934if($skip) {1935$known_suffix=$suffix;1936}1937}1938}1939return($skip,$known_suffix);1940}19411942sub handle_backup_files {1943my@file=@_;1944my($last,$lastlen,$known_suffix,$skip,@result);1945formy$file(@file) {1946($skip,$known_suffix) = handle_backup($last,$lastlen,1947$file,$known_suffix);1948push@result,$fileunless$skip;1949$last=$file;1950$lastlen=length($file);1951}1952return@result;1953}19541955sub file_has_nonascii {1956my$fn=shift;1957open(my$fh,'<',$fn)1958or die sprintf(__("unable to open%s:%s\n"),$fn,$!);1959while(my$line= <$fh>) {1960return1if$line=~/[^[:ascii:]]/;1961}1962return0;1963}19641965sub body_or_subject_has_nonascii {1966my$fn=shift;1967open(my$fh,'<',$fn)1968or die sprintf(__("unable to open%s:%s\n"),$fn,$!);1969while(my$line= <$fh>) {1970last if$line=~/^$/;1971return1if$line=~/^Subject.*[^[:ascii:]]/;1972}1973while(my$line= <$fh>) {1974return1if$line=~/[^[:ascii:]]/;1975}1976return0;1977}