#!/bin/bash verwendung() { >&2 echo 'sendmailadvanced encrypts emails and generates stamps either in situ or in a pipe. For that it uses hashcash and gnupg.' >&2 echo '' >&2 echo 'Usage: sendmailadvanced [OPTIONS]' >&2 echo ' -h,--hook=hook activate hook; "ALL" chooses all available hooks' >&2 echo ' -i,--inline=file enhance email file in situ.' >&2 echo ' "--inline -" has the same effect as "-t".' >&2 echo ' Conflicts with -t|--no-inline.' >&2 echo ' -t,--no-inline enhance email from stdin to stdout.' >&2 echo ' Conflicts with -i|--inline.' >&2 echo ' -e,--[no-]encrypt do (not) encrypt' >&2 echo ' -s,--[no-]stamp do (not) stamp' >&2 echo \ '#HELPTEXT# #' exit 1 } neueAdressaten() { oldIFS="${IFS}" IFS=',' for adressat in ${adressatenString}; do if [[ "${adressat}" == *"<"*">"* ]]; then adressat="${adressat#*<}" adressat="${adressat%>*}" fi adressat="$( echo "${adressat}" | \ tr -d "[:space:]" )" echo "${adressat}" | grep -q "\S" || continue adressaten["${adressat}"]=1 done adressatenString="" IFS="${oldIFS}" } stempeln() { hashcash -b ${hashcash_bits} -Xm "${!adressaten[@]}" } gpgAdressaten() { for adressat in "${!adressaten[@]}"; do if \ ( gpg --with-colons --list-public-keys "${adressat}" 2> /dev/null | \ sed -n ' /^pub\(:[^:]*\)\{10\}:[^:]*E[^:]*:/,/^tru:/ p ' | \ grep '^uid:[mfuw]:' | \ cut -d: -f10 | \ tr '<>' '\n' | \ sort -u echo "${adressat}" ) | \ sort | \ uniq -d | \ grep -q '\S'; then echo -n "-r ${adressat} " else printf -- '-r %s ' "${gpg_recipients[@]}" fi done } gen_boundary() { [ -n "$1" ] && len=$1 || len=33 local bnd bnd='' while [ "${#bnd}" -lt ${len} ]; do bnd="${bnd}$( head -c1 /dev/urandom | \ base64 -w0 | \ tr -d '/+=' )" done echo "${bnd}" } datei="" encrypt=true stamp=true dForced=false eForced=false sForced=false if [ -r '#ETCDIR#/sendmailadvanced.conf' ]; then . '#ETCDIR#/sendmailadvanced.conf' else for konfig in $(readlink -f "$0").conf $(find . -name sendmailadvanced.conf 2> /dev/null); do [ -r "${konfig}" ] || continue . "${konfig}" break done fi # BUG: gpg_recipients should not be relevant, if non-default keys are being found [ -z "${gpg_recipients[*]}" ] && encrypt=false for gpg_recipient in "${gpg_recipients[@]}"; do gpg --list-keys "${gpg_recipient}" &> /dev/null || encrypt=false done command -v hashcash &> /dev/null || stamp=false eval set -- "$(getopt -o eh:i:st --long encrypt,no-encrypt,hook:,help,inline:,no-inline,stamp,no-stamp,version -n "$(basename "$0")" -- "$@" || echo verwendung)" while true; do case "$1" in -h|--hook) shift if printf '%s\n' "$1" | grep -qixF 'ALL'; then for hook in $(find '#ETCDIR#/sendmailadvanced.hooks' -type f); do if [ -x "${hook}" ] && [ -z "${hook%%#ETCDIR#/sendmailadvanced.hooks/*}" ]; then hooks[${#hooks[@]}]="${hook##ETCDIR#/sendmailadvanced.hooks/}" fi done else hooks[${#hooks[@]}]="$1" fi ;; --help) verwendung 0 ;; --version) echo '#VERSION#' exit 0 ;; -i|--inline) ${dForced} && verwendung shift datei="$1" [ "${datei}" == "-" ] && datei="" dForced=true ;; -t|--no-inline) ${dForced} && verwendung datei="" dForced=true ;; -e|--encrypt) ${eForced} && verwendung encrypt=true eForced=true ;; --no-encrypt) ${eForced} && verwendung encrypt=false eForced=true ;; -s|--stamp) ${sForced} && verwendung stamp=true sForced=true ;; --no-stamp) ${sForced} && verwendung stamp=false sForced=true ;; --) shift break ;; *) >&2 echo "FEHLER: Verstehe Option \"$1\" doch nicht! Ich beende." verwendung ;; esac shift done [ $# -ne 0 ] && verwendung MAILER="cantfind" for executable in sendmail msmtp; do for prefix in '#BINDIR#' '/usr/bin' '/usr/sbin' '/bin' '/sbin'; do [ "${MAILER}" == "cantfind" ] && [ -x "${prefix}/${executable}" ] && MAILER="${prefix}/${executable}" done done if [ -z "${datei}" ] && [ "${MAILER}" == "cantfind" ]; then >&2 echo "ERROR: Can't find suitable mailer." exit 1 fi { [ -z "${datei}" ] && cat || cat "${datei}" } | \ ( IFS='' adressatenString="" contentType='Content-Type: text/plain; charset=utf-8' contentTE='Content-Transfer-Encoding: 7bit' unset adressaten declare -A adressaten adressatenSammeln=false while read -r zeile; do echo "${zeile}" | grep -q "\S" || break s="${zeile}" if [ "${s:0:3}" == "To:" ] || [ "${s:0:3}" == "Cc:" ]; then adressatenSammeln=true s=" ,${s:4}" fi if [ ! "${s:0:1}" == " " ]; then ${adressatenSammeln} && neueAdressaten adressatenSammeln=false fi ${adressatenSammeln} && adressatenString="${adressatenString}${s}" if ${encrypt}; then if [ "${zeile:0:14}" == "Content-Type: " ]; then contentType="${zeile}" continue fi if [ "${zeile:0:27}" == "Content-Transfer-Encoding: " ]; then contentTE="${zeile}" continue fi if [ "${zeile:0:14}" == "MIME-Version: " ]; then continue fi fi echo "${zeile}" done ${stamp} && stempeln body=$( ( for hookParam in "${hooks[@]}"; do "#ETCDIR#/sendmailadvanced.hooks/${hookParam}" head echo done \ | sed ' /^$/ { :a N s@^\n$@@ ta } ' cat for hookParam in "${hooks[@]}"; do "#ETCDIR#/sendmailadvanced.hooks/${hookParam}" foot echo done \ | sed ' /^$/ { :a N s@^\n$@@ ta } ' ) | \ if ${encrypt}; then ( echo "${contentType}" echo "${contentTE}" echo '' cat echo '' ) | \ eval "gpg -e --no-tty --batch -a -s $(gpgAdressaten | sed 's| $||')" else cat fi ) \ || exit $? if ${encrypt}; then boundary="" while printf '%s\n' "${body}" | \ grep -qF "${boundary}"; do boundary=$(gen_boundary) done echo 'MIME-Version: 1.0' echo 'Content-Type: multipart/encrypted;' echo ' protocol="application/pgp-encrypted";' echo ' boundary="'"${boundary}"'"' echo '' echo 'This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)' echo '--'"${boundary}" echo 'Content-Type: application/pgp-encrypted' echo 'Content-Description: PGP/MIME version identification' echo '' echo 'Version: 1' echo '' echo '--'"${boundary}" echo 'Content-Type: application/octet-stream; name="encrypted.asc"' echo 'Content-Description: OpenPGP encrypted message' echo 'Content-Disposition: inline; filename="encrypted.asc"' fi printf '%s\n' \ "${zeile}" \ "${body}" if ${encrypt}; then echo '' echo '--'"${boundary}"'--' fi ) | \ ( if [ -z "${datei}" ]; then ${MAILER} -t -i else tmpFile="$(mktemp)" cat > "${tmpFile}" cat "${tmpFile}" > "${datei}" rm -f "${tmpFile}" fi )