test-lib: Replace uses of $(expr ...) by POSIX shell features.
[gitweb.git] / git-send-email.perl
index a6efd1fa277c82ac53d92fec004a6ef4074db1fc..546d2ebc0c72d4a330b3b40ca886af9340afb5af 100755 (executable)
@@ -68,14 +68,15 @@ sub usage {
   Automating:
     --identity              <str>  * Use the sendemail.<id> options.
     --cc-cmd                <str>  * Email Cc: via `<str> \$patch_path`
-    --suppress-cc           <str>  * author, self, sob, cccmd, all.
-    --[no-]signed-off-by-cc        * Send to Cc: and Signed-off-by:
-                                     addresses. Default on.
+    --suppress-cc           <str>  * author, self, sob, cc, cccmd, body, bodycc, all.
+    --[no-]signed-off-by-cc        * Send to Signed-off-by: addresses. Default on.
     --[no-]suppress-from           * Send to self. Default off.
     --[no-]chain-reply-to          * Chain In-Reply-To: fields. Default on.
     --[no-]thread                  * Use In-Reply-To: field. Default on.
 
   Administering:
+    --confirm               <str>  * Confirm recipients before sending;
+                                     auto, cc, compose, always, or never.
     --quiet                        * Output one line of info per email.
     --dry-run                      * Don't actually send the emails.
     --[no-]validate                * Perform patch sanity checks. Default on.
@@ -157,10 +158,7 @@ sub format_2822_time {
 # Behavior modification variables
 my ($quiet, $dry_run) = (0, 0);
 my $format_patch;
-my $compose_filename = ($repo ?
-       tempfile(".gitsendemail.msg.XXXXXX", DIR => $repo->repo_path()) :
-       tempfile(".gitsendemail.msg.XXXXXX", DIR => "."))[1];
-
+my $compose_filename;
 
 # Handle interactive edition of files.
 my $multiedit;
@@ -185,7 +183,7 @@ sub do_edit {
 my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc, $cc_cmd);
 my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_encryption);
 my ($identity, $aliasfiletype, @alias_files, @smtp_host_parts);
-my ($validate);
+my ($validate, $confirm);
 my (@suppress_cc);
 
 my %config_bool_settings = (
@@ -211,6 +209,7 @@ sub do_edit {
     "suppresscc" => \@suppress_cc,
     "envelopesender" => \$envelope_sender,
     "multiedit" => \$multiedit,
+    "confirm"   => \$confirm,
 );
 
 # Handle Uncouth Termination
@@ -223,11 +222,13 @@ sub signal_handler {
        system "stty echo";
 
        # tmp files from --compose
-       if (-e $compose_filename) {
-               print "'$compose_filename' contains an intermediate version of the email you were composing.\n";
-       }
-       if (-e ($compose_filename . ".final")) {
-               print "'$compose_filename.final' contains the composed email.\n"
+       if (defined $compose_filename) {
+               if (-e $compose_filename) {
+                       print "'$compose_filename' contains an intermediate version of the email you were composing.\n";
+               }
+               if (-e ($compose_filename . ".final")) {
+                       print "'$compose_filename.final' contains the composed email.\n"
+               }
        }
 
        exit;
@@ -260,6 +261,7 @@ sub signal_handler {
                    "suppress-from!" => \$suppress_from,
                    "suppress-cc=s" => \@suppress_cc,
                    "signed-off-cc|signed-off-by-cc!" => \$signed_off_by_cc,
+                   "confirm=s" => \$confirm,
                    "dry-run" => \$dry_run,
                    "envelope-sender=s" => \$envelope_sender,
                    "thread!" => \$thread,
@@ -325,13 +327,13 @@ sub read_config {
 if (@suppress_cc) {
        foreach my $entry (@suppress_cc) {
                die "Unknown --suppress-cc field: '$entry'\n"
-                       unless $entry =~ /^(all|cccmd|cc|author|self|sob)$/;
+                       unless $entry =~ /^(all|cccmd|cc|author|self|sob|body|bodycc)$/;
                $suppress_cc{$entry} = 1;
        }
 }
 
 if ($suppress_cc{'all'}) {
-       foreach my $entry (qw (ccmd cc author self sob)) {
+       foreach my $entry (qw (ccmd cc author self sob body bodycc)) {
                $suppress_cc{$entry} = 1;
        }
        delete $suppress_cc{'all'};
@@ -341,6 +343,21 @@ sub read_config {
 $suppress_cc{'self'} = $suppress_from if defined $suppress_from;
 $suppress_cc{'sob'} = !$signed_off_by_cc if defined $signed_off_by_cc;
 
+if ($suppress_cc{'body'}) {
+       foreach my $entry (qw (sob bodycc)) {
+               $suppress_cc{$entry} = 1;
+       }
+       delete $suppress_cc{'body'};
+}
+
+# Set confirm's default value
+my $confirm_unconfigured = !defined $confirm;
+if ($confirm_unconfigured) {
+       $confirm = scalar %suppress_cc ? 'compose' : 'auto';
+};
+die "Unknown --confirm setting: '$confirm'\n"
+       unless $confirm =~ /^(?:auto|cc|compose|always|never)/;
+
 # Debugging, print out the suppressions.
 if (0) {
        print "suppressions:\n";
@@ -499,6 +516,9 @@ ($)
 if ($compose) {
        # Note that this does not need to be secure, but we will make a small
        # effort to have it be unique
+       $compose_filename = ($repo ?
+               tempfile(".gitsendemail.msg.XXXXXX", DIR => $repo->repo_path()) :
+               tempfile(".gitsendemail.msg.XXXXXX", DIR => "."))[1];
        open(C,">",$compose_filename)
                or die "Failed to open for writing $compose_filename: $!";
 
@@ -655,25 +675,13 @@ sub expand_aliases {
        $smtp_server ||= 'localhost'; # could be 127.0.0.1, too... *shrug*
 }
 
-if ($compose) {
-       while (1) {
-               $_ = $term->readline("Send this email? (y|n) ");
-               last if defined $_;
-               print "\n";
-       }
-
-       if (uc substr($_,0,1) ne 'Y') {
-               cleanup_compose_files();
-               exit(0);
-       }
-
-       if ($compose > 0) {
-               @files = ($compose_filename . ".final", @files);
-       }
+if ($compose && $compose > 0) {
+       @files = ($compose_filename . ".final", @files);
 }
 
 # Variables we set as part of the loop over files
-our ($message_id, %mail, $subject, $reply_to, $references, $message);
+our ($message_id, %mail, $subject, $reply_to, $references, $message,
+       $needs_confirm, $message_num);
 
 sub extract_valid_address {
        my $address = shift;
@@ -813,7 +821,7 @@ sub send_message
 Message-Id: $message_id
 X-Mailer: git-send-email $gitversion
 ";
-       if ($thread && $reply_to) {
+       if ($reply_to) {
 
                $header .= "In-Reply-To: $reply_to\n";
                $header .= "References: $references\n";
@@ -829,6 +837,37 @@ sub send_message
        unshift (@sendmail_parameters,
                        '-f', $raw_from) if(defined $envelope_sender);
 
+       if ($needs_confirm && !$dry_run) {
+               print "\n$header\n";
+               if ($needs_confirm eq "inform") {
+                       $confirm_unconfigured = 0; # squelch this message for the rest of this run
+                       print "    The Cc list above has been expanded by additional\n";
+                       print "    addresses found in the patch commit message. By default\n";
+                       print "    send-email prompts before sending whenever this occurs.\n";
+                       print "    This behavior is controlled by the sendemail.confirm\n";
+                       print "    configuration setting.\n";
+                       print "\n";
+                       print "    For additional information, run 'git send-email --help'.\n";
+                       print "    To retain the current behavior, but squelch this message,\n";
+                       print "    run 'git config --global sendemail.confirm auto'.\n\n";
+               }
+               while (1) {
+                       chomp ($_ = $term->readline(
+                               "Send this email? ([y]es|[n]o|[q]uit|[a]ll): "
+                       ));
+                       last if /^(?:yes|y|no|n|quit|q|all|a)/i;
+                       print "\n";
+               }
+               if (/^n/i) {
+                       return;
+               } elsif (/^q/i) {
+                       cleanup_compose_files();
+                       exit(0);
+               } elsif (/^a/i) {
+                       $confirm = 'never';
+               }
+       }
+
        if ($dry_run) {
                # We don't want to send the email.
        } elsif ($smtp_server =~ m#^/#) {
@@ -927,6 +966,7 @@ sub send_message
 $reply_to = $initial_reply_to;
 $references = $initial_reply_to || '';
 $subject = $initial_subject;
+$message_num = 0;
 
 foreach my $t (@files) {
        open(F,"<",$t) or die "can't open file $t";
@@ -935,11 +975,12 @@ sub send_message
        my $author_encoding;
        my $has_content_type;
        my $body_encoding;
-       @cc = @initial_cc;
+       @cc = ();
        @xh = ();
        my $input_format = undef;
        my @header = ();
        $message = "";
+       $message_num++;
        # First unfold multiline header fields
        while(<F>) {
                last if /^\s*$/;
@@ -1020,13 +1061,17 @@ sub send_message
        while(<F>) {
                $message .=  $_;
                if (/^(Signed-off-by|Cc): (.*)$/i) {
-                       next if ($suppress_cc{'sob'});
                        chomp;
-                       my $c = $2;
+                       my ($what, $c) = ($1, $2);
                        chomp $c;
-                       next if ($c eq $sender and $suppress_cc{'self'});
+                       if ($c eq $sender) {
+                               next if ($suppress_cc{'self'});
+                       } else {
+                               next if $suppress_cc{'sob'} and $what =~ /Signed-off-by/i;
+                               next if $suppress_cc{'bodycc'} and $what =~ /Cc/i;
+                       }
                        push @cc, $c;
-                       printf("(sob) Adding cc: %s from line '%s'\n",
+                       printf("(body) Adding cc: %s from line '%s'\n",
                                $c, $_) unless $quiet;
                }
        }
@@ -1068,6 +1113,14 @@ sub send_message
                }
        }
 
+       $needs_confirm = (
+               $confirm eq "always" or
+               ($confirm =~ /^(?:auto|cc)$/ && @cc) or
+               ($confirm =~ /^(?:auto|compose)$/ && $compose && $message_num == 1));
+       $needs_confirm = "inform" if ($needs_confirm && $confirm_unconfigured && @cc);
+
+       @cc = (@initial_cc, @cc);
+
        send_message();
 
        # set up for the next message
@@ -1082,13 +1135,10 @@ sub send_message
        $message_id = undef;
 }
 
-if ($compose) {
-       cleanup_compose_files();
-}
+cleanup_compose_files();
 
 sub cleanup_compose_files() {
-       unlink($compose_filename, $compose_filename . ".final");
-
+       unlink($compose_filename, $compose_filename . ".final") if $compose;
 }
 
 $smtp->quit if $smtp;