📄 smtp.cpp
字号:
sDate,
m_sXMailer);
//Add the optional Reply-To Field
if (m_ReplyTo.m_sEmailAddress.GetLength())
{
CString sReply;
sReply.Format(_T("Reply-To: %s\r\n"), m_ReplyTo.GetRegularFormat());
sBuf += sReply;
}
//Add the optional fields if attachments are included
if (m_Attachments.GetSize())
sBuf += _T("MIME-Version: 1.0\r\nContent-type: multipart/mixed; boundary=\"#BOUNDARY#\"\r\n");
sBuf += _T("\r\n");
//Return the result
return sBuf;
}
void CSMTPMessage::FixSingleDot(CString& sBody)
{
int nFind = sBody.Find(_T("\n."));
if (nFind != -1)
{
CString sLeft(sBody.Left(nFind+1));
CString sRight(sBody.Right(sBody.GetLength()-(nFind+1)));
FixSingleDot(sRight);
sBody = sLeft + _T(".") + sRight;
}
}
void CSMTPMessage::AddBody(const CString& sBody)
{
m_sBody = sBody;
//Fix the case of a single dot on a line in the message body
FixSingleDot(m_sBody);
}
BOOL CSMTPMessage::AddMultipleRecipients(const CString& sRecipients)
{
ASSERT(sRecipients.GetLength()); //An empty string is now allowed
//Loop through the whole string, adding recipients as they are encountered
int length = sRecipients.GetLength();
TCHAR* buf = new TCHAR[length + 1]; // Allocate a work area (don't touch parameter itself)
_tcscpy(buf, sRecipients);
for (int pos=0, start=0; pos<=length; pos++)
{
//Valid separators between addresses are ',' or ';'
if ((buf[pos] == _T(',')) || (buf[pos] == _T(';')) || (buf[pos] == 0))
{
buf[pos] = 0; //Redundant when at the end of string, but who cares.
CString sTemp(&buf[start]);
// Now divide the substring into friendly names and e-mail addresses
CSMTPAddress To;
int nMark = sTemp.Find(_T('<'));
if (nMark >= 0)
{
To.m_sFriendlyName = sTemp.Left(nMark);
int nMark2 = sTemp.Find(_T('>'));
if (nMark2 < nMark)
{
//An invalid string was sent in, fail the call
delete[] buf;
SetLastError(ERROR_INVALID_DATA);
TRACE(_T("An error occurred while parsing the recipients string\n"));
return FALSE;
}
// End of mark at closing bracket or end of string
nMark2 > -1 ? nMark2 = nMark2 : nMark2 = sTemp.GetLength() - 1;
To.m_sEmailAddress = sTemp.Mid(nMark + 1, nMark2 - (nMark + 1));
}
else
{
To.m_sEmailAddress = sTemp;
To.m_sFriendlyName = _T(_T(""));
}
//Finally add the new recipient to the array of recipients
To.m_sEmailAddress.TrimRight();
To.m_sEmailAddress.TrimLeft();
To.m_sFriendlyName.TrimRight();
To.m_sFriendlyName.TrimLeft();
AddRecipient(To);
start = pos + 1;
}
}
delete[] buf;
return TRUE;
}
CSMTPConnection::CSMTPConnection()
{
m_bConnected = FALSE;
#ifdef _DEBUG
m_dwTimeout = 20000; //default timeout of 20 seconds when debugging
#else
m_dwTimeout = 2000; //default timeout of 2 seconds for normal release code
#endif
}
CSMTPConnection::~CSMTPConnection()
{
if (m_bConnected)
Disconnect();
}
BOOL CSMTPConnection::Connect(LPCTSTR pszHostName, int nPort)
{
//For correct operation of the T2A macro, see MFC Tech Note 59
USES_CONVERSION;
//paramater validity checking
ASSERT(pszHostName);
//Create the socket
if (!m_SMTP.Create())
{
TRACE(_T("Failed to create client socket, GetLastError returns: %d\n"), GetLastError());
return FALSE;
}
//Connect to the SMTP Host
if (!m_SMTP.Connect(pszHostName, nPort))
{
TRACE(_T("Could not connect to the SMTP server %s on port %d, GetLastError returns: %d\n"), pszHostName, nPort, GetLastError());
return FALSE;
}
else
{
//We're now connected !!
m_bConnected = TRUE;
//check the response to the login
if (!ReadCommandResponse(220))
{
TRACE(_T("An unexpected SMTP login response was received\n"));
Disconnect();
return FALSE;
}
//retreive the localhost name
char sHostName[100];
gethostname(sHostName, sizeof(sHostName));
TCHAR* pszHostName = A2T(sHostName);
//Send the HELO command
CString sBuf;
sBuf.Format(_T("HELO %s\r\n"), pszHostName);
LPCSTR pszData = T2A((LPTSTR) (LPCTSTR) sBuf);
int nCmdLength = strlen(pszData);
if (!m_SMTP.Send(pszData, nCmdLength))
{
Disconnect();
TRACE(_T("An unexpected error occurred while sending the HELO command\n"));
return FALSE;
}
//check the response to the HELO command
if (!ReadCommandResponse(250))
{
Disconnect();
TRACE(_T("An unexpected HELO response was received\n"));
return FALSE;
}
return TRUE;
}
}
BOOL CSMTPConnection::Disconnect()
{
BOOL bSuccess = FALSE;
//disconnect from the SMTP server if connected
if (m_bConnected)
{
char sBuf[10];
strcpy(sBuf, "QUIT\r\n");
int nCmdLength = strlen(sBuf);
if (!m_SMTP.Send(sBuf, nCmdLength))
TRACE(_T("Failed in call to send QUIT command, GetLastError returns: %d\n"), GetLastError());
//Check the reponse
bSuccess = ReadCommandResponse(221);
if (!bSuccess)
{
SetLastError(ERROR_BAD_COMMAND);
TRACE(_T("An unexpected QUIT response was received\n"));
}
//Reset all the state variables
m_bConnected = FALSE;
}
else
TRACE(_T("Already disconnected from SMTP server, doing nothing\n"));
//free up our socket
m_SMTP.Close();
return bSuccess;
}
BOOL CSMTPConnection::SendMessage(CSMTPMessage& Message)
{
//For correct operation of the T2A macro, see MFC Tech Note 59
USES_CONVERSION;
//paramater validity checking
ASSERT(m_bConnected); //Must be connected to send a message
//Send the MAIL command
ASSERT(Message.m_From.m_sEmailAddress.GetLength());
CString sBuf;
sBuf.Format(_T("MAIL FROM:<%s>\r\n"), Message.m_From.m_sEmailAddress);
LPCSTR pszMailFrom = T2A((LPTSTR) (LPCTSTR) sBuf);
int nCmdLength = strlen(pszMailFrom);
if (!m_SMTP.Send(pszMailFrom, nCmdLength))
{
TRACE(_T("Failed in call to send MAIL command, GetLastError returns: %d\n"), GetLastError());
return FALSE;
}
//check the response to the MAIL command
if (!ReadCommandResponse(250))
{
SetLastError(ERROR_BAD_COMMAND);
TRACE(_T("An unexpected MAIL response was received\n"));
return FALSE;
}
//Send the RCPT command, one for each recipient
ASSERT(Message.GetNumberOfRecipients()); //Must have at least one recipient for the message
for (int i=0; i<Message.GetNumberOfRecipients(); i++)
{
CSMTPAddress recipient = Message.GetRecipient(i);
ASSERT(recipient.m_sEmailAddress.GetLength()); //must have an email address for this recipient
sBuf.Format(_T("RCPT TO:<%s>\r\n"), recipient.m_sEmailAddress);
LPTSTR pszRCPT = T2A((LPTSTR) (LPCTSTR) sBuf);
int nCmdLength = strlen(pszRCPT);
if (!m_SMTP.Send(pszRCPT, nCmdLength))
{
TRACE(_T("Failed in call to send MAIL command, GetLastError returns: %d\n"), GetLastError());
return FALSE;
}
//check the response to each RCPT command
if (!ReadCommandResponse(250))
{
SetLastError(ERROR_BAD_COMMAND);
TRACE(_T("An unexpected RCPT response was received\n"));
return FALSE;
}
}
//Send the DATA command
char* pszDataCommand = "DATA\r\n";
nCmdLength = strlen(pszDataCommand);
if (!m_SMTP.Send(pszDataCommand, nCmdLength))
{
TRACE(_T("Failed in call to send MAIL command, GetLastError returns: %d\n"), GetLastError());
return FALSE;
}
//check the response to the DATA command
if (!ReadCommandResponse(354))
{
SetLastError(ERROR_BAD_COMMAND);
TRACE(_T("An unexpected DATA response was received\n"));
return FALSE;
}
//Send the Header
CString sHeader = Message.GetHeader();
char* pszHeader = T2A((LPTSTR) (LPCTSTR) sHeader);
nCmdLength = strlen(pszHeader);
if (!m_SMTP.Send(pszHeader, nCmdLength))
{
TRACE(_T("Failed in call to send the header, GetLastError returns: %d\n"), GetLastError());
return FALSE;
}
//Send the Mime Header for the body
if (Message.m_Attachments.GetSize())
{
char* psBodyHeader = (_T("\r\n--#BOUNDARY#\r\n")\
_T("Content-Type: text/plain; charset=us-ascii\r\n")\
_T("Content-Transfer-Encoding: quoted-printable\r\n\r\n"));
nCmdLength = strlen(psBodyHeader);
if (!m_SMTP.Send(psBodyHeader, nCmdLength))
{
TRACE(_T("Failed in call to send the body header, GetLastError returns: %d\n"), GetLastError());
return FALSE;
}
}
//Send the body
char* pszBody = T2A((LPTSTR) (LPCTSTR) Message.m_sBody);
nCmdLength = strlen(pszBody);
if (!m_SMTP.Send(pszBody, nCmdLength))
{
TRACE(_T("Failed in call to send the header, GetLastError returns: %d\n"), GetLastError());
return FALSE;
}
//Send all the attachments
for (i=0; i<Message.m_Attachments.GetSize(); i++)
{
CSMTPAttachment* pAttachment = Message.m_Attachments.GetAt(i);
//First send the Mime header for each attachment
CString sContent;
sContent.Format(_T("\r\n\r\n--#BOUNDARY#\r\n")\
_T("Content-Type: application/octet-stream; name=%s\r\n")\
_T("Content-Transfer-Encoding: base64\r\n")\
_T("Content-Disposition: attachment; filename=%s\r\n\r\n"),
pAttachment->GetFilename(), pAttachment->GetFilename());
char* pszContent = T2A((LPTSTR) (LPCTSTR) sContent);
nCmdLength = strlen(pszContent);
if (!m_SMTP.Send(pszContent, nCmdLength))
{
TRACE(_T("Failed in call to send Mime attachment header, GetLastError returns: %d\n"), GetLastError());
return FALSE;
}
//Then send the encoded attachment
if (!m_SMTP.Send(pAttachment->GetEncodedBuffer(), pAttachment->GetEncodedSize()))
{
TRACE(_T("Failed in call to send the attachment, GetLastError returns: %d\n"), GetLastError());
return FALSE;
}
}
//Send the final mime boundary
if (Message.m_Attachments.GetSize())
{
char* pszFinalBoundary = "\r\n--#BOUNDARY#--";
nCmdLength = strlen(pszFinalBoundary);
if (!m_SMTP.Send(pszFinalBoundary, nCmdLength))
{
TRACE(_T("Failed in call to send Mime attachment header, GetLastError returns: %d\n"), GetLastError());
return FALSE;
}
}
//Send the end of message indicator
char* pszEOM = "\r\n.\r\n";
nCmdLength = strlen(pszEOM);
if (!m_SMTP.Send(pszEOM, nCmdLength))
{
TRACE(_T("Failed in call to send end of message indicator, GetLastError returns: %d\n"), GetLastError());
return FALSE;
}
//check the response to the End of Message command
if (!ReadCommandResponse(250))
{
SetLastError(ERROR_BAD_COMMAND);
TRACE(_T("An unexpected end of message response was received\n"));
return FALSE;
}
return TRUE;
}
BOOL CSMTPConnection::ReadCommandResponse(int nExpectedCode)
{
char sBuf[1000];
return ReadResponse(sBuf, sizeof(sBuf), "\r\n", nExpectedCode);
}
BOOL CSMTPConnection::ReadResponse(LPSTR pszBuffer, int nBuf, LPSTR pszTerminator, int nExpectedCode)
{
//paramater validity checking
ASSERT(pszBuffer);
ASSERT(nBuf);
//must have been created first
ASSERT(m_bConnected);
//retrieve the reponse using until we
//get the terminator or a timeout occurs
BOOL bFoundTerminator = FALSE;
int nReceived = 0;
DWORD dwStartTicks = ::GetTickCount();
while (!bFoundTerminator)
{
//timeout has occured
if ((::GetTickCount() - dwStartTicks) > m_dwTimeout)
{
pszBuffer[nReceived] = '\0';
SetLastError(WSAETIMEDOUT);
m_sLastCommandResponse = pszBuffer; //Hive away the last command reponse
return FALSE;
}
//check the socket for readability
BOOL bReadible;
if (!m_SMTP.IsReadible(bReadible))
{
pszBuffer[nReceived] = '\0';
m_sLastCommandResponse = pszBuffer; //Hive away the last command reponse
return FALSE;
}
else if (!bReadible) //no data to receive, just loop around
{
Sleep(0); //Yield up our time slice to other running apps
//helps to improve performance of system
continue;
}
//receive the data from the socket
int nData = m_SMTP.Receive(pszBuffer+nReceived, nBuf-nReceived);
if (nData == SOCKET_ERROR)
{
pszBuffer[nReceived] = '\0';
m_sLastCommandResponse = pszBuffer; //Hive away the last command reponse
return FALSE;
}
else
{
if (nData)
dwStartTicks = ::GetTickCount(); //Reset the idle timeout
nReceived += nData; //Increment the count of data received
}
pszBuffer[nReceived] = '\0'; //temporarily NULL terminate the string
//so that strstr works
bFoundTerminator = (strstr(pszBuffer, pszTerminator) != NULL);
}
//Remove the terminator from the response data
pszBuffer[nReceived - strlen(pszTerminator)] = '\0';
//determine if the response is an error
char sCode[4];
strncpy(sCode, pszBuffer, 3);
sCode[3] = '\0';
sscanf(sCode, "%d", &m_nLastCommandResponseCode);
BOOL bSuccess = (m_nLastCommandResponseCode == nExpectedCode);
if (!bSuccess)
{
SetLastError(WSAEPROTONOSUPPORT);
m_sLastCommandResponse = pszBuffer; //Hive away the last command reponse
}
return bSuccess;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -