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 122sub completion_helper { 123print Git::command('format-patch','--git-completion-helper'); 124exit(0); 125} 126 127# most mail servers generate the Date: header, but not all... 128sub format_2822_time { 129my($time) =@_; 130my@localtm=localtime($time); 131my@gmttm=gmtime($time); 132my$localmin=$localtm[1] +$localtm[2] *60; 133my$gmtmin=$gmttm[1] +$gmttm[2] *60; 134if($localtm[0] !=$gmttm[0]) { 135die __("local zone differs from GMT by a non-minute interval\n"); 136} 137if((($gmttm[6] +1) %7) ==$localtm[6]) { 138$localmin+=1440; 139}elsif((($gmttm[6] -1) %7) ==$localtm[6]) { 140$localmin-=1440; 141}elsif($gmttm[6] !=$localtm[6]) { 142die __("local time offset greater than or equal to 24 hours\n"); 143} 144my$offset=$localmin-$gmtmin; 145my$offhour=$offset/60; 146my$offmin=abs($offset%60); 147if(abs($offhour) >=24) { 148die __("local time offset greater than or equal to 24 hours\n"); 149} 150 151returnsprintf("%s,%2d%s%d%02d:%02d:%02d%s%02d%02d", 152qw(Sun Mon Tue Wed Thu Fri Sat)[$localtm[6]], 153$localtm[3], 154qw(Jan Feb Mar Apr May Jun 155 Jul Aug Sep Oct Nov Dec)[$localtm[4]], 156$localtm[5]+1900, 157$localtm[2], 158$localtm[1], 159$localtm[0], 160($offset>=0) ?'+':'-', 161abs($offhour), 162$offmin, 163); 164} 165 166my$have_email_valid=eval{require Email::Valid;1}; 167my$smtp; 168my$auth; 169my$num_sent=0; 170 171# Regexes for RFC 2047 productions. 172my$re_token=qr/[^][()<>@,;:\\"\/?.=\000-\037\177-\377]+/; 173my$re_encoded_text=qr/[^? \000-\037\177-\377]+/; 174my$re_encoded_word=qr/=\?($re_token)\?($re_token)\?($re_encoded_text)\?=/; 175 176# Variables we fill in automatically, or via prompting: 177my(@to,$no_to,@initial_to,@cc,$no_cc,@initial_cc,@bcclist,$no_bcc,@xh, 178$initial_in_reply_to,$reply_to,$initial_subject,@files, 179$author,$sender,$smtp_authpass,$annotate,$use_xmailer,$compose,$time); 180 181my$envelope_sender; 182 183# Example reply to: 184#$initial_in_reply_to = ''; #<20050203173208.GA23964@foobar.com>'; 185 186my$repo=eval{ Git->repository() }; 187my@repo=$repo? ($repo) : (); 188my$term=eval{ 189$ENV{"GIT_SEND_EMAIL_NOTTY"} 190? new Term::ReadLine 'git-send-email', \*STDIN, \*STDOUT 191: new Term::ReadLine 'git-send-email'; 192}; 193if($@) { 194$term= new FakeTerm "$@: going non-interactive"; 195} 196 197# Behavior modification variables 198my($quiet,$dry_run) = (0,0); 199my$format_patch; 200my$compose_filename; 201my$force=0; 202my$dump_aliases=0; 203 204# Handle interactive edition of files. 205my$multiedit; 206my$editor; 207 208sub do_edit { 209if(!defined($editor)) { 210$editor= Git::command_oneline('var','GIT_EDITOR'); 211} 212if(defined($multiedit) && !$multiedit) { 213map{ 214system('sh','-c',$editor.' "$@"',$editor,$_); 215if(($?&127) || ($?>>8)) { 216die(__("the editor exited uncleanly, aborting everything")); 217} 218}@_; 219}else{ 220system('sh','-c',$editor.' "$@"',$editor,@_); 221if(($?&127) || ($?>>8)) { 222die(__("the editor exited uncleanly, aborting everything")); 223} 224} 225} 226 227# Variables with corresponding config settings 228my($thread,$chain_reply_to,$suppress_from,$signed_off_by_cc); 229my($cover_cc,$cover_to); 230my($to_cmd,$cc_cmd); 231my($smtp_server,$smtp_server_port,@smtp_server_options); 232my($smtp_authuser,$smtp_encryption,$smtp_ssl_cert_path); 233my($batch_size,$relogin_delay); 234my($identity,$aliasfiletype,@alias_files,$smtp_domain,$smtp_auth); 235my($validate,$confirm); 236my(@suppress_cc); 237my($auto_8bit_encoding); 238my($compose_encoding); 239my$target_xfer_encoding='auto'; 240 241my($debug_net_smtp) =0;# Net::SMTP, see send_message() 242 243my%config_bool_settings= ( 244"thread"=> [\$thread,1], 245"chainreplyto"=> [\$chain_reply_to,0], 246"suppressfrom"=> [\$suppress_from,undef], 247"signedoffbycc"=> [\$signed_off_by_cc,undef], 248"cccover"=> [\$cover_cc,undef], 249"tocover"=> [\$cover_to,undef], 250"signedoffcc"=> [\$signed_off_by_cc,undef],# Deprecated 251"validate"=> [\$validate,1], 252"multiedit"=> [\$multiedit,undef], 253"annotate"=> [\$annotate,undef], 254"xmailer"=> [\$use_xmailer,1] 255); 256 257my%config_settings= ( 258"smtpserver"=> \$smtp_server, 259"smtpserverport"=> \$smtp_server_port, 260"smtpserveroption"=> \@smtp_server_options, 261"smtpuser"=> \$smtp_authuser, 262"smtppass"=> \$smtp_authpass, 263"smtpdomain"=> \$smtp_domain, 264"smtpauth"=> \$smtp_auth, 265"smtpbatchsize"=> \$batch_size, 266"smtprelogindelay"=> \$relogin_delay, 267"to"=> \@initial_to, 268"tocmd"=> \$to_cmd, 269"cc"=> \@initial_cc, 270"cccmd"=> \$cc_cmd, 271"aliasfiletype"=> \$aliasfiletype, 272"bcc"=> \@bcclist, 273"suppresscc"=> \@suppress_cc, 274"envelopesender"=> \$envelope_sender, 275"confirm"=> \$confirm, 276"from"=> \$sender, 277"assume8bitencoding"=> \$auto_8bit_encoding, 278"composeencoding"=> \$compose_encoding, 279"transferencoding"=> \$target_xfer_encoding, 280); 281 282my%config_path_settings= ( 283"aliasesfile"=> \@alias_files, 284"smtpsslcertpath"=> \$smtp_ssl_cert_path, 285); 286 287# Handle Uncouth Termination 288sub signal_handler { 289 290# Make text normal 291print color("reset"),"\n"; 292 293# SMTP password masked 294system"stty echo"; 295 296# tmp files from --compose 297if(defined$compose_filename) { 298if(-e $compose_filename) { 299printf __("'%s' contains an intermediate version ". 300"of the email you were composing.\n"), 301$compose_filename; 302} 303if(-e ($compose_filename.".final")) { 304printf __("'%s.final' contains the composed email.\n"), 305$compose_filename; 306} 307} 308 309exit; 310}; 311 312$SIG{TERM} = \&signal_handler; 313$SIG{INT} = \&signal_handler; 314 315# Begin by accumulating all the variables (defined above), that we will end up 316# needing, first, from the command line: 317 318my$help; 319my$git_completion_helper; 320my$rc= GetOptions("h"=> \$help, 321"dump-aliases"=> \$dump_aliases); 322usage()unless$rc; 323die __("--dump-aliases incompatible with other options\n") 324if!$helpand$dump_aliasesand@ARGV; 325$rc= GetOptions( 326"sender|from=s"=> \$sender, 327"in-reply-to=s"=> \$initial_in_reply_to, 328"reply-to=s"=> \$reply_to, 329"subject=s"=> \$initial_subject, 330"to=s"=> \@initial_to, 331"to-cmd=s"=> \$to_cmd, 332"no-to"=> \$no_to, 333"cc=s"=> \@initial_cc, 334"no-cc"=> \$no_cc, 335"bcc=s"=> \@bcclist, 336"no-bcc"=> \$no_bcc, 337"chain-reply-to!"=> \$chain_reply_to, 338"no-chain-reply-to"=>sub{$chain_reply_to=0}, 339"smtp-server=s"=> \$smtp_server, 340"smtp-server-option=s"=> \@smtp_server_options, 341"smtp-server-port=s"=> \$smtp_server_port, 342"smtp-user=s"=> \$smtp_authuser, 343"smtp-pass:s"=> \$smtp_authpass, 344"smtp-ssl"=>sub{$smtp_encryption='ssl'}, 345"smtp-encryption=s"=> \$smtp_encryption, 346"smtp-ssl-cert-path=s"=> \$smtp_ssl_cert_path, 347"smtp-debug:i"=> \$debug_net_smtp, 348"smtp-domain:s"=> \$smtp_domain, 349"smtp-auth=s"=> \$smtp_auth, 350"identity=s"=> \$identity, 351"annotate!"=> \$annotate, 352"no-annotate"=>sub{$annotate=0}, 353"compose"=> \$compose, 354"quiet"=> \$quiet, 355"cc-cmd=s"=> \$cc_cmd, 356"suppress-from!"=> \$suppress_from, 357"no-suppress-from"=>sub{$suppress_from=0}, 358"suppress-cc=s"=> \@suppress_cc, 359"signed-off-cc|signed-off-by-cc!"=> \$signed_off_by_cc, 360"no-signed-off-cc|no-signed-off-by-cc"=>sub{$signed_off_by_cc=0}, 361"cc-cover|cc-cover!"=> \$cover_cc, 362"no-cc-cover"=>sub{$cover_cc=0}, 363"to-cover|to-cover!"=> \$cover_to, 364"no-to-cover"=>sub{$cover_to=0}, 365"confirm=s"=> \$confirm, 366"dry-run"=> \$dry_run, 367"envelope-sender=s"=> \$envelope_sender, 368"thread!"=> \$thread, 369"no-thread"=>sub{$thread=0}, 370"validate!"=> \$validate, 371"no-validate"=>sub{$validate=0}, 372"transfer-encoding=s"=> \$target_xfer_encoding, 373"format-patch!"=> \$format_patch, 374"no-format-patch"=>sub{$format_patch=0}, 375"8bit-encoding=s"=> \$auto_8bit_encoding, 376"compose-encoding=s"=> \$compose_encoding, 377"force"=> \$force, 378"xmailer!"=> \$use_xmailer, 379"no-xmailer"=>sub{$use_xmailer=0}, 380"batch-size=i"=> \$batch_size, 381"relogin-delay=i"=> \$relogin_delay, 382"git-completion-helper"=> \$git_completion_helper, 383); 384 385usage()if$help; 386completion_helper()if$git_completion_helper; 387unless($rc) { 388 usage(); 389} 390 391die __("Cannot run git format-patch from outside a repository\n") 392if$format_patchand not$repo; 393 394die __("`batch-size` and `relogin` must be specified together ". 395"(via command-line or configuration option)\n") 396ifdefined$relogin_delayand not defined$batch_size; 397 398# Now, let's fill any that aren't set in with defaults: 399 400sub read_config { 401my($prefix) =@_; 402 403foreachmy$setting(keys%config_bool_settings) { 404my$target=$config_bool_settings{$setting}->[0]; 405$$target= Git::config_bool(@repo,"$prefix.$setting")unless(defined$$target); 406} 407 408foreachmy$setting(keys%config_path_settings) { 409my$target=$config_path_settings{$setting}; 410if(ref($target)eq"ARRAY") { 411unless(@$target) { 412my@values= Git::config_path(@repo,"$prefix.$setting"); 413@$target=@valuesif(@values&&defined$values[0]); 414} 415} 416else{ 417$$target= Git::config_path(@repo,"$prefix.$setting")unless(defined$$target); 418} 419} 420 421foreachmy$setting(keys%config_settings) { 422my$target=$config_settings{$setting}; 423next if$settingeq"to"and defined$no_to; 424next if$settingeq"cc"and defined$no_cc; 425next if$settingeq"bcc"and defined$no_bcc; 426if(ref($target)eq"ARRAY") { 427unless(@$target) { 428my@values= Git::config(@repo,"$prefix.$setting"); 429@$target=@valuesif(@values&&defined$values[0]); 430} 431} 432else{ 433$$target= Git::config(@repo,"$prefix.$setting")unless(defined$$target); 434} 435} 436 437if(!defined$smtp_encryption) { 438my$enc= Git::config(@repo,"$prefix.smtpencryption"); 439if(defined$enc) { 440$smtp_encryption=$enc; 441}elsif(Git::config_bool(@repo,"$prefix.smtpssl")) { 442$smtp_encryption='ssl'; 443} 444} 445} 446 447# read configuration from [sendemail "$identity"], fall back on [sendemail] 448$identity= Git::config(@repo,"sendemail.identity")unless(defined$identity); 449read_config("sendemail.$identity")if(defined$identity); 450read_config("sendemail"); 451 452# fall back on builtin bool defaults 453foreachmy$setting(values%config_bool_settings) { 454${$setting->[0]} =$setting->[1]unless(defined(${$setting->[0]})); 455} 456 457# 'default' encryption is none -- this only prevents a warning 458$smtp_encryption=''unless(defined$smtp_encryption); 459 460# Set CC suppressions 461my(%suppress_cc); 462if(@suppress_cc) { 463foreachmy$entry(@suppress_cc) { 464die sprintf(__("Unknown --suppress-cc field: '%s'\n"),$entry) 465unless$entry=~/^(?:all|cccmd|cc|author|self|sob|body|bodycc)$/; 466$suppress_cc{$entry} =1; 467} 468} 469 470if($suppress_cc{'all'}) { 471foreachmy$entry(qw (cccmd cc author self sob body bodycc)) { 472$suppress_cc{$entry} =1; 473} 474delete$suppress_cc{'all'}; 475} 476 477# If explicit old-style ones are specified, they trump --suppress-cc. 478$suppress_cc{'self'} =$suppress_fromifdefined$suppress_from; 479$suppress_cc{'sob'} = !$signed_off_by_ccifdefined$signed_off_by_cc; 480 481if($suppress_cc{'body'}) { 482foreachmy$entry(qw (sob bodycc)) { 483$suppress_cc{$entry} =1; 484} 485delete$suppress_cc{'body'}; 486} 487 488# Set confirm's default value 489my$confirm_unconfigured= !defined$confirm; 490if($confirm_unconfigured) { 491$confirm=scalar%suppress_cc?'compose':'auto'; 492}; 493die sprintf(__("Unknown --confirm setting: '%s'\n"),$confirm) 494unless$confirm=~/^(?:auto|cc|compose|always|never)/; 495 496# Debugging, print out the suppressions. 497if(0) { 498print"suppressions:\n"; 499foreachmy$entry(keys%suppress_cc) { 500printf" %-5s ->$suppress_cc{$entry}\n",$entry; 501} 502} 503 504my($repoauthor,$repocommitter); 505($repoauthor) = Git::ident_person(@repo,'author'); 506($repocommitter) = Git::ident_person(@repo,'committer'); 507 508sub parse_address_line { 509returnmap{$_->format} Mail::Address->parse($_[0]); 510} 511 512sub split_addrs { 513return quotewords('\s*,\s*',1,@_); 514} 515 516my%aliases; 517 518sub parse_sendmail_alias { 519local$_=shift; 520if(/"/) { 521printf STDERR __("warning: sendmail alias with quotes is not supported:%s\n"),$_; 522}elsif(/:include:/) { 523printf STDERR __("warning: `:include:` not supported:%s\n"),$_; 524}elsif(/[\/|]/) { 525printf STDERR __("warning: `/file` or `|pipe` redirection not supported:%s\n"),$_; 526}elsif(/^(\S+?)\s*:\s*(.+)$/) { 527my($alias,$addr) = ($1,$2); 528$aliases{$alias} = [ split_addrs($addr) ]; 529}else{ 530printf STDERR __("warning: sendmail line is not recognized:%s\n"),$_; 531} 532} 533 534sub parse_sendmail_aliases { 535my$fh=shift; 536my$s=''; 537while(<$fh>) { 538chomp; 539next if/^\s*$/||/^\s*#/; 540$s.=$_,next if$s=~s/\\$//||s/^\s+//; 541 parse_sendmail_alias($s)if$s; 542$s=$_; 543} 544$s=~s/\\$//;# silently tolerate stray '\' on last line 545 parse_sendmail_alias($s)if$s; 546} 547 548my%parse_alias= ( 549# multiline formats can be supported in the future 550 mutt =>sub{my$fh=shift;while(<$fh>) { 551if(/^\s*alias\s+(?:-group\s+\S+\s+)*(\S+)\s+(.*)$/) { 552my($alias,$addr) = ($1,$2); 553$addr=~s/#.*$//;# mutt allows # comments 554# commas delimit multiple addresses 555my@addr= split_addrs($addr); 556 557# quotes may be escaped in the file, 558# unescape them so we do not double-escape them later. 559s/\\"/"/gforeach@addr; 560$aliases{$alias} = \@addr 561}}}, 562 mailrc =>sub{my$fh=shift;while(<$fh>) { 563if(/^alias\s+(\S+)\s+(.*?)\s*$/) { 564# spaces delimit multiple addresses 565$aliases{$1} = [ quotewords('\s+',0,$2) ]; 566}}}, 567 pine =>sub{my$fh=shift;my$f='\t[^\t]*'; 568for(my$x='';defined($x);$x=$_) { 569chomp$x; 570$x.=$1while(defined($_= <$fh>) &&/^ +(.*)$/); 571$x=~/^(\S+)$f\t\(?([^\t]+?)\)?(:?$f){0,2}$/ornext; 572$aliases{$1} = [ split_addrs($2) ]; 573}}, 574 elm =>sub{my$fh=shift; 575while(<$fh>) { 576if(/^(\S+)\s+=\s+[^=]+=\s(\S+)/) { 577my($alias,$addr) = ($1,$2); 578$aliases{$alias} = [ split_addrs($addr) ]; 579} 580} }, 581 sendmail => \&parse_sendmail_aliases, 582 gnus =>sub{my$fh=shift;while(<$fh>) { 583if(/\(define-mail-alias\s+"(\S+?)"\s+"(\S+?)"\)/) { 584$aliases{$1} = [$2]; 585}}} 586); 587 588if(@alias_filesand$aliasfiletypeand defined$parse_alias{$aliasfiletype}) { 589foreachmy$file(@alias_files) { 590open my$fh,'<',$fileor die"opening$file:$!\n"; 591$parse_alias{$aliasfiletype}->($fh); 592close$fh; 593} 594} 595 596if($dump_aliases) { 597print"$_\n"for(sort keys%aliases); 598exit(0); 599} 600 601# is_format_patch_arg($f) returns 0 if $f names a patch, or 1 if 602# $f is a revision list specification to be passed to format-patch. 603sub is_format_patch_arg { 604return unless$repo; 605my$f=shift; 606try{ 607$repo->command('rev-parse','--verify','--quiet',$f); 608if(defined($format_patch)) { 609return$format_patch; 610} 611die sprintf(__ <<EOF,$f,$f); 612File '%s' exists but it could also be the range of commits 613to produce patches for. Please disambiguate by... 614 615 * Saying "./%s" if you mean a file; or 616 * Giving --format-patch option if you mean a range. 617EOF 618} catch Git::Error::Command with { 619# Not a valid revision. Treat it as a filename. 620return0; 621} 622} 623 624# Now that all the defaults are set, process the rest of the command line 625# arguments and collect up the files that need to be processed. 626my@rev_list_opts; 627while(defined(my$f=shift@ARGV)) { 628if($feq"--") { 629push@rev_list_opts,"--",@ARGV; 630@ARGV= (); 631}elsif(-d $fand!is_format_patch_arg($f)) { 632opendir my$dh,$f 633or die sprintf(__("Failed to opendir%s:%s"),$f,$!); 634 635push@files,grep{ -f $_}map{ catfile($f,$_) } 636sort readdir$dh; 637closedir$dh; 638}elsif((-f $for-p $f)and!is_format_patch_arg($f)) { 639push@files,$f; 640}else{ 641push@rev_list_opts,$f; 642} 643} 644 645if(@rev_list_opts) { 646die __("Cannot run git format-patch from outside a repository\n") 647unless$repo; 648push@files,$repo->command('format-patch','-o', tempdir(CLEANUP =>1),@rev_list_opts); 649} 650 651@files= handle_backup_files(@files); 652 653if($validate) { 654foreachmy$f(@files) { 655unless(-p $f) { 656my$error= validate_patch($f,$target_xfer_encoding); 657$errorand die sprintf(__("fatal:%s:%s\nwarning: no patches were sent\n"), 658$f,$error); 659} 660} 661} 662 663if(@files) { 664unless($quiet) { 665print$_,"\n"for(@files); 666} 667}else{ 668print STDERR __("\nNo patch files specified!\n\n"); 669 usage(); 670} 671 672sub get_patch_subject { 673my$fn=shift; 674open(my$fh,'<',$fn); 675while(my$line= <$fh>) { 676next unless($line=~/^Subject: (.*)$/); 677close$fh; 678return"GIT:$1\n"; 679} 680close$fh; 681die sprintf(__("No subject line in%s?"),$fn); 682} 683 684if($compose) { 685# Note that this does not need to be secure, but we will make a small 686# effort to have it be unique 687$compose_filename= ($repo? 688 tempfile(".gitsendemail.msg.XXXXXX", DIR =>$repo->repo_path()) : 689 tempfile(".gitsendemail.msg.XXXXXX", DIR =>"."))[1]; 690open my$c,">",$compose_filename 691or die sprintf(__("Failed to open for writing%s:%s"),$compose_filename,$!); 692 693 694my$tpl_sender=$sender||$repoauthor||$repocommitter||''; 695my$tpl_subject=$initial_subject||''; 696my$tpl_in_reply_to=$initial_in_reply_to||''; 697my$tpl_reply_to=$reply_to||''; 698 699print$c<<EOT1, Git::prefix_lines("GIT: ", __ <<EOT2), <<EOT3; 700From$tpl_sender# This line is ignored. 701EOT1 702Lines beginning in "GIT:" will be removed. 703Consider including an overall diffstat or table of contents 704for the patch you are writing. 705 706Clear the body content if you don't wish to send a summary. 707EOT2 708From:$tpl_sender 709Reply-To:$tpl_reply_to 710Subject:$tpl_subject 711In-Reply-To:$tpl_in_reply_to 712 713EOT3 714 for my$f(@files) { 715 print$cget_patch_subject($f); 716 } 717 close$c; 718 719 if ($annotate) { 720 do_edit($compose_filename,@files); 721 } else { 722 do_edit($compose_filename); 723 } 724 725 open$c, "<",$compose_filename 726 or die sprintf(__("Failed to open%s:%s"),$compose_filename,$!); 727 728 if (!defined$compose_encoding) { 729$compose_encoding= "UTF-8"; 730 } 731 732 my%parsed_email; 733 while (my$line= <$c>) { 734 next if$line=~ m/^GIT:/; 735 parse_header_line($line, \%parsed_email); 736 if ($line=~ /^$/) { 737$parsed_email{'body'} = filter_body($c); 738 } 739 } 740 close$c; 741 742 open my$c2, ">",$compose_filename. ".final" 743 or die sprintf(__("Failed to open%s.final:%s"),$compose_filename,$!); 744 745 746 if ($parsed_email{'From'}) { 747$sender= delete($parsed_email{'From'}); 748 } 749 if ($parsed_email{'In-Reply-To'}) { 750$initial_in_reply_to= delete($parsed_email{'In-Reply-To'}); 751 } 752 if ($parsed_email{'Reply-To'}) { 753$reply_to= delete($parsed_email{'Reply-To'}); 754 } 755 if ($parsed_email{'Subject'}) { 756$initial_subject= delete($parsed_email{'Subject'}); 757 print$c2"Subject: " . 758 quote_subject($initial_subject,$compose_encoding) . 759 "\n"; 760 } 761 762 if ($parsed_email{'MIME-Version'}) { 763 print$c2"MIME-Version:$parsed_email{'MIME-Version'}\n", 764 "Content-Type:$parsed_email{'Content-Type'};\n", 765 "Content-Transfer-Encoding:$parsed_email{'Content-Transfer-Encoding'}\n"; 766 delete($parsed_email{'MIME-Version'}); 767 delete($parsed_email{'Content-Type'}); 768 delete($parsed_email{'Content-Transfer-Encoding'}); 769 } elsif (file_has_nonascii($compose_filename)) { 770 my$content_type= (delete($parsed_email{'Content-Type'}) or 771 "text/plain; charset=$compose_encoding"); 772 print$c2"MIME-Version: 1.0\n", 773 "Content-Type:$content_type\n", 774 "Content-Transfer-Encoding: 8bit\n"; 775 } 776 # Preserve unknown headers 777 foreach my$key(keys%parsed_email) { 778 next if$keyeq 'body'; 779 print$c2"$key:$parsed_email{$key}"; 780 } 781 782 if ($parsed_email{'body'}) { 783 print$c2"\n$parsed_email{'body'}\n"; 784 delete($parsed_email{'body'}); 785 } else { 786 print __("Summary email is empty, skipping it\n"); 787$compose= -1; 788 } 789 790 close$c2; 791 792} elsif ($annotate) { 793 do_edit(@files); 794} 795 796sub ask { 797 my ($prompt,%arg) =@_; 798 my$valid_re=$arg{valid_re}; 799 my$default=$arg{default}; 800 my$confirm_only=$arg{confirm_only}; 801 my$resp; 802 my$i= 0; 803 return defined$default?$default: undef 804 unless defined$term->IN and defined fileno($term->IN) and 805 defined$term->OUT and defined fileno($term->OUT); 806 while ($i++< 10) { 807$resp=$term->readline($prompt); 808 if (!defined$resp) { # EOF 809 print "\n"; 810 return defined$default?$default: undef; 811 } 812 if ($respeq '' and defined$default) { 813 return$default; 814 } 815 if (!defined$valid_reor$resp=~ /$valid_re/) { 816 return$resp; 817 } 818 if ($confirm_only) { 819 my$yesno=$term->readline( 820 # TRANSLATORS: please keep [y/N] as is. 821 sprintf(__("Are you sure you want to use <%s> [y/N]? "),$resp)); 822 if (defined$yesno&&$yesno=~ /y/i) { 823 return$resp; 824 } 825 } 826 } 827 return; 828} 829 830sub parse_header_line { 831 my$lines= shift; 832 my$parsed_line= shift; 833 my$addr_pat= join "|", qw(To Cc Bcc); 834 835foreach(split(/\n/,$lines)) { 836if(/^($addr_pat):\s*(.+)$/i) { 837$parsed_line->{$1} = [ parse_address_line($2) ]; 838}elsif(/^([^:]*):\s*(.+)\s*$/i) { 839$parsed_line->{$1} =$2; 840} 841} 842} 843 844sub filter_body { 845my$c=shift; 846my$body=""; 847while(my$body_line= <$c>) { 848if($body_line!~m/^GIT:/) { 849$body.=$body_line; 850} 851} 852return$body; 853} 854 855 856my%broken_encoding; 857 858sub file_declares_8bit_cte { 859my$fn=shift; 860open(my$fh,'<',$fn); 861while(my$line= <$fh>) { 862last if($line=~/^$/); 863return1if($line=~/^Content-Transfer-Encoding: .*8bit.*$/); 864} 865close$fh; 866return0; 867} 868 869foreachmy$f(@files) { 870next unless(body_or_subject_has_nonascii($f) 871&& !file_declares_8bit_cte($f)); 872$broken_encoding{$f} =1; 873} 874 875if(!defined$auto_8bit_encoding&&scalar%broken_encoding) { 876print __("The following files are 8bit, but do not declare ". 877"a Content-Transfer-Encoding.\n"); 878foreachmy$f(sort keys%broken_encoding) { 879print"$f\n"; 880} 881$auto_8bit_encoding= ask(__("Which 8bit encoding should I declare [UTF-8]? "), 882 valid_re =>qr/.{4}/, confirm_only =>1, 883default=>"UTF-8"); 884} 885 886if(!$force) { 887formy$f(@files) { 888if(get_patch_subject($f) =~/\Q*** SUBJECT HERE ***\E/) { 889die sprintf(__("Refusing to send because the patch\n\t%s\n" 890."has the template subject '*** SUBJECT HERE ***'. " 891."Pass --force if you really want to send.\n"),$f); 892} 893} 894} 895 896if(defined$sender) { 897$sender=~s/^\s+|\s+$//g; 898($sender) = expand_aliases($sender); 899}else{ 900$sender=$repoauthor||$repocommitter||''; 901} 902 903# $sender could be an already sanitized address 904# (e.g. sendemail.from could be manually sanitized by user). 905# But it's a no-op to run sanitize_address on an already sanitized address. 906$sender= sanitize_address($sender); 907 908my$to_whom= __("To whom should the emails be sent (if anyone)?"); 909my$prompting=0; 910if(!@initial_to&& !defined$to_cmd) { 911my$to= ask("$to_whom", 912default=>"", 913 valid_re =>qr/\@.*\./, confirm_only =>1); 914push@initial_to, parse_address_line($to)ifdefined$to;# sanitized/validated later 915$prompting++; 916} 917 918sub expand_aliases { 919returnmap{ expand_one_alias($_) }@_; 920} 921 922my%EXPANDED_ALIASES; 923sub expand_one_alias { 924my$alias=shift; 925if($EXPANDED_ALIASES{$alias}) { 926die sprintf(__("fatal: alias '%s' expands to itself\n"),$alias); 927} 928local$EXPANDED_ALIASES{$alias} =1; 929return$aliases{$alias} ? expand_aliases(@{$aliases{$alias}}) :$alias; 930} 931 932@initial_to= process_address_list(@initial_to); 933@initial_cc= process_address_list(@initial_cc); 934@bcclist= process_address_list(@bcclist); 935 936if($thread&& !defined$initial_in_reply_to&&$prompting) { 937$initial_in_reply_to= ask( 938 __("Message-ID to be used as In-Reply-To for the first email (if any)? "), 939default=>"", 940 valid_re =>qr/\@.*\./, confirm_only =>1); 941} 942if(defined$initial_in_reply_to) { 943$initial_in_reply_to=~s/^\s*<?//; 944$initial_in_reply_to=~s/>?\s*$//; 945$initial_in_reply_to="<$initial_in_reply_to>"if$initial_in_reply_tone''; 946} 947 948if(defined$reply_to) { 949$reply_to=~s/^\s+|\s+$//g; 950($reply_to) = expand_aliases($reply_to); 951$reply_to= sanitize_address($reply_to); 952} 953 954if(!defined$smtp_server) { 955my@sendmail_paths=qw( /usr/sbin/sendmail /usr/lib/sendmail ); 956push@sendmail_paths,map{"$_/sendmail"}split/:/,$ENV{PATH}; 957foreach(@sendmail_paths) { 958if(-x $_) { 959$smtp_server=$_; 960last; 961} 962} 963$smtp_server||='localhost';# could be 127.0.0.1, too... *shrug* 964} 965 966if($compose&&$compose>0) { 967@files= ($compose_filename.".final",@files); 968} 969 970# Variables we set as part of the loop over files 971our($message_id,%mail,$subject,$in_reply_to,$references,$message, 972$needs_confirm,$message_num,$ask_default); 973 974sub extract_valid_address { 975my$address=shift; 976my$local_part_regexp=qr/[^<>"\s@]+/; 977my$domain_regexp=qr/[^.<>"\s@]+(?:\.[^.<>"\s@]+)+/; 978 979# check for a local address: 980return$addressif($address=~/^($local_part_regexp)$/); 981 982$address=~s/^\s*<(.*)>\s*$/$1/; 983if($have_email_valid) { 984returnscalar Email::Valid->address($address); 985} 986 987# less robust/correct than the monster regexp in Email::Valid, 988# but still does a 99% job, and one less dependency 989return$1if$address=~/($local_part_regexp\@$domain_regexp)/; 990return; 991} 992 993sub extract_valid_address_or_die { 994my$address=shift; 995$address= extract_valid_address($address); 996die sprintf(__("error: unable to extract a valid address from:%s\n"),$address) 997if!$address; 998return$address; 999}10001001sub validate_address {1002my$address=shift;1003while(!extract_valid_address($address)) {1004printf STDERR __("error: unable to extract a valid address from:%s\n"),$address;1005# TRANSLATORS: Make sure to include [q] [d] [e] in your1006# translation. The program will only accept English input1007# at this point.1008$_= ask(__("What to do with this address? ([q]uit|[d]rop|[e]dit): "),1009 valid_re =>qr/^(?:quit|q|drop|d|edit|e)/i,1010default=>'q');1011if(/^d/i) {1012returnundef;1013}elsif(/^q/i) {1014 cleanup_compose_files();1015exit(0);1016}1017$address= ask("$to_whom",1018default=>"",1019 valid_re =>qr/\@.*\./, confirm_only =>1);1020}1021return$address;1022}10231024sub validate_address_list {1025return(grep{defined$_}1026map{ validate_address($_) }@_);1027}10281029# Usually don't need to change anything below here.10301031# we make a "fake" message id by taking the current number1032# of seconds since the beginning of Unix time and tacking on1033# a random number to the end, in case we are called quicker than1034# 1 second since the last time we were called.10351036# We'll setup a template for the message id, using the "from" address:10371038my($message_id_stamp,$message_id_serial);1039sub make_message_id {1040my$uniq;1041if(!defined$message_id_stamp) {1042$message_id_stamp= strftime("%Y%m%d%H%M%S.$$",gmtime(time));1043$message_id_serial=0;1044}1045$message_id_serial++;1046$uniq="$message_id_stamp-$message_id_serial";10471048my$du_part;1049for($sender,$repocommitter,$repoauthor) {1050$du_part= extract_valid_address(sanitize_address($_));1051last if(defined$du_partand$du_partne'');1052}1053if(not defined$du_partor$du_parteq'') {1054require Sys::Hostname;1055$du_part='user@'. Sys::Hostname::hostname();1056}1057my$message_id_template="<%s-%s>";1058$message_id=sprintf($message_id_template,$uniq,$du_part);1059#print "new message id = $message_id\n"; # Was useful for debugging1060}1061106210631064$time=time-scalar$#files;10651066sub unquote_rfc2047 {1067local($_) =@_;1068my$charset;1069my$sep=qr/[ \t]+/;1070 s{$re_encoded_word(?:$sep$re_encoded_word)*}{1071my@words=split$sep,$&;1072foreach(@words) {1073m/$re_encoded_word/;1074$charset=$1;1075my$encoding=$2;1076my$text=$3;1077if($encodingeq'q'||$encodingeq'Q') {1078$_=$text;1079s/_/ /g;1080s/=([0-9A-F]{2})/chr(hex($1))/egi;1081}else{1082# other encodings not supported yet1083}1084}1085join'',@words;1086}eg;1087returnwantarray? ($_,$charset) :$_;1088}10891090sub quote_rfc2047 {1091local$_=shift;1092my$encoding=shift||'UTF-8';1093s/([^-a-zA-Z0-9!*+\/])/sprintf("=%02X",ord($1))/eg;1094s/(.*)/=\?$encoding\?q\?$1\?=/;1095return$_;1096}10971098sub is_rfc2047_quoted {1099my$s=shift;1100length($s) <=75&&1101$s=~m/^(?:"[[:ascii:]]*"|$re_encoded_word)$/o;1102}11031104sub subject_needs_rfc2047_quoting {1105my$s=shift;11061107return($s=~/[^[:ascii:]]/) || ($s=~/=\?/);1108}11091110sub quote_subject {1111local$subject=shift;1112my$encoding=shift||'UTF-8';11131114if(subject_needs_rfc2047_quoting($subject)) {1115return quote_rfc2047($subject,$encoding);1116}1117return$subject;1118}11191120# use the simplest quoting being able to handle the recipient1121sub sanitize_address {1122my($recipient) =@_;11231124# remove garbage after email address1125$recipient=~s/(.*>).*$/$1/;11261127my($recipient_name,$recipient_addr) = ($recipient=~/^(.*?)\s*(<.*)/);11281129if(not$recipient_name) {1130return$recipient;1131}11321133# if recipient_name is already quoted, do nothing1134if(is_rfc2047_quoted($recipient_name)) {1135return$recipient;1136}11371138# remove non-escaped quotes1139$recipient_name=~s/(^|[^\\])"/$1/g;11401141# rfc2047 is needed if a non-ascii char is included1142if($recipient_name=~/[^[:ascii:]]/) {1143$recipient_name= quote_rfc2047($recipient_name);1144}11451146# double quotes are needed if specials or CTLs are included1147elsif($recipient_name=~/[][()<>@,;:\\".\000-\037\177]/) {1148$recipient_name=~s/([\\\r])/\\$1/g;1149$recipient_name=qq["$recipient_name"];1150}11511152return"$recipient_name$recipient_addr";11531154}11551156sub strip_garbage_one_address {1157my($addr) =@_;1158chomp$addr;1159if($addr=~/^(("[^"]*"|[^"<]*)? *<[^>]*>).*/) {1160# "Foo Bar" <foobar@example.com> [possibly garbage here]1161# Foo Bar <foobar@example.com> [possibly garbage here]1162return$1;1163}1164if($addr=~/^(<[^>]*>).*/) {1165# <foo@example.com> [possibly garbage here]1166# if garbage contains other addresses, they are ignored.1167return$1;1168}1169if($addr=~/^([^"#,\s]*)/) {1170# address without quoting: remove anything after the address1171return$1;1172}1173return$addr;1174}11751176sub sanitize_address_list {1177return(map{ sanitize_address($_) }@_);1178}11791180sub process_address_list {1181my@addr_list=map{ parse_address_line($_) }@_;1182@addr_list= expand_aliases(@addr_list);1183@addr_list= sanitize_address_list(@addr_list);1184@addr_list= validate_address_list(@addr_list);1185return@addr_list;1186}11871188# Returns the local Fully Qualified Domain Name (FQDN) if available.1189#1190# Tightly configured MTAa require that a caller sends a real DNS1191# domain name that corresponds the IP address in the HELO/EHLO1192# handshake. This is used to verify the connection and prevent1193# spammers from trying to hide their identity. If the DNS and IP don't1194# match, the receiveing MTA may deny the connection.1195#1196# Here is a deny example of Net::SMTP with the default "localhost.localdomain"1197#1198# Net::SMTP=GLOB(0x267ec28)>>> EHLO localhost.localdomain1199# Net::SMTP=GLOB(0x267ec28)<<< 550 EHLO argument does not match calling host1200#1201# This maildomain*() code is based on ideas in Perl library Test::Reporter1202# /usr/share/perl5/Test/Reporter/Mail/Util.pm ==> sub _maildomain ()12031204sub valid_fqdn {1205my$domain=shift;1206returndefined$domain&& !($^Oeq'darwin'&&$domain=~/\.local$/) &&$domain=~/\./;1207}12081209sub maildomain_net {1210my$maildomain;12111212my$domain= Net::Domain::domainname();1213$maildomain=$domainif valid_fqdn($domain);12141215return$maildomain;1216}12171218sub maildomain_mta {1219my$maildomain;12201221formy$host(qw(mailhost localhost)) {1222my$smtp= Net::SMTP->new($host);1223if(defined$smtp) {1224my$domain=$smtp->domain;1225$smtp->quit;12261227$maildomain=$domainif valid_fqdn($domain);12281229last if$maildomain;1230}1231}12321233return$maildomain;1234}12351236sub maildomain {1237return maildomain_net() || maildomain_mta() ||'localhost.localdomain';1238}12391240sub smtp_host_string {1241if(defined$smtp_server_port) {1242return"$smtp_server:$smtp_server_port";1243}else{1244return$smtp_server;1245}1246}12471248# Returns 1 if authentication succeeded or was not necessary1249# (smtp_user was not specified), and 0 otherwise.12501251sub smtp_auth_maybe {1252if(!defined$smtp_authuser||$auth) {1253return1;1254}12551256# Workaround AUTH PLAIN/LOGIN interaction defect1257# with Authen::SASL::Cyrus1258eval{1259require Authen::SASL;1260 Authen::SASL->import(qw(Perl));1261};12621263# Check mechanism naming as defined in:1264# https://tools.ietf.org/html/rfc4422#page-81265if($smtp_auth&&$smtp_auth!~/^(\b[A-Z0-9-_]{1,20}\s*)*$/) {1266die"invalid smtp auth: '${smtp_auth}'";1267}12681269# TODO: Authentication may fail not because credentials were1270# invalid but due to other reasons, in which we should not1271# reject credentials.1272$auth= Git::credential({1273'protocol'=>'smtp',1274'host'=> smtp_host_string(),1275'username'=>$smtp_authuser,1276# if there's no password, "git credential fill" will1277# give us one, otherwise it'll just pass this one.1278'password'=>$smtp_authpass1279},sub{1280my$cred=shift;12811282if($smtp_auth) {1283my$sasl= Authen::SASL->new(1284 mechanism =>$smtp_auth,1285 callback => {1286 user =>$cred->{'username'},1287 pass =>$cred->{'password'},1288 authname =>$cred->{'username'},1289}1290);12911292return!!$smtp->auth($sasl);1293}12941295return!!$smtp->auth($cred->{'username'},$cred->{'password'});1296});12971298return$auth;1299}13001301sub ssl_verify_params {1302eval{1303require IO::Socket::SSL;1304 IO::Socket::SSL->import(qw/SSL_VERIFY_PEER SSL_VERIFY_NONE/);1305};1306if($@) {1307print STDERR "Not using SSL_VERIFY_PEER due to out-of-date IO::Socket::SSL.\n";1308return;1309}13101311if(!defined$smtp_ssl_cert_path) {1312# use the OpenSSL defaults1313return(SSL_verify_mode => SSL_VERIFY_PEER());1314}13151316if($smtp_ssl_cert_patheq"") {1317return(SSL_verify_mode => SSL_VERIFY_NONE());1318}elsif(-d $smtp_ssl_cert_path) {1319return(SSL_verify_mode => SSL_VERIFY_PEER(),1320 SSL_ca_path =>$smtp_ssl_cert_path);1321}elsif(-f $smtp_ssl_cert_path) {1322return(SSL_verify_mode => SSL_VERIFY_PEER(),1323 SSL_ca_file =>$smtp_ssl_cert_path);1324}else{1325die sprintf(__("CA path\"%s\"does not exist"),$smtp_ssl_cert_path);1326}1327}13281329sub file_name_is_absolute {1330my($path) =@_;13311332# msys does not grok DOS drive-prefixes1333if($^Oeq'msys') {1334return($path=~ m#^/# || $path =~ m#^[a-zA-Z]\:#)1335}13361337require File::Spec::Functions;1338return File::Spec::Functions::file_name_is_absolute($path);1339}13401341# Prepares the email, then asks the user what to do.1342#1343# If the user chooses to send the email, it's sent and 1 is returned.1344# If the user chooses not to send the email, 0 is returned.1345# If the user decides they want to make further edits, -1 is returned and the1346# caller is expected to call send_message again after the edits are performed.1347#1348# If an error occurs sending the email, this just dies.13491350sub send_message {1351my@recipients= unique_email_list(@to);1352@cc= (grep{my$cc= extract_valid_address_or_die($_);1353not grep{$cceq$_||$_=~/<\Q${cc}\E>$/}@recipients1354}1355@cc);1356my$to=join(",\n\t",@recipients);1357@recipients= unique_email_list(@recipients,@cc,@bcclist);1358@recipients= (map{ extract_valid_address_or_die($_) }@recipients);1359my$date= format_2822_time($time++);1360my$gitversion='@@GIT_VERSION@@';1361if($gitversion=~m/..GIT_VERSION../) {1362$gitversion= Git::version();1363}13641365my$cc=join(",\n\t", unique_email_list(@cc));1366my$ccline="";1367if($ccne'') {1368$ccline="\nCc:$cc";1369}1370 make_message_id()unlessdefined($message_id);13711372my$header="From:$sender1373To:$to${ccline}1374Subject:$subject1375Date:$date1376Message-Id:$message_id1377";1378if($use_xmailer) {1379$header.="X-Mailer: git-send-email$gitversion\n";1380}1381if($in_reply_to) {13821383$header.="In-Reply-To:$in_reply_to\n";1384$header.="References:$references\n";1385}1386if($reply_to) {1387$header.="Reply-To:$reply_to\n";1388}1389if(@xh) {1390$header.=join("\n",@xh) ."\n";1391}13921393my@sendmail_parameters= ('-i',@recipients);1394my$raw_from=$sender;1395if(defined$envelope_sender&&$envelope_senderne"auto") {1396$raw_from=$envelope_sender;1397}1398$raw_from= extract_valid_address($raw_from);1399unshift(@sendmail_parameters,1400'-f',$raw_from)if(defined$envelope_sender);14011402if($needs_confirm&& !$dry_run) {1403print"\n$header\n";1404if($needs_confirmeq"inform") {1405$confirm_unconfigured=0;# squelch this message for the rest of this run1406$ask_default="y";# assume yes on EOF since user hasn't explicitly asked for confirmation1407print __ <<EOF ;1408 The Cc list above has been expanded by additional1409 addresses found in the patch commit message. By default1410 send-email prompts before sending whenever this occurs.1411 This behavior is controlled by the sendemail.confirm1412 configuration setting.14131414 For additional information, run 'git send-email --help'.1415 To retain the current behavior, but squelch this message,1416 run 'git config --global sendemail.confirm auto'.14171418EOF1419}1420# TRANSLATORS: Make sure to include [y] [n] [e] [q] [a] in your1421# translation. The program will only accept English input1422# at this point.1423$_= ask(__("Send this email? ([y]es|[n]o|[e]dit|[q]uit|[a]ll): "),1424 valid_re =>qr/^(?:yes|y|no|n|edit|e|quit|q|all|a)/i,1425default=>$ask_default);1426die __("Send this email reply required")unlessdefined$_;1427if(/^n/i) {1428return0;1429}elsif(/^e/i) {1430return-1;1431}elsif(/^q/i) {1432 cleanup_compose_files();1433exit(0);1434}elsif(/^a/i) {1435$confirm='never';1436}1437}14381439unshift(@sendmail_parameters,@smtp_server_options);14401441if($dry_run) {1442# We don't want to send the email.1443}elsif(file_name_is_absolute($smtp_server)) {1444my$pid=open my$sm,'|-';1445defined$pidor die$!;1446if(!$pid) {1447exec($smtp_server,@sendmail_parameters)or die$!;1448}1449print$sm"$header\n$message";1450close$smor die$!;1451}else{14521453if(!defined$smtp_server) {1454die __("The required SMTP server is not properly defined.")1455}14561457require Net::SMTP;1458my$use_net_smtp_ssl= version->parse($Net::SMTP::VERSION) < version->parse("2.34");1459$smtp_domain||= maildomain();14601461if($smtp_encryptioneq'ssl') {1462$smtp_server_port||=465;# ssmtp1463require IO::Socket::SSL;14641465# Suppress "variable accessed once" warning.1466{1467no warnings 'once';1468$IO::Socket::SSL::DEBUG =1;1469}14701471# Net::SMTP::SSL->new() does not forward any SSL options1472 IO::Socket::SSL::set_client_defaults(1473 ssl_verify_params());14741475if($use_net_smtp_ssl) {1476require Net::SMTP::SSL;1477$smtp||= Net::SMTP::SSL->new($smtp_server,1478 Hello =>$smtp_domain,1479 Port =>$smtp_server_port,1480 Debug =>$debug_net_smtp);1481}1482else{1483$smtp||= Net::SMTP->new($smtp_server,1484 Hello =>$smtp_domain,1485 Port =>$smtp_server_port,1486 Debug =>$debug_net_smtp,1487 SSL =>1);1488}1489}1490elsif(!$smtp) {1491$smtp_server_port||=25;1492$smtp||= Net::SMTP->new($smtp_server,1493 Hello =>$smtp_domain,1494 Debug =>$debug_net_smtp,1495 Port =>$smtp_server_port);1496if($smtp_encryptioneq'tls'&&$smtp) {1497if($use_net_smtp_ssl) {1498$smtp->command('STARTTLS');1499$smtp->response();1500if($smtp->code!=220) {1501die sprintf(__("Server does not support STARTTLS!%s"),$smtp->message);1502}1503require Net::SMTP::SSL;1504$smtp= Net::SMTP::SSL->start_SSL($smtp,1505 ssl_verify_params())1506or die sprintf(__("STARTTLS failed!%s"), IO::Socket::SSL::errstr());1507}1508else{1509$smtp->starttls(ssl_verify_params())1510or die sprintf(__("STARTTLS failed!%s"), IO::Socket::SSL::errstr());1511}1512# Send EHLO again to receive fresh1513# supported commands1514$smtp->hello($smtp_domain);1515}1516}15171518if(!$smtp) {1519die __("Unable to initialize SMTP properly. Check config and use --smtp-debug."),1520" VALUES: server=$smtp_server",1521"encryption=$smtp_encryption",1522"hello=$smtp_domain",1523defined$smtp_server_port?" port=$smtp_server_port":"";1524}15251526 smtp_auth_maybe or die$smtp->message;15271528$smtp->mail($raw_from)or die$smtp->message;1529$smtp->to(@recipients)or die$smtp->message;1530$smtp->dataor die$smtp->message;1531$smtp->datasend("$header\n")or die$smtp->message;1532my@lines=split/^/,$message;1533foreachmy$line(@lines) {1534$smtp->datasend("$line")or die$smtp->message;1535}1536$smtp->dataend()or die$smtp->message;1537$smtp->code=~/250|200/or die sprintf(__("Failed to send%s\n"),$subject).$smtp->message;1538}1539if($quiet) {1540printf($dry_run? __("Dry-Sent%s\n") : __("Sent%s\n"),$subject);1541}else{1542print($dry_run? __("Dry-OK. Log says:\n") : __("OK. Log says:\n"));1543if(!file_name_is_absolute($smtp_server)) {1544print"Server:$smtp_server\n";1545print"MAIL FROM:<$raw_from>\n";1546foreachmy$entry(@recipients) {1547print"RCPT TO:<$entry>\n";1548}1549}else{1550print"Sendmail:$smtp_server".join(' ',@sendmail_parameters)."\n";1551}1552print$header,"\n";1553if($smtp) {1554print __("Result: "),$smtp->code,' ',1555($smtp->message=~/\n([^\n]+\n)$/s),"\n";1556}else{1557print __("Result: OK\n");1558}1559}15601561return1;1562}15631564$in_reply_to=$initial_in_reply_to;1565$references=$initial_in_reply_to||'';1566$subject=$initial_subject;1567$message_num=0;15681569# Prepares the email, prompts the user, sends it out1570# Returns 0 if an edit was done and the function should be called again, or 11571# otherwise.1572sub process_file {1573my($t) =@_;15741575open my$fh,"<",$tor die sprintf(__("can't open file%s"),$t);15761577my$author=undef;1578my$sauthor=undef;1579my$author_encoding;1580my$has_content_type;1581my$body_encoding;1582my$xfer_encoding;1583my$has_mime_version;1584@to= ();1585@cc= ();1586@xh= ();1587my$input_format=undef;1588my@header= ();1589$message="";1590$message_num++;1591# First unfold multiline header fields1592while(<$fh>) {1593last if/^\s*$/;1594if(/^\s+\S/and@header) {1595chomp($header[$#header]);1596s/^\s+/ /;1597$header[$#header] .=$_;1598}else{1599push(@header,$_);1600}1601}1602# Now parse the header1603foreach(@header) {1604if(/^From /) {1605$input_format='mbox';1606next;1607}1608chomp;1609if(!defined$input_format&&/^[-A-Za-z]+:\s/) {1610$input_format='mbox';1611}16121613if(defined$input_format&&$input_formateq'mbox') {1614if(/^Subject:\s+(.*)$/i) {1615$subject=$1;1616}1617elsif(/^From:\s+(.*)$/i) {1618($author,$author_encoding) = unquote_rfc2047($1);1619$sauthor= sanitize_address($author);1620next if$suppress_cc{'author'};1621next if$suppress_cc{'self'}and$sauthoreq$sender;1622printf(__("(mbox) Adding cc:%sfrom line '%s'\n"),1623$1,$_)unless$quiet;1624push@cc,$1;1625}1626elsif(/^To:\s+(.*)$/i) {1627foreachmy$addr(parse_address_line($1)) {1628printf(__("(mbox) Adding to:%sfrom line '%s'\n"),1629$addr,$_)unless$quiet;1630push@to,$addr;1631}1632}1633elsif(/^Cc:\s+(.*)$/i) {1634foreachmy$addr(parse_address_line($1)) {1635my$qaddr= unquote_rfc2047($addr);1636my$saddr= sanitize_address($qaddr);1637if($saddreq$sender) {1638next if($suppress_cc{'self'});1639}else{1640next if($suppress_cc{'cc'});1641}1642printf(__("(mbox) Adding cc:%sfrom line '%s'\n"),1643$addr,$_)unless$quiet;1644push@cc,$addr;1645}1646}1647elsif(/^Content-type:/i) {1648$has_content_type=1;1649if(/charset="?([^ "]+)/) {1650$body_encoding=$1;1651}1652push@xh,$_;1653}1654elsif(/^MIME-Version/i) {1655$has_mime_version=1;1656push@xh,$_;1657}1658elsif(/^Message-Id: (.*)/i) {1659$message_id=$1;1660}1661elsif(/^Content-Transfer-Encoding: (.*)/i) {1662$xfer_encoding=$1ifnot defined$xfer_encoding;1663}1664elsif(/^In-Reply-To: (.*)/i) {1665$in_reply_to=$1;1666}1667elsif(/^References: (.*)/i) {1668$references=$1;1669}1670elsif(!/^Date:\s/i&&/^[-A-Za-z]+:\s+\S/) {1671push@xh,$_;1672}1673}else{1674# In the traditional1675# "send lots of email" format,1676# line 1 = cc1677# line 2 = subject1678# So let's support that, too.1679$input_format='lots';1680if(@cc==0&& !$suppress_cc{'cc'}) {1681printf(__("(non-mbox) Adding cc:%sfrom line '%s'\n"),1682$_,$_)unless$quiet;1683push@cc,$_;1684}elsif(!defined$subject) {1685$subject=$_;1686}1687}1688}1689# Now parse the message body1690while(<$fh>) {1691$message.=$_;1692if(/^(Signed-off-by|Cc): (.*)/i) {1693chomp;1694my($what,$c) = ($1,$2);1695# strip garbage for the address we'll use:1696$c= strip_garbage_one_address($c);1697# sanitize a bit more to decide whether to suppress the address:1698my$sc= sanitize_address($c);1699if($sceq$sender) {1700next if($suppress_cc{'self'});1701}else{1702next if$suppress_cc{'sob'}and$what=~/Signed-off-by/i;1703next if$suppress_cc{'bodycc'}and$what=~/Cc/i;1704}1705push@cc,$c;1706printf(__("(body) Adding cc:%sfrom line '%s'\n"),1707$c,$_)unless$quiet;1708}1709}1710close$fh;17111712push@to, recipients_cmd("to-cmd","to",$to_cmd,$t)1713ifdefined$to_cmd;1714push@cc, recipients_cmd("cc-cmd","cc",$cc_cmd,$t)1715ifdefined$cc_cmd&& !$suppress_cc{'cccmd'};17161717if($broken_encoding{$t} && !$has_content_type) {1718$xfer_encoding='8bit'ifnot defined$xfer_encoding;1719$has_content_type=1;1720push@xh,"Content-Type: text/plain; charset=$auto_8bit_encoding";1721$body_encoding=$auto_8bit_encoding;1722}17231724if($broken_encoding{$t} && !is_rfc2047_quoted($subject)) {1725$subject= quote_subject($subject,$auto_8bit_encoding);1726}17271728if(defined$sauthorand$sauthorne$sender) {1729$message="From:$author\n\n$message";1730if(defined$author_encoding) {1731if($has_content_type) {1732if($body_encodingeq$author_encoding) {1733# ok, we already have the right encoding1734}1735else{1736# uh oh, we should re-encode1737}1738}1739else{1740$xfer_encoding='8bit'ifnot defined$xfer_encoding;1741$has_content_type=1;1742push@xh,1743"Content-Type: text/plain; charset=$author_encoding";1744}1745}1746}1747$xfer_encoding='8bit'ifnot defined$xfer_encoding;1748($message,$xfer_encoding) = apply_transfer_encoding(1749$message,$xfer_encoding,$target_xfer_encoding);1750push@xh,"Content-Transfer-Encoding:$xfer_encoding";1751unshift@xh,'MIME-Version: 1.0'unless$has_mime_version;17521753$needs_confirm= (1754$confirmeq"always"or1755($confirm=~/^(?:auto|cc)$/&&@cc)or1756($confirm=~/^(?:auto|compose)$/&&$compose&&$message_num==1));1757$needs_confirm="inform"if($needs_confirm&&$confirm_unconfigured&&@cc);17581759@to= process_address_list(@to);1760@cc= process_address_list(@cc);17611762@to= (@initial_to,@to);1763@cc= (@initial_cc,@cc);17641765if($message_num==1) {1766if(defined$cover_ccand$cover_cc) {1767@initial_cc=@cc;1768}1769if(defined$cover_toand$cover_to) {1770@initial_to=@to;1771}1772}17731774my$message_was_sent= send_message();1775if($message_was_sent== -1) {1776 do_edit($t);1777return0;1778}17791780# set up for the next message1781if($thread&&$message_was_sent&&1782($chain_reply_to|| !defined$in_reply_to||length($in_reply_to) ==0||1783$message_num==1)) {1784$in_reply_to=$message_id;1785if(length$references>0) {1786$references.="\n$message_id";1787}else{1788$references="$message_id";1789}1790}1791$message_id=undef;1792$num_sent++;1793if(defined$batch_size&&$num_sent==$batch_size) {1794$num_sent=0;1795$smtp->quitifdefined$smtp;1796undef$smtp;1797undef$auth;1798sleep($relogin_delay)ifdefined$relogin_delay;1799}18001801return1;1802}18031804foreachmy$t(@files) {1805while(!process_file($t)) {1806# user edited the file1807}1808}18091810# Execute a command (e.g. $to_cmd) to get a list of email addresses1811# and return a results array1812sub recipients_cmd {1813my($prefix,$what,$cmd,$file) =@_;18141815my@addresses= ();1816open my$fh,"-|","$cmd\Q$file\E"1817or die sprintf(__("(%s) Could not execute '%s'"),$prefix,$cmd);1818while(my$address= <$fh>) {1819$address=~s/^\s*//g;1820$address=~s/\s*$//g;1821$address= sanitize_address($address);1822next if($addresseq$senderand$suppress_cc{'self'});1823push@addresses,$address;1824printf(__("(%s) Adding%s:%sfrom: '%s'\n"),1825$prefix,$what,$address,$cmd)unless$quiet;1826}1827close$fh1828or die sprintf(__("(%s) failed to close pipe to '%s'"),$prefix,$cmd);1829return@addresses;1830}18311832cleanup_compose_files();18331834sub cleanup_compose_files {1835unlink($compose_filename,$compose_filename.".final")if$compose;1836}18371838$smtp->quitif$smtp;18391840sub apply_transfer_encoding {1841my$message=shift;1842my$from=shift;1843my$to=shift;18441845return$messageif($fromeq$toand$fromne'7bit');18461847require MIME::QuotedPrint;1848require MIME::Base64;18491850$message= MIME::QuotedPrint::decode($message)1851if($fromeq'quoted-printable');1852$message= MIME::Base64::decode($message)1853if($fromeq'base64');18541855$to= ($message=~/.{999,}/) ?'quoted-printable':'8bit'1856if$toeq'auto';18571858die __("cannot send message as 7bit")1859if($toeq'7bit'and$message=~/[^[:ascii:]]/);1860return($message,$to)1861if($toeq'7bit'or$toeq'8bit');1862return(MIME::QuotedPrint::encode($message,"\n",0),$to)1863if($toeq'quoted-printable');1864return(MIME::Base64::encode($message,"\n"),$to)1865if($toeq'base64');1866die __("invalid transfer encoding");1867}18681869sub unique_email_list {1870my%seen;1871my@emails;18721873foreachmy$entry(@_) {1874my$clean= extract_valid_address_or_die($entry);1875$seen{$clean} ||=0;1876next if$seen{$clean}++;1877push@emails,$entry;1878}1879return@emails;1880}18811882sub validate_patch {1883my($fn,$xfer_encoding) =@_;18841885if($repo) {1886my$validate_hook= catfile(catdir($repo->repo_path(),'hooks'),1887'sendemail-validate');1888my$hook_error;1889if(-x $validate_hook) {1890my$target= abs_path($fn);1891# The hook needs a correct cwd and GIT_DIR.1892my$cwd_save= cwd();1893chdir($repo->wc_path()or$repo->repo_path())1894or die("chdir:$!");1895local$ENV{"GIT_DIR"} =$repo->repo_path();1896$hook_error="rejected by sendemail-validate hook"1897ifsystem($validate_hook,$target);1898chdir($cwd_save)or die("chdir:$!");1899}1900return$hook_errorif$hook_error;1901}19021903# Any long lines will be automatically fixed if we use a suitable transfer1904# encoding.1905unless($xfer_encoding=~/^(?:auto|quoted-printable|base64)$/) {1906open(my$fh,'<',$fn)1907or die sprintf(__("unable to open%s:%s\n"),$fn,$!);1908while(my$line= <$fh>) {1909if(length($line) >998) {1910returnsprintf(__("%s: patch contains a line longer than 998 characters"), $.);1911}1912}1913}1914return;1915}19161917sub handle_backup {1918my($last,$lastlen,$file,$known_suffix) =@_;1919my($suffix,$skip);19201921$skip=0;1922if(defined$last&&1923($lastlen<length($file)) &&1924(substr($file,0,$lastlen)eq$last) &&1925($suffix=substr($file,$lastlen)) !~/^[a-z0-9]/i) {1926if(defined$known_suffix&&$suffixeq$known_suffix) {1927printf(__("Skipping%swith backup suffix '%s'.\n"),$file,$known_suffix);1928$skip=1;1929}else{1930# TRANSLATORS: please keep "[y|N]" as is.1931my$answer= ask(sprintf(__("Do you really want to send%s?[y|N]: "),$file),1932 valid_re =>qr/^(?:y|n)/i,1933default=>'n');1934$skip= ($answerne'y');1935if($skip) {1936$known_suffix=$suffix;1937}1938}1939}1940return($skip,$known_suffix);1941}19421943sub handle_backup_files {1944my@file=@_;1945my($last,$lastlen,$known_suffix,$skip,@result);1946formy$file(@file) {1947($skip,$known_suffix) = handle_backup($last,$lastlen,1948$file,$known_suffix);1949push@result,$fileunless$skip;1950$last=$file;1951$lastlen=length($file);1952}1953return@result;1954}19551956sub file_has_nonascii {1957my$fn=shift;1958open(my$fh,'<',$fn)1959or die sprintf(__("unable to open%s:%s\n"),$fn,$!);1960while(my$line= <$fh>) {1961return1if$line=~/[^[:ascii:]]/;1962}1963return0;1964}19651966sub body_or_subject_has_nonascii {1967my$fn=shift;1968open(my$fh,'<',$fn)1969or die sprintf(__("unable to open%s:%s\n"),$fn,$!);1970while(my$line= <$fh>) {1971last if$line=~/^$/;1972return1if$line=~/^Subject.*[^[:ascii:]]/;1973}1974while(my$line= <$fh>) {1975return1if$line=~/[^[:ascii:]]/;1976}1977return0;1978}