]> diplodocus.org Git - nmh/blob - uip/mhmail
Replace sizeof string constant - 1 with LEN().
[nmh] / uip / mhmail
1 #! /bin/sh
2 #
3 # mhmail -- simple mail program
4 #
5 # This code is Copyright (c) 2012, by the authors of nmh. See the
6 # COPYRIGHT file in the root directory of the nmh distribution for
7 # complete copyright information.
8 #
9 # Emulation of compiled mhmail(1), with these differences:
10 # * Adds -send/-nosend, -headerfield, and -attach options.
11 # * Adds optional -to switch for recipient addresses.
12 # * Supports all post(8) (by default, without -profile) or send(1)
13 # (with -profile) options.
14 # * Optionally (with -profile) obeys the users profile, including
15 # AliasFile and send entries.
16 # * Instead of silently not sending an empty message, notifies user
17 # "mhmail: empty message not sent, use -body '' to force."
18 # * The compiled mhmail dropped a trailing newline from the -body argument.
19 # * Drops support for undocumented -queue option.
20
21 usage='Usage: mhmail [-t(o)] addrs ... [switches]
22 switches are:
23 -at(tach) file [-at(tach) file] ...
24 -b(ody) text
25 -c(c) addrs ...
26 -f(rom) addr
27 -hea(derfield) name:value [-hea(derfield) name:value] ...
28 -su(bject) text
29 -r(esent)
30 -pr(ofile)
31 -se(nd)
32 -nose(nd)
33 -v(ersion)
34 -hel(p)
35 and all post(8)/send(1) switches
36 mhmail with no arguments is equivalent to inc'
37
38
39 #### Find location of a program. Bourne shell just puts the name in
40 #### $0 if it's found from the PATH, so search that if necessary.
41 finddir() {
42 case $1 in
43 */*) dirname "$1" ;;
44 * ) IFS=:
45 for d in $PATH; do
46 [ -f "${d:=.}/$1" -a -x "$d/$1" ] && printf %s "$d" && break
47 done ;;
48 esac
49 }
50
51 help() {
52 printf '%s\n' "$usage"
53 #### Print the nmh intro text.
54 ${nmhbindir}/mhparam -help | sed -n -e '/^$/,$p'
55 exit
56 }
57
58 #### Convenience function to allow concise code below.
59 die() {
60 [ $# -gt 0 ] && printf '%s\n' "$1" >&2
61 exit 1
62 }
63
64
65 bindir=`finddir $0`
66 nmhbindir=`cd "$bindir" && pwd`
67 nmhlibexecdir=`$nmhbindir/mhparam libexecdir`
68 case `printf 'OK\n' | tail -n 1 2>&1` in
69 OK) tail='tail -n ' ;;
70 *) tail='tail -' ;;
71 esac
72
73
74 #### Checks for missing mandatory arguments.
75 checkforargs() {
76 if [ $attacharg -eq 1 ]; then
77 die 'mhmail: missing argument to -attach'
78 elif [ $bodyarg -eq 1 ]; then
79 die 'mhmail: missing argument to -body'
80 elif [ $ccarg -eq 1 -a "$cclist"x = x ]; then
81 die 'mhmail: missing argument to -cc'
82 elif [ $fromarg -eq 1 ]; then
83 die 'mhmail: missing argument to -from'
84 elif [ $headerfieldarg -eq 1 ]; then
85 die 'mhmail: missing argument to -headerfield'
86 elif [ $subjectarg -eq 1 ]; then
87 die 'mhmail: missing argument to -subject'
88 elif [ $toarg -eq 1 ]; then
89 die 'mhmail: missing argument to -to'
90 fi
91 }
92
93 if [ $# -eq 0 ]; then
94 #### Emulate mhmail for reading mail.
95 exec "$nmhbindir"/inc
96 fi
97
98 #### Go through all the switches so we can build the draft.
99 tolist= ## To: addresses
100 toarg=0 ## whether currently handling -to
101 attacharg=0 ## whether currently handling -attach
102 attachind=Attach ## attachment indicator
103 body= ## contents of the message body
104 bodyarg=0 ## whether currently handling -body
105 cclist= ## Cc: addresses
106 ccarg=0 ## whether currently handling -cc
107 from= ## From: contents
108 fromarg=0 ## whether currently handling -from
109 headerfieldlist= ## header fields to be added to draft
110 headerfieldarg=0 ## whether currently handling -headerfield
111 mhmailswitch=0 ## whether currently handling any mhmail switch
112 subject= ## Subject: contents
113 subjectarg=0 ## whether currently handling -subject
114 resent=0 ## whether resending
115 postsendargs= ## switches to pass on to post or send
116 post_send_switch_arg=0 ## whether currently handling a post/send switch
117 use_send=0 ## use post (default) or send (-profile)
118 sendsw=1 ## to send (default) or not to send
119 for arg in "$@"; do
120 case $arg in
121 #### Post and send won't accept -f -or -s because they'd be
122 #### ambiguous, so no conflicts with them. And they don't have
123 #### -b, -c, -r, -t. For the new switches that compiled mhmail
124 #### didn't have: let -p indicate mhmail -profile, not send
125 #### -port. -send masks the send(1) -send switch. -attach
126 #### masks the send(1) -attach switch.
127 -at|-att|-atta|-attac|-attach)
128 mhmailswitch=1
129 attacharg=1
130 use_send=1
131 ;;
132 -b|-bo|-bod|-body) mhmailswitch=1; bodyarg=1 ;;
133 -c|-cc) mhmailswitch=1; ccarg=1 ;;
134 -f|-fr|-fro|-from) mhmailswitch=1; fromarg=1 ;;
135 -hea|-head|-heade|-header|-headerf|-headerfi|-headerfie|-headerfiel|\
136 -headerfield) mhmailswitch=1; headerfieldarg=1 ;;
137 -hel|-help) help ;;
138 -nose|-nosen|-nosend) mhmailswitch=1; sendsw=0 ;;
139 -p|-pr|-pro|-prof|-profi|-profil|-profile) mhmailswitch=1; use_send=1 ;;
140 -resend) die 'mhmail: did you mean -resent instead of -resend?' ;;
141 -r|-re|-res|-rese|-resen|-resent) mhmailswitch=1; resent=1 ;;
142 -se|-sen|-send) mhmailswitch=1; sendsw=1 ;;
143 -su|-sub|-subj|-subje|-subjec|-subject) mhmailswitch=1; subjectarg=1 ;;
144 -t|-to) toarg=1; ccarg=0 ;;
145 -v|-ve|-ver|-vers|-versi|-versio|-version)
146 #### Cheat instead of using autoconf and make to fill in the version.
147 "$nmhbindir"/mhpath -v | sed 's/mhpath/mhmail/'; exit ;;
148 -*) if [ $mhmailswitch -eq 1 ]; then
149 checkforargs
150 mhmailswitch=0
151 fi
152 post_send_switch_arg=1
153 postsendargs="${postsendargs:+$postsendargs }$arg" ;;
154 *) mhmailswitch=0
155 if [ $bodyarg -eq 1 ]; then
156 body="$arg
157 "
158 bodyarg=0
159 #### Allow -body "" by using just a newline for the body.
160 [ "$body"x = x ] && body='
161 '
162 elif [ $fromarg -eq 1 ]; then
163 from="$arg"
164 fromarg=0
165 elif [ $subjectarg -eq 1 ]; then
166 subject="$arg"
167 subjectarg=0
168 elif [ $attacharg -eq 1 ]; then
169 headerfieldlist="${headerfieldlist:+$headerfieldlist}$attachind: $arg
170 "
171 attacharg=0
172 elif [ $headerfieldarg -eq 1 ]; then
173 #### It's not strictly necessary to have one space after
174 #### the : that separates the header field name from the
175 #### body, but do it to avoid surprising someone.
176 #### Solaris sed wants the trailing newline in its input.
177 add=`printf '%s\n' "$arg" | sed -e 's/:/: /' -e 's/: /: /'`
178 headerfieldlist="${headerfieldlist:+$headerfieldlist}$add
179 "
180 headerfieldarg=0
181 elif [ $post_send_switch_arg -eq 1 ]; then
182 postsendargs="${postsendargs:+$postsendargs }$arg"
183 elif [ $ccarg -eq 1 ]; then
184 #### ccarg can only be reset to 0 by -to.
185 cclist="${cclist:+$cclist, }$arg"
186 else
187 #### An address.
188 tolist="${tolist:+$tolist, }$arg"
189 toarg=0
190 fi ;;
191 esac
192 done
193
194 #### Check for at least one address and -from.
195 if [ "$tolist"x = x ]; then
196 die 'Usage: mhmail [-t(o)] addrs ... [switches]'
197 fi
198 if [ "$from"x = x ]; then
199 from=`${nmhlibexecdir}/ap -format '%(localmbox)' 0`
200 fi
201
202 #### Check for missing mandatory arguments.
203 checkforargs
204
205 #### Build header.
206 [ $resent -eq 0 ] && prefix= || prefix='Resent-'
207 header="${prefix}To: $tolist
208 "
209 [ "$cclist"x = x ] || header="$header${prefix}Cc: $cclist
210 "
211 [ "$subject"x = x ] || header="$header${prefix}Subject: $subject
212 "
213 [ "$from"x = x ] || header="$header${prefix}From: $from
214 "
215
216 if [ "$headerfieldlist" ]; then
217 header="$header$headerfieldlist"
218 fi
219
220 #### Set up a file to supply as a draft to post/send. And set a trap
221 #### to remove it. send moves the file to a backup and can create a
222 #### .orig file, so it will remove them, too.
223 umask 077
224 tmpdir="${MHTMPDIR:-${TMPDIR:-`$nmhbindir/mhpath +`}}"
225 tmpfilename=`cd "$tmpdir" && "${nmhlibexecdir}/mkstemp" -p mhmail`
226 [ $? -ne 0 ] && die "mhmail: failed to create temporary file in $tmpdir"
227 tmpfile="$tmpdir/$tmpfilename"
228 backup_char=`"$nmhbindir"/mhparam sbackup`
229 tmpfilebackup="$tmpdir/${backup_char}${tmpfilename}*"
230 tmpfileresent=
231
232 message_file=
233 if [ $resent -eq 0 ]; then
234 #### Add blank line after header if not resending.
235 header="$header
236 "
237 message_file="$tmpfile"
238 else
239 if [ $use_send -eq 0 ]; then
240 postsendargs="${postsendargs:+$postsendargs }-dist"
241 message_file="$tmpfile"
242 else
243 #### When resending with send, tmpfile will just contain the
244 #### Resent- header fields. "$tmpfileresent" will contain
245 #### the message that is being resent.
246 tmpfileresent=`"${nmhlibexecdir}/mkstemp" -d "$tmpdir" -p mhmail-resent`
247 [ $? -ne 0 ] && die "mhmail: failed to create temporary file in $tmpdir"
248 mhdist=1; export mhdist
249 mhaltmsg=$tmpfileresent; export mhaltmsg
250 message_file="$tmpfileresent"
251 printf '' >"$message_file" || exit 2
252 fi
253 fi
254
255 trap "rm -f '$tmpfile' $tmpfilebackup ${tmpfileresent:+'$tmpfileresent'}" 0
256
257 if [ "$body"x = x ]; then
258 #### First put message header in the file.
259 printf %s "$header" >"$tmpfile" || exit 2
260
261 tmpfile_size_before=`wc -c "$message_file"`
262 #### Now grab the body from stdin. cat >> handles blank lines
263 #### better than body=`cat`.
264 cat >>"$message_file" || exit 2
265 tmpfile_size_after=`wc -c "$message_file"`
266
267 #### Don't allow an empty body (from stdin). Use string
268 #### comparison so we don't have to strip the filename, etc.
269 if [ "$tmpfile_size_before" = "$tmpfile_size_after" ]; then
270 die 'mhmail: empty message not sent, use -body '"''"' to force.'
271 fi
272
273 #### Add trailing newline to body if it doesn't have one.
274 if [ `${tail}1 "$message_file" | wc -l` -ne 1 ]; then
275 printf '\n' >>"$message_file" || exit 2
276 fi
277 else
278 #### Add trailing newline to body if it doesn't have one.
279 [ `printf %s "$body" | ${tail}1 | wc -l` -ne 1 ] && body="$body
280 "
281
282 if [ "$tmpfileresent" ]; then
283 #### Put just the new message header in the file.
284 printf %s "$header" >"$tmpfile" || exit 2
285 #### and the body in the file to resend.
286 printf %s "$body" >"$tmpfileresent" || exit 2
287 else
288 #### Put message header and body in the file.
289 printf %s "$header$body" >"$tmpfile" || exit 2
290 fi
291 fi
292
293 if [ $sendsw -eq 0 ]; then
294 cat "$tmpfile"
295 else
296 if [ $use_send -eq 0 ]; then
297 post_or_send=`$nmhbindir/mhparam postproc`
298 else
299 post_or_send="$nmhbindir/send"
300 fi
301
302 if "$post_or_send" $postsendargs "$tmpfile"; then
303 exit
304 else
305 status=$?
306 mv -f "$tmpfile" dead.letter
307 printf 'Letter saved in dead.letter\n' >&2
308 exit $status
309 fi
310 fi