www.pudn.com > pop3.3.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 
                           
                              
 
Copyright (c) 1998 by PJ Naughter.   
All rights reserved. 
 
*/ 
 
//////////////// Includes //////////////////////////////////////////// 
#include "stdafx.h" 
#include  
#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; 
  } 
} 
 
 
 
 
 
 
CPop3Socket::CPop3Socket() 
{ 
  m_hSocket = INVALID_SOCKET; //default to an invalid scoket descriptor 
} 
 
CPop3Socket::~CPop3Socket() 
{ 
  Close(); 
} 
 
BOOL CPop3Socket::Create() 
{ 
  m_hSocket = socket(AF_INET, SOCK_STREAM, 0); 
  return (m_hSocket != INVALID_SOCKET); 
} 
 
BOOL CPop3Socket::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); 
   
	//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); 
  char* pszAsciiHostAddress = T2A((LPTSTR) pszHostAddress); 
	sockAddr.sin_addr.s_addr = inet_addr(pszAsciiHostAddress); 
 
	//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(pszAsciiHostAddress); 
		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 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 = 20000; //default timeout of 20 seconds when debugging 
#else 
  m_dwTimeout = 2000;  //default timeout of 2 seconds for normal release code 
#endif 
} 
 
CPop3Connection::~CPop3Connection() 
{ 
  if (m_bConnected) 
    Disconnect(); 
} 
 
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\n")); 
    return FALSE; 
  } 
 
  //Connect to the POP3 Host 
  if (!m_Pop.Connect(pszHostName, nPort)) 
  { 
    TRACE(_T("Could not connect to the POP3 mailbox\n")); 
    return FALSE; 
  } 
  else 
  { 
    //We're now connected !! 
    m_bConnected = TRUE; 
 
    //check the response 
    if (!ReadCommandResponse()) 
    { 
      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)) 
    { 
      Disconnect(); 
      return FALSE; 
    } 
    if (!ReadCommandResponse()) 
    { 
      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)) 
    { 
      Disconnect(); 
      return FALSE; 
    } 
    if (!ReadCommandResponse()) 
    { 
      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 in call to send QUIT command\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("Already disconnected\n")); 
  
  //free up our socket 
  m_Pop.Close(); 
  
  return bSuccess; 
} 
 
BOOL CPop3Connection::Delete(int nMsg) 
{ 
  //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) 
    List(); 
 
  //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 in call to send DELE command\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 in call to send STAT command\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(); 
 
  //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(); 
 
  //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 in call to send LIST command\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 in call to send UIDL command\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 in call to send RSET command\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 in call to send NOOP command\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 in call to send RETR command\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 in call to send TOP command\n")); 
      return FALSE; 
    } 
 
    // And check the command 
    return ReadReturnResponse(message, dwSize); 
  } 
  else 
      return FALSE; 
} 
 
BOOL CPop3Connection::ReadCommandResponse() 
{ 
	char sBuf[1000]; 
	return ReadResponse(sBuf, sizeof(sBuf), "\r\n"); 
} 
 
BOOL CPop3Connection::ReadReturnResponse(CPop3Message& message, DWORD dwSize) 
{ 
  //Must be connected to perform a "RETR" 
  ASSERT(m_bConnected); 
 
  //We need a flexible sized receiver buffer here 
  int nSize = dwSize + 100; 
  char* sBuf = new char[nSize]; 
	if (!ReadResponse(sBuf, nSize, "\r\n.\r\n")) 
	{ 
		TRACE(_T("Error retrieving the RETR response")); 
		return FALSE; 
	} 
 
  //determine if the response is an error 
  if (strnicmp(sBuf,"+OK", 3) != 0) 
	{ 
    SetLastError(WSAEPROTONOSUPPORT); 
		TRACE(_T("POP3 server did not respond correctly to the RETR response\n")); 
    delete []sBuf; 
		return FALSE; 
	} 
	else 
	{   
    //remove the first line which contains the +OK from the message 
    char* pszFirst = GetFirstCharInResponse(sBuf); 
		VERIFY(pszFirst); 
 
    //transfer the message contents to the message class 
    int nMessageSize = sBuf - pszFirst + strlen(sBuf); 
    message.m_pszMessage = new char[nMessageSize + 1]; 
    memcpy(message.m_pszMessage, pszFirst, nMessageSize); 
    message.m_pszMessage[nMessageSize] = '\0'; 
	} 
  delete []sBuf; 
  return TRUE;  
} 
 
BOOL CPop3Connection::ReadListResponse(int nNumberOfMails) 
{ 
  //Must be connected to perform a "LIST" 
  ASSERT(m_bConnected); 
 
  //We need a flexible sized receiver buffer here 
  int nSize = 14 * nNumberOfMails + 100; 
  char* sBuf = new char[nSize]; 
 
  //retrieve the reponse 
	if (!ReadResponse(sBuf, nSize, "\r\n.\r\n")) 
	{ 
		TRACE(_T("Error retrieving the LIST response\n")); 
		delete []sBuf; 
		return FALSE; 
	} 
 
  //determine if the response is an error 
  if (strnicmp(sBuf,"+OK", 3) != 0) 
	{ 
    SetLastError(WSAEPROTONOSUPPORT); 
		TRACE(_T("POP3 server did not respond correctly to the LIST response\n")); 
    delete[] sBuf; 
		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(sBuf); 
		VERIFY(pszSize); 
		for (; *pszSize != '.'; pszSize++) 
			if (*pszSize == '\t' || *pszSize == ' ') 
				m_msgSizes.Add(atoi(pszSize)); 
	} 
  delete[] sBuf; 
  return TRUE;  
} 
 
BOOL CPop3Connection::ReadUIDLResponse(int nNumberOfMails) 
{ 
  //Must be connected to perform a "LIST" 
  ASSERT(m_bConnected); 
 
  //We need a flexible sized receiver buffer here 
  int nSize = 1000 * nNumberOfMails + 100; 
  char* sBuf = new char[nSize]; 
 
  //retrieve the reponse 
	if (!ReadResponse(sBuf, nSize, "\r\n.\r\n")) 
	{ 
		TRACE(_T("Error retrieving the UIDL response\n")); 
		delete []sBuf; 
		return FALSE; 
	} 
 
  //determine if the response is an error 
  if (strnicmp(sBuf,"+OK", 3) != 0) 
	{ 
    SetLastError(WSAEPROTONOSUPPORT); 
		TRACE(_T("POP3 server did not respond correctly to the UIDL response\n")); 
    delete[] sBuf; 
		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(sBuf); 
		VERIFY(pszSize); 
		for (; *pszSize != '.'; pszSize++) 
    { 
      char* pszBegin = pszSize; 
      while (*pszSize != '\n' && *pszSize != '.') 
      { 
        ++pszSize; 
      } 
      if (*pszSize == '.') 
        continue; 
 
      char sMsg[15]; 
      char sID[1000]; 
      *pszSize = '\0'; 
      sscanf(pszBegin, "%s %s", sMsg, sID); 
 
      m_msgIDs.Add(CString(sID)); 
    } 
	} 
  delete[] sBuf; 
  return TRUE;  
} 
 
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::ReadStatResponse(int& nNumberOfMails, int& nTotalMailSize) 
{ 
  //Must be connected to perform a "STAT" 
  ASSERT(m_bConnected); 
 
  //retrieve the reponse 
	char sBuf[100]; 
	if (!ReadResponse(sBuf, sizeof(sBuf), "\r\n")) 
	{ 
		TRACE(_T("Error retrieving the STAT response")); 
		return FALSE; 
	} 
 
  //determine if the response is an error 
  if (strncmp(sBuf,"+OK", 3) != 0) 
	{ 
		TRACE(_T("POP3 server did not respond correctly to the STAT response\n")); 
		return FALSE; 
	} 
	else 
	{                                           
    //Parse out the Number of Mails and Total mail size values 
		BOOL bGetNumber = TRUE; 
		for (char* pszNum=sBuf; *pszNum!='\0'; pszNum++) 
		{ 
			if (*pszNum=='\t' || *pszNum==' ') 
			{						 
				if (bGetNumber) 
				{ 
					nNumberOfMails = atoi(pszNum); 
          m_nNumberOfMails = nNumberOfMails; 
					bGetNumber = FALSE; 
				} 
				else 
				{ 
					nTotalMailSize = atoi(pszNum); 
					return TRUE; 
				} 
			} 
		} 
	} 
  return FALSE;  
} 
 
BOOL CPop3Connection::ReadResponse(LPSTR pszBuffer, int nBuf, LPSTR pszTerminator) 
{ 
	//paramater validity checking 
	ASSERT(pszBuffer); 
	ASSERT(nBuf); 
 
  //must have been created first 
  ASSERT(m_bConnected); 
 
  //retrieve the reponse using until we 
	//get the terminator or a timeout occurs 
	BOOL bFoundTerminator = FALSE; 
	int nReceived = 0; 
	DWORD dwStartTicks = ::GetTickCount(); 
	while (!bFoundTerminator) 
	{ 
		//timeout has occured 
		if ((::GetTickCount() - dwStartTicks) >	m_dwTimeout) 
		{ 
		  pszBuffer[nReceived] = '\0'; 
			SetLastError(WSAETIMEDOUT); 
      m_sLastCommandResponse = pszBuffer; //Hive away the last command reponse 
			return FALSE; 
		} 
 
    //check the socket for readability 
    BOOL bReadible; 
    if (!m_Pop.IsReadible(bReadible)) 
    { 
	    pszBuffer[nReceived] = '\0'; 
			m_sLastCommandResponse = pszBuffer; //Hive away the last command reponse 
			return FALSE; 
    } 
    else if (!bReadible) //no data to receive, just loop around 
    { 
      Sleep(100); //Wait for 100 ms prior to looping around,  
                  //helps to improve performance of system 
      continue; 
    } 
 
		//receive the data from the socket 
	  int nData = m_Pop.Receive(pszBuffer+nReceived, nBuf-nReceived); 
		if (nData == SOCKET_ERROR) 
		{ 
		  pszBuffer[nReceived] = '\0'; 
      m_sLastCommandResponse = pszBuffer; //Hive away the last command reponse 
		  return FALSE;  
		} 
		else 
		{ 
      if (nData) 
			  dwStartTicks = ::GetTickCount(); //Reset the idle timeout 
		  nReceived += nData;							 //Increment the count of data received 
		} 
 
		pszBuffer[nReceived] = '\0';	//temporarily NULL terminate the string 
																	//so that strstr works 
		bFoundTerminator = (strstr(pszBuffer, pszTerminator) != NULL); 
	} 
 
	//Remove the terminator from the response data 
  pszBuffer[nReceived - strlen(pszTerminator)] = '\0'; 
 
  //determine if the response is an error 
	BOOL bSuccess = (strnicmp(pszBuffer,"+OK", 3) == 0); 
 
  if (!bSuccess) 
  { 
    SetLastError(WSAEPROTONOSUPPORT); 
    m_sLastCommandResponse = pszBuffer; //Hive away the last command reponse 
  } 
 
  return bSuccess; 
}