aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorluis <luis>2006-07-19 05:07:02 +0000
committerluis <luis>2006-07-19 05:07:02 +0000
commit8795f18c414a00967d37652b774e98b8553e8d7b (patch)
tree3e3e119a1a4b01a74e71c98597de3058167444f2
parentcfc69fcdf96f2773c000210775cbf987ca705fbd (diff)
downloadfirma-8795f18c414a00967d37652b774e98b8553e8d7b.tar.gz
firma-8795f18c414a00967d37652b774e98b8553e8d7b.tar.bz2
Lots of changes. Way too many to list here. I will wait until we have a
more solid version to write a decent changelog.
-rwxr-xr-xfirma1011
1 files changed, 597 insertions, 414 deletions
diff --git a/firma b/firma
index 0e389fb..b089e96 100755
--- a/firma
+++ b/firma
@@ -33,10 +33,15 @@
#
# 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
+# 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_MESSAGES_USING_BCC= set to '1' to send list messages to all subscribers
+# 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.)
#
@@ -44,6 +49,7 @@
#
# 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
#
@@ -54,69 +60,70 @@
# more than 4 times.
#
-FIRMA_CONFIG_FILE="/usr/local/etc/firma.conf"
-VERSION="0.3"
-
-function DeclareGpgVars {
+function Usage {
#-------------------------------------------------------------
- # declare gpg global variables
+ # display help
#
# parameter(s): none
# depends on function(s): none
# returns: 0
#-------------------------------------------------------------
- GPG_FLAGS="--no-options --no-default-keyring --homedir $LIST_HOMEDIR --quiet --batch --no-tty --no-use-agent --no-auto-check-trustdb --no-permission-warning"
- GPG="$GPG_BINARY $GPG_FLAGS"
- GPG_LIST_KEYS="$GPG --list-keys --with-colons"
- GPG_DECRYPT="$GPG --passphrase-fd 0 --decrypt"
- GPG_ENCRYPT="$GPG --armor --trust-model always --local-user $LIST_ADDRESS --no-emit-version --passphrase-fd 0 --sign --encrypt"
+
+ # this will be printed to STDOUT, so no indentation here
+ echo "\
+Usage: $(basename $0) OPTION [LIST-NAME]
+GnuPG-based encrypted mailing list manager.
+
+ -a, --admin-task LIST-NAME process administrative tasks on list
+ -c, --create-newlist LIST-NAME create a new mailing list
+ -h, --help display this help and exit
+ -p, --process-message LIST-NAME process a message sent to list
+ -v, --version output version information and exit
+
+If option -a is given, read standard input for tasks to be performed.
+Tasks can be one or more of the following:
+
+ use EMAIL-ADDRESS use the given address for message delivery instead
+ of the primary address on key
+
+Report bugs to <firma@sarava.org>"
}
-function Usage {
+function Version {
#-------------------------------------------------------------
- # display help and exit
+ # display version information
#
# parameter(s): none
# depends on function(s): none
# returns: 0
#-------------------------------------------------------------
- echo "Usage: $(basename $0) OPTION [LIST-NAME]"
- echo "GnuPG-based encrypted mailing list manager."
- echo
- echo " -a, --admin-task LIST-NAME process administrative tasks on list"
- echo " -c, --create-newlist LIST-NAME create a new mailing list"
- echo " -h, --help display this help and exit"
- echo " -p, --process-message LIST-NAME process a message sent to list"
-# echo " -r, --process-request LIST-NAME process administrative and user"
-# echo " requests on list."
- echo " -v, --version output version information and exit"
- echo
- echo "If option -a is given, read standard input for tasks to be performed."
- echo "Tasks can be one or more of the following:"
- echo
- echo " use EMAIL-ADDRESS use the given address for message delivery instead"
- echo " of the primary address on key"
- echo
- echo "Report bugs to <firma@sarava.org>"
+
+ # this will be printed to STDOUT, so no indentation here
+ echo "\
+firma $VERSION
+
+Copyright (C) 2005 A Firma, Inc.
+This program comes with ABSOLUTELY NO WARRANTY.
+This is free software, and you are welcome to redistribute it
+under certain conditions. See the GNU General Public License
+for more details."
}
-function Version {
+function DeclareGpgVars {
#-------------------------------------------------------------
- # output version information and exit
+ # declare GPG-related global variables
#
# parameter(s): none
# depends on function(s): none
# returns: 0
#-------------------------------------------------------------
- echo "firma $VERSION"
- echo
- echo "Copyright (C) 2005 A Firma, Inc."
- echo "This program comes with ABSOLUTELY NO WARRANTY."
- echo "This is free software, and you are welcome to redistribute it"
- echo "under certain conditions. See the GNU General Public License"
- echo "for more details."
+ GPG_FLAGS="--no-options --homedir $LIST_HOMEDIR --quiet --batch --no-tty --no-use-agent --no-permission-warning"
+ GPG="$GPG_BINARY $GPG_FLAGS"
+ GPG_LIST_KEYS="$GPG --list-keys --with-colons"
+ GPG_DECRYPT="$GPG --passphrase-fd 0 --decrypt"
+ GPG_ENCRYPT="$GPG --armor --trust-model always --local-user $LIST_ADDRESS --passphrase-fd 0 --no-emit-version --sign --encrypt"
}
@@ -128,19 +135,29 @@ function CheckFirmaConfigFile {
# depends on function(s): none
# returns: 0 if all checks are passed, 1 if any check fails
#-------------------------------------------------------------
+
+ local -i return_code=0
+
if [[ ! -f "$GPG_BINARY" || ! -x "$GPG_BINARY" ]]; then
- echo "$(basename $0): GPG binary ("$GPG_BINARY") could not be found."
- exit 1
+ ERROR_MESSAGE="GPG binary ("$GPG_BINARY") could not be found"
+ return_code=1
elif [[ ! -f "$MAIL_AGENT" || ! -x "$MAIL_AGENT" ]]; then
- echo "$(basename $0): Mail transport agent binary ("$MAIL_AGENT") could not be found."
- exit 1
+ ERROR_MESSAGE="Mail transport agent binary ("$MAIL_AGENT") could not be found"
+ return_code=1
elif [[ ! -d "$LISTS_DIR" ]]; then
- echo "$(basename $0): Lists directory ("$LISTS_DIR") could not be found."
- exit 1
+ ERROR_MESSAGE="Lists directory ("$LISTS_DIR") could not be found"
+ return_code=1
elif [[ "$USE_GPG_HIDDEN_RECIPIENT_OPTION" == "1" && "$($GPG_BINARY --version | head -n1 | tr -dc '[[:digit:]]')" -lt "140" ]]; then
- echo "$(basename $0): GPG's \"--hidden-recipient\" option is only available from version 1.4.0 onwards."
- exit 1
+ ERROR_MESSAGE="GPG's \"--hidden-recipient\" option is only available from version 1.4.0 onwards"
+ return_code=1
+ elif [[ "$LOG_TO_SYSLOG" == "1" ]]; then
+ if [[ ! -f "$LOGGER" || ! -x "$LOGGER" ]]; then
+ ERROR_MESSAGE="logger binary ("$LOGGER") could not be found"
+ return_code=1
+ fi
fi
+
+ return $return_code
}
@@ -153,39 +170,35 @@ function CheckListConfigFile {
# returns: 0 if all checks are passed, 1 if any check fails
#-------------------------------------------------------------
+ local -i return_code=0
local administrator
if [[ ! -d "$LIST_HOMEDIR" || ! -f "$LIST_HOMEDIR/pubring.gpg" || ! -f "$LIST_HOMEDIR/secring.gpg" ]]; then
- echo "$LIST_NAME: GPG home directory ("$LIST_HOMEDIR") or the GPG keyrings could not be found."
- exit 1
- elif [[ -z "$(cat $LIST_CONFIG_FILE | grep -o "^PASSPHRASE='[^']*'$")" || \
+ 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 -n "$PASSPHRASE" | wc -c)" -lt "25" || \
- -z "$(echo -n "$PASSPHRASE" | tr -dc '[[:lower:]]')" || \
- -z "$(echo -n "$PASSPHRASE" | tr -dc '[[:upper:]]')" || \
- -z "$(echo -n "$PASSPHRASE" | tr -dc '[[:digit:]]')" || \
- "$(echo -n "$PASSPHRASE" | tr -dc '[:punct:]' | wc -c)" -lt "5" || \
- "$(echo -n "$PASSPHRASE" | fold -w1 | uniq -cd | grep -v '^ \{6\}[234] ')" ]]; then
- echo "$LIST_NAME: PASSPHRASE is empty or does not meet the minimum complexity requirements."
- echo "$LIST_NAME: Please set a new passphrase for the list's private key. Make it at least"
- echo "$LIST_NAME: 25 characters long (using a combination of numbers, upper and lower case"
- echo "$LIST_NAME: letters and at least 5 special characters) and enclose it in 'single"
- echo "$LIST_NAME: quotes'. The passphrase itself, though, cannot contain any single quote."
- echo "$LIST_NAME: Also, no character should be sequentially repeated more than 4 times."
- exit 1
- elif [[ -z "$($GPG_LIST_KEYS --fixed-list-mode 2> /dev/null | grep ^uid | cut -d : -f 10 | grep -i "<$LIST_ADDRESS>$")" ]]; then
- echo "$LIST_NAME: Public key for list \"$(echo -ne "$LIST_ADDRESS" | tr '[:upper:]' '[:lower:]')\" could not be found."
- echo "$LIST_NAME: Note that this parameter expects an email address."
- exit 1
+ "$(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_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"
+ return_code=1
else
for administrator in $LIST_ADMIN; do {
- if [[ -z "$($GPG_LIST_KEYS --fixed-list-mode 2> /dev/null | grep ^uid | cut -d : -f 10 | grep -i "<$administrator>$")" ]]; then
- echo "$LIST_NAME: Public key for list administrator \"$(echo -ne "$administrator" | tr '[:upper:]' '[:lower:]')\" could not be found."
- echo "$LIST_NAME: Note that this parameter expects one or more space separated email addresses."
- exit 1
+ 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
fi; }
done
fi
+
+ return $return_code
}
@@ -198,109 +211,89 @@ function GetMessage {
# returns: 0 on success, 1 if there's no input
#-------------------------------------------------------------
- local stdin
- local element
+ local -i return_code=0
- # store message in array ORIG_MESSAGE
- while read stdin; do
- ORIG_MESSAGE[$element]="$stdin\n"
- ((++element))
- done
+ # store message in ORIG_MESSAGE
+ ORIG_MESSAGE="$(sed -ne '1,$p')"
- # check if message was successfully stored in ORIG_MESSAGE
- if [[ "${#ORIG_MESSAGE[@]}" -eq "0" ]]; then
- echo "$(basename $0): Message couldn't be read from standard input."
- exit 1
+ # check if message was successfully stored
+ if (( "${#ORIG_MESSAGE[*]}" == 0 )); then
+ ERROR_MESSAGE="Message couldn't be read from standard input"
+ return_code=1
fi
+
+ return $return_code
}
function GetMessageHeaders {
#-------------------------------------------------------------
- # get message headers and store some of them on separate variables
+ # 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 message
+ # returns: 0 on success, 1 if headers can't be located within the message
#-------------------------------------------------------------
- local element
- local first_blank_line
+ local -i return_code=0
+ local -i first_blank_line
# find the first blank line in the message
- for element in $(seq 0 $((${#ORIG_MESSAGE[@]} - 1))); do
- if [[ "${ORIG_MESSAGE[$element]}" == "\n" ]]; then
- first_blank_line=$element
- # store all lines up to this one in array ORIG_MESSAGE_HEADERS
- for element in $(seq 0 $(($first_blank_line - 1))); do
- ORIG_MESSAGE_HEADERS[$element]="${ORIG_MESSAGE[$element]}"
- done
- # done, exit for loop
- break 1
- fi
- done
+ 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 were successfully stored in ORIG_MESSAGE_HEADERS
- if [[ "${#ORIG_MESSAGE_HEADERS[@]}" -eq "0" ]]; then
- echo "$(basename $0): Message headers could not be located within this message."
- exit 1
+ # 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
- # list ORIG_MESSAGE_HEADERS and get some specific headers for later use
- FROM=$(echo -e "${ORIG_MESSAGE_HEADERS[@]}" | grep -m 1 '^ From:' | cut -d : -f 2- | sed -e 's/^ //')
- SENDER_ADDRESS=$(if [[ -z "$(echo $FROM | grep '>$')" ]]; then echo $FROM; else echo $FROM | grep -o '<[^<>]*>$' | sed -e 's/[<>]//g'; fi)
- DATE=$(echo -e "${ORIG_MESSAGE_HEADERS[@]}" | grep -m 1 '^ Date:' | cut -d : -f 2- | sed -e 's/^ //')
- SUBJECT=$(echo -e "${ORIG_MESSAGE_HEADERS[@]}" | grep -m 1 '^ Subject:' | cut -d : -f 2- | sed -e 's/^ //')
+ return $return_code
}
function GetGpgMessage {
#-------------------------------------------------------------
- # get gpg encrypted part of a message
+ # get the gpg encrypted part of the message
#
# parameter(s): none
# depends on function(s): GetMessage
- # returns: 0 on success, 1 if no encrypted data is found within message
+ # returns: 0 on success, 1 if encrypted bloc can't be located within the message
#-------------------------------------------------------------
- # i = ORIG_MESSAGE element being processed
- # j = ORIG_GPG_MESSAGE element being processed
- local i
- local j
-
- # for elements in ORIG_MESSAGE, do
- for i in $(seq 0 $((${#ORIG_MESSAGE[@]} - 1))); do
-
- # find the first line of the encrypted data
- #+and assign it to the first element of ORIG_GPG_MESSAGE
- if [[ "${ORIG_MESSAGE[$i]}" == "-----BEGIN PGP MESSAGE-----\n" ]]; then
- ORIG_GPG_MESSAGE[$j]="${ORIG_MESSAGE[$i]}"
+ local -i return_code=0
+ local -i encrypted_bloc_begins
+ local -i encrypted_bloc_ends
- # move to next element in both arrays
- ((++i))
- ((++j))
+ # 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)
- # until the end of the encrypted data is reached,
- #+assign subsequent elements in ORIG_MESSAGE to elements of ORIG_GPG_MESSAGE
- until [[ "${ORIG_MESSAGE[$i]}" == "-----END PGP MESSAGE-----\n" ]]; do
- ORIG_GPG_MESSAGE[$j]="${ORIG_MESSAGE[$i]}"
- ((++i))
- ((++j))
- done
-
- # last, assign the line matched above to the last element of ORIG_GPG_MESSAGE
- ORIG_GPG_MESSAGE[$j]="${ORIG_MESSAGE[$i]}"
- # no need to process lines beyond this point,
- #+exit for loop
- break 1
- fi
- done
+ # 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")"
+ fi
- # check if encrypted data was stored in ORIG_GPG_MESSAGE
- if [[ "${#ORIG_GPG_MESSAGE[@]}" -eq "0" ]]; then
- echo "$(basename $0): No GPG encrypted data was found within this message."
- return 1
+ # check if the bloc was successfully stored
+ if (( "${#ORIG_GPG_MESSAGE[*]}" == 0 )); then
+ ERROR_MESSAGE="No valid GPG encrypted bloc found within this message"
+ return_code=1
fi
+
+ return $return_code
}
@@ -315,7 +308,7 @@ function GetGpgDecryptStderr {
# 2 for all other errors (incorrect passphrase, no encrypted data, etc.)
#-------------------------------------------------------------
- GPG_DECRYPT_STDERR="$(echo -e "$PASSPHRASE\n${ORIG_GPG_MESSAGE[@]}" | sed -e 's/^ //' | ($GPG_DECRYPT --status-fd 2 1> /dev/null) 2>&1)"
+ GPG_DECRYPT_STDERR="$(echo -e "${PASSPHRASE}\n${ORIG_GPG_MESSAGE}" | ($GPG_DECRYPT --status-fd 2 1> /dev/null) 2>&1 )"
}
@@ -326,132 +319,225 @@ function GetSubscribersList {
#
# parameter(s): none
# depends on function(s): DeclareGpgVars
- # returns: 0 on success, 1 if there are no subscribers on list
+ # returns: 0 on success, 1 if there are no valid subscribers on list
#-------------------------------------------------------------
- if [[ "$($GPG_LIST_KEYS 2> /dev/null | sed -ne "/$LIST_ADDRESS/Id" -e '/^pub:[ire]:/d' -e '/^pub/p' | wc -l)" -ne "0" ]]; then
- SUBSCRIBERS_LIST="$($GPG_LIST_KEYS 2> /dev/null | sed -ne "/$LIST_ADDRESS/Id" -e '/^pub:[ire]:/d' -e '/^pub/p' | cut -d : -f 10 | grep -o '<[^<>]*>$' | sed -e 's/[<>]//g')"
- else
- echo "$LIST_NAME: There are no valid subscribers on list \"$(echo "$LIST_ADDRESS" | tr '[:upper:]' '[:lower:]')\"."
- exit 1
+ 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' | \
+ cut -d : -f 10 | \
+ grep -o '<[^<>]*>$' | \
+ sed -e 's/[<>]//g' | \
+ 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:]')\""
+ return_code=1
fi
+
+ return $return_code
}
-function SendListMessage {
+function DecryptGpgMessage {
#-------------------------------------------------------------
- # compose and send a message to list members
+ # decrypt the gpg encrypted part of the message
#
# parameter(s): none
- # depends on function(s): DeclareGpgVars, GetMessageHeaders, GetGpgMessage,
- # GetGpgDecryptStderr, GetSubscribersList
- # returns: 0 on success
+ # depends on function(s): DeclareGpgVars, GetGpgMessage
+ # returns: 0
#-------------------------------------------------------------
- local subscriber
+ DECRYPTED_MESSAGE="$(echo -e "${PASSPHRASE}\n${ORIG_GPG_MESSAGE}" | $GPG_DECRYPT 2> /dev/null)"
- GetSubscribersList
+}
- # check if gpg's --hidden-recipient option should be used
- if [[ "$USE_GPG_HIDDEN_RECIPIENT_OPTION" == "1" ]]; then
+
+function MimeWrapMessage {
+ #-------------------------------------------------------------
+ # MIME wrap message to be sent, adding needed headers and body parts
+ #
+ # parameter(s): none
+ # depends on function(s): GetMessageHeaders, EncryptAndSendListMessage,
+ # EncryptAndSendWarningMessage
+ # returns: 0
+ #-------------------------------------------------------------
+
+ local boundary
+
+ if [[ "$CONTENT_TYPE_SUBTYPE" == "multipart/encrypted" ]]; then
+ boundary="---------------firma$( date "+%s" | md5sum | cut -c 1-15 | tr '[:lower:]' '[:upper:]' )"
+
+ # 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"
# 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
+
+Version: 1
+
+--$boundary
+Content-Type: application/octet-stream; name=\"encrypted.asc\"
+Content-Disposition: inline; filename=\"encrypted.asc\"
+Content-Description: OpenPGP encrypted message
+
+${GPG_MESSAGE}
+
+--${boundary}--"
+
+ else
+
+ # 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
+)"
+
+ # this is the body of the message to be sent, so no indentation here
+ MESSAGE_BODY="${GPG_MESSAGE}"
+ fi
+
+ # assemble entire message
+ MESSAGE="\
+${MESSAGE_HEADERS}
+
+${MESSAGE_BODY}"
+}
+
+
+function EncryptAndSendListMessage {
+ #-------------------------------------------------------------
+ # send the message to list members
+ #
+ # parameter(s): none
+ # depends on function(s): DeclareGpgVars, DecryptGpgMessage, GetSubscribersList,
+ # MimeWrapMessage
+ # returns: 0
+ #-------------------------------------------------------------
+
+ local subscriber
+
+ # check if message should be encrypted to all subscribers at once
+ if [[ "$USE_GPG_HIDDEN_RECIPIENT_OPTION" == 1 ]]; then
-MESSAGE_BODY="$(echo "$PASSPHRASE
-Message from: $FROM
-Subject: $SUBJECT
-Date: $DATE
+ GPG_MESSAGE="$(echo -e "${PASSPHRASE}\n${DECRYPTED_MESSAGE}" | \
+ $GPG_ENCRYPT --group subscribers="$(echo $SUBSCRIBERS_LIST)" --hidden-recipient subscribers 2> /dev/null )"
-$(echo "$GPG_DECRYPT_STDERR" | grep -E '^gpg: Signature made|^gpg: Good signature from|^gpg: *aka')
+ # check if message should be sent to all subscribers at once
+ if [[ "$SEND_TO_ALL_AT_ONCE" == 1 ]]; then
-$(echo -e "$PASSPHRASE\n${ORIG_GPG_MESSAGE[@]}" | $GPG_DECRYPT 2> /dev/null | mimencode -q -u | sed -e '/^Content-/d' -e 's/ÿ//')" | $GPG_ENCRYPT --group subscribers="$(echo $SUBSCRIBERS_LIST)" --hidden-recipient subscribers 2> /dev/null)"
+ 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
- # now send the message, either using BCC to sent it to all subscribers at
- #+once or sending it separately to each one of them
- if [[ "$SEND_MESSAGES_USING_BCC" == "1" ]]; then
- echo -e "From: $LIST_ADDRESS\nBCC: $(echo $SUBSCRIBERS_LIST)\nSubject: none\n\n$MESSAGE_BODY" | $MAIL_AGENT $MAIL_AGENT_ARGS
+ # else, send the message to one subscriber at a time
else
for subscriber in $SUBSCRIBERS_LIST; do
- echo -e "From: $LIST_ADDRESS\nTo: $subscriber\nSubject: none\n\n$MESSAGE_BODY" | $MAIL_AGENT $MAIL_AGENT_ARGS
+
+ RECIPIENTS="To: $subscriber"
+ if echo "$SUBJECT" | grep -qF "$SUBJECT_PREFIX"; then
+ SUBJECT_PREFIX=""
+ fi
+ MimeWrapMessage
+ echo "$MESSAGE" | $MAIL_AGENT $MAIL_AGENT_ARGS
+
done
fi
- # else, if gpg's --hidden-recipient option should not be used,
- #+encrypt and send message separately to each list subscriber
+ # else, message should be encrypted and sent to one subscriber at a time
else
for subscriber in $SUBSCRIBERS_LIST; do
- # this is the body of the message to be sent, so no indentation here
-
-MESSAGE_BODY="$(echo "$PASSPHRASE
-Message from: $FROM
-Subject: $SUBJECT
-Date: $DATE
-
-$(echo "$GPG_DECRYPT_STDERR" | grep -E '^gpg: Signature made|^gpg: Good signature from|^gpg: *aka')
-
-$(echo -e "$PASSPHRASE\n${ORIG_GPG_MESSAGE[@]}" | $GPG_DECRYPT 2> /dev/null | mimencode -q -u | sed -e '/^Content-/d' -e 's/ÿ//')" | $GPG_ENCRYPT --recipient $subscriber 2> /dev/null)"
+ 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
- # now send the message
- echo -e "From: $LIST_ADDRESS\nTo: $subscriber\nSubject: none\n\n$MESSAGE_BODY" | $MAIL_AGENT $MAIL_AGENT_ARGS
done
fi
}
-function SendWarningMessage {
+function EncryptAndSendWarningMessage {
#-------------------------------------------------------------
- # compose and send a "BAD signature" warning to the list administrator(s) and to sender
+ # send a "BAD signature" warning message to the list administrator(s) and to sender
#
# parameter(s): none
- # depends on function(s): DeclareGpgVars, GetMessageHeaders, GetGpgMessage, GetGpgDecryptStderr
- # returns: 0 on success
+ # depends on function(s): DeclareGpgVars, GetMessageHeaders, DecryptGpgMessage,
+ # MimeWrapMessage
+ # returns: 0
#-------------------------------------------------------------
local email_address
- # check if gpg's --hidden-recipient option should be used
- if [[ "$USE_GPG_HIDDEN_RECIPIENT_OPTION" == "1" ]]; then
-
- # this is the body of the message to be sent, so no indentation here
+ # check if message should be encrypted to all addresses at once
+ if [[ "$USE_GPG_HIDDEN_RECIPIENT_OPTION" == 1 ]]; then
-MESSAGE_BODY="$(echo "$PASSPHRASE
-Message from: $FROM
-Subject: [BAD SIGNATURE] $SUBJECT
-Date: $DATE
+ 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)"
-$(echo "$GPG_DECRYPT_STDERR" | grep -E '^gpg: Signature made|^gpg: BAD signature from|^gpg: *aka')
+ # check if message should be sent to all addresses at once
+ if [[ "$SEND_TO_ALL_AT_ONCE" == 1 ]]; then
-$(echo -e "$PASSPHRASE\n${ORIG_GPG_MESSAGE[@]}" | $GPG_DECRYPT 2> /dev/null | mimencode -q -u | sed -e '/^Content-/d' -e 's/ÿ//')" | $GPG_ENCRYPT --group addresses="$LIST_ADMIN $SENDER_ADDRESS" --hidden-recipient addresses 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
- # now send the message, either using BCC to sent it to all addresses at
- #+once or sending it separately to each one of them
- if [[ "$SEND_MESSAGES_USING_BCC" == "1" ]]; then
- echo -e "From: $LIST_ADDRESS\nBCC: $LIST_ADMIN $SENDER_ADDRESS\nSubject: none\n\n$MESSAGE_BODY" | $MAIL_AGENT $MAIL_AGENT_ARGS
+ # else, send the message to one address at a time
else
for email_address in $LIST_ADMIN $SENDER_ADDRESS; do
- echo -e "From: $LIST_ADDRESS\nTo: $email_address\nSubject: none\n\n$MESSAGE_BODY" | $MAIL_AGENT $MAIL_AGENT_ARGS
+
+ RECIPIENTS="To: $email_address"
+ SUBJECT_PREFIX="[BAD SIGNATURE] "
+ MimeWrapMessage
+ echo "$MESSAGE" | $MAIL_AGENT $MAIL_AGENT_ARGS
+
done
fi
- # else, if gpg's --hidden-recipient option should not be used,
- #+encrypt and send message separately to each address
+ # else, message should be encrypted and sent to one address at a time
else
for email_address in $LIST_ADMIN $SENDER_ADDRESS; do
- # this is the body of the message to be sent, so no indentation here
+ 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
-MESSAGE_BODY="$(echo "$PASSPHRASE
-Message from: $FROM
-Subject: [BAD SIGNATURE] $SUBJECT
-Date: $DATE
-
-$(echo "$GPG_DECRYPT_STDERR" | grep -E '^gpg: Signature made|^gpg: BAD signature from|^gpg: *aka')
-
-$(echo -e "$PASSPHRASE\n${ORIG_GPG_MESSAGE[@]}" | $GPG_DECRYPT 2> /dev/null | mimencode -q -u | sed -e '/^Content-/d' -e 's/ÿ//')" | $GPG_ENCRYPT --recipient $email_address 2> /dev/null)"
-
- # now send the message
- echo -e "From: $LIST_ADDRESS\nTo: $email_address\nSubject: none\n\n$MESSAGE_BODY" | $MAIL_AGENT $MAIL_AGENT_ARGS
done
fi
}
@@ -462,18 +548,15 @@ function SendBounceMessage {
# send a bounce message back to sender
#
# parameter(s): none
- # depends on function(s): GetMessageHeaders
- # returns: 0 on success
+ # depends on function(s): GetMessageHeaders, ProcessMessage
+ # returns: 0
#-------------------------------------------------------------
# this is the body of the message to be sent, so no indentation here
-echo "From: $LIST_ADDRESS
+ echo "\
+From: $LIST_ADDRESS
To: $SENDER_ADDRESS
-Subject: none
-
-Message from: $FROM
Subject: [RETURNED MAIL] $SUBJECT
-Date: $DATE
$MESSAGE_BODY
@@ -487,63 +570,104 @@ function ProcessMessage {
# process a received message
#
# parameter(s): none
- # depends on function(s): GetMessage, GetMessageHeaders, GetGpgMessage, GetGpgDecryptStderr,
- # SendListMessage, SendWarningMessage, SendBounceMessage
- # returns: 0 on success
+ # 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
#-------------------------------------------------------------
- GetMessage
- GetMessageHeaders
- GetGpgMessage
- GetGpgDecryptStderr
+ local -i return_code=0
- # first, 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
+ # try to read message from STDIN and to fetch its headers
+ if GetMessage && GetMessageHeaders; then
- # if signature in message is valid, decrypt, re-encrypt and send it to list subscribers
- if echo "$GPG_DECRYPT_STDERR" | grep -q '^\[GNUPG:] GOODSIG'; then
- SendListMessage
+ # check if the message was encrypted
+ if GetGpgMessage; then
+ GetGpgDecryptStderr
- # 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
- SendWarningMessage
+ # 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
- # 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
+ # if signature in message is valid
+ if echo "$GPG_DECRYPT_STDERR" | grep -q '^\[GNUPG:] GOODSIG'; then
+
+ # check if list has valid subscribers
+ if GetSubscribersList; then
+
+ # if it does, decrypt, MIME wrap, re-encrypt and send the message
+ DecryptGpgMessage
+ EncryptAndSendListMessage
- # this is the body of the message to be sent, so no indentation here
- MESSAGE_BODY="\
+ 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 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
+ 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. Your email
address is not subscribed to this list. Contact the list
administrator if you have any questions."
- SendBounceMessage
+ SendBounceMessage
+ fi
- # else, if message can be decrypted but its signature can't be checked, then message wasn't signed
- # send a note about this to sender
- elif echo "$GPG_DECRYPT_STDERR" | grep -q '^\[GNUPG:] DECRYPTION_OKAY'; then
+ # 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
- # 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
+ SendBounceMessage
+ fi
+ fi
- # else, message wasn't encrypted with the list's public key
- # send a note about this to sender
- else
+ # 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
- # 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
+ SendBounceMessage
+ fi
+ fi
+
+ # else, message wasn't encrypted at all
+ # send a note about this back to sender
+ else
+ 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
+ fi
+ fi
+
+ # else, message could not be read from STDIN or its headers could not be fetched
+ else
+ return_code=1
fi
+
+ return $return_code
}
@@ -556,26 +680,22 @@ function NewList {
# returns: 0 on success, 1 if list already exists or cannot be created
#-------------------------------------------------------------
- if [ ! -d "$LIST_PATH" ]; then
- echo creating folder $LIST_PATH...
- mkdir "$LIST_PATH" # || (echo "$(basename $0): error creating $LIST_PATH: installation aborted"; exit 1)
- echo "creating list config file and will ask some questions."
+ local -i return_code=0
- # commented:
-# read -rep "path to smtp command (e.g., /usr/sbin/sendmail): " MAIL_AGENT
-# read -rep "command-line arguments passed to the smtp wrapper (e.g., -oem -oi -t): " MAIL_AGENT_ARGS
-# read -rep "path to gpg binary (e.g., /usr/bin/gpg): " GPG_BINARY
+ if [ ! -d "$LIST_PATH" ]; then
- # if [ ! -x "$GPG_BINARY" ]; then
+ echo "Creating folder $LIST_PATH..."
+ if mkdir "$LIST_PATH"; then # || (echo "$(basename $0): error creating $LIST_PATH: installation aborted"; exit 1)
+ echo "creating list config file and will ask some questions."
- # removed: (defaults to $LIST_HOMEDIR)
- read -rep "list keyring folder: " LIST_HOMEDIR
+ read -rep " List keyring location: ("$LIST_PATH") " LIST_HOMEDIR
+ LIST_HOMEDIR=${LIST_HOMEDIR:-"$LIST_PATH"}
- # todo: please no utf-8 (see DETAILS)
- read -rep "list email (e.g., firma@domain.tld): " LIST_ADDRESS
- read -rep "list admins emails (space delimited): " LIST_ADMIN
- read -rep "list description (fake?): " DESCRIPTION
- read -resp "password for list keyring (use a huge one): " PASSPHRASE
+ # NAO USAR UTF-8 (VER DETAILS)
+ read -rep " List email address: " LIST_ADDRESS
+ read -rep " List administrator(s) email address(es) (space delimited): " LIST_ADMIN
+ read -rep " List description (optional): " DESCRIPTION
+ read -resp " Passphrase to protect the list's secret key: " PASSPHRASE
# todo: key specs (size, expiry date...)
@@ -605,11 +725,18 @@ function NewList {
EOF
+ else
+ echo "$(basename $0): cannot create $LIST_PATH: Installation aborted"
+ return_code=1
+ fi
+
fi
else
echo "$(basename $0): cannot create $LIST_NAME: List already exists"
- exit 1
+ return_code=1
fi
+
+ return return_code
}
@@ -617,33 +744,37 @@ function ListAdministration {
#-------------------------------------------------------------
# process administrative tasks
#
- # parameter(s): expects task to be performed (plus its argument(s)) from STDIN
+ # parameter(s): task to be performed (plus its argument(s))
# depends on function(s): ChooseUid
# returns: 0 if task is executed successfully,
- # 1 if task can't be executed (command not found, too many/missing arguments, etc.)
+ # 1 if task can't be executed (command not found, too many/missing arguments, etc.),
+ # 2 if a quit command is entered
#-------------------------------------------------------------
+ local -i return_code=0
+
case $# in
1)
case $1 in
help)
- echo
- echo " quit quit this prompt"
- echo " help show this help"
- echo " use EMAIL-ADDRESS use the given address for message delivery instead"
- echo " of the primary address on key"
- echo
+ # this will be printed to STDOUT, so no indentation here
+ echo "
+ quit quit this prompt
+ help show this help
+ use EMAIL-ADDRESS use the given address for message delivery instead
+ of the primary address on key
+"
;;
quit)
- exit 0
+ return_code=2
;;
use)
echo >&2 "$1: missing arguments (try \"help\")"
- return 1
+ return_code=1
;;
*)
echo >&2 "Command not found -- $1 (try \"help\")"
- return 1
+ return_code=1
;;
esac
;;
@@ -651,20 +782,20 @@ function ListAdministration {
case $1 in
use)
# check if argument is an email address
- if [[ -z "$(echo -ne $2 | grep -o '[^@]\+@[^@]\+')" ]]; then
+ if [[ -z "$(echo $2 | grep -o '[^@]\+@[^@]\+')" ]]; then
echo >&2 "$1: invalid argument -- $2 (try \"help\")"
- return 1
+ return_code=1
else
ChooseUid $2
fi
;;
help|quit)
echo >&2 "$1: too many arguments -- $@ (try \"help\")"
- return 1
+ return_code=1
;;
*)
echo >&2 "Command not found -- $1 (try \"help\")"
- return 1
+ return_code=1
;;
esac
;;
@@ -672,22 +803,24 @@ function ListAdministration {
case $1 in
help|quit|use)
echo >&2 "$1: too many arguments -- $@ (try \"help\")"
- return 1
+ return_code=1
;;
*)
echo >&2 "Command not found -- $1 (try \"help\")"
- return 1
+ return_code=1
;;
esac
;;
esac
+
+ return $return_code
}
function ChooseUid {
#-------------------------------------------------------------
# choose which UID of a public key should be used for message delivery,
- # deleting all other UIDs on this key
+ #+deleting all other UIDs on the key
#
# parameter(s): chosen email address
# depends on function(s): DeclareGpgVars
@@ -695,81 +828,102 @@ function ChooseUid {
# 1 if task can't be executed (public key not found, only one UID on key, etc.)
#-------------------------------------------------------------
+ local -i return_code=0
local keyid="$($GPG_LIST_KEYS --with-fingerprint $1 2> /dev/null | grep ^fpr | cut -d : -f 10 | grep -o '.\{8\}$')"
- local uid_count="$($GPG_LIST_KEYS --fixed-list-mode $keyid 2> /dev/null | grep ^uid | wc -l)"
- local chosen_uid_number="$($GPG_LIST_KEYS --fixed-list-mode $keyid 2> /dev/null | grep ^uid | grep -ni $1 | cut -d : -f 1)"
+ local -i uid_count="$($GPG_LIST_KEYS --fixed-list-mode $keyid 2> /dev/null | grep ^uid | wc -l)"
+ local -i chosen_uid_number="$($GPG_LIST_KEYS --fixed-list-mode $keyid 2> /dev/null | grep ^uid | grep -ni $1 | cut -d : -f 1)"
# check if supplied address is associated with a public key
- if [[ -z "$($GPG_LIST_KEYS --fixed-list-mode $1 2> /dev/null | grep -i "<$1>:$")" ]]; then
- echo >&2 "use: \"$(echo -ne $1 | tr '[:upper:]' '[:lower:]')\" is not associated with any public key on this keyring."
- return 1
+ if [[ -z "$($GPG_LIST_KEYS --fixed-list-mode "<$1>" 2> /dev/null | grep -v '^tru:')" ]]; then
+ echo >&2 "use: \"$(echo $1 | tr '[:upper:]' '[:lower:]')\" is not associated with any public key on this keyring."
+ return_code=1
# then check if there's more than one UID on this public key
- elif [[ "$($GPG_LIST_KEYS --fixed-list-mode $1 2> /dev/null | grep ^uid | wc -l)" -eq "1" ]]; then
- echo >&2 "use: \"$(echo -ne $1 | tr '[:upper:]' '[:lower:]')\" is part of the only UID on public key \"$keyid\"."
- return 1
+ elif (( "$($GPG_LIST_KEYS --fixed-list-mode $1 2> /dev/null | grep ^uid | wc -l)" == 1 )); then
+ echo >&2 "use: \"$(echo $1 | tr '[:upper:]' '[:lower:]')\" is part of the only UID on public key \"$keyid\"."
+ return_code=1
# and then check if there's only one public key associated with this address
- elif [[ "$($GPG_LIST_KEYS --fixed-list-mode $1 2> /dev/null | grep -i "<$1>:$" | wc -l)" -gt "1" ]]; then
- echo >&2 "use: \"$(echo -ne $1 | tr '[:upper:]' '[:lower:]')\" is listed in more than one UID on this keyring."
+ elif (( "$($GPG_LIST_KEYS --fixed-list-mode $1 2> /dev/null | grep -i "<$1>:$" | wc -l)" > 1 )); then
+ echo >&2 "use: \"$(echo $1 | tr '[:upper:]' '[:lower:]')\" is listed in more than one UID on this keyring."
echo >&2 "Delete all but one of the public keys or UIDs associated with this email address."
- return 1
+ return_code=1
fi
# if all checks are OK, run the expect script bellow
- expect -nN -- << EOF
- # no output to STDOUT
- log_user 0
- # set a 5 seconds timeout in case anything goes wrong
- set timeout 5
-
- # call gpg with the "--edit-key" option
- eval spawn -noecho $GPG --with-colons --command-fd 0 --status-fd 1 --edit-key $keyid
- expect "GET_LINE keyedit.prompt" {
- # select for deletion all UIDs other than the chosen one
- set uid 1
- while { \$uid <= $uid_count } {
- if { \$uid != $chosen_uid_number } {
- send "uid \$uid\n"
- expect "GET_LINE keyedit.prompt"
+ if (( $return_code == 0 )); then
+ expect -nN -- << EOF
+ # no output to STDOUT
+ log_user 0
+ # set a 5 seconds timeout in case anything goes wrong
+ set timeout 5
+
+ # call gpg with the "--edit-key" option
+ eval spawn -noecho $GPG --with-colons --command-fd 0 --status-fd 1 --edit-key $keyid
+ expect "GET_LINE keyedit.prompt" {
+ # select for deletion all UIDs other than the chosen one
+ set uid 1
+ while { \$uid <= $uid_count } {
+ if { \$uid != $chosen_uid_number } {
+ send "uid \$uid\n"
+ expect "GET_LINE keyedit.prompt"
+ }
+ set uid [incr uid]
}
- set uid [incr uid]
+ # delete selected UIDs
+ send "deluid\n"
+ # confirm deletion
+ expect "GET_BOOL keyedit.remove.uid.okay" {send "yes\n"}
+ # save and exit
+ expect "GET_LINE keyedit.prompt" {send "save\n"}
+ expect "GOT_IT"
}
- # delete selected UIDs
- send "deluid\n"
- # confirm deletion
- expect "GET_BOOL keyedit.remove.uid.okay" {send "yes\n"}
- # save and exit
- expect "GET_LINE keyedit.prompt" {send "save\n"}
- expect "GOT_IT"
- }
-
- # delay until the process above terminates
- wait
- # send following message to user
- send_user "use: \"$(echo -ne $1 | tr '[:upper:]' '[:lower:]')\" chosen for message delivery. [ expr $uid_count - 1 ] UID(s) deleted from public key \"$keyid\".\n"
- exit
+ # delay until the process above terminates
+ wait
+ # send following message to user
+ send_user "use: \"$(echo $1 | tr '[:upper:]' '[:lower:]')\" chosen for message delivery. [ expr $uid_count - 1 ] UID(s) deleted from public key \"$keyid\".\n"
+ exit
EOF
+ fi
+
+ return $return_code
}
#-------------------------------------------------------------
# main()
#-------------------------------------------------------------
+# path to firma.conf and firma version
+FIRMA_CONFIG_FILE="/usr/local/etc/firma.conf"
+VERSION="0.3"
+
# set environmental variables and options
export LANG=en_US
umask 0077
-# declare global arrays and variables used during execution
-GLOBAL_ARRAYS="ORIG_MESSAGE ORIG_MESSAGE_HEADERS ORIG_GPG_MESSAGE"
-for ARRAY in $GLOBAL_ARRAYS; do
- declare -a $ARRAY
-done
-
-GLOBAL_VARS="GPG_BINARY MAIL_AGENT MAIL_AGENT_ARGS LISTS_DIR USE_GPG_HIDDEN_RECIPIENT_OPTION SEND_MESSAGES_USING_BCC LIST_ADDRESS LIST_ADMIN LIST_HOMEDIR PASSPHRASE FIRMA_CONFIG_FILE VERSION GPG_FLAGS GPG GPG_LIST_KEYS GPG_DECRYPT GPG_ENCRYPT FROM DATE SUBJECT SENDER_ADDRESS GPG_DECRYPT_STDERR SUBSCRIBERS_LIST MESSAGE_BODY DESCRIPTION LIST_NAME LIST_PATH LIST_CONFIG_FILE STDIN GLOBAL_ARRAYS ARRAY GLOBAL_VARS VAR"
+# 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"
for VAR in $GLOBAL_VARS; do
- declare VAR
+ declare $VAR
done
+# set initial exit code
+EXIT_CODE=0
+
# command line parsing:
# first check number of arguments, then check what was entered
# start main case
@@ -777,118 +931,140 @@ case $# in
0)
echo >&2 "$(basename $0): missing arguments"
Usage
- exit 1
+ EXIT_CODE=1
;;
1)
# start case #1
case $1 in
-h|--help)
Usage
+ EXIT_CODE=0
;;
-v|--version)
Version
+ EXIT_CODE=0
;;
# valid option called without its required argument
- -a|--admin-task|-c|--create-newlist|-p|--process-message|-r|--list-request)
+ -a|--admin-task|-c|--create-newlist|-p|--process-message)
echo >&2 "$(basename $0): missing arguments"
Usage
- exit 1
+ EXIT_CODE=1
;;
*)
echo >&2 "$(basename $0): invalid option -- $1"
Usage
- exit 1
+ EXIT_CODE=1
;;
# end case #1
esac
;;
2)
-
- # if firma.conf exists, evaluate its parameters and check them
+ # if firma.conf exists
if [ -f "$FIRMA_CONFIG_FILE" ]; then
+
+ # evaluate its parameters
shopt -u sourcepath && source "$FIRMA_CONFIG_FILE"
- CheckFirmaConfigFile
- else
- echo >&2 "$(basename $0): cannot source \`$FIRMA_CONFIG_FILE': No such file"
- exit 1
- fi
- LIST_NAME="$2"
- LIST_PATH="$LISTS_DIR/$LIST_NAME"
- LIST_CONFIG_FILE="$LIST_PATH/$LIST_NAME.conf"
+ # set SYSLOG_PRIORITY to the default value, if needed
+ if [[ "$LOG_TO_SYSLOG" == 1 ]]; then
+ SYSLOG_PRIORITY=${SYSLOG_PRIORITY:-"user.err"}
+ fi
- # start case #2
- # branch directly to options which don't use a configuration
- #+file or, for those which do, branch to a new case bellow
- case $1 in
- -c|--create-newlist)
- NewList
- ;;
- -a|--admin-task|-p|--process-message|-r|--list-request)
-
- # if the list configuration file exists, disable "sourcepath"
- #+and evaluate list parameters
- if [[ -f "$LIST_CONFIG_FILE" ]]; then
- shopt -u sourcepath && source "$LIST_CONFIG_FILE"
- else
- echo >&2 "$(basename $0): cannot source \`$LIST_CONFIG_FILE': No such file"
- exit 1
- fi
+ # and finally check firma.conf
+ if CheckFirmaConfigFile; then
- # get gpg parameters and check the list configuration file
- DeclareGpgVars
- CheckListConfigFile
+ LIST_NAME="$2"
+ LIST_PATH="$LISTS_DIR/$LIST_NAME"
+ LIST_CONFIG_FILE="$LIST_PATH/$LIST_NAME.conf"
- # start case #3
+ # start case #2
case $1 in
- -a|--admin-task)
-
- # read STDIN and, if line is not empty or commented, process command
- while read -rep "Command> " STDIN; do
- if [[ "$STDIN" && "$STDIN" != "#"* ]]; then
- ListAdministration $STDIN
+ -c|--create-newlist)
+ NewList
+ ;;
+ # options that depend on the list configuration file
+ -a|--admin-task|-p|--process-message)
+
+ # if the configuration file exists, disable bash's
+ #+sourcepath and evaluate list parameters
+ if [[ -f "$LIST_CONFIG_FILE" ]]; then
+ shopt -u sourcepath && source "$LIST_CONFIG_FILE"
+
+ # get gpg parameters
+ DeclareGpgVars
+
+ # check the list configuration file
+ if CheckListConfigFile; then
+
+ # start case #3
+ case $1 in
+ -a|--admin-task)
+
+ # while a quit command isn't entered (returns 2), read STDIN
+ while (( $EXIT_CODE != 2 )) && read -rep "Command> " STDIN; do
+ # if line is not empty or commented, process command
+ if [[ "$STDIN" && "$STDIN" != "#"* ]]; then
+ ListAdministration $STDIN
+ EXIT_CODE=$?
+ fi
+ done
+
+ # since quit was entered, exit without error
+ EXIT_CODE=0
+
+ ;;
+ -p|--process-message)
+ ProcessMessage
+ EXIT_CODE=$?
+ ;;
+ # end case #3
+ esac
+ # else, list configuration file checking returned an error
+ else
+ EXIT_CODE=$?
fi
- done
-
+ # else, list configuration file could not be found
+ else
+ ERROR_MESSAGE="Cannot source \`$LIST_CONFIG_FILE': No such file or directory"
+ EXIT_CODE=1
+ fi
;;
- -p|--process-message)
- ProcessMessage
+ # valid option called with too many arguments
+ -h|--help|-v|--version)
+ echo >&2 "$(basename $0): too many arguments -- $@"
+ Usage
+ EXIT_CODE=1
;;
- -r|--list-request)
- # not implemented yet
- #ListRequest
- exit 0
+ *)
+ echo >&2 "$(basename $0): invalid option -- $1"
+ Usage
+ EXIT_CODE=1
;;
- # end case #3
+ # end case #2
esac
- ;;
- # valid option called with too many arguments
- -h|--help|-v|--version)
- echo >&2 "$(basename $0): too many arguments -- $@"
- Usage
- exit 1
- ;;
- *)
- echo >&2 "$(basename $0): invalid option -- $1"
- Usage
- exit 1
- ;;
- # end case #2
- esac
+ # else, firma.conf checking returned an error
+ else
+ EXIT_CODE=$?
+ fi
+ # else, firma.conf could not be found
+ else
+ ERROR_MESSAGE="Cannot source \`$FIRMA_CONFIG_FILE': No such file or directory"
+ EXIT_CODE=1
+ fi
;;
*)
# start case #4
case $1 in
# again, valid option called with too many arguments
- -a|--admin-task|-c|--create-newlist|-h|--help|-p|--process-message|-r|--list-request|-v|--version)
+ -a|--admin-task|-c|--create-newlist|-h|--help|-p|--process-message|-v|--version)
echo >&2 "$(basename $0): too many arguments -- $@"
Usage
- exit 1
+ EXIT_CODE=1
;;
*)
echo >&2 "$(basename $0): invalid option -- $1"
Usage
- exit 1
+ EXIT_CODE=1
;;
# end case #4
esac
@@ -896,11 +1072,18 @@ case $# in
# end main case
esac
-# erase all global arrays and variables
-for ARRAY in $GLOBAL_ARRAYS; do
- unset $ARRAY
-done
+# print/log error message, if any, and exit
+if [[ -n "$ERROR_MESSAGE" ]]; then
+ if [[ "$LOG_TO_SYSLOG" == 1 ]]; then
+ echo "$ERROR_MESSAGE" | $LOGGER -p "$SYSLOG_PRIORITY" -t "$(basename $0)"
+ else
+ echo >&2 "$(basename $0): $ERROR_MESSAGE"
+ fi
+fi
+
+exit $EXIT_CODE
+# erase all global variables
for VAR in $GLOBAL_VARS; do
unset $VAR
done