Git fork
at reftables-rust 2426 lines 71 kB view raw
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 19require v5.26; 20use strict; 21use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : (); 22use Getopt::Long; 23use Git::LoadCPAN::Error qw(:try); 24use Git; 25use Git::I18N; 26 27Getopt::Long::Configure qw/ pass_through /; 28 29sub usage { 30 print <<EOT; 31git send-email [<options>] <file|directory> 32git send-email [<options>] <format-patch options> 33git send-email --dump-aliases 34git send-email --translate-aliases 35 36 Composing: 37 --from <str> * Email From: 38 --[no-]to <str> * Email To: 39 --[no-]cc <str> * Email Cc: 40 --[no-]bcc <str> * Email Bcc: 41 --subject <str> * Email "Subject:" 42 --reply-to <str> * Email "Reply-To:" 43 --in-reply-to <str> * Email "In-Reply-To:" 44 --[no-]outlook-id-fix * The SMTP host is an Outlook server that munges the 45 Message-ID. Retrieve it from the server. 46 --[no-]xmailer * Add "X-Mailer:" header (default). 47 --[no-]annotate * Review each patch that will be sent in an editor. 48 --compose * Open an editor for introduction. 49 --compose-encoding <str> * Encoding to assume for introduction. 50 --8bit-encoding <str> * Encoding to assume 8bit mails if undeclared 51 --transfer-encoding <str> * Transfer encoding to use (quoted-printable, 8bit, base64) 52 --[no-]mailmap * Use mailmap file to map all email addresses to canonical 53 real names and email addresses. 54 55 Sending: 56 --envelope-sender <str> * Email envelope sender. 57 --sendmail-cmd <str> * Command to run to send email. 58 --smtp-server <str:int> * Outgoing SMTP server to use. The port 59 is optional. Default 'localhost'. 60 --smtp-server-option <str> * Outgoing SMTP server option to use. 61 --smtp-server-port <int> * Outgoing SMTP server port. 62 --smtp-user <str> * Username for SMTP-AUTH. 63 --smtp-pass <str> * Password for SMTP-AUTH; not necessary. 64 --smtp-encryption <str> * tls or ssl; anything else disables. 65 --smtp-ssl * Deprecated. Use `--smtp-encryption ssl`. 66 --smtp-ssl-cert-path <str> * Path to ca-certificates (either directory or file). 67 Pass an empty string to disable certificate 68 verification. 69 --smtp-domain <str> * The domain name sent to HELO/EHLO handshake 70 --smtp-auth <str> * Space-separated list of allowed AUTH mechanisms, or 71 "none" to disable authentication. 72 This setting forces to use one of the listed mechanisms. 73 --no-smtp-auth * Disable SMTP authentication. Shorthand for 74 `--smtp-auth=none` 75 --smtp-debug <0|1> * Disable, enable Net::SMTP debug. 76 --imap-sent-folder <str> * IMAP folder where a copy of the emails should be sent. 77 Make sure `git imap-send` is set up to use this feature. 78 --[no-]use-imap-only * Only copy emails to the IMAP folder specified by 79 `--imap-sent-folder` instead of actually sending them. 80 81 --batch-size <int> * send max <int> message per connection. 82 --relogin-delay <int> * delay <int> seconds between two successive login. 83 This option can only be used with --batch-size 84 85 Automating: 86 --identity <str> * Use the sendemail.<id> options. 87 --to-cmd <str> * Email To: via `<str> \$patch_path`. 88 --cc-cmd <str> * Email Cc: via `<str> \$patch_path`. 89 --header-cmd <str> * Add headers via `<str> \$patch_path`. 90 --no-header-cmd * Disable any header command in use. 91 --suppress-cc <str> * author, self, sob, cc, cccmd, body, bodycc, misc-by, all. 92 --[no-]cc-cover * Email Cc: addresses in the cover letter. 93 --[no-]to-cover * Email To: addresses in the cover letter. 94 --[no-]signed-off-by-cc * Send to Signed-off-by: addresses. Default on. 95 --[no-]suppress-from * Send to self. Default off. 96 --[no-]chain-reply-to * Chain In-Reply-To: fields. Default off. 97 --[no-]thread * Use In-Reply-To: field. Default on. 98 99 Administering: 100 --confirm <str> * Confirm recipients before sending; 101 auto, cc, compose, always, or never. 102 --quiet * Output one line of info per email. 103 --dry-run * Don't actually send the emails. 104 --[no-]validate * Perform patch sanity checks. Default on. 105 --[no-]format-patch * understand any non optional arguments as 106 `git format-patch` ones. 107 --force * Send even if safety checks would prevent it. 108 109 Information: 110 --dump-aliases * Dump configured aliases and exit. 111 --translate-aliases * Translate aliases read from standard 112 input according to the configured email 113 alias file(s), outputting the result to 114 standard output. 115 116EOT 117 exit(1); 118} 119 120sub uniq { 121 my %seen; 122 grep !$seen{$_}++, @_; 123} 124 125sub completion_helper { 126 my ($original_opts) = @_; 127 my %not_for_completion = ( 128 "git-completion-helper" => undef, 129 "h" => undef, 130 ); 131 my @send_email_opts = (); 132 133 foreach my $key (keys %$original_opts) { 134 unless (exists $not_for_completion{$key}) { 135 my $negatable = ($key =~ s/!$//); 136 137 if ($key =~ /[:=][si]$/) { 138 $key =~ s/[:=][si]$//; 139 push (@send_email_opts, "--$_=") foreach (split (/\|/, $key)); 140 } else { 141 push (@send_email_opts, "--$_") foreach (split (/\|/, $key)); 142 if ($negatable) { 143 push (@send_email_opts, "--no-$_") foreach (split (/\|/, $key)); 144 } 145 } 146 } 147 } 148 149 my @format_patch_opts = split(/ /, Git::command('format-patch', '--git-completion-helper')); 150 my @opts = (@send_email_opts, @format_patch_opts); 151 @opts = uniq (grep !/^$/, @opts); 152 # There's an implicit '\n' here already, no need to add an explicit one. 153 print "@opts"; 154 exit(0); 155} 156 157# most mail servers generate the Date: header, but not all... 158sub format_2822_time { 159 my ($time) = @_; 160 my @localtm = localtime($time); 161 my @gmttm = gmtime($time); 162 my $localmin = $localtm[1] + $localtm[2] * 60; 163 my $gmtmin = $gmttm[1] + $gmttm[2] * 60; 164 if ($localtm[0] != $gmttm[0]) { 165 die __("local zone differs from GMT by a non-minute interval\n"); 166 } 167 if ((($gmttm[6] + 1) % 7) == $localtm[6]) { 168 $localmin += 1440; 169 } elsif ((($gmttm[6] - 1) % 7) == $localtm[6]) { 170 $localmin -= 1440; 171 } elsif ($gmttm[6] != $localtm[6]) { 172 die __("local time offset greater than or equal to 24 hours\n"); 173 } 174 my $offset = $localmin - $gmtmin; 175 my $offhour = $offset / 60; 176 my $offmin = abs($offset % 60); 177 if (abs($offhour) >= 24) { 178 die __("local time offset greater than or equal to 24 hours\n"); 179 } 180 181 return sprintf("%s, %2d %s %d %02d:%02d:%02d %s%02d%02d", 182 qw(Sun Mon Tue Wed Thu Fri Sat)[$localtm[6]], 183 $localtm[3], 184 qw(Jan Feb Mar Apr May Jun 185 Jul Aug Sep Oct Nov Dec)[$localtm[4]], 186 $localtm[5]+1900, 187 $localtm[2], 188 $localtm[1], 189 $localtm[0], 190 ($offset >= 0) ? '+' : '-', 191 abs($offhour), 192 $offmin, 193 ); 194} 195 196my $smtp; 197my $auth; 198my $num_sent = 0; 199 200# Regexes for RFC 2047 productions. 201my $re_token = qr/[^][()<>@,;:\\"\/?.= \000-\037\177-\377]+/; 202my $re_encoded_text = qr/[^? \000-\037\177-\377]+/; 203my $re_encoded_word = qr/=\?($re_token)\?($re_token)\?($re_encoded_text)\?=/; 204 205# Variables we fill in automatically, or via prompting: 206my (@to,@cc,@xh,$envelope_sender, 207 $initial_in_reply_to,$reply_to,$initial_subject,@files,@imap_copy, 208 $author,$sender,$smtp_authpass,$annotate,$compose,$time); 209# Things we either get from config, *or* are overridden on the 210# command-line. 211my ($no_cc, $no_to, $no_bcc, $no_identity, $no_header_cmd); 212my (@config_to, @getopt_to); 213my (@config_cc, @getopt_cc); 214my (@config_bcc, @getopt_bcc); 215 216# Example reply to: 217#$initial_in_reply_to = ''; #<20050203173208.GA23964@foobar.com>'; 218 219my $repo = eval { Git->repository() }; 220my @repo = $repo ? ($repo) : (); 221 222# Behavior modification variables 223my ($quiet, $dry_run) = (0, 0); 224my $format_patch; 225my $compose_filename; 226my $force = 0; 227my $dump_aliases = 0; 228my $translate_aliases = 0; 229 230# Variables to prevent short format-patch options from being captured 231# as abbreviated send-email options 232my $reroll_count; 233 234# Handle interactive edition of files. 235my $multiedit; 236my $editor; 237 238sub system_or_msg { 239 my ($args, $msg, $cmd_name) = @_; 240 system(@$args); 241 my $signalled = $? & 127; 242 my $exit_code = $? >> 8; 243 return unless $signalled or $exit_code; 244 245 my @sprintf_args = ($cmd_name ? $cmd_name : $args->[0], $exit_code); 246 if (defined $msg) { 247 # Quiet the 'redundant' warning category, except we 248 # need to support down to Perl 5.8.1, so we can't do a 249 # "no warnings 'redundant'", since that category was 250 # introduced in perl 5.22, and asking for it will die 251 # on older perls. 252 no warnings; 253 return sprintf($msg, @sprintf_args); 254 } 255 return sprintf(__("fatal: command '%s' died with exit code %d"), 256 @sprintf_args); 257} 258 259sub system_or_die { 260 my $msg = system_or_msg(@_); 261 die $msg if $msg; 262} 263 264sub do_edit { 265 if (!defined($editor)) { 266 $editor = Git::command_oneline('var', 'GIT_EDITOR'); 267 } 268 my $die_msg = __("the editor exited uncleanly, aborting everything"); 269 if (defined($multiedit) && !$multiedit) { 270 system_or_die(['sh', '-c', $editor.' "$@"', $editor, $_], $die_msg) for @_; 271 } else { 272 system_or_die(['sh', '-c', $editor.' "$@"', $editor, @_], $die_msg); 273 } 274} 275 276# Variables with corresponding config settings 277my ($suppress_from, $signed_off_by_cc); 278my ($cover_cc, $cover_to); 279my ($to_cmd, $cc_cmd, $header_cmd); 280my ($smtp_server, $smtp_server_port, @smtp_server_options); 281my ($smtp_authuser, $smtp_encryption, $smtp_ssl_cert_path); 282my ($batch_size, $relogin_delay); 283my ($identity, $aliasfiletype, @alias_files, $smtp_domain, $smtp_auth); 284my ($imap_sent_folder); 285my ($confirm); 286my (@suppress_cc); 287my ($auto_8bit_encoding); 288my ($compose_encoding); 289my ($sendmail_cmd); 290my ($mailmap_file, $mailmap_blob); 291# Variables with corresponding config settings & hardcoded defaults 292my ($debug_net_smtp) = 0; # Net::SMTP, see send_message() 293my $thread = 1; 294my $chain_reply_to = 0; 295my $use_xmailer = 1; 296my $validate = 1; 297my $mailmap = 0; 298my $target_xfer_encoding = 'auto'; 299my $forbid_sendmail_variables = 1; 300my $outlook_id_fix = 'auto'; 301my $use_imap_only = 0; 302 303my %config_bool_settings = ( 304 "thread" => \$thread, 305 "chainreplyto" => \$chain_reply_to, 306 "suppressfrom" => \$suppress_from, 307 "signedoffbycc" => \$signed_off_by_cc, 308 "cccover" => \$cover_cc, 309 "tocover" => \$cover_to, 310 "signedoffcc" => \$signed_off_by_cc, 311 "validate" => \$validate, 312 "multiedit" => \$multiedit, 313 "annotate" => \$annotate, 314 "xmailer" => \$use_xmailer, 315 "forbidsendmailvariables" => \$forbid_sendmail_variables, 316 "mailmap" => \$mailmap, 317 "outlookidfix" => \$outlook_id_fix, 318 "useimaponly" => \$use_imap_only, 319); 320 321my %config_settings = ( 322 "smtpencryption" => \$smtp_encryption, 323 "smtpserver" => \$smtp_server, 324 "smtpserverport" => \$smtp_server_port, 325 "smtpserveroption" => \@smtp_server_options, 326 "smtpuser" => \$smtp_authuser, 327 "smtppass" => \$smtp_authpass, 328 "smtpdomain" => \$smtp_domain, 329 "smtpauth" => \$smtp_auth, 330 "smtpbatchsize" => \$batch_size, 331 "smtprelogindelay" => \$relogin_delay, 332 "imapsentfolder" => \$imap_sent_folder, 333 "to" => \@config_to, 334 "tocmd" => \$to_cmd, 335 "cc" => \@config_cc, 336 "cccmd" => \$cc_cmd, 337 "headercmd" => \$header_cmd, 338 "aliasfiletype" => \$aliasfiletype, 339 "bcc" => \@config_bcc, 340 "suppresscc" => \@suppress_cc, 341 "envelopesender" => \$envelope_sender, 342 "confirm" => \$confirm, 343 "from" => \$sender, 344 "assume8bitencoding" => \$auto_8bit_encoding, 345 "composeencoding" => \$compose_encoding, 346 "transferencoding" => \$target_xfer_encoding, 347 "sendmailcmd" => \$sendmail_cmd, 348); 349 350my %config_path_settings = ( 351 "aliasesfile" => \@alias_files, 352 "smtpsslcertpath" => \$smtp_ssl_cert_path, 353 "mailmap.file" => \$mailmap_file, 354 "mailmap.blob" => \$mailmap_blob, 355); 356 357# Handle Uncouth Termination 358sub signal_handler { 359 # Make text normal 360 require Term::ANSIColor; 361 print Term::ANSIColor::color("reset"), "\n"; 362 363 # SMTP password masked 364 system "stty echo"; 365 366 # tmp files from --compose 367 if (defined $compose_filename) { 368 if (-e $compose_filename) { 369 printf __("'%s' contains an intermediate version ". 370 "of the email you were composing.\n"), 371 $compose_filename; 372 } 373 if (-e ($compose_filename . ".final")) { 374 printf __("'%s.final' contains the composed email.\n"), 375 $compose_filename; 376 } 377 } 378 379 exit; 380}; 381 382$SIG{TERM} = \&signal_handler; 383$SIG{INT} = \&signal_handler; 384 385# Read our sendemail.* config 386sub read_config { 387 my ($known_keys, $configured, $prefix) = @_; 388 389 foreach my $setting (keys %config_bool_settings) { 390 my $target = $config_bool_settings{$setting}; 391 my $key = "$prefix.$setting"; 392 next unless exists $known_keys->{$key}; 393 my $v = (@{$known_keys->{$key}} == 1 && 394 (defined $known_keys->{$key}->[0] && 395 $known_keys->{$key}->[0] =~ /^(?:true|false)$/s)) 396 ? $known_keys->{$key}->[0] eq 'true' 397 : Git::config_bool(@repo, $key); 398 next unless defined $v; 399 next if $configured->{$setting}++; 400 $$target = $v; 401 } 402 403 foreach my $setting (keys %config_path_settings) { 404 my $target = $config_path_settings{$setting}; 405 my $key = "$prefix.$setting"; 406 next unless exists $known_keys->{$key}; 407 if (ref($target) eq "ARRAY") { 408 my @values = Git::config_path(@repo, $key); 409 next unless @values; 410 next if $configured->{$setting}++; 411 @$target = @values; 412 } 413 else { 414 my $v = Git::config_path(@repo, "$prefix.$setting"); 415 next unless defined $v; 416 next if $configured->{$setting}++; 417 $$target = $v; 418 } 419 } 420 421 foreach my $setting (keys %config_settings) { 422 my $target = $config_settings{$setting}; 423 my $key = "$prefix.$setting"; 424 next unless exists $known_keys->{$key}; 425 if (ref($target) eq "ARRAY") { 426 my @values = @{$known_keys->{$key}}; 427 @values = grep { defined } @values; 428 next if $configured->{$setting}++; 429 @$target = @values; 430 } 431 else { 432 my $v = $known_keys->{$key}->[-1]; 433 next unless defined $v; 434 next if $configured->{$setting}++; 435 $$target = $v; 436 } 437 } 438} 439 440sub config_regexp { 441 my ($regex) = @_; 442 my @ret; 443 eval { 444 my $ret = Git::command( 445 'config', 446 '--null', 447 '--get-regexp', 448 $regex, 449 ); 450 @ret = map { 451 # We must always return ($k, $v) here, since 452 # empty config values will be just "key\0", 453 # not "key\nvalue\0". 454 my ($k, $v) = split /\n/, $_, 2; 455 ($k, $v); 456 } split /\0/, $ret; 457 1; 458 } or do { 459 # If we have no keys we're OK, otherwise re-throw 460 die $@ if $@->value != 1; 461 }; 462 return @ret; 463} 464 465# Save ourselves a lot of work of shelling out to 'git config' (it 466# parses 'bool' etc.) by only doing so for config keys that exist. 467my %known_config_keys; 468{ 469 my @kv = config_regexp("^sende?mail[.]"); 470 while (my ($k, $v) = splice @kv, 0, 2) { 471 push @{$known_config_keys{$k}} => $v; 472 } 473} 474 475# sendemail.identity yields to --identity. We must parse this 476# special-case first before the rest of the config is read. 477{ 478 my $key = "sendemail.identity"; 479 $identity = Git::config(@repo, $key) if exists $known_config_keys{$key}; 480} 481my %identity_options = ( 482 "identity=s" => \$identity, 483 "no-identity" => \$no_identity, 484); 485my $rc = GetOptions(%identity_options); 486usage() unless $rc; 487undef $identity if $no_identity; 488 489# Now we know enough to read the config 490{ 491 my %configured; 492 read_config(\%known_config_keys, \%configured, "sendemail.$identity") if defined $identity; 493 read_config(\%known_config_keys, \%configured, "sendemail"); 494} 495 496# Begin by accumulating all the variables (defined above), that we will end up 497# needing, first, from the command line: 498 499my $help; 500my $git_completion_helper; 501my %dump_aliases_options = ( 502 "h" => \$help, 503 "dump-aliases" => \$dump_aliases, 504 "translate-aliases" => \$translate_aliases, 505); 506$rc = GetOptions(%dump_aliases_options); 507usage() unless $rc; 508die __("--dump-aliases incompatible with other options\n") 509 if !$help and ($dump_aliases or $translate_aliases) and @ARGV; 510die __("--dump-aliases and --translate-aliases are mutually exclusive\n") 511 if !$help and $dump_aliases and $translate_aliases; 512my %options = ( 513 "sender|from=s" => \$sender, 514 "in-reply-to=s" => \$initial_in_reply_to, 515 "reply-to=s" => \$reply_to, 516 "subject=s" => \$initial_subject, 517 "to=s" => \@getopt_to, 518 "to-cmd=s" => \$to_cmd, 519 "no-to" => \$no_to, 520 "cc=s" => \@getopt_cc, 521 "no-cc" => \$no_cc, 522 "bcc=s" => \@getopt_bcc, 523 "no-bcc" => \$no_bcc, 524 "chain-reply-to!" => \$chain_reply_to, 525 "sendmail-cmd=s" => \$sendmail_cmd, 526 "smtp-server=s" => \$smtp_server, 527 "smtp-server-option=s" => \@smtp_server_options, 528 "smtp-server-port=s" => \$smtp_server_port, 529 "smtp-user=s" => \$smtp_authuser, 530 "smtp-pass:s" => \$smtp_authpass, 531 "smtp-ssl" => sub { $smtp_encryption = 'ssl' }, 532 "smtp-encryption=s" => \$smtp_encryption, 533 "smtp-ssl-cert-path=s" => \$smtp_ssl_cert_path, 534 "smtp-debug:i" => \$debug_net_smtp, 535 "smtp-domain:s" => \$smtp_domain, 536 "smtp-auth=s" => \$smtp_auth, 537 "no-smtp-auth" => sub {$smtp_auth = 'none'}, 538 "imap-sent-folder=s" => \$imap_sent_folder, 539 "use-imap-only!" => \$use_imap_only, 540 "annotate!" => \$annotate, 541 "compose" => \$compose, 542 "quiet" => \$quiet, 543 "cc-cmd=s" => \$cc_cmd, 544 "header-cmd=s" => \$header_cmd, 545 "no-header-cmd" => \$no_header_cmd, 546 "suppress-from!" => \$suppress_from, 547 "suppress-cc=s" => \@suppress_cc, 548 "signed-off-cc|signed-off-by-cc!" => \$signed_off_by_cc, 549 "cc-cover!" => \$cover_cc, 550 "to-cover!" => \$cover_to, 551 "confirm=s" => \$confirm, 552 "dry-run" => \$dry_run, 553 "envelope-sender=s" => \$envelope_sender, 554 "thread!" => \$thread, 555 "validate!" => \$validate, 556 "transfer-encoding=s" => \$target_xfer_encoding, 557 "mailmap!" => \$mailmap, 558 "use-mailmap!" => \$mailmap, 559 "format-patch!" => \$format_patch, 560 "8bit-encoding=s" => \$auto_8bit_encoding, 561 "compose-encoding=s" => \$compose_encoding, 562 "force" => \$force, 563 "xmailer!" => \$use_xmailer, 564 "batch-size=i" => \$batch_size, 565 "relogin-delay=i" => \$relogin_delay, 566 "git-completion-helper" => \$git_completion_helper, 567 "v=s" => \$reroll_count, 568 "outlook-id-fix!" => \$outlook_id_fix, 569); 570$rc = GetOptions(%options); 571 572# Munge any "either config or getopt, not both" variables 573my @initial_to = @getopt_to ? @getopt_to : ($no_to ? () : @config_to); 574my @initial_cc = @getopt_cc ? @getopt_cc : ($no_cc ? () : @config_cc); 575my @initial_bcc = @getopt_bcc ? @getopt_bcc : ($no_bcc ? () : @config_bcc); 576 577usage() if $help; 578my %all_options = (%options, %dump_aliases_options, %identity_options); 579completion_helper(\%all_options) if $git_completion_helper; 580unless ($rc) { 581 usage(); 582} 583 584if ($forbid_sendmail_variables && grep { /^sendmail/s } keys %known_config_keys) { 585 die __("fatal: found configuration options for 'sendmail'\n" . 586 "git-send-email is configured with the sendemail.* options - note the 'e'.\n" . 587 "Set sendemail.forbidSendmailVariables to false to disable this check.\n"); 588} 589 590die __("Cannot run git format-patch from outside a repository\n") 591 if $format_patch and not $repo; 592 593die __("`batch-size` and `relogin` must be specified together " . 594 "(via command-line or configuration option)\n") 595 if defined $relogin_delay and not defined $batch_size; 596 597# 'default' encryption is none -- this only prevents a warning 598$smtp_encryption = '' unless (defined $smtp_encryption); 599 600# Set CC suppressions 601my(%suppress_cc); 602if (@suppress_cc) { 603 foreach my $entry (@suppress_cc) { 604 # Please update $__git_send_email_suppresscc_options 605 # in git-completion.bash when you add new options. 606 die sprintf(__("Unknown --suppress-cc field: '%s'\n"), $entry) 607 unless $entry =~ /^(?:all|cccmd|cc|author|self|sob|body|bodycc|misc-by)$/; 608 $suppress_cc{$entry} = 1; 609 } 610} 611 612if ($suppress_cc{'all'}) { 613 foreach my $entry (qw (cccmd cc author self sob body bodycc misc-by)) { 614 $suppress_cc{$entry} = 1; 615 } 616 delete $suppress_cc{'all'}; 617} 618 619# If explicit old-style ones are specified, they trump --suppress-cc. 620$suppress_cc{'self'} = $suppress_from if defined $suppress_from; 621$suppress_cc{'sob'} = !$signed_off_by_cc if defined $signed_off_by_cc; 622 623if ($suppress_cc{'body'}) { 624 foreach my $entry (qw (sob bodycc misc-by)) { 625 $suppress_cc{$entry} = 1; 626 } 627 delete $suppress_cc{'body'}; 628} 629 630# Set confirm's default value 631my $confirm_unconfigured = !defined $confirm; 632if ($confirm_unconfigured) { 633 $confirm = scalar %suppress_cc ? 'compose' : 'auto'; 634}; 635# Please update $__git_send_email_confirm_options in 636# git-completion.bash when you add new options. 637die sprintf(__("Unknown --confirm setting: '%s'\n"), $confirm) 638 unless $confirm =~ /^(?:auto|cc|compose|always|never)/; 639 640# Debugging, print out the suppressions. 641if (0) { 642 print "suppressions:\n"; 643 foreach my $entry (keys %suppress_cc) { 644 printf " %-5s -> $suppress_cc{$entry}\n", $entry; 645 } 646} 647 648my ($repoauthor, $repocommitter); 649{ 650 my %cache; 651 my ($author, $committer); 652 my $common = sub { 653 my ($what) = @_; 654 return $cache{$what} if exists $cache{$what}; 655 ($cache{$what}) = Git::ident_person(@repo, $what); 656 return $cache{$what}; 657 }; 658 $repoauthor = sub { $common->('author') }; 659 $repocommitter = sub { $common->('committer') }; 660} 661 662sub parse_address_line { 663 require Git::LoadCPAN::Mail::Address; 664 return map { $_->format } Mail::Address->parse($_[0]); 665} 666 667sub split_addrs { 668 require Text::ParseWords; 669 return Text::ParseWords::quotewords('\s*,\s*', 1, @_); 670} 671 672my %aliases; 673 674sub parse_sendmail_alias { 675 local $_ = shift; 676 if (/"/) { 677 printf STDERR __("warning: sendmail alias with quotes is not supported: %s\n"), $_; 678 } elsif (/:include:/) { 679 printf STDERR __("warning: `:include:` not supported: %s\n"), $_; 680 } elsif (/[\/|]/) { 681 printf STDERR __("warning: `/file` or `|pipe` redirection not supported: %s\n"), $_; 682 } elsif (/^(\S+?)\s*:\s*(.+)$/) { 683 my ($alias, $addr) = ($1, $2); 684 $aliases{$alias} = [ split_addrs($addr) ]; 685 } else { 686 printf STDERR __("warning: sendmail line is not recognized: %s\n"), $_; 687 } 688} 689 690sub parse_sendmail_aliases { 691 my $fh = shift; 692 my $s = ''; 693 while (<$fh>) { 694 chomp; 695 next if /^\s*$/ || /^\s*#/; 696 $s .= $_, next if $s =~ s/\\$// || s/^\s+//; 697 parse_sendmail_alias($s) if $s; 698 $s = $_; 699 } 700 $s =~ s/\\$//; # silently tolerate stray '\' on last line 701 parse_sendmail_alias($s) if $s; 702} 703 704my %parse_alias = ( 705 # multiline formats can be supported in the future 706 mutt => sub { my $fh = shift; while (<$fh>) { 707 if (/^\s*alias\s+(?:-group\s+\S+\s+)*(\S+)\s+(.*)$/) { 708 my ($alias, $addr) = ($1, $2); 709 $addr =~ s/#.*$//; # mutt allows # comments 710 # commas delimit multiple addresses 711 my @addr = split_addrs($addr); 712 713 # quotes may be escaped in the file, 714 # unescape them so we do not double-escape them later. 715 s/\\"/"/g foreach @addr; 716 $aliases{$alias} = \@addr 717 }}}, 718 mailrc => sub { my $fh = shift; while (<$fh>) { 719 if (/^alias\s+(\S+)\s+(.*?)\s*$/) { 720 require Text::ParseWords; 721 # spaces delimit multiple addresses 722 $aliases{$1} = [ Text::ParseWords::quotewords('\s+', 0, $2) ]; 723 }}}, 724 pine => sub { my $fh = shift; my $f='\t[^\t]*'; 725 for (my $x = ''; defined($x); $x = $_) { 726 chomp $x; 727 $x .= $1 while(defined($_ = <$fh>) && /^ +(.*)$/); 728 $x =~ /^(\S+)$f\t\(?([^\t]+?)\)?(:?$f){0,2}$/ or next; 729 $aliases{$1} = [ split_addrs($2) ]; 730 }}, 731 elm => sub { my $fh = shift; 732 while (<$fh>) { 733 if (/^(\S+)\s+=\s+[^=]+=\s(\S+)/) { 734 my ($alias, $addr) = ($1, $2); 735 $aliases{$alias} = [ split_addrs($addr) ]; 736 } 737 } }, 738 sendmail => \&parse_sendmail_aliases, 739 gnus => sub { my $fh = shift; while (<$fh>) { 740 if (/\(define-mail-alias\s+"(\S+?)"\s+"(\S+?)"\)/) { 741 $aliases{$1} = [ $2 ]; 742 }}} 743 # Please update _git_config() in git-completion.bash when you 744 # add new MUAs. 745); 746 747if (@alias_files and $aliasfiletype and defined $parse_alias{$aliasfiletype}) { 748 foreach my $file (@alias_files) { 749 open my $fh, '<', $file or die "opening $file: $!\n"; 750 $parse_alias{$aliasfiletype}->($fh); 751 close $fh; 752 } 753} 754 755if ($dump_aliases) { 756 print "$_\n" for (sort keys %aliases); 757 exit(0); 758} 759 760if ($translate_aliases) { 761 while (<STDIN>) { 762 my @addr_list = parse_address_line($_); 763 @addr_list = expand_aliases(@addr_list); 764 @addr_list = sanitize_address_list(@addr_list); 765 print "$_\n" for @addr_list; 766 } 767 exit(0); 768} 769 770# is_format_patch_arg($f) returns 0 if $f names a patch, or 1 if 771# $f is a revision list specification to be passed to format-patch. 772sub is_format_patch_arg { 773 return unless $repo; 774 my $f = shift; 775 try { 776 $repo->command('rev-parse', '--verify', '--quiet', $f); 777 if (defined($format_patch)) { 778 return $format_patch; 779 } 780 die sprintf(__(<<EOF), $f, $f); 781File '%s' exists but it could also be the range of commits 782to produce patches for. Please disambiguate by... 783 784 * Saying "./%s" if you mean a file; or 785 * Giving --format-patch option if you mean a range. 786EOF 787 } catch Git::Error::Command with { 788 # Not a valid revision. Treat it as a filename. 789 return 0; 790 } 791} 792 793# Now that all the defaults are set, process the rest of the command line 794# arguments and collect up the files that need to be processed. 795my @rev_list_opts; 796while (defined(my $f = shift @ARGV)) { 797 if ($f eq "--") { 798 push @rev_list_opts, "--", @ARGV; 799 @ARGV = (); 800 } elsif (-d $f and !is_format_patch_arg($f)) { 801 opendir my $dh, $f 802 or die sprintf(__("Failed to opendir %s: %s"), $f, $!); 803 804 require File::Spec; 805 push @files, grep { -f $_ } map { File::Spec->catfile($f, $_) } 806 sort readdir $dh; 807 closedir $dh; 808 } elsif ((-f $f or -p $f) and !is_format_patch_arg($f)) { 809 push @files, $f; 810 } else { 811 push @rev_list_opts, $f; 812 } 813} 814 815if (@rev_list_opts) { 816 die __("Cannot run git format-patch from outside a repository\n") 817 unless $repo; 818 require File::Temp; 819 push @files, $repo->command('format-patch', '-o', File::Temp::tempdir(CLEANUP => 1), 820 defined $reroll_count ? ('-v', $reroll_count) : (), 821 @rev_list_opts); 822} 823 824if (defined $sender) { 825 $sender =~ s/^\s+|\s+$//g; 826 ($sender) = expand_aliases($sender); 827} else { 828 $sender = $repoauthor->() || $repocommitter->() || ''; 829} 830 831# $sender could be an already sanitized address 832# (e.g. sendemail.from could be manually sanitized by user). 833# But it's a no-op to run sanitize_address on an already sanitized address. 834$sender = sanitize_address($sender); 835 836$time = time - scalar $#files; 837 838@files = handle_backup_files(@files); 839 840if (@files) { 841 unless ($quiet) { 842 print $_,"\n" for (@files); 843 } 844} else { 845 print STDERR __("\nNo patch files specified!\n\n"); 846 usage(); 847} 848 849sub get_patch_subject { 850 my $fn = shift; 851 open (my $fh, '<', $fn); 852 while (my $line = <$fh>) { 853 next unless ($line =~ /^Subject: (.*)$/); 854 close $fh; 855 return "GIT: $1\n"; 856 } 857 close $fh; 858 die sprintf(__("No subject line in %s?"), $fn); 859} 860 861if ($compose) { 862 # Note that this does not need to be secure, but we will make a small 863 # effort to have it be unique 864 require File::Temp; 865 $compose_filename = ($repo ? 866 File::Temp::tempfile(".gitsendemail.msg.XXXXXX", DIR => $repo->repo_path()) : 867 File::Temp::tempfile(".gitsendemail.msg.XXXXXX", DIR => "."))[1]; 868 open my $c, ">", $compose_filename 869 or die sprintf(__("Failed to open for writing %s: %s"), $compose_filename, $!); 870 871 872 my $tpl_sender = $sender || $repoauthor->() || $repocommitter->() || ''; 873 my $tpl_subject = $initial_subject || ''; 874 my $tpl_in_reply_to = $initial_in_reply_to || ''; 875 my $tpl_reply_to = $reply_to || ''; 876 my $tpl_to = join(',', @initial_to); 877 my $tpl_cc = join(',', @initial_cc); 878 my $tpl_bcc = join(', ', @initial_bcc); 879 880 print $c <<EOT1, Git::prefix_lines("GIT: ", __(<<EOT2)), <<EOT3; 881From $tpl_sender # This line is ignored. 882EOT1 883Lines beginning in "GIT:" will be removed. 884Consider including an overall diffstat or table of contents 885for the patch you are writing. 886 887Clear the body content if you don't wish to send a summary. 888EOT2 889From: $tpl_sender 890To: $tpl_to 891Cc: $tpl_cc 892Bcc: $tpl_bcc 893Reply-To: $tpl_reply_to 894Subject: $tpl_subject 895In-Reply-To: $tpl_in_reply_to 896 897EOT3 898 for my $f (@files) { 899 print $c get_patch_subject($f); 900 } 901 close $c; 902 903 if ($annotate) { 904 do_edit($compose_filename, @files); 905 } else { 906 do_edit($compose_filename); 907 } 908 909 open my $c2, ">", $compose_filename . ".final" 910 or die sprintf(__("Failed to open %s.final: %s"), $compose_filename, $!); 911 912 open $c, "<", $compose_filename 913 or die sprintf(__("Failed to open %s: %s"), $compose_filename, $!); 914 915 my $need_8bit_cte = file_has_nonascii($compose_filename); 916 my $in_body = 0; 917 my $summary_empty = 1; 918 if (!defined $compose_encoding) { 919 $compose_encoding = "UTF-8"; 920 } 921 while(<$c>) { 922 next if m/^GIT:/; 923 if ($in_body) { 924 $summary_empty = 0 unless (/^\n$/); 925 } elsif (/^\n$/) { 926 $in_body = 1; 927 if ($need_8bit_cte) { 928 print $c2 "MIME-Version: 1.0\n", 929 "Content-Type: text/plain; ", 930 "charset=$compose_encoding\n", 931 "Content-Transfer-Encoding: 8bit\n"; 932 } 933 } elsif (/^MIME-Version:/i) { 934 $need_8bit_cte = 0; 935 } elsif (/^Subject:\s*(.+)\s*$/i) { 936 $initial_subject = $1; 937 my $subject = $initial_subject; 938 $_ = "Subject: " . 939 quote_subject($subject, $compose_encoding) . 940 "\n"; 941 } elsif (/^In-Reply-To:\s*(.+)\s*$/i) { 942 $initial_in_reply_to = $1; 943 next; 944 } elsif (/^Reply-To:\s*(.+)\s*$/i) { 945 $reply_to = $1; 946 } elsif (/^From:\s*(.+)\s*$/i) { 947 $sender = $1; 948 next; 949 } elsif (/^To:\s*(.+)\s*$/i) { 950 @initial_to = parse_address_line($1); 951 next; 952 } elsif (/^Cc:\s*(.+)\s*$/i) { 953 @initial_cc = parse_address_line($1); 954 next; 955 } elsif (/^Bcc:/i) { 956 @initial_bcc = parse_address_line($1); 957 next; 958 } 959 print $c2 $_; 960 } 961 close $c; 962 close $c2; 963 964 if ($summary_empty) { 965 print __("Summary email is empty, skipping it\n"); 966 $compose = -1; 967 } 968} elsif ($annotate) { 969 do_edit(@files); 970} 971 972{ 973 # Only instantiate one $term per program run, since some 974 # Term::ReadLine providers refuse to create a second instance. 975 my $term; 976 sub term { 977 require Term::ReadLine; 978 if (!defined $term) { 979 $term = $ENV{"GIT_SEND_EMAIL_NOTTY"} 980 ? Term::ReadLine->new('git-send-email', \*STDIN, \*STDOUT) 981 : Term::ReadLine->new('git-send-email'); 982 } 983 return $term; 984 } 985} 986 987sub ask { 988 my ($prompt, %arg) = @_; 989 my $valid_re = $arg{valid_re}; 990 my $default = $arg{default}; 991 my $confirm_only = $arg{confirm_only}; 992 my $resp; 993 my $i = 0; 994 my $term = term(); 995 return defined $default ? $default : undef 996 unless defined $term->IN and defined fileno($term->IN) and 997 defined $term->OUT and defined fileno($term->OUT); 998 while ($i++ < 10) { 999 $resp = $term->readline($prompt); 1000 if (!defined $resp) { # EOF 1001 print "\n"; 1002 return defined $default ? $default : undef; 1003 } 1004 if ($resp eq '' and defined $default) { 1005 return $default; 1006 } 1007 if (!defined $valid_re or $resp =~ /$valid_re/) { 1008 return $resp; 1009 } 1010 if ($confirm_only) { 1011 my $yesno = $term->readline( 1012 # TRANSLATORS: please keep [y/N] as is. 1013 sprintf(__("Are you sure you want to use <%s> [y/N]? "), $resp)); 1014 if (defined $yesno && $yesno =~ /y/i) { 1015 return $resp; 1016 } 1017 } 1018 } 1019 return; 1020} 1021 1022my %broken_encoding; 1023 1024sub file_declares_8bit_cte { 1025 my $fn = shift; 1026 open (my $fh, '<', $fn); 1027 while (my $line = <$fh>) { 1028 last if ($line =~ /^$/); 1029 return 1 if ($line =~ /^Content-Transfer-Encoding: .*8bit.*$/); 1030 } 1031 close $fh; 1032 return 0; 1033} 1034 1035foreach my $f (@files) { 1036 next unless (body_or_subject_has_nonascii($f) 1037 && !file_declares_8bit_cte($f)); 1038 $broken_encoding{$f} = 1; 1039} 1040 1041if (!defined $auto_8bit_encoding && scalar %broken_encoding) { 1042 print __("The following files are 8bit, but do not declare " . 1043 "a Content-Transfer-Encoding.\n"); 1044 foreach my $f (sort keys %broken_encoding) { 1045 print " $f\n"; 1046 } 1047 $auto_8bit_encoding = ask(__("Which 8bit encoding should I declare [UTF-8]? "), 1048 valid_re => qr/.{4}/, confirm_only => 1, 1049 default => "UTF-8"); 1050} 1051 1052if (!$force) { 1053 for my $f (@files) { 1054 if (get_patch_subject($f) =~ /\Q*** SUBJECT HERE ***\E/) { 1055 die sprintf(__("Refusing to send because the patch\n\t%s\n" 1056 . "has the template subject '*** SUBJECT HERE ***'. " 1057 . "Pass --force if you really want to send.\n"), $f); 1058 } 1059 } 1060} 1061 1062my $to_whom = __("To whom should the emails be sent (if anyone)?"); 1063my $prompting = 0; 1064if (!@initial_to && !defined $to_cmd) { 1065 my $to = ask("$to_whom ", 1066 default => "", 1067 valid_re => qr/\@.*\./, confirm_only => 1); 1068 push @initial_to, parse_address_line($to) if defined $to; # sanitized/validated later 1069 $prompting++; 1070} 1071 1072sub expand_aliases { 1073 return map { expand_one_alias($_) } @_; 1074} 1075 1076my %EXPANDED_ALIASES; 1077sub expand_one_alias { 1078 my $alias = shift; 1079 if ($EXPANDED_ALIASES{$alias}) { 1080 die sprintf(__("fatal: alias '%s' expands to itself\n"), $alias); 1081 } 1082 local $EXPANDED_ALIASES{$alias} = 1; 1083 return $aliases{$alias} ? expand_aliases(@{$aliases{$alias}}) : $alias; 1084} 1085 1086@initial_to = process_address_list(@initial_to); 1087@initial_cc = process_address_list(@initial_cc); 1088@initial_bcc = process_address_list(@initial_bcc); 1089 1090if ($thread && !defined $initial_in_reply_to && $prompting) { 1091 $initial_in_reply_to = ask( 1092 __("Message-ID to be used as In-Reply-To for the first email (if any)? "), 1093 default => "", 1094 valid_re => qr/\@.*\./, confirm_only => 1); 1095} 1096if (defined $initial_in_reply_to) { 1097 $initial_in_reply_to =~ s/^\s*<?//; 1098 $initial_in_reply_to =~ s/>?\s*$//; 1099 $initial_in_reply_to = "<$initial_in_reply_to>" if $initial_in_reply_to ne ''; 1100} 1101 1102if (defined $reply_to) { 1103 $reply_to =~ s/^\s+|\s+$//g; 1104 ($reply_to) = expand_aliases($reply_to); 1105 $reply_to = sanitize_address($reply_to); 1106} 1107 1108if (!defined $sendmail_cmd && !defined $smtp_server) { 1109 my @sendmail_paths = qw( /usr/sbin/sendmail /usr/lib/sendmail ); 1110 push @sendmail_paths, map {"$_/sendmail"} split /:/, $ENV{PATH}; 1111 foreach (@sendmail_paths) { 1112 if (-x $_) { 1113 $sendmail_cmd = $_; 1114 last; 1115 } 1116 } 1117 1118 if (!defined $sendmail_cmd) { 1119 $smtp_server = 'localhost'; # could be 127.0.0.1, too... *shrug* 1120 } 1121} 1122 1123if ($compose && $compose > 0) { 1124 @files = ($compose_filename . ".final", @files); 1125} 1126 1127# Variables we set as part of the loop over files 1128our ($message_id, %mail, $subject, $in_reply_to, $references, $message, 1129 $needs_confirm, $message_num, $ask_default); 1130 1131sub mailmap_address_list { 1132 return @_ unless @_ and $mailmap; 1133 my @options = (); 1134 push(@options, "--mailmap-file=$mailmap_file") if $mailmap_file; 1135 push(@options, "--mailmap-blob=$mailmap_blob") if $mailmap_blob; 1136 my @addr_list = Git::command('check-mailmap', @options, @_); 1137 s/^<(.*)>$/$1/ for @addr_list; 1138 return @addr_list; 1139} 1140 1141sub extract_valid_address { 1142 my $address = shift; 1143 my $local_part_regexp = qr/[^<>"\s@]+/; 1144 my $domain_regexp = qr/[^.<>"\s@]+(?:\.[^.<>"\s@]+)+/; 1145 1146 # check for a local address: 1147 return $address if ($address =~ /^($local_part_regexp)$/); 1148 1149 $address =~ s/^\s*<(.*)>\s*$/$1/; 1150 my $have_email_valid = eval { require Email::Valid; 1 }; 1151 if ($have_email_valid) { 1152 return scalar Email::Valid->address($address); 1153 } 1154 1155 # less robust/correct than the monster regexp in Email::Valid, 1156 # but still does a 99% job, and one less dependency 1157 return $1 if $address =~ /($local_part_regexp\@$domain_regexp)/; 1158 return; 1159} 1160 1161sub extract_valid_address_or_die { 1162 my $address = shift; 1163 my $valid_address = extract_valid_address($address); 1164 die sprintf(__("error: unable to extract a valid address from: %s\n"), $address) 1165 if !$valid_address; 1166 return $valid_address; 1167} 1168 1169sub validate_address { 1170 my $address = shift; 1171 while (!extract_valid_address($address)) { 1172 printf STDERR __("error: unable to extract a valid address from: %s\n"), $address; 1173 # TRANSLATORS: Make sure to include [q] [d] [e] in your 1174 # translation. The program will only accept English input 1175 # at this point. 1176 $_ = ask(__("What to do with this address? ([q]uit|[d]rop|[e]dit): "), 1177 valid_re => qr/^(?:quit|q|drop|d|edit|e)/i, 1178 default => 'q'); 1179 if (/^d/i) { 1180 return undef; 1181 } elsif (/^q/i) { 1182 cleanup_compose_files(); 1183 exit(0); 1184 } 1185 $address = ask("$to_whom ", 1186 default => "", 1187 valid_re => qr/\@.*\./, confirm_only => 1); 1188 } 1189 return $address; 1190} 1191 1192sub validate_address_list { 1193 return (grep { defined $_ } 1194 map { validate_address($_) } @_); 1195} 1196 1197# Usually don't need to change anything below here. 1198 1199# we make a "fake" message id by taking the current number 1200# of seconds since the beginning of Unix time and tacking on 1201# a random number to the end, in case we are called quicker than 1202# 1 second since the last time we were called. 1203 1204# We'll setup a template for the message id, using the "from" address: 1205 1206my ($message_id_stamp, $message_id_serial); 1207sub make_message_id { 1208 my $uniq; 1209 if (!defined $message_id_stamp) { 1210 require POSIX; 1211 $message_id_stamp = POSIX::strftime("%Y%m%d%H%M%S.$$", gmtime(time)); 1212 $message_id_serial = 0; 1213 } 1214 $message_id_serial++; 1215 $uniq = "$message_id_stamp-$message_id_serial"; 1216 1217 my $du_part; 1218 for ($sender, $repocommitter->(), $repoauthor->()) { 1219 $du_part = extract_valid_address(sanitize_address($_)); 1220 last if (defined $du_part and $du_part ne ''); 1221 } 1222 if (not defined $du_part or $du_part eq '') { 1223 require Sys::Hostname; 1224 $du_part = 'user@' . Sys::Hostname::hostname(); 1225 } 1226 my $message_id_template = "<%s-%s>"; 1227 $message_id = sprintf($message_id_template, $uniq, $du_part); 1228 #print "new message id = $message_id\n"; # Was useful for debugging 1229} 1230 1231sub unquote_rfc2047 { 1232 local ($_) = @_; 1233 my $charset; 1234 my $sep = qr/[ \t]+/; 1235 s{$re_encoded_word(?:$sep$re_encoded_word)*}{ 1236 my @words = split $sep, $&; 1237 foreach (@words) { 1238 m/$re_encoded_word/; 1239 $charset = $1; 1240 my $encoding = $2; 1241 my $text = $3; 1242 if ($encoding eq 'q' || $encoding eq 'Q') { 1243 $_ = $text; 1244 s/_/ /g; 1245 s/=([0-9A-F]{2})/chr(hex($1))/egi; 1246 } else { 1247 # other encodings not supported yet 1248 } 1249 } 1250 join '', @words; 1251 }eg; 1252 return wantarray ? ($_, $charset) : $_; 1253} 1254 1255sub quote_rfc2047 { 1256 local $_ = shift; 1257 my $encoding = shift || 'UTF-8'; 1258 s/([^-a-zA-Z0-9!*+\/])/sprintf("=%02X", ord($1))/eg; 1259 s/(.*)/=\?$encoding\?q\?$1\?=/; 1260 return $_; 1261} 1262 1263sub is_rfc2047_quoted { 1264 my $s = shift; 1265 length($s) <= 75 && 1266 $s =~ m/^(?:"[[:ascii:]]*"|$re_encoded_word)$/o; 1267} 1268 1269sub subject_needs_rfc2047_quoting { 1270 my $s = shift; 1271 1272 return ($s =~ /[^[:ascii:]]/) || ($s =~ /=\?/); 1273} 1274 1275sub quote_subject { 1276 local $subject = shift; 1277 my $encoding = shift || 'UTF-8'; 1278 1279 if (subject_needs_rfc2047_quoting($subject)) { 1280 return quote_rfc2047($subject, $encoding); 1281 } 1282 return $subject; 1283} 1284 1285# use the simplest quoting being able to handle the recipient 1286sub sanitize_address { 1287 my ($recipient) = @_; 1288 1289 # remove garbage after email address 1290 $recipient =~ s/(.*>).*$/$1/; 1291 1292 my ($recipient_name, $recipient_addr) = ($recipient =~ /^(.*?)\s*(<.*)/); 1293 1294 if (not $recipient_name) { 1295 return $recipient; 1296 } 1297 1298 # if recipient_name is already quoted, do nothing 1299 if (is_rfc2047_quoted($recipient_name)) { 1300 return $recipient; 1301 } 1302 1303 # remove non-escaped quotes 1304 $recipient_name =~ s/(^|[^\\])"/$1/g; 1305 1306 # rfc2047 is needed if a non-ascii char is included 1307 if ($recipient_name =~ /[^[:ascii:]]/) { 1308 $recipient_name = quote_rfc2047($recipient_name); 1309 } 1310 1311 # double quotes are needed if specials or CTLs are included 1312 elsif ($recipient_name =~ /[][()<>@,;:\\".\000-\037\177]/) { 1313 $recipient_name =~ s/([\\\r])/\\$1/g; 1314 $recipient_name = qq["$recipient_name"]; 1315 } 1316 1317 return "$recipient_name $recipient_addr"; 1318 1319} 1320 1321sub strip_garbage_one_address { 1322 my ($addr) = @_; 1323 chomp $addr; 1324 if ($addr =~ /^(("[^"]*"|[^"<]*)? *<[^>]*>).*/) { 1325 # "Foo Bar" <foobar@example.com> [possibly garbage here] 1326 # Foo Bar <foobar@example.com> [possibly garbage here] 1327 return $1; 1328 } 1329 if ($addr =~ /^(<[^>]*>).*/) { 1330 # <foo@example.com> [possibly garbage here] 1331 # if garbage contains other addresses, they are ignored. 1332 return $1; 1333 } 1334 if ($addr =~ /^([^"#,\s]*)/) { 1335 # address without quoting: remove anything after the address 1336 return $1; 1337 } 1338 return $addr; 1339} 1340 1341sub sanitize_address_list { 1342 return (map { sanitize_address($_) } @_); 1343} 1344 1345sub process_address_list { 1346 my @addr_list = map { parse_address_line($_) } @_; 1347 @addr_list = expand_aliases(@addr_list); 1348 @addr_list = sanitize_address_list(@addr_list); 1349 @addr_list = validate_address_list(@addr_list); 1350 @addr_list = mailmap_address_list(@addr_list); 1351 return @addr_list; 1352} 1353 1354# Returns the local Fully Qualified Domain Name (FQDN) if available. 1355# 1356# Tightly configured MTAa require that a caller sends a real DNS 1357# domain name that corresponds the IP address in the HELO/EHLO 1358# handshake. This is used to verify the connection and prevent 1359# spammers from trying to hide their identity. If the DNS and IP don't 1360# match, the receiving MTA may deny the connection. 1361# 1362# Here is a deny example of Net::SMTP with the default "localhost.localdomain" 1363# 1364# Net::SMTP=GLOB(0x267ec28)>>> EHLO localhost.localdomain 1365# Net::SMTP=GLOB(0x267ec28)<<< 550 EHLO argument does not match calling host 1366# 1367# This maildomain*() code is based on ideas in Perl library Test::Reporter 1368# /usr/share/perl5/Test/Reporter/Mail/Util.pm ==> sub _maildomain () 1369 1370sub valid_fqdn { 1371 my $domain = shift; 1372 my $subdomain = '(?!-)[A-Za-z0-9-]{1,63}(?<!-)'; 1373 return defined $domain && !($^O eq 'darwin' && $domain =~ /\.local$/) 1374 && $domain =~ /^$subdomain(?:\.$subdomain)*$/; 1375} 1376 1377sub maildomain_net { 1378 my $maildomain; 1379 1380 require Net::Domain; 1381 my $domain = Net::Domain::domainname(); 1382 $maildomain = $domain if valid_fqdn($domain); 1383 1384 return $maildomain; 1385} 1386 1387sub maildomain_mta { 1388 my $maildomain; 1389 1390 for my $host (qw(mailhost localhost)) { 1391 require Net::SMTP; 1392 my $smtp = Net::SMTP->new($host); 1393 if (defined $smtp) { 1394 my $domain = $smtp->domain; 1395 $smtp->quit; 1396 1397 $maildomain = $domain if valid_fqdn($domain); 1398 1399 last if $maildomain; 1400 } 1401 } 1402 1403 return $maildomain; 1404} 1405 1406sub maildomain_hostname_command { 1407 my $maildomain; 1408 1409 if ($^O eq 'linux' || $^O eq 'darwin') { 1410 my $domain = `(hostname -f) 2>/dev/null`; 1411 if (!$?) { 1412 chomp($domain); 1413 $maildomain = $domain if valid_fqdn($domain); 1414 } 1415 } 1416 return $maildomain; 1417} 1418 1419sub maildomain { 1420 return maildomain_net() || maildomain_mta() || 1421 maildomain_hostname_command || 'localhost.localdomain'; 1422} 1423 1424sub smtp_host_string { 1425 if (defined $smtp_server_port) { 1426 return "$smtp_server:$smtp_server_port"; 1427 } else { 1428 return $smtp_server; 1429 } 1430} 1431 1432# Returns 1 if authentication succeeded or was not necessary 1433# (smtp_user was not specified), and 0 otherwise. 1434 1435sub smtp_auth_maybe { 1436 if (!defined $smtp_authuser || $auth || (defined $smtp_auth && $smtp_auth eq "none")) { 1437 return 1; 1438 } 1439 1440 # Workaround AUTH PLAIN/LOGIN interaction defect 1441 # with Authen::SASL::Cyrus 1442 eval { 1443 require Authen::SASL; 1444 Authen::SASL->import(qw(Perl)); 1445 }; 1446 1447 # Check mechanism naming as defined in: 1448 # https://tools.ietf.org/html/rfc4422#page-8 1449 if ($smtp_auth && $smtp_auth !~ /^(\b[A-Z0-9-_]{1,20}\s*)*$/) { 1450 die "invalid smtp auth: '${smtp_auth}'"; 1451 } 1452 1453 # Authentication may fail not because credentials were 1454 # invalid but due to other reasons, in which we should not 1455 # reject credentials. 1456 $auth = Git::credential({ 1457 'protocol' => 'smtp', 1458 'host' => smtp_host_string(), 1459 'username' => $smtp_authuser, 1460 # if there's no password, "git credential fill" will 1461 # give us one, otherwise it'll just pass this one. 1462 'password' => $smtp_authpass 1463 }, sub { 1464 my $cred = shift; 1465 my $result; 1466 my $error; 1467 1468 # catch all SMTP auth error in a unified eval block 1469 eval { 1470 if ($smtp_auth) { 1471 my $sasl = Authen::SASL->new( 1472 mechanism => $smtp_auth, 1473 callback => { 1474 user => $cred->{'username'}, 1475 pass => $cred->{'password'}, 1476 authname => $cred->{'username'}, 1477 } 1478 ); 1479 $result = $smtp->auth($sasl); 1480 } else { 1481 $result = $smtp->auth($cred->{'username'}, $cred->{'password'}); 1482 } 1483 1; # ensure true value is returned if no exception is thrown 1484 } or do { 1485 $error = $@ || 'Unknown error'; 1486 }; 1487 1488 return ($error 1489 ? handle_smtp_error($error) 1490 : ($result ? 1 : 0)); 1491 }); 1492 1493 return $auth; 1494} 1495 1496sub handle_smtp_error { 1497 my ($error) = @_; 1498 1499 # Parse SMTP status code from error message in: 1500 # https://www.rfc-editor.org/rfc/rfc5321.html 1501 if ($error =~ /\b(\d{3})\b/) { 1502 my $status_code = $1; 1503 if ($status_code =~ /^4/) { 1504 # 4yz: Transient Negative Completion reply 1505 warn "SMTP transient error (status code $status_code): $error"; 1506 return 1; 1507 } elsif ($status_code =~ /^5/) { 1508 # 5yz: Permanent Negative Completion reply 1509 warn "SMTP permanent error (status code $status_code): $error"; 1510 return 0; 1511 } 1512 # If no recognized status code is found, treat as transient error 1513 warn "SMTP unknown error: $error. Treating as transient failure."; 1514 return 1; 1515 } 1516 1517 # If no status code is found, treat as transient error 1518 warn "SMTP generic error: $error"; 1519 return 1; 1520} 1521 1522sub ssl_verify_params { 1523 eval { 1524 require IO::Socket::SSL; 1525 IO::Socket::SSL->import(qw/SSL_VERIFY_PEER SSL_VERIFY_NONE/); 1526 }; 1527 if ($@) { 1528 print STDERR "Not using SSL_VERIFY_PEER due to out-of-date IO::Socket::SSL.\n"; 1529 return; 1530 } 1531 1532 if (!defined $smtp_ssl_cert_path) { 1533 # use the OpenSSL defaults 1534 return (SSL_verify_mode => SSL_VERIFY_PEER()); 1535 } 1536 1537 if ($smtp_ssl_cert_path eq "") { 1538 return (SSL_verify_mode => SSL_VERIFY_NONE()); 1539 } elsif (-d $smtp_ssl_cert_path) { 1540 return (SSL_verify_mode => SSL_VERIFY_PEER(), 1541 SSL_ca_path => $smtp_ssl_cert_path); 1542 } elsif (-f $smtp_ssl_cert_path) { 1543 return (SSL_verify_mode => SSL_VERIFY_PEER(), 1544 SSL_ca_file => $smtp_ssl_cert_path); 1545 } else { 1546 die sprintf(__("CA path \"%s\" does not exist"), $smtp_ssl_cert_path); 1547 } 1548} 1549 1550sub file_name_is_absolute { 1551 my ($path) = @_; 1552 1553 # msys does not grok DOS drive-prefixes 1554 if ($^O eq 'msys') { 1555 return ($path =~ m#^/# || $path =~ m#^[a-zA-Z]\:#) 1556 } 1557 1558 require File::Spec::Functions; 1559 return File::Spec::Functions::file_name_is_absolute($path); 1560} 1561 1562sub gen_header { 1563 my @recipients = unique_email_list(@to); 1564 @cc = (grep { my $cc = extract_valid_address_or_die($_); 1565 not grep { $cc eq $_ || $_ =~ /<\Q${cc}\E>$/ } @recipients 1566 } 1567 @cc); 1568 my $to = join (",\n\t", @recipients); 1569 @recipients = unique_email_list(@recipients,@cc,@initial_bcc); 1570 @recipients = (map { extract_valid_address_or_die($_) } @recipients); 1571 my $date = format_2822_time($time++); 1572 my $gitversion = '@GIT_VERSION@'; 1573 if ($gitversion =~ m/..GIT_VERSION../) { 1574 $gitversion = Git::version(); 1575 } 1576 1577 my $cc = join(",\n\t", unique_email_list(@cc)); 1578 my $ccline = ""; 1579 if ($cc ne '') { 1580 $ccline = "\nCc: $cc"; 1581 } 1582 make_message_id() unless defined($message_id); 1583 1584 my $header = "From: $sender 1585To: $to${ccline} 1586Subject: $subject 1587Date: $date 1588Message-ID: $message_id 1589"; 1590 if ($use_xmailer) { 1591 $header .= "X-Mailer: git-send-email $gitversion\n"; 1592 } 1593 if ($in_reply_to) { 1594 1595 $header .= "In-Reply-To: $in_reply_to\n"; 1596 $header .= "References: $references\n"; 1597 } 1598 if ($reply_to) { 1599 $header .= "Reply-To: $reply_to\n"; 1600 } 1601 if (@xh) { 1602 $header .= join("\n", @xh) . "\n"; 1603 } 1604 my $recipients_ref = \@recipients; 1605 return ($recipients_ref, $to, $date, $gitversion, $cc, $ccline, $header); 1606} 1607 1608sub is_outlook { 1609 my ($host) = @_; 1610 if ($outlook_id_fix eq 'auto') { 1611 $outlook_id_fix = 1612 ($host eq 'smtp.office365.com' || 1613 $host eq 'smtp-mail.outlook.com') ? 1 : 0; 1614 } 1615 return $outlook_id_fix; 1616} 1617 1618# Prepares the email, then asks the user what to do. 1619# 1620# If the user chooses to send the email, it's sent and 1 is returned. 1621# If the user chooses not to send the email, 0 is returned. 1622# If the user decides they want to make further edits, -1 is returned and the 1623# caller is expected to call send_message again after the edits are performed. 1624# 1625# If an error occurs sending the email, this just dies. 1626 1627sub send_message { 1628 my ($recipients_ref, $to, $date, $gitversion, $cc, $ccline, $header) = gen_header(); 1629 my @recipients = @$recipients_ref; 1630 1631 my @sendmail_parameters = ('-i', @recipients); 1632 my $raw_from = $sender; 1633 if (defined $envelope_sender && $envelope_sender ne "auto") { 1634 $raw_from = $envelope_sender; 1635 } 1636 $raw_from = extract_valid_address($raw_from); 1637 unshift (@sendmail_parameters, 1638 '-f', $raw_from) if(defined $envelope_sender); 1639 1640 if ($needs_confirm && !$dry_run) { 1641 print "\n$header\n"; 1642 if ($needs_confirm eq "inform") { 1643 $confirm_unconfigured = 0; # squelch this message for the rest of this run 1644 $ask_default = "y"; # assume yes on EOF since user hasn't explicitly asked for confirmation 1645 print __ <<EOF ; 1646 The Cc list above has been expanded by additional 1647 addresses found in the patch commit message. By default 1648 send-email prompts before sending whenever this occurs. 1649 This behavior is controlled by the sendemail.confirm 1650 configuration setting. 1651 1652 For additional information, run 'git send-email --help'. 1653 To retain the current behavior, but squelch this message, 1654 run 'git config --global sendemail.confirm auto'. 1655 1656EOF 1657 } 1658 # TRANSLATORS: Make sure to include [y] [n] [e] [q] [a] in your 1659 # translation. The program will only accept English input 1660 # at this point. 1661 $_ = ask(__("Send this email? ([y]es|[n]o|[e]dit|[q]uit|[a]ll): "), 1662 valid_re => qr/^(?:yes|y|no|n|edit|e|quit|q|all|a)/i, 1663 default => $ask_default); 1664 die __("Send this email reply required") unless defined $_; 1665 if (/^n/i) { 1666 # If we are skipping a message, we should make sure that 1667 # the next message is treated as the successor to the 1668 # previously sent message, and not the skipped message. 1669 $message_num--; 1670 return 0; 1671 } elsif (/^e/i) { 1672 # Since the same message will be sent again, we need to 1673 # decrement the message number to the previous message. 1674 # Otherwise, the edited message will be treated as a 1675 # different message sent after the original non-edited 1676 # message. 1677 $message_num--; 1678 return -1; 1679 } elsif (/^q/i) { 1680 cleanup_compose_files(); 1681 exit(0); 1682 } elsif (/^a/i) { 1683 $confirm = 'never'; 1684 } 1685 } 1686 1687 unshift (@sendmail_parameters, @smtp_server_options); 1688 1689 if ($dry_run) { 1690 # We don't want to send the email. 1691 } elsif ($use_imap_only) { 1692 die __("The destination IMAP folder is not properly defined.") if !defined $imap_sent_folder; 1693 } elsif (defined $sendmail_cmd || file_name_is_absolute($smtp_server)) { 1694 my $pid = open my $sm, '|-'; 1695 defined $pid or die $!; 1696 if (!$pid) { 1697 if (defined $sendmail_cmd) { 1698 exec ("sh", "-c", "$sendmail_cmd \"\$@\"", "-", @sendmail_parameters) 1699 or die $!; 1700 } else { 1701 exec ($smtp_server, @sendmail_parameters) 1702 or die $!; 1703 } 1704 } 1705 print $sm "$header\n$message"; 1706 close $sm or die $!; 1707 } else { 1708 1709 if (!defined $smtp_server) { 1710 die __("The required SMTP server is not properly defined.") 1711 } 1712 1713 require Net::SMTP; 1714 my $use_net_smtp_ssl = version->parse($Net::SMTP::VERSION) < version->parse("2.34"); 1715 $smtp_domain ||= maildomain(); 1716 1717 if ($smtp_encryption eq 'ssl') { 1718 $smtp_server_port ||= 465; # ssmtp 1719 require IO::Socket::SSL; 1720 1721 # Suppress "variable accessed once" warning. 1722 { 1723 no warnings 'once'; 1724 $IO::Socket::SSL::DEBUG = 1; 1725 } 1726 1727 # Net::SMTP::SSL->new() does not forward any SSL options 1728 IO::Socket::SSL::set_client_defaults( 1729 ssl_verify_params()); 1730 1731 if ($use_net_smtp_ssl) { 1732 require Net::SMTP::SSL; 1733 $smtp ||= Net::SMTP::SSL->new($smtp_server, 1734 Hello => $smtp_domain, 1735 Port => $smtp_server_port, 1736 Debug => $debug_net_smtp); 1737 } 1738 else { 1739 $smtp ||= Net::SMTP->new($smtp_server, 1740 Hello => $smtp_domain, 1741 Port => $smtp_server_port, 1742 Debug => $debug_net_smtp, 1743 SSL => 1); 1744 } 1745 } 1746 elsif (!$smtp) { 1747 $smtp_server_port ||= 25; 1748 $smtp ||= Net::SMTP->new($smtp_server, 1749 Hello => $smtp_domain, 1750 Debug => $debug_net_smtp, 1751 Port => $smtp_server_port); 1752 if ($smtp_encryption eq 'tls' && $smtp) { 1753 if ($use_net_smtp_ssl) { 1754 $smtp->command('STARTTLS'); 1755 $smtp->response(); 1756 if ($smtp->code != 220) { 1757 die sprintf(__("Server does not support STARTTLS! %s"), $smtp->message); 1758 } 1759 require Net::SMTP::SSL; 1760 $smtp = Net::SMTP::SSL->start_SSL($smtp, 1761 ssl_verify_params()) 1762 or die sprintf(__("STARTTLS failed! %s"), IO::Socket::SSL::errstr()); 1763 } 1764 else { 1765 $smtp->starttls(ssl_verify_params()) 1766 or die sprintf(__("STARTTLS failed! %s"), IO::Socket::SSL::errstr()); 1767 } 1768 # Send EHLO again to receive fresh 1769 # supported commands 1770 $smtp->hello($smtp_domain); 1771 } 1772 } 1773 1774 if (!$smtp) { 1775 die __("Unable to initialize SMTP properly. Check config and use --smtp-debug."), 1776 " VALUES: server=$smtp_server ", 1777 "encryption=$smtp_encryption ", 1778 "hello=$smtp_domain", 1779 defined $smtp_server_port ? " port=$smtp_server_port" : ""; 1780 } 1781 1782 smtp_auth_maybe or die $smtp->message; 1783 1784 $smtp->mail( $raw_from ) or die $smtp->message; 1785 $smtp->to( @recipients ) or die $smtp->message; 1786 $smtp->data or die $smtp->message; 1787 $smtp->datasend("$header\n") or die $smtp->message; 1788 my @lines = split /^/, $message; 1789 foreach my $line (@lines) { 1790 $smtp->datasend("$line") or die $smtp->message; 1791 } 1792 $smtp->dataend() or die $smtp->message; 1793 1794 # Outlook discards the Message-ID header we set while sending the email 1795 # and generates a new random Message-ID. So in order to avoid breaking 1796 # threads, we simply retrieve the Message-ID from the server response 1797 # and assign it to the $message_id variable, which will then be 1798 # assigned to $in_reply_to by the caller when the next message is sent 1799 # as a response to this message. 1800 if (is_outlook($smtp_server)) { 1801 if ($smtp->message =~ /<([^>]+)>/) { 1802 $message_id = "<$1>"; 1803 $header =~ s/^(Message-ID:\s*).*\n/${1}$message_id\n/m; 1804 printf __("Outlook reassigned Message-ID to: %s\n"), $message_id if $smtp->debug; 1805 } else { 1806 warn __("Warning: Could not retrieve Message-ID from server response.\n"); 1807 } 1808 } 1809 1810 $smtp->code =~ /250|200/ or die sprintf(__("Failed to send %s\n"), $subject).$smtp->message; 1811 } 1812 if ($quiet) { 1813 printf($dry_run ? __("Dry-Sent %s") : __("Sent %s"), $subject); 1814 print "\n"; 1815 } else { 1816 print($dry_run ? __("Dry-OK. Log says:") : __("OK. Log says:")); 1817 print "\n"; 1818 if (!defined $sendmail_cmd && !file_name_is_absolute($smtp_server)) { 1819 print "Server: $smtp_server\n"; 1820 print "MAIL FROM:<$raw_from>\n"; 1821 foreach my $entry (@recipients) { 1822 print "RCPT TO:<$entry>\n"; 1823 } 1824 } else { 1825 my $sm; 1826 if (defined $sendmail_cmd) { 1827 $sm = $sendmail_cmd; 1828 } else { 1829 $sm = $smtp_server; 1830 } 1831 1832 print "Sendmail: $sm ".join(' ',@sendmail_parameters)."\n"; 1833 } 1834 print $header, "\n"; 1835 if ($smtp) { 1836 print __("Result: "), $smtp->code, ' ', 1837 ($smtp->message =~ /\n([^\n]+\n)$/s); 1838 } else { 1839 print __("Result: OK"); 1840 } 1841 print "\n"; 1842 } 1843 1844 if ($imap_sent_folder && !$dry_run) { 1845 my $imap_header = $header; 1846 if (@initial_bcc) { 1847 # Bcc is not a part of $header, so we add it here. 1848 # This is only for the IMAP copy, not for the actual email 1849 # sent to the recipients. 1850 $imap_header .= "Bcc: " . join(", ", @initial_bcc) . "\n"; 1851 } 1852 push @imap_copy, "From git-send-email\n$imap_header\n$message"; 1853 } 1854 1855 return 1; 1856} 1857 1858sub pre_process_file { 1859 my ($t, $quiet) = @_; 1860 1861 open my $fh, "<", $t or die sprintf(__("can't open file %s"), $t); 1862 1863 my $author = undef; 1864 my $sauthor = undef; 1865 my $author_encoding; 1866 my $has_content_type; 1867 my $body_encoding; 1868 my $xfer_encoding; 1869 my $has_mime_version; 1870 @to = (); 1871 @cc = (); 1872 @xh = (); 1873 my $input_format = undef; 1874 my @header = (); 1875 $subject = $initial_subject; 1876 $message = ""; 1877 $message_num++; 1878 undef $message_id; 1879 # Retrieve and unfold header fields. 1880 my @header_lines = (); 1881 while(<$fh>) { 1882 last if /^\s*$/; 1883 push(@header_lines, $_); 1884 } 1885 @header = unfold_headers(@header_lines); 1886 # Add computed headers, if applicable. 1887 unless ($no_header_cmd || ! $header_cmd) { 1888 push @header, invoke_header_cmd($header_cmd, $t); 1889 } 1890 # Now parse the header 1891 foreach(@header) { 1892 if (/^From /) { 1893 $input_format = 'mbox'; 1894 next; 1895 } 1896 chomp; 1897 if (!defined $input_format && /^[-A-Za-z]+:\s/) { 1898 $input_format = 'mbox'; 1899 } 1900 1901 if (defined $input_format && $input_format eq 'mbox') { 1902 if (/^Subject:\s+(.*)$/i) { 1903 $subject = $1; 1904 } 1905 elsif (/^From:\s+(.*)$/i) { 1906 ($author, $author_encoding) = unquote_rfc2047($1); 1907 $sauthor = sanitize_address($author); 1908 next if $suppress_cc{'author'}; 1909 next if $suppress_cc{'self'} and $sauthor eq $sender; 1910 printf(__("(mbox) Adding cc: %s from line '%s'\n"), 1911 $1, $_) unless $quiet; 1912 push @cc, $1; 1913 } 1914 elsif (/^To:\s+(.*)$/i) { 1915 foreach my $addr (parse_address_line($1)) { 1916 printf(__("(mbox) Adding to: %s from line '%s'\n"), 1917 $addr, $_) unless $quiet; 1918 push @to, $addr; 1919 } 1920 } 1921 elsif (/^Cc:\s+(.*)$/i) { 1922 foreach my $addr (parse_address_line($1)) { 1923 my $qaddr = unquote_rfc2047($addr); 1924 my $saddr = sanitize_address($qaddr); 1925 if ($saddr eq $sender) { 1926 next if ($suppress_cc{'self'}); 1927 } else { 1928 next if ($suppress_cc{'cc'}); 1929 } 1930 printf(__("(mbox) Adding cc: %s from line '%s'\n"), 1931 $addr, $_) unless $quiet; 1932 push @cc, $addr; 1933 } 1934 } 1935 elsif (/^Content-type:/i) { 1936 $has_content_type = 1; 1937 if (/charset="?([^ "]+)/) { 1938 $body_encoding = $1; 1939 } 1940 push @xh, $_; 1941 } 1942 elsif (/^MIME-Version/i) { 1943 $has_mime_version = 1; 1944 push @xh, $_; 1945 } 1946 elsif (/^Message-ID: (.*)/i) { 1947 $message_id = $1; 1948 } 1949 elsif (/^Content-Transfer-Encoding: (.*)/i) { 1950 $xfer_encoding = $1 if not defined $xfer_encoding; 1951 } 1952 elsif (/^In-Reply-To: (.*)/i) { 1953 if (!$initial_in_reply_to || $thread) { 1954 $in_reply_to = $1; 1955 } 1956 } 1957 elsif (/^Reply-To: (.*)/i) { 1958 $reply_to = $1; 1959 } 1960 elsif (/^References: (.*)/i) { 1961 if (!$initial_in_reply_to || $thread) { 1962 $references = $1; 1963 } 1964 } 1965 elsif (!/^Date:\s/i && /^[-A-Za-z]+:\s+\S/) { 1966 push @xh, $_; 1967 } 1968 } else { 1969 # In the traditional 1970 # "send lots of email" format, 1971 # line 1 = cc 1972 # line 2 = subject 1973 # So let's support that, too. 1974 $input_format = 'lots'; 1975 if (@cc == 0 && !$suppress_cc{'cc'}) { 1976 printf(__("(non-mbox) Adding cc: %s from line '%s'\n"), 1977 $_, $_) unless $quiet; 1978 push @cc, $_; 1979 } elsif (!defined $subject) { 1980 $subject = $_; 1981 } 1982 } 1983 } 1984 # Now parse the message body 1985 while(<$fh>) { 1986 $message .= $_; 1987 if (/^([a-z][a-z-]*-by|Cc): (.*)/i) { 1988 chomp; 1989 my ($what, $c) = ($1, $2); 1990 # strip garbage for the address we'll use: 1991 $c = strip_garbage_one_address($c); 1992 # sanitize a bit more to decide whether to suppress the address: 1993 my $sc = sanitize_address($c); 1994 if ($sc eq $sender) { 1995 next if ($suppress_cc{'self'}); 1996 } else { 1997 if ($what =~ /^Signed-off-by$/i) { 1998 next if $suppress_cc{'sob'}; 1999 } elsif ($what =~ /-by$/i) { 2000 next if $suppress_cc{'misc-by'}; 2001 } elsif ($what =~ /Cc/i) { 2002 next if $suppress_cc{'bodycc'}; 2003 } 2004 } 2005 if ($c !~ /.+@.+|<.+>/) { 2006 printf("(body) Ignoring %s from line '%s'\n", 2007 $what, $_) unless $quiet; 2008 next; 2009 } 2010 push @cc, $sc; 2011 printf(__("(body) Adding cc: %s from line '%s'\n"), 2012 $sc, $_) unless $quiet; 2013 } 2014 } 2015 close $fh; 2016 2017 push @to, recipients_cmd("to-cmd", "to", $to_cmd, $t, $quiet) 2018 if defined $to_cmd; 2019 push @cc, recipients_cmd("cc-cmd", "cc", $cc_cmd, $t, $quiet) 2020 if defined $cc_cmd && !$suppress_cc{'cccmd'}; 2021 2022 if ($broken_encoding{$t} && !$has_content_type) { 2023 $xfer_encoding = '8bit' if not defined $xfer_encoding; 2024 $has_content_type = 1; 2025 push @xh, "Content-Type: text/plain; charset=$auto_8bit_encoding"; 2026 $body_encoding = $auto_8bit_encoding; 2027 } 2028 2029 if ($broken_encoding{$t} && !is_rfc2047_quoted($subject)) { 2030 $subject = quote_subject($subject, $auto_8bit_encoding); 2031 } 2032 2033 if (defined $sauthor and $sauthor ne $sender) { 2034 $message = "From: $author\n\n$message"; 2035 if (defined $author_encoding) { 2036 if ($has_content_type) { 2037 if ($body_encoding eq $author_encoding) { 2038 # ok, we already have the right encoding 2039 } 2040 else { 2041 # uh oh, we should re-encode 2042 } 2043 } 2044 else { 2045 $xfer_encoding = '8bit' if not defined $xfer_encoding; 2046 $has_content_type = 1; 2047 push @xh, 2048 "Content-Type: text/plain; charset=$author_encoding"; 2049 } 2050 } 2051 } 2052 $xfer_encoding = '8bit' if not defined $xfer_encoding; 2053 ($message, $xfer_encoding) = apply_transfer_encoding( 2054 $message, $xfer_encoding, $target_xfer_encoding); 2055 push @xh, "Content-Transfer-Encoding: $xfer_encoding"; 2056 unshift @xh, 'MIME-Version: 1.0' unless $has_mime_version; 2057 2058 $needs_confirm = ( 2059 $confirm eq "always" or 2060 ($confirm =~ /^(?:auto|cc)$/ && @cc) or 2061 ($confirm =~ /^(?:auto|compose)$/ && $compose && $message_num == 1)); 2062 $needs_confirm = "inform" if ($needs_confirm && $confirm_unconfigured && @cc); 2063 2064 @to = process_address_list(@to); 2065 @cc = process_address_list(@cc); 2066 2067 @to = (@initial_to, @to); 2068 @cc = (@initial_cc, @cc); 2069 2070 if ($message_num == 1) { 2071 if (defined $cover_cc and $cover_cc) { 2072 @initial_cc = @cc; 2073 } 2074 if (defined $cover_to and $cover_to) { 2075 @initial_to = @to; 2076 } 2077 } 2078} 2079 2080# Prepares the email, prompts the user, and sends it out 2081# Returns 0 if an edit was done and the function should be called again, or 1 2082# on the email being successfully sent out. 2083sub process_file { 2084 my ($t) = @_; 2085 2086 pre_process_file($t, $quiet); 2087 2088 my $message_was_sent = send_message(); 2089 if ($message_was_sent == -1) { 2090 do_edit($t); 2091 return 0; 2092 } 2093 2094 # set up for the next message 2095 if ($thread) { 2096 if ($message_was_sent && 2097 ($chain_reply_to || !defined $in_reply_to || length($in_reply_to) == 0 || 2098 $message_num == 1)) { 2099 $in_reply_to = $message_id; 2100 if (length $references > 0) { 2101 $references .= "\n $message_id"; 2102 } else { 2103 $references = "$message_id"; 2104 } 2105 } 2106 } elsif (!defined $initial_in_reply_to) { 2107 # --thread and --in-reply-to manage the "In-Reply-To" header and by 2108 # extension the "References" header. If these commands are not used, reset 2109 # the header values to their defaults. 2110 $in_reply_to = undef; 2111 $references = ''; 2112 } 2113 $message_id = undef; 2114 $num_sent++; 2115 if (defined $batch_size && $num_sent == $batch_size) { 2116 $num_sent = 0; 2117 $smtp->quit if defined $smtp; 2118 undef $smtp; 2119 undef $auth; 2120 sleep($relogin_delay) if defined $relogin_delay; 2121 } 2122 2123 return 1; 2124} 2125 2126sub initialize_modified_loop_vars { 2127 $in_reply_to = $initial_in_reply_to; 2128 $references = $initial_in_reply_to || ''; 2129 $message_num = 0; 2130} 2131 2132if ($validate) { 2133 # FIFOs can only be read once, exclude them from validation. 2134 my @real_files = (); 2135 foreach my $f (@files) { 2136 unless (-p $f) { 2137 push(@real_files, $f); 2138 } 2139 } 2140 2141 # Validate the SMTP server port, if provided. 2142 if (defined $smtp_server_port) { 2143 my $port = Git::port_num($smtp_server_port); 2144 if ($port) { 2145 $smtp_server_port = $port; 2146 } else { 2147 die sprintf(__("error: invalid SMTP port '%s'\n"), 2148 $smtp_server_port); 2149 } 2150 } 2151 2152 # Run the loop once again to avoid gaps in the counter due to FIFO 2153 # arguments provided by the user. 2154 my $num = 1; 2155 my $num_files = scalar @real_files; 2156 $ENV{GIT_SENDEMAIL_FILE_TOTAL} = "$num_files"; 2157 initialize_modified_loop_vars(); 2158 foreach my $r (@real_files) { 2159 $ENV{GIT_SENDEMAIL_FILE_COUNTER} = "$num"; 2160 pre_process_file($r, 1); 2161 validate_patch($r, $target_xfer_encoding); 2162 $num += 1; 2163 } 2164 delete $ENV{GIT_SENDEMAIL_FILE_COUNTER}; 2165 delete $ENV{GIT_SENDEMAIL_FILE_TOTAL}; 2166} 2167 2168initialize_modified_loop_vars(); 2169foreach my $t (@files) { 2170 while (!process_file($t)) { 2171 # user edited the file 2172 } 2173} 2174 2175# Execute a command and return its output lines as an array. Blank 2176# lines which do not appear at the end of the output are reported as 2177# errors. 2178sub execute_cmd { 2179 my ($prefix, $cmd, $file) = @_; 2180 my @lines = (); 2181 my $seen_blank_line = 0; 2182 open my $fh, "-|", "$cmd \Q$file\E" 2183 or die sprintf(__("(%s) Could not execute '%s'"), $prefix, $cmd); 2184 while (my $line = <$fh>) { 2185 die sprintf(__("(%s) Malformed output from '%s'"), $prefix, $cmd) 2186 if $seen_blank_line; 2187 if ($line =~ /^$/) { 2188 $seen_blank_line = $line =~ /^$/; 2189 next; 2190 } 2191 push @lines, $line; 2192 } 2193 close $fh 2194 or die sprintf(__("(%s) failed to close pipe to '%s'"), $prefix, $cmd); 2195 return @lines; 2196} 2197 2198# Process headers lines, unfolding multiline headers as defined by RFC 2199# 2822. 2200sub unfold_headers { 2201 my @headers; 2202 foreach(@_) { 2203 last if /^\s*$/; 2204 if (/^\s+\S/ and @headers) { 2205 chomp($headers[$#headers]); 2206 s/^\s+/ /; 2207 $headers[$#headers] .= $_; 2208 } else { 2209 push(@headers, $_); 2210 } 2211 } 2212 return @headers; 2213} 2214 2215# Invoke the provided CMD with FILE as an argument, which should 2216# output RFC 2822 email headers. Fold multiline headers and return the 2217# headers as an array. 2218sub invoke_header_cmd { 2219 my ($cmd, $file) = @_; 2220 my @lines = execute_cmd("header-cmd", $header_cmd, $file); 2221 return unfold_headers(@lines); 2222} 2223 2224# Execute a command (e.g. $to_cmd) to get a list of email addresses 2225# and return a results array 2226sub recipients_cmd { 2227 my ($prefix, $what, $cmd, $file, $quiet) = @_; 2228 my @lines = (); 2229 my @addresses = (); 2230 2231 @lines = execute_cmd($prefix, $cmd, $file); 2232 for my $address (@lines) { 2233 $address =~ s/^\s*//g; 2234 $address =~ s/\s*$//g; 2235 $address = sanitize_address($address); 2236 next if ($address eq $sender and $suppress_cc{'self'}); 2237 push @addresses, $address; 2238 printf(__("(%s) Adding %s: %s from: '%s'\n"), 2239 $prefix, $what, $address, $cmd) unless $quiet; 2240 } 2241 return @addresses; 2242} 2243 2244cleanup_compose_files(); 2245 2246sub cleanup_compose_files { 2247 unlink($compose_filename, $compose_filename . ".final") if $compose; 2248} 2249 2250$smtp->quit if $smtp; 2251 2252if ($imap_sent_folder && @imap_copy && !$dry_run) { 2253 my $imap_input = join("\n", @imap_copy); 2254 eval { 2255 print "\nStarting git imap-send...\n"; 2256 my ($fh, $ctx) = Git::command_input_pipe(['imap-send', '-f', $imap_sent_folder]); 2257 print $fh $imap_input; 2258 Git::command_close_pipe($fh, $ctx); 2259 1; 2260 } or do { 2261 warn "Warning: failed to send messages to IMAP folder $imap_sent_folder: $@"; 2262 }; 2263} 2264 2265sub apply_transfer_encoding { 2266 my $message = shift; 2267 my $from = shift; 2268 my $to = shift; 2269 2270 return ($message, $to) if ($from eq $to and $from ne '7bit'); 2271 2272 require MIME::QuotedPrint; 2273 require MIME::Base64; 2274 2275 $message = MIME::QuotedPrint::decode($message) 2276 if ($from eq 'quoted-printable'); 2277 $message = MIME::Base64::decode($message) 2278 if ($from eq 'base64'); 2279 2280 $to = ($message =~ /(?:.{999,}|\r)/) ? 'quoted-printable' : '8bit' 2281 if $to eq 'auto'; 2282 2283 die __("cannot send message as 7bit") 2284 if ($to eq '7bit' and $message =~ /[^[:ascii:]]/); 2285 return ($message, $to) 2286 if ($to eq '7bit' or $to eq '8bit'); 2287 return (MIME::QuotedPrint::encode($message, "\n", 0), $to) 2288 if ($to eq 'quoted-printable'); 2289 return (MIME::Base64::encode($message, "\n"), $to) 2290 if ($to eq 'base64'); 2291 die __("invalid transfer encoding"); 2292} 2293 2294sub unique_email_list { 2295 my %seen; 2296 my @emails; 2297 2298 foreach my $entry (@_) { 2299 my $clean = extract_valid_address_or_die($entry); 2300 $seen{$clean} ||= 0; 2301 next if $seen{$clean}++; 2302 push @emails, $entry; 2303 } 2304 return @emails; 2305} 2306 2307sub validate_patch { 2308 my ($fn, $xfer_encoding) = @_; 2309 2310 if ($repo) { 2311 my $hook_name = 'sendemail-validate'; 2312 my $hooks_path = $repo->command_oneline('rev-parse', '--git-path', 'hooks'); 2313 require File::Spec; 2314 my $validate_hook = File::Spec->catfile($hooks_path, $hook_name); 2315 my $hook_error; 2316 if (-x $validate_hook) { 2317 require Cwd; 2318 my $target = Cwd::abs_path($fn); 2319 # The hook needs a correct cwd and GIT_DIR. 2320 my $cwd_save = Cwd::getcwd(); 2321 chdir($repo->wc_path() or $repo->repo_path()) 2322 or die("chdir: $!"); 2323 local $ENV{"GIT_DIR"} = $repo->repo_path(); 2324 2325 my ($recipients_ref, $to, $date, $gitversion, $cc, $ccline, $header) = gen_header(); 2326 2327 require File::Temp; 2328 my ($header_filehandle, $header_filename) = File::Temp::tempfile( 2329 TEMPLATE => ".gitsendemail.header.XXXXXX", 2330 DIR => $repo->repo_path(), 2331 UNLINK => 1, 2332 ); 2333 print $header_filehandle $header; 2334 2335 my @cmd = ("git", "hook", "run", "--ignore-missing", 2336 $hook_name, "--"); 2337 my @cmd_msg = (@cmd, "<patch>", "<header>"); 2338 my @cmd_run = (@cmd, $target, $header_filename); 2339 $hook_error = system_or_msg(\@cmd_run, undef, "@cmd_msg"); 2340 chdir($cwd_save) or die("chdir: $!"); 2341 } 2342 if ($hook_error) { 2343 $hook_error = sprintf( 2344 __("fatal: %s: rejected by %s hook\n%s\nwarning: no patches were sent\n"), 2345 $fn, $hook_name, $hook_error); 2346 die $hook_error; 2347 } 2348 } 2349 2350 # Any long lines will be automatically fixed if we use a suitable transfer 2351 # encoding. 2352 unless ($xfer_encoding =~ /^(?:auto|quoted-printable|base64)$/) { 2353 open(my $fh, '<', $fn) 2354 or die sprintf(__("unable to open %s: %s\n"), $fn, $!); 2355 while (my $line = <$fh>) { 2356 if (length($line) > 998) { 2357 die sprintf(__("fatal: %s:%d is longer than 998 characters\n" . 2358 "warning: no patches were sent\n"), $fn, $.); 2359 } 2360 } 2361 } 2362 return; 2363} 2364 2365sub handle_backup { 2366 my ($last, $lastlen, $file, $known_suffix) = @_; 2367 my ($suffix, $skip); 2368 2369 $skip = 0; 2370 if (defined $last && 2371 ($lastlen < length($file)) && 2372 (substr($file, 0, $lastlen) eq $last) && 2373 ($suffix = substr($file, $lastlen)) !~ /^[a-z0-9]/i) { 2374 if (defined $known_suffix && $suffix eq $known_suffix) { 2375 printf(__("Skipping %s with backup suffix '%s'.\n"), $file, $known_suffix); 2376 $skip = 1; 2377 } else { 2378 # TRANSLATORS: please keep "[y|N]" as is. 2379 my $answer = ask(sprintf(__("Do you really want to send %s? [y|N]: "), $file), 2380 valid_re => qr/^(?:y|n)/i, 2381 default => 'n'); 2382 $skip = ($answer ne 'y'); 2383 if ($skip) { 2384 $known_suffix = $suffix; 2385 } 2386 } 2387 } 2388 return ($skip, $known_suffix); 2389} 2390 2391sub handle_backup_files { 2392 my @file = @_; 2393 my ($last, $lastlen, $known_suffix, $skip, @result); 2394 for my $file (@file) { 2395 ($skip, $known_suffix) = handle_backup($last, $lastlen, 2396 $file, $known_suffix); 2397 push @result, $file unless $skip; 2398 $last = $file; 2399 $lastlen = length($file); 2400 } 2401 return @result; 2402} 2403 2404sub file_has_nonascii { 2405 my $fn = shift; 2406 open(my $fh, '<', $fn) 2407 or die sprintf(__("unable to open %s: %s\n"), $fn, $!); 2408 while (my $line = <$fh>) { 2409 return 1 if $line =~ /[^[:ascii:]]/; 2410 } 2411 return 0; 2412} 2413 2414sub body_or_subject_has_nonascii { 2415 my $fn = shift; 2416 open(my $fh, '<', $fn) 2417 or die sprintf(__("unable to open %s: %s\n"), $fn, $!); 2418 while (my $line = <$fh>) { 2419 last if $line =~ /^$/; 2420 return 1 if $line =~ /^Subject.*[^[:ascii:]]/; 2421 } 2422 while (my $line = <$fh>) { 2423 return 1 if $line =~ /[^[:ascii:]]/; 2424 } 2425 return 0; 2426}