diff options
Diffstat (limited to 'git-send-email.perl')
-rwxr-xr-x | git-send-email.perl | 152 |
1 files changed, 131 insertions, 21 deletions
diff --git a/git-send-email.perl b/git-send-email.perl index 9949db01e1..ae9f8698c5 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -54,10 +54,12 @@ git send-email [options] <file | directory | rev-list options > --[no-]bcc <str> * Email Bcc: --subject <str> * Email "Subject:" --in-reply-to <str> * Email "In-Reply-To:" + --[no-]xmailer * Add "X-Mailer:" header (default). --[no-]annotate * Review each patch that will be sent in an editor. --compose * Open an editor for introduction. --compose-encoding <str> * Encoding to assume for introduction. --8bit-encoding <str> * Encoding to assume 8bit mails if undeclared + --transfer-encoding <str> * Transfer encoding to use (quoted-printable, 8bit, base64) Sending: --envelope-sender <str> * Email envelope sender. @@ -145,10 +147,15 @@ my $have_mail_address = eval { require Mail::Address; 1 }; my $smtp; my $auth; +# Regexes for RFC 2047 productions. +my $re_token = qr/[^][()<>@,;:\\"\/?.= \000-\037\177-\377]+/; +my $re_encoded_text = qr/[^? \000-\037\177-\377]+/; +my $re_encoded_word = qr/=\?($re_token)\?($re_token)\?($re_encoded_text)\?=/; + # Variables we fill in automatically, or via prompting: my (@to,$no_to,@initial_to,@cc,$no_cc,@initial_cc,@bcclist,$no_bcc,@xh, $initial_reply_to,$initial_subject,@files, - $author,$sender,$smtp_authpass,$annotate,$compose,$time); + $author,$sender,$smtp_authpass,$annotate,$use_xmailer,$compose,$time); my $envelope_sender; @@ -206,6 +213,7 @@ my ($validate, $confirm); my (@suppress_cc); my ($auto_8bit_encoding); my ($compose_encoding); +my ($target_xfer_encoding); my ($debug_net_smtp) = 0; # Net::SMTP, see send_message() @@ -219,7 +227,8 @@ my %config_bool_settings = ( "signedoffcc" => [\$signed_off_by_cc, undef], # Deprecated "validate" => [\$validate, 1], "multiedit" => [\$multiedit, undef], - "annotate" => [\$annotate, undef] + "annotate" => [\$annotate, undef], + "xmailer" => [\$use_xmailer, 1] ); my %config_settings = ( @@ -242,6 +251,7 @@ my %config_settings = ( "from" => \$sender, "assume8bitencoding" => \$auto_8bit_encoding, "composeencoding" => \$compose_encoding, + "transferencoding" => \$target_xfer_encoding, ); my %config_path_settings = ( @@ -289,6 +299,7 @@ my $rc = GetOptions("h" => \$help, "bcc=s" => \@bcclist, "no-bcc" => \$no_bcc, "chain-reply-to!" => \$chain_reply_to, + "no-chain-reply-to" => sub {$chain_reply_to = 0}, "smtp-server=s" => \$smtp_server, "smtp-server-option=s" => \@smtp_server_options, "smtp-server-port=s" => \$smtp_server_port, @@ -301,23 +312,34 @@ my $rc = GetOptions("h" => \$help, "smtp-domain:s" => \$smtp_domain, "identity=s" => \$identity, "annotate!" => \$annotate, + "no-annotate" => sub {$annotate = 0}, "compose" => \$compose, "quiet" => \$quiet, "cc-cmd=s" => \$cc_cmd, "suppress-from!" => \$suppress_from, + "no-suppress-from" => sub {$suppress_from = 0}, "suppress-cc=s" => \@suppress_cc, "signed-off-cc|signed-off-by-cc!" => \$signed_off_by_cc, + "no-signed-off-cc|no-signed-off-by-cc" => sub {$signed_off_by_cc = 0}, "cc-cover|cc-cover!" => \$cover_cc, + "no-cc-cover" => sub {$cover_cc = 0}, "to-cover|to-cover!" => \$cover_to, + "no-to-cover" => sub {$cover_to = 0}, "confirm=s" => \$confirm, "dry-run" => \$dry_run, "envelope-sender=s" => \$envelope_sender, "thread!" => \$thread, + "no-thread" => sub {$thread = 0}, "validate!" => \$validate, + "no-validate" => sub {$validate = 0}, + "transfer-encoding=s" => \$target_xfer_encoding, "format-patch!" => \$format_patch, + "no-format-patch" => sub {$format_patch = 0}, "8bit-encoding=s" => \$auto_8bit_encoding, "compose-encoding=s" => \$compose_encoding, "force" => \$force, + "xmailer!" => \$use_xmailer, + "no-xmailer" => sub {$use_xmailer = 0}, ); usage() if $help; @@ -465,6 +487,37 @@ sub split_addrs { } my %aliases; + +sub parse_sendmail_alias { + local $_ = shift; + if (/"/) { + print STDERR "warning: sendmail alias with quotes is not supported: $_\n"; + } elsif (/:include:/) { + print STDERR "warning: `:include:` not supported: $_\n"; + } elsif (/[\/|]/) { + print STDERR "warning: `/file` or `|pipe` redirection not supported: $_\n"; + } elsif (/^(\S+?)\s*:\s*(.+)$/) { + my ($alias, $addr) = ($1, $2); + $aliases{$alias} = [ split_addrs($addr) ]; + } else { + print STDERR "warning: sendmail line is not recognized: $_\n"; + } +} + +sub parse_sendmail_aliases { + my $fh = shift; + my $s = ''; + while (<$fh>) { + chomp; + next if /^\s*$/ || /^\s*#/; + $s .= $_, next if $s =~ s/\\$// || s/^\s+//; + parse_sendmail_alias($s) if $s; + $s = $_; + } + $s =~ s/\\$//; # silently tolerate stray '\' on last line + parse_sendmail_alias($s) if $s; +} + my %parse_alias = ( # multiline formats can be supported in the future mutt => sub { my $fh = shift; while (<$fh>) { @@ -493,7 +546,7 @@ my %parse_alias = ( $aliases{$alias} = [ split_addrs($addr) ]; } } }, - + sendmail => \&parse_sendmail_aliases, gnus => sub { my $fh = shift; while (<$fh>) { if (/\(define-mail-alias\s+"(\S+?)"\s+"(\S+?)"\)/) { $aliases{$1} = [ $2 ]; @@ -740,6 +793,7 @@ if (!defined $auto_8bit_encoding && scalar %broken_encoding) { print " $f\n"; } $auto_8bit_encoding = ask("Which 8bit encoding should I declare [UTF-8]? ", + valid_re => qr/.{4}/, confirm_only => 1, default => "UTF-8"); } @@ -913,15 +967,26 @@ $time = time - scalar $#files; sub unquote_rfc2047 { local ($_) = @_; - my $encoding; - s{=\?([^?]+)\?q\?(.*?)\?=}{ - $encoding = $1; - my $e = $2; - $e =~ s/_/ /g; - $e =~ s/=([0-9A-F]{2})/chr(hex($1))/eg; - $e; + my $charset; + my $sep = qr/[ \t]+/; + s{$re_encoded_word(?:$sep$re_encoded_word)*}{ + my @words = split $sep, $&; + foreach (@words) { + m/$re_encoded_word/; + $charset = $1; + my $encoding = $2; + my $text = $3; + if ($encoding eq 'q' || $encoding eq 'Q') { + $_ = $text; + s/_/ /g; + s/=([0-9A-F]{2})/chr(hex($1))/egi; + } else { + # other encodings not supported yet + } + } + join '', @words; }eg; - return wantarray ? ($_, $encoding) : $_; + return wantarray ? ($_, $charset) : $_; } sub quote_rfc2047 { @@ -934,10 +999,8 @@ sub quote_rfc2047 { sub is_rfc2047_quoted { my $s = shift; - my $token = qr/[^][()<>@,;:"\/?.= \000-\037\177-\377]+/; - my $encoded_text = qr/[!->@-~]+/; length($s) <= 75 && - $s =~ m/^(?:"[[:ascii:]]*"|=\?$token\?$token\?$encoded_text\?=)$/o; + $s =~ m/^(?:"[[:ascii:]]*"|$re_encoded_word)$/o; } sub subject_needs_rfc2047_quoting { @@ -1163,8 +1226,10 @@ To: $to${ccline} Subject: $subject Date: $date Message-Id: $message_id -X-Mailer: git-send-email $gitversion "; + if ($use_xmailer) { + $header .= "X-Mailer: git-send-email $gitversion\n"; + } if ($reply_to) { $header .= "In-Reply-To: $reply_to\n"; @@ -1324,6 +1389,8 @@ foreach my $t (@files) { my $author_encoding; my $has_content_type; my $body_encoding; + my $xfer_encoding; + my $has_mime_version; @to = (); @cc = (); @xh = (); @@ -1394,9 +1461,16 @@ foreach my $t (@files) { } push @xh, $_; } + elsif (/^MIME-Version/i) { + $has_mime_version = 1; + push @xh, $_; + } elsif (/^Message-Id: (.*)/i) { $message_id = $1; } + elsif (/^Content-Transfer-Encoding: (.*)/i) { + $xfer_encoding = $1 if not defined $xfer_encoding; + } elsif (!/^Date:\s/i && /^[-A-Za-z]+:\s+\S/) { push @xh, $_; } @@ -1444,10 +1518,9 @@ foreach my $t (@files) { if defined $cc_cmd && !$suppress_cc{'cccmd'}; if ($broken_encoding{$t} && !$has_content_type) { + $xfer_encoding = '8bit' if not defined $xfer_encoding; $has_content_type = 1; - push @xh, "MIME-Version: 1.0", - "Content-Type: text/plain; charset=$auto_8bit_encoding", - "Content-Transfer-Encoding: 8bit"; + push @xh, "Content-Type: text/plain; charset=$auto_8bit_encoding"; $body_encoding = $auto_8bit_encoding; } @@ -1467,14 +1540,25 @@ foreach my $t (@files) { } } else { + $xfer_encoding = '8bit' if not defined $xfer_encoding; $has_content_type = 1; push @xh, - 'MIME-Version: 1.0', - "Content-Type: text/plain; charset=$author_encoding", - 'Content-Transfer-Encoding: 8bit'; + "Content-Type: text/plain; charset=$author_encoding"; } } } + if (defined $target_xfer_encoding) { + $xfer_encoding = '8bit' if not defined $xfer_encoding; + $message = apply_transfer_encoding( + $message, $xfer_encoding, $target_xfer_encoding); + $xfer_encoding = $target_xfer_encoding; + } + if (defined $xfer_encoding) { + push @xh, "Content-Transfer-Encoding: $xfer_encoding"; + } + if (defined $xfer_encoding or $has_content_type) { + unshift @xh, 'MIME-Version: 1.0' unless $has_mime_version; + } $needs_confirm = ( $confirm eq "always" or @@ -1543,6 +1627,32 @@ sub cleanup_compose_files { $smtp->quit if $smtp; +sub apply_transfer_encoding { + my $message = shift; + my $from = shift; + my $to = shift; + + return $message if ($from eq $to and $from ne '7bit'); + + require MIME::QuotedPrint; + require MIME::Base64; + + $message = MIME::QuotedPrint::decode($message) + if ($from eq 'quoted-printable'); + $message = MIME::Base64::decode($message) + if ($from eq 'base64'); + + die "cannot send message as 7bit" + if ($to eq '7bit' and $message =~ /[^[:ascii:]]/); + return $message + if ($to eq '7bit' or $to eq '8bit'); + return MIME::QuotedPrint::encode($message, "\n", 0) + if ($to eq 'quoted-printable'); + return MIME::Base64::encode($message, "\n") + if ($to eq 'base64'); + die "invalid transfer encoding"; +} + sub unique_email_list { my %seen; my @emails; |