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