Merge branch 'nz/send-email-headers-are-case-insensitive' into maint
authorJunio C Hamano <gitster@pobox.com>
Mon, 21 Jan 2013 01:22:49 +0000 (17:22 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 21 Jan 2013 01:22:49 +0000 (17:22 -0800)
When users spell "cc:" in lowercase in the fake "header" in the
trailer part, "git send-email" failed to pick up the addresses from
there. As e-mail headers field names are case insensitive, this
script should follow suit and treat "cc:" and "Cc:" the same way.

* nz/send-email-headers-are-case-insensitive:
git-send-email: treat field names as case-insensitively

1  2 
git-send-email.perl
diff --combined git-send-email.perl
index 94c7f76a156f1eaa4c622db267710f2982cf7826,7198d2ec9ac3ae3852decc9eba994a03f50294c3..be809e5b59394a4fd2f4f2b73cad6bac47a39600
@@@ -56,7 -56,6 +56,7 @@@ git send-email [options] <file | direct
      --in-reply-to           <str>  * Email "In-Reply-To:"
      --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
  
    Sending:
@@@ -199,7 -198,6 +199,7 @@@ my ($identity, $aliasfiletype, @alias_f
  my ($validate, $confirm);
  my (@suppress_cc);
  my ($auto_8bit_encoding);
 +my ($compose_encoding);
  
  my ($debug_net_smtp) = 0;             # Net::SMTP, see send_message()
  
@@@ -233,7 -231,6 +233,7 @@@ my %config_settings = 
      "confirm"   => \$confirm,
      "from" => \$sender,
      "assume8bitencoding" => \$auto_8bit_encoding,
 +    "composeencoding" => \$compose_encoding,
  );
  
  my %config_path_settings = (
@@@ -318,7 -315,6 +318,7 @@@ my $rc = GetOptions("h" => \$help
                    "validate!" => \$validate,
                    "format-patch!" => \$format_patch,
                    "8bit-encoding=s" => \$auto_8bit_encoding,
 +                  "compose-encoding=s" => \$compose_encoding,
                    "force" => \$force,
         );
  
@@@ -636,9 -632,6 +636,9 @@@ EO
        my $need_8bit_cte = file_has_nonascii($compose_filename);
        my $in_body = 0;
        my $summary_empty = 1;
 +      if (!defined $compose_encoding) {
 +              $compose_encoding = "UTF-8";
 +      }
        while(<$c>) {
                next if m/^GIT:/;
                if ($in_body) {
                        if ($need_8bit_cte) {
                                print $c2 "MIME-Version: 1.0\n",
                                         "Content-Type: text/plain; ",
 -                                         "charset=UTF-8\n",
 +                                         "charset=$compose_encoding\n",
                                         "Content-Transfer-Encoding: 8bit\n";
                        }
                } elsif (/^MIME-Version:/i) {
                        $initial_subject = $1;
                        my $subject = $initial_subject;
                        $_ = "Subject: " .
 -                              ($subject =~ /[^[:ascii:]]/ ?
 -                               quote_rfc2047($subject) :
 -                               $subject) .
 +                              quote_subject($subject, $compose_encoding) .
                                "\n";
                } elsif (/^In-Reply-To:\s*(.+)\s*$/i) {
                        $initial_reply_to = $1;
@@@ -753,11 -748,16 +753,11 @@@ if (!$force) 
        }
  }
  
 -my $prompting = 0;
  if (!defined $sender) {
        $sender = $repoauthor || $repocommitter || '';
 -      $sender = ask("Who should the emails appear to be from? [$sender] ",
 -                    default => $sender,
 -                    valid_re => qr/\@.*\./, confirm_only => 1);
 -      print "Emails will be sent from: ", $sender, "\n";
 -      $prompting++;
  }
  
 +my $prompting = 0;
  if (!@initial_to && !defined $to_cmd) {
        my $to = ask("Who should the emails be sent to (if any)? ",
                     default => "",
@@@ -781,11 -781,9 +781,11 @@@ sub expand_one_alias 
  }
  
  @initial_to = expand_aliases(@initial_to);
 -@initial_to = (map { sanitize_address($_) } @initial_to);
 +@initial_to = validate_address_list(sanitize_address_list(@initial_to));
  @initial_cc = expand_aliases(@initial_cc);
 +@initial_cc = validate_address_list(sanitize_address_list(@initial_cc));
  @bcclist = expand_aliases(@bcclist);
 +@bcclist = validate_address_list(sanitize_address_list(@bcclist));
  
  if ($thread && !defined $initial_reply_to && $prompting) {
        $initial_reply_to = ask(
@@@ -828,45 -826,12 +828,45 @@@ sub extract_valid_address 
        $address =~ s/^\s*<(.*)>\s*$/$1/;
        if ($have_email_valid) {
                return scalar Email::Valid->address($address);
 -      } else {
 -              # less robust/correct than the monster regexp in Email::Valid,
 -              # but still does a 99% job, and one less dependency
 -              $address =~ /($local_part_regexp\@$domain_regexp)/;
 -              return $1;
        }
 +
 +      # less robust/correct than the monster regexp in Email::Valid,
 +      # but still does a 99% job, and one less dependency
 +      return $1 if $address =~ /($local_part_regexp\@$domain_regexp)/;
 +      return undef;
 +}
 +
 +sub extract_valid_address_or_die {
 +      my $address = shift;
 +      $address = extract_valid_address($address);
 +      die "error: unable to extract a valid address from: $address\n"
 +              if !$address;
 +      return $address;
 +}
 +
 +sub validate_address {
 +      my $address = shift;
 +      while (!extract_valid_address($address)) {
 +              print STDERR "error: unable to extract a valid address from: $address\n";
 +              $_ = ask("What to do with this address? ([q]uit|[d]rop|[e]dit): ",
 +                      valid_re => qr/^(?:quit|q|drop|d|edit|e)/i,
 +                      default => 'q');
 +              if (/^d/i) {
 +                      return undef;
 +              } elsif (/^q/i) {
 +                      cleanup_compose_files();
 +                      exit(0);
 +              }
 +              $address = ask("Who should the email be sent to (if any)? ",
 +                      default => "",
 +                      valid_re => qr/\@.*\./, confirm_only => 1);
 +      }
 +      return $address;
 +}
 +
 +sub validate_address_list {
 +      return (grep { defined $_ }
 +              map { validate_address($_) } @_);
  }
  
  # Usually don't need to change anything below here.
@@@ -935,29 -900,9 +935,29 @@@ sub is_rfc2047_quoted 
        $s =~ m/^(?:"[[:ascii:]]*"|=\?$token\?$token\?$encoded_text\?=)$/o;
  }
  
 +sub subject_needs_rfc2047_quoting {
 +      my $s = shift;
 +
 +      return ($s =~ /[^[:ascii:]]/) || ($s =~ /=\?/);
 +}
 +
 +sub quote_subject {
 +      local $subject = shift;
 +      my $encoding = shift || 'UTF-8';
 +
 +      if (subject_needs_rfc2047_quoting($subject)) {
 +              return quote_rfc2047($subject, $encoding);
 +      }
 +      return $subject;
 +}
 +
  # use the simplest quoting being able to handle the recipient
  sub sanitize_address {
        my ($recipient) = @_;
 +
 +      # remove garbage after email address
 +      $recipient =~ s/(.*>).*$/$1/;
 +
        my ($recipient_name, $recipient_addr) = ($recipient =~ /^(.*?)\s*(<.*)/);
  
        if (not $recipient_name) {
  
  }
  
 +sub sanitize_address_list {
 +      return (map { sanitize_address($_) } @_);
 +}
 +
  # Returns the local Fully Qualified Domain Name (FQDN) if available.
  #
  # Tightly configured MTAa require that a caller sends a real DNS
@@@ -1051,13 -992,14 +1051,13 @@@ sub maildomain 
  
  sub send_message {
        my @recipients = unique_email_list(@to);
 -      @cc = (grep { my $cc = extract_valid_address($_);
 +      @cc = (grep { my $cc = extract_valid_address_or_die($_);
                      not grep { $cc eq $_ || $_ =~ /<\Q${cc}\E>$/ } @recipients
                    }
 -             map { sanitize_address($_) }
               @cc);
        my $to = join (",\n\t", @recipients);
        @recipients = unique_email_list(@recipients,@cc,@bcclist);
 -      @recipients = (map { extract_valid_address($_) } @recipients);
 +      @recipients = (map { extract_valid_address_or_die($_) } @recipients);
        my $date = format_2822_time($time++);
        my $gitversion = '@@GIT_VERSION@@';
        if ($gitversion =~ m/..GIT_VERSION../) {
@@@ -1285,10 -1227,10 +1285,10 @@@ foreach my $t (@files) 
                }
  
                if (defined $input_format && $input_format eq 'mbox') {
-                       if (/^Subject:\s+(.*)$/) {
+                       if (/^Subject:\s+(.*)$/i) {
                                $subject = $1;
                        }
-                       elsif (/^From:\s+(.*)$/) {
+                       elsif (/^From:\s+(.*)$/i) {
                                ($author, $author_encoding) = unquote_rfc2047($1);
                                next if $suppress_cc{'author'};
                                next if $suppress_cc{'self'} and $author eq $sender;
                                        $1, $_) unless $quiet;
                                push @cc, $1;
                        }
-                       elsif (/^To:\s+(.*)$/) {
+                       elsif (/^To:\s+(.*)$/i) {
                                foreach my $addr (parse_address_line($1)) {
                                        printf("(mbox) Adding to: %s from line '%s'\n",
                                                $addr, $_) unless $quiet;
 -                                      push @to, sanitize_address($addr);
 +                                      push @to, $addr;
                                }
                        }
-                       elsif (/^Cc:\s+(.*)$/) {
+                       elsif (/^Cc:\s+(.*)$/i) {
                                foreach my $addr (parse_address_line($1)) {
                                        if (unquote_rfc2047($addr) eq $sender) {
                                                next if ($suppress_cc{'self'});
                        elsif (/^Message-Id: (.*)/i) {
                                $message_id = $1;
                        }
-                       elsif (!/^Date:\s/ && /^[-A-Za-z]+:\s+\S/) {
+                       elsif (!/^Date:\s/i && /^[-A-Za-z]+:\s+\S/) {
                                push @xh, $_;
                        }
  
        }
  
        if ($broken_encoding{$t} && !is_rfc2047_quoted($subject)) {
 -              $subject = quote_rfc2047($subject, $auto_8bit_encoding);
 +              $subject = quote_subject($subject, $auto_8bit_encoding);
        }
  
        if (defined $author and $author ne $sender) {
                ($confirm =~ /^(?:auto|compose)$/ && $compose && $message_num == 1));
        $needs_confirm = "inform" if ($needs_confirm && $confirm_unconfigured && @cc);
  
 +      @to = validate_address_list(sanitize_address_list(@to));
 +      @cc = validate_address_list(sanitize_address_list(@cc));
 +
        @to = (@initial_to, @to);
        @cc = (@initial_cc, @cc);
  
@@@ -1467,10 -1406,14 +1467,10 @@@ sub unique_email_list 
        my @emails;
  
        foreach my $entry (@_) {
 -              if (my $clean = extract_valid_address($entry)) {
 -                      $seen{$clean} ||= 0;
 -                      next if $seen{$clean}++;
 -                      push @emails, $entry;
 -              } else {
 -                      print STDERR "W: unable to extract a valid address",
 -                                      " from: $entry\n";
 -              }
 +              my $clean = extract_valid_address_or_die($entry);
 +              $seen{$clean} ||= 0;
 +              next if $seen{$clean}++;
 +              push @emails, $entry;
        }
        return @emails;
  }