Mercurial > hgrepos > FreeBSD > ports > sysutils > local-bsdtools
annotate bin/bsmtp2dma @ 119:5c92aeaec114
Try to make the mailer's input independent of a trailing single "." -- as opensmtpd's sendmail does not grok them.
Rely on closing fds in the proper order alone.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Tue, 15 Oct 2019 23:28:39 +0200 |
| parents | dad9f2d80c10 |
| children | 5366fb3b222c |
| rev | line source |
|---|---|
| 110 | 1 #!/bin/sh |
| 2 # -*- indent-tabs-mode: nil; -*- | |
| 3 : 'A simple replacement for Bacula `bsmtp` when the underlying mailer does | |
| 4 not listen on TCP ports (e.g. `dma`, `ssmtp` et al.). | |
| 5 | |
| 6 :Author: Franz Glasner | |
| 7 :Copyright: (c) 2019 Franz Glasner. | |
| 8 All rights reserved. | |
| 9 :License: BSD 3-Clause "New" or "Revised" License. | |
| 10 See LICENSE for details. | |
| 11 If you cannot find LICENSE see | |
| 12 <https://opensource.org/licenses/BSD-3-Clause> | |
| 13 :ID: @(#)@@PKGORIGIN@@ $HGid$ | |
| 14 | |
| 15 ' | |
| 16 | |
| 17 VERSION="@@VERSION@@" | |
| 18 | |
| 19 USAGE=' | |
| 20 USAGE: bsmtp2dma [OPTIONS] RECIPIENT ... | |
| 21 | |
| 22 Options: | |
| 23 | |
| 111 | 24 -V Show the program version and usage and exit. |
| 25 | |
| 110 | 26 -8 Does nothing. Just a compatibility option for `bsmtp`. |
| 27 | |
| 111 | 28 -c ADDRESS Set then "CC:" header. |
| 29 | |
| 110 | 30 -d n Does nothing. Just a compatibility option for `bsmtp`. |
| 31 | |
| 32 -f ADDRESS Set the "From:" header. | |
| 33 | |
| 34 -h MAILHOST:PORT Does nothing. Just a compatibility option for `bsmtp`. | |
| 35 | |
| 111 | 36 -l NUMBER Does nothing. Just a compatibility option for `bsmtp`. |
| 110 | 37 |
| 38 -r ADDRESS Set the "Reply-To:" header | |
| 39 | |
| 111 | 40 -s SUBJECT Set the "Subject:" header |
| 110 | 41 |
| 42 | |
| 43 Usage: | |
| 44 | |
| 45 The body of the email message is read from standard input. Message is | |
| 46 ended by sending the `EOF` character (`Ctrl-D` on many systems) on the | |
| 47 start of a new line, much like many `mail` commands. | |
| 48 | |
| 49 ' | |
| 50 | |
| 51 # | |
| 52 # Configuration directory | |
| 53 # | |
| 54 : ${CONFIGDIR:=@@ETCDIR@@} | |
| 55 | |
| 56 test -r "${CONFIGDIR}/bsmtp2dma.conf" && . "${CONFIGDIR}/bsmtp2dma.conf" | |
| 57 | |
| 58 | |
| 111 | 59 # |
| 110 | 60 # Default configuration values |
| 111 | 61 # |
| 62 # `sendmail` is also valid for `dma` because of the mapping within | |
| 63 # `/etc/mail/mailer.conf` | |
| 64 # | |
| 110 | 65 : ${MAILER:=/usr/sbin/sendmail} |
| 66 | |
| 67 | |
| 68 parse_addr() { | |
| 69 : 'Parse an possibly complex email address. | |
| 70 | |
| 71 Addresses can be of the form | |
| 72 | |
| 73 - Name Parts <user@domain.tld> | |
| 74 - user@domain.tld | |
| 75 | |
| 76 `Name Parts` may not contain ``<`` or ``>`` characters. | |
| 77 | |
| 78 Args: | |
| 79 _addr: the complex email address | |
| 80 | |
| 81 Returns: | |
| 82 0 on success, 1 on errors | |
| 83 | |
| 84 Output (Globals): | |
| 85 email_name: the name part (or empty) | |
| 86 email_addr: the technical address part (or empty) | |
| 87 | |
| 88 ' | |
| 89 local _addr | |
| 90 | |
| 91 _addr="$1" | |
| 111 | 92 test -n "${_addr}" || return 1 |
| 110 | 93 |
| 94 if printf "%s" "${_addr}" | grep -q -E -e '^[^<>]+<[^<>]+@[^<>]+>$'; then | |
| 95 email_name=$(printf '%s' "${_addr}" | sed -E -e 's/[[:space:]]*<.+$//') | |
| 96 email_addr=$(printf '%s' "${_addr}" | sed -E -e 's/^[^<>]+<//' | sed -E -e 's/>$//') | |
| 97 return 0 | |
| 98 fi | |
| 99 if printf "%s" "${_addr}" | grep -q -E -e '^[^<>]+@[^<>]+$'; then | |
| 100 email_name="" | |
| 101 email_addr="${_addr}" | |
| 102 return 0 | |
| 103 fi | |
| 104 return 1 | |
| 105 } | |
| 106 | |
| 107 | |
| 108 send_mail() { | |
| 109 : 'Send the mail via the underlying configured mailer (dma, sendmail et al.). | |
| 110 | |
| 111 Args: | |
| 112 _recipient: The recipient name. | |
| 113 | |
| 114 Will be written into the "To:" header also. | |
| 115 | |
| 116 Input (Globals): | |
| 117 MAILER | |
| 118 MAILCONTENT | |
|
119
5c92aeaec114
Try to make the mailer's input independent of a trailing single "." -- as opensmtpd's sendmail does not grok them.
Franz Glasner <fzglas.hg@dom66.de>
parents:
114
diff
changeset
|
119 MAILFIFO_STDIN |
|
5c92aeaec114
Try to make the mailer's input independent of a trailing single "." -- as opensmtpd's sendmail does not grok them.
Franz Glasner <fzglas.hg@dom66.de>
parents:
114
diff
changeset
|
120 MAILFIFO_STDOUT |
| 110 | 121 CC |
| 122 FROM | |
| 123 REPLYTO | |
| 124 SUBJECT | |
| 125 | |
| 126 Returns: | |
| 127 0 on success, other values on errors or the error exit code from the | |
| 128 underlying mailer | |
| 129 | |
| 111 | 130 This procedure starts the configured mailer as coproc and sends |
| 131 email headers and contents to the started mailer. | |
| 132 | |
| 110 | 133 ' |
| 111 | 134 local _recipient _rc _oifs _text _pid_mailer _recipient_addr |
|
119
5c92aeaec114
Try to make the mailer's input independent of a trailing single "." -- as opensmtpd's sendmail does not grok them.
Franz Glasner <fzglas.hg@dom66.de>
parents:
114
diff
changeset
|
135 local _from_from _from_addr _sender_addr _dummy |
| 110 | 136 |
| 137 _recipient="$1" | |
| 138 _rc=0 | |
| 139 | |
| 111 | 140 if parse_addr "${_recipient}"; then |
| 141 _recipient_addr="${email_addr}" | |
| 142 else | |
| 143 echo "ERROR: unknown recipient address format in \`${_recipient}'" >&2 | |
| 144 return 1 | |
| 145 fi | |
| 146 _sender_addr="$(whoami)@$(hostname -f)" | |
| 147 if [ -z "${FROM}" ]; then | |
| 148 _from_addr="${_sender_addr}" | |
| 149 _from_from="${_from_addr}" | |
| 150 else | |
| 151 if parse_addr "${FROM}"; then | |
| 152 _from_from="${FROM}" | |
| 153 _from_addr="${email_addr}" | |
| 154 else | |
| 155 echo "ERROR: unknown sender name in \`${FROM}'" >&2 | |
| 156 return 1 | |
| 157 fi | |
| 158 fi | |
| 159 | |
|
119
5c92aeaec114
Try to make the mailer's input independent of a trailing single "." -- as opensmtpd's sendmail does not grok them.
Franz Glasner <fzglas.hg@dom66.de>
parents:
114
diff
changeset
|
160 mkfifo -m 0600 "${MAILFIFO_STDIN}" |
| 110 | 161 _rc=$? |
| 162 if [ ${_rc} -ne 0 ]; then | |
| 163 return ${_rc} | |
| 164 fi | |
|
119
5c92aeaec114
Try to make the mailer's input independent of a trailing single "." -- as opensmtpd's sendmail does not grok them.
Franz Glasner <fzglas.hg@dom66.de>
parents:
114
diff
changeset
|
165 mkfifo -m 0600 "${MAILFIFO_STDOUT}" |
|
5c92aeaec114
Try to make the mailer's input independent of a trailing single "." -- as opensmtpd's sendmail does not grok them.
Franz Glasner <fzglas.hg@dom66.de>
parents:
114
diff
changeset
|
166 _rc=$? |
|
5c92aeaec114
Try to make the mailer's input independent of a trailing single "." -- as opensmtpd's sendmail does not grok them.
Franz Glasner <fzglas.hg@dom66.de>
parents:
114
diff
changeset
|
167 if [ ${_rc} -ne 0 ]; then |
|
5c92aeaec114
Try to make the mailer's input independent of a trailing single "." -- as opensmtpd's sendmail does not grok them.
Franz Glasner <fzglas.hg@dom66.de>
parents:
114
diff
changeset
|
168 rm -f "${MAILFIFO_STDIN}" |
|
5c92aeaec114
Try to make the mailer's input independent of a trailing single "." -- as opensmtpd's sendmail does not grok them.
Franz Glasner <fzglas.hg@dom66.de>
parents:
114
diff
changeset
|
169 return ${_rc} |
|
5c92aeaec114
Try to make the mailer's input independent of a trailing single "." -- as opensmtpd's sendmail does not grok them.
Franz Glasner <fzglas.hg@dom66.de>
parents:
114
diff
changeset
|
170 fi |
| 110 | 171 |
|
119
5c92aeaec114
Try to make the mailer's input independent of a trailing single "." -- as opensmtpd's sendmail does not grok them.
Franz Glasner <fzglas.hg@dom66.de>
parents:
114
diff
changeset
|
172 |
|
5c92aeaec114
Try to make the mailer's input independent of a trailing single "." -- as opensmtpd's sendmail does not grok them.
Franz Glasner <fzglas.hg@dom66.de>
parents:
114
diff
changeset
|
173 "$MAILER" -f "${_sender_addr}" "${_recipient_addr}" <${MAILFIFO_STDIN} >${MAILFIFO_STDOUT} & |
| 110 | 174 _pid_mailer=$! |
| 175 | |
|
119
5c92aeaec114
Try to make the mailer's input independent of a trailing single "." -- as opensmtpd's sendmail does not grok them.
Franz Glasner <fzglas.hg@dom66.de>
parents:
114
diff
changeset
|
176 exec 3>"${MAILFIFO_STDIN}" |
|
5c92aeaec114
Try to make the mailer's input independent of a trailing single "." -- as opensmtpd's sendmail does not grok them.
Franz Glasner <fzglas.hg@dom66.de>
parents:
114
diff
changeset
|
177 exec 4<"${MAILFIFO_STDOUT}" |
|
5c92aeaec114
Try to make the mailer's input independent of a trailing single "." -- as opensmtpd's sendmail does not grok them.
Franz Glasner <fzglas.hg@dom66.de>
parents:
114
diff
changeset
|
178 |
| 111 | 179 printf "To: %s\n" "${_recipient}" >&3 |
| 180 printf "From: %s\n" "${_from_from}" >&3 | |
| 181 if [ "${_sender_addr}" != "${_from_addr}" ]; then | |
| 182 printf "Sender: %s\n" "${_sender_addr}" >&3 | |
| 183 fi | |
| 184 if [ -n "${SUBJECT}" ]; then | |
| 185 printf "Subject: %s\n" "${SUBJECT}" >&3 | |
| 186 fi | |
| 187 if [ -n "${REPLYTO}" ]; then | |
| 114 | 188 # |
| 189 # XXX TBD proper Reply-To header value checks: | |
| 190 # a comma separated list of full mail addresses | |
| 191 # | |
| 111 | 192 printf "Reply-To: %s\n" "${REPLYTO}" >&3 |
| 193 fi | |
| 194 if [ -n "${CC}" ]; then | |
| 114 | 195 # |
| 196 # XXX TBD proper CC header value checks: | |
| 197 # a comma separated list of full mail addresses | |
| 198 # | |
| 111 | 199 printf "Cc: %s\n" "${CC}" >&3 |
| 200 fi | |
| 201 printf "\n" >&3 | |
| 202 | |
| 203 # preserve leading white space when reading with `read` | |
| 110 | 204 _oifs="$IFS" |
| 205 IFS=" | |
| 206 " | |
| 207 cat "${MAILCONTENT}" | | |
| 208 while read _text; do | |
| 209 printf "%s\n" "$_text" >&3 | |
| 210 done | |
|
119
5c92aeaec114
Try to make the mailer's input independent of a trailing single "." -- as opensmtpd's sendmail does not grok them.
Franz Glasner <fzglas.hg@dom66.de>
parents:
114
diff
changeset
|
211 # not all mailer recognize this |
|
5c92aeaec114
Try to make the mailer's input independent of a trailing single "." -- as opensmtpd's sendmail does not grok them.
Franz Glasner <fzglas.hg@dom66.de>
parents:
114
diff
changeset
|
212 # printf ".\n" >&3 |
| 110 | 213 IFS="$_oifs" |
| 214 | |
| 215 # close the fd to the pipe: coproc should get EOF | |
| 216 exec 3>&- | |
|
119
5c92aeaec114
Try to make the mailer's input independent of a trailing single "." -- as opensmtpd's sendmail does not grok them.
Franz Glasner <fzglas.hg@dom66.de>
parents:
114
diff
changeset
|
217 # read eventually remaining stuff from the mailer until EOF |
|
5c92aeaec114
Try to make the mailer's input independent of a trailing single "." -- as opensmtpd's sendmail does not grok them.
Franz Glasner <fzglas.hg@dom66.de>
parents:
114
diff
changeset
|
218 IFS='' read _dummy <&4 |
|
5c92aeaec114
Try to make the mailer's input independent of a trailing single "." -- as opensmtpd's sendmail does not grok them.
Franz Glasner <fzglas.hg@dom66.de>
parents:
114
diff
changeset
|
219 exec 4<&- |
| 110 | 220 |
| 221 wait $_pid_mailer | |
| 222 _rc=$? | |
| 223 | |
|
119
5c92aeaec114
Try to make the mailer's input independent of a trailing single "." -- as opensmtpd's sendmail does not grok them.
Franz Glasner <fzglas.hg@dom66.de>
parents:
114
diff
changeset
|
224 # we are done with the named pipes |
|
5c92aeaec114
Try to make the mailer's input independent of a trailing single "." -- as opensmtpd's sendmail does not grok them.
Franz Glasner <fzglas.hg@dom66.de>
parents:
114
diff
changeset
|
225 rm -f "${MAILFIFO_STDIN}" |
|
5c92aeaec114
Try to make the mailer's input independent of a trailing single "." -- as opensmtpd's sendmail does not grok them.
Franz Glasner <fzglas.hg@dom66.de>
parents:
114
diff
changeset
|
226 rm -f "${MAILFIFO_STDOUT}" |
| 110 | 227 |
| 228 return ${_rc} | |
| 229 } | |
| 230 | |
| 231 | |
| 111 | 232 while getopts "V8c:d:f:h:l:nr:s:" _opt; do |
| 110 | 233 case ${_opt} in |
| 234 V) | |
| 235 echo "bsmtp2dma v${VERSION} (rv:@@HGREVISION@@)" | |
| 236 echo "$USAGE" | |
| 237 exit 0; | |
| 238 ;; | |
| 239 8) | |
| 240 : # VOID | |
| 241 ;; | |
| 242 c) | |
| 243 CC="$OPTARG" | |
| 244 ;; | |
| 111 | 245 d) |
| 246 : # VOID | |
| 247 ;; | |
| 110 | 248 f) |
| 249 FROM="$OPTARG" | |
| 250 ;; | |
| 251 h) | |
| 252 : # VOID | |
| 253 ;; | |
| 254 l) | |
| 255 : # VOID | |
| 256 ;; | |
| 257 r) | |
| 258 REPLYTO="$OPTARG" | |
| 259 ;; | |
| 260 s) | |
| 261 SUBJECT="$OPTARG" | |
| 262 ;; | |
| 263 \?) | |
| 264 exit 2; | |
| 265 ;; | |
| 266 *) | |
| 267 echo "ERROR: inconsistent option handling" >&2 | |
| 268 exit 2; | |
| 269 ;; | |
| 270 esac | |
| 271 done | |
| 272 | |
| 273 # return code | |
| 274 _rc=0 | |
| 275 | |
| 276 MAILTMPDIR="$(mktemp -d)" | |
|
119
5c92aeaec114
Try to make the mailer's input independent of a trailing single "." -- as opensmtpd's sendmail does not grok them.
Franz Glasner <fzglas.hg@dom66.de>
parents:
114
diff
changeset
|
277 MAILFIFO_STDIN="${MAILTMPDIR}/mail-stdin" |
|
5c92aeaec114
Try to make the mailer's input independent of a trailing single "." -- as opensmtpd's sendmail does not grok them.
Franz Glasner <fzglas.hg@dom66.de>
parents:
114
diff
changeset
|
278 MAILFIFO_STDOUT="${MAILTMPDIR}/mail-stdout" |
| 110 | 279 MAILCONTENT="${MAILTMPDIR}/mail-text" |
| 280 | |
| 281 # | |
| 282 # Clean up existing temporary stuff on all sorts of exit | |
| 283 # (including the "exit" call (signal 0)) | |
| 284 # | |
| 111 | 285 trap 'if [ -d "${MAILTMPDIR}" ]; then rm -rf "${MAILTMPDIR}"; fi; exit;' 0 1 2 15 |
| 110 | 286 |
| 287 test -d "${MAILTMPDIR}" || { echo "ERROR: no existing private tmp dir" >&2; exit 1; } | |
| 288 | |
| 289 # | |
| 290 # Reset the Shell's option handling system to prepare for handling | |
| 291 # other arguments and probably command-local options | |
| 292 # | |
| 293 shift $((OPTIND-1)) | |
| 294 OPTIND=1 | |
| 295 | |
| 296 # early check whether some recipients are given | |
| 297 if [ $# -eq 0 ]; then | |
| 298 echo "ERROR: no recipient given" >&2 | |
| 299 exit 2; | |
| 300 fi | |
| 301 | |
| 302 # | |
| 303 # Collect the mail text from stdin into a temporary file | |
| 304 # | |
| 305 exec 3>"${MAILCONTENT}" | |
| 111 | 306 # preserve leading white space when reading with `read` |
| 110 | 307 _oifs="$IFS" |
| 308 IFS=" | |
| 309 " | |
| 310 while read _text; do | |
| 311 if [ "${_text}" = "." ]; then | |
| 312 break | |
| 313 else | |
| 314 printf "%s\n" "${_text}" >&3 | |
| 315 fi | |
| 316 done | |
| 317 exec 3>&- | |
| 318 IFS="$_oifs" | |
| 319 | |
| 320 # | |
| 321 # Now send the content of the collected mail content to all recipients | |
| 322 # | |
| 323 until [ $# -eq 0 ]; do | |
| 324 send_mail "$1" | |
| 325 _rcsm=$? | |
| 326 if [ \( ${_rcsm} -ne 0 \) -a \( ${_rc} -eq 0 \) ]; then | |
| 327 _rc=${_rcsm} | |
| 328 fi | |
| 329 shift | |
| 330 done | |
| 331 | |
| 332 exit ${_rc} |
