Forum OpenACS Q&A: Re: ns_sendmail replacement?
This is quite the long thread, but I think no one has yet added
generation of the Message-Id in any of the versions of ns_sendmail
above? Hm, at first glance, looks like the AOLserver 4.0 beta
sendmail.tcl has some slight improvements over 3.3, but still doesn't
do that. Well, I hacked my ns_sendmail to do some simply Message-Id
generation a year or two ago to do that. It wasn't hard, but FYI,
here's the code:
My _ns_smtp_recv
, _ns_smtp_send
, and
ns_sendmail
procs are unchanged from the versions shipped
with 3.3+ad13. I just added this extra top level nsv command, and
changed _ns_sendmail
a bit. Note there are only a few
lines of changed code here! (But I find trying to read diffs in
BBoard annoying, so I didn't do that.) After the
if { ! $message_id_already_done_p }
line it's all stock, except for some comments:
if { ![nsv_exists ns_sendmail sequence] } { nsv_set ns_sendmail sequence 0 } proc _ns_sendmail {smtp smtpport timeout tolist bcclist \ from subject body extraheaders} { ## Put the tolist in the headers set rfcto [join $tolist ", "] ## Build headers set msg "To: $rfcto\nFrom: $from\nSubject: $subject\nDate: [ns_httptime [ns_time]]" ## Insert extra headers, if any (not for BCC) set message_id_already_done_p 0 if ![string match "" $extraheaders] { set size [ns_set size $extraheaders] for {set i 0} {$i < $size} {incr i} { set key [ns_set key $extraheaders $i] # This is rather hack-ish... if { [string compare $key {Message-ID}] == 0 } { set message_id_already_done_p 1 } append msg "\n${key}: [ns_set value $extraheaders $i]" } } # Insert a unique "Message-ID:" header, but only if the caller did # not manually include a Message-ID header: # # An application could use the Message-ID header for # e.g. threading support, but we're not trying to do anything # fancy like that here. We just want to include a globally-unique # ID. Why? Well, for one thing, since most email user agents # include a Message-ID, but most SPAM software does not, some # anti-SPAM software filters out email which does not have a # Message-ID... # # Note: The $message_id below is guaranteed to be globally unique # if and only if *ALL* of the following conditions are true: # # 1. Your unix box's hostname (which is what [ns_info hostname] # returns) is set to a fully-qualified name like # "philip.greenspun.com", NOT just a local hostname like # "philip". # 2. Your fully-qualified hostname is in fact globally-unique. # AKA, you didn't do something foolish like set up two separate # machines that both think their hostname is # "philip.greenspun.com". # 3. On your unix host, you have only ONE AOLserver running with # the server name returned by [ns_info server]. # 4. Since [ns_info boottime] is in seconds, you never restart # your AOLserver multiple times in < 1 second, jump your system # clock backwards in time, or etc. # 5. Once the "ns_sendmail sequence" nsv variable is set, you # never manually fool with it to re-set it to a previous value. # While the server is running, this value must always increase, # never decrease. # # --atp@piskorski.com, 2001/10/11 11:51 EDT if { ! $message_id_already_done_p } { set message_id "[nsv_incr ns_sendmail sequence].[ns_info boottime].[ns_info server]@[ns_info hostname]" append msg "\nMessage-ID: $message_id" } ## Blank line between headers and body append msg "\n\n$body\n" ## Terminate body with a solitary period foreach line [split $msg "\n"] { if [string match . $line] { append data . } append data $line append data "\r\n" } append data . ## Open the connection set sock [ns_sockopen $smtp $smtpport] set rfp [lindex $sock 0] set wfp [lindex $sock 1] ## Perform the SMTP conversation if { [catch { _ns_smtp_recv $rfp 220 $timeout _ns_smtp_send $wfp "HELO AOLserver [ns_info hostname]" $timeout _ns_smtp_recv $rfp 250 $timeout _ns_smtp_send $wfp "MAIL FROM:<$from>" $timeout _ns_smtp_recv $rfp 250 $timeout # TODO: Above, should optionally take a "Return-Path" to use # as the envelope sender address (aka, envelope return path) # rather than always using $from. This would allow using # VERPs, for instance, as discussed at: # "http://cr.yp.to/proto/verp.txt" # See also discussion at: # "http://www.arsdigita.com/bboard/q-and-a-fetch-msg?msg%5fid=000awU" # --atp@piskorski.com, 2001/10/11 10:25 EDT ## Loop through To list via multiple RCPT TO lines foreach toto $tolist { _ns_smtp_send $wfp "RCPT TO:<$toto>" $timeout _ns_smtp_recv $rfp 250 $timeout } ## Loop through BCC list via multiple RCPT TO lines ## A BCC should never, ever appear in the header. Ever. Not even. foreach bccto $bcclist { _ns_smtp_send $wfp "RCPT TO:<$bccto>" $timeout _ns_smtp_recv $rfp 250 $timeout } _ns_smtp_send $wfp DATA $timeout _ns_smtp_recv $rfp 354 $timeout _ns_smtp_send $wfp $data $timeout _ns_smtp_recv $rfp 250 $timeout _ns_smtp_send $wfp QUIT $timeout _ns_smtp_recv $rfp 221 $timeout } errMsg ] } { ## Error, close and report close $rfp close $wfp return -code error $errMsg } ## Close the connection close $rfp close $wfp }