Directly relay the email to the target domain SMTP server for immediate transmission, as opposed to queuing the email to the local SMTP server. This is the fastest means of establishing the validity of the recipient address, widely used by webapps during new member registration to verify the member's email address. The following scenarios are taken into account:- 1) The DNS may be down or temporarily busy so it is not possible to determine if the domain part of the email address actually exists.# 2) The DNS provides the mail servers for the domain but none can be connected to at the moment. 3) The DNS is up but cannot find a mail server for the domain 4) The mail server was connected to but rejected the sender (the web application on your server) 5) The mail server was connected to but denied the existance of the recipient. 6) The mail server accepted the email. Of the above, scenarios 1 and 2 are inconclusive. Yet the HTTP response to the form submission (containing the user email address), must be sent without delay. The message should be queued and the user notified of this. The message should make clear the email could not be verified but if it is a live email address, the verification code will arrive in due course. Any sessions left unfullfilled after a certain period must be purged. Senarios 3, 4 and 5 are terminal. The email will not be sent at any point. Only scenario 6 is where the response can say "an email has been sent". It could be of course, that the address is incorrect. Note that bypassing the local SMTP server foregoes any email management the local SMTP server may offer. If you need a permanent and easily searchable record of the email, the calling applications will have to make arrangements for this.
| Return Type | Function name | Arguments |
|---|---|---|
| hzEcode | hzEmail::SendSmtp | (const char*,const char*,const char*,) |
Declared in file: hzMailer.h
Defined in file : hzMailer.cpp
Function Logic:
Function body:
hzEcode hzEmail::SendSmtp (const char* server)const char* uname, const char* passwd,
{
// Directly relay the email to the target domain SMTP server for immediate transmission, as opposed to queuing the email to the local SMTP server. This is the fastest means of
// establishing the validity of the recipient address, widely used by webapps during new member registration to verify the member's email address. The following scenarios are
// taken into account:-
//
// 1) The DNS may be down or temporarily busy so it is not possible to determine if the domain part of the email address actually exists.#
// 2) The DNS provides the mail servers for the domain but none can be connected to at the moment.
// 3) The DNS is up but cannot find a mail server for the domain
// 4) The mail server was connected to but rejected the sender (the web application on your server)
// 5) The mail server was connected to but denied the existance of the recipient.
// 6) The mail server accepted the email.
//
// Of the above, scenarios 1 and 2 are inconclusive. Yet the HTTP response to the form submission (containing the user email address), must be sent without delay. The message
// should be queued and the user notified of this. The message should make clear the email could not be verified but if it is a live email address, the verification code will
// arrive in due course. Any sessions left unfullfilled after a certain period must be purged.
//
// Senarios 3, 4 and 5 are terminal. The email will not be sent at any point. Only scenario 6 is where the response can say "an email has been sent". It could be of course,
// that the address is incorrect.
//
// Note that bypassing the local SMTP server foregoes any email management the local SMTP server may offer. If you need a permanent and easily searchable record of the email,
// the calling applications will have to make arrangements for this.
//
// Arguments: 1) server The alien domain SMTP server
// 2) uname The username for SMTP auth
// 3) passwd The password for SMTP auth
//
// Returns: E_ARGUMENT If no server, username, password or sender is supplied or if no recipients are specified
// E_NOACCOUNT If the recipient does not exist
// E_HOSTRETRY If the server is too busy
_hzfunc("hzEmail::SendSmtp") ;
hzList<hzEmaddr>::Iter rx ; // Recipients iterator
hzTcpClient C ; // Client connection to destination server
hzChain inp ; // Input chain
hzChain oup ; // Output chain
chIter ci ; // For iterating the composed chain
hzLogger* plog ; // Current thread logger
char* i ; // Send buffer populator
hzString S ; // Temporary string
hzEmaddr e ; // Email address
uint32_t nRecv ; // Bytes received by Recv()
uint32_t nSend ; // Bytes sent by Send()
uint32_t nTotal = 0; // Total bytes sent (diagnostics only)
char sbuf[HZ_MAXPACKET+4]; // Send buffer
char rbuf[HZ_MAXPACKET+4]; // Recv buffer
SMTPCode smtpCode ; // Server SMTP return code
hzEcode rc = E_OK ; // Return code
// Check arguments
plog = GetThreadLogger() ;
if (!plog)
Fatal("No thread logger\n") ;
if (!server || !server[0]) { rc = E_ARGUMENT ; plog->Out("No SMTP server supplied\n") ; }
if (!uname || !uname[0]){ rc = E_ARGUMENT ; plog->Out("No SMTP username supplied\n") ; }
if (!passwd || !passwd[0]) { rc = E_ARGUMENT ; plog->Out("No SMTP password supplied\n") ; }
if (!m_Recipients.Count()) { rc = E_ARGUMENT ; plog->Out("No recipients specified\n") ; }
if (!m_AddrFrom) { rc = E_ARGUMENT ; plog->Out("No sender address specified\n") ; }
if (rc != E_OK)
return rc ;
if (!m_RealFrom)
m_RealFrom = *m_AddrFrom ;
// Connect to port 25 on destination machine.
S = server ;
rc = C.ConnectStd(S, 25);
if (rc != E_OK)
{
hzerr(rc, "Could not conect to SMTP server [%s]", server) ;
return rc ;
}
// Wait for SMTP greeting
C.Recv(rbuf, nRecv, HZ_MAXPACKET) ;
if (nRecv >&eq; 0)
rbuf[nRecv] = 0;
else
rbuf[0]= 0;
smtpCode = _getSmtpCode(rbuf) ;
if (smtpCode != SMTP_READY)
{
plog->Out("%d Server not ready so quiting\n", smtpCode) ;
rc = E_HOSTRETRY ;
goto Quit ;
}
// Send the EHLO command
sprintf(sbuf, "EHLO %s\r\n", *m_AddrFrom) ;
if ((rc = C.Send(sbuf, strlen(sbuf))) != E_OK)
{
plog->Out("Could not send HELO command\n") ;
goto Quit ;
}
if (_hzGlobal_Debug & HZ_DEBUG_MAILER)
plog->Out("Client -> %s", sbuf) ;
// Recv the server responds
if ((rc = C.Recv(rbuf, nRecv, HZ_MAXPACKET)) != E_OK)
{
plog->Out("Could not get ACK to EHLO msg\n") ;
goto Quit ;
}
if (nRecv >&eq; 0)
rbuf[nRecv] = 0;
else
rbuf[0]= 0;
smtpCode = _getSmtpCode(rbuf) ;
if (smtpCode != SMTP_OK)
{ rc = E_PROTOCOL ; plog->Out("Expected ACK so quitting\n") ; goto Quit ; }
if (_hzGlobal_Debug & HZ_DEBUG_MAILER)
plog->Out("Server -> %s\n", rbuf) ;
// If there is an AUTH, do this first
if (uname && passwd)
{
if (_hzGlobal_Debug & HZ_DEBUG_MAILER)
plog->Out("Have username of %s and password of %s so will auth\n", uname, passwd) ;
strcpy(sbuf, "AUTH LOGIN\r\n") ;
if ((rc = C.Send(sbuf, strlen(sbuf))) != E_OK)
{ plog->Out("Could not send MAIL FROM message\n") ; goto Quit ; }
if (_hzGlobal_Debug & HZ_DEBUG_MAILER)
plog->Out("Client -> %s", sbuf) ;
// Get back the message 'send username' (written in base64)
C.Recv(rbuf, nRecv, HZ_MAXPACKET) ;
if (nRecv > 0)
rbuf[nRecv] = 0;
else
rbuf[0]= 0;
smtpCode = _getSmtpCode(rbuf) ;
if (_hzGlobal_Debug & HZ_DEBUG_MAILER)
plog->Out("Server -> %s", rbuf) ;
if (smtpCode != SMTP_LOGINFO)
{ rc = E_BADSENDER ; plog->Out("Expected code 334. Got instead code %d\n", smtpCode) ; goto Quit ; }
// Send the username
inp.Clear() ;
oup.Clear() ;
inp = uname ;
Base64Encode(oup, inp) ;
oup << "\r\n" ;
S = oup ;
if ((rc = C.Send(*S, S.Length())) != E_OK)
{ plog->Out("Could not send username\n") ; goto Quit ; }
if (_hzGlobal_Debug & HZ_DEBUG_MAILER)
plog->Out("Client -> %s", *S) ;
// Expect the message 'send password' (written in base64)
C.Recv(rbuf, nRecv, HZ_MAXPACKET) ;
if (nRecv > 0)
rbuf[nRecv] = 0;
else
rbuf[0]= 0;
if (_hzGlobal_Debug & HZ_DEBUG_MAILER)
plog->Out("Server -> %s", rbuf) ;
smtpCode = _getSmtpCode(rbuf) ;
if (smtpCode != SMTP_LOGINFO)
{
plog->Out("Expected code 334 (request for password). Got %d\n", smtpCode) ;
rc = E_BADSENDER ;
goto Quit ;
}
// Send the password
inp.Clear() ;
oup.Clear() ;
inp = passwd ;
Base64Encode(oup, inp) ;
oup << "\r\n" ;
S = oup ;
if ((rc = C.Send(*S, S.Length())) != E_OK)
{ plog->Out("Could not send password\n") ; goto Quit ; }
if (_hzGlobal_Debug & HZ_DEBUG_MAILER)
plog->Out("Client -> %s", *S) ;
// Expect the 235 Go Ahead
C.Recv(rbuf, nRecv, HZ_MAXPACKET) ;
if (nRecv > 0)
rbuf[nRecv] = 0;
else
rbuf[0]= 0;
if (_hzGlobal_Debug & HZ_DEBUG_MAILER)
plog->Out("Server -> %s", rbuf) ;
smtpCode = _getSmtpCode(rbuf) ;
if (smtpCode != SMTP_GO_AHEAD)
{
plog->Out("Expected code 235 (Login OK, Go Ahead). Got %d\n", smtpCode) ;
rc = E_BADSENDER ;
goto Quit ;
}
}
/*
** ** Now send the sender details
** */
sprintf(sbuf, "MAIL FROM: <%s>\r\n", *m_AddrFrom) ;
if ((rc = C.Send(sbuf, strlen(sbuf))) != E_OK)
{ plog->Out("Could not send MAIL FROM message\n") ; goto Quit ; }
if (_hzGlobal_Debug & HZ_DEBUG_MAILER)
plog->Out("Client -> %s", sbuf) ;
// Expect 'sender ok'
C.Recv(rbuf, nRecv, HZ_MAXPACKET) ;
if (nRecv > 0)
rbuf[nRecv] = 0;
else
rbuf[0]= 0;
if (_hzGlobal_Debug & HZ_DEBUG_MAILER)
plog->Out("Server -> %s", rbuf) ;
smtpCode = _getSmtpCode(rbuf) ;
if (smtpCode != SMTP_OK)
{
plog->Out("Expected a 250 (OK) code. Got %d\n", smtpCode) ;
rc = E_BADSENDER ;
goto Quit ;
}
/*
** ** Now send the reciprient details
** */
for (rx = m_Recipients ; rx.Valid() ; rx++)
{
e = rx.Element() ;
sprintf(sbuf, "RCPT TO: <%s>\r\n", *e) ;
C.Send(sbuf, strlen(sbuf)) ;
if (_hzGlobal_Debug & HZ_DEBUG_MAILER)
plog->Out("Client -> %s", sbuf) ;
// Expect 'recipient ok'
rc = C.Recv(rbuf, nRecv, HZ_MAXPACKET) ;
if (rc != E_OK)
{
plog->Out("Broken pipe while sending recipient details\n") ;
goto Quit ;
}
if (nRecv > 0)
rbuf[nRecv] = 0;
else
rbuf[0]= 0;
if (_hzGlobal_Debug & HZ_DEBUG_MAILER)
plog->Out("Server -> %s", rbuf) ;
smtpCode = _getSmtpCode(rbuf) ;
if (smtpCode != SMTP_OK && smtpCode != SMTP_GO_AHEAD)
{
plog->Out("Expected a 250 (OK) or 235 (Go Ahead) code. Got %d\n", smtpCode) ;
rc = E_NOACCOUNT ;
goto Quit ;
}
}
// Carbon copy recipients
for (rx = m_CC ; rx.Valid() ; rx++)
{
e = rx.Element() ;
sprintf(sbuf, "RCPT TO: <%s>\r\n", *e) ;
C.Send(sbuf, strlen(sbuf)) ;
if (_hzGlobal_Debug & HZ_DEBUG_MAILER)
plog->Out("Client -> %s", sbuf) ;
// Expect 'recipient ok'
rc = C.Recv(rbuf, nRecv, HZ_MAXPACKET) ;
if (rc != E_OK)
{
plog->Out("Broken pipe while sending recipient details\n") ;
goto Quit ;
}
if (nRecv > 0)
rbuf[nRecv] = 0;
else
rbuf[0]= 0;
if (_hzGlobal_Debug & HZ_DEBUG_MAILER)
plog->Out("Server -> %s", rbuf) ;
smtpCode = _getSmtpCode(rbuf) ;
if (smtpCode != SMTP_OK)
{
plog->Out("Target user not ok so quiting\n") ;
rc = E_NOACCOUNT ;
goto Quit ;
}
}
// Blind carbon copy recipients
for (rx = m_BCC ; rx.Valid() ; rx++)
{
e = rx.Element() ;
sprintf(sbuf, "RCPT TO: <%s>\r\n", *e) ;
C.Send(sbuf, strlen(sbuf)) ;
if (_hzGlobal_Debug & HZ_DEBUG_MAILER)
plog->Out("Client -> %s", sbuf) ;
// Expect 'recipient ok'
rc = C.Recv(rbuf, nRecv, HZ_MAXPACKET) ;
if (rc != E_OK)
{
plog->Out("Broken pipe while sending recipient details\n") ;
goto Quit ;
}
if (nRecv > 0)
rbuf[nRecv] = 0;
else
rbuf[0]= 0;
if (_hzGlobal_Debug & HZ_DEBUG_MAILER)
plog->Out("Server -> %s", rbuf) ;
smtpCode = _getSmtpCode(rbuf) ;
if (smtpCode != SMTP_OK)
{
plog->Out("Target user not ok so quiting\n") ;
rc = E_NOACCOUNT ;
goto Quit ;
}
}
// Now send the data marker
sprintf(sbuf, "DATA\r\n") ;
C.Send(sbuf, strlen(sbuf)) ;
if (_hzGlobal_Debug & HZ_DEBUG_MAILER)
plog->Out("Client -> %s", sbuf) ;
// The target should ask for the mail
// 354 Enter mail, end with "." on a line by itself
C.Recv(rbuf, nRecv, HZ_MAXPACKET) ;
if (nRecv > 0)
rbuf[nRecv] = 0;
else
rbuf[0]= 0;
if (_hzGlobal_Debug & HZ_DEBUG_MAILER)
plog->Out("Server -> %s", rbuf) ;
// So we send it
if (_hzGlobal_Debug & HZ_DEBUG_MAILER)
plog->Out("Sending %d bytes of data\n", m_Final.Size()) ;
// m_Final.Rewind() ;
// Repeat calls to Send() to transmit email message
ci = m_Final ;
for (rc = E_OK ; rc == E_OK ;)
{
for (i = sbuf, nSend = 0; !ci.eof() && nSend < HZ_MAXPACKET ; nSend++, ci++)
*i++ = *ci ;
if (nSend == 0)
break ;
nTotal += nSend ;
rc = C.Send(sbuf, nSend) ;
}
if (_hzGlobal_Debug & HZ_DEBUG_MAILER)
plog->Out("Sent %d of %d bytes\n", nTotal, m_Final.Size()) ;
// Send terminator sequence
strcpy(sbuf, ".\r\n") ;
C.Send(sbuf, strlen(sbuf)) ;
if (_hzGlobal_Debug & HZ_DEBUG_MAILER)
plog->Out("Client -> %s", sbuf) ;
// rc = C.Send(m_Final) ;
if (rc != E_OK)
{
plog->Out("Could not transmit msg body\n") ;
goto Quit ;
}
// Target should send us a message like
// 250 SAA00984 Message accepted for delivery
C.Recv(rbuf, nRecv, HZ_MAXPACKET) ;
if (nRecv > 0)
rbuf[nRecv] = 0;
else
rbuf[0]= 0;
if (_hzGlobal_Debug & HZ_DEBUG_MAILER)
plog->Out("Server -> %s", rbuf) ;
Quit:
// Now send QUIT command
sprintf(sbuf, "QUIT\r\n") ;
C.Send(sbuf, strlen(sbuf)) ;
if (_hzGlobal_Debug & HZ_DEBUG_MAILER)
plog->Out("Client -> %s", sbuf) ;
// Expect the following msg from the target
// 221 osuks.densitron.net closing connection
C.Recv(rbuf, nRecv, HZ_MAXPACKET) ;
if (nRecv > 0)
rbuf[nRecv] = 0;
else
rbuf[0]= 0;
if (_hzGlobal_Debug & HZ_DEBUG_MAILER)
plog->Out("Server -> %s", rbuf) ;
// It's all over!
C.Close() ;
return rc ;
}