]> gitweb.fluxo.info Git - firma.git/commitdiff
Again, lots of changes. Please, wait for the changelog.
authorluis <luis>
Fri, 28 Jul 2006 16:00:27 +0000 (16:00 +0000)
committerluis <luis>
Fri, 28 Jul 2006 16:00:27 +0000 (16:00 +0000)
firma

diff --git a/firma b/firma
index b089e96dc7f5d145cc9aab099abd28399a45774e..866a39e7da1cdc3782d4ab74360f7605c9e3a5df 100755 (executable)
--- a/firma
+++ b/firma
 #
 # And it may contain the following optional parameters:
 #
-# LOG_TO_SYSLOG= set to "1" to log fatal errors to syslog, else firma will
-#                print errors to STDERR
-# LOGGER= if logging to syslog, set the path to logger's binary
+# LOG_TO_SYSLOG= set to "1" to log errors and warnings to syslog, else firma
+#                will print errors to STDERR
+# LOGGER_BINARY= if logging to syslog, set the path to logger's binary
 # SYSLOG_PRIORITY= if logging to syslog, set a priority for the error messages
 #                  (defaults to "user.err")
 # USE_GPG_HIDDEN_RECIPIENT_OPTION= set to '1' to use GnuPG's --hidden-recipient
-#                                   option, available from version 1.4.0 onwards
-#                                   (try 'man gpg' for more information)
-# SEND_TO_ALL_AT_ONCE= set to '1' to send list messages to all subscribers
-#                           at once using BCC (NOTE: this option can only be
-#                           used in conjunction with the option above.)
+#                                  option, available from version 1.4.0 onwards
+#                                  (try 'man gpg' for more information)
+# REMOVE_THESE_HEADERS_ON_ALL_LISTS= headers that should be stripped from list
+#                                    messages on all lists running under firma
+#                                    (space separated case-insensitive entries)
+#                                    (may include regexps (e.g., X-.*)
+# SEND_BOUNCE_MESSAGES= set to '1' to send bounces back to sender when an
+#                       invalid message is received (message not signed/
+#                       encrypted, sender not subscribed to the list, etc.)
 #
 # And the list configuration file should contain:
 #
 # LIST_ADDRESS= list's email address
 # LIST_ADMIN= list's administrators email addresses (space separated)
-# SUBJECT_PREFIX= prefix to be included in the subject of list messages, if any
 # LIST_HOMEDIR= list's GnuPG homedir, where the list's keyrings are located
 # PASSPHRASE= passphrase for the list's private keyring
 #
+# And it may contain the following optional parameters:
+#
+# SUBJECT_PREFIX= prefix to be included in the subject of list messages
+# REMOVE_THESE_HEADERS= headers that should be stripped from list messages
+#                       (space separated case-insensitive entries)
+#                       (may include regexps (e.g., X-.*)
+# REPLIES_SHOULD_GO_TO_LIST= set to '1' to add a Reply-To header containing the
+#                            list address
+#
 # NOTE: The passphrase _has_ to be enclosed in single quotes and _cannot_
 # contain any additional single quote as part of itself. It has to be at least
 # 25 characters long, combining numbers, upper and lower case letters and at
@@ -113,7 +125,7 @@ for more details."
 
 function DeclareGpgVars {
   #-------------------------------------------------------------
-  # declare GPG-related global variables
+  # declare gpg-related global variables
   #
   # parameter(s): none
   # depends on function(s): none
@@ -133,27 +145,32 @@ function CheckFirmaConfigFile {
   #
   # parameter(s): none
   # depends on function(s): none
-  # returns: 0 if all checks are passed, 1 if any check fails
+  # returns: 0 if all checks are passed,
+  #          1 on any fatal errors
   #-------------------------------------------------------------
 
   local -i return_code=0
 
   if [[ ! -f "$GPG_BINARY" || ! -x "$GPG_BINARY" ]]; then
-    ERROR_MESSAGE="GPG binary ("$GPG_BINARY") could not be found"
+    ERROR_MESSAGE="FATAL: GPG binary ("$GPG_BINARY") could not be found. Quitting."
     return_code=1
   elif [[ ! -f "$MAIL_AGENT" || ! -x "$MAIL_AGENT" ]]; then
-    ERROR_MESSAGE="Mail transport agent binary ("$MAIL_AGENT") could not be found"
+    ERROR_MESSAGE="FATAL: Mail transport agent binary ("$MAIL_AGENT") could not be found. Quitting."
     return_code=1
   elif [[ ! -d "$LISTS_DIR" ]]; then
-    ERROR_MESSAGE="Lists directory ("$LISTS_DIR") could not be found"
+    ERROR_MESSAGE="FATAL: Lists directory ("$LISTS_DIR") could not be found. Quitting."
     return_code=1
   elif [[ "$USE_GPG_HIDDEN_RECIPIENT_OPTION" == "1" && "$($GPG_BINARY --version | head -n1 | tr -dc '[[:digit:]]')" -lt "140" ]]; then
-    ERROR_MESSAGE="GPG's \"--hidden-recipient\" option is only available from version 1.4.0 onwards"
-    return_code=1
+    ERROR_MESSAGE="\
+WARNING: GPG's \"--hidden-recipient\" option is only available from version 1.4.0 onwards.
+WARNING: Setting USE_GPG_HIDDEN_RECIPIENT_OPTION to '0'."
+    USE_GPG_HIDDEN_RECIPIENT_OPTION=0
   elif [[ "$LOG_TO_SYSLOG" == "1" ]]; then
-    if [[ ! -f "$LOGGER" || ! -x "$LOGGER" ]]; then
-      ERROR_MESSAGE="logger binary ("$LOGGER") could not be found"
-      return_code=1
+    if [[ ! -f "$LOGGER_BINARY" || ! -x "$LOGGER_BINARY" ]]; then
+      ERROR_MESSAGE="\
+WARNING: logger binary ("$LOGGER_BINARY") could not be found.
+WARNING: Setting LOG_TO_SYSLOG to '0'."
+      LOG_TO_SYSLOG=0
     fi
   fi
 
@@ -167,35 +184,41 @@ function CheckListConfigFile {
   #
   # parameter(s): none
   # depends on function(s): DeclareGpgVars
-  # returns: 0 if all checks are passed, 1 if any check fails
+  # returns: 0 if all checks are passed,
+  #          1 on any fatal errors
   #-------------------------------------------------------------
 
   local -i return_code=0
   local administrator
+  local valid_admins
 
   if [[ ! -d "$LIST_HOMEDIR" || ! -f "$LIST_HOMEDIR/pubring.gpg" || ! -f "$LIST_HOMEDIR/secring.gpg" ]]; then
-    ERROR_MESSAGE="$LIST_NAME: GPG home directory ("$LIST_HOMEDIR") or the GPG keyrings could not be found"
-    return_code=1
-  elif [[ -z "$(grep -o "^PASSPHRASE='[^']*'$" $LIST_CONFIG_FILE)" || \
-         -z "$PASSPHRASE" || \
-         "$(echo "$PASSPHRASE" | wc -c)" -lt "25" || \
-         -z "$(echo "$PASSPHRASE" | tr -dc '[[:lower:]]')" || \
-         -z "$(echo "$PASSPHRASE" | tr -dc '[[:upper:]]')" || \
-         -z "$(echo "$PASSPHRASE" | tr -dc '[[:digit:]]')" || \
-         "$(echo "$PASSPHRASE" | tr -dc '[:punct:]' | wc -c)" -lt "5" || \
-         "$(echo "$PASSPHRASE" | fold -w1 | uniq -cd | grep -v '^ \{6\}[234] ')" ]]; then
-    ERROR_MESSAGE="$LIST_NAME: List passphrase is empty or does not meet the minimum complexity requirements"
+    ERROR_MESSAGE="FATAL: $LIST_NAME: GPG home directory ("$LIST_HOMEDIR") or the GPG keyrings could not be found. Quitting."
     return_code=1
-  elif [[ -z "$($GPG_LIST_KEYS --fixed-list-mode "<$LIST_ADDRESS>" 2> /dev/null | grep -v '^tru:')" ]]; then
-    ERROR_MESSAGE="$LIST_NAME: Public key for list \"$(echo "$LIST_ADDRESS" | tr '[:upper:]' '[:lower:]')\" could not be found"
+#  elif [[ -z "$(grep -o "^PASSPHRASE='[^']*'$" $LIST_CONFIG_FILE)" || \
+#         -z "$PASSPHRASE" || \
+#         "$(echo "$PASSPHRASE" | wc -c)" -lt "25" || \
+#         -z "$(echo "$PASSPHRASE" | tr -dc '[[:lower:]]')" || \
+#         -z "$(echo "$PASSPHRASE" | tr -dc '[[:upper:]]')" || \
+#         -z "$(echo "$PASSPHRASE" | tr -dc '[[:digit:]]')" || \
+#         "$(echo "$PASSPHRASE" | tr -dc '[:punct:]' | wc -c)" -lt "5" || \
+#         "$(echo "$PASSPHRASE" | fold -w1 | uniq -cd | grep -v '^ \{6\}[234] ')" ]]; then
+#    ERROR_MESSAGE="$LIST_NAME: List passphrase is empty or does not meet the minimum complexity requirements"
+#    return_code=1
+  elif [[ -z "$($GPG --list-secret-keys --with-colons --fixed-list-mode "<$LIST_ADDRESS>" 2> /dev/null)" ]]; then
+    ERROR_MESSAGE="FATAL: $LIST_NAME: Secret key for list "$LIST_ADDRESS" could not be found. Quitting."
     return_code=1
   else
     for administrator in $LIST_ADMIN; do {
       if [[ -z "$($GPG_LIST_KEYS --fixed-list-mode "<$administrator>" 2> /dev/null | grep -v '^tru:')" ]]; then
-        ERROR_MESSAGE="$LIST_NAME: Public key for list administrator \"$(echo "$administrator" | tr '[:upper:]' '[:lower:]')\" could not be found"
-        return_code=1
+        ERROR_MESSAGE="\
+WARNING: $LIST_NAME: Public key for list administrator "$administrator" could not be found.
+WARNING: $LIST_NAME: Removing this address from LIST_ADMIN."
+      else
+        valid_admins="$valid_admins $administrator"
       fi; }
     done
+    LIST_ADMIN="$valid_admins"
   fi
 
   return $return_code
@@ -206,9 +229,10 @@ function GetMessage {
   #-------------------------------------------------------------
   # read message from STDIN
   #
-  # parameter(s): expects message from STDIN
+  # parameter(s): none
   # depends on function(s): none
-  # returns: 0 on success, 1 if there's no input
+  # returns: 0 on success,
+  #          1 if there's no input
   #-------------------------------------------------------------
 
   local -i return_code=0
@@ -217,7 +241,7 @@ function GetMessage {
   ORIG_MESSAGE="$(sed -ne '1,$p')"
 
   # check if message was successfully stored
-  if (( "${#ORIG_MESSAGE[*]}" == 0 )); then
+  if [[ -z "$ORIG_MESSAGE" ]]; then
     ERROR_MESSAGE="Message couldn't be read from standard input"
     return_code=1
   fi
@@ -226,70 +250,44 @@ function GetMessage {
 }
 
 
-function GetMessageHeaders {
-  #-------------------------------------------------------------
-  # get the message headers and store some of them on separate variables
-  #
-  # parameter(s): none
-  # depends on function(s): GetMessage
-  # returns: 0 on success, 1 if headers can't be located within the message
-  #-------------------------------------------------------------
-
-  local -i return_code=0
-  local -i first_blank_line
-
-  # find the first blank line in the message
-  first_blank_line=$(echo "$ORIG_MESSAGE" | grep -nm 1 '^$' | cut -d : -f 1)
-
-  # store everything up to this line in ORIG_MESSAGE_HEADERS
-  if [[ "$first_blank_line" != 0 ]]; then
-    ORIG_MESSAGE_HEADERS="$(echo "$ORIG_MESSAGE" | sed -ne "1,${first_blank_line}p" | sed -e :a -e '$!N;s/[ \t]*\n[ \t]\+/ /;ta' -e 'P;D')"
-  fi
-
-  # check if message headers could not be stored successfully
-  if (( "${#ORIG_MESSAGE_HEADERS[*]}" == 0 )); then
-    ERROR_MESSAGE="Message headers could not be located within this message"
-    return_code=1
-  # else, list ORIG_MESSAGE_HEADERS and get some specific headers for later use
-  else
-    FROM=$(echo "$ORIG_MESSAGE_HEADERS" | grep -im 1 '^From:' | cut -d : -f 2- | sed -e 's/^[ \t]\+//' -e 's/[ \t]\+$//')
-    SENDER_ADDRESS=$(if [[ -z "$(echo $FROM | grep '>$')" ]]; then echo $FROM; else echo $FROM | grep -o '<[^<>]*>$' | sed -e 's/[<>]//g'; fi)
-    SUBJECT=$(echo "$ORIG_MESSAGE_HEADERS" | grep -im 1 '^Subject:' | cut -d : -f 2- | sed -e 's/^[ \t]\+//' -e 's/[ \t]\+$//')
-    CONTENT_HEADERS=$(echo "$ORIG_MESSAGE_HEADERS" | sed -ne '/^Content-/p')
-    if [[ -n "$CONTENT_HEADERS" ]]; then
-      CONTENT_TYPE_SUBTYPE=$(echo "$CONTENT_HEADERS" | grep -iom 1 '^Content-Type:[^;]*' | cut -d : -f 2 | sed -e 's/^[ \t]\+//' -e 's/[ \t]\+$//')
-    fi
-  fi
-
-  return $return_code
-}
-
-
 function GetGpgMessage {
   #-------------------------------------------------------------
   # get the gpg encrypted part of the message
   #
   # parameter(s): none
   # depends on function(s): GetMessage
-  # returns: 0 on success, 1 if encrypted bloc can't be located within the message
+  # returns: 0 on success,
+  #          1 if encrypted bloc can't be located within the message
   #-------------------------------------------------------------
 
   local -i return_code=0
-  local -i encrypted_bloc_begins
-  local -i encrypted_bloc_ends
-
-  # find the beginning and the end of the encrypted bloc, if any
-  encrypted_bloc_begins=$(echo "$ORIG_MESSAGE" | grep -nm 1 -- '-----BEGIN PGP MESSAGE-----' | cut -d : -f 1)
-  encrypted_bloc_ends=$(echo "$ORIG_MESSAGE" | grep -nm 1 -- '-----END PGP MESSAGE-----' | cut -d : -f 1)
 
-  # if there's an encrypted bloc, store it in ORIG_GPG_MESSAGE
-  if [[ "$encrypted_bloc_begins" != 0 && "$encrypted_bloc_ends" != 0 ]]; then
-    ORIG_GPG_MESSAGE="$(echo "$ORIG_MESSAGE" | sed -ne "${encrypted_bloc_begins},${encrypted_bloc_ends}p")"
+  # find the first blank line in the message
+  FIRST_BLANK_LINE=$(echo "$ORIG_MESSAGE" | grep -nm 1 '^$' | cut -d : -f 1)
+
+  # then, find the beginning of the encrypted bloc
+  if [[ -n $FIRST_BLANK_LINE ]]; then
+    ENCRYPTED_BLOC_BEGINS=$(echo "$ORIG_MESSAGE" | sed -ne "$(($FIRST_BLANK_LINE + 1)),\$p" | grep -nm 1 -- '^-----BEGIN PGP MESSAGE-----' | cut -d : -f 1)
+
+    # and then find the end of the bloc
+    if [[ -n $ENCRYPTED_BLOC_BEGINS ]]; then
+      ENCRYPTED_BLOC_BEGINS=$(($ENCRYPTED_BLOC_BEGINS + $FIRST_BLANK_LINE))
+      ENCRYPTED_BLOC_ENDS=$(echo "$ORIG_MESSAGE" | sed -ne "$((ENCRYPTED_BLOC_BEGINS + 1)),\$p" | grep -nm 1 -- '^-----END PGP MESSAGE-----' | cut -d : -f 1)
+
+      # if there's an encrypted bloc, store it in ORIG_GPG_MESSAGE
+      if [[ -n $ENCRYPTED_BLOC_ENDS ]]; then
+        ENCRYPTED_BLOC_ENDS=$(($ENCRYPTED_BLOC_ENDS + $ENCRYPTED_BLOC_BEGINS))
+        ORIG_GPG_MESSAGE="$(
+          echo "$ORIG_MESSAGE" | \
+          sed -ne "$((${ENCRYPTED_BLOC_ENDS} + 1))q;${ENCRYPTED_BLOC_BEGINS},${ENCRYPTED_BLOC_ENDS}p"
+        )"
+      fi
+    fi
   fi
 
   # check if the bloc was successfully stored
-  if (( "${#ORIG_GPG_MESSAGE[*]}" == 0 )); then
-    ERROR_MESSAGE="No valid GPG encrypted bloc found within this message"
+  if [[ -z "$ORIG_GPG_MESSAGE" ]]; then
+    ERROR_MESSAGE="No valid GPG encrypted bloc found within the message"
     return_code=1
   fi
 
@@ -297,19 +295,64 @@ function GetGpgMessage {
 }
 
 
-function GetGpgDecryptStderr {
+function ParseGpgDecryptStderr {
   #-------------------------------------------------------------
-  # discard $GPG_DECRYPT STDOUT and get its STDERR for signature checking
+  # parse $GPG_DECRYPT STDERR for signature checking
   #
   # parameter(s): none
   # depends on function(s): DeclareGpgVars, GetGpgMessage
-  # returns: 0 if signature in ORIG_GPG_MESSAGE is valid,
-  #          1 if signature is invalid,
-  #          2 for all other errors (incorrect passphrase, no encrypted data, etc.)
+  # returns: 0
   #-------------------------------------------------------------
 
-  GPG_DECRYPT_STDERR="$(echo -e "${PASSPHRASE}\n${ORIG_GPG_MESSAGE}" | ($GPG_DECRYPT --status-fd 2 1> /dev/null) 2>&1 )"
+  local gpg_decrypt_stderr
+
+  # get GPG_DECRYPT STDERR, discarding its STDOUT
+  gpg_decrypt_stderr="$(
+    echo -e "${PASSPHRASE}\n${ORIG_GPG_MESSAGE}" | \
+    ($GPG_DECRYPT --status-fd 2 1> /dev/null) 2>&1
+  )"
+
+  # check if message was encrypted with the list's public key
+  if
+    echo "$gpg_decrypt_stderr" | \
+    grep -q "^\[GNUPG:] ENC_TO $(
+      $GPG_LIST_KEYS $LIST_ADDRESS | \
+      sed -ne '/^sub:[^ired]:/p' -e '/:[sca]*[^e][sca]*:$/d' | \
+      cut -d : -f 5
+    )"
+  then
+    ENCRYPTED_TO_LIST=1
+
+    # if it was, check if its signature is valid
+    if
+      echo "$gpg_decrypt_stderr" | \
+      grep -q '^\[GNUPG:] GOODSIG'
+    then
+      GOOD_SIGNATURE=1
+
+    # else, check if the signature is invalid (BAD signature)
+    elif
+      echo "$gpg_decrypt_stderr" | \
+      grep -q '^\[GNUPG:] BADSIG'
+    then
+      BAD_SIGNATURE=1
+
+    # else, check if the signature couldn't be verified
+    elif
+      echo "$gpg_decrypt_stderr" | \
+      grep -q '^\[GNUPG:] ERRSIG'
+    then
+      SIGNATURE_CHECKING_FAILED=1
+
+    # else, check if the message could at least be decrypted
+    elif
+      echo "$gpg_decrypt_stderr" | \
+      grep -q '^\[GNUPG:] DECRYPTION_OKAY'
+    then
+      MESSAGE_DECRYPTION_OKAY=1
 
+    fi
+  fi
 }
 
 
@@ -319,23 +362,26 @@ function GetSubscribersList {
   #
   # parameter(s): none
   # depends on function(s): DeclareGpgVars
-  # returns: 0 on success, 1 if there are no valid subscribers on list
+  # returns: 0 on success,
+  #          1 if there are no valid subscribers on list
   #-------------------------------------------------------------
 
   local -i return_code=0
 
-  # get subscribers' email addresses, excluding invalid, revoked, expired and disabled keys,
-  #+as well as any signing only keys
-  SUBSCRIBERS_LIST="$($GPG_LIST_KEYS 2> /dev/null | \
-    sed -ne "/$LIST_ADDRESS/Id" -e '/^pub:[ired]:/d' -e '/:[^:]*D[^:]*:[^:]*$/d' -e '/:[^E:]*:[^:]*$/d' -e '/^pub/p' | \
+  # get subscribers' email addresses, excluding invalid, revoked,
+  #+expired and disabled keys, as well as any signing only keys
+  SUBSCRIBERS_LIST="$(
+    $GPG_LIST_KEYS 2> /dev/null | \
+    sed -ne "/$LIST_ADDRESS/Id" -e '/:[scaeSCA]*[^E][scaeSCA]*:$/d' -e '/:[scaeSCAE]*D[scaeSCAE]*:$/d' -e '/^pub:[^ired]:/p' | \
     cut -d : -f 10 | \
     grep -o '<[^<>]*>$' | \
     sed -e 's/[<>]//g' | \
-    sort -d)"
+    sort -d
+  )"
 
   # check if the list has valid subscribers
-  if (( "${#SUBSCRIBERS_LIST[*]}" == 0 )); then
-    ERROR_MESSAGE="$LIST_NAME: No valid subscribers on list \"$(echo "$LIST_ADDRESS" | tr '[:upper:]' '[:lower:]')\""
+  if [[ -z "$SUBSCRIBERS_LIST" ]]; then
+    ERROR_MESSAGE="FATAL: $LIST_NAME: No valid subscribers on list \"$LIST_ADDRESS\""
     return_code=1
   fi
 
@@ -343,225 +389,311 @@ function GetSubscribersList {
 }
 
 
-function DecryptGpgMessage {
+function GetMessageHeadersAndBody {
   #-------------------------------------------------------------
-  # decrypt the gpg encrypted part of the message
+  # store the message headers and body in two separate variables
   #
   # parameter(s): none
-  # depends on function(s): DeclareGpgVars, GetGpgMessage
+  # depends on function(s): GetMessage, GetGpgMessage
   # returns: 0
   #-------------------------------------------------------------
 
-  DECRYPTED_MESSAGE="$(echo -e "${PASSPHRASE}\n${ORIG_GPG_MESSAGE}" | $GPG_DECRYPT 2> /dev/null)"
-
+  # store everything up to the first blank line in ORIG_MESSAGE_HEADERS,
+  #+unfolding any folded headers
+  ORIG_MESSAGE_HEADERS="$(
+    echo "$ORIG_MESSAGE" | \
+    sed -ne "${FIRST_BLANK_LINE}q;1,$(($FIRST_BLANK_LINE - 1))p" | \
+    sed -e :a -e '$!N;s/[ \t]*\n[ \t]\+/ /;ta' -e 'P;D'
+  )"
+
+  # store everything after this line in ORIG_MESSAGE_BODY
+  ORIG_MESSAGE_BODY="$(
+    echo "$ORIG_MESSAGE" | \
+    sed -ne "$(($FIRST_BLANK_LINE + 1)),\$p"
+  )"
 }
 
 
-function MimeWrapMessage {
+function EditListMessageHeaders {
   #-------------------------------------------------------------
-  # MIME wrap message to be sent, adding needed headers and body parts
+  # edit the headers of a list message, removing specific lines, adding
+  #+a prefix to the Subject, etc
   #
   # parameter(s): none
-  # depends on function(s): GetMessageHeaders, EncryptAndSendListMessage,
-  #                         EncryptAndSendWarningMessage
+  # depends on function(s): GetMessageHeadersAndBody
   # returns: 0
   #-------------------------------------------------------------
 
-  local boundary
+  local header
+  local sed_args
 
-  if [[ "$CONTENT_TYPE_SUBTYPE" == "multipart/encrypted" ]]; then
-    boundary="---------------firma$( date "+%s" | md5sum | cut -c 1-15 | tr '[:lower:]' '[:upper:]' )"
+  # remove headers as/if defined by firma configuration file
+  if [[ -n "$REMOVE_THESE_HEADERS_ON_ALL_LISTS" ]]; then
+    for header in $REMOVE_THESE_HEADERS_ON_ALL_LISTS; do
+      sed_args="$sed_args -e /^${header}/Id"
+    done
 
-    # these are the headers of the message to be sent, so no indentation here
-    MESSAGE_HEADERS="\
-From: $FROM
-${RECIPIENTS}
-Reply-To: $LIST_ADDRESS
-Subject: ${SUBJECT_PREFIX}${SUBJECT}
-MIME-Version: 1.0
-Content-Type: multipart/encrypted;
-  protocol=\"application/pgp-encrypted\";
-  boundary=\"${boundary}\"
-Content-Disposition: inline"
+    MESSAGE_HEADERS="$(
+      echo "$ORIG_MESSAGE_HEADERS" | \
+      sed $sed_args
+    )"
+  fi
 
-    # this is the body of the message to be sent, so no indentation here
-    MESSAGE_BODY="\
-This is an OpenPGP/MIME encrypted message (RFC 2440 and 3156)
---$boundary
-Content-Type: application/pgp-encrypted
-Content-Description: PGP/MIME version identification
+  # remove additional headers as/if defined by the list configuration file
+  if [[ -n "$REMOVE_THESE_HEADERS" ]]; then
 
-Version: 1
+    # remove local variables contents, in case they have been used above
+    header=''
+    sed_args=''
 
---$boundary
-Content-Type: application/octet-stream; name=\"encrypted.asc\"
-Content-Disposition: inline; filename=\"encrypted.asc\"
-Content-Description: OpenPGP encrypted message
+    for header in $REMOVE_THESE_HEADERS; do
+      sed_args="$sed_args -e /^${header}/Id"
+    done
 
-${GPG_MESSAGE}
+    MESSAGE_HEADERS="$(
+      echo "$MESSAGE_HEADERS" | \
+      sed $sed_args
+    )"
+  fi
 
---${boundary}--"
+  # insert/replace the Reply-To header
+  if [[ -n "$REPLIES_SHOULD_GO_TO_LIST" ]]; then
 
-  else
+    if ! echo "$MESSAGE_HEADERS" | \
+         grep -iq '^Reply-To:'; then
 
-    # these are the headers of the message to be sent, so no indentation here
-    MESSAGE_HEADERS="\
-From: $FROM
-${RECIPIENTS}
-Reply-To: $LIST_ADDRESS
-Subject: ${SUBJECT_PREFIX}${SUBJECT}\
-$(
-  if [[ -n "${CONTENT_HEADERS}" ]]; then
-    echo -e "\nMIME-Version: 1.0"
-    echo "${CONTENT_HEADERS}" | sed -e 's/;/;\n /g'
-  else
-    echo -n
-  fi
-)"
+      # these are the headers of the message to be sent, so no indentation here
+      MESSAGE_HEADERS="\
+$MESSAGE_HEADERS
+Reply-To: $LIST_ADDRESS"
 
-    # this is the body of the message to be sent, so no indentation here
-    MESSAGE_BODY="${GPG_MESSAGE}"
+    else
+      MESSAGE_HEADERS="$(
+        echo "$MESSAGE_HEADERS" | \
+        sed -e "s/^Reply-To:.*$/Reply-To: $LIST_ADDRESS/I"
+      )"
+    fi
   fi
 
-  # assemble entire message
-  MESSAGE="\
-${MESSAGE_HEADERS}
+  # insert the Subject prefix, if any
+  if [[ -n "$SUBJECT_PREFIX" ]]; then
 
-${MESSAGE_BODY}"
+    # first, check if there's a Subject line
+    if echo "$MESSAGE_HEADERS" | grep -iq '^Subject:'; then
+
+      # and then check if the Subject already contains the list prefix
+      if ! echo "$MESSAGE_HEADERS" | \
+           grep -im 1 '^Subject:' | \
+           grep -qF "$SUBJECT_PREFIX"; then
+
+        # if it doesn't, insert it
+        MESSAGE_HEADERS="$(
+          echo "$MESSAGE_HEADERS" | \
+          sed -e "s/^Subject:[ \t]*/Subject: $SUBJECT_PREFIX/I"
+        )"
+      fi
+
+    # else, if there's no Subject line, add one containing only the list prefix
+    else
+
+      # these are the headers of the message to be sent, so no indentation here
+      MESSAGE_HEADERS="\
+$MESSAGE_HEADERS
+Subject: $SUBJECT_PREFIX"
+
+    fi
+  fi
 }
 
 
-function EncryptAndSendListMessage {
+function DecryptGpgMessage {
   #-------------------------------------------------------------
-  # send the message to list members
+  # decrypt the gpg encrypted part of the message
   #
   # parameter(s): none
-  # depends on function(s): DeclareGpgVars, DecryptGpgMessage, GetSubscribersList,
-  #                         MimeWrapMessage
+  # depends on function(s): DeclareGpgVars, GetGpgMessage
   # returns: 0
   #-------------------------------------------------------------
 
-  local subscriber
+  DECRYPTED_MESSAGE="$(
+    echo -e "${PASSPHRASE}\n${ORIG_GPG_MESSAGE}" | \
+    $GPG_DECRYPT 2> /dev/null
+  )"
+}
 
-  # check if message should be encrypted to all subscribers at once
-  if [[ "$USE_GPG_HIDDEN_RECIPIENT_OPTION" == 1 ]]; then
 
-    GPG_MESSAGE="$(echo -e "${PASSPHRASE}\n${DECRYPTED_MESSAGE}" | \
-      $GPG_ENCRYPT --group subscribers="$(echo $SUBSCRIBERS_LIST)" --hidden-recipient subscribers 2> /dev/null )"
+function ReplaceGpgMessage {
+  #-------------------------------------------------------------
+  # replace the original encrypted bloc by one generated for the list subscribers
+  #
+  # parameter(s): none
+  # depends on function(s): GetGpgMessage, GetMessageHeadersAndBody
+  # returns: 0
+  #-------------------------------------------------------------
 
-    # check if message should be sent to all subscribers at once
-    if [[ "$SEND_TO_ALL_AT_ONCE" == 1 ]]; then
+  MESSAGE_BODY="$(
+    echo "$ORIG_MESSAGE_BODY" | \
+    sed -e "$(($ENCRYPTED_BLOC_BEGINS - $FIRST_BLANK_LINE)),$(($ENCRYPTED_BLOC_ENDS - $FIRST_BLANK_LINE))c $(
+      echo "$GPG_MESSAGE" | \
+      sed -e '$! s/$/\\/'
+    )"
+  )"
+}
 
-      RECIPIENTS="To: $(echo $SUBSCRIBERS_LIST | sed -e 's/ /,\n  /g')"
-      if echo "$SUBJECT" | grep -qF "$SUBJECT_PREFIX"; then
-        SUBJECT_PREFIX=""
-      fi
-      MimeWrapMessage
-      echo "$MESSAGE" | $MAIL_AGENT $MAIL_AGENT_ARGS
 
-    # else, send the message to one subscriber at a time
-    else
-      for subscriber in $SUBSCRIBERS_LIST; do
+function GetSenderAddress {
+  #-------------------------------------------------------------
+  # get the sender address, needed for warning and bounce messages processing
+  #
+  # parameter(s): none
+  # depends on function(s): GetMessage
+  # returns: 0
+  #-------------------------------------------------------------
 
-        RECIPIENTS="To: $subscriber"
-        if echo "$SUBJECT" | grep -qF "$SUBJECT_PREFIX"; then
-          SUBJECT_PREFIX=""
-        fi
-        MimeWrapMessage
-        echo "$MESSAGE" | $MAIL_AGENT $MAIL_AGENT_ARGS
+  local from
 
-      done
+  from=$(echo "$ORIG_MESSAGE" | grep -im 1 '^From:')
+  SENDER_ADDRESS=$(
+    if [[ -z "$(echo $from | grep '>$')" ]]; then
+      echo $from
+    else
+      echo $from | grep -o '<[^<>]*>$' | sed -e 's/[<>]//g'
     fi
+  )
+}
 
-  # else, message should be encrypted and sent to one subscriber at a time
-  else
-    for subscriber in $SUBSCRIBERS_LIST; do
 
-      GPG_MESSAGE="$(echo -e "${PASSPHRASE}\n${DECRYPTED_MESSAGE}" | $GPG_ENCRYPT --recipient $subscriber )"
-      RECIPIENTS="To: $subscriber"
-      if echo "$SUBJECT" | grep -qF "$SUBJECT_PREFIX"; then
-        SUBJECT_PREFIX=""
-      fi
-      MimeWrapMessage
-      echo "$MESSAGE" | $MAIL_AGENT $MAIL_AGENT_ARGS
+function AssembleMessage {
+  #-------------------------------------------------------------
+  # just put the message headers and body together
+  #
+  # parameter(s): none
+  # depends on function(s): EditListMessageHeaders, ReplaceGpgMessage,
+  #                         ComposeAndSendWarningMessage,
+  #                         ComposeAndSendBounceMessage
+  # returns: 0
+  #-------------------------------------------------------------
+
+  # this is the actual message which will be sent, so no indentation here
+  MESSAGE="\
+$MESSAGE_HEADERS
+
+$MESSAGE_BODY"
 
-    done
-  fi
 }
 
 
-function EncryptAndSendWarningMessage {
+function ReEncryptAndSendListMessage {
   #-------------------------------------------------------------
-  # send a "BAD signature" warning message to the list administrator(s) and to sender
+  # send message to list subscribers
   #
   # parameter(s): none
-  # depends on function(s): DeclareGpgVars, GetMessageHeaders, DecryptGpgMessage,
-  #                         MimeWrapMessage
+  # depends on function(s): DeclareGpgVars, DecryptGpgMessage,
+  #                         GetSubscribersList, AssembleMessage
   # returns: 0
   #-------------------------------------------------------------
 
-  local email_address
+  local recipients
+  local subscriber
 
-  # check if message should be encrypted to all addresses at once
-  if [[ "$USE_GPG_HIDDEN_RECIPIENT_OPTION" == 1 ]]; then
+  recipients="$(echo $SUBSCRIBERS_LIST)"
 
-    GPG_MESSAGE="$(echo -e "${PASSPHRASE}\n$DECRYPTED_MESSAGE" | \
-      $GPG_ENCRYPT --group admin_and_sender="$LIST_ADMIN $SENDER_ADDRESS" --hidden-recipient admin_and_sender 2> /dev/null)"
+  # check if message should be encrypted and sent to all subscribers at once
+  if [[ "$USE_GPG_HIDDEN_RECIPIENT_OPTION" == 1 ]]; then
 
-    # check if message should be sent to all addresses at once
-    if [[ "$SEND_TO_ALL_AT_ONCE" == 1 ]]; then
+    GPG_MESSAGE="$(
+      echo -e "${PASSPHRASE}\n${DECRYPTED_MESSAGE}" | \
+      $GPG_ENCRYPT --group subscribers="$recipients" --hidden-recipient subscribers 2> /dev/null
+    )"
 
-      RECIPIENTS="To: $(echo "$LIST_ADMIN $SENDER_ADDRESS" | sed -e 's/ /,\n  /g' )"
-      SUBJECT_PREFIX="[BAD SIGNATURE] "
-      MimeWrapMessage
-      echo "$MESSAGE" | $MAIL_AGENT $MAIL_AGENT_ARGS
+    ReplaceGpgMessage
+    AssembleMessage
 
-    # else, send the message to one address at a time
-    else
-      for email_address in $LIST_ADMIN $SENDER_ADDRESS; do
+    # send message
+    echo "$MESSAGE" | $MAIL_AGENT $MAIL_AGENT_ARGS $recipients
 
-        RECIPIENTS="To: $email_address"
-        SUBJECT_PREFIX="[BAD SIGNATURE] "
-        MimeWrapMessage
-        echo "$MESSAGE" | $MAIL_AGENT $MAIL_AGENT_ARGS
+  # else, message should be encrypted and sent to one subscriber at a time
+  else
+    for subscriber in $recipients; do
 
-      done
-    fi
+      GPG_MESSAGE="$(
+        echo -e "${PASSPHRASE}\n${DECRYPTED_MESSAGE}" | \
+        $GPG_ENCRYPT --recipient $subscriber
+      )"
 
-  # else, message should be encrypted and sent to one address at a time
-  else
-    for email_address in $LIST_ADMIN $SENDER_ADDRESS; do
+      ReplaceGpgMessage
+      AssembleMessage
 
-      GPG_MESSAGE="$(echo -e "${PASSPHRASE}\n$DECRYPTED_MESSAGE" | $GPG_ENCRYPT --recipient $email_address 2> /dev/null )"
-      RECIPIENTS="To: $email_address"
-      SUBJECT_PREFIX="[BAD SIGNATURE] "
-      MimeWrapMessage
-      echo "$MESSAGE" | $MAIL_AGENT $MAIL_AGENT_ARGS
+      # send message
+      echo "$MESSAGE" | $MAIL_AGENT $MAIL_AGENT_ARGS $subscriber
 
     done
   fi
 }
 
 
-function SendBounceMessage {
+function ComposeAndSendWarningMessage {
   #-------------------------------------------------------------
-  # send a bounce message back to sender
+  # send a "BAD signature" warning to the list administrator(s) and to sender
   #
   # parameter(s): none
-  # depends on function(s): GetMessageHeaders, ProcessMessage
+  # depends on function(s): GetMessage, GetSenderAddress, AssembleMessage
   # returns: 0
   #-------------------------------------------------------------
 
-  # this is the body of the message to be sent, so no indentation here
-  echo "\
+  local recipients
+
+  recipients="$LIST_ADMIN $SENDER_ADDRESS"
+
+  # these are the headers of the message to be sent, so no indentation here
+  MESSAGE_HEADERS="\
 From: $LIST_ADDRESS
+Subject: BAD signature from $SENDER_ADDRESS
 To: $SENDER_ADDRESS
-Subject: [RETURNED MAIL] $SUBJECT
+Cc: $(echo $LIST_ADMIN | sed -e 's/ /, /g')"
+
+  # this is the body of the message to be sent, so no indentation here
+  MESSAGE_BODY="\
+-------- Original Message --------
+$ORIG_MESSAGE"
+
+  AssembleMessage
+
+  # send message
+  echo "$MESSAGE" | $MAIL_AGENT $MAIL_AGENT_ARGS $recipients
+}
+
+
+function ComposeAndSendBounceMessage {
+  #-------------------------------------------------------------
+  # send a bounce message back to sender
+  #
+  # parameter(s): none
+  # depends on function(s): GetMessage, GetSenderAddress, AssembleMessage
+  # returns: 0
+  #-------------------------------------------------------------
+
+  local subject
+
+  subject="$(echo "$ORIG_MESSAGE" | grep -im 1 '^Subject:' | cut -d : -f 2- )"
 
+  # these are the headers of the message to be sent, so no indentation here
+  MESSAGE_HEADERS="\
+From: $LIST_ADDRESS
+Subject: [RETURNED MAIL]$subject
+To: $SENDER_ADDRESS"
+
+  # this is the body of the message to be sent, so no indentation here
+  MESSAGE_BODY="\
 $MESSAGE_BODY
 
 -- 
-firma v$VERSION" | $MAIL_AGENT $MAIL_AGENT_ARGS
+firma"
+
+  AssembleMessage
+
+  # send message
+  echo "$MESSAGE" | $MAIL_AGENT $MAIL_AGENT_ARGS $SENDER_ADDRESS
 }
 
 
@@ -570,99 +702,120 @@ function ProcessMessage {
   # process a received message
   #
   # parameter(s): none
-  # depends on function(s): GetMessage, GetMessageHeaders, GetGpgMessage,
-  #                         GetGpgDecryptStderr, GetSubscribersList, DecryptGpgMessage,
-  #                         EncryptAndSendListMessage, EncryptAndSendWarningMessage,
-  #                         SendBounceMessage
-  # returns: 0 on success, 1 if any of the above functions return an error
+  # depends on function(s): GetMessage, GetGpgMessage, GetSubscribersList,
+  #                         GetSenderAddress
+  # returns: 0 on success,
+  #          1 if any of the above functions return an error
   #-------------------------------------------------------------
 
   local -i return_code=0
 
-  # try to read message from STDIN and to fetch its headers
-  if GetMessage && GetMessageHeaders; then
+  # try to read message from STDIN
+  if GetMessage; then
 
     # check if the message was encrypted
     if GetGpgMessage; then
-      GetGpgDecryptStderr
 
-      # and then check if the message was encrypted with the list's public key
-      if echo "$GPG_DECRYPT_STDERR" | grep -q "^\[GNUPG:] ENC_TO $($GPG_LIST_KEYS $LIST_ADDRESS | sed -ne '/^sub:[ire]:/d' -e '/:e:$/p' | cut -d : -f 5)"; then
+      # if it was, parse gpg decrypt STDERR to decide what to do next
+      ParseGpgDecryptStderr
 
-        # if signature in message is valid
-        if echo "$GPG_DECRYPT_STDERR" | grep -q '^\[GNUPG:] GOODSIG'; then
+      # if the message was encrypted with the list's public key and if the
+      #+message signature is valid, send message to list subscribers
+      if [[ $ENCRYPTED_TO_LIST == 1 && $GOOD_SIGNATURE == 1 ]]; then
 
-          # check if list has valid subscribers
-          if GetSubscribersList; then
+        # check if the list has valid subscribers
+        if GetSubscribersList; then
 
-            # if it does, decrypt, MIME wrap, re-encrypt and send the message
-            DecryptGpgMessage
-            EncryptAndSendListMessage
+          GetMessageHeadersAndBody
+          EditListMessageHeaders
+          DecryptGpgMessage
+          ReEncryptAndSendListMessage
 
-          else
-            return_code=1
-          fi
+        else
+          return_code=1
+        fi
 
-        # else, if signature is invalid, send a warning about this to the list administrator(s) and to sender
-        elif echo "$GPG_DECRYPT_STDERR" | grep -q '^\[GNUPG:] BADSIG'; then
-          EncryptAndSendWarningMessage
+      # else, if the message was correctly encrypted but its signature is invalid,
+      #+send a warning about this to the list administrator(s) and to sender
+      elif [[ $ENCRYPTED_TO_LIST == 1 && $BAD_SIGNATURE == 1 ]]; then
 
-        # else, if signature can't be checked, then probably the sender is not subscribed to the list
-        # send a note about this back to sender
-        elif echo "$GPG_DECRYPT_STDERR" | grep -q '^\[GNUPG:] ERRSIG'; then
+        GetSenderAddress
+
+        if [[ -n $(echo $LIST_ADMINS) || -n "$SENDER_ADDRESS" ]]; then
+          ComposeAndSendWarningMessage
+        fi
+
+      # else, a bounce should be sent
+      else
+
+        # if bounce processing is enabled, continue
+        if [[ "$SEND_BOUNCE_MESSAGES" == 1 ]]; then
+
+          GetSenderAddress
           if [[ -n "$SENDER_ADDRESS" ]]; then
 
-            # this is the body of the message to be sent, so no indentation here
-            MESSAGE_BODY="\
+            # if the message was encrypted with the list's public key
+            if [[ $ENCRYPTED_TO_LIST == 1 ]]; then
+
+              # then, if signature can't be checked, then probably the sender is not subscribed to the list
+              # send a bounce, if possible
+              if [[ $SIGNATURE_CHECKING_FAILED == 1 ]]; then
+
+                # this is the body of the message to be sent, so no indentation here
+                MESSAGE_BODY="\
  It was not possible to process this message. Your email
  address is not subscribed to this list. Contact the list
  administrator if you have any questions."
-            SendBounceMessage
-          fi
+                ComposeAndSendBounceMessage
 
-        # else, if message can be decrypted but its signature can't be checked, then message wasn't signed
-        # send a note about this back to sender
-        elif echo "$GPG_DECRYPT_STDERR" | grep -q '^\[GNUPG:] DECRYPTION_OKAY'; then
-          if [[ -n "$SENDER_ADDRESS" ]]; then
+              # or, if message can be decrypted but its signature can't be checked, then message wasn't signed
+              # send a bounce, if possible
+              elif [[ $MESSAGE_DECRYPTION_OKAY == 1 ]]; then
 
-            # this is the body of the message to be sent, so no indentation here
-            MESSAGE_BODY="\
+                # this is the body of the message to be sent, so no indentation here
+                MESSAGE_BODY="\
  It was not possible to process this message. Message was
  not signed. Contact the list administrator if you have any
  questions."
-            SendBounceMessage
-          fi
-        fi
+                ComposeAndSendBounceMessage
+              fi
 
-      # else, message wasn't encrypted with the list's public key
-      # send a note about this back to sender
-      else
-        if [[ -n "$SENDER_ADDRESS" ]]; then
+            # else, message wasn't encrypted with the list's public key
+            # send a bounce, if possible
+            else
 
-          # this is the body of the message to be sent, so no indentation here
-          MESSAGE_BODY="\
+              # this is the body of the message to be sent, so no indentation here
+              MESSAGE_BODY="\
  It was not possible to process this message. Message was
  not encrypted with the list's public key. Contact the list
  administrator if you have any questions."
-          SendBounceMessage
+              ComposeAndSendBounceMessage
+            fi
+          fi
         fi
       fi
 
     # else, message wasn't encrypted at all
-    # send a note about this back to sender
+    # send a bounce, if possible
     else
-      if [[ -n "$SENDER_ADDRESS" ]]; then
 
-        # this is the body of the message to be sent, so no indentation here
-        MESSAGE_BODY="\
+      # if bounce processing is enabled, continue
+      if [[ "$SEND_BOUNCE_MESSAGES" == 1 ]]; then
+
+        GetSenderAddress
+        if [[ -n "$SENDER_ADDRESS" ]]; then
+
+          # this is the body of the message to be sent, so no indentation here
+          MESSAGE_BODY="\
  It was not possible to process this message. Message was
  not encrypted. Contact the list administrator if you have
  have any questions."
-        SendBounceMessage
+          ComposeAndSendBounceMessage
+        fi
       fi
     fi
 
-  # else, message could not be read from STDIN or its headers could not be fetched
+  # else, message could not be read from STDIN
   else
     return_code=1
   fi
@@ -900,23 +1053,52 @@ VERSION="0.3"
 export LANG=en_US
 umask 0077
 
-# declare global variables used during execution
-GLOBAL_VARS="GPG_BINARY MAIL_AGENT MAIL_AGENT_ARGS LISTS_DIR LOG_TO_SYSLOG LOGGER \
-             SYSLOG_PRIORITY USE_GPG_HIDDEN_RECIPIENT_OPTION SEND_TO_ALL_AT_ONCE \
-             LIST_ADDRESS LIST_ADMIN LIST_HOMEDIR PASSPHRASE SUBJECT_PREFIX \
-             FIRMA_CONFIG_FILE VERSION \
-             ERROR_MESSAGE EXIT_CODE \
-             DESCRIPTION LIST_NAME LIST_PATH LIST_CONFIG_FILE \
-             GPG_FLAGS GPG GPG_LIST_KEYS GPG_DECRYPT GPG_ENCRYPT \
-             STDIN \
-             ORIG_MESSAGE \
-             ORIG_MESSAGE_HEADERS FROM SENDER_ADDRESS SUBJECT \
-             ORIG_GPG_MESSAGE \
-             GPG_DECRYPT_STDERR \
-             SUBSCRIBERS_LIST \
-             DECRYPTED_MESSAGE \
-             GPG_MESSAGE RECIPIENTS MESSAGE_HEADERS MESSAGE_BODY MESSAGE \
-             GLOBAL_VARS VAR"
+# declare global variables and functions used during execution
+GLOBAL_VARS="
+  GPG_BINARY MAIL_AGENT MAIL_AGENT_ARGS LISTS_DIR LOG_TO_SYSLOG LOGGER_BINARY SYSLOG_PRIORITY
+  USE_GPG_HIDDEN_RECIPIENT_OPTION REMOVE_THESE_HEADERS_ON_ALL_LISTS SEND_BOUNCE_MESSAGES
+  LIST_ADDRESS LIST_ADMIN LIST_HOMEDIR PASSPHRASE SUBJECT_PREFIX REMOVE_THESE_HEADERS REPLIES_SHOULD_GO_TO_LIST
+  FIRMA_CONFIG_FILE VERSION
+  ERROR_MESSAGE EXIT_CODE
+  DESCRIPTION LIST_NAME LIST_PATH LIST_CONFIG_FILE
+  GPG_FLAGS GPG GPG_LIST_KEYS GPG_DECRYPT GPG_ENCRYPT
+  STDIN
+  ORIG_MESSAGE
+  FIRST_BLANK_LINE ENCRYPTED_BLOC_BEGINS ENCRYPTED_BLOC_ENDS ORIG_GPG_MESSAGE
+  ENCRYPTED_TO_LIST GOOD_SIGNATURE BAD_SIGNATURE SIGNATURE_CHECKING_FAILED MESSAGE_DECRYPTION_OKAY
+  SUBSCRIBERS_LIST
+  ORIG_MESSAGE_HEADERS ORIG_MESSAGE_BODY
+  GPG_MESSAGE
+  DECRYPTED_MESSAGE
+  MESSAGE_HEADERS MESSAGE_BODY
+  MESSAGE
+  FUNCTION FUNCTIONS
+  GLOBAL_VARS VAR"
+
+FUNCTIONS="
+  Usage
+  Version
+  DeclareGpgVars
+  CheckFirmaConfigFile
+  CheckListConfigFile
+  GetMessage
+  GetGpgMessage
+  ParseGpgDecryptStderr
+  GetSubscribersList
+  GetMessageHeadersAndBody
+  EditListMessageHeaders
+  DecryptGpgMessage
+  ReplaceGpgMessage
+  GetSenderAddress
+  AssembleMessage
+  ReEncryptAndSendListMessage
+  ComposeAndSendWarningMessage
+  ComposeAndSendBounceMessage
+  ProcessMessage
+  NewList
+  ListAdministration
+  ChooseUid"
+
 for VAR in $GLOBAL_VARS; do
   declare $VAR
 done
@@ -1072,19 +1254,24 @@ case $# in
 # end main case
 esac
 
-# print/log error message, if any, and exit
+# print/log error message, if any
 if [[ -n "$ERROR_MESSAGE" ]]; then
   if [[ "$LOG_TO_SYSLOG" == 1 ]]; then
-    echo "$ERROR_MESSAGE" | $LOGGER -p "$SYSLOG_PRIORITY" -t "$(basename $0)"
+    echo "$ERROR_MESSAGE" | $LOGGER_BINARY -p "$SYSLOG_PRIORITY" -t "$(basename $0)"
   else
     echo >&2 "$(basename $0): $ERROR_MESSAGE"
   fi
 fi
 
-exit $EXIT_CODE
+# erase all functions and global variables
+for FUNCTION in $FUNCTIONS; do
+  unset -f $FUNCTION
+done
 
-# erase all global variables
 for VAR in $GLOBAL_VARS; do
   unset $VAR
 done
 
+# exit
+exit $EXIT_CODE
+