Windows2003-3790/inetsrv/pop3/service/pop3svc/pop3context.cpp
2020-09-30 16:53:55 +02:00

1368 lines
50 KiB
C++

/************************************************************************************************
Copyright (c) 2001 Microsoft Corporation
File Name: Pop3Context.cpp
Abstract: Implementation of the POP3_CONTEXT Class
Notes:
History: 08/01/2001 Created by Hao Yu (haoyu)
************************************************************************************************/
#include <stdafx.h>
#include <ThdPool.hxx>
#include <SockPool.hxx>
#include <POP3Context.h>
#ifdef ROCKALL3
void * __cdecl
operator new(size_t cb)
{
void *const pv = g_RockallHeap.New(cb);
return pv;
}
void __cdecl
operator delete(void *pv)
{
g_RockallHeap.Delete(pv);
}
#endif
POP3_CONTEXT::POP3_CONTEXT()
{
Reset();
}
POP3_CONTEXT::~POP3_CONTEXT()
{
}
void POP3_CONTEXT::Reset()
{
m_dwCurrentState=INIT_STATE;
m_bFileTransmitPending=FALSE;
m_bCommandComplete=TRUE;
m_dwCommandSize=0;
m_wszUserName[0]=0;
m_szPassword[0]=0;
m_szDomainName[0]=0;
m_szCommandBuffer[0]=0;
m_cPswdSize=0;
m_dwAuthStatus=0;
m_dwFailedAuthCount=0;
m_AuthServer.Cleanup();
}
void POP3_CONTEXT::TimeOut(IO_CONTEXT *pIoContext)
{
TerminateConnection(pIoContext);
}
void POP3_CONTEXT::ProcessRequest(IO_CONTEXT *pIoContext,OVERLAPPED *pOverlapped,DWORD dwBytesRcvd)
{
POP3_CMD CurrentCmd=CMD_UNKNOWN;
char szGreetingBuffer[MAX_PATH*2];
LONG lTotalMsgSize, lMsgCnt;
char *pEndOfCmd=NULL;
if( ( NULL == pIoContext) ||
( NULL == pIoContext->m_hAsyncIO) )
{
//This is a rare shutdown case.
//IO completion received after socket is shut down.
if(NULL!=pIoContext)
{
//Signal that the IO Context should be deleted/reused
pIoContext->m_ConType = DELETE_PENDING;
}
return;
}
ASSERT( LOCKED_TO_PROCESS_POP3_CMD == pIoContext->m_lLock);
m_pIoContext=pIoContext;
// BLaPorte - I moved transmit completion handling here to avoid confusion.
// Check if this should be signal of TransmitFile completion
if(m_bFileTransmitPending)
{
m_bFileTransmitPending=FALSE;
//Here we calculate the perf on message size downloaded
g_PerfCounters.AddPerfCntr(e_gcBytesTransmitted, dwBytesRcvd);
g_PerfCounters.AddPerfCntr(e_gcBytesTransmitRate, dwBytesRcvd);
WaitForCommand();
return;
}
if(INIT_STATE == m_dwCurrentState)
{
if(SERVICE_RUNNING!=g_dwServerStatus)
{
//Reject the connection
SendResponse(RESP_SERVER_NOT_AVAILABLE);
TerminateConnection(pIoContext);
return;
}
if( 0 > _snwprintf(m_wszGreeting,
sizeof(m_wszGreeting)/sizeof(WCHAR),
L"<%u@%s>",
GetTickCount(),
g_wszComputerName))
{
//Make sure length of <TimeStamp@Machine> is less than MAX_PATH
m_wszGreeting[sizeof(m_wszGreeting)/sizeof(WCHAR)-1]=0;
m_wszGreeting[sizeof(m_wszGreeting)/sizeof(WCHAR)-2]=L'>';
}
if(L'\0'!=g_wszGreeting[0])
{
_snprintf(szGreetingBuffer,
sizeof(szGreetingBuffer)/sizeof(char),
RESP_SERVER_READY,
g_wszGreeting,
m_wszGreeting);
}
else
{
_snprintf(szGreetingBuffer,
sizeof(szGreetingBuffer)/sizeof(char),
RESP_SERVER_READY,
RESP_SERVER_GREETING,
m_wszGreeting);
}
//Make sure the NULL is there
szGreetingBuffer[sizeof(szGreetingBuffer)/sizeof(char)-1]=0;
m_dwCurrentState=AUTH_STATE;
g_PerfCounters.IncPerfCntr(e_gcAuthStateCnt);
SendResponse(szGreetingBuffer);
if(0==dwBytesRcvd)
{
WaitForCommand();
return;
}
}
// BLaPorte - oversized/undersized data should be rejected immediately.
if(dwBytesRcvd >= POP3_REQUEST_BUF_SIZE ||
dwBytesRcvd == 0)
{
//Error this command is too big or is nil
//Consider this is an attack or
// termination of a connection unexpectedly.
TerminateConnection(pIoContext);
return;
}
if( g_SocketPool.IsMaxSocketUsed() ) //Possible DoS Attack situation
{
DWORD dwTime=GetTickCount();
if(AUTH_STATE==m_dwCurrentState)
{
//The connection is not authenticated for twice the shorted timeout
if(dwTime>m_pIoContext->m_dwConnectionTime+SHORTENED_TIMEOUT) //The connection is not authenticated
{
TerminateConnection(pIoContext);
return; //The connection will be terminated
}
}
}
// BLaPorte - moved the counter increment here since we do it in either case.
g_PerfCounters.AddPerfCntr(e_gcBytesReceived, dwBytesRcvd);
g_PerfCounters.AddPerfCntr(e_gcBytesReceiveRate, dwBytesRcvd);
if(m_bCommandComplete)
{
m_dwCommandSize=dwBytesRcvd;
memcpy(m_szCommandBuffer, m_pIoContext->m_Buffer,dwBytesRcvd);
}
else
{
if(m_dwCommandSize+dwBytesRcvd >= POP3_REQUEST_BUF_SIZE)
{
//Error this command is too big!
//Consider this is an attack.
TerminateConnection(pIoContext);
return;
}
memcpy(m_szCommandBuffer+m_dwCommandSize, m_pIoContext->m_Buffer,dwBytesRcvd);
m_dwCommandSize+=dwBytesRcvd;
}
m_szCommandBuffer[m_dwCommandSize]='\0';
pEndOfCmd=strstr(m_szCommandBuffer,"\r\n");
if(NULL == pEndOfCmd)
{
m_bCommandComplete=FALSE;
WaitForCommand();
return;
}
else
{
m_bCommandComplete=TRUE;
*pEndOfCmd='\0'; //Cut the \r\n
m_dwCommandSize-=2;
}
if(m_dwAuthStatus!=1)
{
CurrentCmd=ParseCommand();
if(CMD_UNKNOWN == CurrentCmd)
{
// Count the bad commands?
SendResponse(RESP_UNKNOWN_COMMAND);
if(!g_SocketPool.IsMaxSocketUsed() )
{
WaitForCommand();
}
else
{
TerminateConnection(pIoContext);
}
return;
}
}
else
{
CurrentCmd=CMD_AUTH;
}
if(AUTH_STATE == m_dwCurrentState)
{
if(!ProcessAuthStateCommands(CurrentCmd,m_dwCommandSize))
{
TerminateConnection(pIoContext);
}
}
else // TRANS_STATE == m_dwCurrentState
{
if(dwBytesRcvd == 0)
{
//Connection terminated
TerminateConnection(pIoContext);
}
if(!ProcessTransStateCommands(CurrentCmd, m_dwCommandSize))
{
TerminateConnection(pIoContext);
}
}
}
void POP3_CONTEXT::WaitForCommand()
{
int iRet;
DWORD cbRevd=0;
DWORD Flags=0;
ASSERT( NULL != m_pIoContext);
ASSERT( NULL != m_pIoContext->m_hAsyncIO);
if(NULL == m_pIoContext->m_hAsyncIO)
{
TerminateConnection(m_pIoContext);
return;
}
WSABUF wsaBuf={POP3_REQUEST_BUF_SIZE, m_pIoContext->m_Buffer};
iRet=WSARecv(m_pIoContext->m_hAsyncIO,
&wsaBuf,
1,
&cbRevd,
&Flags,
&(m_pIoContext->m_Overlapped),
NULL);
if(SOCKET_ERROR == iRet )
{
iRet=WSAGetLastError();
if(iRet != ERROR_IO_PENDING )
{
//Problem with this connection
//We terminate the connection
TerminateConnection(m_pIoContext);
}
}
}
void POP3_CONTEXT::TerminateConnection(PIO_CONTEXT pIoContext)
{
SOCKET hSocket;
if(NULL!=pIoContext)
{
if(pIoContext->m_ConType != DELETE_PENDING)
{
m_MailBox.QuitAndClose();
hSocket=(SOCKET)InterlockedExchange((LPLONG)( &(pIoContext->m_hAsyncIO)), NULL);
if(NULL != hSocket )
{
closesocket(hSocket);
g_SocketPool.DecrementTotalSocketCount();
switch (m_dwCurrentState)
{
case UPDATE_STATE:
break;
case TRANS_STATE:
g_PerfCounters.DecPerfCntr(e_gcTransStateCnt);
break;
case AUTH_STATE:
g_PerfCounters.DecPerfCntr(e_gcAuthStateCnt);
break;
}
g_PerfCounters.DecPerfCntr(e_gcConnectedSocketCnt);
}
if(!m_bFileTransmitPending)
{
pIoContext->m_ConType = DELETE_PENDING;
}
}
}
}
POP3_CMD POP3_CONTEXT::ParseCommand()
{
int i=0;
if(strlen(m_szCommandBuffer) < COMMAND_SIZE-1)
{
return CMD_UNKNOWN;
}
//Check if any invalid characters
for(i=0; i< m_dwCommandSize; i++)
{
if(!isprint(m_szCommandBuffer[i]))
{
return CMD_UNKNOWN;
}
}
for(i=0; i< CMD_UNKNOWN; i++)
{
if( 0 == _strnicmp(m_szCommandBuffer, cszCommands[i], ciCommandSize[i]) )
{
return (POP3_CMD)i;
}
}
return CMD_UNKNOWN;
}
BOOL POP3_CONTEXT::ProcessAuthStateCommands(POP3_CMD CurrentCmd,
DWORD dwBytesRcvd)
{
BOOL bRetVal=FALSE;
char szBuf[POP3_RESPONSE_BUF_SIZE];
char szUserName[POP3_MAX_ADDRESS_LENGTH];
BSTR bstrUserName=NULL;
szBuf[POP3_RESPONSE_BUF_SIZE-1]='\0';
// BLaPorte - wszPassword could potentially be used to concatenate 2 strings of length MAX_PATH + change.
WCHAR wszPassword[2*MAX_PATH+32];
HRESULT hr=E_FAIL;
VARIANT vPassword;
VariantInit(&vPassword);
switch (CurrentCmd)
{
case CMD_USER: if( (m_szCommandBuffer[COMMAND_SIZE]!=' ') &&
(m_szCommandBuffer[COMMAND_SIZE]!='\0') )
{
SendResponse(RESP_UNKNOWN_COMMAND);
}
else if(g_dwRequireSPA)
{
SendResponse(RESP_SPA_REQUIRED);
bRetVal=TRUE;
}
else if(m_wszUserName[0] != 0 )
{
SendResponse(RESP_CMD_NOT_SUPPORTED);
}
else
{
if(GetNextStringParameter(
&(m_szCommandBuffer[COMMAND_SIZE]),
szUserName,
sizeof(szUserName)/sizeof(char)))
{
AnsiToUnicode(szUserName, -1, m_wszUserName, sizeof(m_wszUserName)/sizeof(WCHAR));
SendResponse(RESP_OK);
bRetVal=TRUE;
}
else
{
SendResponse(RESP_CMD_NOT_VALID);
}
}
if(! g_SocketPool.IsMaxSocketUsed() )
{
bRetVal=TRUE;
}
break;
case CMD_PASS:
//
// BLaPorte - make sure the password is cleared from the receive buffer.
//
SecureZeroMemory(m_pIoContext->m_Buffer,sizeof(m_pIoContext->m_Buffer));
if(m_wszUserName[0] == 0)
{
SendResponse(RESP_CMD_NOT_SUPPORTED);
if(! g_SocketPool.IsMaxSocketUsed() )
{
bRetVal=TRUE;
}
break;
}
else //USER command alread issued
{
if( (m_dwCommandSize == COMMAND_SIZE ) ||
((m_dwCommandSize == COMMAND_SIZE +1) &&
(' '==m_szCommandBuffer[COMMAND_SIZE] )) )
{
//No password
m_cPswdSize=0;
}
else
{
m_cPswdSize=m_dwCommandSize-COMMAND_SIZE-1;
if( (m_cPswdSize >= sizeof(m_szPassword)/sizeof(char)) ||
(' '!=m_szCommandBuffer[COMMAND_SIZE]) )
{
SendResponse(RESP_CMD_NOT_VALID);
m_wszUserName[0] = 0;
//
// BLaPorte - Zero out command buffer so cleartext password isn't
// lying around in memory.
//
SecureZeroMemory(m_szCommandBuffer,m_dwCommandSize);
if(! g_SocketPool.IsMaxSocketUsed() )
{
bRetVal=TRUE;
}
break;
}
strncpy(m_szPassword, &(m_szCommandBuffer[COMMAND_SIZE+1]), sizeof(m_szPassword)/sizeof(char)-1);
m_szPassword[sizeof(m_szPassword)/sizeof(char)-1]=0;
SecureZeroMemory(m_szCommandBuffer,m_dwCommandSize);
}
}
//Do Authentication here
bstrUserName=SysAllocString(m_wszUserName);
AnsiToUnicode(m_szPassword, -1, wszPassword, sizeof(wszPassword)/sizeof(WCHAR));
//
// BLaPorte - clear the password.
//
SecureZeroMemory(m_szPassword,sizeof(m_szPassword));
vPassword.vt=VT_BSTR;
if(0==m_cPswdSize)
{
vPassword.bstrVal=NULL;
}
else
{
vPassword.bstrVal=SysAllocString(wszPassword);
SecureZeroMemory(wszPassword,sizeof(wszPassword));
}
if(NULL != bstrUserName)
{
if(S_OK == ( hr= g_pAuthMethod->Authenticate(bstrUserName, vPassword)))
{
bRetVal=TRUE;
}
else if(E_ACCESSDENIED == hr )
{
g_EventLogger.LogEvent(LOGTYPE_ERR_WARNING,
POP3SVR_MAILROOT_ACCESS_DENIED,
m_wszUserName,
1);
}
}
SysFreeString(bstrUserName);
if(NULL != vPassword.bstrVal)
{
SecureZeroMemory(vPassword.bstrVal,SysStringByteLen(vPassword.bstrVal));
SysFreeString(vPassword.bstrVal);
}
//Open the mailbox
if(bRetVal)
{
bRetVal=m_MailBox.OpenMailBox(m_wszUserName);
}
if (bRetVal)
{
bRetVal=m_MailBox.LockMailBox();
if(bRetVal)
{
bRetVal=m_MailBox.EnumerateMailBox(g_dwMaxMsgPerDnld);
if(!bRetVal)
{
m_MailBox.UnlockMailBox();
}
}
}
else
{
//Open mailbox failed
if(ERROR_ACCESS_DENIED==GetLastError())
{ //Log the event
g_EventLogger.LogEvent(LOGTYPE_ERR_WARNING,
POP3SVR_MAILROOT_ACCESS_DENIED,
m_wszUserName,
1);
}
}
if (!bRetVal)
{
g_PerfCounters.IncPerfCntr(e_gcFailedLogonCnt);
m_dwFailedAuthCount++;
if( MAX_FAILED_AUTH<=m_dwFailedAuthCount )
{
g_EventLogger.LogEvent(LOGTYPE_ERR_WARNING,
POP3SVR_MAX_LOGON_FAILURES,
m_wszUserName,
1);
}
else
{
if(! g_SocketPool.IsMaxSocketUsed() )
{
bRetVal=TRUE; //Don't disconnect
}
}
SendResponse(RESP_ACCOUNT_ERROR);
m_wszUserName[0] = 0;
}
else
{
m_dwCurrentState=TRANS_STATE;
g_PerfCounters.DecPerfCntr(e_gcAuthStateCnt);
g_PerfCounters.IncPerfCntr(e_gcTransStateCnt);
SendResponse(RESP_AUTH_DONE);
}
break;
case CMD_APOP: char *pPswd;
//
// BLaPorte - Clear the receive buffer.
//
SecureZeroMemory(m_pIoContext->m_Buffer,sizeof(m_pIoContext->m_Buffer));
pPswd=strchr( &(m_szCommandBuffer[COMMAND_SIZE+1]), ' ');
if(NULL == pPswd)
{
SecureZeroMemory(m_szCommandBuffer,m_dwCommandSize);
SendResponse(RESP_ACCOUNT_ERROR);
if(! g_SocketPool.IsMaxSocketUsed() )
{
bRetVal=TRUE;
}
break;
}
*pPswd='\0';
strncpy(szUserName, &(m_szCommandBuffer[COMMAND_SIZE+1]), sizeof(szUserName)/sizeof(char)-1);
szUserName[sizeof(szUserName)/sizeof(char)-1]=0;
pPswd++;
strncpy(m_szPassword, pPswd, sizeof(m_szPassword)/sizeof(char)-1);
m_szPassword[sizeof(m_szPassword)/sizeof(char)-1]=0;
SecureZeroMemory(m_szCommandBuffer,m_dwCommandSize);
if(strlen(m_szPassword) != MD5_HASH_SIZE )
{
SecureZeroMemory(m_szPassword,sizeof(m_szPassword));
SendResponse(RESP_ACCOUNT_ERROR);
if(! g_SocketPool.IsMaxSocketUsed() )
{
bRetVal=TRUE;
}
break;
}
//Do the authentication
AnsiToUnicode(szUserName, -1,m_wszUserName, sizeof(m_wszUserName)/sizeof(WCHAR));
bstrUserName=SysAllocString(m_wszUserName);
AnsiToUnicode(m_szPassword, -1, wszPassword, sizeof(wszPassword)/sizeof(WCHAR));
SecureZeroMemory(m_szPassword,sizeof(m_szPassword));
wcscat(wszPassword, m_wszGreeting);
vPassword.vt=VT_BSTR;
vPassword.bstrVal=SysAllocString(wszPassword);
SecureZeroMemory(wszPassword,sizeof(wszPassword));
if(NULL != bstrUserName &&
NULL != vPassword.bstrVal)
{
if(S_OK == (hr= g_pAuthMethod->Authenticate(bstrUserName, vPassword)))
{
bRetVal=TRUE;
}
else if(E_ACCESSDENIED == hr )
{
g_EventLogger.LogEvent(LOGTYPE_ERR_WARNING,
POP3SVR_MAILROOT_ACCESS_DENIED,
m_wszUserName,
1);
}
}
SysFreeString(bstrUserName);
SecureZeroMemory(vPassword.bstrVal,SysStringByteLen(vPassword.bstrVal));
SysFreeString(vPassword.bstrVal);
//Open the mailbox
if(bRetVal)
{
bRetVal=m_MailBox.OpenMailBox(m_wszUserName);
}
if (bRetVal)
{
bRetVal=m_MailBox.LockMailBox();
if(bRetVal)
{
bRetVal=m_MailBox.EnumerateMailBox(g_dwMaxMsgPerDnld);
if(!bRetVal)
{
m_MailBox.UnlockMailBox();
}
}
}
else
{
//Open mailbox failed
if(ERROR_ACCESS_DENIED==GetLastError())
{ //Log the event
g_EventLogger.LogEvent(LOGTYPE_ERR_WARNING,
POP3SVR_MAILROOT_ACCESS_DENIED,
m_wszUserName,
1);
}
}
if (!bRetVal)
{
g_PerfCounters.IncPerfCntr(e_gcFailedLogonCnt);
m_dwFailedAuthCount++;
if( MAX_FAILED_AUTH<=m_dwFailedAuthCount )
{
g_EventLogger.LogEvent(LOGTYPE_ERR_WARNING,
POP3SVR_MAX_LOGON_FAILURES,
m_wszUserName,
1);
}
else
{
if(! g_SocketPool.IsMaxSocketUsed() )
{
bRetVal=TRUE; //Don't disconnect
}
}
SendResponse(RESP_ACCOUNT_ERROR);
}
else
{
m_dwCurrentState=TRANS_STATE;
g_PerfCounters.DecPerfCntr(e_gcAuthStateCnt);
g_PerfCounters.IncPerfCntr(e_gcTransStateCnt);
SendResponse(RESP_AUTH_DONE);
}
break;
case CMD_AUTH: if(0==m_dwAuthStatus)
{
//First time AUTH command
//Only when AD/Local SAM Auth is used,
//we support NTLM
if(AUTH_OTHER==g_dwAuthMethod)
{
SendResponse(RESP_CMD_NOT_SUPPORTED);
bRetVal=TRUE;
}
else
{
if(IsEndOfCommand(&(m_szCommandBuffer[COMMAND_SIZE])))
{
SendResponse(RESP_AUTH_METHODS);
bRetVal=TRUE;
}
else
{
szBuf[0]='\0';
if((GetNextStringParameter( &(m_szCommandBuffer[COMMAND_SIZE]),
szBuf,
POP3_RESPONSE_BUF_SIZE) ) &&
(0==_stricmp(szBuf, SZ_NTLM)) )
{
SendResponse(RESP_OK);
m_dwAuthStatus=1;
bRetVal=TRUE;
}
else
{
SendResponse(RESP_CMD_NOT_VALID);
}
}
}
}
else // This is specific to auth protocol
{
char OutBuf[AUTH_BUF_SIZE];
DWORD dwOutBufSize=AUTH_BUF_SIZE;
SecureZeroMemory(OutBuf, AUTH_BUF_SIZE);
hr=m_AuthServer.HandShake((LPBYTE)m_szCommandBuffer,
dwBytesRcvd,
(LPBYTE)OutBuf,
&dwOutBufSize);
if(S_FALSE==hr)
{
SendResponse(OutBuf);
bRetVal=TRUE;
}
else if(S_OK==hr)
{
m_dwAuthStatus=0;
//Authentication Done!
if(S_OK==m_AuthServer.GetUserName(m_wszUserName))
{
bRetVal=TRUE;
}
else
{
bRetVal=FALSE;
}
//Now open the mailbox
if(bRetVal)
{
bRetVal=m_MailBox.OpenMailBox(m_wszUserName);
}
if (bRetVal)
{
bRetVal=m_MailBox.LockMailBox();
if(bRetVal)
{
bRetVal=m_MailBox.EnumerateMailBox(g_dwMaxMsgPerDnld);
if(!bRetVal)
{
m_MailBox.UnlockMailBox();
}
}
}
else
{
//Open mailbox failed
if(ERROR_ACCESS_DENIED==GetLastError())
{ //Log the event
g_EventLogger.LogEvent(LOGTYPE_ERR_WARNING,
POP3SVR_MAILROOT_ACCESS_DENIED,
m_wszUserName,
1);
}
}
if (!bRetVal)
{
m_dwAuthStatus=0;
m_AuthServer.Cleanup();
g_PerfCounters.IncPerfCntr(e_gcFailedLogonCnt);
m_dwFailedAuthCount++;
SendResponse(RESP_ACCOUNT_ERROR);
if( (MAX_FAILED_AUTH>m_dwFailedAuthCount) &&
(! g_SocketPool.IsMaxSocketUsed() ) )
{
bRetVal=TRUE; //Don't disconnect
}
}
else
{
m_dwCurrentState=TRANS_STATE;
g_PerfCounters.DecPerfCntr(e_gcAuthStateCnt);
g_PerfCounters.IncPerfCntr(e_gcTransStateCnt);
SendResponse(RESP_AUTH_DONE);
bRetVal=TRUE;
}
}
else // Failed Auth
{
m_dwAuthStatus=0;
m_AuthServer.Cleanup();
g_PerfCounters.IncPerfCntr(e_gcFailedLogonCnt);
m_dwFailedAuthCount++;
SendResponse(RESP_CMD_NOT_VALID);
if( (MAX_FAILED_AUTH>m_dwFailedAuthCount) &&
(! g_SocketPool.IsMaxSocketUsed() ) )
{
bRetVal=TRUE; //Don't disconnect
}
}
}
break;
case CMD_QUIT: if(IsEndOfCommand(&(m_szCommandBuffer[COMMAND_SIZE])))
{
g_PerfCounters.DecPerfCntr(e_gcAuthStateCnt);
m_dwCurrentState=UPDATE_STATE;
if(L'\0'!=g_wszGreeting[0])
{
_snprintf(szBuf,
POP3_RESPONSE_BUF_SIZE-1,
RESP_SERVER_QUIT,
g_wszGreeting,
m_wszGreeting);
}
else
{
_snprintf(szBuf,
POP3_RESPONSE_BUF_SIZE-1,
RESP_SERVER_QUIT,
RESP_SERVER_GREETING,
m_wszGreeting);
}
szBuf[POP3_RESPONSE_BUF_SIZE-1]=0;
SendResponse(szBuf);
}
else
{
SendResponse(RESP_CMD_NOT_VALID);
if(! g_SocketPool.IsMaxSocketUsed() )
{
bRetVal=TRUE;
}
}
break;
case CMD_STAT:
case CMD_LIST:
case CMD_RETR:
case CMD_DELE:
case CMD_UIDL:
case CMD_RSET:
case CMD_TOP: SendResponse(RESP_CMD_NOT_SUPPORTED);
if(! g_SocketPool.IsMaxSocketUsed() )
{
bRetVal=TRUE;
}
break;
default: //CountUnknownCommand?
SendResponse(RESP_UNKNOWN_COMMAND);
if(! g_SocketPool.IsMaxSocketUsed() )
{
bRetVal=TRUE;// Still allow client to send another command
}
}
if(bRetVal)
{
WaitForCommand();
}
return bRetVal;
}
BOOL POP3_CONTEXT::ProcessTransStateCommands(POP3_CMD CurrentCmd,
DWORD dwBytesRcvd)
{
char szReBuf[POP3_RESPONSE_BUF_SIZE];
char szReHelpBuf[POP3_RESPONSE_BUF_SIZE];
DWORD dwLen=0;
DWORD dwCurLen;
BOOL bRetVal=TRUE;
int iArg=-1;
int iArg2=-1;
int iMailCount=0;
DWORD dwResult;
char *pCur=NULL;
//The buffer will always be NULL terminated
szReBuf[POP3_RESPONSE_BUF_SIZE-1]='\0';
switch (CurrentCmd)
{
case CMD_STAT:if(!IsEndOfCommand(&(m_szCommandBuffer[COMMAND_SIZE])))
{
SendResponse(RESP_CMD_NOT_VALID);
}
else if(0 >_snprintf(szReBuf,
POP3_RESPONSE_BUF_SIZE-1,
"+OK %d %d\r\n",
m_MailBox.GetCurrentMailCount(),
m_MailBox.GetTotalSize()))
{
//This should not happen
//EventLog??
SendResponse(RESP_SERVER_ERROR);
}
else
{
szReBuf[POP3_RESPONSE_BUF_SIZE-1]=0;
SendResponse(szReBuf);
}
WaitForCommand();
break;
case CMD_LIST:pCur=&(m_szCommandBuffer[COMMAND_SIZE]);
if(!IsEndOfCommand(pCur))
{
if( (GetNextNumParameter(&pCur, &iArg)) &&
(IsEndOfCommand(pCur)) )
{
dwResult=m_MailBox.ListMail(iArg-1, szReBuf, sizeof(szReBuf)/sizeof(char));
SendResponse(dwResult, szReBuf);
}
else
{
SendResponse(RESP_CMD_NOT_VALID);
}
}
else
{
if(0> _snprintf(szReBuf,
POP3_RESPONSE_BUF_SIZE-1,
"+OK %d messages (%d octets)\r\n",
m_MailBox.GetCurrentMailCount(),
m_MailBox.GetTotalSize()))
{
//This should not happen
//EventLog??
SendResponse(RESP_SERVER_ERROR);
}
else
{
iMailCount=m_MailBox.GetMailCount();
dwLen=strlen(szReBuf);
for(iArg=0; iArg<iMailCount; iArg++)
{
if(ERROR_SUCCESS==m_MailBox.ListMail(iArg,
szReHelpBuf,
sizeof(szReHelpBuf)/sizeof(char)))
{
dwCurLen=strlen(szReHelpBuf);
if(dwLen+dwCurLen< POP3_RESPONSE_BUF_SIZE )
{
strcat(szReBuf, szReHelpBuf);
dwLen+=dwCurLen;
}
else
{
SendResponse(szReBuf);
strcpy(szReBuf, szReHelpBuf);
dwLen=dwCurLen;
}
}
}
if(dwLen+sizeof(RESP_END_OF_LIST)<POP3_RESPONSE_BUF_SIZE)
{
strcat(szReBuf, RESP_END_OF_LIST);
SendResponse(szReBuf);
}
else
{
if(dwLen)
{
SendResponse(szReBuf);
}
SendResponse(RESP_END_OF_LIST);
}
}
}
WaitForCommand();
break;
case CMD_UIDL:pCur=&(m_szCommandBuffer[COMMAND_SIZE]);
if(!IsEndOfCommand(pCur))
{
if( (GetNextNumParameter(&pCur, &iArg)) &&
(IsEndOfCommand(pCur)) )
{
dwResult=m_MailBox.UidlMail(iArg-1, szReBuf, sizeof(szReBuf)/sizeof(char));
SendResponse(dwResult, szReBuf);
}
else
{
SendResponse(RESP_CMD_NOT_VALID);
}
}
else
{
if(0> _snprintf(szReBuf,
POP3_RESPONSE_BUF_SIZE-1,
"+OK %d messages (%d octets)\r\n",
m_MailBox.GetCurrentMailCount(),
m_MailBox.GetTotalSize()))
{
//This should not happen
//EventLog??
SendResponse(RESP_SERVER_ERROR);
}
else
{
iMailCount=m_MailBox.GetMailCount();
dwLen=strlen(szReBuf);
for(iArg=0; iArg<iMailCount; iArg++)
{
if(ERROR_SUCCESS==m_MailBox.UidlMail(iArg,
szReHelpBuf,
sizeof(szReBuf)/sizeof(char)))
{
dwCurLen=strlen(szReHelpBuf);
if(dwLen+dwCurLen< POP3_RESPONSE_BUF_SIZE )
{
strcat(szReBuf, szReHelpBuf);
dwLen+=dwCurLen;
}
else
{
SendResponse(szReBuf);
strcpy(szReBuf, szReHelpBuf);
dwLen=dwCurLen;
}
}
}
if(dwLen+sizeof(RESP_END_OF_LIST)<POP3_RESPONSE_BUF_SIZE)
{
strcat(szReBuf, RESP_END_OF_LIST);
SendResponse(szReBuf);
}
else
{
if(dwLen)
{
SendResponse(szReBuf);
}
SendResponse(RESP_END_OF_LIST);
}
}
}
WaitForCommand();
break;
case CMD_RETR://RETR must hava one argument
pCur=&(m_szCommandBuffer[COMMAND_SIZE]);
if( (GetNextNumParameter(&pCur, &iArg)) &&
(IsEndOfCommand(pCur)) )
{
m_bFileTransmitPending=TRUE;
dwResult=m_MailBox.TransmitMail(m_pIoContext, iArg-1);
if(ERROR_SUCCESS != dwResult)
{
m_bFileTransmitPending=FALSE;
SendResponse(RESP_INVALID_MAIL_NUMBER);
WaitForCommand();
}
else
{
g_PerfCounters.IncPerfCntr(e_gcTotMsgDnldCnt);
g_PerfCounters.IncPerfCntr(e_gcMsgDnldRate);
}
}
else
{
SendResponse(RESP_CMD_NOT_VALID);
WaitForCommand();
}
break;
case CMD_TOP: //TOP must have two parameters
pCur=&(m_szCommandBuffer[COMMAND_SIZE-1]);
if( IsEndOfCommand(pCur) )
{
SendResponse(RESP_CMD_NOT_VALID);
}
else
{
if( (GetNextNumParameter(&pCur, &iArg)) &&
!(IsEndOfCommand(pCur)) )
{
if((GetNextNumParameter(&pCur, &iArg2)) &&
(IsEndOfCommand(pCur)) )
{
m_bFileTransmitPending=TRUE;
dwResult=m_MailBox.TransmitMail(m_pIoContext, iArg-1, iArg2);
if(ERROR_SUCCESS !=dwResult)
{
m_bFileTransmitPending=FALSE;
SendResponse(RESP_INVALID_MAIL_NUMBER);
}
else
{
g_PerfCounters.IncPerfCntr(e_gcTotMsgDnldCnt);
g_PerfCounters.IncPerfCntr(e_gcMsgDnldRate);
//
// BLaPorte - do not call WaitForCommand in success case. This
// avoids the situation where we have two pending async
// completions.
//
break;
}
}
else
{
SendResponse(RESP_CMD_NOT_VALID);
}
}
else
{
SendResponse(RESP_CMD_NOT_VALID);
}
}
WaitForCommand();
break;
case CMD_DELE://DELE must have one argument
pCur=&(m_szCommandBuffer[COMMAND_SIZE]);
if( (GetNextNumParameter(&pCur, &iArg)) &&
(IsEndOfCommand(pCur)) )
{
dwResult=m_MailBox.DeleteMail(iArg-1);
if(ERROR_SUCCESS == dwResult)
{
SendResponse(RESP_MSG_MARKED_DELETED);
}
else
{
SendResponse(RESP_INVALID_MAIL_NUMBER);
}
}
else
{
SendResponse(RESP_CMD_NOT_VALID);
}
WaitForCommand();
break;
case CMD_NOOP://NOOP has no argument
if(!IsEndOfCommand(&(m_szCommandBuffer[COMMAND_SIZE])))
{
SendResponse(RESP_CMD_NOT_VALID);
}
else
{
SendResponse(RESP_OK);
}
WaitForCommand();
break;
case CMD_RSET://RSET has no argument
if(!IsEndOfCommand(&(m_szCommandBuffer[COMMAND_SIZE])))
{
SendResponse(RESP_CMD_NOT_VALID);
}
else
{
m_MailBox.Reset();
SendResponse(RESP_RESET);
}
WaitForCommand();
break;
case CMD_QUIT://QUIT has no argument
//Commit all changes to the mailbox and close the connection
if( m_MailBox.CommitAndClose())
{
g_PerfCounters.DecPerfCntr(e_gcTransStateCnt);
m_dwCurrentState=UPDATE_STATE;
if(L'\0'!=g_wszGreeting[0])
{
_snprintf(szReBuf,
POP3_RESPONSE_BUF_SIZE-1,
RESP_SERVER_QUIT,
g_wszGreeting,
m_wszGreeting);
}
else
{
_snprintf(szReBuf,
POP3_RESPONSE_BUF_SIZE-1,
RESP_SERVER_QUIT,
RESP_SERVER_GREETING,
m_wszGreeting);
}
szReBuf[POP3_RESPONSE_BUF_SIZE-1]=0;
SendResponse(szReBuf);
bRetVal=FALSE;
}
else
{
SendResponse(RESP_SERVER_ERROR);
bRetVal=FALSE; // In this case terminate the connection
}
break;
case CMD_USER:
case CMD_APOP:
case CMD_AUTH:
case CMD_PASS:SendResponse(RESP_CMD_NOT_SUPPORTED);
WaitForCommand();
break;
default:
SendResponse(RESP_UNKNOWN_COMMAND);
WaitForCommand();
}
return bRetVal;
}
void POP3_CONTEXT::SendResponse(char *szBuf)
{
int iErr;
ASSERT(m_pIoContext!=NULL);
ASSERT(m_pIoContext->m_hAsyncIO!=NULL);
if(SOCKET_ERROR == send(m_pIoContext->m_hAsyncIO,
szBuf,
strlen(szBuf),
0))
{
iErr=WSAGetLastError();
//Can not send through the socket
//The connection will be terminated later
//in the WaitForCommand call.
}
}
void POP3_CONTEXT::SendResponse(DWORD dwResult, char *szBuf)
{
char szResp[POP3_RESPONSE_BUF_SIZE];
if(ERROR_SUCCESS == dwResult)
{
if( 0 > _snprintf(szResp,
POP3_RESPONSE_BUF_SIZE-1,
"+OK %s",
szBuf))
{
SendResponse(RESP_SERVER_ERROR);
}
else
{
szResp[POP3_RESPONSE_BUF_SIZE-1]=0;
SendResponse(szResp);
}
}
else
{
SendResponse(RESP_INVALID_MAIL_NUMBER);
}
}
BOOL POP3_CONTEXT::GetNextStringParameter(char *szInput, char *szOutput, DWORD dwOutputSize)
{
ASSERT(szInput!=NULL);
ASSERT(szOutput!=NULL);
//Must have at lease one space
if(!isspace(*szInput))
{
return FALSE;
}
do
{
szInput++;
}while(isspace(*szInput));
if('\0'==*szInput)
{
return FALSE;
}
//
// BLaPorte - added output size parameter to prevent buffer overflow.
//
if (strlen(szInput) >= dwOutputSize) {
return FALSE;
}
strcpy(szOutput,szInput);
return TRUE;
}
BOOL POP3_CONTEXT::GetNextNumParameter(char **pszInput, int *piOutput)
{
ASSERT(pszInput!=NULL);
ASSERT(*pszInput!=NULL);
char *szInput=*pszInput;
char *szEndInput=NULL;
if(!isspace(*szInput))
{
return FALSE;
}
do
{
szInput++;
}while(isspace(*szInput));
szEndInput=szInput;
if(!isdigit(*szEndInput))
{
return FALSE;
}
do
{
szEndInput++;
}while(isdigit(*szEndInput));
if((szEndInput-szInput) > MAX_INT_LEN )
{
return FALSE;
}
*piOutput=atoi(szInput);
*pszInput=szEndInput;
return TRUE;
}
BOOL POP3_CONTEXT::IsEndOfCommand(char *szInput)
{
ASSERT(szInput!=NULL);
while(isspace(*szInput))
{
szInput++;
}
if('\0'==*szInput)
{
return TRUE;
}
else
{
return FALSE;
}
}
BOOL POP3_CONTEXT::Unauthenticated()
{
return ( m_dwCurrentState==AUTH_STATE );
}