Savane supports follow-up comments by email.  This file describes
the arrangements needed for that.

= Basics

When Savane sends notifications from trackers to specific users, it adds
information about the relevant tracker, item, and the user who would reply
to the notitication by email.  When the user replies to the message,
it comes to a specific address, and a simple script redirects the
data into a HTTP request to $SERVER_URL/$tracker/comment.php.  Then
comment.php validates the request and adds a comment to the tracker
in case of success.  If Savane GnuPG key has a usable encryption-capable
subkey, users can send their messages encrypted.  (For more info on Savane
GnuPG key setup, see doc/signed-notifications.)

Note that Savane sends personal notifications to specific users in the next
cases.

* The user submitted the item or contributed to it; then the user
  is automatically added to the CC list of the item before the notification
  on the update or on the new item is sent.
* The user has been explicitly added to the CC list of the item, either
  as the login name or as the numeric user ID.
* The user is listed in the tracker CC list in the preferences of the group,
  either as the login name or as the numeric user ID.

When the accumulated CC list of the item contains an email address
(as opposed to Savane account references), the Savane account isn't
defined, so Savane sends notifications to such addresses without
the data for replying by email.

= Users' perspective

Savane accepts messages signed with any of the GnuPG keys registered
in the respective account.  Both 'clearsigned' messages and messages
with detached signatures are accepted.  When the user replies
to a notification, the MUA inserts a specific address in the 'To:' header;
the address includes the numerical Savane user ID and the name of the tracker,
for example,

  83521-task-savannah <savannah-trackers@gnu.org>

The procmail script used to pass the message to the Web UI doesn't parse
its body (in fact, it may not be able to do that when it's encrypted),
so this data should be preserved in the 'To:' header.  Without it,
the message is silently ignored.

Also, the quoted body of the message contains a line with the same data plus
the number of the item, like

  > {savane: user = 83521; tracker = task; item = 4913}

For the request to be accepted, this info should be present in your message.
The PHP code ignores anything that's written below that line, and it also
removes the quoted Savane footer, 'To reply to this notification&c.'
Other text is used as the comment.

For basic troubleshooting, you can visit /$tracker/comment.php and fill
the 'user' field with either your account name or user ID, insert your
message in the mbox format in the 'email (mbox)' field, submit the request
manually and see the feedback.

Note that the script checks whether the user has already added the same
comment in that item, and rejects duplicate comments, so an intercepted
message can't be used to add more comments signed by the user.

= Savane admins' perspective

In order to enable the feature savane.conf.php should configure a template
for the reply-to address, like

  $sys_reply_to = '%UID-%TRACKER-savannah <savannah-trackers@gnu.org>';

%UID is replaced with user ID, %TRACKER is replaced with tracker name.
Other substituted variables are %GROUP (the same as %PROJECT), %ITEM,
and %SERVER; however, only %UID and %TRACKER are needed in our case.

Savane will add the relevant info to the notifications.  Including %UID
in the message means that every user is sent a personalized version
of the notification.

The user ID from the 'To:' header is used to get the registered keys, %TRACKER
is used as part of the URL of the request sent to comment.php; the comment.php
script checks if user ID and tracker name match the information coming
from the '{savane:...}' line in the message.

Then, the admin should make sure to process the data incoming to
the address listed in $sys_reply_to.  This can be done with a simple
procmail script; a sample script follows.

  # Public domain.  Originally written by Ineiev <ineiev@gnu.org> (2024, 2025).

  WDIR = procmail/sv-trackers
  HEADER = "Content-Type: multipart/form-data; boundary="
  TRACKER_EMAIL = "savannah-trackers@gnu[.]org"
  SV_HOST = savannah
  SV_URL = https://savannah.gnu.org

  # Space and Tab.
  ST = " 	"
  # Space, Tab or end of line, once or more times.
  S = "([$ST]|$)+"

  # Look for Savannah tracker address in Cc; override if also found in To.
  ADDRESS = ""

  :0 D
  * $ ^Cc:.*$S\/[0-9]+-[a-z]+-$SV_HOST$S<$TRACKER_EMAIL>
  {
    ADDRESS = "$MATCH"
  }

  :0 D
  * $ ^To:.*$S\/[0-9]+-[a-z]+-$SV_HOST$S<$TRACKER_EMAIL>
  {
    ADDRESS = "$MATCH"
  }

  :0: D
  * ADDRESS ?? .
  {
    # The address is present; pass the message to Savannah Web UI.
    :0 w
    * ADDRESS ?? ()\/[0-9]+
    { UID = "$MATCH" }
    :0 w
    * ADDRESS ?? [0-9]+-\/[a-z]+
    { TRACKER = "$MATCH" }
    :0 w
    # Actual SV_HOST; it isn't actually used, but the script could
    # modify the URL to process requests from multiple sources.
    * ADDRESS ?? [0-9]+-[a-z]+-\/[^ ]+
    { SV_HOST = "$MATCH" }
    # It looks like the length of procmail variable values is limited,
    # so format messages in files.
    :0
    | cat > $WDIR/msg; cat < $WDIR/msg >> $WDIR/../tracker-processed \
      BOUNDARY=$(date +%sp%N); i=0; \
      seq 34 | while read i; do \
        grep -q $BOUNDARY $WDIR/msg || break; \
        BOUNDARY=$(echo $BOUNDARY | md5sum - | head -c 17); \
      done; \
      HEADER="$HEADER$BOUNDARY"; \
      BOUNDARY="--$BOUNDARY"; \
      echo "" > $WDIR/post; \
      echo "$BOUNDARY" >> $WDIR/post; \
      echo "Content-Disposition: form-data; name=user" >> $WDIR/post; \
      echo "" >> $WDIR/post; \
      echo "$UID" >> $WDIR/post; \
      echo "$BOUNDARY" >> $WDIR/post; \
      echo "Content-Disposition: form-data; name=mbox" >> $WDIR/post; \
      echo "" >> $WDIR/post; \
      cat $WDIR/msg >> $WDIR/post; \
      echo "$BOUNDARY--" >> $WDIR/post; \
      echo "$HEADER" > $WDIR/header; \
      wget --content-on-error --header="$HEADER" --post-file=$WDIR/post \
        $SV_URL/$TRACKER/comment.php -O $WDIR/wget-file.html \
        2> $WDIR/wget-err > $WDIR/wget-out
  }
  # Save in a dedicated mailbox if something went wrong in the addresses.
  :0
  * $ ^TO_$TRACKER_EMAIL
  $WDIR/../tracker-malformed

Copyright (C) 1999, 2000 The SourceForge Crew
Copyright (C) 2000-2006 Mathieu Roy
Copyright (C) 2014, 2016, 2017 Assaf Gordon
Copyright (C) 2001-2011, 2013, 2017 Sylvain Beucler
Copyright (C) 2013, 2014, 2017-2025 Ineiev

Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved.  This file is offered as-is,
without any warranty.
