Forum OpenACS Q&A: Re: ns_sendmail replacement?
Later in _ns_sendmail there is this code:
## 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
}
which means it would for the first item in the $tolist send the following line to the SMTP server:
RCPT TO:<Fritz <mailto:fritz@frotz.com>>
which has one enclosing pair of angle brackets too much. Shouldn't that be:
RCPT TO: Fritz <mailto:fritz@frotz.com>
? You might get away with it if it is a forgiving mailserver, but it isn't right IMHO. Or am I missing something?
Here is a somewhat relevant example from the RFC (it has explicit relaying for the MAIL FROM though):
S: 220 xyz.com Simple Mail Transfer Service Ready
C: EHLO foo.com
S: 250 xyz.com is on the air
C: MAIL FROM:<@foo.com:JQP@bar.com>
S: 250 OK
C: RCPT TO:<ones@XYZ.COM>
S: 250 OK
C: DATA
S: 354 Start mail input; end with <CRLF>.<CRLF>
C: Received: from bar.com by foo.com ; Thu, 21 May 1998
C: 05:33:29 -0700
C: Date: Thu, 21 May 1998 05:33:22 -0700
C: From: John Q. Public <JQP@bar.com>
C: Subject: The Next Meeting of the Board
C: To: Jones@xyz.com
C:
C: Bill:
C: The next meeting of the board of directors will be
C: on Tuesday.
C: John.
C: .
S: 250 OK
C: QUIT
S: 221 foo.com Service closing transmission channel
Til is right indeed.
It is a suprise that my qmail mailer has been so forgiving.
To remain RFC 2821 compliant, the extraction of the e-mail addresses should be moved from ns_sendmail to _ns_sendmail like so:
proc ns_sendmail { to from subject body {extraheaders {}} {bcc {}} } {
## Takes comma-separated values in the "to" parm
## Multiple To and BCC addresses are handled appropriately.
## Original ns_sendmail functionality is preserved.
## Cut out carriage returns
regsub -all "\n" $to "" to
regsub -all "\r" $to "" to
regsub -all "\n" $bcc "" bcc
regsub -all "\r" $bcc "" bcc
## Split to into a proper list
set tolist_in [split $to ","]
set bcclist_in [split $bcc ","]
## Get smtp server into, if none then use localhost
set smtp [ns_config ns/parameters smtphost]
if [string match "" $smtp] {
set smtp [ns_config ns/parameters mailhost]
}
if [string match "" $smtp] {
set smtp localhost
}
set timeout [ns_config ns/parameters smtptimeout]
if [string match "" $timeout] {
set timeout 60
}
set smtpport [ns_config ns/parameters smtpport]
if [string match "" $smtpport] {
set smtpport 25
}
set tolist [list]
foreach toaddr $tolist_in {
lappend tolist "[string trim $toaddr]"
}
set bcclist [list]
if ![string match "" $bcclist_in] {
foreach bccaddr $bcclist_in {
lappend bcclist "[string trim $bccaddr]"
}
}
## Send it along to _ns_sendmail
_ns_sendmail $smtp $smtpport $timeout $tolist $bcclist \
$from $subject $body $extraheaders
}
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)
if ![string match "" $extraheaders] {
set size [ns_set size $extraheaders]
for {set i 0} {$i < $size} {incr i} {
append msg "\n[ns_set key $extraheaders $i]: [ns_set value $extraheaders $i]"
}
}
## 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
## Extract "from" email address
if [regexp {.*<(.*)>} $from ig address] {
set from $address
}
_ns_smtp_send $wfp "MAIL FROM: $from" $timeout
_ns_smtp_recv $rfp 250 $timeout
## Loop through To list via multiple RCPT TO lines
foreach toto $tolist {
if [regexp {.*<(.*)>} $toto ig address] {
set toto $address
}
_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 {
if [regexp {.*<(.*)>} $bccto ig address] {
set bccto $address
}
_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
}
/Bart
## Loop through To list via multiple RCPT TO lines
foreach toto $tolist {
if [regexp {.*<(.*)>} $toto ig address] {
set toto $address
}
_ns_smtp_send $wfp "RCPT TO: $toto" $timeout
_ns_smtp_recv $rfp 250 $timeout
}
The rfc specifies it like this:
MAIL FROM:<userx@y.foo.org> RCPT TO:<userc@d.bar.org>so aolserver would have to do that:
## Loop through To list via multiple RCPT TO lines
foreach toto $tolist {
if [regexp {.*<(.*)>} $toto ig address] {
set toto $address
}
_ns_smtp_send $wfp "RCPT TO:<$toto>" $timeout
_ns_smtp_recv $rfp 250 $timeout
}
and while we're at it we might as well join the processing of the $tolist above and the $bcclist - they do the same.
*** root/aolserver/tcl/sendmail.tcl Fri Aug 11 22:04:10 2000
--- aolserver/modules/tcl/sendmail.tcl Fri Jan 17 12:34:59 2003
***************
*** 96,120 ****
set smtpport 25
}
- ## Extract "from" email address
- if [regexp {.*<(.*)>} $from ig address] {
- set from $address
- }
-
set tolist [list]
foreach toaddr $tolist_in {
- if [regexp {.*<(.*)>} $toaddr ig address] {
- set toaddr $address
- }
lappend tolist "[string trim $toaddr]"
}
set bcclist [list]
if ![string match "" $bcclist_in] {
foreach bccaddr $bcclist_in {
- if [regexp {.*<(.*)>} $bccaddr ig address] {
- set bccaddr $address
- }
lappend bcclist "[string trim $bccaddr]"
}
}
--- 96,109 ----
***************
*** 160,165 ****
--- 149,157 ----
set rfp [lindex $sock 0]
set wfp [lindex $sock 1]
+ ## Strip "from" email address
+ regexp {.*<(.*)>} $from ig from
+
## Perform the SMTP conversation
if { [catch {
_ns_smtp_recv $rfp 220 $timeout
***************
*** 168,183 ****
_ns_smtp_send $wfp "MAIL FROM:<$from>" $timeout
_ns_smtp_recv $rfp 250 $timeout
! ## 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
}
--- 160,173 ----
_ns_smtp_send $wfp "MAIL FROM:<$from>" $timeout
_ns_smtp_recv $rfp 250 $timeout
! ## Loop through To and Bcc list via multiple RCPT TO lines
## A BCC should never, ever appear in the header. Ever. Not even.
! foreach toto [concat $tolist $bcclist] {
!
! # Transform "Fritz <fritz@foo.com>" into "fritz@foo.com".
! regexp {.*<(.*)>} $toto ig toto
!
! _ns_smtp_send $wfp "RCPT TO:<$toto>" $timeout
_ns_smtp_recv $rfp 250 $timeout
}