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.
22 use POSIX qw/strftime/;
27 use File::Temp qw/ tempdir tempfile /;
28 use File::Spec::Functions qw(catfile);
33 Getopt::Long::Configure qw/ pass_through /;
37 my ($class, $reason) = @_;
38 return bless \$reason, shift;
42 die "Cannot use readline on FakeTerm: $$self";
49 git send-email [options] <file | directory | rev-list options >
50 git send-email --dump-aliases
53 --from <str> * Email From:
54 --[no-]to <str> * Email To:
55 --[no-]cc <str> * Email Cc:
56 --[no-]bcc <str> * Email Bcc:
57 --subject <str> * Email "Subject:"
58 --in-reply-to <str> * Email "In-Reply-To:"
59 --[no-]xmailer * Add "X-Mailer:" header (default).
60 --[no-]annotate * Review each patch that will be sent in an editor.
61 --compose * Open an editor for introduction.
62 --compose-encoding <str> * Encoding to assume for introduction.
63 --8bit-encoding <str> * Encoding to assume 8bit mails if undeclared
64 --transfer-encoding <str> * Transfer encoding to use (quoted-printable, 8bit, base64)
67 --envelope-sender <str> * Email envelope sender.
68 --smtp-server <str:int> * Outgoing SMTP server to use. The port
69 is optional. Default 'localhost'.
70 --smtp-server-option <str> * Outgoing SMTP server option to use.
71 --smtp-server-port <int> * Outgoing SMTP server port.
72 --smtp-user <str> * Username for SMTP-AUTH.
73 --smtp-pass <str> * Password for SMTP-AUTH; not necessary.
74 --smtp-encryption <str> * tls or ssl; anything else disables.
75 --smtp-ssl * Deprecated. Use '--smtp-encryption ssl'.
76 --smtp-ssl-cert-path <str> * Path to ca-certificates (either directory or file).
77 Pass an empty string to disable certificate
79 --smtp-domain <str> * The domain name sent to HELO/EHLO handshake
80 --smtp-auth <str> * Space-separated list of allowed AUTH mechanisms.
81 This setting forces to use one of the listed mechanisms.
82 --smtp-debug <0|1> * Disable, enable Net::SMTP debug.
85 --identity <str> * Use the sendemail.<id> options.
86 --to-cmd <str> * Email To: via `<str> \$patch_path`
87 --cc-cmd <str> * Email Cc: via `<str> \$patch_path`
88 --suppress-cc <str> * author, self, sob, cc, cccmd, body, bodycc, all.
89 --[no-]cc-cover * Email Cc: addresses in the cover letter.
90 --[no-]to-cover * Email To: addresses in the cover letter.
91 --[no-]signed-off-by-cc * Send to Signed-off-by: addresses. Default on.
92 --[no-]suppress-from * Send to self. Default off.
93 --[no-]chain-reply-to * Chain In-Reply-To: fields. Default off.
94 --[no-]thread * Use In-Reply-To: field. Default on.
97 --confirm <str> * Confirm recipients before sending;
98 auto, cc, compose, always, or never.
99 --quiet * Output one line of info per email.
100 --dry-run * Don't actually send the emails.
101 --[no-]validate * Perform patch sanity checks. Default on.
102 --[no-]format-patch * understand any non optional arguments as
103 `git format-patch` ones.
104 --force * Send even if safety checks would prevent it.
107 --dump-aliases * Dump configured aliases and exit.
113 # most mail servers generate the Date: header, but not all...
114 sub format_2822_time {
116 my @localtm = localtime($time);
117 my @gmttm = gmtime($time);
118 my $localmin = $localtm[1] + $localtm[2] * 60;
119 my $gmtmin = $gmttm[1] + $gmttm[2] * 60;
120 if ($localtm[0] != $gmttm[0]) {
121 die __("local zone differs from GMT by a non-minute interval\n");
123 if ((($gmttm[6] + 1) % 7) == $localtm[6]) {
125 } elsif ((($gmttm[6] - 1) % 7) == $localtm[6]) {
127 } elsif ($gmttm[6] != $localtm[6]) {
128 die __("local time offset greater than or equal to 24 hours\n");
130 my $offset = $localmin - $gmtmin;
131 my $offhour = $offset / 60;
132 my $offmin = abs($offset % 60);
133 if (abs($offhour) >= 24) {
134 die __("local time offset greater than or equal to 24 hours\n");
137 return sprintf("%s, %2d %s %d %02d:%02d:%02d %s%02d%02d",
138 qw(Sun Mon Tue Wed Thu Fri Sat)[$localtm[6]],
140 qw(Jan Feb Mar Apr May Jun
141 Jul Aug Sep Oct Nov Dec)[$localtm[4]],
146 ($offset >= 0) ? '+' : '-',
152 my $have_email_valid = eval { require Email::Valid; 1 };
153 my $have_mail_address = eval { require Mail::Address; 1 };
157 # Regexes for RFC 2047 productions.
158 my $re_token = qr/[^][()<>@,;:\\"\/?.= \000-\037\177-\377]+/;
159 my $re_encoded_text = qr/[^? \000-\037\177-\377]+/;
160 my $re_encoded_word = qr/=\?($re_token)\?($re_token)\?($re_encoded_text)\?=/;
162 # Variables we fill in automatically, or via prompting:
163 my (@to,$no_to,@initial_to,@cc,$no_cc,@initial_cc,@bcclist,$no_bcc,@xh,
164 $initial_reply_to,$initial_subject,@files,
165 $author,$sender,$smtp_authpass,$annotate,$use_xmailer,$compose,$time);
170 #$initial_reply_to = ''; #<20050203173208.GA23964@foobar.com>';
172 my $repo = eval { Git->repository() };
173 my @repo = $repo ? ($repo) : ();
175 $ENV{"GIT_SEND_EMAIL_NOTTY"}
176 ? new Term::ReadLine 'git-send-email', \*STDIN, \*STDOUT
177 : new Term::ReadLine 'git-send-email';
180 $term = new FakeTerm "$@: going non-interactive";
183 # Behavior modification variables
184 my ($quiet, $dry_run) = (0, 0);
186 my $compose_filename;
188 my $dump_aliases = 0;
190 # Handle interactive edition of files.
195 if (!defined($editor)) {
196 $editor = Git::command_oneline('var', 'GIT_EDITOR');
198 if (defined($multiedit) && !$multiedit) {
200 system('sh', '-c', $editor.' "$@"', $editor, $_);
201 if (($? & 127) || ($? >> 8)) {
202 die(__("the editor exited uncleanly, aborting everything"));
206 system('sh', '-c', $editor.' "$@"', $editor, @_);
207 if (($? & 127) || ($? >> 8)) {
208 die(__("the editor exited uncleanly, aborting everything"));
213 # Variables with corresponding config settings
214 my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc);
215 my ($cover_cc, $cover_to);
216 my ($to_cmd, $cc_cmd);
217 my ($smtp_server, $smtp_server_port, @smtp_server_options);
218 my ($smtp_authuser, $smtp_encryption, $smtp_ssl_cert_path);
219 my ($identity, $aliasfiletype, @alias_files, $smtp_domain, $smtp_auth);
220 my ($validate, $confirm);
222 my ($auto_8bit_encoding);
223 my ($compose_encoding);
224 my ($target_xfer_encoding);
226 my ($debug_net_smtp) = 0; # Net::SMTP, see send_message()
228 my %config_bool_settings = (
229 "thread" => [\$thread, 1],
230 "chainreplyto" => [\$chain_reply_to, 0],
231 "suppressfrom" => [\$suppress_from, undef],
232 "signedoffbycc" => [\$signed_off_by_cc, undef],
233 "cccover" => [\$cover_cc, undef],
234 "tocover" => [\$cover_to, undef],
235 "signedoffcc" => [\$signed_off_by_cc, undef], # Deprecated
236 "validate" => [\$validate, 1],
237 "multiedit" => [\$multiedit, undef],
238 "annotate" => [\$annotate, undef],
239 "xmailer" => [\$use_xmailer, 1]
242 my %config_settings = (
243 "smtpserver" => \$smtp_server,
244 "smtpserverport" => \$smtp_server_port,
245 "smtpserveroption" => \@smtp_server_options,
246 "smtpuser" => \$smtp_authuser,
247 "smtppass" => \$smtp_authpass,
248 "smtpdomain" => \$smtp_domain,
249 "smtpauth" => \$smtp_auth,
250 "to" => \@initial_to,
252 "cc" => \@initial_cc,
254 "aliasfiletype" => \$aliasfiletype,
256 "suppresscc" => \@suppress_cc,
257 "envelopesender" => \$envelope_sender,
258 "confirm" => \$confirm,
260 "assume8bitencoding" => \$auto_8bit_encoding,
261 "composeencoding" => \$compose_encoding,
262 "transferencoding" => \$target_xfer_encoding,
265 my %config_path_settings = (
266 "aliasesfile" => \@alias_files,
267 "smtpsslcertpath" => \$smtp_ssl_cert_path,
270 # Handle Uncouth Termination
274 print color("reset"), "\n";
276 # SMTP password masked
279 # tmp files from --compose
280 if (defined $compose_filename) {
281 if (-e $compose_filename) {
282 printf __("'%s' contains an intermediate version ".
283 "of the email you were composing.\n"),
286 if (-e ($compose_filename . ".final")) {
287 printf __("'%s.final' contains the composed email.\n"),
295 $SIG{TERM} = \&signal_handler;
296 $SIG{INT} = \&signal_handler;
298 # Begin by accumulating all the variables (defined above), that we will end up
299 # needing, first, from the command line:
302 my $rc = GetOptions("h" => \$help,
303 "dump-aliases" => \$dump_aliases);
305 die __("--dump-aliases incompatible with other options\n")
306 if !$help and $dump_aliases and @ARGV;
308 "sender|from=s" => \$sender,
309 "in-reply-to=s" => \$initial_reply_to,
310 "subject=s" => \$initial_subject,
311 "to=s" => \@initial_to,
312 "to-cmd=s" => \$to_cmd,
314 "cc=s" => \@initial_cc,
316 "bcc=s" => \@bcclist,
317 "no-bcc" => \$no_bcc,
318 "chain-reply-to!" => \$chain_reply_to,
319 "no-chain-reply-to" => sub {$chain_reply_to = 0},
320 "smtp-server=s" => \$smtp_server,
321 "smtp-server-option=s" => \@smtp_server_options,
322 "smtp-server-port=s" => \$smtp_server_port,
323 "smtp-user=s" => \$smtp_authuser,
324 "smtp-pass:s" => \$smtp_authpass,
325 "smtp-ssl" => sub { $smtp_encryption = 'ssl' },
326 "smtp-encryption=s" => \$smtp_encryption,
327 "smtp-ssl-cert-path=s" => \$smtp_ssl_cert_path,
328 "smtp-debug:i" => \$debug_net_smtp,
329 "smtp-domain:s" => \$smtp_domain,
330 "smtp-auth=s" => \$smtp_auth,
331 "identity=s" => \$identity,
332 "annotate!" => \$annotate,
333 "no-annotate" => sub {$annotate = 0},
334 "compose" => \$compose,
336 "cc-cmd=s" => \$cc_cmd,
337 "suppress-from!" => \$suppress_from,
338 "no-suppress-from" => sub {$suppress_from = 0},
339 "suppress-cc=s" => \@suppress_cc,
340 "signed-off-cc|signed-off-by-cc!" => \$signed_off_by_cc,
341 "no-signed-off-cc|no-signed-off-by-cc" => sub {$signed_off_by_cc = 0},
342 "cc-cover|cc-cover!" => \$cover_cc,
343 "no-cc-cover" => sub {$cover_cc = 0},
344 "to-cover|to-cover!" => \$cover_to,
345 "no-to-cover" => sub {$cover_to = 0},
346 "confirm=s" => \$confirm,
347 "dry-run" => \$dry_run,
348 "envelope-sender=s" => \$envelope_sender,
349 "thread!" => \$thread,
350 "no-thread" => sub {$thread = 0},
351 "validate!" => \$validate,
352 "no-validate" => sub {$validate = 0},
353 "transfer-encoding=s" => \$target_xfer_encoding,
354 "format-patch!" => \$format_patch,
355 "no-format-patch" => sub {$format_patch = 0},
356 "8bit-encoding=s" => \$auto_8bit_encoding,
357 "compose-encoding=s" => \$compose_encoding,
359 "xmailer!" => \$use_xmailer,
360 "no-xmailer" => sub {$use_xmailer = 0},
368 die __("Cannot run git format-patch from outside a repository\n")
369 if $format_patch and not $repo;
371 # Now, let's fill any that aren't set in with defaults:
376 foreach my $setting (keys %config_bool_settings) {
377 my $target = $config_bool_settings{$setting}->[0];
378 $$target = Git::config_bool(@repo, "$prefix.$setting") unless (defined $$target);
381 foreach my $setting (keys %config_path_settings) {
382 my $target = $config_path_settings{$setting};
383 if (ref($target) eq "ARRAY") {
385 my @values = Git::config_path(@repo, "$prefix.$setting");
386 @$target = @values if (@values && defined $values[0]);
390 $$target = Git::config_path(@repo, "$prefix.$setting") unless (defined $$target);
394 foreach my $setting (keys %config_settings) {
395 my $target = $config_settings{$setting};
396 next if $setting eq "to" and defined $no_to;
397 next if $setting eq "cc" and defined $no_cc;
398 next if $setting eq "bcc" and defined $no_bcc;
399 if (ref($target) eq "ARRAY") {
401 my @values = Git::config(@repo, "$prefix.$setting");
402 @$target = @values if (@values && defined $values[0]);
406 $$target = Git::config(@repo, "$prefix.$setting") unless (defined $$target);
410 if (!defined $smtp_encryption) {
411 my $enc = Git::config(@repo, "$prefix.smtpencryption");
413 $smtp_encryption = $enc;
414 } elsif (Git::config_bool(@repo, "$prefix.smtpssl")) {
415 $smtp_encryption = 'ssl';
420 # read configuration from [sendemail "$identity"], fall back on [sendemail]
421 $identity = Git::config(@repo, "sendemail.identity") unless (defined $identity);
422 read_config("sendemail.$identity") if (defined $identity);
423 read_config("sendemail");
425 # fall back on builtin bool defaults
426 foreach my $setting (values %config_bool_settings) {
427 ${$setting->[0]} = $setting->[1] unless (defined (${$setting->[0]}));
430 # 'default' encryption is none -- this only prevents a warning
431 $smtp_encryption = '' unless (defined $smtp_encryption);
433 # Set CC suppressions
436 foreach my $entry (@suppress_cc) {
437 die sprintf(__("Unknown --suppress-cc field: '%s'\n"), $entry)
438 unless $entry =~ /^(?:all|cccmd|cc|author|self|sob|body|bodycc)$/;
439 $suppress_cc{$entry} = 1;
443 if ($suppress_cc{'all'}) {
444 foreach my $entry (qw (cccmd cc author self sob body bodycc)) {
445 $suppress_cc{$entry} = 1;
447 delete $suppress_cc{'all'};
450 # If explicit old-style ones are specified, they trump --suppress-cc.
451 $suppress_cc{'self'} = $suppress_from if defined $suppress_from;
452 $suppress_cc{'sob'} = !$signed_off_by_cc if defined $signed_off_by_cc;
454 if ($suppress_cc{'body'}) {
455 foreach my $entry (qw (sob bodycc)) {
456 $suppress_cc{$entry} = 1;
458 delete $suppress_cc{'body'};
461 # Set confirm's default value
462 my $confirm_unconfigured = !defined $confirm;
463 if ($confirm_unconfigured) {
464 $confirm = scalar %suppress_cc ? 'compose' : 'auto';
466 die sprintf(__("Unknown --confirm setting: '%s'\n"), $confirm)
467 unless $confirm =~ /^(?:auto|cc|compose|always|never)/;
469 # Debugging, print out the suppressions.
471 print "suppressions:\n";
472 foreach my $entry (keys %suppress_cc) {
473 printf " %-5s -> $suppress_cc{$entry}\n", $entry;
477 my ($repoauthor, $repocommitter);
478 ($repoauthor) = Git::ident_person(@repo, 'author');
479 ($repocommitter) = Git::ident_person(@repo, 'committer');
481 sub parse_address_line {
482 if ($have_mail_address) {
483 return map { $_->format } Mail::Address->parse($_[0]);
485 return Git::parse_mailboxes($_[0]);
490 return quotewords('\s*,\s*', 1, @_);
495 sub parse_sendmail_alias {
498 printf STDERR __("warning: sendmail alias with quotes is not supported: %s\n"), $_;
499 } elsif (/:include:/) {
500 printf STDERR __("warning: `:include:` not supported: %s\n"), $_;
502 printf STDERR __("warning: `/file` or `|pipe` redirection not supported: %s\n"), $_;
503 } elsif (/^(\S+?)\s*:\s*(.+)$/) {
504 my ($alias, $addr) = ($1, $2);
505 $aliases{$alias} = [ split_addrs($addr) ];
507 printf STDERR __("warning: sendmail line is not recognized: %s\n"), $_;
511 sub parse_sendmail_aliases {
516 next if /^\s*$/ || /^\s*#/;
517 $s .= $_, next if $s =~ s/\\$// || s/^\s+//;
518 parse_sendmail_alias($s) if $s;
521 $s =~ s/\\$//; # silently tolerate stray '\' on last line
522 parse_sendmail_alias($s) if $s;
526 # multiline formats can be supported in the future
527 mutt => sub { my $fh = shift; while (<$fh>) {
528 if (/^\s*alias\s+(?:-group\s+\S+\s+)*(\S+)\s+(.*)$/) {
529 my ($alias, $addr) = ($1, $2);
530 $addr =~ s/#.*$//; # mutt allows # comments
531 # commas delimit multiple addresses
532 my @addr = split_addrs($addr);
534 # quotes may be escaped in the file,
535 # unescape them so we do not double-escape them later.
536 s/\\"/"/g foreach @addr;
537 $aliases{$alias} = \@addr
539 mailrc => sub { my $fh = shift; while (<$fh>) {
540 if (/^alias\s+(\S+)\s+(.*?)\s*$/) {
541 # spaces delimit multiple addresses
542 $aliases{$1} = [ quotewords('\s+', 0, $2) ];
544 pine => sub { my $fh = shift; my $f='\t[^\t]*';
545 for (my $x = ''; defined($x); $x = $_) {
547 $x .= $1 while(defined($_ = <$fh>) && /^ +(.*)$/);
548 $x =~ /^(\S+)$f\t\(?([^\t]+?)\)?(:?$f){0,2}$/ or next;
549 $aliases{$1} = [ split_addrs($2) ];
551 elm => sub { my $fh = shift;
553 if (/^(\S+)\s+=\s+[^=]+=\s(\S+)/) {
554 my ($alias, $addr) = ($1, $2);
555 $aliases{$alias} = [ split_addrs($addr) ];
558 sendmail => \&parse_sendmail_aliases,
559 gnus => sub { my $fh = shift; while (<$fh>) {
560 if (/\(define-mail-alias\s+"(\S+?)"\s+"(\S+?)"\)/) {
561 $aliases{$1} = [ $2 ];
565 if (@alias_files and $aliasfiletype and defined $parse_alias{$aliasfiletype}) {
566 foreach my $file (@alias_files) {
567 open my $fh, '<', $file or die "opening $file: $!\n";
568 $parse_alias{$aliasfiletype}->($fh);
574 print "$_\n" for (sort keys %aliases);
578 # is_format_patch_arg($f) returns 0 if $f names a patch, or 1 if
579 # $f is a revision list specification to be passed to format-patch.
580 sub is_format_patch_arg {
584 $repo->command('rev-parse', '--verify', '--quiet', $f);
585 if (defined($format_patch)) {
586 return $format_patch;
588 die sprintf(__ <<EOF, $f, $f);
589 File '%s' exists but it could also be the range of commits
590 to produce patches for. Please disambiguate by...
592 * Saying "./%s" if you mean a file; or
593 * Giving --format-patch option if you mean a range.
595 } catch Git::Error::Command with {
596 # Not a valid revision. Treat it as a filename.
601 # Now that all the defaults are set, process the rest of the command line
602 # arguments and collect up the files that need to be processed.
604 while (defined(my $f = shift @ARGV)) {
606 push @rev_list_opts, "--", @ARGV;
608 } elsif (-d $f and !is_format_patch_arg($f)) {
610 or die sprintf(__("Failed to opendir %s: %s"), $f, $!);
612 push @files, grep { -f $_ } map { catfile($f, $_) }
615 } elsif ((-f $f or -p $f) and !is_format_patch_arg($f)) {
618 push @rev_list_opts, $f;
622 if (@rev_list_opts) {
623 die __("Cannot run git format-patch from outside a repository\n")
625 push @files, $repo->command('format-patch', '-o', tempdir(CLEANUP => 1), @rev_list_opts);
628 @files = handle_backup_files(@files);
631 foreach my $f (@files) {
633 my $error = validate_patch($f);
634 $error and die sprintf(__("fatal: %s: %s\nwarning: no patches were sent\n"),
642 print $_,"\n" for (@files);
645 print STDERR __("\nNo patch files specified!\n\n");
649 sub get_patch_subject {
651 open (my $fh, '<', $fn);
652 while (my $line = <$fh>) {
653 next unless ($line =~ /^Subject: (.*)$/);
658 die sprintf(__("No subject line in %s?"), $fn);
662 # Note that this does not need to be secure, but we will make a small
663 # effort to have it be unique
664 $compose_filename = ($repo ?
665 tempfile(".gitsendemail.msg.XXXXXX", DIR => $repo->repo_path()) :
666 tempfile(".gitsendemail.msg.XXXXXX", DIR => "."))[1];
667 open my $c, ">", $compose_filename
668 or die sprintf(__("Failed to open for writing %s: %s"), $compose_filename, $!);
671 my $tpl_sender = $sender || $repoauthor || $repocommitter || '';
672 my $tpl_subject = $initial_subject || '';
673 my $tpl_reply_to = $initial_reply_to || '';
676 From $tpl_sender # This line is ignored.
677 GIT: Lines beginning in "GIT:" will be removed.
678 GIT: Consider including an overall diffstat or table of contents
679 GIT: for the patch you are writing.
681 GIT: Clear the body content if you don't wish to send a summary.
683 Subject: $tpl_subject
684 In-Reply-To: $tpl_reply_to
688 print $c get_patch_subject($f);
693 do_edit($compose_filename, @files);
695 do_edit($compose_filename);
698 open my $c2, ">", $compose_filename . ".final"
699 or die sprintf(__("Failed to open %s.final: %s"), $compose_filename, $!);
701 open $c, "<", $compose_filename
702 or die sprintf(__("Failed to open %s: %s"), $compose_filename, $!);
704 my $need_8bit_cte = file_has_nonascii($compose_filename);
706 my $summary_empty = 1;
707 if (!defined $compose_encoding) {
708 $compose_encoding = "UTF-8";
713 $summary_empty = 0 unless (/^\n$/);
716 if ($need_8bit_cte) {
717 print $c2 "MIME-Version: 1.0\n",
718 "Content-Type: text/plain; ",
719 "charset=$compose_encoding\n",
720 "Content-Transfer-Encoding: 8bit\n";
722 } elsif (/^MIME-Version:/i) {
724 } elsif (/^Subject:\s*(.+)\s*$/i) {
725 $initial_subject = $1;
726 my $subject = $initial_subject;
728 quote_subject($subject, $compose_encoding) .
730 } elsif (/^In-Reply-To:\s*(.+)\s*$/i) {
731 $initial_reply_to = $1;
733 } elsif (/^From:\s*(.+)\s*$/i) {
736 } elsif (/^(?:To|Cc|Bcc):/i) {
737 print __("To/Cc/Bcc fields are not interpreted yet, they have been ignored\n");
745 if ($summary_empty) {
746 print __("Summary email is empty, skipping it\n");
749 } elsif ($annotate) {
754 my ($prompt, %arg) = @_;
755 my $valid_re = $arg{valid_re};
756 my $default = $arg{default};
757 my $confirm_only = $arg{confirm_only};
760 return defined $default ? $default : undef
761 unless defined $term->IN and defined fileno($term->IN) and
762 defined $term->OUT and defined fileno($term->OUT);
764 $resp = $term->readline($prompt);
765 if (!defined $resp) { # EOF
767 return defined $default ? $default : undef;
769 if ($resp eq '' and defined $default) {
772 if (!defined $valid_re or $resp =~ /$valid_re/) {
776 my $yesno = $term->readline(
777 # TRANSLATORS: please keep [y/N] as is.
778 sprintf(__("Are you sure you want to use <%s> [y/N]? "), $resp));
779 if (defined $yesno && $yesno =~ /y/i) {
789 sub file_declares_8bit_cte {
791 open (my $fh, '<', $fn);
792 while (my $line = <$fh>) {
793 last if ($line =~ /^$/);
794 return 1 if ($line =~ /^Content-Transfer-Encoding: .*8bit.*$/);
800 foreach my $f (@files) {
801 next unless (body_or_subject_has_nonascii($f)
802 && !file_declares_8bit_cte($f));
803 $broken_encoding{$f} = 1;
806 if (!defined $auto_8bit_encoding && scalar %broken_encoding) {
807 print __("The following files are 8bit, but do not declare " .
808 "a Content-Transfer-Encoding.\n");
809 foreach my $f (sort keys %broken_encoding) {
812 $auto_8bit_encoding = ask(__("Which 8bit encoding should I declare [UTF-8]? "),
813 valid_re => qr/.{4}/, confirm_only => 1,
819 if (get_patch_subject($f) =~ /\Q*** SUBJECT HERE ***\E/) {
820 die sprintf(__("Refusing to send because the patch\n\t%s\n"
821 . "has the template subject '*** SUBJECT HERE ***'. "
822 . "Pass --force if you really want to send.\n"), $f);
827 if (defined $sender) {
828 $sender =~ s/^\s+|\s+$//g;
829 ($sender) = expand_aliases($sender);
831 $sender = $repoauthor || $repocommitter || '';
834 # $sender could be an already sanitized address
835 # (e.g. sendemail.from could be manually sanitized by user).
836 # But it's a no-op to run sanitize_address on an already sanitized address.
837 $sender = sanitize_address($sender);
839 my $to_whom = __("To whom should the emails be sent (if anyone)?");
841 if (!@initial_to && !defined $to_cmd) {
842 my $to = ask("$to_whom ",
844 valid_re => qr/\@.*\./, confirm_only => 1);
845 push @initial_to, parse_address_line($to) if defined $to; # sanitized/validated later
850 return map { expand_one_alias($_) } @_;
853 my %EXPANDED_ALIASES;
854 sub expand_one_alias {
856 if ($EXPANDED_ALIASES{$alias}) {
857 die sprintf(__("fatal: alias '%s' expands to itself\n"), $alias);
859 local $EXPANDED_ALIASES{$alias} = 1;
860 return $aliases{$alias} ? expand_aliases(@{$aliases{$alias}}) : $alias;
863 @initial_to = process_address_list(@initial_to);
864 @initial_cc = process_address_list(@initial_cc);
865 @bcclist = process_address_list(@bcclist);
867 if ($thread && !defined $initial_reply_to && $prompting) {
868 $initial_reply_to = ask(
869 __("Message-ID to be used as In-Reply-To for the first email (if any)? "),
871 valid_re => qr/\@.*\./, confirm_only => 1);
873 if (defined $initial_reply_to) {
874 $initial_reply_to =~ s/^\s*<?//;
875 $initial_reply_to =~ s/>?\s*$//;
876 $initial_reply_to = "<$initial_reply_to>" if $initial_reply_to ne '';
879 if (!defined $smtp_server) {
880 foreach (qw( /usr/sbin/sendmail /usr/lib/sendmail )) {
886 $smtp_server ||= 'localhost'; # could be 127.0.0.1, too... *shrug*
889 if ($compose && $compose > 0) {
890 @files = ($compose_filename . ".final", @files);
893 # Variables we set as part of the loop over files
894 our ($message_id, %mail, $subject, $reply_to, $references, $message,
895 $needs_confirm, $message_num, $ask_default);
897 sub extract_valid_address {
899 my $local_part_regexp = qr/[^<>"\s@]+/;
900 my $domain_regexp = qr/[^.<>"\s@]+(?:\.[^.<>"\s@]+)+/;
902 # check for a local address:
903 return $address if ($address =~ /^($local_part_regexp)$/);
905 $address =~ s/^\s*<(.*)>\s*$/$1/;
906 if ($have_email_valid) {
907 return scalar Email::Valid->address($address);
910 # less robust/correct than the monster regexp in Email::Valid,
911 # but still does a 99% job, and one less dependency
912 return $1 if $address =~ /($local_part_regexp\@$domain_regexp)/;
916 sub extract_valid_address_or_die {
918 $address = extract_valid_address($address);
919 die sprintf(__("error: unable to extract a valid address from: %s\n"), $address)
924 sub validate_address {
926 while (!extract_valid_address($address)) {
927 printf STDERR __("error: unable to extract a valid address from: %s\n"), $address;
928 # TRANSLATORS: Make sure to include [q] [d] [e] in your
929 # translation. The program will only accept English input
931 $_ = ask(__("What to do with this address? ([q]uit|[d]rop|[e]dit): "),
932 valid_re => qr/^(?:quit|q|drop|d|edit|e)/i,
937 cleanup_compose_files();
940 $address = ask("$to_whom ",
942 valid_re => qr/\@.*\./, confirm_only => 1);
947 sub validate_address_list {
948 return (grep { defined $_ }
949 map { validate_address($_) } @_);
952 # Usually don't need to change anything below here.
954 # we make a "fake" message id by taking the current number
955 # of seconds since the beginning of Unix time and tacking on
956 # a random number to the end, in case we are called quicker than
957 # 1 second since the last time we were called.
959 # We'll setup a template for the message id, using the "from" address:
961 my ($message_id_stamp, $message_id_serial);
962 sub make_message_id {
964 if (!defined $message_id_stamp) {
965 $message_id_stamp = strftime("%Y%m%d%H%M%S.$$", gmtime(time));
966 $message_id_serial = 0;
968 $message_id_serial++;
969 $uniq = "$message_id_stamp-$message_id_serial";
972 for ($sender, $repocommitter, $repoauthor) {
973 $du_part = extract_valid_address(sanitize_address($_));
974 last if (defined $du_part and $du_part ne '');
976 if (not defined $du_part or $du_part eq '') {
977 require Sys::Hostname;
978 $du_part = 'user@' . Sys::Hostname::hostname();
980 my $message_id_template = "<%s-%s>";
981 $message_id = sprintf($message_id_template, $uniq, $du_part);
982 #print "new message id = $message_id\n"; # Was useful for debugging
987 $time = time - scalar $#files;
989 sub unquote_rfc2047 {
992 my $sep = qr/[ \t]+/;
993 s{$re_encoded_word(?:$sep$re_encoded_word)*}{
994 my @words = split $sep, $&;
1000 if ($encoding eq 'q' || $encoding eq 'Q') {
1003 s/=([0-9A-F]{2})/chr(hex($1))/egi;
1005 # other encodings not supported yet
1010 return wantarray ? ($_, $charset) : $_;
1015 my $encoding = shift || 'UTF-8';
1016 s/([^-a-zA-Z0-9!*+\/])/sprintf("=%02X", ord($1))/eg;
1017 s/(.*)/=\?$encoding\?q\?$1\?=/;
1021 sub is_rfc2047_quoted {
1024 $s =~ m/^(?:"[[:ascii:]]*"|$re_encoded_word)$/o;
1027 sub subject_needs_rfc2047_quoting {
1030 return ($s =~ /[^[:ascii:]]/) || ($s =~ /=\?/);
1034 local $subject = shift;
1035 my $encoding = shift || 'UTF-8';
1037 if (subject_needs_rfc2047_quoting($subject)) {
1038 return quote_rfc2047($subject, $encoding);
1043 # use the simplest quoting being able to handle the recipient
1044 sub sanitize_address {
1045 my ($recipient) = @_;
1047 # remove garbage after email address
1048 $recipient =~ s/(.*>).*$/$1/;
1050 my ($recipient_name, $recipient_addr) = ($recipient =~ /^(.*?)\s*(<.*)/);
1052 if (not $recipient_name) {
1056 # if recipient_name is already quoted, do nothing
1057 if (is_rfc2047_quoted($recipient_name)) {
1061 # remove non-escaped quotes
1062 $recipient_name =~ s/(^|[^\\])"/$1/g;
1064 # rfc2047 is needed if a non-ascii char is included
1065 if ($recipient_name =~ /[^[:ascii:]]/) {
1066 $recipient_name = quote_rfc2047($recipient_name);
1069 # double quotes are needed if specials or CTLs are included
1070 elsif ($recipient_name =~ /[][()<>@,;:\\".\000-\037\177]/) {
1071 $recipient_name =~ s/([\\\r])/\\$1/g;
1072 $recipient_name = qq["$recipient_name"];
1075 return "$recipient_name $recipient_addr";
1079 sub sanitize_address_list {
1080 return (map { sanitize_address($_) } @_);
1083 sub process_address_list {
1084 my @addr_list = map { parse_address_line($_) } @_;
1085 @addr_list = expand_aliases(@addr_list);
1086 @addr_list = sanitize_address_list(@addr_list);
1087 @addr_list = validate_address_list(@addr_list);
1091 # Returns the local Fully Qualified Domain Name (FQDN) if available.
1093 # Tightly configured MTAa require that a caller sends a real DNS
1094 # domain name that corresponds the IP address in the HELO/EHLO
1095 # handshake. This is used to verify the connection and prevent
1096 # spammers from trying to hide their identity. If the DNS and IP don't
1097 # match, the receiveing MTA may deny the connection.
1099 # Here is a deny example of Net::SMTP with the default "localhost.localdomain"
1101 # Net::SMTP=GLOB(0x267ec28)>>> EHLO localhost.localdomain
1102 # Net::SMTP=GLOB(0x267ec28)<<< 550 EHLO argument does not match calling host
1104 # This maildomain*() code is based on ideas in Perl library Test::Reporter
1105 # /usr/share/perl5/Test/Reporter/Mail/Util.pm ==> sub _maildomain ()
1109 return defined $domain && !($^O eq 'darwin' && $domain =~ /\.local$/) && $domain =~ /\./;
1112 sub maildomain_net {
1115 if (eval { require Net::Domain; 1 }) {
1116 my $domain = Net::Domain::domainname();
1117 $maildomain = $domain if valid_fqdn($domain);
1123 sub maildomain_mta {
1126 if (eval { require Net::SMTP; 1 }) {
1127 for my $host (qw(mailhost localhost)) {
1128 my $smtp = Net::SMTP->new($host);
1129 if (defined $smtp) {
1130 my $domain = $smtp->domain;
1133 $maildomain = $domain if valid_fqdn($domain);
1135 last if $maildomain;
1144 return maildomain_net() || maildomain_mta() || 'localhost.localdomain';
1147 sub smtp_host_string {
1148 if (defined $smtp_server_port) {
1149 return "$smtp_server:$smtp_server_port";
1151 return $smtp_server;
1155 # Returns 1 if authentication succeeded or was not necessary
1156 # (smtp_user was not specified), and 0 otherwise.
1158 sub smtp_auth_maybe {
1159 if (!defined $smtp_authuser || $auth) {
1163 # Workaround AUTH PLAIN/LOGIN interaction defect
1164 # with Authen::SASL::Cyrus
1166 require Authen::SASL;
1167 Authen::SASL->import(qw(Perl));
1170 # Check mechanism naming as defined in:
1171 # https://tools.ietf.org/html/rfc4422#page-8
1172 if ($smtp_auth && $smtp_auth !~ /^(\b[A-Z0-9-_]{1,20}\s*)*$/) {
1173 die "invalid smtp auth: '${smtp_auth}'";
1176 # TODO: Authentication may fail not because credentials were
1177 # invalid but due to other reasons, in which we should not
1178 # reject credentials.
1179 $auth = Git::credential({
1180 'protocol' => 'smtp',
1181 'host' => smtp_host_string(),
1182 'username' => $smtp_authuser,
1183 # if there's no password, "git credential fill" will
1184 # give us one, otherwise it'll just pass this one.
1185 'password' => $smtp_authpass
1190 my $sasl = Authen::SASL->new(
1191 mechanism => $smtp_auth,
1193 user => $cred->{'username'},
1194 pass => $cred->{'password'},
1195 authname => $cred->{'username'},
1199 return !!$smtp->auth($sasl);
1202 return !!$smtp->auth($cred->{'username'}, $cred->{'password'});
1208 sub ssl_verify_params {
1210 require IO::Socket::SSL;
1211 IO::Socket::SSL->import(qw/SSL_VERIFY_PEER SSL_VERIFY_NONE/);
1214 print STDERR "Not using SSL_VERIFY_PEER due to out-of-date IO::Socket::SSL.\n";
1218 if (!defined $smtp_ssl_cert_path) {
1219 # use the OpenSSL defaults
1220 return (SSL_verify_mode => SSL_VERIFY_PEER());
1223 if ($smtp_ssl_cert_path eq "") {
1224 return (SSL_verify_mode => SSL_VERIFY_NONE());
1225 } elsif (-d $smtp_ssl_cert_path) {
1226 return (SSL_verify_mode => SSL_VERIFY_PEER(),
1227 SSL_ca_path => $smtp_ssl_cert_path);
1228 } elsif (-f $smtp_ssl_cert_path) {
1229 return (SSL_verify_mode => SSL_VERIFY_PEER(),
1230 SSL_ca_file => $smtp_ssl_cert_path);
1232 die sprintf(__("CA path \"%s\" does not exist"), $smtp_ssl_cert_path);
1236 sub file_name_is_absolute {
1239 # msys does not grok DOS drive-prefixes
1240 if ($^O eq 'msys') {
1241 return ($path =~ m#^/# || $path =~ m#^[a-zA-Z]\:#)
1244 require File::Spec::Functions;
1245 return File::Spec::Functions::file_name_is_absolute($path);
1248 # Returns 1 if the message was sent, and 0 otherwise.
1249 # In actuality, the whole program dies when there
1250 # is an error sending a message.
1253 my @recipients = unique_email_list(@to);
1254 @cc = (grep { my $cc = extract_valid_address_or_die($_);
1255 not grep { $cc eq $_ || $_ =~ /<\Q${cc}\E>$/ } @recipients
1258 my $to = join (",\n\t", @recipients);
1259 @recipients = unique_email_list(@recipients,@cc,@bcclist);
1260 @recipients = (map { extract_valid_address_or_die($_) } @recipients);
1261 my $date = format_2822_time($time++);
1262 my $gitversion = '@@GIT_VERSION@@';
1263 if ($gitversion =~ m/..GIT_VERSION../) {
1264 $gitversion = Git::version();
1267 my $cc = join(",\n\t", unique_email_list(@cc));
1270 $ccline = "\nCc: $cc";
1272 make_message_id() unless defined($message_id);
1274 my $header = "From: $sender
1278 Message-Id: $message_id
1281 $header .= "X-Mailer: git-send-email $gitversion\n";
1285 $header .= "In-Reply-To: $reply_to\n";
1286 $header .= "References: $references\n";
1289 $header .= join("\n", @xh) . "\n";
1292 my @sendmail_parameters = ('-i', @recipients);
1293 my $raw_from = $sender;
1294 if (defined $envelope_sender && $envelope_sender ne "auto") {
1295 $raw_from = $envelope_sender;
1297 $raw_from = extract_valid_address($raw_from);
1298 unshift (@sendmail_parameters,
1299 '-f', $raw_from) if(defined $envelope_sender);
1301 if ($needs_confirm && !$dry_run) {
1302 print "\n$header\n";
1303 if ($needs_confirm eq "inform") {
1304 $confirm_unconfigured = 0; # squelch this message for the rest of this run
1305 $ask_default = "y"; # assume yes on EOF since user hasn't explicitly asked for confirmation
1307 The Cc list above has been expanded by additional
1308 addresses found in the patch commit message. By default
1309 send-email prompts before sending whenever this occurs.
1310 This behavior is controlled by the sendemail.confirm
1311 configuration setting.
1313 For additional information, run 'git send-email --help'.
1314 To retain the current behavior, but squelch this message,
1315 run 'git config --global sendemail.confirm auto'.
1319 # TRANSLATORS: Make sure to include [y] [n] [q] [a] in your
1320 # translation. The program will only accept English input
1322 $_ = ask(__("Send this email? ([y]es|[n]o|[q]uit|[a]ll): "),
1323 valid_re => qr/^(?:yes|y|no|n|quit|q|all|a)/i,
1324 default => $ask_default);
1325 die __("Send this email reply required") unless defined $_;
1329 cleanup_compose_files();
1336 unshift (@sendmail_parameters, @smtp_server_options);
1339 # We don't want to send the email.
1340 } elsif (file_name_is_absolute($smtp_server)) {
1341 my $pid = open my $sm, '|-';
1342 defined $pid or die $!;
1344 exec($smtp_server, @sendmail_parameters) or die $!;
1346 print $sm "$header\n$message";
1347 close $sm or die $!;
1350 if (!defined $smtp_server) {
1351 die __("The required SMTP server is not properly defined.")
1354 if ($smtp_encryption eq 'ssl') {
1355 $smtp_server_port ||= 465; # ssmtp
1356 require Net::SMTP::SSL;
1357 $smtp_domain ||= maildomain();
1358 require IO::Socket::SSL;
1360 # Suppress "variable accessed once" warning.
1363 $IO::Socket::SSL::DEBUG = 1;
1366 # Net::SMTP::SSL->new() does not forward any SSL options
1367 IO::Socket::SSL::set_client_defaults(
1368 ssl_verify_params());
1369 $smtp ||= Net::SMTP::SSL->new($smtp_server,
1370 Hello => $smtp_domain,
1371 Port => $smtp_server_port,
1372 Debug => $debug_net_smtp);
1376 $smtp_domain ||= maildomain();
1377 $smtp_server_port ||= 25;
1378 $smtp ||= Net::SMTP->new($smtp_server,
1379 Hello => $smtp_domain,
1380 Debug => $debug_net_smtp,
1381 Port => $smtp_server_port);
1382 if ($smtp_encryption eq 'tls' && $smtp) {
1383 require Net::SMTP::SSL;
1384 $smtp->command('STARTTLS');
1386 if ($smtp->code == 220) {
1387 $smtp = Net::SMTP::SSL->start_SSL($smtp,
1388 ssl_verify_params())
1389 or die "STARTTLS failed! ".IO::Socket::SSL::errstr();
1390 $smtp_encryption = '';
1391 # Send EHLO again to receive fresh
1392 # supported commands
1393 $smtp->hello($smtp_domain);
1395 die sprintf(__("Server does not support STARTTLS! %s"), $smtp->message);
1401 die __("Unable to initialize SMTP properly. Check config and use --smtp-debug."),
1402 " VALUES: server=$smtp_server ",
1403 "encryption=$smtp_encryption ",
1404 "hello=$smtp_domain",
1405 defined $smtp_server_port ? " port=$smtp_server_port" : "";
1408 smtp_auth_maybe or die $smtp->message;
1410 $smtp->mail( $raw_from ) or die $smtp->message;
1411 $smtp->to( @recipients ) or die $smtp->message;
1412 $smtp->data or die $smtp->message;
1413 $smtp->datasend("$header\n") or die $smtp->message;
1414 my @lines = split /^/, $message;
1415 foreach my $line (@lines) {
1416 $smtp->datasend("$line") or die $smtp->message;
1418 $smtp->dataend() or die $smtp->message;
1419 $smtp->code =~ /250|200/ or die sprintf(__("Failed to send %s\n"), $subject).$smtp->message;
1422 printf($dry_run ? __("Dry-Sent %s\n") : __("Sent %s\n"), $subject);
1424 print($dry_run ? __("Dry-OK. Log says:\n") : __("OK. Log says:\n"));
1425 if (!file_name_is_absolute($smtp_server)) {
1426 print "Server: $smtp_server\n";
1427 print "MAIL FROM:<$raw_from>\n";
1428 foreach my $entry (@recipients) {
1429 print "RCPT TO:<$entry>\n";
1432 print "Sendmail: $smtp_server ".join(' ',@sendmail_parameters)."\n";
1434 print $header, "\n";
1436 print __("Result: "), $smtp->code, ' ',
1437 ($smtp->message =~ /\n([^\n]+\n)$/s), "\n";
1439 print __("Result: OK\n");
1446 $reply_to = $initial_reply_to;
1447 $references = $initial_reply_to || '';
1448 $subject = $initial_subject;
1451 foreach my $t (@files) {
1452 open my $fh, "<", $t or die sprintf(__("can't open file %s"), $t);
1455 my $sauthor = undef;
1456 my $author_encoding;
1457 my $has_content_type;
1460 my $has_mime_version;
1464 my $input_format = undef;
1468 # First unfold multiline header fields
1471 if (/^\s+\S/ and @header) {
1472 chomp($header[$#header]);
1474 $header[$#header] .= $_;
1479 # Now parse the header
1482 $input_format = 'mbox';
1486 if (!defined $input_format && /^[-A-Za-z]+:\s/) {
1487 $input_format = 'mbox';
1490 if (defined $input_format && $input_format eq 'mbox') {
1491 if (/^Subject:\s+(.*)$/i) {
1494 elsif (/^From:\s+(.*)$/i) {
1495 ($author, $author_encoding) = unquote_rfc2047($1);
1496 $sauthor = sanitize_address($author);
1497 next if $suppress_cc{'author'};
1498 next if $suppress_cc{'self'} and $sauthor eq $sender;
1499 printf(__("(mbox) Adding cc: %s from line '%s'\n"),
1500 $1, $_) unless $quiet;
1503 elsif (/^To:\s+(.*)$/i) {
1504 foreach my $addr (parse_address_line($1)) {
1505 printf(__("(mbox) Adding to: %s from line '%s'\n"),
1506 $addr, $_) unless $quiet;
1510 elsif (/^Cc:\s+(.*)$/i) {
1511 foreach my $addr (parse_address_line($1)) {
1512 my $qaddr = unquote_rfc2047($addr);
1513 my $saddr = sanitize_address($qaddr);
1514 if ($saddr eq $sender) {
1515 next if ($suppress_cc{'self'});
1517 next if ($suppress_cc{'cc'});
1519 printf(__("(mbox) Adding cc: %s from line '%s'\n"),
1520 $addr, $_) unless $quiet;
1524 elsif (/^Content-type:/i) {
1525 $has_content_type = 1;
1526 if (/charset="?([^ "]+)/) {
1527 $body_encoding = $1;
1531 elsif (/^MIME-Version/i) {
1532 $has_mime_version = 1;
1535 elsif (/^Message-Id: (.*)/i) {
1538 elsif (/^Content-Transfer-Encoding: (.*)/i) {
1539 $xfer_encoding = $1 if not defined $xfer_encoding;
1541 elsif (!/^Date:\s/i && /^[-A-Za-z]+:\s+\S/) {
1546 # In the traditional
1547 # "send lots of email" format,
1550 # So let's support that, too.
1551 $input_format = 'lots';
1552 if (@cc == 0 && !$suppress_cc{'cc'}) {
1553 printf(__("(non-mbox) Adding cc: %s from line '%s'\n"),
1554 $_, $_) unless $quiet;
1556 } elsif (!defined $subject) {
1561 # Now parse the message body
1564 if (/^(Signed-off-by|Cc): (.*)$/i) {
1566 my ($what, $c) = ($1, $2);
1568 my $sc = sanitize_address($c);
1569 if ($sc eq $sender) {
1570 next if ($suppress_cc{'self'});
1572 next if $suppress_cc{'sob'} and $what =~ /Signed-off-by/i;
1573 next if $suppress_cc{'bodycc'} and $what =~ /Cc/i;
1576 printf(__("(body) Adding cc: %s from line '%s'\n"),
1577 $c, $_) unless $quiet;
1582 push @to, recipients_cmd("to-cmd", "to", $to_cmd, $t)
1584 push @cc, recipients_cmd("cc-cmd", "cc", $cc_cmd, $t)
1585 if defined $cc_cmd && !$suppress_cc{'cccmd'};
1587 if ($broken_encoding{$t} && !$has_content_type) {
1588 $xfer_encoding = '8bit' if not defined $xfer_encoding;
1589 $has_content_type = 1;
1590 push @xh, "Content-Type: text/plain; charset=$auto_8bit_encoding";
1591 $body_encoding = $auto_8bit_encoding;
1594 if ($broken_encoding{$t} && !is_rfc2047_quoted($subject)) {
1595 $subject = quote_subject($subject, $auto_8bit_encoding);
1598 if (defined $sauthor and $sauthor ne $sender) {
1599 $message = "From: $author\n\n$message";
1600 if (defined $author_encoding) {
1601 if ($has_content_type) {
1602 if ($body_encoding eq $author_encoding) {
1603 # ok, we already have the right encoding
1606 # uh oh, we should re-encode
1610 $xfer_encoding = '8bit' if not defined $xfer_encoding;
1611 $has_content_type = 1;
1613 "Content-Type: text/plain; charset=$author_encoding";
1617 if (defined $target_xfer_encoding) {
1618 $xfer_encoding = '8bit' if not defined $xfer_encoding;
1619 $message = apply_transfer_encoding(
1620 $message, $xfer_encoding, $target_xfer_encoding);
1621 $xfer_encoding = $target_xfer_encoding;
1623 if (defined $xfer_encoding) {
1624 push @xh, "Content-Transfer-Encoding: $xfer_encoding";
1626 if (defined $xfer_encoding or $has_content_type) {
1627 unshift @xh, 'MIME-Version: 1.0' unless $has_mime_version;
1631 $confirm eq "always" or
1632 ($confirm =~ /^(?:auto|cc)$/ && @cc) or
1633 ($confirm =~ /^(?:auto|compose)$/ && $compose && $message_num == 1));
1634 $needs_confirm = "inform" if ($needs_confirm && $confirm_unconfigured && @cc);
1636 @to = process_address_list(@to);
1637 @cc = process_address_list(@cc);
1639 @to = (@initial_to, @to);
1640 @cc = (@initial_cc, @cc);
1642 if ($message_num == 1) {
1643 if (defined $cover_cc and $cover_cc) {
1646 if (defined $cover_to and $cover_to) {
1651 my $message_was_sent = send_message();
1653 # set up for the next message
1654 if ($thread && $message_was_sent &&
1655 ($chain_reply_to || !defined $reply_to || length($reply_to) == 0 ||
1656 $message_num == 1)) {
1657 $reply_to = $message_id;
1658 if (length $references > 0) {
1659 $references .= "\n $message_id";
1661 $references = "$message_id";
1664 $message_id = undef;
1667 # Execute a command (e.g. $to_cmd) to get a list of email addresses
1668 # and return a results array
1669 sub recipients_cmd {
1670 my ($prefix, $what, $cmd, $file) = @_;
1673 open my $fh, "-|", "$cmd \Q$file\E"
1674 or die sprintf(__("(%s) Could not execute '%s'"), $prefix, $cmd);
1675 while (my $address = <$fh>) {
1676 $address =~ s/^\s*//g;
1677 $address =~ s/\s*$//g;
1678 $address = sanitize_address($address);
1679 next if ($address eq $sender and $suppress_cc{'self'});
1680 push @addresses, $address;
1681 printf(__("(%s) Adding %s: %s from: '%s'\n"),
1682 $prefix, $what, $address, $cmd) unless $quiet;
1685 or die sprintf(__("(%s) failed to close pipe to '%s'"), $prefix, $cmd);
1689 cleanup_compose_files();
1691 sub cleanup_compose_files {
1692 unlink($compose_filename, $compose_filename . ".final") if $compose;
1695 $smtp->quit if $smtp;
1697 sub apply_transfer_encoding {
1698 my $message = shift;
1702 return $message if ($from eq $to and $from ne '7bit');
1704 require MIME::QuotedPrint;
1705 require MIME::Base64;
1707 $message = MIME::QuotedPrint::decode($message)
1708 if ($from eq 'quoted-printable');
1709 $message = MIME::Base64::decode($message)
1710 if ($from eq 'base64');
1712 die __("cannot send message as 7bit")
1713 if ($to eq '7bit' and $message =~ /[^[:ascii:]]/);
1715 if ($to eq '7bit' or $to eq '8bit');
1716 return MIME::QuotedPrint::encode($message, "\n", 0)
1717 if ($to eq 'quoted-printable');
1718 return MIME::Base64::encode($message, "\n")
1719 if ($to eq 'base64');
1720 die __("invalid transfer encoding");
1723 sub unique_email_list {
1727 foreach my $entry (@_) {
1728 my $clean = extract_valid_address_or_die($entry);
1729 $seen{$clean} ||= 0;
1730 next if $seen{$clean}++;
1731 push @emails, $entry;
1736 sub validate_patch {
1738 open(my $fh, '<', $fn)
1739 or die sprintf(__("unable to open %s: %s\n"), $fn, $!);
1740 while (my $line = <$fh>) {
1741 if (length($line) > 998) {
1742 return sprintf(__("%s: patch contains a line longer than 998 characters"), $.);
1749 my ($last, $lastlen, $file, $known_suffix) = @_;
1750 my ($suffix, $skip);
1753 if (defined $last &&
1754 ($lastlen < length($file)) &&
1755 (substr($file, 0, $lastlen) eq $last) &&
1756 ($suffix = substr($file, $lastlen)) !~ /^[a-z0-9]/i) {
1757 if (defined $known_suffix && $suffix eq $known_suffix) {
1758 printf(__("Skipping %s with backup suffix '%s'.\n"), $file, $known_suffix);
1761 # TRANSLATORS: please keep "[y|N]" as is.
1762 my $answer = ask(sprintf(__("Do you really want to send %s? [y|N]: "), $file),
1763 valid_re => qr/^(?:y|n)/i,
1765 $skip = ($answer ne 'y');
1767 $known_suffix = $suffix;
1771 return ($skip, $known_suffix);
1774 sub handle_backup_files {
1776 my ($last, $lastlen, $known_suffix, $skip, @result);
1777 for my $file (@file) {
1778 ($skip, $known_suffix) = handle_backup($last, $lastlen,
1779 $file, $known_suffix);
1780 push @result, $file unless $skip;
1782 $lastlen = length($file);
1787 sub file_has_nonascii {
1789 open(my $fh, '<', $fn)
1790 or die sprintf(__("unable to open %s: %s\n"), $fn, $!);
1791 while (my $line = <$fh>) {
1792 return 1 if $line =~ /[^[:ascii:]]/;
1797 sub body_or_subject_has_nonascii {
1799 open(my $fh, '<', $fn)
1800 or die sprintf(__("unable to open %s: %s\n"), $fn, $!);
1801 while (my $line = <$fh>) {
1802 last if $line =~ /^$/;
1803 return 1 if $line =~ /^Subject.*[^[:ascii:]]/;
1805 while (my $line = <$fh>) {
1806 return 1 if $line =~ /[^[:ascii:]]/;