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