📄 csmtp.cpp
字号:
BOOL CTextPlain::AppendPart(LPCTSTR szContent,
LPCTSTR szParameters,
int nEncoding,
BOOL bPath,
CString & sDestination)
{
CString sSubHeader;
CString sWrapped;
sSubHeader = build_sub_header( szContent,
szParameters,
nEncoding,
bPath );
sWrapped = wrap_text( szContent );
sDestination += (sSubHeader + sWrapped);
return TRUE;
}
CString CTextPlain::build_sub_header(LPCTSTR szContent,
LPCTSTR szParameters,
int nEncoding,
BOOL bPath)
{
CString sSubHeader;
sSubHeader.Format( _T( "Content-Type: %s%s\r\n" ),
(LPCTSTR)GetContentTypeString(),
szParameters );
sSubHeader += _T( "Content-Transfer-Encoding: " );
switch( nEncoding )
{
// This class handles only 7bit encoding, but others
// may be added here.
default:
//Fall through to...
case CMIMEMessage::_7BIT:
sSubHeader += _T( "7Bit" );
}
sSubHeader += _T( "\r\n\r\n" );
return sSubHeader;
}
CString CTextPlain::wrap_text(LPCTSTR szText)
{
CString sTemp;
CString sLeft;
CString sRight;
int lp = 0;
UINT nCount = 0;
int nSpacePos = 0;
ASSERT( szText != NULL );
if( szText == NULL )
return sTemp;
sTemp = szText;
while( lp < sTemp.GetLength() )
{
if( sTemp[ lp ] == ' ' )
nSpacePos = lp;
// Reset counter on newline
if( sTemp.Mid( lp, 2 ) == _T( "\r\n" ) )
nCount = 0;
// Wrap text at last found space
if( nCount > m_nWrapPos )
{
sLeft = sTemp.Left( nSpacePos );
sRight = sTemp.Right( sTemp.GetLength() - nSpacePos );
sLeft.TrimRight();
sRight.TrimLeft();
sLeft += _T( "\r\n" );
sTemp = sLeft + sRight;
nCount = 0;
}
else
nCount++;
lp++;
}
return sTemp;
}
//////////////////////////////////////////////////////////////////////
// CMIMEMessage
//////////////////////////////////////////////////////////////////////
// Static Member Initializers
CMIMEMessage::CMIMETypeManager CMIMEMessage::m_MIMETypeManager;
CMIMEMessage::CMIMEMessage()
{
m_sMIMEContentType = _T( "multipart/mixed");
m_sPartBoundary = _T( "WC_MAIL_PaRt_BoUnDaRy_05151998" );
m_sNoMIMEText = _T( "This is a multi-part message in MIME format." );
// Register the MIME types handled by this class
//
CMIMEContentAgent* pType;
// These objects are deleted by CMIMTypeManager's destructor
pType = new CTextPlain( TEXT_PLAIN, GetCharsPerLine() );
register_mime_type( pType );
pType = new CAppOctetStream( APPLICATION_OCTETSTREAM );
register_mime_type( pType );
}
CMIMEMessage::~CMIMEMessage()
{
}
// This implementation adds the part to the part-list used
// to build the body.
BOOL CMIMEMessage::AddMIMEPart(LPCTSTR szContent,
int nContentType,
LPCTSTR szParameters,
int nEncoding,
BOOL bPath )
{
CMIMEPart part;
part.m_nContentType = nContentType;
part.m_sParameters = szParameters;
part.m_nEncoding = nEncoding;
part.m_bPath = bPath;
part.m_sContent = szContent;
part.m_sContent.TrimLeft();
part.m_sContent.TrimRight();
if( nContentType == TEXT_PLAIN )
m_MIMEPartList.AddHead( part );
else
m_MIMEPartList.AddTail( part );
return TRUE;
}
void CMIMEMessage::prepare_header()
{
CString sTemp;
// Let the base class add its headers
CMailMessage::prepare_header();
add_header_line( _T( "MIME-Version: 1.0" ) );
sTemp.Format( _T( "Content-Type: %s; boundary=%s" ),
(LPCTSTR)m_sMIMEContentType,
(LPCTSTR)m_sPartBoundary );
add_header_line( (LPCTSTR)sTemp );
}
void CMIMEMessage::prepare_body()
{
// Class user may have assigned body text directly.
// Convert it to just another MIME part to be processed.
// If this default Content-Type isn't good enough for the
// class user, he or she should have used AddMIMEPart() instead.
if( m_sBody != _T( "" ) )
AddMIMEPart( (LPCTSTR)m_sBody, TEXT_PLAIN, "", _7BIT, FALSE );
// Initialize the body (replace current contents).
m_sBody = m_sNoMIMEText;
m_sBody += _T( "\r\n\r\n" );
append_mime_parts();
insert_message_end( m_sBody );
// Let the base class take me to Funky Town
CMailMessage::prepare_body();
}
void CMIMEMessage::insert_boundary( CString& sText )
{
CString sTemp;
if( sText.Right( 2 ) != _T( "\r\n" ) )
sText += _T( "\r\n" );
sTemp.Format( _T( "--%s\r\n" ), (LPCTSTR)m_sPartBoundary );
sText += sTemp;
}
void CMIMEMessage::insert_message_end( CString& sText )
{
CString sTemp;
if( sText.Right( 2 ) != _T( "\r\n" ) )
sText += _T( "\r\n" );
sTemp.Format( _T( "--%s--\r\n" ), (LPCTSTR)m_sPartBoundary );
sText += sTemp;
}
void CMIMEMessage::register_mime_type(CMIMEContentAgent* pMIMEType)
{
ASSERT( pMIMEType != NULL );
if( pMIMEType == NULL )
return;
m_MIMETypeManager.RegisterMIMEType( pMIMEType );
}
void CMIMEMessage::append_mime_parts()
{
POSITION part_position;
CMIMEPart* pMIMEPart = NULL;
CMIMEContentAgent* pMIMEType = NULL;
part_position = m_MIMEPartList.GetHeadPosition();
// Get each part from the list, retrieve a handler for it,
// and let the handler do its thing.
while( part_position != NULL )
{
pMIMEPart = & m_MIMEPartList.GetNext( part_position );
pMIMEType = m_MIMETypeManager.GetHandler( pMIMEPart->m_nContentType );
if( pMIMEType != NULL )
{
insert_boundary( m_sBody );
pMIMEType->AppendPart( pMIMEPart->m_sContent,
pMIMEPart->m_sParameters,
pMIMEPart->m_nEncoding,
pMIMEPart->m_bPath,
m_sBody );
}
}
}
//////////////////////////////////////////////////////////////////////
// CMIMETypeManager Implementation
//////////////////////////////////////////////////////////////////////
CMIMEMessage::CMIMETypeManager::CMIMETypeManager()
{
}
CMIMEMessage::CMIMETypeManager::~CMIMETypeManager()
{
POSITION pos;
CMIMEContentAgent* p;
m_csAccess.Lock();
pos = m_MIMETypeList.GetHeadPosition();
while( pos != NULL )
{
p = m_MIMETypeList.GetNext( pos );
delete p;
}
}
void CMIMEMessage::CMIMETypeManager::RegisterMIMEType(CMIMEContentAgent *pMIMEType)
{
ASSERT( pMIMEType != NULL );
if( pMIMEType == NULL )
return;
m_csAccess.Lock();
m_MIMETypeList.AddTail( pMIMEType );
}
CMIMEContentAgent* CMIMEMessage::CMIMETypeManager::GetHandler(int nContentType)
{
POSITION pos;
CMIMEContentAgent* pType = NULL;
m_csAccess.Lock();
pos = m_MIMETypeList.GetHeadPosition();
while( pos != NULL )
{
pType = m_MIMETypeList.GetNext( pos );
if( pType->QueryType( nContentType ) == TRUE )
break;
}
return pType;
}
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
// Static member initializers
//
// Note: the order of the entries is important.
// They must be synchronized with eResponse entries.
CSmtp::response_code CSmtp::response_table[] = {
// GENERIC_SUCCESS
{250, _T("SMTP server error")},
// CONNECT_SUCCESS
{220, _T("SMTP server not available")},
// DATA_SUCCESS
{354, _T("SMTP server not ready for data")},
// QUIT_SUCCESS
{221, _T("SMTP server didn't terminate session")}
};
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CSmtp::CSmtp(LPCTSTR szSMTPServerName, UINT nPort)
{
ASSERT(szSMTPServerName != NULL);
AfxSocketInit();
m_sSMTPServerHostName = szSMTPServerName;
m_nPort = nPort;
m_bConnected = FALSE;
m_sError = _T("OK");
response_buf = NULL;
}
CSmtp::~CSmtp()
{ Disconnect(); }
CString CSmtp::GetServerHostName()
{
return m_sSMTPServerHostName;
}
BOOL CSmtp::Connect()
{
CString sHello;
TCHAR local_host[80]; // Warning: arbitrary size
if(m_bConnected) return TRUE;
try {
// This will be deleted in Disconnect();
response_buf = new TCHAR[RESPONSE_BUFFER_SIZE];
// I can't count on all class users' applications
// to have exception-throwing operator-new implementations,
// so I'll soul-kiss the ones that don't.
if(response_buf == NULL) {
m_sError = _T("Not enough memory");
return FALSE;
}
} catch(CException* e) {
response_buf = NULL;
m_sError = _T("Not enough memory");
delete e;
return FALSE;
}
if(!m_wsSMTPServer.Create()) {
m_sError = _T("Unable to create the socket");
delete response_buf;
response_buf = NULL;
return FALSE;
}
if( !m_wsSMTPServer.Connect(GetServerHostName(), GetPort())) {
m_sError = _T("Unable to connect to server");
m_wsSMTPServer.Close();
delete response_buf;
response_buf = NULL;
return FALSE;
}
if(!get_response(CONNECT_SUCCESS)) {
m_sError = _T( "Server didn't respond" );
m_wsSMTPServer.Close();
delete response_buf;
response_buf = NULL;
return FALSE;
}
gethostname(local_host, 80);
sHello.Format(_T( "HELO %s\r\n" ), local_host);
m_wsSMTPServer.Send((LPCTSTR)sHello, sHello.GetLength());
if(!get_response(GENERIC_SUCCESS)) {
m_wsSMTPServer.Close();
delete response_buf;
response_buf = NULL;
return FALSE;
}
m_bConnected = TRUE;
return TRUE;
}
BOOL CSmtp::Disconnect()
{
BOOL ret;
if(!m_bConnected) return TRUE;
// Disconnect gracefully from the server and close the socket
CString sQuit = _T("QUIT\r\n");
m_wsSMTPServer.Send((LPCTSTR)sQuit, sQuit.GetLength());
// No need to check return value here.
// If it fails, the message is available with GetLastError
ret = get_response(QUIT_SUCCESS);
m_wsSMTPServer.Close();
if(response_buf != NULL) {
delete[] response_buf;
response_buf = NULL;
}
m_bConnected = FALSE;
return ret;
}
UINT CSmtp::GetPort()
{ return m_nPort; }
CString CSmtp::GetLastError()
{ return m_sError; }
BOOL CSmtp::SendMessage(CMailMessage* msg)
{
ASSERT(msg != NULL);
if(!m_bConnected) {
m_sError = _T("Must be connected");
return FALSE;
}
if(FormatMailMessage(msg) == FALSE) {
return FALSE;
}
if(transmit_message(msg) == FALSE) {
return FALSE;
}
return TRUE;
}
BOOL CSmtp::FormatMailMessage(CMailMessage* msg)
{
ASSERT(msg != NULL);
if(msg -> GetNumRecipients() == 0) {
m_sError = _T("No Recipients");
return FALSE;
}
msg -> FormatMessage();
return TRUE;
}
void CSmtp::SetServerProperties(LPCTSTR szSMTPServerName, UINT nPort)
{
ASSERT(szSMTPServerName != NULL);
// Needs to be safe in non-debug too
if(szSMTPServerName == NULL) return;
m_sSMTPServerHostName = szSMTPServerName;
m_nPort = nPort;
}
CString CSmtp::cook_body(CMailMessage* msg)
{
ASSERT(msg != NULL);
CString sTemp;
CString sCooked = _T("");
LPTSTR szBad = _T("\r\n.\r\n");
LPTSTR szGood = _T("\r\n..\r\n");
int nPos;
int nStart = 0;
int nBadLength = strlen(szBad);
sTemp = msg -> m_sBody;
if(sTemp.Left(3) == _T(".\r\n"))
sTemp = _T(".") + sTemp;
//
// This is a little inefficient because it beings a search
// at the beginning of the string each time. This was
// the only thing I could think of that handled ALL variations.
// In particular, the sequence "\r\n.\r\n.\r\n" is troublesome.
// (Even CStringEx's FindReplace wouldn't handle that situation
// with the global flag set.)
//
while((nPos = sTemp.Find(szBad)) > -1) {
sCooked = sTemp.Mid(nStart, nPos);
sCooked += szGood;
sTemp = sCooked + sTemp.Right(sTemp.GetLength() - (nPos + nBadLength));
}
return sTemp;
}
BOOL CSmtp::transmit_message(CMailMessage* msg)
{
CString sFrom;
CString sTo;
CString sTemp;
CString sEmail;
ASSERT(msg != NULL);
if(!m_bConnected) {
m_sError = _T("Must be connected");
return FALSE;
}
// Send the MAIL command
sFrom.Format(_T( "MAIL From: <%s>\r\n" ), (LPCTSTR)msg->m_sFrom);
m_wsSMTPServer.Send((LPCTSTR)sFrom, sFrom.GetLength());
if(!get_response(GENERIC_SUCCESS)) return FALSE;
// Send RCPT commands (one for each recipient)
for(int i = 0; i < msg->GetNumRecipients(); i++) {
msg->GetRecipient(sEmail, sTemp, i);
sTo.Format(_T("RCPT TO: <%s>\r\n"), (LPCTSTR)sEmail);
m_wsSMTPServer.Send((LPCTSTR)sTo, sTo.GetLength());
get_response(GENERIC_SUCCESS);
}
// Send the DATA command
sTemp = _T("DATA\r\n");
m_wsSMTPServer.Send((LPCTSTR)sTemp, sTemp.GetLength());
if( !get_response(DATA_SUCCESS)) {
return FALSE;
}
// Send the header
m_wsSMTPServer.Send((LPCTSTR)msg -> m_sHeader, msg -> m_sHeader.GetLength());
// Send the body
sTemp = cook_body(msg);
m_wsSMTPServer.Send((LPCTSTR)sTemp, sTemp.GetLength());
// Signal end of data
sTemp = _T("\r\n.\r\n");
m_wsSMTPServer.Send((LPCTSTR)sTemp, sTemp.GetLength());
if( !get_response(GENERIC_SUCCESS)) {
return FALSE;
}
return TRUE;
}
BOOL CSmtp::get_response(UINT response_expected)
{
ASSERT(response_expected >= GENERIC_SUCCESS);
ASSERT(response_expected < LAST_RESPONSE);
CString sResponse;
UINT response;
response_code* pResp; // Shorthand
if(m_wsSMTPServer.Receive(response_buf, RESPONSE_BUFFER_SIZE) == SOCKET_ERROR) {
m_sError = _T("Socket Error");
return FALSE;
}
sResponse = response_buf;
sscanf((LPCTSTR)sResponse.Left(3), _T("%d"), &response);
pResp = &response_table[response_expected];
if(response != pResp -> nResponse) {
m_sError.Format( _T("%d:%s"), response, (LPCTSTR)pResp -> sMessage);
return FALSE;
}
return TRUE;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -