📄 smtp.cpp
字号:
while (dwLen > dwPosition)
{
dwMax = min(CMD_BLOCK_SIZE,dwLen - dwPosition);
nRet = send(m_hSocket,&pszBuff[dwPosition],dwMax,0);
if (nRet == SOCKET_ERROR)
{
free(pszBuff);
return nRet;
}
dwPosition += dwMax;
if(dwLen>1024)SetText ("Remain%d Bytes,Total:%d Have Sent %d%。",dwPosition,dwLen,(int)dwPosition*100/dwLen);
if (bReportProgress)
{
if (!SmtpProgress(pszBuff,dwPosition,dwLen))
{
free(pszBuff);
return -1;
}
}
}
// Wait for the CMD to finish being sent
FD_ZERO(&set);
FD_SET(m_hSocket,&set);
nRet = select(1,NULL,&set,NULL,NULL);
free(pszBuff);
}
// Prepare to receive a response
ZeroMemory(szResult,CMD_RESPONSE_SIZE);
pszPos = szResult;
// Wait for the specified timeout for a full response string
dwTick = GetTickCount();
while (GetTickCount() - dwTick < (m_dwCmdTimeout * 1000))
{
FD_SET(m_hSocket,&set);
tv.tv_sec = m_dwCmdTimeout - ((GetTickCount() - dwTick) / 1000);
tv.tv_usec = 0;
// Check the socket for readability
nRet = select(1,&set,NULL,NULL,&tv);
if (nRet == SOCKET_ERROR) break;
// If the socket has data, read it.
if (nRet == 1)
{
nRet = recv(m_hSocket,pszPos,CMD_RESPONSE_SIZE - (pszPos - szResult),0);
// Treats a graceful shutdown as an error
if (nRet == 0) nRet = SOCKET_ERROR;
if (nRet == SOCKET_ERROR) break;
// Add the data to the total response string & check for a LF
pszPos += nRet;
pszTok = strrchr(szResult,'\n');
if (pszTok)
{
// Truncate CRLF combination and exit our wait loop
pszTok --;
pszTok[0] = 0;
break;
}
}
}
// Assign the response string
m_strResult = A2CT(szResult);
// Evaluate the numeric response code
if (nRet && nRet != SOCKET_ERROR)
{
szResult[3] = 0;
nRet = atoi(szResult);
SmtpCommandResponse(pszCmd, nRet, (LPTSTR)m_strResult.c_str());
}
else nRet = -1;
return nRet;
}
// Placeholder function -- overridable
// This function is called when the SMTP server gives us an unexpected error
// The <nError> value is the SMTP server's numeric error response, and the <pszErr>
// is the descriptive error text
//
// <pszErr> may be NULL if the server failed to respond before the timeout!
// <nError> will be -1 if a catastrophic failure occurred.
//
// Return 0, or nError. The return value is currently ignored.
int CSmtp::SmtpError(int /*nError*/, LPTSTR pszErr)
{
#ifdef _DEBUG
if (pszErr)
{
OutputDebugString(_T("SmtpError: "));
OutputDebugString(pszErr);
OutputDebugString(_T("\n"));
}
#endif
return 0;
}
// Placeholder function -- overridable
// Currently the only warning condition that this class is designed for is
// an authentication failure. In that case, <nWarning> will be 535,
// which is the RFC error for authentication failure. If authentication
// fails, you can override this function to prompt the user for a new
// username and password. Change the <m_strUser> and <m_strPass> member
// variables and return TRUE to retry authentication.
//
// <pszWarning> may be NULL if the server did not respond in time!
//
// Return FALSE to abort authentication, or TRUE to retry.
int CSmtp::SmtpWarning(int /*nWarning*/, LPTSTR pszWarning)
{
#ifdef _DEBUG
if (pszWarning)
{
OutputDebugString(_T("SmtpWarning: "));
OutputDebugString(pszWarning);
OutputDebugString(_T("\n"));
}
#endif
return 0;
}
// Placeholder function -- overridable
// This is an informational callback only, and provides a means to inform
// the caller as the SMTP session progresses.
// ALWAYS check for NULL values on <pszCmd> and <pszResponse> before performing
// any actions!
// <nResponse> will be -1 if a catastrophic failure occurred, but that will
// be raised in the SmtpError() event later on during processing.
void CSmtp::SmtpCommandResponse(LPTSTR pszCmd, int /*nResponse*/, LPTSTR pszResponse)
{
#ifdef _DEBUG
/* if (pszCmd)
{
TCHAR szOut[MAX_PATH+1];
OutputDebugString(_T("SmtpCommand : "));
while (lstrlen(pszCmd) > MAX_PATH)
{
lstrcpyn(szOut,pszCmd,MAX_PATH+1);
OutputDebugString(szOut);
Sleep(100);
pszCmd += MAX_PATH;
}
OutputDebugString(pszCmd);
}
OutputDebugString(_T("SmtpResponse: "));
OutputDebugString(pszResponse);
OutputDebugString(_T("\n"));*/
#endif
}
// Placeholder function -- overridable
// This is a progress callback to indicate that data is being sent over the wire
// and that the operation may take some time.
// Return TRUE to continue sending, or FALSE to abort the transfer
BOOL CSmtp::SmtpProgress(LPSTR /*pszBuffer*/, DWORD /*dwBytesSent*/, DWORD /*dwBytesTotal*/)
{
return TRUE; // Continue sending the data
}
// Raises a SmtpError() condition
int CSmtp::RaiseError(int nError)
{
// If the error code is -1, something catastrophic happened
// so we're effectively not connected to any SMTP server.
if (nError == -1) m_bConnected = FALSE;
return SmtpError(nError, (LPTSTR)m_strResult.c_str());
}
// Warnings are recoverable errors that we may be able to continue working with
int CSmtp::RaiseWarning(int nWarning)
{
return SmtpWarning(nWarning, (LPTSTR)m_strResult.c_str());
}
// E-Mail's a message
// Returns 0 if successful, -1 if an internal error occurred, or a positive
// error value if the SMTP server gave an error or failure response.
int CSmtp::SendMessage(CSmtpMessage &msg)
{
int nRet;
int n;
// int nRecipients = 0;
int nRecipientCount = 0;
// Check if we have a sender
if (!msg.Sender.Address.length()) return -1;
// Check if we have recipients
if (!msg.Recipient.Address.length() && !msg.CC.GetSize()) return -1;
// Check if we have a message body or attachments
// *** Commented out to remove the requirement that a message have a body or attachments
// if (!msg.Message.GetSize() && !msg.Attachments.GetSize()) return -1;
// Send the sender's address
nRet = SendFrom((LPTSTR)msg.Sender.Address.c_str());
if (nRet) return nRet;
// If we have a recipient, send it
nRecipientCount = 0; // Count of recipients
if (msg.Recipient.Address.length())
{
nRet = SendTo((LPTSTR)msg.Recipient.Address.c_str());
if (!nRet) nRecipientCount ++;
}
// If we have any CC's, send those.
for (n = 0;n < msg.CC.GetSize();n++)
{
nRet = SendTo((LPTSTR)msg.CC[n].Address.c_str());
if (!nRet) nRecipientCount ++;
}
// If we have any bcc's, send those.
for (n = 0;n < msg.BCC.GetSize();n++)
{
nRet = SendTo((LPTSTR)msg.BCC[n].Address.c_str());
if (!nRet) nRecipientCount ++;
}
// If we failed on all recipients, we must abort.
if (!nRecipientCount)
RaiseError(nRet);
else
nRet = SendData(msg);
return nRet;
}
// Simplified way to send a message.
// <pvAttachments> can be either an LPTSTR containing NULL terminated strings, in which
// case <dwAttachmentCount> should be zero, or <pvAttachments> can be an LPTSTR *
// containing an array of LPTSTR's, in which case <dwAttachmentCount> should equal the
// number of strings in the array.
int CSmtp::SendMessage(CSmtpAddress &addrFrom, CSmtpAddress &addrTo, LPCTSTR pszSubject, LPTSTR pszMessage, LPVOID pvAttachments, DWORD dwAttachmentCount)
{
CSmtpMessage message;
CSmtpMessageBody body;
CSmtpAttachment attach;
body = pszMessage;
message.Sender = addrFrom;
message.Recipient = addrTo;
message.Message.Add(body);
message.Subject = pszSubject;
// If the attachment count is zero, but the pvAttachments variable is not NULL,
// assume that the ppvAttachments variable is a string value containing NULL terminated
// strings. A double NULL ends the list.
// Example: LPTSTR pszAttachments = "foo.exe\0bar.zip\0autoexec.bat\0\0";
if (!dwAttachmentCount && pvAttachments)
{
LPTSTR pszAttachments = (LPTSTR)pvAttachments;
while (lstrlen(pszAttachments))
{
attach.FileName = pszAttachments;
message.Attachments.Add(attach);
pszAttachments = &pszAttachments[lstrlen(pszAttachments)];
}
}
// dwAttachmentCount is not zero, so assume pvAttachments is an array of LPTSTR's
// Example: LPTSTR *ppszAttachments = {"foo.exe","bar.exe","autoexec.bat"};
if (pvAttachments && dwAttachmentCount)
{
LPTSTR *ppszAttachments = (LPTSTR *)pvAttachments;
while (dwAttachmentCount-- && ppszAttachments)
{
attach.FileName = ppszAttachments[dwAttachmentCount];
message.Attachments.Add(attach);
}
}
return SendMessage(message);
}
// Yet an even simpler method for sending a message
// <pszAddrFrom> and <pszAddrTo> should be e-mail addresses with no decorations
// Example: "foo@bar.com"
// <pvAttachments> and <dwAttachmentCount> are described above in the alternative
// version of this function
int CSmtp::SendMessage(LPTSTR pszAddrFrom, LPTSTR pszAddrTo, LPTSTR pszSubject, LPTSTR pszMessage, LPVOID pvAttachments, DWORD dwAttachmentCount)
{
CSmtpAddress addrFrom(pszAddrFrom);
CSmtpAddress addrTo(pszAddrTo);
return SendMessage(addrFrom,addrTo,pszSubject,pszMessage,pvAttachments,dwAttachmentCount);
}
// Tell the SMTP server we're quitting
// Returns 0 if successful, or a positive
// error value if the SMTP server gave an error or failure response.
int CSmtp::SendQuitCmd()
{
int nRet;
if (!m_bConnected) return 0;
nRet = SendCmd(_T("QUIT\r\n"));
if (nRet != 221) RaiseError(nRet);
m_bConnected = FALSE;
return (nRet == 221) ? 0:nRet;
}
// Initiate a conversation with the SMTP server
// Returns 0 if successful, or a positive
// error value if the SMTP server gave an error or failure response.
int CSmtp::SendHello()
{
int nRet = 0;
TCHAR szName[64];
TCHAR szMsg[MAX_PATH];
DWORD dwSize = 64;
GetComputerName(szName,&dwSize);
// First try a EHLO if we're using ESMTP
wsprintf(szMsg,_T("EHLO %s\r\n"),szName);
if (m_bExtensions) nRet = SendCmd(szMsg);
// If we got a 250 response, we're using ESMTP, otherwise revert to regular SMTP
if (nRet != 250)
{
m_bUsingExtensions = FALSE;
szMsg[0] = 'H';
szMsg[1] = 'E';
nRet = SendCmd(szMsg);
}
else m_bUsingExtensions = TRUE;
// Raise any unexpected responses
if (nRet != 250)
{
RaiseError(nRet);
return nRet;
}
// We're connected!
m_bConnected = TRUE;
// Send authentication if we have any.
// We don't fail just because authentication failed, however.
if (m_bUsingExtensions) SendAuthentication();
return 0;
}
// Requests authentication for the session if the server supports it,
// and attempts to submit the user's credentials.
// Returns 0 if successful, or a positive
// error value if the SMTP server gave an error or failure response.
int CSmtp::SendAuthentication()
{
USES_CONVERSION;
int nRet = 0;
CBase64 cvt;
LPCSTR pszTemp;
TCHAR szMsg[MAX_PATH];
CHAR szAuthType[MAX_PATH];
// This is an authentication loop, we can authenticate multiple times in case of failure.
while(1)
{
// If we don't have a username, skip authentication
if (!m_strUser.length()) return 0;
// Make the authentication request
nRet = SendCmd(_T("AUTH LOGIN\r\n"));
// If it was rejected, we have to abort.
if (nRet != 334)
{
RaiseWarning(nRet);
return nRet;
}
// Authentication has 2 stages for username and password.
// It is possible if the authentication fails here that we can
// resubmit proper credentials.
while (1)
{
// Decode the authentication string being requested
_T2A(szAuthType,&(m_strResult.c_str())[4]);
cvt.Decode(szAuthType);
pszTemp = cvt.DecodedMessage();
if (!lstrcmpiA(pszTemp,"Username:"))
cvt.Encode(T2CA(m_strUser.c_str()));
else if (!lstrcmpiA(pszTemp,"Password:"))
cvt.Encode(T2CA(m_strPass.c_str()));
else break;
wsprintf(szMsg,_T("%s\r\n"),A2CT(cvt.EncodedMessage()));
nRet = SendCmd(szMsg);
// If we got a failed authentication request, raise a warning.
// this gives the owner a chance to change the username and password.
if (nRet == 535)
{
// Return FALSE to fail, or TRUE to retry
nRet = RaiseWarning(nRet);
if (!nRet)
{
// Reset the error back to 535. It's now an error rather than a warning
nRet = 535;
break;
}
}
// Break on any response other than 334, which indicates a request for more information
if (nRet != 334) break;
}
// Break if we're not retrying a failed authentication
if (nRet != TRUE) break;
}
// Raise an error if we failed to authenticate
if (nRet != 235) RaiseError(nRet);
return (nRet == 235) ? 0:nRet;
}
// Send a MAIL FROM command to the server
// Returns 0 if successful, or a positive
// error value if the SMTP server gave an error or failure response.
int CSmtp::SendFrom(LPTSTR pszFrom)
{
int nRet = 0;
TCHAR szMsg[MAX_PATH];
wsprintf(szMsg,_T("MAIL FROM: <%s>\r\n"),pszFrom);
while (1)
{
nRet = SendCmd(szMsg);
// Send authentication if required, and retry the command
if (nRet == 530||nRet == 505) nRet = SendAuthentication();
else break;
}
// Raise an error if we failed
if (nRet != 250) RaiseError(nRet);
return (nRet == 250) ? 0:nRet;
}
// Send a RCPT TO command to the server
// Returns 0 if successful, or a positive
// error value if the SMTP server gave an error or failure response.
int CSmtp::SendTo(LPTSTR pszTo)
{
int nRet;
TCHAR szMsg[MAX_PATH];
wsprintf(szMsg,_T("RCPT TO: <%s>\r\n"),pszTo);
nRet = SendCmd(szMsg);
if (nRet != 250 && nRet != 251) RaiseWarning(nRet);
return (nRet == 250 || nRet == 251) ? 0:nRet;
}
// Send the body of an e-mail message to the server
// Returns 0 if successful, or a positive
// error value if the SMTP server gave an error or failure response.
int CSmtp::SendData(CSmtpMessage &msg)
{
int nRet;
String strMsg;
// Send the DATA command. We need a 354 to proceed
nRet = SendCmd(_T("DATA\r\n"));
if (nRet != 354)
{
RaiseError(nRet);
return nRet;
}
// Parse the body of the email message
msg.Parse(strMsg);
strMsg += _T("\r\n.\r\n");
// Send the body and expect a 250 OK reply.
nRet = SendCmd((LPTSTR)strMsg.c_str());
if (nRet != 250) RaiseError(nRet);
return (nRet == 250) ? 0:nRet;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -