--- /usr/bin/bts	2008-01-11 21:59:45.000000000 +0100
+++ bts	2008-01-27 20:38:48.000000000 +0100
@@ -266,6 +266,13 @@
 shell.  Default is 'mutt -f %s'.  (Also, %% will be substituted by a
 single % if this is needed.)
 
+=item --mailcomposer=COMPOSER
+
+Specify the command to compose mail from a template. Must contain a "%s"
+as per --mailreader, however it will be replaced by a mail template
+containing some headers and body, similar to what is expected by mutt
+when invoked with -H. Default is, no wonder, 'mutt -H %s'.
+
 =item --cc-addr=CC_EMAIL_ADDRESS
 
 Send carbon copies to a list of users. CC_EMAIL_ADDRESS should be a 
@@ -339,6 +346,7 @@
 my $refreshmode=0;
 my $updatemode=0;
 my $mailreader='mutt -f %s';
+my $mailcomposer='mutt -H %s';
 my $sendmailcmd='/usr/sbin/sendmail';
 my $smtphost='';
 my $noaction=0;
@@ -361,6 +369,7 @@
 		       'BTS_FORCE_REFRESH' => 'no',
 		       'BTS_ONLY_NEW' => 'no',
 		       'BTS_MAIL_READER' => 'mutt -f %s',
+		       'BTS_MAIL_COMPOSER' => 'mutt -H %s',
 		       'BTS_SENDMAIL_COMMAND' => '/usr/sbin/sendmail',
 		       'BTS_INCLUDE_RESOLVED' => 'yes',
 		       'BTS_SMTP_HOST' => '',
@@ -423,6 +432,7 @@
     $refreshmode = $config_vars{'BTS_FORCE_REFRESH'} eq 'yes' ? 1 : 0;
     $updatemode = $config_vars{'BTS_ONLY_NEW'} eq 'yes' ? 1 : 0;
     $mailreader = $config_vars{'BTS_MAIL_READER'};
+    $mailcomposer = $config_vars{'BTS_MAIL_COMPOSER'};
     $sendmailcmd = $config_vars{'BTS_SENDMAIL_COMMAND'};
     $smtphost = $config_vars{'BTS_SMTP_HOST'};
     $includeresolved = $config_vars{'BTS_INCLUDE_RESOLVED'} eq 'yes' ? 1 : 0;
@@ -433,7 +443,7 @@
 }
 
 my ($opt_help, $opt_version, $opt_noconf);
-my ($opt_cachemode, $opt_mailreader, $opt_sendmail, $opt_smtphost);
+my ($opt_cachemode, $opt_mailreader, $opt_mailcomposer, $opt_sendmail, $opt_smtphost);
 my $opt_cachedelay=5;
 my $mboxmode = 0;
 my $quiet=0;
@@ -451,6 +461,7 @@
 	   "cache-delay=i" => \$opt_cachedelay,
 	   "m|mbox" => \$mboxmode,
 	   "mailreader|mail-reader=s" => \$opt_mailreader,
+	   "mailcomposer|mail-composer=s" => \$opt_mailcomposer,
 	   "cc-addr=s" => \$ccemail,
 	   "sendmail=s" => \$opt_sendmail,
 	   "smtp-host|smtphost=s" => \$opt_smtphost,
@@ -478,6 +489,14 @@
     }
 }
 
+if ($opt_mailcomposer) {
+    if ($opt_mailcomposer =~ /\%s/) {
+	$mailcomposer=$opt_mailcomposer;
+    } else {
+	warn "bts: ignoring invalid --mailcomposer option: invalid mail command following it.\n";
+    }
+}
+
 if ($opt_sendmail and $opt_smtphost) {
     die "bts: --sendmail and --smtp-host mutually exclusive\n";
 }
@@ -901,6 +920,77 @@
      print map {qq($_\n)} @{$bugs};
 }
 
+=item followup <bug> [bug log message ID]
+
+Fire up a mail user agent to follow up to a given bug report, quoting the bug
+log text. Per default the text of the first message in the bug log is inlined
+for quoting purposes, you can specify an alternative one providing an optional
+bug log ID. The first message in the bug log has bug log ID 1, second message
+2, and so on. Alternatively, "last" can be specified as a bug log ID to choose
+the last message, "last-1" to choose the next to last, and so on.
+
+=cut
+
+sub bts_followup {
+  die "bts: Couldn't run bts followup: $soap_broken\n" unless have_soap();
+  my $bug = checkbug(shift) or die "bts followup: follow up on which bug?\n";
+  my $msglogid = shift;
+  $msglogid = 1 unless defined $msglogid;
+  my $soap = SOAP::Lite->uri($soapurl)->proxy($soapproxyurl);
+  my $log = $soap->get_bug_log($bug)->result();
+  my @logs = @$log;
+  my $msg;
+  if ($msglogid =~ /^last(-(\d+))?$/i) {
+    my $idx = defined $2 ? $#logs-$2 : $#logs;
+    $msg = $logs[$idx];
+  } else {
+    $msg = $logs[$msglogid-1];
+  }
+  my %headers = parse_rfc822_headers($msg->{header});
+
+  # extract data needed to compose the follow up email
+  my $qbody = # quoted text body
+    join "\n", map { s/^/> /; $_ } (split /\r?\n/, $msg->{body});
+  my $subject = "follow up for $bug";	# default subject
+  $subject = "Re: $headers{subject}" if defined $headers{'subject'};
+  my $from = "anonymous"; # default author
+  if (defined $headers{'from'} and $headers{'from'} =~ /^(.*)\s+<[^<>]+>\s*$/) {
+    $from = $1;
+  }
+  my $ref = "";	# id for In-Reply-To 
+  $ref = $headers{'message-id'} if defined $headers{'message-id'};
+  my $submitter = "";
+  $submitter = $headers{'from'} if defined $headers{'from'};
+
+  # compose follow up email
+  my ($fh, $mailtpl) = tempfile("btsXXXXXX",
+    SUFFIX => ".mail",
+    DIR => File::Spec->tmpdir,
+    UNLINK => 1);
+  open (MAIL, ">/dev/fd/" . fileno($fh))
+    or die "bts: writing to temporary file: $!\n";
+  print MAIL "To: $submitter\n";
+  print MAIL "Cc: $bug\@$btsserver\n";
+  print MAIL "Subject: $subject\n";
+  print MAIL "In-Reply-To: $ref\n" if $ref;
+  print MAIL "\n";
+  print MAIL "On $headers{'date'}, " if defined $headers{'date'};
+  print MAIL "$from wrote:\n";
+  print MAIL "$qbody\n";
+  close MAIL;
+
+  # fire up MUA on email template
+  my $cmd = $mailcomposer;
+  $cmd =~ s/\%s/$mailtpl/g;
+  system $cmd;
+  unless ($? == 0) {
+    my $rc = $? >> 8;
+    print "Failure to follow up: mail composer returned exit status $rc.\n";
+    print "Dumping intended mail template to standard output:\n\n";
+    system "cat $mailtpl";
+  }
+}
+
 =item clone <bug> [new IDs]
 
 The clone control command allows you to duplicate a bug report. It is useful
@@ -2194,6 +2284,24 @@
     return $header;
 }
 
+# Given mail headers as a raw string, parses them and return a hash mapping
+# (lowercase) header names to header values; handles properly (?) RFC822 line
+# continuation.
+sub parse_rfc822_headers {
+  my ($raw) = @_;
+  my %headers;
+  my $key = "";	# remember last seen header name
+  foreach my $line (split /\r?\n/, $raw) {
+    if ($line =~ /^([^:]+):\s+(.*)$/) {	# 1st header field
+      my $key = lc $1;
+      $headers{$key} = $2;
+    } elsif ($line =~ /^\s+(.*)/ ) { # header field continuation
+      $headers{$key} .= " $1";
+    }
+  }
+  return %headers;
+}
+
 ##########  Browsing and caching subroutines
 
 # Mirrors a given thing; if the online version is no newer than our
@@ -3217,6 +3325,11 @@
 If this is set, specifies a mail reader to use instead of mutt.  Same as
 the --mailreader command line option.
 
+=item BTS_MAIL_COMPOSER
+
+If this is set, specifies a mail composer to use instead of mutt.  Same
+as the --mailcomposer command line option.
+
 =item BTS_SENDMAIL_COMMAND
 
 If this is set, specifies a sendmail command to use instead of
