// 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;