📄 csmtp.cpp
字号:
// CSmtp.cpp: implementation of the CSmtp class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "smtp.h"
#include "CSmtp.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// CMailMessage
// Formats a message compliant with RFC 822.
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CMailMessage::CMailMessage()
{
m_sMailerName = IDS_APPNAME;
SetCharsPerLine(76);
}
CMailMessage::~CMailMessage()
{
}
BOOL CMailMessage::AddRecipient(LPCTSTR szEmailAddress, LPCTSTR szFriendlyName)
{
ASSERT(szEmailAddress != NULL);
ASSERT(szFriendlyName != NULL);
CRecipient to;
to.m_sEmailAddress = szEmailAddress;
to.m_sFriendlyName = szFriendlyName;
m_Recipients.Add(to);
return TRUE;
}
// sEmailAddress and sFriendlyName are OUTPUT parameters.
// If the function fails, it will return FALSE, and the OUTPUT
// parameters will not be touched.
BOOL CMailMessage::GetRecipient(CString & sEmailAddress, CString & sFriendlyName, int nIndex)
{
CRecipient to;
if(nIndex < 0 || nIndex > m_Recipients.GetUpperBound())
return FALSE;
to = m_Recipients[nIndex];
sEmailAddress = to.m_sEmailAddress;
sFriendlyName = to.m_sFriendlyName;
return TRUE;
}
int CMailMessage::GetNumRecipients()
{ return m_Recipients.GetSize(); }
BOOL CMailMessage::AddMultipleRecipients(LPCTSTR szRecipients)
{
TCHAR* buf;
UINT pos;
UINT start;
CString sTemp;
CString sEmail;
CString sFriendly;
UINT length;
int nMark;
int nMark2;
ASSERT(szRecipients != NULL);
// Add Recipients
length = strlen(szRecipients);
buf = new TCHAR[length + 1]; // Allocate a work area (don't touch parameter itself)
strcpy(buf, szRecipients);
for(pos = 0, start = 0; pos <= length; pos++) {
if(buf[pos] == ';' || buf[pos] == 0) {
// First, pick apart the sub-strings (separated by ';')
// Store it in sTemp.
buf[pos] = 0; // Redundant when at the end of string, but who cares.
sTemp = &buf[start];
// Now divide the substring into friendly names and e-mail addresses.
nMark = sTemp.Find('<');
if(nMark >= 0) {
sFriendly = sTemp.Left(nMark);
nMark2 = sTemp.Find('>');
if(nMark2 < nMark) {
delete[] buf;
return FALSE;
}
// End of mark at closing bracket or end of string
nMark2 > -1 ? nMark2 = nMark2 : nMark2 = sTemp.GetLength() - 1;
sEmail = sTemp.Mid(nMark + 1, nMark2 - (nMark + 1));
} else {
sEmail = sTemp;
sFriendly = _T("");
}
AddRecipient(sEmail, sFriendly);
start = pos + 1;
}
}
delete[] buf;
return TRUE;
}
void CMailMessage::FormatMessage()
{
start_header();
prepare_header();
end_header();
prepare_body();
}
void CMailMessage::SetCharsPerLine(UINT nCharsPerLine)
{ m_nCharsPerLine = nCharsPerLine; }
UINT CMailMessage::GetCharsPerLine()
{
return m_nCharsPerLine;
}
// Create header as per RFC 822
//
void CMailMessage::prepare_header()
{
CString sTemp;
sTemp = _T("");
// From:
sTemp = _T("From: ") + m_sFrom;
add_header_line((LPCTSTR) sTemp);
// To:
sTemp = _T("To: ");
CString sEmail = _T("");
CString sFriendly = _T("");
for(int i = 0; i < GetNumRecipients(); i++) {
GetRecipient(sEmail, sFriendly, i);
sTemp += (i > 0 ? _T(",") : _T(""));
sTemp += sFriendly;
sTemp += _T("<");
sTemp += sEmail;
sTemp += _T(">");
}
add_header_line((LPCTSTR) sTemp);
// Date:
m_tDateTime = m_tDateTime.GetCurrentTime();
// Format: Mon, 01 Jun 98 01:10:30 GMT
sTemp = _T("Date: ");
sTemp += m_tDateTime.Format("%a, %d %b %y %H:%M:%S %Z");
add_header_line((LPCTSTR) sTemp);
// Subject:
sTemp = _T("Subject: ") + m_sSubject;
add_header_line((LPCTSTR) sTemp);
// X-Mailer
sTemp = _T("X-Mailer: ") + m_sMailerName;
add_header_line((LPCTSTR) sTemp);
}
void CMailMessage::prepare_body()
{
// Append a CR/LF to body if necessary.
if(m_sBody.Right(2) != _T("\r\n"))
m_sBody += _T("\r\n");
}
void CMailMessage::start_header()
{ m_sHeader = _T(""); }
void CMailMessage::end_header()
{ m_sHeader += _T("\r\n"); }
void CMailMessage::add_header_line(LPCTSTR szHeaderLine)
{
CString sTemp;
sTemp.Format(_T("%s\r\n"), szHeaderLine);
m_sHeader += sTemp;
}
//////////////////////////////////////////////////////////////////////
// CMIMEContentAgent
//////////////////////////////////////////////////////////////////////
CMIMEContentAgent::CMIMEContentAgent(int nMIMEType)
{ m_nMIMETypeIHandle = nMIMEType; }
CMIMEContentAgent::~CMIMEContentAgent()
{}
BOOL CMIMEContentAgent::QueryType(int nContentType)
{ return nContentType == m_nMIMETypeIHandle ? TRUE : FALSE; }
//////////////////////////////////////////////////////////////////////
// CMIMECode
//////////////////////////////////////////////////////////////////////
CMIMECode::CMIMECode()
{
}
CMIMECode::~CMIMECode()
{
}
//////////////////////////////////////////////////////////////////////
// CBase64
//////////////////////////////////////////////////////////////////////
// Static Member Initializers
//
// The 7-bit alphabet used to encode binary information
CString CBase64::m_sBase64Alphabet =
_T( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" );
int CBase64::m_nMask[] = { 0, 1, 3, 7, 15, 31, 63, 127, 255 };
CBase64::CBase64()
{
}
CBase64::~CBase64()
{
}
CString CBase64::Encode(LPCTSTR szEncoding, int nSize)
{
CString sOutput = _T( "" );
int nNumBits = 6;
UINT nDigit;
int lp = 0;
ASSERT( szEncoding != NULL );
if( szEncoding == NULL )
return sOutput;
m_szInput = szEncoding;
m_nInputSize = nSize;
m_nBitsRemaining = 0;
nDigit = read_bits( nNumBits, &nNumBits, lp );
while( nNumBits > 0 )
{
sOutput += m_sBase64Alphabet[ (int)nDigit ];
nDigit = read_bits( nNumBits, &nNumBits, lp );
}
// Pad with '=' as per RFC 1521
while( sOutput.GetLength() % 4 != 0 )
{
sOutput += '=';
}
return sOutput;
}
// The size of the output buffer must not be less than
// 3/4 the size of the input buffer. For simplicity,
// make them the same size.
int CBase64::Decode(LPCTSTR szDecoding, LPTSTR szOutput)
{
CString sInput;
int c, lp =0;
int nDigit;
int nDecode[ 256 ];
ASSERT( szDecoding != NULL );
ASSERT( szOutput != NULL );
if( szOutput == NULL )
return 0;
if( szDecoding == NULL )
return 0;
sInput = szDecoding;
if( sInput.GetLength() == 0 )
return 0;
// Build Decode Table
//
for( int i = 0; i < 256; i++ )
nDecode[i] = -2; // Illegal digit
for( i=0; i < 64; i++ )
{
nDecode[ m_sBase64Alphabet[ i ] ] = i;
nDecode[ m_sBase64Alphabet[ i ] | 0x80 ] = i; // Ignore 8th bit
nDecode[ '=' ] = -1;
nDecode[ '=' | 0x80 ] = -1; // Ignore MIME padding char
}
// Clear the output buffer
memset( szOutput, 0, sInput.GetLength() + 1 );
// Decode the Input
//
for( lp = 0, i = 0; lp < sInput.GetLength(); lp++ )
{
c = sInput[ lp ];
nDigit = nDecode[ c & 0x7F ];
if( nDigit < -1 )
{
return 0;
}
else if( nDigit >= 0 )
// i (index into output) is incremented by write_bits()
write_bits( nDigit & 0x3F, 6, szOutput, i );
}
return i;
}
UINT CBase64::read_bits(int nNumBits, int * pBitsRead, int& lp)
{
ULONG lScratch;
while( ( m_nBitsRemaining < nNumBits ) &&
( lp < m_nInputSize ) )
{
int c = m_szInput[ lp++ ];
m_lBitStorage <<= 8;
m_lBitStorage |= (c & 0xff);
m_nBitsRemaining += 8;
}
if( m_nBitsRemaining < nNumBits )
{
lScratch = m_lBitStorage << ( nNumBits - m_nBitsRemaining );
*pBitsRead = m_nBitsRemaining;
m_nBitsRemaining = 0;
}
else
{
lScratch = m_lBitStorage >> ( m_nBitsRemaining - nNumBits );
*pBitsRead = nNumBits;
m_nBitsRemaining -= nNumBits;
}
return (UINT)lScratch & m_nMask[nNumBits];
}
void CBase64::write_bits(UINT nBits,
int nNumBits,
LPTSTR szOutput,
int& i)
{
UINT nScratch;
m_lBitStorage = (m_lBitStorage << nNumBits) | nBits;
m_nBitsRemaining += nNumBits;
while( m_nBitsRemaining > 7 )
{
nScratch = m_lBitStorage >> (m_nBitsRemaining - 8);
szOutput[ i++ ] = nScratch & 0xFF;
m_nBitsRemaining -= 8;
}
}
//////////////////////////////////////////////////////////////////////
// CAppOctetStream
//////////////////////////////////////////////////////////////////////
// IMPORTANT: The number of bytes we read must be
// a multiple of 3 because CBase64's Encode()
// method will append padding characters ('=')
// to make the output's size a multiple of 4.
// (Base64 treats 3 8-bit bytes as 4 6-bit 'bytes').
// MIME decoders are free to treat '=' as a signal
// that there's no more data, so we don't want to pad
// until we're supposed to.
// When at the end of the file, the # of bytes read
// may not be a multiple of 3, but that's okay
// because we DO want the padding chars then.
#define BYTES_TO_READ 54 // This number guarantess output won't
// won't exceed line-length limit
CAppOctetStream::CAppOctetStream(int nContentType)
:CMIMEContentAgent(nContentType)
{
}
CAppOctetStream::~CAppOctetStream()
{
}
BOOL CAppOctetStream::AppendPart(LPCTSTR szContent,
LPCTSTR szParameters,
int nEncoding,
BOOL bPath,
CString & sDestination)
{
CStdioFile fAttachment;
ASSERT(szContent != NULL);
// This class handles only file attachments, so
// it ignores the bPath parameter.
if(szContent == NULL)
return FALSE;
if(!fAttachment.Open(szContent, (CFile::modeRead|CFile::shareDenyWrite|CFile::typeBinary)))
return FALSE;
sDestination += build_sub_header(szContent,
szParameters,
nEncoding,
TRUE);
attach_file(&fAttachment, CMIMEMessage::BASE64, sDestination );
fAttachment.Close();
return TRUE;
}
CString CAppOctetStream::build_sub_header(LPCTSTR szContent,
LPCTSTR szParameters,
int nEncoding,
BOOL bPath)
{
CString sSubHeader;
CString sTemp;
TCHAR szFName[ _MAX_FNAME ];
TCHAR szExt[ _MAX_EXT ];
_tsplitpath( szContent, NULL, NULL, szFName, szExt );
// This class ignores szParameters and nEncoding.
// It controls its own parameters and only handles
// Base64 encoding.
if( bPath )
sTemp.Format( "; file=%s%s", szFName, szExt );
else
sTemp = _T( "" );
sSubHeader.Format( _T( "Content-Type: %s%s\r\n" ),
(LPCTSTR)GetContentTypeString(),
(LPCTSTR)sTemp );
sSubHeader += _T( "Content-Transfer-Encoding: base64\r\n" );
sTemp.Format( _T( "Content-Disposition: attachment; filename=%s%s\r\n" ),
szFName, szExt );
sSubHeader += sTemp;
// Signal end of sub-header.
sSubHeader += _T( "\r\n" ); // Warning: numerous concatenations
// are inefficient.
return sSubHeader;
}
CString CAppOctetStream::GetContentTypeString()
{
CString s;
s = _T( "application/octet-stream" );
return s;
}
// Caller is responsible for opening and closing the file
void CAppOctetStream::attach_file(CStdioFile* pFileAtt,
int nEncoding,
CString & sDestination)
{
CMIMECode* pEncoder;
int nBytesRead;
TCHAR szBuffer[ BYTES_TO_READ + 1 ];
ASSERT( pFileAtt != NULL );
if( pFileAtt == NULL )
return;
switch( nEncoding )
{
// This class handles only Base64 encoding, but others
// may be added here.
default:
// Fall through to...
case CMIMEMessage::BASE64:
try
{
pEncoder = new CBase64;
}
catch( CMemoryException* e )
{
delete e;
return;
}
}
if( pEncoder == NULL ) // Old habits are hard to break
return;
do
{
try
{
nBytesRead = pFileAtt->Read( szBuffer, BYTES_TO_READ );
}
catch( CFileException* e )
{
delete e;
break;
}
szBuffer[ nBytesRead ] = 0; // Terminate the string
sDestination += pEncoder->Encode( szBuffer, nBytesRead );
sDestination += _T( "\r\n" );
} while( nBytesRead == BYTES_TO_READ );
sDestination += _T( "\r\n" );
delete pEncoder;
}
//////////////////////////////////////////////////////////////////////
// CTextPlain
//////////////////////////////////////////////////////////////////////
CTextPlain::CTextPlain( int nContentType, UINT nWrapPos )
:CMIMEContentAgent( nContentType )
{
m_nWrapPos = nWrapPos;
}
CTextPlain::~CTextPlain()
{
}
CString CTextPlain::GetContentTypeString()
{
CString s;
s = _T( "text/plain" );
return s;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -