📄 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
PJN / 11-09-1998 1) VC 5 project file is now provided
2) Attachment array which the message class contains now uses
references instead of pointers.
3) Now uses Sleep(0) to yield our time slice instead of Sleep(100),
this is the preferred way of writting polling style code in Win32
without serverly impacting performance.
4) All Trace statements now display the value as returned from
GetLastError
5) A number of extra asserts have been added
6) A AddMultipleRecipients function has been added which supports added a
number of recipients at one time from a single string
7) Extra trace statements have been added to help in debugging
PJN / 12-09-98 1) Removed a couple of unreferenced variable compiler warnings when code
was compiled with Visual C++ 6.0
2) Fixed a major bug which was causing an ASSERT when the CSMTPAttachment
destructor was being called in the InitInstance of the sample app.
This was inadvertingly introduced for the 1.2 release. The fix is to revert
fix 2) as done on 11-09-1998. This will also help to reduce the number of
attachment images kept in memory at one time.
PJN / 18-01-99 1) Full CC & BCC support has been added to the classes
PJN / 22-02-99 1) Addition of a Get and SetTitle function which allows a files attachment
title to be different that the original filename
2) AddMultipleRecipients now ignores addresses if they are empty.
3) Improved the reading of responses back from the server by implementing
a growable receive buffer
4) timeout is now 60 seconds when building for debug
PJN / 25-03-99 1) Now sleeps for 250 ms instead of yielding the time slice. This helps
reduce CPU usage when waiting for data to arrive in the socket
PJN / 14-05-99 1) Fixed a bug with the way the code generates time zone fields in the Date headers.
PJN / 10-09-99 1) Improved CSMTPMessage::GetHeader to include mime field even when no attachments
are included.
Copyright (c) 1998 - 1999 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()
{
}
CSMTPAddress::CSMTPAddress(const CString& sAddress) :
m_sEmailAddress(sAddress)
{
ASSERT(m_sEmailAddress.GetLength()); //An empty address is not allowed
}
CSMTPAddress::CSMTPAddress(const CString& sFriendly, const CString& sAddress) :
m_sFriendlyName(sFriendly), m_sEmailAddress(sAddress)
{
ASSERT(m_sEmailAddress.GetLength()); //An empty address is not allowed
}
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.GetLength()); //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))
{
TRACE(_T("Failed to get the status for file %s, probably does not exist\n"), sFilename);
return FALSE;
}
//open up the file for reading in
CFile infile;
if (!infile.Open(sFilename, CFile::modeRead | CFile::shareDenyWrite))
{
TRACE(_T("Failed to open file to be attached\n"));
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;
m_sTitle = 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:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -