|
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 |
|
|
24 -8 Does nothing. Just a compatibility option for `bsmtp`. |
|
|
25 |
|
|
26 -d n Does nothing. Just a compatibility option for `bsmtp`. |
|
|
27 |
|
|
28 -f ADDRESS Set the "From:" header. |
|
|
29 |
|
|
30 -h MAILHOST:PORT Does nothing. Just a compatibility option for `bsmtp`. |
|
|
31 |
|
|
32 -n Do a dry-run: just print out what would be done |
|
|
33 |
|
|
34 -s SUBJECT Set the "Subject:" header |
|
|
35 |
|
|
36 -r ADDRESS Set the "Reply-To:" header |
|
|
37 |
|
|
38 -l NUMBER Does nothing. Just a compatibility option for `bsmtp`. |
|
|
39 |
|
|
40 -V Show the program version and usage and exit. |
|
|
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 |
|
|
59 # Default configuration values |
|
|
60 : ${MAILER:=/usr/sbin/sendmail} |
|
|
61 |
|
|
62 |
|
|
63 parse_addr() { |
|
|
64 : 'Parse an possibly complex email address. |
|
|
65 |
|
|
66 Addresses can be of the form |
|
|
67 |
|
|
68 - Name Parts <user@domain.tld> |
|
|
69 - user@domain.tld |
|
|
70 |
|
|
71 `Name Parts` may not contain ``<`` or ``>`` characters. |
|
|
72 |
|
|
73 Args: |
|
|
74 _addr: the complex email address |
|
|
75 |
|
|
76 Returns: |
|
|
77 0 on success, 1 on errors |
|
|
78 |
|
|
79 Output (Globals): |
|
|
80 email_name: the name part (or empty) |
|
|
81 email_addr: the technical address part (or empty) |
|
|
82 |
|
|
83 ' |
|
|
84 local _addr |
|
|
85 |
|
|
86 _addr="$1" |
|
|
87 |
|
|
88 if printf "%s" "${_addr}" | grep -q -E -e '^[^<>]+<[^<>]+@[^<>]+>$'; then |
|
|
89 email_name=$(printf '%s' "${_addr}" | sed -E -e 's/[[:space:]]*<.+$//') |
|
|
90 email_addr=$(printf '%s' "${_addr}" | sed -E -e 's/^[^<>]+<//' | sed -E -e 's/>$//') |
|
|
91 return 0 |
|
|
92 fi |
|
|
93 if printf "%s" "${_addr}" | grep -q -E -e '^[^<>]+@[^<>]+$'; then |
|
|
94 email_name="" |
|
|
95 email_addr="${_addr}" |
|
|
96 return 0 |
|
|
97 fi |
|
|
98 return 1 |
|
|
99 } |
|
|
100 |
|
|
101 |
|
|
102 send_mail() { |
|
|
103 : 'Send the mail via the underlying configured mailer (dma, sendmail et al.). |
|
|
104 |
|
|
105 Args: |
|
|
106 _recipient: The recipient name. |
|
|
107 |
|
|
108 Will be written into the "To:" header also. |
|
|
109 |
|
|
110 Input (Globals): |
|
|
111 MAILER |
|
|
112 MAILCONTENT |
|
|
113 MAILFIFO |
|
|
114 CC |
|
|
115 FROM |
|
|
116 REPLYTO |
|
|
117 SUBJECT |
|
|
118 |
|
|
119 Returns: |
|
|
120 0 on success, other values on errors or the error exit code from the |
|
|
121 underlying mailer |
|
|
122 |
|
|
123 ' |
|
|
124 local _recipient _rc _oifs _text _pid_mailer |
|
|
125 |
|
|
126 _recipient="$1" |
|
|
127 _rc=0 |
|
|
128 |
|
|
129 mkfifo -m 0600 "${MAILFIFO}" |
|
|
130 _rc=$? |
|
|
131 if [ ${_rc} -ne 0 ]; then |
|
|
132 return ${_rc} |
|
|
133 fi |
|
|
134 |
|
|
135 exec 3<>"${MAILFIFO}" |
|
|
136 |
|
|
137 "$MAILER" -f bacula@fmgapp7-bacula9.intern.feldmann-mg.com hostmaster@feldmann-mg.com <&3 & |
|
|
138 _pid_mailer=$! |
|
|
139 |
|
|
140 # preserve leading white space when reading |
|
|
141 _oifs="$IFS" |
|
|
142 IFS=" |
|
|
143 " |
|
|
144 cat "${MAILCONTENT}" | |
|
|
145 while read _text; do |
|
|
146 printf "%s\n" "$_text" >&3 |
|
|
147 done |
|
|
148 printf ".\n" >&3 |
|
|
149 |
|
|
150 IFS="$_oifs" |
|
|
151 |
|
|
152 # close the fd to the pipe: coproc should get EOF |
|
|
153 exec 3>&- |
|
|
154 |
|
|
155 wait $_pid_mailer |
|
|
156 _rc=$? |
|
|
157 |
|
|
158 # we are done with the named pipe |
|
|
159 rm -f "${MAILFIFO}" |
|
|
160 |
|
|
161 return ${_rc} |
|
|
162 } |
|
|
163 |
|
|
164 |
|
|
165 while getopts "V8c:f:h:l:nr:s:" _opt; do |
|
|
166 case ${_opt} in |
|
|
167 V) |
|
|
168 echo "bsmtp2dma v${VERSION} (rv:@@HGREVISION@@)" |
|
|
169 echo "$USAGE" |
|
|
170 exit 0; |
|
|
171 ;; |
|
|
172 8) |
|
|
173 : # VOID |
|
|
174 ;; |
|
|
175 c) |
|
|
176 CC="$OPTARG" |
|
|
177 ;; |
|
|
178 f) |
|
|
179 FROM="$OPTARG" |
|
|
180 ;; |
|
|
181 h) |
|
|
182 : # VOID |
|
|
183 ;; |
|
|
184 l) |
|
|
185 : # VOID |
|
|
186 ;; |
|
|
187 n) |
|
|
188 DRYRUN="YES" |
|
|
189 ;; |
|
|
190 r) |
|
|
191 REPLYTO="$OPTARG" |
|
|
192 ;; |
|
|
193 s) |
|
|
194 SUBJECT="$OPTARG" |
|
|
195 ;; |
|
|
196 \?) |
|
|
197 exit 2; |
|
|
198 ;; |
|
|
199 *) |
|
|
200 echo "ERROR: inconsistent option handling" >&2 |
|
|
201 exit 2; |
|
|
202 ;; |
|
|
203 esac |
|
|
204 done |
|
|
205 |
|
|
206 # return code |
|
|
207 _rc=0 |
|
|
208 |
|
|
209 DRYRUN="NO" |
|
|
210 |
|
|
211 MAILTMPDIR="$(mktemp -d)" |
|
|
212 MAILFIFO="${MAILTMPDIR}/mail-stdin" |
|
|
213 MAILCONTENT="${MAILTMPDIR}/mail-text" |
|
|
214 |
|
|
215 # |
|
|
216 # Clean up existing temporary stuff on all sorts of exit |
|
|
217 # (including the "exit" call (signal 0)) |
|
|
218 # |
|
|
219 trap 'if [ -d "${MAILTMPDIR}" ]; then rm -r "${MAILTMPDIR}"; fi; exit;' 0 1 2 15 |
|
|
220 |
|
|
221 test -d "${MAILTMPDIR}" || { echo "ERROR: no existing private tmp dir" >&2; exit 1; } |
|
|
222 |
|
|
223 # |
|
|
224 # Reset the Shell's option handling system to prepare for handling |
|
|
225 # other arguments and probably command-local options |
|
|
226 # |
|
|
227 shift $((OPTIND-1)) |
|
|
228 OPTIND=1 |
|
|
229 |
|
|
230 # early check whether some recipients are given |
|
|
231 if [ $# -eq 0 ]; then |
|
|
232 echo "ERROR: no recipient given" >&2 |
|
|
233 exit 2; |
|
|
234 fi |
|
|
235 |
|
|
236 # |
|
|
237 # Collect the mail text from stdin into a temporary file |
|
|
238 # |
|
|
239 exec 3>"${MAILCONTENT}" |
|
|
240 # preserve leading white space when reading |
|
|
241 _oifs="$IFS" |
|
|
242 IFS=" |
|
|
243 " |
|
|
244 while read _text; do |
|
|
245 if [ "${_text}" = "." ]; then |
|
|
246 break |
|
|
247 else |
|
|
248 printf "%s\n" "${_text}" >&3 |
|
|
249 fi |
|
|
250 done |
|
|
251 exec 3>&- |
|
|
252 IFS="$_oifs" |
|
|
253 |
|
|
254 # |
|
|
255 # Now send the content of the collected mail content to all recipients |
|
|
256 # |
|
|
257 until [ $# -eq 0 ]; do |
|
|
258 send_mail "$1" |
|
|
259 _rcsm=$? |
|
|
260 if [ \( ${_rcsm} -ne 0 \) -a \( ${_rc} -eq 0 \) ]; then |
|
|
261 _rc=${_rcsm} |
|
|
262 fi |
|
|
263 shift |
|
|
264 done |
|
|
265 |
|
|
266 exit ${_rc} |