3 # Copyright 2002,2005 Greg Kroah-Hartman <greg@kroah.com>
4 # Copyright 2005 Ryan Anderson <ryan@michonline.com>
8 # Ported to support git "mbox" format files by Ryan Anderson <ryan@michonline.com>
10 # Sends a collection of emails to the given email addresses, disturbingly fast.
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.
21 use warnings
$ENV{GIT_PERL_FATAL_WARNINGS
} ?
qw(FATAL all) : ();
23 use Git
::LoadCPAN
::Error
qw(:try);
27 Getopt
::Long
::Configure qw
/ pass_through /;
31 git send-email [<options>] <file|directory>
32 git send-email [<options>] <format-patch options>
33 git send-email --dump-aliases
34 git send-email --translate-aliases
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-]xmailer * Add "X-Mailer:" header (default).
45 --[no-]annotate * Review each patch that will be sent in an editor.
46 --compose * Open an editor for introduction.
47 --compose-encoding <str> * Encoding to assume for introduction.
48 --8bit-encoding <str> * Encoding to assume 8bit mails if undeclared
49 --transfer-encoding <str> * Transfer encoding to use (quoted-printable, 8bit, base64)
50 --[no-]mailmap * Use mailmap file to map all email addresses to canonical
51 real names and email addresses.
54 --envelope-sender <str> * Email envelope sender.
55 --sendmail-cmd <str> * Command to run to send email.
56 --smtp-server <str:int> * Outgoing SMTP server to use. The port
57 is optional. Default 'localhost'.
58 --smtp-server-option <str> * Outgoing SMTP server option to use.
59 --smtp-server-port <int> * Outgoing SMTP server port.
60 --smtp-user <str> * Username for SMTP-AUTH.
61 --smtp-pass <str> * Password for SMTP-AUTH; not necessary.
62 --smtp-encryption <str> * tls or ssl; anything else disables.
63 --smtp-ssl * Deprecated. Use '--smtp-encryption ssl'.
64 --smtp-ssl-cert-path <str> * Path to ca-certificates (either directory or file).
65 Pass an empty string to disable certificate
67 --smtp-domain <str> * The domain name sent to HELO/EHLO handshake
68 --smtp-auth <str> * Space-separated list of allowed AUTH mechanisms, or
69 "none" to disable authentication.
70 This setting forces to use one of the listed mechanisms.
71 --no-smtp-auth Disable SMTP authentication. Shorthand for
73 --smtp-debug <0|1> * Disable, enable Net::SMTP debug.
75 --batch-size <int> * send max <int> message per connection.
76 --relogin-delay <int> * delay <int> seconds between two successive login.
77 This option can only be used with --batch-size
80 --identity <str> * Use the sendemail.<id> options.
81 --to-cmd <str> * Email To: via `<str> \$patch_path`.
82 --cc-cmd <str> * Email Cc: via `<str> \$patch_path`.
83 --header-cmd <str> * Add headers via `<str> \$patch_path`.
84 --no-header-cmd * Disable any header command in use.
85 --suppress-cc <str> * author, self, sob, cc, cccmd, body, bodycc, misc-by, all.
86 --[no-]cc-cover * Email Cc: addresses in the cover letter.
87 --[no-]to-cover * Email To: addresses in the cover letter.
88 --[no-]signed-off-by-cc * Send to Signed-off-by: addresses. Default on.
89 --[no-]suppress-from * Send to self. Default off.
90 --[no-]chain-reply-to * Chain In-Reply-To: fields. Default off.
91 --[no-]thread * Use In-Reply-To: field. Default on.
94 --confirm <str> * Confirm recipients before sending;
95 auto, cc, compose, always, or never.
96 --quiet * Output one line of info per email.
97 --dry-run * Don't actually send the emails.
98 --[no-]validate * Perform patch sanity checks. Default on.
99 --[no-]format-patch * understand any non optional arguments as
100 `git format-patch` ones.
101 --force * Send even if safety checks would prevent it.
104 --dump-aliases * Dump configured aliases and exit.
105 --translate-aliases * Translate aliases read from standard
106 input according to the configured email
107 alias file(s), outputting the result to
116 grep !$seen{$_}++, @_;
119 sub completion_helper
{
120 my ($original_opts) = @_;
121 my %not_for_completion = (
122 "git-completion-helper" => undef,
125 my @send_email_opts = ();
127 foreach my $key (keys %$original_opts) {
128 unless (exists $not_for_completion{$key}) {
129 my $negatable = ($key =~ s/!$//);
131 if ($key =~ /[:=][si]$/) {
132 $key =~ s/[:=][si]$//;
133 push (@send_email_opts, "--$_=") foreach (split (/\|/, $key));
135 push (@send_email_opts, "--$_") foreach (split (/\|/, $key));
137 push (@send_email_opts, "--no-$_") foreach (split (/\|/, $key));
143 my @format_patch_opts = split(/ /, Git
::command
('format-patch', '--git-completion-helper'));
144 my @opts = (@send_email_opts, @format_patch_opts);
145 @opts = uniq
(grep !/^$/, @opts);
146 # There's an implicit '\n' here already, no need to add an explicit one.
151 # most mail servers generate the Date: header, but not all...
152 sub format_2822_time
{
154 my @localtm = localtime($time);
155 my @gmttm = gmtime($time);
156 my $localmin = $localtm[1] + $localtm[2] * 60;
157 my $gmtmin = $gmttm[1] + $gmttm[2] * 60;
158 if ($localtm[0] != $gmttm[0]) {
159 die __
("local zone differs from GMT by a non-minute interval\n");
161 if ((($gmttm[6] + 1) % 7) == $localtm[6]) {
163 } elsif ((($gmttm[6] - 1) % 7) == $localtm[6]) {
165 } elsif ($gmttm[6] != $localtm[6]) {
166 die __
("local time offset greater than or equal to 24 hours\n");
168 my $offset = $localmin - $gmtmin;
169 my $offhour = $offset / 60;
170 my $offmin = abs($offset % 60);
171 if (abs($offhour) >= 24) {
172 die __
("local time offset greater than or equal to 24 hours\n");
175 return sprintf("%s, %2d %s %d %02d:%02d:%02d %s%02d%02d",
176 qw(Sun Mon Tue Wed Thu Fri Sat)[$localtm[6]],
178 qw(Jan Feb Mar Apr May Jun
179 Jul Aug Sep Oct Nov Dec)[$localtm[4]],
184 ($offset >= 0) ?
'+' : '-',
194 # Regexes for RFC 2047 productions.
195 my $re_token = qr/[^][()<>@,;:\\"\/?
.= \000-\037\177-\377]+/;
196 my $re_encoded_text = qr/[^? \000-\037\177-\377]+/;
197 my $re_encoded_word = qr/=\?($re_token)\?($re_token)\?($re_encoded_text)\?=/;
199 # Variables we fill in automatically, or via prompting:
200 my (@to,@cc,@xh,$envelope_sender,
201 $initial_in_reply_to,$reply_to,$initial_subject,@files,
202 $author,$sender,$smtp_authpass,$annotate,$compose,$time);
203 # Things we either get from config, *or* are overridden on the
205 my ($no_cc, $no_to, $no_bcc, $no_identity, $no_header_cmd);
206 my (@config_to, @getopt_to);
207 my (@config_cc, @getopt_cc);
208 my (@config_bcc, @getopt_bcc);
211 #$initial_in_reply_to = ''; #<20050203173208.GA23964@foobar.com>';
213 my $repo = eval { Git
->repository() };
214 my @repo = $repo ?
($repo) : ();
216 # Behavior modification variables
217 my ($quiet, $dry_run) = (0, 0);
219 my $compose_filename;
221 my $dump_aliases = 0;
222 my $translate_aliases = 0;
224 # Variables to prevent short format-patch options from being captured
225 # as abbreviated send-email options
228 # Handle interactive edition of files.
233 my ($args, $msg, $cmd_name) = @_;
235 my $signalled = $?
& 127;
236 my $exit_code = $?
>> 8;
237 return unless $signalled or $exit_code;
239 my @sprintf_args = ($cmd_name ?
$cmd_name : $args->[0], $exit_code);
241 # Quiet the 'redundant' warning category, except we
242 # need to support down to Perl 5.8.1, so we can't do a
243 # "no warnings 'redundant'", since that category was
244 # introduced in perl 5.22, and asking for it will die
247 return sprintf($msg, @sprintf_args);
249 return sprintf(__
("fatal: command '%s' died with exit code %d"),
254 my $msg = system_or_msg
(@_);
259 if (!defined($editor)) {
260 $editor = Git
::command_oneline
('var', 'GIT_EDITOR');
262 my $die_msg = __
("the editor exited uncleanly, aborting everything");
263 if (defined($multiedit) && !$multiedit) {
264 system_or_die
(['sh', '-c', $editor.' "$@"', $editor, $_], $die_msg) for @_;
266 system_or_die
(['sh', '-c', $editor.' "$@"', $editor, @_], $die_msg);
270 # Variables with corresponding config settings
271 my ($suppress_from, $signed_off_by_cc);
272 my ($cover_cc, $cover_to);
273 my ($to_cmd, $cc_cmd, $header_cmd);
274 my ($smtp_server, $smtp_server_port, @smtp_server_options);
275 my ($smtp_authuser, $smtp_encryption, $smtp_ssl_cert_path);
276 my ($batch_size, $relogin_delay);
277 my ($identity, $aliasfiletype, @alias_files, $smtp_domain, $smtp_auth);
280 my ($auto_8bit_encoding);
281 my ($compose_encoding);
283 my ($mailmap_file, $mailmap_blob);
284 # Variables with corresponding config settings & hardcoded defaults
285 my ($debug_net_smtp) = 0; # Net::SMTP, see send_message()
287 my $chain_reply_to = 0;
291 my $target_xfer_encoding = 'auto';
292 my $forbid_sendmail_variables = 1;
294 my %config_bool_settings = (
295 "thread" => \
$thread,
296 "chainreplyto" => \
$chain_reply_to,
297 "suppressfrom" => \
$suppress_from,
298 "signedoffbycc" => \
$signed_off_by_cc,
299 "cccover" => \
$cover_cc,
300 "tocover" => \
$cover_to,
301 "signedoffcc" => \
$signed_off_by_cc,
302 "validate" => \
$validate,
303 "multiedit" => \
$multiedit,
304 "annotate" => \
$annotate,
305 "xmailer" => \
$use_xmailer,
306 "forbidsendmailvariables" => \
$forbid_sendmail_variables,
307 "mailmap" => \
$mailmap,
310 my %config_settings = (
311 "smtpencryption" => \
$smtp_encryption,
312 "smtpserver" => \
$smtp_server,
313 "smtpserverport" => \
$smtp_server_port,
314 "smtpserveroption" => \
@smtp_server_options,
315 "smtpuser" => \
$smtp_authuser,
316 "smtppass" => \
$smtp_authpass,
317 "smtpdomain" => \
$smtp_domain,
318 "smtpauth" => \
$smtp_auth,
319 "smtpbatchsize" => \
$batch_size,
320 "smtprelogindelay" => \
$relogin_delay,
325 "headercmd" => \
$header_cmd,
326 "aliasfiletype" => \
$aliasfiletype,
327 "bcc" => \
@config_bcc,
328 "suppresscc" => \
@suppress_cc,
329 "envelopesender" => \
$envelope_sender,
330 "confirm" => \
$confirm,
332 "assume8bitencoding" => \
$auto_8bit_encoding,
333 "composeencoding" => \
$compose_encoding,
334 "transferencoding" => \
$target_xfer_encoding,
335 "sendmailcmd" => \
$sendmail_cmd,
338 my %config_path_settings = (
339 "aliasesfile" => \
@alias_files,
340 "smtpsslcertpath" => \
$smtp_ssl_cert_path,
341 "mailmap.file" => \
$mailmap_file,
342 "mailmap.blob" => \
$mailmap_blob,
345 # Handle Uncouth Termination
348 require Term
::ANSIColor
;
349 print Term
::ANSIColor
::color
("reset"), "\n";
351 # SMTP password masked
354 # tmp files from --compose
355 if (defined $compose_filename) {
356 if (-e
$compose_filename) {
357 printf __
("'%s' contains an intermediate version ".
358 "of the email you were composing.\n"),
361 if (-e
($compose_filename . ".final")) {
362 printf __
("'%s.final' contains the composed email.\n"),
370 $SIG{TERM
} = \
&signal_handler
;
371 $SIG{INT
} = \
&signal_handler
;
373 # Read our sendemail.* config
375 my ($known_keys, $configured, $prefix) = @_;
377 foreach my $setting (keys %config_bool_settings) {
378 my $target = $config_bool_settings{$setting};
379 my $key = "$prefix.$setting";
380 next unless exists $known_keys->{$key};
381 my $v = (@
{$known_keys->{$key}} == 1 &&
382 (defined $known_keys->{$key}->[0] &&
383 $known_keys->{$key}->[0] =~ /^(?:true|false)$/s))
384 ?
$known_keys->{$key}->[0] eq 'true'
385 : Git
::config_bool
(@repo, $key);
386 next unless defined $v;
387 next if $configured->{$setting}++;
391 foreach my $setting (keys %config_path_settings) {
392 my $target = $config_path_settings{$setting};
393 my $key = "$prefix.$setting";
394 next unless exists $known_keys->{$key};
395 if (ref($target) eq "ARRAY") {
396 my @values = Git
::config_path
(@repo, $key);
398 next if $configured->{$setting}++;
402 my $v = Git
::config_path
(@repo, "$prefix.$setting");
403 next unless defined $v;
404 next if $configured->{$setting}++;
409 foreach my $setting (keys %config_settings) {
410 my $target = $config_settings{$setting};
411 my $key = "$prefix.$setting";
412 next unless exists $known_keys->{$key};
413 if (ref($target) eq "ARRAY") {
414 my @values = @
{$known_keys->{$key}};
415 @values = grep { defined } @values;
416 next if $configured->{$setting}++;
420 my $v = $known_keys->{$key}->[-1];
421 next unless defined $v;
422 next if $configured->{$setting}++;
432 my $ret = Git
::command
(
439 # We must always return ($k, $v) here, since
440 # empty config values will be just "key\0",
441 # not "key\nvalue\0".
442 my ($k, $v) = split /\n/, $_, 2;
447 # If we have no keys we're OK, otherwise re-throw
448 die $@
if $@
->value != 1;
453 # Save ourselves a lot of work of shelling out to 'git config' (it
454 # parses 'bool' etc.) by only doing so for config keys that exist.
455 my %known_config_keys;
457 my @kv = config_regexp
("^sende?mail[.]");
458 while (my ($k, $v) = splice @kv, 0, 2) {
459 push @
{$known_config_keys{$k}} => $v;
463 # sendemail.identity yields to --identity. We must parse this
464 # special-case first before the rest of the config is read.
466 my $key = "sendemail.identity";
467 $identity = Git
::config
(@repo, $key) if exists $known_config_keys{$key};
469 my %identity_options = (
470 "identity=s" => \
$identity,
471 "no-identity" => \
$no_identity,
473 my $rc = GetOptions
(%identity_options);
475 undef $identity if $no_identity;
477 # Now we know enough to read the config
480 read_config
(\
%known_config_keys, \
%configured, "sendemail.$identity") if defined $identity;
481 read_config
(\
%known_config_keys, \
%configured, "sendemail");
484 # Begin by accumulating all the variables (defined above), that we will end up
485 # needing, first, from the command line:
488 my $git_completion_helper;
489 my %dump_aliases_options = (
491 "dump-aliases" => \
$dump_aliases,
492 "translate-aliases" => \
$translate_aliases,
494 $rc = GetOptions
(%dump_aliases_options);
496 die __
("--dump-aliases incompatible with other options\n")
497 if !$help and ($dump_aliases or $translate_aliases) and @ARGV;
498 die __
("--dump-aliases and --translate-aliases are mutually exclusive\n")
499 if !$help and $dump_aliases and $translate_aliases;
501 "sender|from=s" => \
$sender,
502 "in-reply-to=s" => \
$initial_in_reply_to,
503 "reply-to=s" => \
$reply_to,
504 "subject=s" => \
$initial_subject,
505 "to=s" => \
@getopt_to,
506 "to-cmd=s" => \
$to_cmd,
508 "cc=s" => \
@getopt_cc,
510 "bcc=s" => \
@getopt_bcc,
511 "no-bcc" => \
$no_bcc,
512 "chain-reply-to!" => \
$chain_reply_to,
513 "sendmail-cmd=s" => \
$sendmail_cmd,
514 "smtp-server=s" => \
$smtp_server,
515 "smtp-server-option=s" => \
@smtp_server_options,
516 "smtp-server-port=s" => \
$smtp_server_port,
517 "smtp-user=s" => \
$smtp_authuser,
518 "smtp-pass:s" => \
$smtp_authpass,
519 "smtp-ssl" => sub { $smtp_encryption = 'ssl' },
520 "smtp-encryption=s" => \
$smtp_encryption,
521 "smtp-ssl-cert-path=s" => \
$smtp_ssl_cert_path,
522 "smtp-debug:i" => \
$debug_net_smtp,
523 "smtp-domain:s" => \
$smtp_domain,
524 "smtp-auth=s" => \
$smtp_auth,
525 "no-smtp-auth" => sub {$smtp_auth = 'none'},
526 "annotate!" => \
$annotate,
527 "compose" => \
$compose,
529 "cc-cmd=s" => \
$cc_cmd,
530 "header-cmd=s" => \
$header_cmd,
531 "no-header-cmd" => \
$no_header_cmd,
532 "suppress-from!" => \
$suppress_from,
533 "suppress-cc=s" => \
@suppress_cc,
534 "signed-off-cc|signed-off-by-cc!" => \
$signed_off_by_cc,
535 "cc-cover!" => \
$cover_cc,
536 "to-cover!" => \
$cover_to,
537 "confirm=s" => \
$confirm,
538 "dry-run" => \
$dry_run,
539 "envelope-sender=s" => \
$envelope_sender,
540 "thread!" => \
$thread,
541 "validate!" => \
$validate,
542 "transfer-encoding=s" => \
$target_xfer_encoding,
543 "mailmap!" => \
$mailmap,
544 "use-mailmap!" => \
$mailmap,
545 "format-patch!" => \
$format_patch,
546 "8bit-encoding=s" => \
$auto_8bit_encoding,
547 "compose-encoding=s" => \
$compose_encoding,
549 "xmailer!" => \
$use_xmailer,
550 "batch-size=i" => \
$batch_size,
551 "relogin-delay=i" => \
$relogin_delay,
552 "git-completion-helper" => \
$git_completion_helper,
553 "v=s" => \
$reroll_count,
555 $rc = GetOptions
(%options);
557 # Munge any "either config or getopt, not both" variables
558 my @initial_to = @getopt_to ?
@getopt_to : ($no_to ?
() : @config_to);
559 my @initial_cc = @getopt_cc ?
@getopt_cc : ($no_cc ?
() : @config_cc);
560 my @initial_bcc = @getopt_bcc ?
@getopt_bcc : ($no_bcc ?
() : @config_bcc);
563 my %all_options = (%options, %dump_aliases_options, %identity_options);
564 completion_helper
(\
%all_options) if $git_completion_helper;
569 if ($forbid_sendmail_variables && grep { /^sendmail/s } keys %known_config_keys) {
570 die __
("fatal: found configuration options for 'sendmail'\n" .
571 "git-send-email is configured with the sendemail.* options - note the 'e'.\n" .
572 "Set sendemail.forbidSendmailVariables to false to disable this check.\n");
575 die __
("Cannot run git format-patch from outside a repository\n")
576 if $format_patch and not $repo;
578 die __
("`batch-size` and `relogin` must be specified together " .
579 "(via command-line or configuration option)\n")
580 if defined $relogin_delay and not defined $batch_size;
582 # 'default' encryption is none -- this only prevents a warning
583 $smtp_encryption = '' unless (defined $smtp_encryption);
585 # Set CC suppressions
588 foreach my $entry (@suppress_cc) {
589 # Please update $__git_send_email_suppresscc_options
590 # in git-completion.bash when you add new options.
591 die sprintf(__
("Unknown --suppress-cc field: '%s'\n"), $entry)
592 unless $entry =~ /^(?:all|cccmd|cc|author|self|sob|body|bodycc|misc-by)$/;
593 $suppress_cc{$entry} = 1;
597 if ($suppress_cc{'all'}) {
598 foreach my $entry (qw
(cccmd cc author self sob body bodycc misc
-by
)) {
599 $suppress_cc{$entry} = 1;
601 delete $suppress_cc{'all'};
604 # If explicit old-style ones are specified, they trump --suppress-cc.
605 $suppress_cc{'self'} = $suppress_from if defined $suppress_from;
606 $suppress_cc{'sob'} = !$signed_off_by_cc if defined $signed_off_by_cc;
608 if ($suppress_cc{'body'}) {
609 foreach my $entry (qw
(sob bodycc misc
-by
)) {
610 $suppress_cc{$entry} = 1;
612 delete $suppress_cc{'body'};
615 # Set confirm's default value
616 my $confirm_unconfigured = !defined $confirm;
617 if ($confirm_unconfigured) {
618 $confirm = scalar %suppress_cc ?
'compose' : 'auto';
620 # Please update $__git_send_email_confirm_options in
621 # git-completion.bash when you add new options.
622 die sprintf(__
("Unknown --confirm setting: '%s'\n"), $confirm)
623 unless $confirm =~ /^(?:auto|cc|compose|always|never)/;
625 # Debugging, print out the suppressions.
627 print "suppressions:\n";
628 foreach my $entry (keys %suppress_cc) {
629 printf " %-5s -> $suppress_cc{$entry}\n", $entry;
633 my ($repoauthor, $repocommitter);
636 my ($author, $committer);
639 return $cache{$what} if exists $cache{$what};
640 ($cache{$what}) = Git
::ident_person
(@repo, $what);
641 return $cache{$what};
643 $repoauthor = sub { $common->('author') };
644 $repocommitter = sub { $common->('committer') };
647 sub parse_address_line
{
648 require Git
::LoadCPAN
::Mail
::Address
;
649 return map { $_->format } Mail
::Address
->parse($_[0]);
653 require Text
::ParseWords
;
654 return Text
::ParseWords
::quotewords
('\s*,\s*', 1, @_);
659 sub parse_sendmail_alias
{
662 printf STDERR __
("warning: sendmail alias with quotes is not supported: %s\n"), $_;
663 } elsif (/:include:/) {
664 printf STDERR __
("warning: `:include:` not supported: %s\n"), $_;
666 printf STDERR __
("warning: `/file` or `|pipe` redirection not supported: %s\n"), $_;
667 } elsif (/^(\S+?)\s*:\s*(.+)$/) {
668 my ($alias, $addr) = ($1, $2);
669 $aliases{$alias} = [ split_addrs
($addr) ];
671 printf STDERR __
("warning: sendmail line is not recognized: %s\n"), $_;
675 sub parse_sendmail_aliases
{
680 next if /^\s*$/ || /^\s*#/;
681 $s .= $_, next if $s =~ s/\\$// || s/^\s+//;
682 parse_sendmail_alias
($s) if $s;
685 $s =~ s/\\$//; # silently tolerate stray '\' on last line
686 parse_sendmail_alias
($s) if $s;
690 # multiline formats can be supported in the future
691 mutt
=> sub { my $fh = shift; while (<$fh>) {
692 if (/^\s*alias\s+(?:-group\s+\S+\s+)*(\S+)\s+(.*)$/) {
693 my ($alias, $addr) = ($1, $2);
694 $addr =~ s/#.*$//; # mutt allows # comments
695 # commas delimit multiple addresses
696 my @addr = split_addrs
($addr);
698 # quotes may be escaped in the file,
699 # unescape them so we do not double-escape them later.
700 s/\\"/"/g foreach @addr;
701 $aliases{$alias} = \
@addr
703 mailrc
=> sub { my $fh = shift; while (<$fh>) {
704 if (/^alias\s+(\S+)\s+(.*?)\s*$/) {
705 require Text
::ParseWords
;
706 # spaces delimit multiple addresses
707 $aliases{$1} = [ Text
::ParseWords
::quotewords
('\s+', 0, $2) ];
709 pine
=> sub { my $fh = shift; my $f='\t[^\t]*';
710 for (my $x = ''; defined($x); $x = $_) {
712 $x .= $1 while(defined($_ = <$fh>) && /^ +(.*)$/);
713 $x =~ /^(\S+)$f\t\(?([^\t]+?)\)?(:?$f){0,2}$/ or next;
714 $aliases{$1} = [ split_addrs
($2) ];
716 elm
=> sub { my $fh = shift;
718 if (/^(\S+)\s+=\s+[^=]+=\s(\S+)/) {
719 my ($alias, $addr) = ($1, $2);
720 $aliases{$alias} = [ split_addrs
($addr) ];
723 sendmail
=> \
&parse_sendmail_aliases
,
724 gnus
=> sub { my $fh = shift; while (<$fh>) {
725 if (/\(define-mail-alias\s+"(\S+?)"\s+"(\S+?)"\)/) {
726 $aliases{$1} = [ $2 ];
728 # Please update _git_config() in git-completion.bash when you
732 if (@alias_files and $aliasfiletype and defined $parse_alias{$aliasfiletype}) {
733 foreach my $file (@alias_files) {
734 open my $fh, '<', $file or die "opening $file: $!\n";
735 $parse_alias{$aliasfiletype}->($fh);
741 print "$_\n" for (sort keys %aliases);
745 if ($translate_aliases) {
747 my @addr_list = parse_address_line
($_);
748 @addr_list = expand_aliases
(@addr_list);
749 @addr_list = sanitize_address_list
(@addr_list);
750 print "$_\n" for @addr_list;
755 # is_format_patch_arg($f) returns 0 if $f names a patch, or 1 if
756 # $f is a revision list specification to be passed to format-patch.
757 sub is_format_patch_arg
{
761 $repo->command('rev-parse', '--verify', '--quiet', $f);
762 if (defined($format_patch)) {
763 return $format_patch;
765 die sprintf(__
(<<EOF), $f, $f);
766 File '%s' exists but it could also be the range of commits
767 to produce patches for. Please disambiguate by...
769 * Saying "./%s" if you mean a file; or
770 * Giving --format-patch option if you mean a range.
772 } catch Git
::Error
::Command with
{
773 # Not a valid revision. Treat it as a filename.
778 # Now that all the defaults are set, process the rest of the command line
779 # arguments and collect up the files that need to be processed.
781 while (defined(my $f = shift @ARGV)) {
783 push @rev_list_opts, "--", @ARGV;
785 } elsif (-d
$f and !is_format_patch_arg
($f)) {
787 or die sprintf(__
("Failed to opendir %s: %s"), $f, $!);
790 push @files, grep { -f
$_ } map { File
::Spec
->catfile($f, $_) }
793 } elsif ((-f
$f or -p
$f) and !is_format_patch_arg
($f)) {
796 push @rev_list_opts, $f;
800 if (@rev_list_opts) {
801 die __
("Cannot run git format-patch from outside a repository\n")
804 push @files, $repo->command('format-patch', '-o', File
::Temp
::tempdir
(CLEANUP
=> 1),
805 defined $reroll_count ?
('-v', $reroll_count) : (),
809 if (defined $sender) {
810 $sender =~ s/^\s+|\s+$//g;
811 ($sender) = expand_aliases
($sender);
813 $sender = $repoauthor->() || $repocommitter->() || '';
816 # $sender could be an already sanitized address
817 # (e.g. sendemail.from could be manually sanitized by user).
818 # But it's a no-op to run sanitize_address on an already sanitized address.
819 $sender = sanitize_address
($sender);
821 $time = time - scalar $#files;
823 @files = handle_backup_files
(@files);
827 print $_,"\n" for (@files);
830 print STDERR __
("\nNo patch files specified!\n\n");
834 sub get_patch_subject
{
836 open (my $fh, '<', $fn);
837 while (my $line = <$fh>) {
838 next unless ($line =~ /^Subject: (.*)$/);
843 die sprintf(__
("No subject line in %s?"), $fn);
847 # Note that this does not need to be secure, but we will make a small
848 # effort to have it be unique
850 $compose_filename = ($repo ?
851 File
::Temp
::tempfile
(".gitsendemail.msg.XXXXXX", DIR
=> $repo->repo_path()) :
852 File
::Temp
::tempfile
(".gitsendemail.msg.XXXXXX", DIR
=> "."))[1];
853 open my $c, ">", $compose_filename
854 or die sprintf(__
("Failed to open for writing %s: %s"), $compose_filename, $!);
857 my $tpl_sender = $sender || $repoauthor->() || $repocommitter->() || '';
858 my $tpl_subject = $initial_subject || '';
859 my $tpl_in_reply_to = $initial_in_reply_to || '';
860 my $tpl_reply_to = $reply_to || '';
861 my $tpl_to = join(',', @initial_to);
862 my $tpl_cc = join(',', @initial_cc);
863 my $tpl_bcc = join(', ', @initial_bcc);
865 print $c <<EOT1, Git::prefix_lines("GIT: ", __(<<EOT2)), <<EOT3;
866 From $tpl_sender # This line is ignored.
868 Lines beginning in "GIT:" will be removed.
869 Consider including an overall diffstat or table of contents
870 for the patch you are writing.
872 Clear the body content if you don't wish to send a summary.
878 Reply-To: $tpl_reply_to
879 Subject: $tpl_subject
880 In-Reply-To: $tpl_in_reply_to
884 print $c get_patch_subject($f);
889 do_edit($compose_filename, @files);
891 do_edit($compose_filename);
894 open my $c2, ">", $compose_filename . ".final"
895 or die sprintf(__("Failed to open %s.final: %s"), $compose_filename, $!);
897 open $c, "<", $compose_filename
898 or die sprintf(__("Failed to open %s: %s"), $compose_filename, $!);
900 my $need_8bit_cte = file_has_nonascii($compose_filename);
902 my $summary_empty = 1;
903 if (!defined $compose_encoding) {
904 $compose_encoding = "UTF-8";
909 $summary_empty = 0 unless (/^\n$/);
912 if ($need_8bit_cte) {
913 print $c2 "MIME-Version: 1.0\n",
914 "Content-Type: text/plain; ",
915 "charset=$compose_encoding\n",
916 "Content-Transfer-Encoding: 8bit\n";
918 } elsif (/^MIME-Version:/i) {
920 } elsif (/^Subject:\s*(.+)\s*$/i) {
921 $initial_subject = $1;
922 my $subject = $initial_subject;
924 quote_subject($subject, $compose_encoding) .
926 } elsif (/^In-Reply-To:\s*(.+)\s*$/i) {
927 $initial_in_reply_to = $1;
929 } elsif (/^Reply-To:\s*(.+)\s*$/i) {
931 } elsif (/^From:\s*(.+)\s*$/i) {
934 } elsif (/^To:\s*(.+)\s*$/i) {
935 @initial_to = parse_address_line($1);
937 } elsif (/^Cc:\s*(.+)\s*$/i) {
938 @initial_cc = parse_address_line($1);
941 @initial_bcc = parse_address_line($1);
949 if ($summary_empty) {
950 print __("Summary email is empty, skipping it\n");
953 } elsif ($annotate) {
958 # Only instantiate one $term per program run, since some
959 # Term::ReadLine providers refuse to create a second instance.
962 require Term::ReadLine;
963 if (!defined $term) {
964 $term = $ENV{"GIT_SEND_EMAIL_NOTTY"}
965 ? Term::ReadLine->new('git-send-email', \*STDIN, \*STDOUT)
966 : Term::ReadLine->new('git-send-email');
973 my ($prompt, %arg) = @_;
974 my $valid_re = $arg{valid_re};
975 my $default = $arg{default};
976 my $confirm_only = $arg{confirm_only};
980 return defined $default ? $default : undef
981 unless defined $term->IN and defined fileno($term->IN) and
982 defined $term->OUT and defined fileno($term->OUT);
984 $resp = $term->readline($prompt);
985 if (!defined $resp) { # EOF
987 return defined $default ? $default : undef;
989 if ($resp eq '' and defined $default) {
992 if (!defined $valid_re or $resp =~ /$valid_re/) {
996 my $yesno = $term->readline(
997 # TRANSLATORS: please keep [y/N] as is.
998 sprintf(__("Are you sure you want to use <%s> [y/N]? "), $resp));
999 if (defined $yesno && $yesno =~ /y/i) {
1007 my %broken_encoding;
1009 sub file_declares_8bit_cte {
1011 open (my $fh, '<', $fn);
1012 while (my $line = <$fh>) {
1013 last if ($line =~ /^$/);
1014 return 1 if ($line =~ /^Content-Transfer-Encoding: .*8bit.*$/);
1020 foreach my $f (@files) {
1021 next unless (body_or_subject_has_nonascii($f)
1022 && !file_declares_8bit_cte($f));
1023 $broken_encoding{$f} = 1;
1026 if (!defined $auto_8bit_encoding && scalar %broken_encoding) {
1027 print __("The following files are 8bit, but do not declare " .
1028 "a Content-Transfer-Encoding.\n");
1029 foreach my $f (sort keys %broken_encoding) {
1032 $auto_8bit_encoding = ask(__("Which 8bit encoding should I declare [UTF-8]? "),
1033 valid_re => qr/.{4}/, confirm_only => 1,
1034 default => "UTF-8");
1038 for my $f (@files) {
1039 if (get_patch_subject($f) =~ /\Q*** SUBJECT HERE ***\E/) {
1040 die sprintf(__("Refusing to send because the patch\n\t%s\n"
1041 . "has the template subject '*** SUBJECT HERE ***'. "
1042 . "Pass --force if you really want to send.\n"), $f);
1047 my $to_whom = __("To whom should the emails be sent (if anyone)?");
1049 if (!@initial_to && !defined $to_cmd) {
1050 my $to = ask("$to_whom ",
1052 valid_re => qr/\@.*\./, confirm_only => 1);
1053 push @initial_to, parse_address_line($to) if defined $to; # sanitized/validated later
1057 sub expand_aliases {
1058 return map { expand_one_alias($_) } @_;
1061 my %EXPANDED_ALIASES;
1062 sub expand_one_alias {
1064 if ($EXPANDED_ALIASES{$alias}) {
1065 die sprintf(__("fatal: alias '%s' expands to itself\n"), $alias);
1067 local $EXPANDED_ALIASES{$alias} = 1;
1068 return $aliases{$alias} ? expand_aliases(@{$aliases{$alias}}) : $alias;
1071 @initial_to = process_address_list(@initial_to);
1072 @initial_cc = process_address_list(@initial_cc);
1073 @initial_bcc = process_address_list(@initial_bcc);
1075 if ($thread && !defined $initial_in_reply_to && $prompting) {
1076 $initial_in_reply_to = ask(
1077 __("Message-ID to be used as In-Reply-To for the first email (if any)? "),
1079 valid_re => qr/\@.*\./, confirm_only => 1);
1081 if (defined $initial_in_reply_to) {
1082 $initial_in_reply_to =~ s/^\s*<?//;
1083 $initial_in_reply_to =~ s/>?\s*$//;
1084 $initial_in_reply_to = "<$initial_in_reply_to>" if $initial_in_reply_to ne '';
1087 if (defined $reply_to) {
1088 $reply_to =~ s/^\s+|\s+$//g;
1089 ($reply_to) = expand_aliases($reply_to);
1090 $reply_to = sanitize_address($reply_to);
1093 if (!defined $sendmail_cmd && !defined $smtp_server) {
1094 my @sendmail_paths = qw( /usr/sbin/sendmail /usr/lib/sendmail );
1095 push @sendmail_paths, map {"$_/sendmail"} split /:/, $ENV{PATH
};
1096 foreach (@sendmail_paths) {
1103 if (!defined $sendmail_cmd) {
1104 $smtp_server = 'localhost'; # could be 127.0.0.1, too... *shrug*
1108 if ($compose && $compose > 0) {
1109 @files = ($compose_filename . ".final", @files);
1112 # Variables we set as part of the loop over files
1113 our ($message_id, %mail, $subject, $in_reply_to, $references, $message,
1114 $needs_confirm, $message_num, $ask_default);
1116 sub mailmap_address_list
{
1117 return @_ unless @_ and $mailmap;
1119 push(@options, "--mailmap-file=$mailmap_file") if $mailmap_file;
1120 push(@options, "--mailmap-blob=$mailmap_blob") if $mailmap_blob;
1121 my @addr_list = Git
::command
('check-mailmap', @options, @_);
1122 s/^<(.*)>$/$1/ for @addr_list;
1126 sub extract_valid_address
{
1127 my $address = shift;
1128 my $local_part_regexp = qr/[^<>"\s@]+/;
1129 my $domain_regexp = qr/[^.<>"\s@]+(?:\.[^.<>"\s@]+)+/;
1131 # check for a local address:
1132 return $address if ($address =~ /^($local_part_regexp)$/);
1134 $address =~ s/^\s*<(.*)>\s*$/$1/;
1135 my $have_email_valid = eval { require Email
::Valid
; 1 };
1136 if ($have_email_valid) {
1137 return scalar Email
::Valid
->address($address);
1140 # less robust/correct than the monster regexp in Email::Valid,
1141 # but still does a 99% job, and one less dependency
1142 return $1 if $address =~ /($local_part_regexp\@$domain_regexp)/;
1146 sub extract_valid_address_or_die
{
1147 my $address = shift;
1148 my $valid_address = extract_valid_address
($address);
1149 die sprintf(__
("error: unable to extract a valid address from: %s\n"), $address)
1151 return $valid_address;
1154 sub validate_address
{
1155 my $address = shift;
1156 while (!extract_valid_address
($address)) {
1157 printf STDERR __
("error: unable to extract a valid address from: %s\n"), $address;
1158 # TRANSLATORS: Make sure to include [q] [d] [e] in your
1159 # translation. The program will only accept English input
1161 $_ = ask
(__
("What to do with this address? ([q]uit|[d]rop|[e]dit): "),
1162 valid_re
=> qr/^(?:quit|q|drop|d|edit|e)/i,
1167 cleanup_compose_files
();
1170 $address = ask
("$to_whom ",
1172 valid_re
=> qr/\@.*\./, confirm_only
=> 1);
1177 sub validate_address_list
{
1178 return (grep { defined $_ }
1179 map { validate_address
($_) } @_);
1182 # Usually don't need to change anything below here.
1184 # we make a "fake" message id by taking the current number
1185 # of seconds since the beginning of Unix time and tacking on
1186 # a random number to the end, in case we are called quicker than
1187 # 1 second since the last time we were called.
1189 # We'll setup a template for the message id, using the "from" address:
1191 my ($message_id_stamp, $message_id_serial);
1192 sub make_message_id
{
1194 if (!defined $message_id_stamp) {
1196 $message_id_stamp = POSIX
::strftime
("%Y%m%d%H%M%S.$$", gmtime(time));
1197 $message_id_serial = 0;
1199 $message_id_serial++;
1200 $uniq = "$message_id_stamp-$message_id_serial";
1203 for ($sender, $repocommitter->(), $repoauthor->()) {
1204 $du_part = extract_valid_address
(sanitize_address
($_));
1205 last if (defined $du_part and $du_part ne '');
1207 if (not defined $du_part or $du_part eq '') {
1208 require Sys
::Hostname
;
1209 $du_part = 'user@' . Sys
::Hostname
::hostname
();
1211 my $message_id_template = "<%s-%s>";
1212 $message_id = sprintf($message_id_template, $uniq, $du_part);
1213 #print "new message id = $message_id\n"; # Was useful for debugging
1216 sub unquote_rfc2047
{
1219 my $sep = qr/[ \t]+/;
1220 s
{$re_encoded_word(?
:$sep$re_encoded_word)*}{
1221 my @words = split $sep, $&;
1223 m/$re_encoded_word/;
1227 if ($encoding eq 'q' || $encoding eq 'Q') {
1230 s/=([0-9A-F]{2})/chr(hex($1))/egi;
1232 # other encodings not supported yet
1237 return wantarray ?
($_, $charset) : $_;
1242 my $encoding = shift || 'UTF-8';
1243 s/([^-a-zA-Z0-9!*+\/])/sprintf
("=%02X", ord($1))/eg
;
1244 s/(.*)/=\?$encoding\?q\?$1\?=/;
1248 sub is_rfc2047_quoted
{
1251 $s =~ m/^(?:"[[:ascii:]]*"|$re_encoded_word)$/o;
1254 sub subject_needs_rfc2047_quoting
{
1257 return ($s =~ /[^[:ascii:]]/) || ($s =~ /=\?/);
1261 local $subject = shift;
1262 my $encoding = shift || 'UTF-8';
1264 if (subject_needs_rfc2047_quoting
($subject)) {
1265 return quote_rfc2047
($subject, $encoding);
1270 # use the simplest quoting being able to handle the recipient
1271 sub sanitize_address
{
1272 my ($recipient) = @_;
1274 # remove garbage after email address
1275 $recipient =~ s/(.*>).*$/$1/;
1277 my ($recipient_name, $recipient_addr) = ($recipient =~ /^(.*?)\s*(<.*)/);
1279 if (not $recipient_name) {
1283 # if recipient_name is already quoted, do nothing
1284 if (is_rfc2047_quoted
($recipient_name)) {
1288 # remove non-escaped quotes
1289 $recipient_name =~ s/(^|[^\\])"/$1/g;
1291 # rfc2047 is needed if a non-ascii char is included
1292 if ($recipient_name =~ /[^[:ascii:]]/) {
1293 $recipient_name = quote_rfc2047
($recipient_name);
1296 # double quotes are needed if specials or CTLs are included
1297 elsif ($recipient_name =~ /[][()<>@,;:\\".\000-\037\177]/) {
1298 $recipient_name =~ s/([\\\r])/\\$1/g;
1299 $recipient_name = qq["$recipient_name"];
1302 return "$recipient_name $recipient_addr";
1306 sub strip_garbage_one_address
{
1309 if ($addr =~ /^(("[^"]*"|[^"<]*)? *<[^>]*>).*/) {
1310 # "Foo Bar" <foobar@example.com> [possibly garbage here]
1311 # Foo Bar <foobar@example.com> [possibly garbage here]
1314 if ($addr =~ /^(<[^>]*>).*/) {
1315 # <foo@example.com> [possibly garbage here]
1316 # if garbage contains other addresses, they are ignored.
1319 if ($addr =~ /^([^"#,\s]*)/) {
1320 # address without quoting: remove anything after the address
1326 sub sanitize_address_list
{
1327 return (map { sanitize_address
($_) } @_);
1330 sub process_address_list
{
1331 my @addr_list = map { parse_address_line
($_) } @_;
1332 @addr_list = expand_aliases
(@addr_list);
1333 @addr_list = sanitize_address_list
(@addr_list);
1334 @addr_list = validate_address_list
(@addr_list);
1335 @addr_list = mailmap_address_list
(@addr_list);
1339 # Returns the local Fully Qualified Domain Name (FQDN) if available.
1341 # Tightly configured MTAa require that a caller sends a real DNS
1342 # domain name that corresponds the IP address in the HELO/EHLO
1343 # handshake. This is used to verify the connection and prevent
1344 # spammers from trying to hide their identity. If the DNS and IP don't
1345 # match, the receiving MTA may deny the connection.
1347 # Here is a deny example of Net::SMTP with the default "localhost.localdomain"
1349 # Net::SMTP=GLOB(0x267ec28)>>> EHLO localhost.localdomain
1350 # Net::SMTP=GLOB(0x267ec28)<<< 550 EHLO argument does not match calling host
1352 # This maildomain*() code is based on ideas in Perl library Test::Reporter
1353 # /usr/share/perl5/Test/Reporter/Mail/Util.pm ==> sub _maildomain ()
1357 return defined $domain && !($^O
eq 'darwin' && $domain =~ /\.local$/) && $domain =~ /\./;
1360 sub maildomain_net
{
1363 require Net
::Domain
;
1364 my $domain = Net
::Domain
::domainname
();
1365 $maildomain = $domain if valid_fqdn
($domain);
1370 sub maildomain_mta
{
1373 for my $host (qw(mailhost localhost)) {
1375 my $smtp = Net
::SMTP
->new($host);
1376 if (defined $smtp) {
1377 my $domain = $smtp->domain;
1380 $maildomain = $domain if valid_fqdn
($domain);
1382 last if $maildomain;
1390 return maildomain_net
() || maildomain_mta
() || 'localhost.localdomain';
1393 sub smtp_host_string
{
1394 if (defined $smtp_server_port) {
1395 return "$smtp_server:$smtp_server_port";
1397 return $smtp_server;
1401 # Returns 1 if authentication succeeded or was not necessary
1402 # (smtp_user was not specified), and 0 otherwise.
1404 sub smtp_auth_maybe
{
1405 if (!defined $smtp_authuser || $auth || (defined $smtp_auth && $smtp_auth eq "none")) {
1409 # Workaround AUTH PLAIN/LOGIN interaction defect
1410 # with Authen::SASL::Cyrus
1412 require Authen
::SASL
;
1413 Authen
::SASL
->import(qw(Perl));
1416 # Check mechanism naming as defined in:
1417 # https://tools.ietf.org/html/rfc4422#page-8
1418 if ($smtp_auth && $smtp_auth !~ /^(\b[A-Z0-9-_]{1,20}\s*)*$/) {
1419 die "invalid smtp auth: '${smtp_auth}'";
1422 # TODO: Authentication may fail not because credentials were
1423 # invalid but due to other reasons, in which we should not
1424 # reject credentials.
1425 $auth = Git
::credential
({
1426 'protocol' => 'smtp',
1427 'host' => smtp_host_string
(),
1428 'username' => $smtp_authuser,
1429 # if there's no password, "git credential fill" will
1430 # give us one, otherwise it'll just pass this one.
1431 'password' => $smtp_authpass
1436 my $sasl = Authen
::SASL
->new(
1437 mechanism
=> $smtp_auth,
1439 user
=> $cred->{'username'},
1440 pass
=> $cred->{'password'},
1441 authname
=> $cred->{'username'},
1445 return !!$smtp->auth($sasl);
1448 return !!$smtp->auth($cred->{'username'}, $cred->{'password'});
1454 sub ssl_verify_params
{
1456 require IO
::Socket
::SSL
;
1457 IO
::Socket
::SSL
->import(qw
/SSL_VERIFY_PEER SSL_VERIFY_NONE/);
1460 print STDERR
"Not using SSL_VERIFY_PEER due to out-of-date IO::Socket::SSL.\n";
1464 if (!defined $smtp_ssl_cert_path) {
1465 # use the OpenSSL defaults
1466 return (SSL_verify_mode
=> SSL_VERIFY_PEER
());
1469 if ($smtp_ssl_cert_path eq "") {
1470 return (SSL_verify_mode
=> SSL_VERIFY_NONE
());
1471 } elsif (-d
$smtp_ssl_cert_path) {
1472 return (SSL_verify_mode
=> SSL_VERIFY_PEER
(),
1473 SSL_ca_path
=> $smtp_ssl_cert_path);
1474 } elsif (-f
$smtp_ssl_cert_path) {
1475 return (SSL_verify_mode
=> SSL_VERIFY_PEER
(),
1476 SSL_ca_file
=> $smtp_ssl_cert_path);
1478 die sprintf(__
("CA path \"%s\" does not exist"), $smtp_ssl_cert_path);
1482 sub file_name_is_absolute
{
1485 # msys does not grok DOS drive-prefixes
1486 if ($^O
eq 'msys') {
1487 return ($path =~ m
#^/# || $path =~ m#^[a-zA-Z]\:#)
1490 require File
::Spec
::Functions
;
1491 return File
::Spec
::Functions
::file_name_is_absolute
($path);
1495 my @recipients = unique_email_list
(@to);
1496 @cc = (grep { my $cc = extract_valid_address_or_die
($_);
1497 not grep { $cc eq $_ || $_ =~ /<\Q${cc}\E>$/ } @recipients
1500 my $to = join (",\n\t", @recipients);
1501 @recipients = unique_email_list
(@recipients,@cc,@initial_bcc);
1502 @recipients = (map { extract_valid_address_or_die
($_) } @recipients);
1503 my $date = format_2822_time
($time++);
1504 my $gitversion = '@@GIT_VERSION@@';
1505 if ($gitversion =~ m/..GIT_VERSION../) {
1506 $gitversion = Git
::version
();
1509 my $cc = join(",\n\t", unique_email_list
(@cc));
1512 $ccline = "\nCc: $cc";
1514 make_message_id
() unless defined($message_id);
1516 my $header = "From: $sender
1520 Message-ID: $message_id
1523 $header .= "X-Mailer: git-send-email $gitversion\n";
1527 $header .= "In-Reply-To: $in_reply_to\n";
1528 $header .= "References: $references\n";
1531 $header .= "Reply-To: $reply_to\n";
1534 $header .= join("\n", @xh) . "\n";
1536 my $recipients_ref = \
@recipients;
1537 return ($recipients_ref, $to, $date, $gitversion, $cc, $ccline, $header);
1540 # Prepares the email, then asks the user what to do.
1542 # If the user chooses to send the email, it's sent and 1 is returned.
1543 # If the user chooses not to send the email, 0 is returned.
1544 # If the user decides they want to make further edits, -1 is returned and the
1545 # caller is expected to call send_message again after the edits are performed.
1547 # If an error occurs sending the email, this just dies.
1550 my ($recipients_ref, $to, $date, $gitversion, $cc, $ccline, $header) = gen_header
();
1551 my @recipients = @
$recipients_ref;
1553 my @sendmail_parameters = ('-i', @recipients);
1554 my $raw_from = $sender;
1555 if (defined $envelope_sender && $envelope_sender ne "auto") {
1556 $raw_from = $envelope_sender;
1558 $raw_from = extract_valid_address
($raw_from);
1559 unshift (@sendmail_parameters,
1560 '-f', $raw_from) if(defined $envelope_sender);
1562 if ($needs_confirm && !$dry_run) {
1563 print "\n$header\n";
1564 if ($needs_confirm eq "inform") {
1565 $confirm_unconfigured = 0; # squelch this message for the rest of this run
1566 $ask_default = "y"; # assume yes on EOF since user hasn't explicitly asked for confirmation
1568 The Cc list above has been expanded by additional
1569 addresses found in the patch commit message. By default
1570 send-email prompts before sending whenever this occurs.
1571 This behavior is controlled by the sendemail.confirm
1572 configuration setting.
1574 For additional information, run 'git send-email --help'.
1575 To retain the current behavior, but squelch this message,
1576 run 'git config --global sendemail.confirm auto'.
1580 # TRANSLATORS: Make sure to include [y] [n] [e] [q] [a] in your
1581 # translation. The program will only accept English input
1583 $_ = ask
(__
("Send this email? ([y]es|[n]o|[e]dit|[q]uit|[a]ll): "),
1584 valid_re
=> qr/^(?:yes|y|no|n|edit|e|quit|q|all|a)/i,
1585 default => $ask_default);
1586 die __
("Send this email reply required") unless defined $_;
1592 cleanup_compose_files
();
1599 unshift (@sendmail_parameters, @smtp_server_options);
1602 # We don't want to send the email.
1603 } elsif (defined $sendmail_cmd || file_name_is_absolute
($smtp_server)) {
1604 my $pid = open my $sm, '|-';
1605 defined $pid or die $!;
1607 if (defined $sendmail_cmd) {
1608 exec ("sh", "-c", "$sendmail_cmd \"\$@\"", "-", @sendmail_parameters)
1611 exec ($smtp_server, @sendmail_parameters)
1615 print $sm "$header\n$message";
1616 close $sm or die $!;
1619 if (!defined $smtp_server) {
1620 die __
("The required SMTP server is not properly defined.")
1624 my $use_net_smtp_ssl = version
->parse($Net::SMTP
::VERSION
) < version
->parse("2.34");
1625 $smtp_domain ||= maildomain
();
1627 if ($smtp_encryption eq 'ssl') {
1628 $smtp_server_port ||= 465; # ssmtp
1629 require IO
::Socket
::SSL
;
1631 # Suppress "variable accessed once" warning.
1634 $IO::Socket
::SSL
::DEBUG
= 1;
1637 # Net::SMTP::SSL->new() does not forward any SSL options
1638 IO
::Socket
::SSL
::set_client_defaults
(
1639 ssl_verify_params
());
1641 if ($use_net_smtp_ssl) {
1642 require Net
::SMTP
::SSL
;
1643 $smtp ||= Net
::SMTP
::SSL
->new($smtp_server,
1644 Hello
=> $smtp_domain,
1645 Port
=> $smtp_server_port,
1646 Debug
=> $debug_net_smtp);
1649 $smtp ||= Net
::SMTP
->new($smtp_server,
1650 Hello
=> $smtp_domain,
1651 Port
=> $smtp_server_port,
1652 Debug
=> $debug_net_smtp,
1657 $smtp_server_port ||= 25;
1658 $smtp ||= Net
::SMTP
->new($smtp_server,
1659 Hello
=> $smtp_domain,
1660 Debug
=> $debug_net_smtp,
1661 Port
=> $smtp_server_port);
1662 if ($smtp_encryption eq 'tls' && $smtp) {
1663 if ($use_net_smtp_ssl) {
1664 $smtp->command('STARTTLS');
1666 if ($smtp->code != 220) {
1667 die sprintf(__
("Server does not support STARTTLS! %s"), $smtp->message);
1669 require Net
::SMTP
::SSL
;
1670 $smtp = Net
::SMTP
::SSL
->start_SSL($smtp,
1671 ssl_verify_params
())
1672 or die sprintf(__
("STARTTLS failed! %s"), IO
::Socket
::SSL
::errstr
());
1675 $smtp->starttls(ssl_verify_params
())
1676 or die sprintf(__
("STARTTLS failed! %s"), IO
::Socket
::SSL
::errstr
());
1678 # Send EHLO again to receive fresh
1679 # supported commands
1680 $smtp->hello($smtp_domain);
1685 die __
("Unable to initialize SMTP properly. Check config and use --smtp-debug."),
1686 " VALUES: server=$smtp_server ",
1687 "encryption=$smtp_encryption ",
1688 "hello=$smtp_domain",
1689 defined $smtp_server_port ?
" port=$smtp_server_port" : "";
1692 smtp_auth_maybe
or die $smtp->message;
1694 $smtp->mail( $raw_from ) or die $smtp->message;
1695 $smtp->to( @recipients ) or die $smtp->message;
1696 $smtp->data or die $smtp->message;
1697 $smtp->datasend("$header\n") or die $smtp->message;
1698 my @lines = split /^/, $message;
1699 foreach my $line (@lines) {
1700 $smtp->datasend("$line") or die $smtp->message;
1702 $smtp->dataend() or die $smtp->message;
1703 $smtp->code =~ /250|200/ or die sprintf(__
("Failed to send %s\n"), $subject).$smtp->message;
1706 printf($dry_run ? __
("Dry-Sent %s") : __
("Sent %s"), $subject);
1709 print($dry_run ? __
("Dry-OK. Log says:") : __
("OK. Log says:"));
1711 if (!defined $sendmail_cmd && !file_name_is_absolute
($smtp_server)) {
1712 print "Server: $smtp_server\n";
1713 print "MAIL FROM:<$raw_from>\n";
1714 foreach my $entry (@recipients) {
1715 print "RCPT TO:<$entry>\n";
1719 if (defined $sendmail_cmd) {
1720 $sm = $sendmail_cmd;
1725 print "Sendmail: $sm ".join(' ',@sendmail_parameters)."\n";
1727 print $header, "\n";
1729 print __
("Result: "), $smtp->code, ' ',
1730 ($smtp->message =~ /\n([^\n]+\n)$/s);
1732 print __
("Result: OK");
1740 sub pre_process_file
{
1741 my ($t, $quiet) = @_;
1743 open my $fh, "<", $t or die sprintf(__
("can't open file %s"), $t);
1746 my $sauthor = undef;
1747 my $author_encoding;
1748 my $has_content_type;
1751 my $has_mime_version;
1755 my $input_format = undef;
1757 $subject = $initial_subject;
1761 # Retrieve and unfold header fields.
1762 my @header_lines = ();
1765 push(@header_lines, $_);
1767 @header = unfold_headers
(@header_lines);
1768 # Add computed headers, if applicable.
1769 unless ($no_header_cmd || ! $header_cmd) {
1770 push @header, invoke_header_cmd
($header_cmd, $t);
1772 # Now parse the header
1775 $input_format = 'mbox';
1779 if (!defined $input_format && /^[-A-Za-z]+:\s/) {
1780 $input_format = 'mbox';
1783 if (defined $input_format && $input_format eq 'mbox') {
1784 if (/^Subject:\s+(.*)$/i) {
1787 elsif (/^From:\s+(.*)$/i) {
1788 ($author, $author_encoding) = unquote_rfc2047
($1);
1789 $sauthor = sanitize_address
($author);
1790 next if $suppress_cc{'author'};
1791 next if $suppress_cc{'self'} and $sauthor eq $sender;
1792 printf(__
("(mbox) Adding cc: %s from line '%s'\n"),
1793 $1, $_) unless $quiet;
1796 elsif (/^To:\s+(.*)$/i) {
1797 foreach my $addr (parse_address_line
($1)) {
1798 printf(__
("(mbox) Adding to: %s from line '%s'\n"),
1799 $addr, $_) unless $quiet;
1803 elsif (/^Cc:\s+(.*)$/i) {
1804 foreach my $addr (parse_address_line
($1)) {
1805 my $qaddr = unquote_rfc2047
($addr);
1806 my $saddr = sanitize_address
($qaddr);
1807 if ($saddr eq $sender) {
1808 next if ($suppress_cc{'self'});
1810 next if ($suppress_cc{'cc'});
1812 printf(__
("(mbox) Adding cc: %s from line '%s'\n"),
1813 $addr, $_) unless $quiet;
1817 elsif (/^Content-type:/i) {
1818 $has_content_type = 1;
1819 if (/charset="?([^ "]+)/) {
1820 $body_encoding = $1;
1824 elsif (/^MIME-Version/i) {
1825 $has_mime_version = 1;
1828 elsif (/^Message-ID: (.*)/i) {
1831 elsif (/^Content-Transfer-Encoding: (.*)/i) {
1832 $xfer_encoding = $1 if not defined $xfer_encoding;
1834 elsif (/^In-Reply-To: (.*)/i) {
1835 if (!$initial_in_reply_to || $thread) {
1839 elsif (/^References: (.*)/i) {
1840 if (!$initial_in_reply_to || $thread) {
1844 elsif (!/^Date:\s/i && /^[-A-Za-z]+:\s+\S/) {
1848 # In the traditional
1849 # "send lots of email" format,
1852 # So let's support that, too.
1853 $input_format = 'lots';
1854 if (@cc == 0 && !$suppress_cc{'cc'}) {
1855 printf(__
("(non-mbox) Adding cc: %s from line '%s'\n"),
1856 $_, $_) unless $quiet;
1858 } elsif (!defined $subject) {
1863 # Now parse the message body
1866 if (/^([a-z][a-z-]*-by|Cc): (.*)/i) {
1868 my ($what, $c) = ($1, $2);
1869 # strip garbage for the address we'll use:
1870 $c = strip_garbage_one_address
($c);
1871 # sanitize a bit more to decide whether to suppress the address:
1872 my $sc = sanitize_address
($c);
1873 if ($sc eq $sender) {
1874 next if ($suppress_cc{'self'});
1876 if ($what =~ /^Signed-off-by$/i) {
1877 next if $suppress_cc{'sob'};
1878 } elsif ($what =~ /-by$/i) {
1879 next if $suppress_cc{'misc-by'};
1880 } elsif ($what =~ /Cc/i) {
1881 next if $suppress_cc{'bodycc'};
1884 if ($c !~ /.+@.+|<.+>/) {
1885 printf("(body) Ignoring %s from line '%s'\n",
1886 $what, $_) unless $quiet;
1890 printf(__
("(body) Adding cc: %s from line '%s'\n"),
1891 $sc, $_) unless $quiet;
1896 push @to, recipients_cmd
("to-cmd", "to", $to_cmd, $t, $quiet)
1898 push @cc, recipients_cmd
("cc-cmd", "cc", $cc_cmd, $t, $quiet)
1899 if defined $cc_cmd && !$suppress_cc{'cccmd'};
1901 if ($broken_encoding{$t} && !$has_content_type) {
1902 $xfer_encoding = '8bit' if not defined $xfer_encoding;
1903 $has_content_type = 1;
1904 push @xh, "Content-Type: text/plain; charset=$auto_8bit_encoding";
1905 $body_encoding = $auto_8bit_encoding;
1908 if ($broken_encoding{$t} && !is_rfc2047_quoted
($subject)) {
1909 $subject = quote_subject
($subject, $auto_8bit_encoding);
1912 if (defined $sauthor and $sauthor ne $sender) {
1913 $message = "From: $author\n\n$message";
1914 if (defined $author_encoding) {
1915 if ($has_content_type) {
1916 if ($body_encoding eq $author_encoding) {
1917 # ok, we already have the right encoding
1920 # uh oh, we should re-encode
1924 $xfer_encoding = '8bit' if not defined $xfer_encoding;
1925 $has_content_type = 1;
1927 "Content-Type: text/plain; charset=$author_encoding";
1931 $xfer_encoding = '8bit' if not defined $xfer_encoding;
1932 ($message, $xfer_encoding) = apply_transfer_encoding
(
1933 $message, $xfer_encoding, $target_xfer_encoding);
1934 push @xh, "Content-Transfer-Encoding: $xfer_encoding";
1935 unshift @xh, 'MIME-Version: 1.0' unless $has_mime_version;
1938 $confirm eq "always" or
1939 ($confirm =~ /^(?:auto|cc)$/ && @cc) or
1940 ($confirm =~ /^(?:auto|compose)$/ && $compose && $message_num == 1));
1941 $needs_confirm = "inform" if ($needs_confirm && $confirm_unconfigured && @cc);
1943 @to = process_address_list
(@to);
1944 @cc = process_address_list
(@cc);
1946 @to = (@initial_to, @to);
1947 @cc = (@initial_cc, @cc);
1949 if ($message_num == 1) {
1950 if (defined $cover_cc and $cover_cc) {
1953 if (defined $cover_to and $cover_to) {
1959 # Prepares the email, prompts the user, and sends it out
1960 # Returns 0 if an edit was done and the function should be called again, or 1
1961 # on the email being successfully sent out.
1965 pre_process_file
($t, $quiet);
1967 my $message_was_sent = send_message
();
1968 if ($message_was_sent == -1) {
1973 # set up for the next message
1975 if ($message_was_sent &&
1976 ($chain_reply_to || !defined $in_reply_to || length($in_reply_to) == 0 ||
1977 $message_num == 1)) {
1978 $in_reply_to = $message_id;
1979 if (length $references > 0) {
1980 $references .= "\n $message_id";
1982 $references = "$message_id";
1985 } elsif (!defined $initial_in_reply_to) {
1986 # --thread and --in-reply-to manage the "In-Reply-To" header and by
1987 # extension the "References" header. If these commands are not used, reset
1988 # the header values to their defaults.
1989 $in_reply_to = undef;
1992 $message_id = undef;
1994 if (defined $batch_size && $num_sent == $batch_size) {
1996 $smtp->quit if defined $smtp;
1999 sleep($relogin_delay) if defined $relogin_delay;
2005 sub initialize_modified_loop_vars
{
2006 $in_reply_to = $initial_in_reply_to;
2007 $references = $initial_in_reply_to || '';
2012 # FIFOs can only be read once, exclude them from validation.
2013 my @real_files = ();
2014 foreach my $f (@files) {
2016 push(@real_files, $f);
2020 # Run the loop once again to avoid gaps in the counter due to FIFO
2021 # arguments provided by the user.
2023 my $num_files = scalar @real_files;
2024 $ENV{GIT_SENDEMAIL_FILE_TOTAL
} = "$num_files";
2025 initialize_modified_loop_vars
();
2026 foreach my $r (@real_files) {
2027 $ENV{GIT_SENDEMAIL_FILE_COUNTER
} = "$num";
2028 pre_process_file
($r, 1);
2029 validate_patch
($r, $target_xfer_encoding);
2032 delete $ENV{GIT_SENDEMAIL_FILE_COUNTER
};
2033 delete $ENV{GIT_SENDEMAIL_FILE_TOTAL
};
2036 initialize_modified_loop_vars
();
2037 foreach my $t (@files) {
2038 while (!process_file
($t)) {
2039 # user edited the file
2043 # Execute a command and return its output lines as an array. Blank
2044 # lines which do not appear at the end of the output are reported as
2047 my ($prefix, $cmd, $file) = @_;
2049 my $seen_blank_line = 0;
2050 open my $fh, "-|", "$cmd \Q$file\E"
2051 or die sprintf(__
("(%s) Could not execute '%s'"), $prefix, $cmd);
2052 while (my $line = <$fh>) {
2053 die sprintf(__
("(%s) Malformed output from '%s'"), $prefix, $cmd)
2054 if $seen_blank_line;
2055 if ($line =~ /^$/) {
2056 $seen_blank_line = $line =~ /^$/;
2062 or die sprintf(__
("(%s) failed to close pipe to '%s'"), $prefix, $cmd);
2066 # Process headers lines, unfolding multiline headers as defined by RFC
2068 sub unfold_headers
{
2072 if (/^\s+\S/ and @headers) {
2073 chomp($headers[$#headers]);
2075 $headers[$#headers] .= $_;
2083 # Invoke the provided CMD with FILE as an argument, which should
2084 # output RFC 2822 email headers. Fold multiline headers and return the
2085 # headers as an array.
2086 sub invoke_header_cmd
{
2087 my ($cmd, $file) = @_;
2088 my @lines = execute_cmd
("header-cmd", $header_cmd, $file);
2089 return unfold_headers
(@lines);
2092 # Execute a command (e.g. $to_cmd) to get a list of email addresses
2093 # and return a results array
2094 sub recipients_cmd
{
2095 my ($prefix, $what, $cmd, $file, $quiet) = @_;
2099 @lines = execute_cmd
($prefix, $cmd, $file);
2100 for my $address (@lines) {
2101 $address =~ s/^\s*//g;
2102 $address =~ s/\s*$//g;
2103 $address = sanitize_address
($address);
2104 next if ($address eq $sender and $suppress_cc{'self'});
2105 push @addresses, $address;
2106 printf(__
("(%s) Adding %s: %s from: '%s'\n"),
2107 $prefix, $what, $address, $cmd) unless $quiet;
2112 cleanup_compose_files
();
2114 sub cleanup_compose_files
{
2115 unlink($compose_filename, $compose_filename . ".final") if $compose;
2118 $smtp->quit if $smtp;
2120 sub apply_transfer_encoding
{
2121 my $message = shift;
2125 return ($message, $to) if ($from eq $to and $from ne '7bit');
2127 require MIME
::QuotedPrint
;
2128 require MIME
::Base64
;
2130 $message = MIME
::QuotedPrint
::decode
($message)
2131 if ($from eq 'quoted-printable');
2132 $message = MIME
::Base64
::decode
($message)
2133 if ($from eq 'base64');
2135 $to = ($message =~ /(?:.{999,}|\r)/) ?
'quoted-printable' : '8bit'
2138 die __
("cannot send message as 7bit")
2139 if ($to eq '7bit' and $message =~ /[^[:ascii:]]/);
2140 return ($message, $to)
2141 if ($to eq '7bit' or $to eq '8bit');
2142 return (MIME
::QuotedPrint
::encode
($message, "\n", 0), $to)
2143 if ($to eq 'quoted-printable');
2144 return (MIME
::Base64
::encode
($message, "\n"), $to)
2145 if ($to eq 'base64');
2146 die __
("invalid transfer encoding");
2149 sub unique_email_list
{
2153 foreach my $entry (@_) {
2154 my $clean = extract_valid_address_or_die
($entry);
2155 $seen{$clean} ||= 0;
2156 next if $seen{$clean}++;
2157 push @emails, $entry;
2162 sub validate_patch
{
2163 my ($fn, $xfer_encoding) = @_;
2166 my $hook_name = 'sendemail-validate';
2167 my $hooks_path = $repo->command_oneline('rev-parse', '--git-path', 'hooks');
2169 my $validate_hook = File
::Spec
->catfile($hooks_path, $hook_name);
2171 if (-x
$validate_hook) {
2173 my $target = Cwd
::abs_path
($fn);
2174 # The hook needs a correct cwd and GIT_DIR.
2175 my $cwd_save = Cwd
::getcwd
();
2176 chdir($repo->wc_path() or $repo->repo_path())
2177 or die("chdir: $!");
2178 local $ENV{"GIT_DIR"} = $repo->repo_path();
2180 my ($recipients_ref, $to, $date, $gitversion, $cc, $ccline, $header) = gen_header
();
2183 my ($header_filehandle, $header_filename) = File
::Temp
::tempfile
(
2184 TEMPLATE
=> ".gitsendemail.header.XXXXXX",
2185 DIR
=> $repo->repo_path(),
2188 print $header_filehandle $header;
2190 my @cmd = ("git", "hook", "run", "--ignore-missing",
2192 my @cmd_msg = (@cmd, "<patch>", "<header>");
2193 my @cmd_run = (@cmd, $target, $header_filename);
2194 $hook_error = system_or_msg
(\
@cmd_run, undef, "@cmd_msg");
2195 chdir($cwd_save) or die("chdir: $!");
2198 $hook_error = sprintf(
2199 __
("fatal: %s: rejected by %s hook\n%s\nwarning: no patches were sent\n"),
2200 $fn, $hook_name, $hook_error);
2205 # Any long lines will be automatically fixed if we use a suitable transfer
2207 unless ($xfer_encoding =~ /^(?:auto|quoted-printable|base64)$/) {
2208 open(my $fh, '<', $fn)
2209 or die sprintf(__
("unable to open %s: %s\n"), $fn, $!);
2210 while (my $line = <$fh>) {
2211 if (length($line) > 998) {
2212 die sprintf(__
("fatal: %s:%d is longer than 998 characters\n" .
2213 "warning: no patches were sent\n"), $fn, $.);
2221 my ($last, $lastlen, $file, $known_suffix) = @_;
2222 my ($suffix, $skip);
2225 if (defined $last &&
2226 ($lastlen < length($file)) &&
2227 (substr($file, 0, $lastlen) eq $last) &&
2228 ($suffix = substr($file, $lastlen)) !~ /^[a-z0-9]/i) {
2229 if (defined $known_suffix && $suffix eq $known_suffix) {
2230 printf(__
("Skipping %s with backup suffix '%s'.\n"), $file, $known_suffix);
2233 # TRANSLATORS: please keep "[y|N]" as is.
2234 my $answer = ask
(sprintf(__
("Do you really want to send %s? [y|N]: "), $file),
2235 valid_re
=> qr/^(?:y|n)/i,
2237 $skip = ($answer ne 'y');
2239 $known_suffix = $suffix;
2243 return ($skip, $known_suffix);
2246 sub handle_backup_files
{
2248 my ($last, $lastlen, $known_suffix, $skip, @result);
2249 for my $file (@file) {
2250 ($skip, $known_suffix) = handle_backup
($last, $lastlen,
2251 $file, $known_suffix);
2252 push @result, $file unless $skip;
2254 $lastlen = length($file);
2259 sub file_has_nonascii
{
2261 open(my $fh, '<', $fn)
2262 or die sprintf(__
("unable to open %s: %s\n"), $fn, $!);
2263 while (my $line = <$fh>) {
2264 return 1 if $line =~ /[^[:ascii:]]/;
2269 sub body_or_subject_has_nonascii
{
2271 open(my $fh, '<', $fn)
2272 or die sprintf(__
("unable to open %s: %s\n"), $fn, $!);
2273 while (my $line = <$fh>) {
2274 last if $line =~ /^$/;
2275 return 1 if $line =~ /^Subject.*[^[:ascii:]]/;
2277 while (my $line = <$fh>) {
2278 return 1 if $line =~ /[^[:ascii:]]/;