www.pudn.com > popmail.zip > Pop3.cpp


/* 
Module : POP3.CPP 
Purpose: Implementation for a MFC class encapsulation of the POP3 protocol 
Created: PJN / 04-05-1998 
History: PJN / 27-06-1998	 
			1) Fixed a potential buffer overflow problem in Delete 
			and Retrieve functions when a large message number was 
			specified. 
			2) Improve the ReadResponse code by a) passing the  
			readability check onto the scoket class and b) Sleeping 
			for 100 ms prior to looping around waiting for new  
			response data 
			3) Classes are now fully Unicode compliant. Unicode 
			build configurations are also now included. 
			4) Now supports the TOP POP3 command which can be 
			issued using the GetHeader function. 
		PJN / 04-01-1999  
			1) Properly UNICODE enabled the code 
		PJN / 22-02-1999  
			1) Improved the reading of responses back from the server by implementing 
			a growable receive buffer 
			2) timeout is now 60 seconds when building for debug 
			3) Code now yields its time slice while waiting for a timeout 
			4) Made most functions virtual to help end user usage 
		PJN / 25-03-1999	 
			1) Fixed memory leak in the CPop3Connection::ReadReturnResponse function. 
			2) 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 / 15-06-1999  
			1) Added functions to return the message body, header or a particular 
			header field of a message 
			2) Tidied up some of the TRACE messages which the code generates 
		PJN / 16-06-1999  
			1) Fixed a bug in the GetHeaderItem function which was causing a header 
			item which appeared at the begining of the header fields to fail to be  
			parsed incorrectly. 
		PJN / 27-06-1999  
			1) Fixed a bug in the GetHeaderItem function when a header spanned  
			multiple lines as is allowed by RFC 822 
		PJN / 29-06-1999  
			1) Another improvement to GetHeaderItem. When will it end . Originally  
			this was reported as a bug but upon further investigation it turns out that 
			the message which was causing the problems had embedded tabs in the header.  
			This is discouraged by the RFC which refers to mail headers (RFC 822).  
			The code has been enhanced to handle this case. Thanks to Chris Bishop  
			for spotting this. 
			2) Fix for a bug in GetHeaderItem where I accidently was using "=" instead of 
			"==". Thanks to Angelini Fabio for spotting this. 
		PJN / 05-07-1999  
			1) Addition of the following functions: 
				i)   CPop3Message::GetReplyTo 
				ii)  CPop3Message::GetRawBody 	  
				iii) CPop3Message::GetSubject 				 
				iv)  CPop3Message::GetFrom					 
				v)   CPop3Message::GetDate					 
			2) GetHeaderItem function now uses case insensitive searching 
			3) GetHeaderItem now allows you to search for the "n'th" header of a specified type 
		PJN / 24-08-1999  
			1) Fixed a bug whereby the function GetHeader was sometimes failing when it 
			was called when the message was retrieved using the "TOP" command. 
		PJN / 27-03-2000  
			1) Fixed a problem where GetBody and GetRawBody will fail if you call it for a  
			CPop3Message object that doesn't have a message in it (i.e m_pszMessage == NULL).  
			This was previously causing a goof old access violation in GetRawBody. 
		PJN / 20-09-2000  
			1) Fixed a bug in CPop3Connection::UIDL when message id's were returned 
			with embedded dots in them. Thanks to Konstantin Vasserman for finding and resolving 
			this problem. 
		PJN / 25-03-2001  
			1) Updated copyright information 
		PJN / 27-07-2001  
			1) Added two methods namely: GetTo() and GetCC() to the CPop3Message class 
		PJN / 11-08-2001  
			1) Fixed a bug in CPop3Connection::ReadResponse where determining the terminator 
			was failing when an embedded NULL was in the message. 
		PJN / 27-09-2001  
			1) Fixed a bug in CPop3Connection::ReadResponse when handling disconnection errors 
			2) Improved the error handling in Delete, GetMessageSize & GetMessageID methods 
		PJN / 29-09-2001  
			1) Further improved the error handling in CPop3Connection::ReadResponse 
Copyright (c) 1998 - 2001 by PJ Naughter.  (Web: www.naughter.com, Email: pjna@naughter.com) 
All rights reserved. 
Copyright / Usage Details: 
You are allowed to include the source code in any product (commercial, shareware, freeware or otherwise)  
when your product is released in binary form. You are allowed to modify the source code in any way you want  
except you cannot modify the copyright details at the top of each module. If you want to distribute source  
code with your application, then you are only allowed to distribute versions released by the author. This is  
to maintain a single distribution point for the source code.  
*/ 
 
//	AV / 15-01-2002 
//	CPop3Message::GetBoundary() added 
//  CPop3Message::GetContentType() added 
//  CPop3Connection::~CPop3Connection() changed 
 
//////////////// Includes //////////////////////////////////////////// 
#include "stdafx.h" 
#include "pop3.h" 
 
 
 
//////////////// Macros ////////////////////////////////////////////// 
#ifdef _DEBUG 
#define new DEBUG_NEW 
#undef THIS_FILE 
static char THIS_FILE[] = __FILE__; 
#endif 
 
 
//////////////// Implementation ////////////////////////////////////// 
CPop3Message::CPop3Message() 
{ 
	m_pszMessage = NULL; 
} 
 
CPop3Message::~CPop3Message() 
{ 
	if (m_pszMessage) 
	{ 
		delete [] m_pszMessage; 
		m_pszMessage = NULL; 
	} 
} 
 
CString CPop3Message::GetHeaderItem(const CString& sName, int nItem) const 
{ 
	//Value which will be returned by this function 
	CString sField; 
	 
	//Get the message header (add an extra "\r\n" at the 
	//begining to aid in the parsing)   
	CString sHeader(_T("\r\n")); 
	sHeader += GetHeader(); 
	CString sUpCaseHeader(sHeader); 
	sUpCaseHeader.MakeUpper(); 
	 
	CString sUpCaseName(sName); 
	sUpCaseName.MakeUpper(); 
	 
	//Find the specified line in the header 
	CString sFind(CString(_T("\r\n")) + sUpCaseName + _T(":")); 
	int nFindLength = sFind.GetLength(); 
	int nFindStart = sUpCaseHeader.Find(sFind); 
	int nFind = nFindStart; 
	for (int i=0; ih_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 CPop3Socket::Connect(const SOCKADDR* lpSockAddr, int nSockAddrLen) 
{ 
	return (connect(m_hSocket, lpSockAddr, nSockAddrLen) != SOCKET_ERROR); 
} 
 
BOOL CPop3Socket::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 CPop3Socket::Receive(LPSTR pszBuf, int nBuf) 
{ 
	//must have been created first 
	ASSERT(m_hSocket != INVALID_SOCKET); 
	 
	return recv(m_hSocket, pszBuf, nBuf, 0);  
} 
 
void CPop3Socket::Close() 
{ 
	if (m_hSocket != INVALID_SOCKET) 
	{ 
		VERIFY(SOCKET_ERROR != closesocket(m_hSocket)); 
		m_hSocket = INVALID_SOCKET; 
	} 
} 
 
BOOL CPop3Socket::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; 
	} 
} 
 
 
 
 
 
CPop3Connection::CPop3Connection() 
{ 
	m_nNumberOfMails = 0; 
	m_bListRetrieved = FALSE; 
	m_bStatRetrieved = FALSE; 
	m_bUIDLRetrieved = FALSE; 
	m_msgSizes.RemoveAll(); 
	m_bConnected = FALSE; 
#ifdef _DEBUG 
	m_dwTimeout = 60000; //default timeout of 60 seconds when debugging 
#else 
	m_dwTimeout = 2000;  //default timeout of 2 seconds for normal release code 
#endif 
} 
 
CPop3Connection::~CPop3Connection() 
{ 
	//if (m_bConnected) 
	//	Disconnect(); 
	//free up our socket 
	m_Pop.Close(); 
 
} 
 
BOOL CPop3Connection::Connect(LPCTSTR pszHostName, LPCTSTR pszUser, LPCTSTR pszPassword, int nPort) 
{ 
	//For correct operation of the T2A macro, see MFC Tech Note 59 
	USES_CONVERSION; 
 
	//Create the socket 
	if (!m_Pop.Create()) 
	{ 
		TRACE(_T("Failed to create client socket, GetLastError:%d\n"), GetLastError()); 
		return FALSE; 
	} 
 
	//Connect to the POP3 Host 
	if (!m_Pop.Connect(pszHostName, nPort)) 
	{ 
		TRACE(_T("Could not connect to the POP3 mailbox, GetLastError:%d\n"), GetLastError()); 
		return FALSE; 
	} 
	else 
	{ 
		//We're now connected !! 
		m_bConnected = TRUE; 
		 
		//check the response 
		if (!ReadCommandResponse()) 
		{ 
			TRACE(_T("Failed while connected to read a command response from the POP3 server\n")); 
			Disconnect(); 
			return FALSE; 
		} 
		 
		//Send the POP3 username and check the response 
		char sBuf[128]; 
		char* pszAsciiUser = T2A((LPTSTR) pszUser); 
		ASSERT(strlen(pszAsciiUser) < 100);  
		sprintf(sBuf, "USER %s\r\n", pszAsciiUser); 
		int nCmdLength = strlen(sBuf); 
		if (!m_Pop.Send(sBuf, nCmdLength)) 
		{ 
			TRACE(_T("Failed to send the USER command to the POP3 server\n")); 
			Disconnect(); 
			return FALSE; 
		} 
		if (!ReadCommandResponse()) 
		{ 
			TRACE(_T("Failed while connected to read a USER command response from the POP3 server\n")); 
			Disconnect(); 
			return FALSE; 
		}  
		 
		//Send the POP3 password and check the response 
		char* pszAsciiPassword = T2A((LPTSTR) pszPassword); 
		ASSERT(strlen(pszAsciiPassword) < 100); 
		sprintf(sBuf, "PASS %s\r\n", pszAsciiPassword); 
		nCmdLength = strlen(sBuf); 
		if (!m_Pop.Send(sBuf, nCmdLength)) 
		{ 
			TRACE(_T("Failed to send the PASS command to the POP3 server\n")); 
			Disconnect(); 
			return FALSE; 
		} 
		if (!ReadCommandResponse()) 
		{ 
			TRACE(_T("Failed while connected to read a PASS command response from the POP3 server\n")); 
			Disconnect(); 
			return FALSE; 
		} 
		 
		return TRUE; 
	} 
} 
 
BOOL CPop3Connection::Disconnect() 
{		    
	BOOL bSuccess = FALSE;		 
	 
	//disconnect from the POP3 server if connected  
	if (m_bConnected) 
	{ 
		char sBuf[10]; 
		strcpy(sBuf, "QUIT\r\n"); 
		int nCmdLength = strlen(sBuf); 
		if (!m_Pop.Send(sBuf, nCmdLength)) 
			TRACE(_T("Failed to send the QUIT command to the POP3 server\n")); 
		 
		//Check the reponse 
		bSuccess = ReadCommandResponse(); 
		 
		//Reset all the state variables 
		m_bConnected = FALSE; 
		m_bListRetrieved = FALSE; 
		m_bStatRetrieved = FALSE; 
		m_bUIDLRetrieved = FALSE; 
	} 
	else 
		TRACE(_T("CPop3Connection, Already disconnected\n")); 
	 
	//free up our socket 
	m_Pop.Close(); 
	 
	return bSuccess; 
} 
 
BOOL CPop3Connection::Delete(int nMsg) 
{ 
	BOOL bSuccess = TRUE; 
	 
	//Must be connected to perform a delete 
	ASSERT(m_bConnected); 
	 
	//if we haven't executed the LIST command then do it now 
	if (!m_bListRetrieved) 
		bSuccess = List(); 
	 
	//Handle the error if necessary   
	if (!bSuccess) 
		return FALSE; 
	 
	//Send the DELE command along with the message ID 
	char sBuf[20]; 
	sprintf(sBuf, "DELE %d\r\n", nMsg); 
	int nCmdLength = strlen(sBuf); 
	if (!m_Pop.Send(sBuf, nCmdLength)) 
	{ 
		TRACE(_T("Failed to send the DELE command to the POP3 server\n")); 
		return FALSE; 
	} 
	 
	return ReadCommandResponse(); 
} 
 
BOOL CPop3Connection::Statistics(int& nNumberOfMails, int& nTotalMailSize) 
{ 
	//Must be connected to perform a "STAT" 
	ASSERT(m_bConnected); 
	 
	//Send the STAT command 
	char sBuf[10]; 
	strcpy(sBuf, "STAT\r\n"); 
	int nCmdLength = strlen(sBuf); 
	if (!m_Pop.Send(sBuf, nCmdLength)) 
	{ 
		TRACE(_T("Failed to send the STAT command to the POP3 server\n")); 
		return FALSE; 
	} 
	 
	return ReadStatResponse(nNumberOfMails, nTotalMailSize); 
} 
 
BOOL CPop3Connection::GetMessageSize(int nMsg, DWORD& dwSize) 
{ 
	BOOL bSuccess = TRUE; 
	 
	//if we haven't executed the LIST command then do it now 
	if (!m_bListRetrieved) 
		bSuccess = List(); 
	 
	//Handle the error if necessary   
	if (!bSuccess) 
		return FALSE; 
	 
	//nMsg must be in the correct range 
	ASSERT((nMsg > 0) && (nMsg <= m_msgSizes.GetSize())); 
	 
	//retrieve the size from the message size array 
	dwSize = m_msgSizes.GetAt(nMsg - 1); 
	 
	return bSuccess; 
} 
 
BOOL CPop3Connection::GetMessageID(int nMsg, CString& sID) 
{ 
	BOOL bSuccess = TRUE; 
	 
	//if we haven't executed the UIDL command then do it now 
	if (!m_bUIDLRetrieved) 
		bSuccess = UIDL(); 
	 
	//Handle the error if necessary   
	if (!bSuccess) 
		return FALSE; 
	 
	//nMsg must be in the correct range 
	ASSERT((nMsg > 0) && (nMsg <= m_msgIDs.GetSize())); 
	 
	//retrieve the size from the message size array 
	sID = m_msgIDs.GetAt(nMsg - 1); 
	 
	return bSuccess; 
} 
 
BOOL CPop3Connection::List() 
{ 
	//Must be connected to perform a "LIST" 
	ASSERT(m_bConnected); 
	 
	//if we haven't executed the STAT command then do it now 
	int nNumberOfMails = m_nNumberOfMails; 
	int nTotalMailSize; 
	if (!m_bStatRetrieved) 
	{ 
		if (!Statistics(nNumberOfMails, nTotalMailSize)) 
			return FALSE; 
		else 
			m_bStatRetrieved = TRUE; 
	} 
	 
	//Send the LIST command 
	char sBuf[10]; 
	strcpy(sBuf, "LIST\r\n"); 
	int nCmdLength = strlen(sBuf); 
	if (!m_Pop.Send(sBuf, nCmdLength)) 
	{ 
		TRACE(_T("Failed to send the LIST command to the POP3 server\n")); 
		return FALSE; 
	} 
	//And check the response 
	m_bListRetrieved = ReadListResponse(nNumberOfMails); 
	return m_bListRetrieved; 
} 
 
BOOL CPop3Connection::UIDL() 
{ 
	//Must be connected to perform a "UIDL" 
	ASSERT(m_bConnected); 
	 
	//if we haven't executed the STAT command then do it now 
	int nNumberOfMails = m_nNumberOfMails; 
	int nTotalMailSize; 
	if (!m_bStatRetrieved) 
	{ 
		if (!Statistics(nNumberOfMails, nTotalMailSize)) 
			return FALSE; 
		else 
			m_bStatRetrieved = TRUE; 
	} 
	 
	//Send the UIDL command 
	char sBuf[10]; 
	strcpy(sBuf, "UIDL\r\n"); 
	int nCmdLength = strlen(sBuf); 
	if (!m_Pop.Send(sBuf, nCmdLength)) 
	{ 
		TRACE(_T("Failed to send the UIDL command to the POP3 server\n")); 
		return FALSE; 
	} 
	//And check the response 
	m_bUIDLRetrieved = ReadUIDLResponse(nNumberOfMails); 
	return m_bUIDLRetrieved; 
} 
 
BOOL CPop3Connection::Reset() 
{ 
	//Must be connected to perform a "RSET" 
	ASSERT(m_bConnected); 
	 
	//Send the RSET command 
	char sBuf[10]; 
	strcpy(sBuf, "RSET\r\n"); 
	int nCmdLength = strlen(sBuf); 
	if (!m_Pop.Send(sBuf, nCmdLength)) 
	{ 
		TRACE(_T("Failed to send the RSET command to the POP3 server\n")); 
		return FALSE; 
	} 
	 
	//And check the command 
	return ReadCommandResponse(); 
} 
 
BOOL CPop3Connection::Noop() 
{ 
	//Must be connected to perform a "NOOP" 
	ASSERT(m_bConnected); 
	 
	//Send the NOOP command 
	char sBuf[10]; 
	strcpy(sBuf, "NOOP\r\n"); 
	int nCmdLength = strlen(sBuf); 
	if (!m_Pop.Send(sBuf, nCmdLength)) 
	{ 
		TRACE(_T("Failed to send the NOOP command to the POP3 server\n")); 
		return FALSE; 
	} 
	 
	//And check the response 
	return ReadCommandResponse(); 
} 
 
BOOL CPop3Connection::Retrieve(int nMsg, CPop3Message& message) 
{ 
	//Must be connected to retrieve a message 
	ASSERT(m_bConnected); 
	 
	//work out the size of the message to retrieve 
	DWORD dwSize; 
	if (GetMessageSize(nMsg, dwSize)) 
	{ 
		//Send the RETR command 
		char sBuf[20]; 
		sprintf(sBuf, "RETR %d\r\n", nMsg);  
		int nCmdLength = strlen(sBuf); 
		if (!m_Pop.Send(sBuf, nCmdLength)) 
		{ 
			TRACE(_T("Failed to send the RETR command to the POP3 server\n")); 
			return FALSE; 
		} 
		 
		//And check the command 
		return ReadReturnResponse(message, dwSize); 
	} 
	else 
		return FALSE; 
} 
 
BOOL CPop3Connection::GetMessageHeader(int nMsg, CPop3Message& message) 
{ 
	// Must be connected to retrieve a message 
	ASSERT(m_bConnected); 
	 
	// make sure the message actually exists 
	DWORD dwSize; 
	if (GetMessageSize(nMsg, dwSize)) 
	{ 
		// Send the TOP command 
		char sBuf[16]; 
		sprintf(sBuf, "TOP %d 0\r\n", nMsg); 
		int nCmdLength = strlen(sBuf); 
		if (!m_Pop.Send(sBuf, nCmdLength)) 
		{ 
			TRACE(_T("Failed to send the TOP command to the POP3 server\n")); 
			return FALSE; 
		} 
		 
		// And check the command 
		return ReadReturnResponse(message, dwSize); 
	} 
	else 
		return FALSE; 
} 
 
BOOL CPop3Connection::ReadCommandResponse() 
{ 
	LPSTR pszOverFlowBuffer = NULL; 
	char sBuf[1000]; 
	BOOL bSuccess = ReadResponse(sBuf, 1000, "\r\n", &pszOverFlowBuffer); 
	if (pszOverFlowBuffer) 
		delete [] pszOverFlowBuffer; 
	 
	return bSuccess; 
} 
 
LPSTR CPop3Connection::GetFirstCharInResponse(LPSTR pszData) const 
{ 
	while ((*pszData != '\n') && *pszData) 
		++pszData; 
	 
	//skip over the "\n" onto the next line 
	if (*pszData) 
		++pszData; 
	 
	return pszData; 
} 
 
BOOL CPop3Connection::ReadResponse(LPSTR pszBuffer, int nInitialBufSize, LPSTR pszTerminator, LPSTR* ppszOverFlowBuffer, int nGrowBy) 
{ 
	ASSERT(ppszOverFlowBuffer); 		 //Must have a valid string pointer 
	ASSERT(*ppszOverFlowBuffer == NULL); //Initially it must point to a NULL string 
	 
	//must have been created first 
	ASSERT(m_bConnected); 
	 
	int nTerminatorLen = strlen(pszTerminator); 
	 
	//The local variables which will receive the data 
	LPSTR pszRecvBuffer = pszBuffer; 
	int nBufSize = nInitialBufSize; 
	 
	//retrieve the reponse using until we 
	//get the terminator or a timeout occurs 
	BOOL bFoundTerminator = FALSE; 
	int nReceived = 0; 
	DWORD dwStartTicks = ::GetTickCount(); 
	while (!bFoundTerminator) 
	{ 
		//Has the timeout occured 
		if ((::GetTickCount() - dwStartTicks) > m_dwTimeout) 
		{ 
			pszRecvBuffer[nReceived] = '\0'; 
			SetLastError(WSAETIMEDOUT); 
			m_sLastCommandResponse = pszRecvBuffer; //Hive away the last command reponse 
			return FALSE; 
		} 
		 
		//check the socket for readability 
		BOOL bReadible; 
		if (!m_Pop.IsReadible(bReadible)) 
		{ 
			pszRecvBuffer[nReceived] = '\0'; 
			m_sLastCommandResponse = pszRecvBuffer; //Hive away the last command reponse 
			return FALSE; 
		} 
		else if (!bReadible) //no data to receive, just loop around 
		{ 
			Sleep(250); //Sleep for a while before we loop around again 
			continue; 
		} 
		 
		//receive the data from the socket 
		int nBufRemaining = nBufSize-nReceived-1; //Allows allow one space for the NULL terminator 
		if (nBufRemaining<0) 
			nBufRemaining = 0; 
		int nData = m_Pop.Receive(pszRecvBuffer+nReceived, nBufRemaining); 
		 
		//Reset the idle timeout if data was received 
		if (nData > 0) 
		{ 
			dwStartTicks = ::GetTickCount(); 
			 
			//Increment the count of data received 
			nReceived += nData; 						    
		} 
		 
		//If an error occurred receiving the data 
		if (nData < 1) 
		{ 
			//NULL terminate the data received 
			if (pszRecvBuffer) 
				pszBuffer[nReceived] = '\0'; 
			 
			m_sLastCommandResponse = pszRecvBuffer; //Hive away the last command reponse 
			return FALSE;  
		} 
		else 
		{ 
			//NULL terminate the data received 
			if (pszRecvBuffer) 
				pszRecvBuffer[nReceived] = '\0'; 
			 
			if (nBufRemaining-nData == 0) //No space left in the current buffer 
			{ 
				//Allocate the new receive buffer 
				nBufSize += nGrowBy; //Grow the buffer by the specified amount 
				LPSTR pszNewBuf = new char[nBufSize]; 
				 
				//copy the old contents over to the new buffer and assign  
				//the new buffer to the local variable used for retreiving  
				//from the socket 
				if (pszRecvBuffer) 
					strcpy(pszNewBuf, pszRecvBuffer); 
				pszRecvBuffer = pszNewBuf; 
				 
				//delete the old buffer if it was allocated 
				if (*ppszOverFlowBuffer) 
					delete [] *ppszOverFlowBuffer; 
				 
				//Remember the overflow buffer for the next time around 
				*ppszOverFlowBuffer = pszNewBuf;		 
			} 
		} 
		 
		//Check to see if the terminator character(s) have been found 
		bFoundTerminator = (strncmp(&pszRecvBuffer[nReceived - nTerminatorLen], pszTerminator, nTerminatorLen) == 0); 
	} 
	 
	//Remove the terminator from the response data 
	pszRecvBuffer[nReceived - nTerminatorLen] = '\0'; 
	 
	//determine if the response is an error 
	BOOL bSuccess = (strnicmp(pszRecvBuffer,"+OK", 3) == 0); 
	 
	if (!bSuccess) 
	{ 
		SetLastError(WSAEPROTONOSUPPORT); 
		m_sLastCommandResponse = pszRecvBuffer; //Hive away the last command reponse 
	} 
	 
	return bSuccess; 
} 
 
BOOL CPop3Connection::ReadReturnResponse(CPop3Message& message, DWORD dwSize) 
{ 
	//Must be connected to perform a "RETR" 
	ASSERT(m_bConnected); 
	 
	//Retrieve the message body 
	LPSTR pszOverFlowBuffer = NULL; 
	int nSize = dwSize + 100; 
	char* sBuf = new char[nSize]; 
	char* sMessageBuf = sBuf; 
	if (!ReadResponse(sBuf, nSize, "\r\n.\r\n", &pszOverFlowBuffer, 32000)) 
	{ 
		delete [] sBuf; 
		if (pszOverFlowBuffer) 
			delete [] pszOverFlowBuffer; 
		 
		TRACE(_T("Error retrieving the RETR response")); 
		return FALSE; 
	} 
	if (pszOverFlowBuffer) 
		sMessageBuf = pszOverFlowBuffer; 
	 
	//determine if the response is an error 
	if (strnicmp(sMessageBuf,"+OK", 3) != 0) 
	{ 
		delete [] sBuf; 
		if (pszOverFlowBuffer) 
			delete [] pszOverFlowBuffer; 
		 
		SetLastError(WSAEPROTONOSUPPORT); 
		TRACE(_T("POP3 server did not respond correctly to the RETR response\n")); 
		return FALSE; 
	} 
	else 
	{   
		//remove the first line which contains the +OK from the message 
		char* pszFirst = GetFirstCharInResponse(sMessageBuf); 
		VERIFY(pszFirst); 
		 
		//transfer the message contents to the message class 
		int nMessageSize = sMessageBuf - pszFirst + strlen(sMessageBuf); 
		 
		// Do we already have memory allocated? If so, destroy it! 
		if (message.m_pszMessage) 
		{ 
			delete [] message.m_pszMessage; 
			message.m_pszMessage = NULL; 
		} 
		 
		message.m_pszMessage = new char[nMessageSize + 1]; 
		memcpy(message.m_pszMessage, pszFirst, nMessageSize); 
		message.m_pszMessage[nMessageSize] = '\0'; 
	} 
	delete [] sBuf; 
	if (pszOverFlowBuffer) 
		delete [] pszOverFlowBuffer; 
	 
	return TRUE;  
} 
 
BOOL CPop3Connection::ReadListResponse(int nNumberOfMails) 
{ 
	//Must be connected to perform a "LIST" 
	ASSERT(m_bConnected); 
	 
	//retrieve the reponse 
	LPSTR pszOverFlowBuffer = NULL; 
	int nSize = 14 * nNumberOfMails + 100; 
	char* sBuf = new char[nSize]; 
	char* sMessageBuf = sBuf; 
	if (!ReadResponse(sBuf, nSize, "\r\n.\r\n", &pszOverFlowBuffer)) 
	{ 
		delete [] sBuf; 
		if (pszOverFlowBuffer) 
			delete [] pszOverFlowBuffer; 
		 
		TRACE(_T("Error retrieving the LIST response from the POP3 server")); 
		return FALSE; 
	} 
	if (pszOverFlowBuffer) 
		sMessageBuf = pszOverFlowBuffer; 
	 
	//determine if the response is an error 
	if (strnicmp(sMessageBuf,"+OK", 3) != 0) 
	{ 
		delete [] sBuf; 
		if (pszOverFlowBuffer) 
			delete [] pszOverFlowBuffer; 
		 
		SetLastError(WSAEPROTONOSUPPORT); 
		TRACE(_T("POP3 server did not respond correctly to the LIST response\n")); 
		return FALSE; 
	} 
	else 
	{   
		//Retrieve the message sizes and put them 
		//into the m_msgSizes array 
		m_msgSizes.RemoveAll(); 
		m_msgSizes.SetSize(0, nNumberOfMails); 
		 
		//then parse the LIST response 
		char* pszSize = GetFirstCharInResponse(sMessageBuf); 
		VERIFY(pszSize); 
		for (; *pszSize != '.'; pszSize++) 
			if (*pszSize == '\t' || *pszSize == ' ') 
				m_msgSizes.Add(atoi(pszSize)); 
	} 
	delete [] sBuf; 
	if (pszOverFlowBuffer) 
		delete [] pszOverFlowBuffer; 
	 
	return TRUE;  
} 
 
BOOL CPop3Connection::ReadUIDLResponse(int nNumberOfMails) 
{ 
	//Must be connected to perform a "UIDL" 
	ASSERT(m_bConnected); 
	 
	//retrieve the reponse 
	LPSTR pszOverFlowBuffer = NULL; 
	int nSize = 14 * nNumberOfMails + 100; 
	char* sBuf = new char[nSize]; 
	char* sMessageBuf = sBuf; 
	if (!ReadResponse(sBuf, nSize, "\r\n.\r\n", &pszOverFlowBuffer)) 
	{ 
		delete [] sBuf; 
		if (pszOverFlowBuffer) 
			delete [] pszOverFlowBuffer; 
		 
		TRACE(_T("Error retrieving the UIDL response from the POP3 server")); 
		return FALSE; 
	} 
	if (pszOverFlowBuffer) 
		sMessageBuf = pszOverFlowBuffer; 
	 
	//determine if the response is an error 
	if (strnicmp(sMessageBuf,"+OK", 3) != 0) 
	{ 
		delete [] sBuf; 
		if (pszOverFlowBuffer) 
			delete [] pszOverFlowBuffer; 
		 
		SetLastError(WSAEPROTONOSUPPORT); 
		TRACE(_T("POP3 server did not respond correctly to the UIDL response\n")); 
		return FALSE; 
	} 
	else 
	{   
		//Retrieve the message ID's and put them 
		//into the m_msgIDs array 
		m_msgIDs.RemoveAll(); 
		m_msgIDs.SetSize(0, nNumberOfMails); 
		 
		//then parse the UIDL response 
		char* pszSize = GetFirstCharInResponse(sMessageBuf); 
		 
		VERIFY(pszSize); 
		for (int iCount=0; iCount