@matheusbsbs / matheus-bagliano / commits / 5012699d98
Fork 0

send-email: handle multiple Cc addresses when reading mbox message

When git format-patch is given multiple --cc arguments, it generates a Cc header that looks like: Cc: first@example.com, second@example.com, third@example.com Before this commit, send-email was unable to handle such a message as it did not handle folded header lines, nor multiple recipients in a Cc line. This patch: - Unfolds header lines by pre-processing the header before extracting any of its fields. - Handles Cc lines with multiple recipients. - Adds use of Mail::Address if available for splitting Cc line and the "Who should the emails be sent to?" prompt", with fall back to existing split_addrs() function. - Tests the new functionality and adds two tests for detecting whether "From:" appears correctly in message body when patch author differs from patch sender. Signed-off-by: Jay Soffian <jaysoffian@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jay Soffian committed Feb 14, 2009 at 23:32 UTC 5012699d9840fe34fe0838ea0d529c2f32f76b82
2 files changed +123 -74
git-send-email.perl
+86 -67
@@ -126,6 +126,7 @@ sub format_2822_time {
126 }
127
128 my $have_email_valid = eval { require Email::Valid; 1 };
129 +my $have_mail_address = eval { require Mail::Address; 1 };
130 my $smtp;
131 my $auth;
132
@@ -366,6 +367,14 @@ foreach my $entry (@bcclist) {
367 die "Comma in --bcclist entry: $entry'\n" unless $entry !~ m/,/;
368 }
369
370 +sub parse_address_line {
371 + if ($have_mail_address) {
372 + return map { $_->format } Mail::Address->parse($_[0]);
373 + } else {
374 + return split_addrs($_[0]);
375 + }
376 +}
377 +
378 sub split_addrs {
379 return quotewords('\s*,\s*', 1, @_);
380 }
@@ -602,7 +611,7 @@ if (!@to) {
611 }
612
613 my $to = $_;
605 - push @to, split_addrs($to);
614 + push @to, parse_address_line($to);
615 $prompting++;
616 }
617
@@ -929,88 +938,98 @@ foreach my $t (@files) {
938 @cc = @initial_cc;
939 @xh = ();
940 my $input_format = undef;
932 - my $header_done = 0;
941 + my @header = ();
942 $message = "";
943 + # First unfold multiline header fields
944 while(<F>) {
935 - if (!$header_done) {
936 - if (/^From /) {
937 - $input_format = 'mbox';
938 - next;
945 + last if /^\s*$/;
946 + if (/^\s+\S/ and @header) {
947 + chomp($header[$#header]);
948 + s/^\s+/ /;
949 + $header[$#header] .= $_;
950 + } else {
951 + push(@header, $_);
952 + }
953 + }
954 + # Now parse the header
955 + foreach(@header) {
956 + if (/^From /) {
957 + $input_format = 'mbox';
958 + next;
959 + }
960 + chomp;
961 + if (!defined $input_format && /^[-A-Za-z]+:\s/) {
962 + $input_format = 'mbox';
963 + }
964 +
965 + if (defined $input_format && $input_format eq 'mbox') {
966 + if (/^Subject:\s+(.*)$/) {
967 + $subject = $1;
968 }
940 - chomp;
941 - if (!defined $input_format && /^[-A-Za-z]+:\s/) {
942 - $input_format = 'mbox';
969 + elsif (/^From:\s+(.*)$/) {
970 + ($author, $author_encoding) = unquote_rfc2047($1);
971 + next if $suppress_cc{'author'};
972 + next if $suppress_cc{'self'} and $author eq $sender;
973 + printf("(mbox) Adding cc: %s from line '%s'\n",
974 + $1, $_) unless $quiet;
975 + push @cc, $1;
976 }
944 -
945 - if (defined $input_format && $input_format eq 'mbox') {
946 - if (/^Subject:\s+(.*)$/) {
947 - $subject = $1;
948 -
949 - } elsif (/^(Cc|From):\s+(.*)$/) {
950 - if (unquote_rfc2047($2) eq $sender) {
977 + elsif (/^Cc:\s+(.*)$/) {
978 + foreach my $addr (parse_address_line($1)) {
979 + if (unquote_rfc2047($addr) eq $sender) {
980 next if ($suppress_cc{'self'});
952 - }
953 - elsif ($1 eq 'From') {
954 - ($author, $author_encoding)
955 - = unquote_rfc2047($2);
956 - next if ($suppress_cc{'author'});
981 } else {
982 next if ($suppress_cc{'cc'});
983 }
984 printf("(mbox) Adding cc: %s from line '%s'\n",
961 - $2, $_) unless $quiet;
962 - push @cc, $2;
985 + $addr, $_) unless $quiet;
986 + push @cc, $addr;
987 }
964 - elsif (/^Content-type:/i) {
965 - $has_content_type = 1;
966 - if (/charset="?([^ "]+)/) {
967 - $body_encoding = $1;
968 - }
969 - push @xh, $_;
970 - }
971 - elsif (/^Message-Id: (.*)/i) {
972 - $message_id = $1;
973 - }
974 - elsif (!/^Date:\s/ && /^[-A-Za-z]+:\s+\S/) {
975 - push @xh, $_;
976 - }
977 -
978 - } else {
979 - # In the traditional
980 - # "send lots of email" format,
981 - # line 1 = cc
982 - # line 2 = subject
983 - # So let's support that, too.
984 - $input_format = 'lots';
985 - if (@cc == 0 && !$suppress_cc{'cc'}) {
986 - printf("(non-mbox) Adding cc: %s from line '%s'\n",
987 - $_, $_) unless $quiet;
988 -
989 - push @cc, $_;
990 -
991 - } elsif (!defined $subject) {
992 - $subject = $_;
988 + }
989 + elsif (/^Content-type:/i) {
990 + $has_content_type = 1;
991 + if (/charset="?([^ "]+)/) {
992 + $body_encoding = $1;
993 }
994 + push @xh, $_;
995 }
995 -
996 - # A whitespace line will terminate the headers
997 - if (m/^\s*$/) {
998 - $header_done = 1;
996 + elsif (/^Message-Id: (.*)/i) {
997 + $message_id = $1;
998 }
999 + elsif (!/^Date:\s/ && /^[-A-Za-z]+:\s+\S/) {
1000 + push @xh, $_;
1001 + }
1002 +
1003 } else {
1001 - $message .= $_;
1002 - if (/^(Signed-off-by|Cc): (.*)$/i) {
1003 - next if ($suppress_cc{'sob'});
1004 - chomp;
1005 - my $c = $2;
1006 - chomp $c;
1007 - next if ($c eq $sender and $suppress_cc{'self'});
1008 - push @cc, $c;
1009 - printf("(sob) Adding cc: %s from line '%s'\n",
1010 - $c, $_) unless $quiet;
1004 + # In the traditional
1005 + # "send lots of email" format,
1006 + # line 1 = cc
1007 + # line 2 = subject
1008 + # So let's support that, too.
1009 + $input_format = 'lots';
1010 + if (@cc == 0 && !$suppress_cc{'cc'}) {
1011 + printf("(non-mbox) Adding cc: %s from line '%s'\n",
1012 + $_, $_) unless $quiet;
1013 + push @cc, $_;
1014 + } elsif (!defined $subject) {
1015 + $subject = $_;
1016 }
1017 }
1018 }
1019 + # Now parse the message body
1020 + while(<F>) {
1021 + $message .= $_;
1022 + if (/^(Signed-off-by|Cc): (.*)$/i) {
1023 + next if ($suppress_cc{'sob'});
1024 + chomp;
1025 + my $c = $2;
1026 + chomp $c;
1027 + next if ($c eq $sender and $suppress_cc{'self'});
1028 + push @cc, $c;
1029 + printf("(sob) Adding cc: %s from line '%s'\n",
1030 + $c, $_) unless $quiet;
1031 + }
1032 + }
1033 close F;
1034
1035 if (defined $cc_cmd && !$suppress_cc{'cccmd'}) {
@@ -1029,7 +1048,7 @@ foreach my $t (@files) {
1048 or die "(cc-cmd) failed to close pipe to '$cc_cmd'";
1049 }
1050
1032 - if (defined $author) {
1051 + if (defined $author and $author ne $sender) {
1052 $message = "From: $author\n\n$message";
1053 if (defined $author_encoding) {
1054 if ($has_content_type) {
t/t9001-send-email.sh
+37 -7
@@ -32,7 +32,7 @@ clean_fake_sendmail() {
32 }
33
34 test_expect_success 'Extract patches' '
35 - patches=`git format-patch -n HEAD^1`
35 + patches=`git format-patch --cc="One <one@example.com>" --cc=two@example.com -n HEAD^1`
36 '
37
38 test_expect_success 'Send patches' '
@@ -42,6 +42,8 @@ test_expect_success 'Send patches' '
42 cat >expected <<\EOF
43 !nobody@example.com!
44 !author@example.com!
45 +!one@example.com!
46 +!two@example.com!
47 EOF
48 test_expect_success \
49 'Verify commandline' \
@@ -50,13 +52,15 @@ test_expect_success \
52 cat >expected-show-all-headers <<\EOF
53 0001-Second.patch
54 (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
55 +(mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com'
56 +(mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com'
57 Dry-OK. Log says:
58 Server: relay.example.com
59 MAIL FROM:<from@example.com>
56 -RCPT TO:<to@example.com>,<cc@example.com>,<author@example.com>,<bcc@example.com>
60 +RCPT TO:<to@example.com>,<cc@example.com>,<author@example.com>,<one@example.com>,<two@example.com>,<bcc@example.com>
61 From: Example <from@example.com>
62 To: to@example.com
59 -Cc: cc@example.com, A <author@example.com>
63 +Cc: cc@example.com, A <author@example.com>, One <one@example.com>, two@example.com
64 Subject: [PATCH 1/1] Second.
65 Date: DATE-STRING
66 Message-Id: MESSAGE-ID-STRING
@@ -104,6 +108,28 @@ test_expect_success 'no patch was sent' '
108 ! test -e commandline1
109 '
110
111 +test_expect_success 'Author From: in message body' '
112 + clean_fake_sendmail &&
113 + git send-email \
114 + --from="Example <nobody@example.com>" \
115 + --to=nobody@example.com \
116 + --smtp-server="$(pwd)/fake.sendmail" \
117 + $patches &&
118 + sed "1,/^$/d" < msgtxt1 > msgbody1
119 + grep "From: A <author@example.com>" msgbody1
120 +'
121 +
122 +test_expect_success 'Author From: not in message body' '
123 + clean_fake_sendmail &&
124 + git send-email \
125 + --from="A <author@example.com>" \
126 + --to=nobody@example.com \
127 + --smtp-server="$(pwd)/fake.sendmail" \
128 + $patches &&
129 + sed "1,/^$/d" < msgtxt1 > msgbody1
130 + ! grep "From: A <author@example.com>" msgbody1
131 +'
132 +
133 test_expect_success 'allow long lines with --no-validate' '
134 git send-email \
135 --from="Example <nobody@example.com>" \
@@ -170,13 +196,15 @@ test_expect_success 'second message is patch' '
196 cat >expected-show-all-headers <<\EOF
197 0001-Second.patch
198 (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
199 +(mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com'
200 +(mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com'
201 Dry-OK. Log says:
202 Server: relay.example.com
203 MAIL FROM:<from@example.com>
176 -RCPT TO:<to@example.com>,<cc@example.com>,<author@example.com>
204 +RCPT TO:<to@example.com>,<cc@example.com>,<author@example.com>,<one@example.com>,<two@example.com>
205 From: Example <from@example.com>
206 To: to@example.com
179 -Cc: cc@example.com, A <author@example.com>
207 +Cc: cc@example.com, A <author@example.com>, One <one@example.com>, two@example.com
208 Subject: [PATCH 1/1] Second.
209 Date: DATE-STRING
210 Message-Id: MESSAGE-ID-STRING
@@ -203,13 +231,15 @@ test_expect_success 'sendemail.cc set' '
231 cat >expected-show-all-headers <<\EOF
232 0001-Second.patch
233 (mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
234 +(mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com'
235 +(mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com'
236 Dry-OK. Log says:
237 Server: relay.example.com
238 MAIL FROM:<from@example.com>
209 -RCPT TO:<to@example.com>,<author@example.com>
239 +RCPT TO:<to@example.com>,<author@example.com>,<one@example.com>,<two@example.com>
240 From: Example <from@example.com>
241 To: to@example.com
212 -Cc: A <author@example.com>
242 +Cc: A <author@example.com>, One <one@example.com>, two@example.com
243 Subject: [PATCH 1/1] Second.
244 Date: DATE-STRING
245 Message-Id: MESSAGE-ID-STRING