Mercurial > hgrepos > FreeBSD > ports > sysutils > local-bsdtools
diff sbin/bsmtp2dma @ 124:47c7223bea76
Move bsmtp2dma to sbin.
This is because Bacula's bsmtp lives in sbin also.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Wed, 16 Oct 2019 00:49:48 +0200 |
| parents | bin/bsmtp2dma@397bf58e85d2 |
| children | 6be3742d21f7 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sbin/bsmtp2dma Wed Oct 16 00:49:48 2019 +0200 @@ -0,0 +1,344 @@ +#!/bin/sh +# -*- indent-tabs-mode: nil; -*- +: 'A simple replacement for Bacula `bsmtp` when the underlying mailer does +not listen on TCP ports (e.g. `dma`, `ssmtp` et al.). + +:Author: Franz Glasner +:Copyright: (c) 2019 Franz Glasner. + All rights reserved. +:License: BSD 3-Clause "New" or "Revised" License. + See LICENSE for details. + If you cannot find LICENSE see + <https://opensource.org/licenses/BSD-3-Clause> +:ID: @(#)@@PKGORIGIN@@ $HGid$ + +' + +VERSION="@@VERSION@@" + +USAGE=' +USAGE: bsmtp2dma [OPTIONS] RECIPIENT ... + +Options: + + -V Show the program version and usage and exit. + + -8 Does nothing. Just a compatibility option for `bsmtp`. + + -c ADDRESS Set then "CC:" header. + + -d n Does nothing. Just a compatibility option for `bsmtp`. + + -f ADDRESS Set the "From:" header. + + -h MAILHOST:PORT Does nothing. Just a compatibility option for `bsmtp`. + + -l NUMBER Does nothing. Just a compatibility option for `bsmtp`. + + -r ADDRESS Set the "Reply-To:" header + + -s SUBJECT Set the "Subject:" header + + +Usage: + + The body of the email message is read from standard input. Message is + ended by sending the `EOF` character (`Ctrl-D` on many systems) on the + start of a new line, much like many `mail` commands. + + +Files: + + The shell style configuration file in + + @@ETCDIR@@/bsmtp2dma.conf is + + sourced in at script start. + +' + +# +# Configuration directory +# +: ${CONFIGDIR:=@@ETCDIR@@} + +test -r "${CONFIGDIR}/bsmtp2dma.conf" && . "${CONFIGDIR}/bsmtp2dma.conf" + + +# +# Default configuration values +# +# `sendmail` is also valid for `dma` because of the mapping within +# `/etc/mail/mailer.conf` +# +: ${MAILER:=/usr/sbin/sendmail} + + +parse_addr() { + : 'Parse an possibly complex email address. + + Addresses can be of the form + + - Name Parts <user@domain.tld> + - user@domain.tld + + `Name Parts` may not contain ``<`` or ``>`` characters. + + Args: + _addr: the complex email address + + Returns: + 0 on success, 1 on errors + + Output (Globals): + email_name: the name part (or empty) + email_addr: the technical address part (or empty) + + ' + local _addr + + _addr="$1" + test -n "${_addr}" || return 1 + + if printf "%s" "${_addr}" | grep -q -E -e '^[^<>]+<[^<>]+@[^<>]+>$'; then + email_name=$(printf '%s' "${_addr}" | sed -E -e 's/[[:space:]]*<.+$//') + email_addr=$(printf '%s' "${_addr}" | sed -E -e 's/^[^<>]+<//' | sed -E -e 's/>$//') + return 0 + fi + if printf "%s" "${_addr}" | grep -q -E -e '^[^<>]+@[^<>]+$'; then + email_name="" + email_addr="${_addr}" + return 0 + fi + return 1 +} + + +send_mail() { + : 'Send the mail via the underlying configured mailer (dma, sendmail et al.). + + Args: + _recipient: The recipient name. + + Will be written into the "To:" header also. + + Input (Globals): + MAILER + MAILCONTENT + MAILFIFO_STDIN + MAILFIFO_STDOUT + CC + FROM + REPLYTO + SUBJECT + + Returns: + 0 on success, other values on errors or the error exit code from the + underlying mailer + + This procedure starts the configured mailer as coproc and sends + email headers and contents to the started mailer. + + ' + local _recipient _rc _oifs _text _pid_mailer _recipient_addr + local _from_from _from_addr _sender_addr _dummy + + _recipient="$1" + _rc=0 + + if parse_addr "${_recipient}"; then + _recipient_addr="${email_addr}" + else + echo "ERROR: unknown recipient address format in \`${_recipient}'" >&2 + return 1 + fi + _sender_addr="$(whoami)@$(hostname -f)" + if [ -z "${FROM}" ]; then + _from_addr="${_sender_addr}" + _from_from="${_from_addr}" + else + if parse_addr "${FROM}"; then + _from_from="${FROM}" + _from_addr="${email_addr}" + else + echo "ERROR: unknown sender name in \`${FROM}'" >&2 + return 1 + fi + fi + + mkfifo -m 0600 "${MAILFIFO_STDIN}" + _rc=$? + if [ ${_rc} -ne 0 ]; then + return ${_rc} + fi + mkfifo -m 0600 "${MAILFIFO_STDOUT}" + _rc=$? + if [ ${_rc} -ne 0 ]; then + rm -f "${MAILFIFO_STDIN}" + return ${_rc} + fi + + # + # Start the mailer **before** opening the pipe; otherwise a + # deadlock occurs + # + "$MAILER" -f "${_sender_addr}" "${_recipient_addr}" <${MAILFIFO_STDIN} >${MAILFIFO_STDOUT} & + _pid_mailer=$! + + exec 3>"${MAILFIFO_STDIN}" + exec 4<"${MAILFIFO_STDOUT}" + + printf "To: %s\n" "${_recipient}" >&3 + printf "From: %s\n" "${_from_from}" >&3 + if [ "${_sender_addr}" != "${_from_addr}" ]; then + printf "Sender: %s\n" "${_sender_addr}" >&3 + fi + if [ -n "${SUBJECT}" ]; then + printf "Subject: %s\n" "${SUBJECT}" >&3 + fi + if [ -n "${REPLYTO}" ]; then + # + # XXX TBD proper Reply-To header value checks: + # a comma separated list of full mail addresses + # + printf "Reply-To: %s\n" "${REPLYTO}" >&3 + fi + if [ -n "${CC}" ]; then + # + # XXX TBD proper CC header value checks: + # a comma separated list of full mail addresses + # + printf "Cc: %s\n" "${CC}" >&3 + fi + printf "\n" >&3 + + # preserve leading white space when reading with `read` + _oifs="$IFS" + IFS=" +" + cat "${MAILCONTENT}" | + while read _text; do + printf "%s\n" "$_text" >&3 + done + # not all mailer recognize this + # printf ".\n" >&3 + IFS="$_oifs" + + # close the fd to the pipe: coproc should get EOF and terminate + exec 3>&- + # read eventually remaining stuff from the mailer until EOF + IFS='' read _dummy <&4 + exec 4<&- + + wait $_pid_mailer + _rc=$? + + # we are done with the named pipes + rm -f "${MAILFIFO_STDIN}" + rm -f "${MAILFIFO_STDOUT}" + + return ${_rc} +} + + +while getopts "V8c:d:f:h:l:nr:s:" _opt; do + case ${_opt} in + V) + echo "bsmtp2dma v${VERSION} (rv:@@HGREVISION@@)" + echo "$USAGE" + exit 0; + ;; + 8) + : # VOID + ;; + c) + CC="$OPTARG" + ;; + d) + : # VOID + ;; + f) + FROM="$OPTARG" + ;; + h) + : # VOID + ;; + l) + : # VOID + ;; + r) + REPLYTO="$OPTARG" + ;; + s) + SUBJECT="$OPTARG" + ;; + \?) + exit 2; + ;; + *) + echo "ERROR: inconsistent option handling" >&2 + exit 2; + ;; + esac +done + +# return code +_rc=0 + +MAILTMPDIR="$(mktemp -d)" +MAILFIFO_STDIN="${MAILTMPDIR}/mail-stdin" +MAILFIFO_STDOUT="${MAILTMPDIR}/mail-stdout" +MAILCONTENT="${MAILTMPDIR}/mail-text" + +# +# Clean up existing temporary stuff on all sorts of exit +# (including the "exit" call (signal 0)) +# +trap 'if [ -d "${MAILTMPDIR}" ]; then rm -rf "${MAILTMPDIR}"; fi; exit;' 0 1 2 15 + +test -d "${MAILTMPDIR}" || { echo "ERROR: no existing private tmp dir" >&2; exit 1; } + +# +# Reset the Shell's option handling system to prepare for handling +# other arguments and probably command-local options +# +shift $((OPTIND-1)) +OPTIND=1 + +# early check whether some recipients are given +if [ $# -eq 0 ]; then + echo "ERROR: no recipient given" >&2 + exit 2; +fi + +# +# Collect the mail text from stdin into a temporary file +# +exec 3>"${MAILCONTENT}" +# preserve leading white space when reading with `read` +_oifs="$IFS" +IFS=" +" +while read _text; do + if [ "${_text}" = "." ]; then + break + else + printf "%s\n" "${_text}" >&3 + fi +done +exec 3>&- +IFS="$_oifs" + +# +# Now send the content of the collected mail content to all recipients +# +until [ $# -eq 0 ]; do + send_mail "$1" + _rcsm=$? + if [ \( ${_rcsm} -ne 0 \) -a \( ${_rc} -eq 0 \) ]; then + _rc=${_rcsm} + fi + shift +done + +exit ${_rc}
