23
23
use Text::ParseWords;
24
24
use Data::Dumper;
25
25
use Term::ANSIColor;
26
- use File::Temp qw/ tempdir / ;
26
+ use File::Temp qw/ tempdir tempfile / ;
27
27
use Error qw( :try) ;
28
28
use Git;
29
29
@@ -68,9 +68,8 @@ sub usage {
68
68
Automating:
69
69
--identity <str> * Use the sendemail.<id> options.
70
70
--cc-cmd <str> * Email Cc: via `<str> \$ patch_path`
71
- --suppress-cc <str> * author, self, sob, cccmd, all.
72
- --[no-]signed-off-by-cc * Send to Cc: and Signed-off-by:
73
- addresses. Default on.
71
+ --suppress-cc <str> * author, self, sob, cc, cccmd, body, bodycc, all.
72
+ --[no-]signed-off-by-cc * Send to Signed-off-by: addresses. Default on.
74
73
--[no-]suppress-from * Send to self. Default off.
75
74
--[no-]chain-reply-to * Chain In-Reply-To: fields. Default on.
76
75
--[no-]thread * Use In-Reply-To: field. Default on.
@@ -126,6 +125,7 @@ sub format_2822_time {
126
125
}
127
126
128
127
my $have_email_valid = eval { require Email::Valid; 1 };
128
+ my $have_mail_address = eval { require Mail::Address; 1 };
129
129
my $smtp ;
130
130
my $auth ;
131
131
@@ -156,7 +156,7 @@ sub format_2822_time {
156
156
# Behavior modification variables
157
157
my ($quiet , $dry_run ) = (0, 0);
158
158
my $format_patch ;
159
- my $compose_filename = $repo -> repo_path() . " /.gitsendemail.msg. $$ " ;
159
+ my $compose_filename ;
160
160
161
161
# Handle interactive edition of files.
162
162
my $multiedit ;
@@ -219,11 +219,13 @@ sub signal_handler {
219
219
system " stty echo" ;
220
220
221
221
# tmp files from --compose
222
- if (-e $compose_filename ) {
223
- print " '$compose_filename ' contains an intermediate version of the email you were composing.\n " ;
224
- }
225
- if (-e ($compose_filename . " .final" )) {
226
- print " '$compose_filename .final' contains the composed email.\n "
222
+ if (defined $compose_filename ) {
223
+ if (-e $compose_filename ) {
224
+ print " '$compose_filename ' contains an intermediate version of the email you were composing.\n " ;
225
+ }
226
+ if (-e ($compose_filename . " .final" )) {
227
+ print " '$compose_filename .final' contains the composed email.\n "
228
+ }
227
229
}
228
230
229
231
exit ;
@@ -267,6 +269,9 @@ sub signal_handler {
267
269
usage();
268
270
}
269
271
272
+ die " Cannot run git format-patch from outside a repository\n "
273
+ if $format_patch and not $repo ;
274
+
270
275
# Now, let's fill any that aren't set in with defaults:
271
276
272
277
sub read_config {
@@ -318,13 +323,13 @@ sub read_config {
318
323
if (@suppress_cc ) {
319
324
foreach my $entry (@suppress_cc ) {
320
325
die " Unknown --suppress-cc field: '$entry '\n "
321
- unless $entry =~ / ^(all|cccmd|cc|author|self|sob)$ / ;
326
+ unless $entry =~ / ^(all|cccmd|cc|author|self|sob|body|bodycc )$ / ;
322
327
$suppress_cc {$entry } = 1;
323
328
}
324
329
}
325
330
326
331
if ($suppress_cc {' all' }) {
327
- foreach my $entry (qw ( ccmd cc author self sob) ) {
332
+ foreach my $entry (qw ( ccmd cc author self sob body bodycc ) ) {
328
333
$suppress_cc {$entry } = 1;
329
334
}
330
335
delete $suppress_cc {' all' };
@@ -334,6 +339,13 @@ sub read_config {
334
339
$suppress_cc {' self' } = $suppress_from if defined $suppress_from ;
335
340
$suppress_cc {' sob' } = !$signed_off_by_cc if defined $signed_off_by_cc ;
336
341
342
+ if ($suppress_cc {' body' }) {
343
+ foreach my $entry (qw ( sob bodycc) ) {
344
+ $suppress_cc {$entry } = 1;
345
+ }
346
+ delete $suppress_cc {' body' };
347
+ }
348
+
337
349
# Debugging, print out the suppressions.
338
350
if (0) {
339
351
print " suppressions:\n " ;
@@ -360,6 +372,14 @@ sub read_config {
360
372
die " Comma in --bcclist entry: $entry '\n " unless $entry !~ m / ,/ ;
361
373
}
362
374
375
+ sub parse_address_line {
376
+ if ($have_mail_address ) {
377
+ return map { $_ -> format } Mail::Address-> parse($_ [0]);
378
+ } else {
379
+ return split_addrs($_ [0]);
380
+ }
381
+ }
382
+
363
383
sub split_addrs {
364
384
return quotewords(' \s*,\s*' , 1, @_ );
365
385
}
@@ -404,6 +424,7 @@ sub split_addrs {
404
424
405
425
# returns 1 if the conflict must be solved using it as a format-patch argument
406
426
sub check_file_rev_conflict ($) {
427
+ return unless $repo ;
407
428
my $f = shift ;
408
429
try {
409
430
$repo -> command(' rev-parse' , ' --verify' , ' --quiet' , $f );
445
466
}
446
467
447
468
if (@rev_list_opts ) {
469
+ die " Cannot run git format-patch from outside a repository\n "
470
+ unless $repo ;
448
471
push @files , $repo -> command(' format-patch' , ' -o' , tempdir(CLEANUP => 1), @rev_list_opts );
449
472
}
450
473
481
504
if ($compose ) {
482
505
# Note that this does not need to be secure, but we will make a small
483
506
# effort to have it be unique
507
+ $compose_filename = ($repo ?
508
+ tempfile(" .gitsendemail.msg.XXXXXX" , DIR => $repo -> repo_path()) :
509
+ tempfile(" .gitsendemail.msg.XXXXXX" , DIR => " ." ))[1];
484
510
open (C," >" ,$compose_filename )
485
511
or die " Failed to open for writing $compose_filename : $! " ;
486
512
593
619
}
594
620
595
621
my $to = $_ ;
596
- push @to , split_addrs ($to );
622
+ push @to , parse_address_line ($to );
597
623
$prompting ++;
598
624
}
599
625
@@ -920,88 +946,102 @@ sub send_message
920
946
@cc = @initial_cc ;
921
947
@xh = ();
922
948
my $input_format = undef ;
923
- my $header_done = 0 ;
949
+ my @header = () ;
924
950
$message = " " ;
951
+ # First unfold multiline header fields
925
952
while (<F>) {
926
- if (!$header_done ) {
927
- if (/ ^From / ) {
928
- $input_format = ' mbox' ;
929
- next ;
953
+ last if / ^\s *$ / ;
954
+ if (/ ^\s +\S / and @header ) {
955
+ chomp ($header [$#header ]);
956
+ s / ^\s +/ / ;
957
+ $header [$#header ] .= $_ ;
958
+ } else {
959
+ push (@header , $_ );
960
+ }
961
+ }
962
+ # Now parse the header
963
+ foreach (@header ) {
964
+ if (/ ^From / ) {
965
+ $input_format = ' mbox' ;
966
+ next ;
967
+ }
968
+ chomp ;
969
+ if (!defined $input_format && / ^[-A-Za-z]+:\s / ) {
970
+ $input_format = ' mbox' ;
971
+ }
972
+
973
+ if (defined $input_format && $input_format eq ' mbox' ) {
974
+ if (/ ^Subject:\s +(.*)$ / ) {
975
+ $subject = $1 ;
930
976
}
931
- chomp ;
932
- if (!defined $input_format && / ^[-A-Za-z]+:\s / ) {
933
- $input_format = ' mbox' ;
977
+ elsif (/ ^From:\s +(.*)$ / ) {
978
+ ($author , $author_encoding ) = unquote_rfc2047($1 );
979
+ next if $suppress_cc {' author' };
980
+ next if $suppress_cc {' self' } and $author eq $sender ;
981
+ printf (" (mbox) Adding cc: %s from line '%s '\n " ,
982
+ $1 , $_ ) unless $quiet ;
983
+ push @cc , $1 ;
934
984
}
935
-
936
- if (defined $input_format && $input_format eq ' mbox' ) {
937
- if (/ ^Subject:\s +(.*)$ / ) {
938
- $subject = $1 ;
939
-
940
- } elsif (/ ^(Cc|From):\s +(.*)$ / ) {
941
- if (unquote_rfc2047($2 ) eq $sender ) {
985
+ elsif (/ ^Cc:\s +(.*)$ / ) {
986
+ foreach my $addr (parse_address_line($1 )) {
987
+ if (unquote_rfc2047($addr ) eq $sender ) {
942
988
next if ($suppress_cc {' self' });
943
- }
944
- elsif ($1 eq ' From' ) {
945
- ($author , $author_encoding )
946
- = unquote_rfc2047($2 );
947
- next if ($suppress_cc {' author' });
948
989
} else {
949
990
next if ($suppress_cc {' cc' });
950
991
}
951
992
printf (" (mbox) Adding cc: %s from line '%s '\n " ,
952
- $2 , $_ ) unless $quiet ;
953
- push @cc , $2 ;
954
- }
955
- elsif (/ ^Content-type:/i ) {
956
- $has_content_type = 1;
957
- if (/ charset="?([^ "]+)/ ) {
958
- $body_encoding = $1 ;
959
- }
960
- push @xh , $_ ;
961
- }
962
- elsif (/ ^Message-Id: (.*)/i ) {
963
- $message_id = $1 ;
993
+ $addr , $_ ) unless $quiet ;
994
+ push @cc , $addr ;
964
995
}
965
- elsif (!/^Date:\s / && / ^[-A-Za-z]+:\s +\S / ) {
966
- push @xh , $_ ;
967
- }
968
-
969
- } else {
970
- # In the traditional
971
- # "send lots of email" format,
972
- # line 1 = cc
973
- # line 2 = subject
974
- # So let's support that, too.
975
- $input_format = ' lots' ;
976
- if (@cc == 0 && !$suppress_cc {' cc' }) {
977
- printf (" (non-mbox) Adding cc: %s from line '%s '\n " ,
978
- $_ , $_ ) unless $quiet ;
979
-
980
- push @cc , $_ ;
981
-
982
- } elsif (!defined $subject ) {
983
- $subject = $_ ;
996
+ }
997
+ elsif (/ ^Content-type:/i ) {
998
+ $has_content_type = 1;
999
+ if (/ charset="?([^ "]+)/ ) {
1000
+ $body_encoding = $1 ;
984
1001
}
1002
+ push @xh , $_ ;
985
1003
}
986
-
987
- # A whitespace line will terminate the headers
988
- if (m / ^\s *$ / ) {
989
- $header_done = 1;
1004
+ elsif (/ ^Message-Id: (.*)/i ) {
1005
+ $message_id = $1 ;
990
1006
}
1007
+ elsif (!/^Date:\s / && / ^[-A-Za-z]+:\s +\S / ) {
1008
+ push @xh , $_ ;
1009
+ }
1010
+
991
1011
} else {
992
- $message .= $_ ;
993
- if (/ ^(Signed-off-by|Cc): (.*)$ /i ) {
994
- next if ($suppress_cc {' sob' });
995
- chomp ;
996
- my $c = $2 ;
997
- chomp $c ;
998
- next if ($c eq $sender and $suppress_cc {' self' });
999
- push @cc , $c ;
1000
- printf (" (sob) Adding cc: %s from line '%s '\n " ,
1001
- $c , $_ ) unless $quiet ;
1012
+ # In the traditional
1013
+ # "send lots of email" format,
1014
+ # line 1 = cc
1015
+ # line 2 = subject
1016
+ # So let's support that, too.
1017
+ $input_format = ' lots' ;
1018
+ if (@cc == 0 && !$suppress_cc {' cc' }) {
1019
+ printf (" (non-mbox) Adding cc: %s from line '%s '\n " ,
1020
+ $_ , $_ ) unless $quiet ;
1021
+ push @cc , $_ ;
1022
+ } elsif (!defined $subject ) {
1023
+ $subject = $_ ;
1002
1024
}
1003
1025
}
1004
1026
}
1027
+ # Now parse the message body
1028
+ while (<F>) {
1029
+ $message .= $_ ;
1030
+ if (/ ^(Signed-off-by|Cc): (.*)$ /i ) {
1031
+ chomp ;
1032
+ my ($what , $c ) = ($1 , $2 );
1033
+ chomp $c ;
1034
+ if ($c eq $sender ) {
1035
+ next if ($suppress_cc {' self' });
1036
+ } else {
1037
+ next if $suppress_cc {' sob' } and $what =~ / Signed-off-by/i ;
1038
+ next if $suppress_cc {' bodycc' } and $what =~ / Cc/i ;
1039
+ }
1040
+ push @cc , $c ;
1041
+ printf (" (body) Adding cc: %s from line '%s '\n " ,
1042
+ $c , $_ ) unless $quiet ;
1043
+ }
1044
+ }
1005
1045
close F;
1006
1046
1007
1047
if (defined $cc_cmd && !$suppress_cc {' cccmd' }) {
@@ -1020,7 +1060,7 @@ sub send_message
1020
1060
or die " (cc-cmd) failed to close pipe to '$cc_cmd '" ;
1021
1061
}
1022
1062
1023
- if (defined $author ) {
1063
+ if (defined $author and $author ne $sender ) {
1024
1064
$message = " From: $author \n\n $message " ;
1025
1065
if (defined $author_encoding ) {
1026
1066
if ($has_content_type ) {
0 commit comments