1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
# send_mail.bash
# Author: Noah Friedman <friedman@prep.ai.mit.edu>
# Created: 1992-07-02
# Public domain
# Commentary:
# TODO: implement Fcc headers (see emacs manual)
# Code:
#:docstring send_mail:
# Usage: send_mail
#
# This function serves as a simple replacement for sendmail as a client
# interface on those systems where it is not available. It does assume
# that one can talk to an SMTP mailer on port 25 either on the local host
# or on the host specified by the MAILHOST environment variable. If you
# have access to sendmail, it's better to use 'sendmail -t' instead of this
# script (which probably isn't as robust).
#
# Message is read from stdin, and headers are parsed to determine
# recipients.
#:end docstring:
###;;;autoload
function send_mail ()
{
# Need gawk, since several extensions are taken advantage of (like
# IGNORECASE for regexps).
local awk="${GAWK_LOCATION:-gawk}"
local DefaultFrom="${USER:-${LOGNAME}}"
local From
local To
local Cc
local Bcc
local tmpfile="/tmp/send_mail$$"
while [ -e "${tmpfile}" ]; do
tmpfile="/tmp/send_mail${RANDOM}"
done
# Lines consisting only of dots need one more dot appended. SMTP
# servers eat one of the dots (and if only 1 dot appears, it signifies
# the end of the message).
sed '/^\.\.*/s/^\(\.\.*\)$/\1./' > "${tmpfile}"
# Parse mail headers in message to extract recipients list.
# This doesn't affect what the user sees---it's only used to generate
# the rcpt-to lines for SMTP.
eval $(${awk} -f - "${tmpfile}" <<- '__EOF__'
# Try to extract email address from amidst random data
function parse_address (data)
{
# From: "real name" <foobar@host>
# From: "" <foobar@host>
if (match(data, /^\"[^\"]*\"[ \t]*<.*>/)) {
data_idx = match(data, /^\"[^\"]*\"[ \t]*</)
data = substr(data, RSTART + RLENGTH);
if (data_idx = match(data, ">.*"))
data = substr(data, 1, RSTART - 1);
return data
}
# From: real name <foobar@host>
if (match(data, /<.*>/)) {
data_idx = match(data, /</)
data = substr(data, RSTART + RLENGTH);
if (data_idx = match(data, ">"))
data = substr(data, 1, RSTART - 1);
return data
}
# From: foobar@host (real name)
if (match(data, /\(.*\)/)) {
data_idx = match(data, /\(/);
data = substr(data, 1, RSTART - 1);
return data
}
# (hopefully) From: foobar@host
return data
}
BEGIN { IGNORECASE = 1; }
# Blank line signifies end of headers, so we can stop looking.
/^$/ { exit(0) }
/^from:|^to:|^cc:|^bcc:/ {
header_idx = match($0, /^[^:]*:/)
if (header_idx) {
# Capitalize header name
header_firstchar = toupper(substr($0, RSTART, 1));
header_rest = tolower(substr($0, RSTART + 1, RLENGTH - 2));
header = header_firstchar header_rest
$0 = substr($0, RSTART + RLENGTH + 1);
addresses = ""
# parse addresses
while ($0) {
# Strip leading whitespace
if (idx = match($0, /[ \t]*/))
$0 = substr($0, RSTART + RLENGTH);
# Find everything up to a nonquoted comma
# FIXME: doesnt handle quoting yet
if (idx = match($0, /,/)) {
data = substr($0, 1, RSTART);
$0 = substr($0, RSTART + 1);
} else {
data = $0
$0 = ""
}
addresses = addresses " " parse_address(data)
}
printf("%s='%s'\n", header, addresses);
}
}
__EOF__)
# Not sure if an address is *required* after the HELO.. every sendmail
# I tried talking to didn't seem to care. Some sendmails don't care
# if there's a HELO at all.
cat <<- __EOF__ | telnet ${MAILHOST:-localhost} 25 > /dev/null 2>&1
HELO
mail from: ${From:-${DefaultFrom}}
$(for name in ${To} ${Cc} ${Bcc} ; do
echo "rcpt to: ${name}"
done)
data
$(cat "${tmpfile}")
.
quit
__EOF__
rm -f "${tmpfile}"
}
provide send_mail
# send_mail.bash ends here
|