📄 smtp.cpp
字号:
/*
Module : SMTP.CPP
Purpose: Implementation for a MFC class encapsulation of the SMTP protocol
Created: PJN / 22-05-1998
History: PJN / 15-06-1998 1) Fixed the case where a single dot occurs on its own
in the body of a message
2) Class now supports Reply-To Header Field
3) Class now supports file attachments
PJN / 18-06-1998 1) Fixed a memory overwrite problem which was occurring
with the buffer used for encoding base64 attachments
PJN / 27-06-1998 1) The case where a line begins with a "." but contains
other text is now also catered for. See RFC821, Section 4.5.2
for further details.
2) m_sBody in CSMTPMessage has now been made protected.
Client applications now should call AddBody instead. This
ensures that FixSingleDot is only called once even if the
same message is sent a number of times.
3) Fixed a number of problems with how the MIME boundaries
were defined and sent.
4) Got rid of an unreferenced formal parameter
compiler warning when doing a release build
Copyright (c) 1998 by PJ Naughter.
All rights reserved.
*/
//////////////// Includes ////////////////////////////////////////////
#include "stdafx.h"
#include "smtp.h"
//////////////// Macros / Locals /////////////////////////////////////
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
char CSMTPAttachment::m_base64tab[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz0123456789+/";
#define BASE64_MAXLINE 76
#define EOL "\r\n"
//////////////// Implementation //////////////////////////////////////
CSMTPSocket::CSMTPSocket()
{
m_hSocket = INVALID_SOCKET; //default to an invalid scoket descriptor
}
CSMTPSocket::~CSMTPSocket()
{
Close();
}
BOOL CSMTPSocket::Create()
{
m_hSocket = socket(AF_INET, SOCK_STREAM, 0);
return (m_hSocket != INVALID_SOCKET);
}
BOOL CSMTPSocket::Connect(LPCTSTR pszHostAddress, int nPort)
{
//For correct operation of the T2A macro, see MFC Tech Note 59
USES_CONVERSION;
//must have been created first
ASSERT(m_hSocket != INVALID_SOCKET);
LPSTR lpszAscii = T2A((LPTSTR)pszHostAddress);
//Determine if the address is in dotted notation
SOCKADDR_IN sockAddr;
ZeroMemory(&sockAddr, sizeof(sockAddr));
sockAddr.sin_family = AF_INET;
sockAddr.sin_port = htons((u_short)nPort);
sockAddr.sin_addr.s_addr = inet_addr(lpszAscii);
//If the address is not dotted notation, then do a DNS
//lookup of it.
if (sockAddr.sin_addr.s_addr == INADDR_NONE)
{
LPHOSTENT lphost;
lphost = gethostbyname(lpszAscii);
if (lphost != NULL)
sockAddr.sin_addr.s_addr = ((LPIN_ADDR)lphost->h_addr)->s_addr;
else
{
WSASetLastError(WSAEINVAL);
return FALSE;
}
}
//Call the protected version which takes an address
//in the form of a standard C style struct.
return Connect((SOCKADDR*)&sockAddr, sizeof(sockAddr));
}
BOOL CSMTPSocket::Connect(const SOCKADDR* lpSockAddr, int nSockAddrLen)
{
return (connect(m_hSocket, lpSockAddr, nSockAddrLen) != SOCKET_ERROR);
}
BOOL CSMTPSocket::Send(LPCSTR pszBuf, int nBuf)
{
//must have been created first
ASSERT(m_hSocket != INVALID_SOCKET);
return (send(m_hSocket, pszBuf, nBuf, 0) != SOCKET_ERROR);
}
int CSMTPSocket::Receive(LPSTR pszBuf, int nBuf)
{
//must have been created first
ASSERT(m_hSocket != INVALID_SOCKET);
return recv(m_hSocket, pszBuf, nBuf, 0);
}
void CSMTPSocket::Close()
{
if (m_hSocket != INVALID_SOCKET)
{
VERIFY(SOCKET_ERROR != closesocket(m_hSocket));
m_hSocket = INVALID_SOCKET;
}
}
BOOL CSMTPSocket::IsReadible(BOOL& bReadible)
{
timeval timeout = {0, 0};
fd_set fds;
FD_ZERO(&fds);
FD_SET(m_hSocket, &fds);
int nStatus = select(0, &fds, NULL, NULL, &timeout);
if (nStatus == SOCKET_ERROR)
{
return FALSE;
}
else
{
bReadible = !(nStatus == 0);
return TRUE;
}
}
CSMTPAddress::CSMTPAddress(const CString& sAddress) : m_sEmailAddress(sAddress)
{
}
CSMTPAddress::CSMTPAddress(const CString& sFriendly, const CString& sAddress) :
m_sFriendlyName(sFriendly), m_sEmailAddress(sAddress)
{
}
CSMTPAddress& CSMTPAddress::operator=(const CSMTPAddress& r)
{
m_sFriendlyName = r.m_sFriendlyName;
m_sEmailAddress = r.m_sEmailAddress;
return *this;
}
CString CSMTPAddress::GetRegularFormat() const
{
ASSERT(!m_sEmailAddress.IsEmpty()); //Email Address must be valid
CString sAddress;
if (m_sFriendlyName.IsEmpty())
sAddress = m_sEmailAddress; //Just transfer the address across directly
else
sAddress.Format(_T("%s <%s>"), m_sFriendlyName, m_sEmailAddress);
return sAddress;
}
CSMTPAttachment::CSMTPAttachment()
{
m_pszEncoded = NULL;
m_nEncodedSize = 0;
}
CSMTPAttachment::~CSMTPAttachment()
{
//free up any memory we allocated
if (m_pszEncoded)
{
delete [] m_pszEncoded;
m_pszEncoded = NULL;
}
}
BOOL CSMTPAttachment::Attach(const CString& sFilename)
{
ASSERT(sFilename.GetLength()); //Empty Filename !
//free up any memory we previously allocated
if (m_pszEncoded)
{
delete [] m_pszEncoded;
m_pszEncoded = NULL;
}
//determine the file size
CFileStatus fs;
if (!CFile::GetStatus(sFilename, fs))
return FALSE;
//open up the file for reading in
CFile infile;
if (!infile.Open(sFilename, CFile::modeRead | CFile::shareDenyWrite))
return FALSE;
//read in the contents of the input file
char* pszIn = new char[fs.m_size];
infile.Read(pszIn, fs.m_size);
//allocate the encoded buffer
int nOutSize = Base64BufferSize(fs.m_size);
m_pszEncoded = new char[nOutSize];
//Do the encoding
EncodeBase64(pszIn, fs.m_size, m_pszEncoded, nOutSize, &m_nEncodedSize);
//delete the input buffer
delete [] pszIn;
//Close the input file
infile.Close();
//Hive away the filename
TCHAR sPath[_MAX_PATH];
TCHAR sFname[_MAX_FNAME];
TCHAR sExt[_MAX_EXT];
_tsplitpath(sFilename, NULL, NULL, sFname, sExt);
_tmakepath(sPath, NULL, NULL, sFname, sExt);
m_sFilename = sPath;
return TRUE;
}
int CSMTPAttachment::Base64BufferSize(int nInputSize)
{
int nOutSize = (nInputSize+2)/3*4; // 3:4 conversion ratio
nOutSize += strlen(EOL)*nOutSize/BASE64_MAXLINE + 3; // Space for newlines and NUL
return nOutSize;
}
BOOL CSMTPAttachment::EncodeBase64(const char* pszIn, int nInLen, char* pszOut, int nOutSize, int* nOutLen)
{
//Input Parameter validation
ASSERT(pszIn);
ASSERT(pszOut);
ASSERT(nOutSize);
ASSERT(nOutSize >= Base64BufferSize(nInLen));
#ifndef _DEBUG
//justs get rid of "unreferenced formal parameter"
//compiler warning when doing a release build
nOutSize;
#endif
//Set up the parameters prior to the main encoding loop
int nInPos = 0;
int nOutPos = 0;
int nLineLen = 0;
// Get three characters at a time from the input buffer and encode them
for (int i=0; i<nInLen/3; ++i)
{
//Get the next 2 characters
int c1 = pszIn[nInPos++] & 0xFF;
int c2 = pszIn[nInPos++] & 0xFF;
int c3 = pszIn[nInPos++] & 0xFF;
//Encode into the 4 6 bit characters
pszOut[nOutPos++] = m_base64tab[(c1 & 0xFC) >> 2];
pszOut[nOutPos++] = m_base64tab[((c1 & 0x03) << 4) | ((c2 & 0xF0) >> 4)];
pszOut[nOutPos++] = m_base64tab[((c2 & 0x0F) << 2) | ((c3 & 0xC0) >> 6)];
pszOut[nOutPos++] = m_base64tab[c3 & 0x3F];
nLineLen += 4;
//Handle the case where we have gone over the max line boundary
if (nLineLen >= BASE64_MAXLINE-3)
{
char* cp = EOL;
pszOut[nOutPos++] = *cp++;
if (*cp)
pszOut[nOutPos++] = *cp;
nLineLen = 0;
}
}
// Encode the remaining one or two characters in the input buffer
char* cp;
switch (nInLen % 3)
{
case 0:
{
cp = EOL;
pszOut[nOutPos++] = *cp++;
if (*cp)
pszOut[nOutPos++] = *cp;
break;
}
case 1:
{
int c1 = pszIn[nInPos] & 0xFF;
pszOut[nOutPos++] = m_base64tab[(c1 & 0xFC) >> 2];
pszOut[nOutPos++] = m_base64tab[((c1 & 0x03) << 4)];
pszOut[nOutPos++] = '=';
pszOut[nOutPos++] = '=';
cp = EOL;
pszOut[nOutPos++] = *cp++;
if (*cp)
pszOut[nOutPos++] = *cp;
break;
}
case 2:
{
int c1 = pszIn[nInPos++] & 0xFF;
int c2 = pszIn[nInPos] & 0xFF;
pszOut[nOutPos++] = m_base64tab[(c1 & 0xFC) >> 2];
pszOut[nOutPos++] = m_base64tab[((c1 & 0x03) << 4) | ((c2 & 0xF0) >> 4)];
pszOut[nOutPos++] = m_base64tab[((c2 & 0x0F) << 2)];
pszOut[nOutPos++] = '=';
cp = EOL;
pszOut[nOutPos++] = *cp++;
if (*cp)
pszOut[nOutPos++] = *cp;
break;
}
default:
{
ASSERT(FALSE);
break;
}
}
pszOut[nOutPos] = 0;
*nOutLen = nOutPos;
return TRUE;
}
CSMTPMessage::CSMTPMessage(LPCSTR sXMailer, BOOL bWithDate, BOOL bWithXMailer)
{
m_sXMailer = sXMailer;
m_bWithDate = bWithDate;
m_bWithXMailer = bWithXMailer;
}
CSMTPMessage::~CSMTPMessage()
{
}
int CSMTPMessage::AddRecipient(CSMTPAddress& recipient)
{
return m_Recipients.Add(recipient);
}
void CSMTPMessage::RemoveRecipient(int nIndex)
{
m_Recipients.RemoveAt(nIndex);
}
CSMTPAddress CSMTPMessage::GetRecipient(int nIndex) const
{
return m_Recipients.GetAt(nIndex);
}
int CSMTPMessage::GetNumberOfRecipients() const
{
return m_Recipients.GetSize();
}
int CSMTPMessage::AddAttachment(CSMTPAttachment* pAttachment)
{
ASSERT(pAttachment->GetFilename().GetLength()); //an empty filename !
return m_Attachments.Add(pAttachment);
}
void CSMTPMessage::RemoveAttachment(int nIndex)
{
m_Attachments.RemoveAt(nIndex);
}
CSMTPAttachment* CSMTPMessage::GetAttachment(int nIndex) const
{
return m_Attachments.GetAt(nIndex);
}
int CSMTPMessage::GetNumberOfAttachments() const
{
return m_Attachments.GetSize();
}
CString CSMTPMessage::GetHeader() const
{
//Create the "Date:" part of the header
CTime now(CTime::GetCurrentTime());
CString sDate(now.Format(_T("%a, %d %b %Y %H:%M:%S %Z")));
//Create the "To:" part of the header
CString sTo;
for (int i=0; i<GetNumberOfRecipients(); i++)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -