|
|
#define INCL_INETSRV_INCS
#include "smtpinc.h"
#include "dirnot.hxx"
#include "headers.hxx"
#include "timeconv.h"
#include "smtpcli.hxx"
//
// ProgID for IMsg - this needs to be published in an SDK
//
#define TIMEOUT_INTERVAL 30
#define DIRNOT_IP_ADDRESS "127.0.0.1"
#define IMSG_PROGID L"Exchange.IMsg"
#define MAILMSG_PROGID L"Exchange.MailMsg"
extern void GenerateMessageId (char * Buffer, DWORD BuffLen); extern DWORD GetIncreasingMsgId(); extern BOOL FindNextUnquotedOccurrence(char *lpszString,DWORD dwStringLength, char cSearch,char **ppszLocation);
static char * Daynames[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
//Event used make write files blocking
HANDLE g_hFileWriteEvent;
//
// provide memory for static declared in SMTP_DIRNOT
//
CPool CIoBuffer::Pool( DIRNOT_BUFFER_SIGNATURE ); CPool CBuffer::Pool( DIRNOT_IO_BUFFER_SIGNATURE );
int strcasecmp(char *s1, char *s2); int strncasecmp(char *s1, char *s2, int n);
//+---------------------------------------------------------------
//
// Function: CBuffer
//
// Synopsis: constructor
//
// Arguments: void
//
// Returns: void
//
//----------------------------------------------------------------
CBuffer::CBuffer( BOOL bEncrypted ) : m_dwSignature( DIRNOT_BUFFER_SIGNATURE ), m_bEncrypted( bEncrypted ) { TraceFunctEnterEx( (LPARAM)this, "CBuffer::CBuffer" );
//
// allocate the IO Buffer for this CBuffer
// allocator needs to call GetData to ensure m_pIoBuffer is not NULL
//
ZeroMemory (&m_Overlapped, sizeof(m_Overlapped)); m_pIoBuffer = new CIoBuffer;
m_Overlapped.pBuffer = this; m_cCount = 0; }
//+---------------------------------------------------------------
//
// Function: CBuffer::~CBuffer
//
// Synopsis: frees associated IO buffer
//
// Arguments: void
//
// Returns: void
//
//----------------------------------------------------------------
CBuffer::~CBuffer( void ) { TraceFunctEnterEx( (LPARAM)this, "CBuffer::~CBuffer" );
//
// delete the IO Buffer for this CBuffer
//
if ( m_pIoBuffer != NULL ) { delete m_pIoBuffer; m_pIoBuffer = NULL; } TraceFunctLeaveEx((LPARAM)this); }
/*++
Name:
SMTP_DIRNOT::SMTP_DIRNOT
Constructs a new SMTP connection object for the client connection given the client connection socket and socket address. This constructor is private. Only the Static member funtion, declared below, can call it.
--*/ SMTP_DIRNOT::SMTP_DIRNOT(SMTP_SERVER_INSTANCE * pInstance) { TraceFunctEnterEx( (LPARAM)this, "SMTP_DIRNOT::SMTP_DIRNOT" );
_ASSERT(pInstance != NULL);
m_hDir = INVALID_HANDLE_VALUE; m_pAtqContext = NULL; m_cPendingIoCount = 0; m_cDirChangeIoCount = 0; m_cActiveThreads = 0; m_pInstance = pInstance; //m_pRetryQ = NULL;
m_Signature = SMTP_DIRNOT_SIGNATURE_VALID; InitializeCriticalSection (&m_CritFindLock);
g_hFileWriteEvent = INVALID_HANDLE_VALUE;
m_FindThreads = 0; m_FindFirstHandle = INVALID_HANDLE_VALUE; m_bDelayedFind = FALSE;
TraceFunctLeaveEx((LPARAM)this);
}
SMTP_DIRNOT::~SMTP_DIRNOT (void) { PATQ_CONTEXT pAtqContext = NULL; HANDLE hTemp = INVALID_HANDLE_VALUE;
TraceFunctEnterEx( (LPARAM)this, "SMTP_DIRNOT::~SMTP_DIRNOT" );
_ASSERT(GetThreadCount() == 0);
//release the context from Atq
pAtqContext = (PATQ_CONTEXT)InterlockedExchangePointer( (PVOID *)&m_pAtqContext, (PVOID) NULL); if ( pAtqContext != NULL ) { pAtqContext->hAsyncIO = NULL; AtqFreeContext( pAtqContext, TRUE ); }
// Invalidate the signature. since this connection is trashed.
m_Signature = SMTP_DIRNOT_SIGNATURE_FREE;
hTemp = (HANDLE)InterlockedExchangePointer( (PVOID *)&g_hFileWriteEvent, (PVOID) INVALID_HANDLE_VALUE); if ( hTemp != INVALID_HANDLE_VALUE ) { CloseHandle(hTemp); }
DeleteCriticalSection (&m_CritFindLock);
TraceFunctLeaveEx((LPARAM)this); }
void SMTP_DIRNOT::SetPickupRetryQueueEvent(void) { TraceFunctEnterEx((LPARAM)this, "SMTP_SERVER_INSTANCE::SetPickupRetryQueueEvent");
//if(m_pRetryQ)
//{
// m_pRetryQ->SetQueueEvent();
//}
TraceFunctLeaveEx((LPARAM)this); }
BOOL SMTP_DIRNOT::InitializeObject (char *DirPickupName, ATQ_COMPLETION pfnCompletion) { DWORD error = 0; //PATQ_CONT pContext;
HANDLE StopHandle; DWORD i;
TraceFunctEnterEx( (LPARAM)this, "SMTP_DIRNOT::InitializeObject" );
_ASSERT(m_pInstance != NULL);
//open the directory
m_hDir = CreateFile (DirPickupName, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL); if(m_hDir == INVALID_HANDLE_VALUE) { ErrorTrace((LPARAM) this, "CreateFile on %s failed with error %d ", DirPickupName, GetLastError()); TraceFunctLeaveEx((LPARAM) this); return FALSE; }
g_hFileWriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if(g_hFileWriteEvent == INVALID_HANDLE_VALUE) { ErrorTrace((LPARAM) this, "CreateEvent() failed for FileWriteEvent with error %d ", GetLastError()); TraceFunctLeaveEx((LPARAM) this); return FALSE; }
StopHandle = CreateEvent(NULL, TRUE, FALSE, NULL); if(StopHandle == NULL) { ErrorTrace((LPARAM) this, "CreateEvent() failed with error %d ", GetLastError()); TraceFunctLeaveEx((LPARAM) this); return FALSE; }
//add it to our Gibraltar interface
if(!AtqAddAsyncHandle( &m_pAtqContext, NULL, this, pfnCompletion, INFINITE, m_hDir)) { error = GetLastError(); ErrorTrace((LPARAM) this, "AtqAddAsyncHandle on %s failed with error %d ", DirPickupName, GetLastError()); CloseHandle (m_hDir); CloseHandle (StopHandle); m_hDir = INVALID_HANDLE_VALUE; StopHandle = NULL; TraceFunctLeaveEx((LPARAM) this); return FALSE; }
QuerySmtpInstance()->SetDirnotStopHandle(StopHandle);
//
// pend a set of outstanding directory change notifications, at all times, we want
// at least one notification outstanding, so pend 3 or 4
//
for (i = 0; i < OUTSTANDING_NOTIFICATIONS; i++) { if(!PendDirChangeNotification ()) { ErrorTrace((LPARAM) this, "PendDirChangeNotification on failed with error %d ", GetLastError()); ErrorTrace((LPARAM) this, "Setting stop handle because PendDirChangeNotification () failed"); SetEvent(StopHandle); TraceFunctLeaveEx((LPARAM) this); return FALSE; } }
TraceFunctLeaveEx((LPARAM) this); return TRUE; }
void SMTP_DIRNOT::CloseDirHandle (void) { HANDLE hDir = NULL; int i = 0; int Count = 0; int AfterSleepCount = 0; DWORD dwLastAllocCount = 0; DWORD dwAllocCount = 0; DWORD dwStopHint = 2; DWORD dwTickCount = 0;
TraceFunctEnterEx( (LPARAM)this, "SMTP_DIRNOT::CloseDirHandle" );
_ASSERT(m_pInstance != NULL);
hDir = (HANDLE) InterlockedExchangePointer((PVOID *) &m_hDir, NULL); if(hDir != NULL) { CloseHandle (hDir); }
//
// need to check Pool.GetAllocCount instead of InUseList.Empty
// because alloc goes to zero during the delete operator
// instead of during the destructor
//
//
dwTickCount = GetTickCount(); for( i = 0; i < 240; i++ ) { dwAllocCount = (DWORD) QuerySmtpInstance()->GetCBufferAllocCount ();
if ( dwAllocCount == 0) { DebugTrace((LPARAM)this, "All pickup CBuffers are gone!"); break; }
Sleep( 1000 );
// Update the stop hint checkpoint when we get within 1 second (1000 ms), of the timeout...
if ((SERVICE_STOP_WAIT_HINT - 1000) < (GetTickCount() - dwTickCount) && g_pInetSvc && (g_pInetSvc->QueryCurrentServiceState() == SERVICE_STOP_PENDING)) { DebugTrace((LPARAM)this, "Updating stop hint in pickup, checkpoint = %u", dwStopHint);
g_pInetSvc->UpdateServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, dwStopHint, SERVICE_STOP_WAIT_HINT ) ;
dwStopHint++ ; dwTickCount = GetTickCount(); }
DebugTrace((LPARAM)this, "Alloc counts: current = %u, last = %u;", dwAllocCount, dwLastAllocCount); if (dwAllocCount < dwLastAllocCount) { DebugTrace((LPARAM)this, "Pickup CBuffers are going away, reseting i"); i = 0; }
dwLastAllocCount = dwAllocCount; }
DebugTrace((LPARAM)this, "Waiting for QuerySmtpInstance()->GetDirnotStopHandle()!");
WaitForSingleObject(QuerySmtpInstance()->GetDirnotStopHandle(), INFINITE);
DebugTrace((LPARAM)this, "End waiting for QuerySmtpInstance()->GetDirnotStopHandle()!");
QuerySmtpInstance()->SetStopHint(2);
TraceFunctLeaveEx((LPARAM)this); }
/*++
Name : SMTP_DIRNOT::CreateSmtpDirNotification
Description: This is the static member function than is the only entity that is allowed to create an SMTP_CONNOUT class. This class cannot be allocated on the stack.
Arguments:
Returns:
A pointer to an SMTP_DIRNOT class or NULL --*/ SMTP_DIRNOT * SMTP_DIRNOT::CreateSmtpDirNotification (char * DirPickupName, ATQ_COMPLETION pfnCompletion, SMTP_SERVER_INSTANCE * pInstance) { SMTP_DIRNOT * pSmtpDirNotObj;
TraceFunctEnterEx((LPARAM) 0, "SMTP_CONNOUT::CreateSmtpConnection");
pSmtpDirNotObj = new SMTP_DIRNOT (pInstance); if(pSmtpDirNotObj == NULL) { ErrorTrace(0, "new SMTP_DIRNOT () failed"); TraceFunctLeaveEx((LPARAM)NULL); return NULL; }
if(!pSmtpDirNotObj->InitializeObject(pSmtpDirNotObj->QuerySmtpInstance()->GetMailPickupDir(), SMTP_DIRNOT::ReadDirectoryCompletion)) { TraceFunctLeaveEx((LPARAM)NULL); return NULL; }
TraceFunctLeaveEx((LPARAM)NULL); return pSmtpDirNotObj; }
BOOL SMTP_DIRNOT::DoFindFirstFile(BOOL bIISThread) { //
// re-entrent FindFirst... the first thread does the FindFirst, all other threads up to
// MAXFIND_THREADS do the FindNext.
//
char Buffer [MAX_PATH + 1]; HANDLE hFindFile = INVALID_HANDLE_VALUE; DWORD BytesRead = 0; DWORD NumFiles = 0; WIN32_FIND_DATA find; BOOL bClosed; PSMTP_IIS_SERVICE pService;
TraceFunctEnterEx((LPARAM)this, "DoFindFirstFile");
_ASSERT(m_pInstance != NULL);
if (!QuerySmtpInstance()->GetAcceptConnBool()) { return TRUE; }
pService = (PSMTP_IIS_SERVICE) g_pInetSvc; //
// ensure only one thread gets in here at a time. we can only have one thread either setting
// the find first at a time.
//
LockFind();
hFindFile = GetFindFirstHandle(); if (hFindFile == INVALID_HANDLE_VALUE) { //make up the file spec we want to find
lstrcpy(Buffer, QuerySmtpInstance()->GetMailPickupDir()); lstrcat(Buffer, "*.*");
hFindFile = FindFirstFile(Buffer, &find); if (hFindFile == INVALID_HANDLE_VALUE) { // should not fail as we look for *.* and the directory root should be there.
// setting the flag will make sure that next drop posts a findfirst.
SetDelayedFindNotification(TRUE);
ErrorTrace((LPARAM) this, "FindFirst failed for %s. Error %d", Buffer, GetLastError());
UnLockFind(); TraceFunctLeaveEx((LPARAM)this); return TRUE; } else {
IncFindThreads(); // there should be not find threads running at this point.
//
// We have no IIS threads available for the single findfirst... we must create a thread.
// hopefull this will happen seldom.
SetFindFirstHandle(hFindFile); SetDelayedFindNotification(FALSE); } } else { SetDelayedFindNotification(TRUE); if (!IncFindThreads()) { UnLockFind(); DebugTrace((LPARAM)this, "Have hit the max num Find Threads."); TraceFunctLeaveEx((LPARAM)this); return TRUE; }
if (!FindNextFile(hFindFile, &find)) { if (GetLastError() != ERROR_NO_MORE_FILES) { SetDelayedFindNotification(TRUE); ErrorTrace((LPARAM) this,"FindNextFile() failed with error %d", GetLastError()); } CloseFindHandle(); // will DecFindThreads.
//
// In the case below, it is possible that some files were missed by FindFirst.
// Create an ATQ thread for a final findfirst iteration to make sure.
//
if ((GetNumFindThreads() == 0) && GetDelayedFindNotification()) { IncPendingIoCount(); // AtqContext with buffer size of zero to get a FindFirst Going.
if(!AtqPostCompletionStatus(QueryAtqContext(), 0)) { DecPendingIoCount(); ErrorTrace((LPARAM) this,"AtqPostCompletionStatus() failed with error %d", GetLastError()); } }
UnLockFind(); TraceFunctLeaveEx((LPARAM)this); return TRUE; } }
UnLockFind();
bClosed = FALSE; do { //format the name of the stream and then open the file.
BytesRead = wsprintf(Buffer, "%s%s",QuerySmtpInstance()->GetMailPickupDir(), find.cFileName); if (!(find.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { HRESULT hr = S_OK; IMailMsgProperties *pIMsg = NULL;
hr = CoCreateInstance(CLSID_MsgImp, NULL, CLSCTX_INPROC_SERVER, IID_IMailMsgProperties, (LPVOID *)&pIMsg);
// Next, check if we are over the inbound cutoff limit. If so, we will release the message
// and not proceed.
if (SUCCEEDED(hr)) { DWORD dwCreationFlags; hr = pIMsg->GetDWORD( IMMPID_MPV_MESSAGE_CREATION_FLAGS, &dwCreationFlags); if (FAILED(hr) || (dwCreationFlags & MPV_INBOUND_CUTOFF_EXCEEDED)) { // If we fail to get this property of if the inbound cutoff
// exceeded flag is set, discard the message and return failure
if (SUCCEEDED(hr)) { DebugTrace((LPARAM)this, "Failing because inbound cutoff reached"); hr = E_OUTOFMEMORY; } pIMsg->Release(); pIMsg = NULL; } }
DebugTrace((LPARAM)this,"Found file %s", find.cFileName);
if ((pIMsg == NULL) || FAILED(hr)) { // We are out of resources, there is absolutely nothing
// we can do: can't NDR, can't retry ...
ErrorTrace((LPARAM) this, "new MAILQ_ENTRY failed for file: %s", find.cFileName);
//
// Will want to run a findfirst when things free up a little. Flag the post-processing findfirst.
//
SetDelayedFindNotification(TRUE); IncPendingIoCount (); AtqContextSetInfo(QueryAtqContext(), ATQ_INFO_TIMEOUT, TIMEOUT_INTERVAL); //retry after a while
ErrorTrace((LPARAM)this, "Failed to create message will retry later."); break; } else { // We are in faith that upon delivery, the allocated
// MailQEntry structure will be freed
NumFiles++; pIMsg->PutStringA(IMMPID_MP_PICKUP_FILE_NAME, find.cFileName);
if(!ProcessFile(pIMsg)) { // will be mail left in pickup. queue a findfirst to take care of it.
SetDelayedFindNotification(TRUE); } } }
LockFind(); if (!FindNextFile(hFindFile, &find)) { if (GetLastError() != ERROR_NO_MORE_FILES) { SetDelayedFindNotification(TRUE); ErrorTrace((LPARAM) this,"FindNextFile() failed with error %d", GetLastError()); }
CloseFindHandle();
//
// In the case below, it is possible that some files were missed by FindFirst.
// Create an ATQ thread for a final findfirst iteration to make sure.
//
if ((GetNumFindThreads() == 0) && GetDelayedFindNotification()) { IncPendingIoCount(); // AtqContext with buffer size of zero to get a FindFirst Going.
if(!AtqPostCompletionStatus(QueryAtqContext(), 0)) { DecPendingIoCount(); ErrorTrace((LPARAM) this,"AtqPostCompletionStatus() failed with error %d", GetLastError()); } }
UnLockFind(); bClosed = TRUE; break; } UnLockFind();
} while ((!QuerySmtpInstance()->IsShuttingDown()) && (QuerySmtpInstance()->QueryServerState( ) != MD_SERVER_STATE_STOPPED) && (QuerySmtpInstance()->QueryServerState( ) != MD_SERVER_STATE_INVALID));
if (!bClosed) // termination by the while condition above.
{ LockFind(); CloseFindHandle(); UnLockFind(); }
TraceFunctLeaveEx((LPARAM)this); return TRUE; }
DWORD WINAPI SMTP_DIRNOT::CreateNonIISFindThread(void * ClassPtr) { //
// Called by a CreateThread when we have run out of IIS threads.
//
SMTP_DIRNOT * ThisPtr = (SMTP_DIRNOT *) ClassPtr;
TraceFunctEnterEx((LPARAM) ThisPtr,"CreateNonIISFindThread");
_ASSERT(ThisPtr != NULL);
_ASSERT(ThisPtr->QuerySmtpInstance() != NULL);
//
// Build the initial list - THE FLAG bIISThread IS SET TO FALSE.
// We IncCBufferAllocCount and DecCBufferAllocCount to make sure that we clean up properly in CloseDirHandle.
// We don't want to destroy the Dirnot Object before this thread finishes.
//
ThisPtr->QuerySmtpInstance()->IncCBufferObjs();
ThisPtr->DoFindFirstFile(FALSE);
ThisPtr->QuerySmtpInstance()->DecCBufferObjs();
TraceFunctLeaveEx((LPARAM)ThisPtr); return TRUE; }
DWORD WINAPI SMTP_DIRNOT::PickupInitialFiles(void * ClassPtr) { SMTP_DIRNOT * ThisPtr = (SMTP_DIRNOT *) ClassPtr;
TraceFunctEnterEx((LPARAM) ThisPtr,"PickupInitialFiles");
_ASSERT(ThisPtr != NULL);
_ASSERT(ThisPtr->QuerySmtpInstance() != NULL);
// Just quit if we are suhtting down already
if (ThisPtr->QuerySmtpInstance()->IsShuttingDown()) return TRUE;
// Build the initial list
ThisPtr->DoFindFirstFile();
TraceFunctLeaveEx((LPARAM)ThisPtr); return TRUE; }
#define PRIVATE_OPTIMAL_BUFFER_SIZE 4096
#define PRIVATE_LINE_BUFFER_SIZE 1024
#define IS_SPACE_OR_TAB(ch) (((ch) == ' ') || ((ch) == '\t'))
#define IS_WHITESPACE_OR_CRLF(ch) (((ch) == ' ') || ((ch) == '\t') || ((ch) == '\n') || ((ch) == '\r'))
static void pReplaceCrLfWithSpaces(CHAR *szIn, CHAR *szOut) { while (*szIn) { if ((*szIn == '\r') || (*szIn == '\n')) *szOut++ = ' '; else *szOut++ = *szIn; szIn++; } *szOut = '\0'; }
BOOL SMTP_DIRNOT::CreateToList (char *AddrsList, IMailMsgRecipientsAdd *pIMsgRecips, IMailMsgProperties *pIMsgProps) { char *p = NULL; //points to the ',' or '\0'
char * StartOfAddress = NULL; //start of recipient address
char * EndOfAddress = NULL; // end of recipient address
char * ThisAddress = NULL; CAddr * NewAddress = NULL; //new CAddr to add to our list
char szAddress[MAX_INTERNET_NAME + 1], *pszAddress = szAddress; DWORD dwPropId = IMMPID_RP_ADDRESS_SMTP; DWORD dwNewRecipIndex = 0; HRESULT hr = S_OK; BOOL fNotFound = FALSE;
TraceFunctEnterEx((LPARAM) this, "SMTP_DIRNOT::CreateToList");
_ASSERT(m_pInstance != NULL);
//start at the top of the list
p = AddrsList;
//get rid of leading white space, newlines, and commas
while ((*p == ',') || IS_WHITESPACE_OR_CRLF(*p)) p++;
while((p != NULL) && (*p != '\0')) { char LastChar;
StartOfAddress = p;
// Find the ending delimiter of the address
//while((*p != '\0') && (*p != ','))
// p++;
// The first unquoted comma indicates the end of the address
if(!FindNextUnquotedOccurrence(p,strlen(p),',',&EndOfAddress)) { SetLastError(ERROR_INVALID_DATA); NewAddress = NULL; ErrorTrace((LPARAM) this, "Failed to parse out the address"); return FALSE; } else if(!EndOfAddress) EndOfAddress = p + strlen(p);
p = EndOfAddress;
_ASSERT(EndOfAddress != NULL); // We don't like trailing spaces either, so walk backwards
// to get rid of them
//EndOfAddress = p;
while (EndOfAddress > StartOfAddress) { EndOfAddress--; if (!IS_WHITESPACE_OR_CRLF(*EndOfAddress)) { EndOfAddress++; break; } }
// Save the character we are about to overrite
LastChar = *EndOfAddress;
// NULL terminate the address
*EndOfAddress = '\0';
if(lstrlen(StartOfAddress) > (MAX_INTERNET_NAME + 1)) { SetLastError(ERROR_INVALID_DATA); NewAddress = NULL; ErrorTrace((LPARAM) this, "Address too long : %d bytes",lstrlen(StartOfAddress)); return FALSE; }
pReplaceCrLfWithSpaces(StartOfAddress, szAddress);
DebugTrace((LPARAM) this, "found address [%s]", szAddress);
//
// Run it through the addr821 library
//
NewAddress = CAddr::CreateAddress(szAddress); BOOL fValidAddress = FALSE; if(NewAddress) { if(!NewAddress->IsDomainOffset()) { CAddr * TempAddress = NULL; TempAddress = QuerySmtpInstance()->AppendLocalDomain (NewAddress); if (TempAddress) { delete NewAddress; NewAddress = TempAddress; TempAddress = NULL; } else if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY) { ErrorTrace((LPARAM) this, "CAddr::CreateAddress (StartOfAddress) failed . err: %u", ERROR_NOT_ENOUGH_MEMORY); SetLastError (ERROR_NOT_ENOUGH_MEMORY); return FALSE; }
}
DebugTrace((LPARAM) this, "CreateAddress returned %s", NewAddress->GetAddress());
ThisAddress = NewAddress->GetAddress(); DWORD dwLen = strlen(ThisAddress); if(Validate821Address( ThisAddress, dwLen)) { LPSTR pszDomain; if(Get821AddressDomain( ThisAddress, dwLen, &pszDomain) && pszDomain) { DWORD dwDomain = strlen(pszDomain); if (Validate821Domain(pszDomain, dwDomain)) { // everything is valid
fValidAddress = TRUE; } } else { ErrorTrace((LPARAM)0, "Detected legal address without a domain: %s", ThisAddress); } } else { ErrorTrace((LPARAM)0, "Detected ILLEGAL address: %s", ThisAddress); } } else { if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY) { ErrorTrace((LPARAM) this, "CAddr::CreateAddress (StartOfAddress) failed . err: %u", ERROR_NOT_ENOUGH_MEMORY); return FALSE; } ThisAddress = szAddress; fValidAddress = FALSE; }
hr = pIMsgRecips->AddPrimary(1, (LPCTSTR *) &ThisAddress, &dwPropId, &dwNewRecipIndex, NULL, 0); if (FAILED(hr)) { SetLastError(hr); return FALSE; }
if (NewAddress) delete NewAddress;
if (!fValidAddress) { // AQ will look for this special domain and won't add it to the
// DMT. This stops us from putting corrupted domains into the
// DMT
hr = pIMsgRecips->PutStringA(dwNewRecipIndex, IMMPID_RP_DOMAIN, "==NDR=="); if (FAILED(hr)) { DebugTrace((LPARAM) 0, "PutString(RP_DOMAIN) failed 0x%x", hr); SetLastError(hr); return FALSE; }
// set the recipient to NDR
hr = pIMsgRecips->PutDWORD(dwNewRecipIndex, IMMPID_RP_RECIPIENT_FLAGS, (RP_ERROR_CONTEXT_CAT | RP_UNRESOLVED)); if (FAILED(hr)) { DebugTrace((LPARAM) 0, "PutDWORD(RP_FLAGS) failed 0x%x", hr); SetLastError(hr); return FALSE; }
// tell AQ why it is NDRing
hr = pIMsgRecips->PutDWORD(dwNewRecipIndex, IMMPID_RP_ERROR_CODE, CAT_E_ILLEGAL_ADDRESS); if (FAILED(hr)) { DebugTrace((LPARAM) 0, "PutDWORD(RP_ERROR) failed 0x%x", hr); SetLastError(hr); return FALSE; }
// tell AQ that some recips are NDRing
hr = pIMsgProps->PutDWORD(IMMPID_MP_HR_CAT_STATUS, CAT_W_SOME_UNDELIVERABLE_MSGS); if (FAILED(hr)) { DebugTrace((LPARAM) 0, "SetMailMsgCatStatus failed 0x%x", hr); SetLastError(hr); return FALSE; } }
// Go find the start of the next address
*EndOfAddress = LastChar; if(*p == ',') { p++; while(IS_WHITESPACE_OR_CRLF(*p)) p++; } }
TraceFunctLeave(); return (TRUE); }
static HANDLE pOpenPickupFile(LPSTR szFileName) { DWORD dwError; HANDLE hFile;
TraceFunctEnterEx((LPARAM)NULL, "pOpenPickupFile");
// Open the file
hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL ); if (hFile == INVALID_HANDLE_VALUE) { dwError = GetLastError(); ErrorTrace((LPARAM)NULL, "Error: Can't open source file %s (err=%d)...skipping it", szFileName, dwError); } TraceFunctLeaveEx((LPARAM)NULL); return(hFile); }
static inline BOOL pFetchFileBuffer( HANDLE hFile, CHAR *lpBuffer, LPDWORD lpdwSize) { BOOL fRet = TRUE; DWORD Error = 0;
fRet = ReadFile(hFile, lpBuffer, *lpdwSize, lpdwSize, NULL);
if(!fRet) { Error = GetLastError(); if(Error == 998) { _ASSERT(FALSE); } }
return fRet; }
static CHAR *pGetLineBuffer(CHAR *lpOldBuffer, DWORD *lpdwLength) { DWORD dwLength = *lpdwLength; CHAR *lpBuffer;
if (lpOldBuffer) { // We have an old buffer, we double the size of the old buffer
_ASSERT(lpdwLength); dwLength <<= 1; } else dwLength = PRIVATE_LINE_BUFFER_SIZE;
// Allocate the new buffer
lpBuffer = (CHAR *)HeapAlloc(GetProcessHeap(), 0, dwLength); if (!lpBuffer) return(NULL);
if (lpOldBuffer) { // Copy the info over and free the old buffer
CopyMemory((LPVOID)lpBuffer, (LPVOID)lpOldBuffer, *lpdwLength); _VERIFY( HeapFree(GetProcessHeap(), 0, lpOldBuffer) ); }
*lpdwLength = dwLength; return(lpBuffer); }
static BOOL pFreeLineBuffer(CHAR *lpBuffer) { return( HeapFree(GetProcessHeap(), 0, lpBuffer) ); }
static CHAR *pGetValueFromHeader(CHAR *szHeader) { while (*szHeader && (*szHeader++ != ':')) ; while (*szHeader) { if (!IS_SPACE_OR_TAB(*szHeader)) return(szHeader); else szHeader++; } return(NULL); }
static CHAR *pReadNextLineFromBuffer( HANDLE hFile, CHAR *lpBuffer, LPDWORD lpdwSize, CHAR *lpStart, LPSTR *ppszLine, DWORD *lpdwMaxLineLen, LPSTR *ppOriginalBuffer, DWORD *lpdwOriginalBufferLen) { DWORD dwLineLen = 0; DWORD dwMaxLineLen = *lpdwMaxLineLen; BOOL fThisIsCR = FALSE; BOOL fLastIsCR = FALSE; BOOL fThisIsLF = FALSE; BOOL fLastIsLF = FALSE; CHAR *lpEnd; CHAR *lpszLine = *ppszLine; CHAR ch; BOOL bEndOfFile;
TraceFunctEnter("pReadNextLineFromBuffer");
_ASSERT(hFile != INVALID_HANDLE_VALUE); _ASSERT(!IsBadWritePtr(lpdwSize, sizeof(DWORD))); _ASSERT(!IsBadWritePtr(lpBuffer, *lpdwSize)); _ASSERT(!IsBadWritePtr(*ppszLine, *lpdwMaxLineLen));
// raid 181922/88855 - replace recusive loop with while loop.
do { dwLineLen = 0; dwMaxLineLen = *lpdwMaxLineLen; fThisIsCR = FALSE; fLastIsCR = FALSE; fThisIsLF = FALSE; fLastIsLF = FALSE; bEndOfFile = FALSE;
// Now, make sure the supplied start pointer is within
// buffer supplied (We allow it to be one byte past the
// end of the buffer, but we will never dereference it).
if ((lpStart < lpBuffer) || (lpStart > (lpBuffer + *lpdwSize))) { _ASSERT(0); return(NULL); }
// Now, keep copying until we hit one of the following scenarios:
// i) We hit CRLF
// ii) We hit the end of the buffer, which we reload more data
// or if it is the end of the mail, we postpend a CRLF.
lpEnd = lpBuffer + *lpdwSize; do { // See if this is past the buffer
if (lpStart == lpEnd) { DWORD dwNewLength;
dwNewLength = *lpdwSize; if (!pFetchFileBuffer(hFile, lpBuffer, &dwNewLength)) { return(NULL); }
// Done!
if (!dwNewLength) { bEndOfFile = TRUE; break; }
// Get the new buffer length
*lpdwSize = dwNewLength;
// Reset the start and end pointers
lpStart = lpBuffer; lpEnd = lpBuffer + *lpdwSize; }
ch = *lpszLine++ = *lpStart++;
// Too long?
if (++dwLineLen >= dwMaxLineLen) { CHAR *lpTemp; DWORD dwUsedPortion = (DWORD)(lpszLine - *ppOriginalBuffer);
// Yep, get a bigger buffer, all the existing stuff is copied over
DebugTrace((LPARAM)*ppOriginalBuffer, "Growing buffer at %u bytes", *lpdwOriginalBufferLen); lpTemp = pGetLineBuffer(*ppOriginalBuffer, lpdwOriginalBufferLen); if (!lpTemp) { DebugTrace((LPARAM)NULL, "Failed to obtain buffer (%u)", GetLastError()); TraceFunctLeave(); return(NULL); }
// Got it, adjust all associated pointers and lengths
DebugTrace((LPARAM)lpTemp, "Obtained buffer at %u bytes", *lpdwOriginalBufferLen);
// New beginning of line buffer
*ppOriginalBuffer = lpTemp;
// New beginning of line
*ppszLine = lpTemp;
// New pointer to next character
lpszLine = lpTemp + dwUsedPortion;
// New maximum length for current string before re-growing
dwMaxLineLen = *lpdwOriginalBufferLen - dwUsedPortion;
}
fLastIsCR = fThisIsCR; if (ch == '\r') fThisIsCR = TRUE; else fThisIsCR = FALSE;
fLastIsLF = fThisIsLF; if (ch == '\n') fThisIsLF = TRUE; else fThisIsLF = FALSE;
// If we have CRLF or LFCR we leave
if ((fLastIsCR && fThisIsLF) || (fLastIsLF && fThisIsCR)) break;
} while (1);
*lpszLine = '\0';
// Calculate remaining buffer size
*lpdwMaxLineLen = dwMaxLineLen - dwLineLen;
// raid 178234 - If we have CRLF or LFCR and no more continue line we leave
if (((fLastIsCR && fThisIsLF) || (fLastIsLF && fThisIsCR)) && !IS_SPACE_OR_TAB(*lpStart)) { // raid 166777 - Make sure we do leave if we find a line.
break; }
} while (!bEndOfFile && (IS_SPACE_OR_TAB(*lpStart) || (lpStart == lpEnd))); // We always return the start of line to be the start of our buffer
// Note that the buffer could have changed during recursion due to growth
*ppszLine = *ppOriginalBuffer;
TraceFunctLeave(); return(lpStart); }
BOOL inline WriteToSpooledFile(PFIO_CONTEXT hDstFile, CHAR *lpszLine, DWORD &DestOffset) { DWORD dwBytesToWrite; DWORD dwBytesWritten; BOOL fResult = FALSE; FH_OVERLAPPED ov; DWORD err = NO_ERROR; HANDLE HackedHandle = NULL; BOOL fRet = FALSE;
ZeroMemory(&ov, sizeof(ov));
ov.Offset = DestOffset;
dwBytesToWrite = lstrlen(lpszLine); if (!dwBytesToWrite) { fRet = TRUE; //This is not really a failure
goto Exit; }
//HackedHandle = ((DWORD)g_hFileWriteEvent | 1);
HackedHandle = CreateEvent(NULL, TRUE, FALSE, NULL); if(HackedHandle == NULL) { return FALSE; } ov.hEvent = (HANDLE) ((ULONG_PTR) HackedHandle | 1);
//ov.hEvent = (HANDLE)HackedHandle;
fResult = FIOWriteFile(hDstFile, lpszLine, dwBytesToWrite, &ov);
if (!fResult) err = GetLastError(); if((err == ERROR_IO_PENDING) || (((DWORD)STATUS_PENDING == ov.Internal) && ( err == NO_ERROR ))) {
// 12/16/98 - MikeSwa Modified
// OK... this is the theory... we are getting random AV, from what
// looks like async completion to I/O. It looks like
// GetOverlappedResult cannot be called to wait for a hacked handle,
// and it will not use the first argument unless the event in the
// overlapped structure is NULL. The solution is to call WaitForSingleObject
// if we detect that the IO is still pending
WaitForSingleObject(HackedHandle, INFINITE); _ASSERT((DWORD)STATUS_PENDING != ov.Internal); } else if (NO_ERROR != err) { SetLastError (err); //preserve the last error
if(err == 998) { _ASSERT(FALSE); }
goto Exit; }
DestOffset += dwBytesToWrite;
fRet = TRUE;
Exit:
if(HackedHandle) { CloseHandle(HackedHandle); }
//TraceFunctLeaveEx((LPARAM)NULL);
return fRet; }
BOOL CopyRestOfMessage(HANDLE hSrcFile, PFIO_CONTEXT hDstFile, DWORD &DestOffset) { CHAR acBuffer[PRIVATE_OPTIMAL_BUFFER_SIZE]; DWORD dwBytesRead; DWORD dwBytesWritten; DWORD dwTotalBytes = 0; DWORD err = NO_ERROR; BOOL fResult = FALSE; BOOL fRet = FALSE; HANDLE HackedHandle; FH_OVERLAPPED ov;
CHAR acCrLfDotCrLf[5] = { '\r', '\n', '.', '\r', '\n' }; CHAR acLastBytes[5] = { '\0', '\0', '\0', '\0', '\0' };
// Copies from the current file pointer to the end of hSrcFile
// and appends to the current file pointer of hDstFile.
_ASSERT(hSrcFile != INVALID_HANDLE_VALUE); _ASSERT(hDstFile != NULL);
ZeroMemory(&ov, sizeof(ov)); HackedHandle = CreateEvent(NULL, TRUE, FALSE, NULL); if(HackedHandle == NULL) { return FALSE; } ov.hEvent = (HANDLE) ((ULONG_PTR) HackedHandle | 1);
do { if (!ReadFile(hSrcFile, acBuffer, PRIVATE_OPTIMAL_BUFFER_SIZE, &dwBytesRead, NULL)) { err = GetLastError(); if(err == 998) { _ASSERT(FALSE); }
goto Exit; }
if (dwBytesRead) { ov.Offset = DestOffset;
//
// Save the last two bytes ever read/written. the buffer after
// writing could be modified due to dot-stripping.
//
if (dwBytesRead > 4) { CopyMemory(acLastBytes, &acBuffer[dwBytesRead-5], 5); } else { MoveMemory(acLastBytes, &acLastBytes[dwBytesRead], 5-dwBytesRead); CopyMemory(&acLastBytes[5-dwBytesRead], acBuffer, dwBytesRead); }
fResult = FIOWriteFile(hDstFile, acBuffer, dwBytesRead, &ov);
if (!fResult) err = GetLastError();
if((err == ERROR_IO_PENDING) || (((DWORD)STATUS_PENDING == ov.Internal) && ( err == NO_ERROR ))) {
// 12/16/98 - MikeSwa Modified
// OK... this is the theory... we are getting random AV, from what
// looks like async completion to I/O. It looks like
// GetOverlappedResult cannot be called to wait for a hacked handle,
// and it will not use the first argument unless the event in the
// overlapped structure is NULL. The solution is to call
// WaitForSingleObject and pass in the un-munged handle (only if
// we know that IO is still pending).
WaitForSingleObject(HackedHandle, INFINITE); _ASSERT((DWORD)STATUS_PENDING != ov.Internal); } else if (NO_ERROR != err) { SetLastError (err); //preserve the last error
if(err == 998) { _ASSERT(FALSE); }
goto Exit; }
//
// this is because Fcache keeps track of the offset were we need
// to write. So we update dwBytesWritten to what we actually read.
//
dwBytesWritten = dwBytesRead; } else { dwBytesWritten = 0; }
if (dwBytesWritten) { dwTotalBytes += dwBytesWritten; DestOffset += dwBytesWritten; }
} while (dwBytesRead);
// Now, see if the file ends with a CRLF, if not, add it
if ((dwTotalBytes > 1) && memcmp(&acLastBytes[3], &acCrLfDotCrLf[3], 2)) { // Add the trailing CRLF
if (!WriteToSpooledFile(hDstFile, "\r\n", DestOffset)) { goto Exit; }
dwTotalBytes+=2;
}
//If file ends with CRLF.CRLF, remove the trailing .CRLF
//NimishK ** : this was decided per the bug 63394
if ((dwTotalBytes > 4) && !memcmp(acLastBytes, acCrLfDotCrLf, 5)) { DWORD dwFileSizeHigh = 0; DWORD dwFileSizeLow = GetFileSizeFromContext( hDstFile, &dwFileSizeHigh );
DWORD Offset = SetFilePointer(hDstFile->m_hFile, dwFileSizeLow, NULL, FILE_BEGIN);
// Remove the trailing CRLF only as the <DOT> would have been removed by
// dot-stripping by file handle cache.
if ((SetFilePointer(hDstFile->m_hFile, -2, NULL, FILE_CURRENT) == 0xffffffff) || !SetEndOfFile(hDstFile->m_hFile)) { _ASSERT(0 && "SetFilePointerFailed"); goto Exit; } }
fRet = TRUE;
Exit:
if(HackedHandle) { CloseHandle(HackedHandle); }
return fRet; }
BOOL SMTP_DIRNOT::ProcessFile(IMailMsgProperties *pIMsg) { LONGLONG LastAccessTime = (LONGLONG) 0; CHAR* acCrLf = "\r\n"; DWORD AbOffset = 0; DWORD DestWriteOffset = 0; DWORD HeaderFlags = 0; DWORD dwPickupFileSize = 0; BOOL fIsStartOfFile = TRUE; BOOL fIsXSenderRead = FALSE; BOOL fAreXRcptsRead = FALSE; BOOL fIsSenderSeen = FALSE; BOOL fInvalidAddresses = FALSE; BOOL fDateExists = FALSE; BOOL fMessageIdExists = FALSE; BOOL fXOriginalArrivalTime = FALSE; BOOL fFromSeen = FALSE; BOOL fRcptSeen = FALSE; BOOL fSeenRFC822FromAddress = FALSE; BOOL fSeenRFC822ToAddress = FALSE; BOOL fSeenRFC822CcAddress = FALSE; BOOL fSeenRFC822BccAddress = FALSE; BOOL fSeenRFC822Subject = FALSE; BOOL fSeenRFC822SenderAddress = FALSE; BOOL fSeenXPriority = FALSE; BOOL fSeenContentType = FALSE; BOOL fSetContentType = FALSE; HANDLE hSource = INVALID_HANDLE_VALUE; PFIO_CONTEXT hDest = NULL; SYSTEMTIME SysTime; DWORD dwLineBufferSize = 0; DWORD dwMaxLineSize = 0; CHAR* szLineBuffer = NULL; CHAR* szLine = NULL; CHAR* szValue = NULL; CHAR szScratch[512]; CHAR szMsgId[512]; CHAR acReadBuffer[PRIVATE_OPTIMAL_BUFFER_SIZE]; CHAR szPickupFilePath[MAX_PATH + 1]; CHAR szDateBuf [cMaxArpaDate]; DWORD dwBufferSize = 0; CHAR FileName[MAX_PATH + 1]; CHAR* lpStart = NULL; CHAR* lpXMarker = NULL; DWORD dwBytesToRewind = 0; LONG lOffset = 0; CAddr* Sender = NULL; PSMTP_IIS_SERVICE pService = NULL; IMailMsgRecipientsAdd* pIMsgRecips = NULL; IMailMsgRecipients* pIMsgOrigRecips = NULL; IMailMsgBind* pBindInterface = NULL; HRESULT hr = S_OK; DWORD dwTotalRecips = 0; SMTP_ALLOC_PARAMS AllocParams; BOOL fResult = FALSE; BOOL DeleteIMsg = TRUE;
TraceFunctEnterEx((LPARAM) this, "SMTP_DIRNOT::ProcessFile" );
_ASSERT(m_pInstance != NULL); _ASSERT(pIMsg); if (!pIMsg) { ErrorTrace((LPARAM)this, "Internal error: pIMsg is NULL."); SetLastError(ERROR_INVALID_PARAMETER); TraceFunctLeaveEx((LPARAM)this); return FALSE; }
// Compose the pickup path
hr = pIMsg->GetStringA(IMMPID_MP_PICKUP_FILE_NAME, sizeof(FileName), FileName); if (FAILED(hr)) { ErrorTrace((LPARAM)this, "MailQEntry->GetFileName() is NULL."); goto RetryPickup; }
hr = pIMsg->QueryInterface(IID_IMailMsgBind, (void **)&pBindInterface); if(FAILED(hr)) { ErrorTrace((LPARAM)this, "IMsg->QueryInterface(IID_IMailMsgBindATQ) failed."); goto RetryPickup; }
//Get recipient list
hr = pIMsg->QueryInterface(IID_IMailMsgRecipients, (void **) &pIMsgOrigRecips); if (FAILED(hr)) { ErrorTrace((LPARAM)this, "IMsg->QueryInterface(IID_IMailMsgRecipients) failed."); goto RetryPickup; }
hr = pIMsgOrigRecips->AllocNewList(&pIMsgRecips); if (FAILED(hr)) { ErrorTrace((LPARAM)this, "pIMsgOrigRecips->AllocNewList"); goto RetryPickup; }
lstrcpy(szPickupFilePath, QuerySmtpInstance()->GetMailPickupDir()); lstrcat(szPickupFilePath, FileName);
// Open the pickup file, if this failsas a sharing violation,
// we would like to retry it ...
hSource = pOpenPickupFile(szPickupFilePath); if (hSource == INVALID_HANDLE_VALUE) { //
// this is probably caused by the file still being written, wait a small amount of time
// we manage our IO compl. threads to ensure that there are enough, so this shouldn't be
// a big problem. It avoids the retry queue.
//
WaitForSingleObject(QuerySmtpInstance()->GetQStopEvent(), g_PickupWait); hSource = pOpenPickupFile(szPickupFilePath); if (hSource == INVALID_HANDLE_VALUE) { if (GetLastError() == ERROR_FILE_NOT_FOUND) { //delete MailQEntry;
ErrorTrace((LPARAM)this, "File %s is deleted from pickup dir", szPickupFilePath); goto RetryPickup; }
// Schedule pickup mail for retry ...
goto RetryPickup; } }
AllocParams.BindInterfacePtr = (PVOID) pBindInterface; AllocParams.IMsgPtr = (PVOID)pIMsg; AllocParams.hContent = NULL; AllocParams.pAtqClientContext = QuerySmtpInstance(); AllocParams.m_pNotify = NULL; fResult = QuerySmtpInstance()->AllocNewMessage (&AllocParams);
//Get the context and the atq context
hDest = AllocParams.hContent;
if((!fResult) || (hDest == NULL)) { goto RetryPickup; }
// Get the handle of the spooled file
_ASSERT(hDest != NULL);
//
// Winse:13699 and X5:174038
// Remove Dot stuffing based on the metabase key.
// Default behavior is that pickup directory messages are dot stuffed.
// If the metabase key DisablePickupDotStuff is set then the pickup directory
// messages should not be dot stuffed.
//
if( !SetDotStuffingOnWrites( hDest, !m_pInstance->DisablePickupDotStuff(), TRUE ) ) { ErrorTrace((LPARAM)this, "SetDotStuffingonWrites failed" ); goto RetryPickup; } // Spit a Received: line to the spooled file
GetArpaDate(szDateBuf); GetLocalTime(&SysTime);
// Allocate the needed line buffer
szLineBuffer = pGetLineBuffer(NULL, &dwLineBufferSize); if (!szLineBuffer) { // Schedule pickup mail for retry ...
ErrorTrace((LPARAM)this, "Unable to allocate line buffer (%u)", GetLastError()); goto RetryPickup; }
DebugTrace((LPARAM)this, "Allocated line buffer at %p, %u bytes", szLineBuffer, dwLineBufferSize);
// Set up the line pointers
szLine = szLineBuffer; dwMaxLineSize = dwLineBufferSize;
// Prefetch a buffer-full of text
dwBufferSize = PRIVATE_OPTIMAL_BUFFER_SIZE; if (!pFetchFileBuffer(hSource, acReadBuffer, &dwBufferSize)) { // Schedule pickup mail for retry ...
ErrorTrace((LPARAM)this, "Retrying because cannot fetch file buffer (%u)",GetLastError()); goto RetryPickup; }
m_pInstance->LockGenCrit(); wsprintf( szScratch, "Received: from mail pickup service by %s with Microsoft SMTPSVC;\r\n\t %s, %s\r\n", QuerySmtpInstance()->GetFQDomainName(), Daynames[SysTime.wDayOfWeek], szDateBuf); m_pInstance->UnLockGenCrit();
//We will copy out to the temp out buffer
//NK** : This is a safe assumption that this will not result in IO
#if 0
if ((SetFilePointer(hDest, 0, NULL, FILE_BEGIN) == 0xffffffff) || !WriteToSpooledFile(hDest, szScratch, DestWriteOffset)) #else
if (!WriteToSpooledFile(hDest, szScratch, DestWriteOffset)) #endif
goto RetryPickup;
lpStart = acReadBuffer; while (1) { HeaderFlags = 0;
// The X marker is used to mark the start of the
// non-X-headers if X-headers are used
if (fIsStartOfFile) lpXMarker = lpStart;
// This function will read a complete header line,
// into a single NULL-terminated string.
// Raid 181922 - The function no longer unfolds the line.
if (!(lpStart = pReadNextLineFromBuffer( hSource, acReadBuffer, &dwBufferSize, lpStart, &szLine, &dwMaxLineSize, &szLineBuffer, &dwLineBufferSize))) { // We failed a file operation, we will retry later
goto RetryPickup; }
// See if we are still in the header portion of the body
if (!IsHeader(szLine)) break;
// Get the set of headers that we know about.
ChompHeader(szLine, HeaderFlags); szValue = pGetValueFromHeader(szLine); if(!szValue) { continue; }
// Strip away the CRLF at the end of the value string
lOffset = lstrlen(szValue); if (lOffset >= 2) { if (((szValue[lOffset-2] == '\r') && (szValue[lOffset-1] == '\n')) || ((szValue[lOffset-2] == '\n') && (szValue[lOffset-1] == '\r')))
szValue[lOffset-2] = '\0'; }
hr = GetAndPersistRFC822Headers( szLine, szValue, pIMsg, fSeenRFC822FromAddress, fSeenRFC822ToAddress, fSeenRFC822BccAddress, fSeenRFC822CcAddress, fSeenRFC822Subject, fSeenRFC822SenderAddress, fSeenXPriority, fSeenContentType, fSetContentType ); if( FAILED( hr ) ) { ErrorTrace((LPARAM)this, "Retrying because cannot getAndPersistRFC822 headers(%x)", hr); goto RetryPickup; }
// Check for leading X-Sender and X-Receiver header lines ...
if (fIsStartOfFile) { if (HeaderFlags & H_X_SENDER) { // NOTE: if we have more than one sender,
// we always use the first one encountered
// Yes, we found an X-header
fIsXSenderRead = TRUE; } else if (HeaderFlags & H_X_RECEIVER) { // We don't check for sender here, since if we don't
// have one, we will barf downstream
// Yes, we found an X-header
fAreXRcptsRead = TRUE; } else { // We are done with our X-headers, now we have
// normal headers!
fIsStartOfFile = FALSE; } }
if (HeaderFlags & H_FROM) { fFromSeen = TRUE; }
if (HeaderFlags & H_RCPT) { fRcptSeen = TRUE; }
// Handle senders and recipients
if (!fIsSenderSeen && ((HeaderFlags & H_X_SENDER) || (HeaderFlags & H_FROM))) { // Selectively do so
if (fIsStartOfFile || !fIsXSenderRead) { char *Address = NULL; DWORD cbText = 0; DWORD dwlen = strlen(szValue); _ASSERT(dwlen < 512 );
Sender = CAddr::CreateAddress (szValue, FROMADDR); if (Sender) { Address = Sender->GetAddress(); if(!Sender->IsDomainOffset() && !ISNULLADDRESS(Address)) { CAddr * TempAddress = NULL; TempAddress = QuerySmtpInstance()->AppendLocalDomain (Sender); delete Sender; if (TempAddress) { Sender = TempAddress; Address = Sender->GetAddress(); TempAddress = NULL; } else { ErrorTrace((LPARAM) this,"Retrying because cannot to append local domain (%u)",GetLastError()); goto RetryPickup; } }
// Set the size of the FromName so that we can
// read it out of the queue file if we need it.
//cbText = lstrlen(Address) + 1;
//MailQEntry->SetFromNameSize (cbText);
//MailQEntry->SetSenderToStream(Address);
hr = pIMsg->PutStringA(IMMPID_MP_SENDER_ADDRESS_SMTP, Address); fIsSenderSeen = TRUE; } else { // Undo the flag
if (fIsXSenderRead) fIsXSenderRead = FALSE;
// If the sender is not invalid, it is likely to be a
// resource constraint, so we go and retry it
if (GetLastError() != ERROR_INVALID_DATA) { ErrorTrace((LPARAM)this, "Retrying because cannot allocate sender (%u)", GetLastError()); goto RetryPickup; } } } } else if ((HeaderFlags & H_X_RECEIVER) || (HeaderFlags & H_RCPT)) { // Selectively do so
if (fIsStartOfFile || !fAreXRcptsRead) { DebugTrace((LPARAM)szLine, "To: line read <%s>", szValue);
if (!CreateToList(szValue, pIMsgRecips, pIMsg)) { // If we're out of memory, we retry.
// If it is an invalid address, we remember the fact and
// remember to throw a copy into badmail
if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY) { ErrorTrace((LPARAM)this, "Retrying because cannot allocate recipient (%u)", GetLastError()); goto RetryPickup; } else fInvalidAddresses = TRUE; }
if (fIsStartOfFile) { // X-headers!
lpXMarker = lpStart; } } } else if (HeaderFlags & H_MID) { // Message-ID already exists
fMessageIdExists = TRUE; hr = pIMsg->PutStringA( IMMPID_MP_RFC822_MSG_ID , szValue ); if (FAILED(hr)) { ErrorTrace((LPARAM) this, "PutStringA of IMMPID_MP_RFC822_MSG_ID failed - hr 0x%08X", hr); goto RetryPickup; } } else if (HeaderFlags & H_DATE) { // Date line already exists
fDateExists = TRUE; } // 10/15/98 - MikeSwa Added supersedes functionality
else if (HeaderFlags & H_X_MSGGUID) { hr = pIMsg->PutStringA(IMMPID_MP_MSG_GUID, szValue); if (FAILED(hr)) { ErrorTrace((LPARAM) this, "PutStringA of MSG_GUID failed - hr 0x%08X", hr); goto RetryPickup; } } else if (HeaderFlags & H_X_SUPERSEDES_MSGGUID) { hr = pIMsg->PutStringA(IMMPID_MP_SUPERSEDES_MSG_GUID, szValue); if (FAILED(hr)) { ErrorTrace((LPARAM) this, "PutStringA of SUPERSEDES_MSG_GUID failed - hr 0x%08X", hr); goto RetryPickup; } } else if( HeaderFlags & H_X_ORIGINAL_ARRIVAL_TIME ) { fXOriginalArrivalTime = TRUE; hr = pIMsg->PutStringA(IMMPID_MP_ORIGINAL_ARRIVAL_TIME, szValue); if (FAILED(hr)) { ErrorTrace((LPARAM) this, "PutStringA of ORIGINAL_ARRIVAL_TIME failed - hr 0x%08X", hr); goto RetryPickup; } }
// If this is an ordinary header, we will dump this to
// the spooled file
if (!fIsStartOfFile) { // Non-x-header; dump the line!
lstrcat(szLine, acCrLf); if (!WriteToSpooledFile(hDest, szLine, DestWriteOffset)) { ErrorTrace((LPARAM)this, "Retrying because cannot write to file (%u)", GetLastError()); goto RetryPickup; } } }
hr = pIMsg->PutStringA(IMMPID_MP_HELO_DOMAIN, m_pInstance->GetDefaultDomain()); if(FAILED(hr)) { ErrorTrace((LPARAM)this, "Retrying because pIMsg->PutString(helo domain) failed (%x)", hr); goto RetryPickup;
}
hr = pIMsg->PutStringA(IMMPID_MP_CONNECTION_IP_ADDRESS, DIRNOT_IP_ADDRESS); if(FAILED(hr)) { ErrorTrace((LPARAM)this, "Retrying because pIMsg->PutString(IP address) failed (%x)", hr); goto RetryPickup;
}
hr = SetAvailableMailMsgProperties( pIMsg ); if(FAILED(hr)) { ErrorTrace((LPARAM)this, "Retrying because pIMsg->PutString( for all available props) failed (%x)", hr); goto RetryPickup;
} wsprintf( szScratch, "%s, %s", Daynames[SysTime.wDayOfWeek], szDateBuf);
hr = pIMsg->PutStringA(IMMPID_MP_ARRIVAL_TIME, szScratch); if( FAILED( hr ) ) { ErrorTrace((LPARAM)this,"Retrying because cannot putString IMPPID_MP_ARRIVAL_TIME (%x)", hr); goto RetryPickup; }
// Now, before we go on and waste time on copying the rest of the message, etc.
// we want to make sure we saw the sender and at least one valid recipient.
// If not, we will just move the message to bad mail and be done with it.
hr = pIMsgRecips->Count(&dwTotalRecips); if (!fIsSenderSeen || FAILED(hr) || (dwTotalRecips == 0)) { // If no recipients but sender is seen, we must not leak the sender
// object
if (fIsSenderSeen) { delete Sender; Sender = NULL; }
_VERIFY( pFreeLineBuffer(szLineBuffer) ); szLineBuffer = NULL; if (hSource != INVALID_HANDLE_VALUE) { _VERIFY( CloseHandle(hSource) ); hSource = INVALID_HANDLE_VALUE; }
if(FAILED(hr)) { goto RetryPickup; } else { // No choice but move to bad mail directory
if( !QuerySmtpInstance()->MoveToBadMail( pIMsg, FALSE, FileName, QuerySmtpInstance()->GetMailPickupDir()) ) { if (!DeleteFile(szPickupFilePath)) { DWORD dwError = GetLastError(); ErrorTrace((LPARAM)this, "Error deleting file %s (%u)", szPickupFilePath, dwError); } } goto RetryPickup; } }
hr = pIMsg->PutDWORD( IMMPID_MP_NUM_RECIPIENTS, dwTotalRecips ); if( FAILED( hr ) ) { ErrorTrace((LPARAM)this,"Retrying because cannot putDWORD IMPPID_MP_NUM_RECIPIENTS (%x)", hr); goto RetryPickup; }
// if we did not read a From Line, create a From: line from the xSender.
// if we did not read a To, CC, BCC line, create a To: line from the xReceiver.
if (!fFromSeen) { char *Address = NULL; char szUserName[MAX_INTERNET_NAME + 5];
Address = Sender->GetAddress(); if (!Address) { _VERIFY( pFreeLineBuffer(szLineBuffer) ); szLineBuffer = NULL; if (hSource != INVALID_HANDLE_VALUE) { _VERIFY( CloseHandle(hSource) ); hSource = INVALID_HANDLE_VALUE; }
// No choice but move to bad mail directory
if( !QuerySmtpInstance()->MoveToBadMail( pIMsg, FALSE, FileName, QuerySmtpInstance()->GetMailPickupDir()) ) { if (!DeleteFile(szPickupFilePath)) { DWORD dwError = GetLastError(); ErrorTrace((LPARAM)this, "Error deleting file %s (%u)", szPickupFilePath, dwError); } } goto RetryPickup; }
wsprintf(szUserName, "From: %s\r\n", Address); if (!WriteToSpooledFile(hDest, szUserName, DestWriteOffset)) { ErrorTrace((LPARAM)this,"Retrying because cannot write to file (%u)", GetLastError()); goto RetryPickup; } }
if (!fRcptSeen) { //
// fix for bug: 78275
// add an emty Bcc line:
//
static char * endRcpt = "Bcc:\r\n";
if (!WriteToSpooledFile(hDest, endRcpt, DestWriteOffset)) { ErrorTrace((LPARAM)this, "Retrying because cannot write to file (%u)", GetLastError()); goto RetryPickup; } }
if (fIsSenderSeen) { delete Sender; }
// We still have to dump the current line to the spool
// file, then we copy the rest over.
// Since we use buffered read, the current file position
// really doesn't mean much. So we calculate how many bytes
// we have to rewind
dwBytesToRewind = dwBufferSize - (DWORD)(lpStart - acReadBuffer);
// Negative, since we are going backwards
_ASSERT(dwBytesToRewind <= PRIVATE_OPTIMAL_BUFFER_SIZE); lOffset = -(long)dwBytesToRewind; if (SetFilePointer(hSource, lOffset, NULL, FILE_CURRENT) == 0xffffffff) { ErrorTrace((LPARAM)this, "Retrying because cannot move file pointer (%u)", GetLastError()); goto RetryPickup; }
// See if we have to add a Date: or Message-ID: line
if (!fMessageIdExists) { char MessageId[MAX_PATH];
MessageId[0] = '\0';
GenerateMessageId (MessageId, sizeof(MessageId)); m_pInstance->LockGenCrit(); wsprintf( szMsgId, "<%s%8.8x@%s>", MessageId, GetIncreasingMsgId(),QuerySmtpInstance()->GetFQDomainName()); m_pInstance->UnLockGenCrit(); wsprintf(szScratch, "Message-ID: %s\r\n",szMsgId );
hr = pIMsg->PutStringA( IMMPID_MP_RFC822_MSG_ID, szMsgId ); if( FAILED( hr ) ) { ErrorTrace((LPARAM)this, "Retrying because Put string of IMMPID_MP_RFC822 failed (%x)", hr ); goto RetryPickup; }
if (!WriteToSpooledFile(hDest, szScratch, DestWriteOffset)) { ErrorTrace((LPARAM)this, "Retrying because cannot write to file (%u)", GetLastError()); goto RetryPickup; } }
//
// see if we have to add Original Arrival time
//
if (!fXOriginalArrivalTime ) { char szOriginalArrivalTime[64];
szOriginalArrivalTime[0] = '\0';
GetSysAndFileTimeAsString( szOriginalArrivalTime ); wsprintf(szScratch, "X-OriginalArrivalTime: %s\r\n",szOriginalArrivalTime );
hr = pIMsg->PutStringA( IMMPID_MP_ORIGINAL_ARRIVAL_TIME, szOriginalArrivalTime ); if( FAILED( hr ) ) { ErrorTrace((LPARAM)this, "Retrying because Put string of IMMPID_MP_ORIGINAL_ARRIVAL_TIME failed (%x)", hr ); goto RetryPickup; }
if (!WriteToSpooledFile(hDest, szScratch, DestWriteOffset)) { ErrorTrace((LPARAM)this, "Retrying because cannot write to file (%u)", GetLastError()); goto RetryPickup; } }
//
// see if we have to add the date
//
if (!fDateExists) { lstrcpy(szScratch, "Date: "); lstrcat(szScratch, szDateBuf); lstrcat(szScratch, acCrLf); if (!WriteToSpooledFile(hDest, szScratch, DestWriteOffset)) { ErrorTrace((LPARAM)this, "Retrying because cannot write to file (%u)", GetLastError()); goto RetryPickup; } }
// Write out the currently buffered line, then copy the
// pickup file to the spooler
if (!WriteToSpooledFile(hDest, szLine, DestWriteOffset) || !CopyRestOfMessage(hSource, hDest, DestWriteOffset)) { ErrorTrace((LPARAM)this, "Retrying because cannot write to file (%u)", GetLastError()); goto RetryPickup; }
// We will no longer read headers at this point, so we free our
// line buffer here ...
DebugTrace((LPARAM)this, "Freeing line buffer at %p, %u bytes", szLineBuffer, dwLineBufferSize); _VERIFY( pFreeLineBuffer(szLineBuffer) ); szLineBuffer = NULL;
// Get the size of the spooled file before we close it
DWORD dwFileSizeHigh; dwPickupFileSize = GetFileSizeFromContext(hDest, &dwFileSizeHigh); _ASSERT(dwFileSizeHigh == 0); // why??
if (dwPickupFileSize == 0xffffffff) { dwPickupFileSize = 0; }
pIMsg->PutDWORD( IMMPID_MP_MSG_SIZE_HINT, dwPickupFileSize );
// Now, we see if there are any invalid addresses; if so, we will
// throw a copy og the original message into the badmail directory
if (fInvalidAddresses) { // Close the pickup file
if (hSource != INVALID_HANDLE_VALUE) { _VERIFY( CloseHandle(hSource) ); hSource = INVALID_HANDLE_VALUE; }
// Move it to badmail instead of deleting it
DebugTrace((LPARAM)this, "Saving a copy in badmail due to bad recipients"); // No choice but move to bad mail directory
if( !QuerySmtpInstance()->MoveToBadMail( pIMsg, FALSE, FileName, QuerySmtpInstance()->GetMailPickupDir() ) ) { if (!DeleteFile(szPickupFilePath)) { DWORD dwError = GetLastError(); ErrorTrace((LPARAM)this, "Error deleting file %s (%u)", szPickupFilePath, dwError); } } } else { // Delete the file
if (!DeleteFile(szPickupFilePath)) { DWORD dwError = GetLastError(); ErrorTrace((LPARAM)this, "Error deleting file %s (%u)", szPickupFilePath, dwError); } // Close the pickup file
if (hSource != INVALID_HANDLE_VALUE) { _VERIFY( CloseHandle(hSource) ); hSource = INVALID_HANDLE_VALUE; } }
//
// put the file into the local queue.
//
pService = (PSMTP_IIS_SERVICE) g_pInetSvc;
hr = pIMsgOrigRecips->WriteList(pIMsgRecips); if(FAILED(hr)) { ErrorTrace((LPARAM)this, "Retrying because pIMsgOrigRecips->WriteList failed (%x)", hr); goto RetryPickup; }
//
// we scan for dots & strip them away while storing, so set these two properties
// so we can get the dot stuffed context while sending it out.
//
pIMsg->PutDWORD( IMMPID_MP_SCANNED_FOR_CRLF_DOT_CRLF, TRUE ); pIMsg->PutDWORD( IMMPID_MP_FOUND_EMBEDDED_CRLF_DOT_CRLF, TRUE );
CompleteDotStuffingOnWrites( hDest, TRUE );
hr = pIMsg->Commit(NULL); if(FAILED(hr)) { ErrorTrace((LPARAM)this, "Retrying because pIMsg->Commit(NULL) failed (%u)", hr); goto RetryPickup; }
pIMsgRecips->Release(); pIMsgOrigRecips->Release(); pIMsgRecips = NULL; pIMsgOrigRecips = NULL;
if(m_pInstance->InsertIntoQueue(pIMsg)) { DeleteIMsg = FALSE; // Up the msgs received and avg bytes per message counters
ADD_BIGCOUNTER(QuerySmtpInstance(), BytesRcvdMsg, (dwPickupFileSize)); BUMP_COUNTER(QuerySmtpInstance(), NumMsgsForwarded); }
RetryPickup:
IMailMsgQueueMgmt * pMgmt = NULL;
if(pBindInterface) { pBindInterface->Release(); }
if(pIMsgRecips) { pIMsgRecips->Release(); pIMsgRecips = NULL; }
if(pIMsgOrigRecips) { pIMsgOrigRecips->Release(); pIMsgOrigRecips = NULL; }
if(DeleteIMsg) { hr = pIMsg->QueryInterface(IID_IMailMsgQueueMgmt, (void **)&pMgmt); if(!FAILED(hr)) { pMgmt->Delete(NULL); pMgmt->Release(); } }
if(pIMsg) { pIMsg->Release(); pIMsg = NULL; }
// Retry message
if (szLineBuffer) { _VERIFY( pFreeLineBuffer(szLineBuffer) ); szLineBuffer = NULL; }
if (hSource != INVALID_HANDLE_VALUE) { _VERIFY( CloseHandle( hSource ) ); hSource = INVALID_HANDLE_VALUE; }
TraceFunctLeaveEx((LPARAM)this); return FALSE; }
//////////////////////////////////////////////////////////////////////////////
HRESULT SMTP_DIRNOT::GetAndPersistRFC822Headers( char* InputLine, char* pszValueBuf, IMailMsgProperties* pIMsg, BOOL & fSeenRFC822FromAddress, BOOL & fSeenRFC822ToAddress, BOOL & fSeenRFC822BccAddress, BOOL & fSeenRFC822CcAddress, BOOL & fSeenRFC822Subject, BOOL & fSeenRFC822SenderAddress, BOOL & fSeenXPriority, BOOL & fSeenContentType, BOOL & fSetContentType ) { HRESULT hr = S_OK; //
// get the Subject: & persist it
//
if( !fSeenRFC822Subject && ( !strncasecmp( InputLine, "Subject:", strlen("Subject:") ) || !strncasecmp( InputLine, "Subject :", strlen("Subject :") ) ) ) { fSeenRFC822Subject = TRUE; if( pszValueBuf ) { if( FAILED( hr = pIMsg->PutStringA( IMMPID_MP_RFC822_MSG_SUBJECT, pszValueBuf ) ) ) { return( hr ); } } }
//
// get the To: address & persist it
//
if( !fSeenRFC822ToAddress && ( !strncasecmp( InputLine, "To:", strlen("To:") ) || !strncasecmp( InputLine, "To :", strlen("To :") ) ) ) { fSeenRFC822ToAddress = TRUE; if( pszValueBuf ) { if( FAILED( hr = pIMsg->PutStringA( IMMPID_MP_RFC822_TO_ADDRESS, pszValueBuf ) ) ) { return( hr ); } } }
//
// get the Cc: address & persist it
//
if( !fSeenRFC822CcAddress && ( !strncasecmp( InputLine, "Cc:", strlen("Cc:") ) || !strncasecmp( InputLine, "Cc :", strlen("Cc :") ) ) ) { fSeenRFC822CcAddress = TRUE; if( pszValueBuf ) { if( FAILED( hr = pIMsg->PutStringA( IMMPID_MP_RFC822_CC_ADDRESS, pszValueBuf ) ) ) { return( hr ); } } }
//
// get the Bcc: address & persist it
//
if( !fSeenRFC822BccAddress && ( !strncasecmp( InputLine, "Bcc:", strlen("Bcc:") ) || !strncasecmp( InputLine, "Bcc :", strlen("Bcc :") ) ) ) { fSeenRFC822BccAddress = TRUE; if( pszValueBuf ) { if( FAILED( hr = pIMsg->PutStringA( IMMPID_MP_RFC822_BCC_ADDRESS, pszValueBuf ) ) ) { return( hr ); } } }
//
// get the From: address & persist it
//
if( !fSeenRFC822FromAddress && ( !strncasecmp( InputLine, "From:", strlen("From:") ) || !strncasecmp( InputLine, "From :", strlen("From :") ) ) ) { fSeenRFC822FromAddress = TRUE; if( pszValueBuf ) { if( FAILED( hr = pIMsg->PutStringA( IMMPID_MP_RFC822_FROM_ADDRESS, pszValueBuf ) ) ) { return( hr ); }
}
char szSmtpFromAddress[MAX_INTERNET_NAME + 6] = "smtp:"; char *pszDomainOffset; DWORD cAddr; if (CAddr::ExtractCleanEmailName(szSmtpFromAddress + 5, &pszDomainOffset, &cAddr, pszValueBuf)) { if (FAILED(hr = pIMsg->PutStringA(IMMPID_MP_FROM_ADDRESS, szSmtpFromAddress))) { return hr; } } }
if( !fSeenRFC822SenderAddress && ( !strncasecmp( InputLine, "Sender:", strlen("Sender:") ) || !strncasecmp( InputLine, "Sender :", strlen("Sender :") ) ) ) { fSeenRFC822SenderAddress = TRUE; if( pszValueBuf ) { char szSmtpSenderAddress[MAX_INTERNET_NAME + 6] = "smtp:"; char *pszDomainOffset; DWORD cAddr; if (CAddr::ExtractCleanEmailName(szSmtpSenderAddress + 5, &pszDomainOffset, &cAddr, pszValueBuf)) { if (FAILED(hr = pIMsg->PutStringA(IMMPID_MP_SENDER_ADDRESS, szSmtpSenderAddress))) { return hr; } } } }
//
// get the X-Priority & persist it
//
if( !fSeenXPriority && ( !strncasecmp( InputLine, "X-Priority:", strlen("X-Priority:") ) || !strncasecmp( InputLine, "X-Priority :", strlen("X-Priority :") ) ) ) { fSeenXPriority = TRUE; if( pszValueBuf ) { DWORD dwPri = (DWORD)atoi( pszValueBuf ); if( FAILED( hr = pIMsg->PutDWORD( IMMPID_MP_X_PRIORITY, dwPri ) ) ) { return( hr ); } } }
//
// get the content type & persist it
//
if( !fSeenContentType && ( !strncasecmp( InputLine, "Content-Type:", strlen("Content-Type:") ) || !strncasecmp( InputLine, "Content-Type :", strlen("Content-Type :") ) ) ) { fSeenContentType = TRUE; fSetContentType = TRUE; DWORD dwContentType = 0;
if( pszValueBuf ) { if( !strncasecmp( pszValueBuf, "multipart/signed", strlen("multipart/signed") ) ) { dwContentType = 1; } else if( !strncasecmp( pszValueBuf, "application/x-pkcs7-mime", strlen("application/x-pkcs7-mime") ) || !strncasecmp( pszValueBuf, "application/pkcs7-mime", strlen("application/pkcs7-mime") ) ) { dwContentType = 2; }
if( FAILED( hr = pIMsg->PutStringA( IMMPID_MP_CONTENT_TYPE, pszValueBuf ) ) ) { return( hr ); } }
if( FAILED( hr = pIMsg->PutDWORD( IMMPID_MP_ENCRYPTION_TYPE, dwContentType ) ) ) { return( hr ); } }
if( !fSetContentType ) { fSetContentType = TRUE; if( FAILED( hr = pIMsg->PutDWORD( IMMPID_MP_ENCRYPTION_TYPE, 0 ) ) ) { return( hr ); } }
return( hr ); }
//////////////////////////////////////////////////////////////////////////////
HRESULT SMTP_DIRNOT::SetAvailableMailMsgProperties( IMailMsgProperties *pIMsg ) { //set IPaddress that is already available
HRESULT hr = S_OK; hr = pIMsg->PutStringA(IMMPID_MP_CONNECTION_SERVER_IP_ADDRESS, DIRNOT_IP_ADDRESS); if(FAILED(hr)) { return( hr ); } hr = pIMsg->PutStringA(IMMPID_MP_SERVER_NAME, g_ComputerName); if(FAILED(hr)) { return( hr ); } hr = pIMsg->PutStringA(IMMPID_MP_SERVER_VERSION, g_VersionString); if(FAILED(hr)) { return( hr ); }
return( hr ); }
//////////////////////////////////////////////////////////////////////////////
void SMTP_DIRNOT::CreateLocalDeliveryThread (void) { TraceFunctEnterEx((LPARAM)this, "SMTP_DIRNOT::CreateLocalDeliveryThread"); //
// Add a delivery thread with the ATQPorts Overlapped structure. For the dir notifications,
// our overlapped structure is used... this is how we tell the difference in Process Client.
//
IncPendingIoCount(); if(!AtqPostCompletionStatus(QueryAtqContext(), 50)) { DecPendingIoCount(); ErrorTrace((LPARAM) this,"AtqPostCompletionStatus() failed with error %d", GetLastError()); } TraceFunctLeaveEx((LPARAM)this); }
//////////////////////////////////////////////////////////////////////////////
BOOL SMTP_DIRNOT::PendDirChangeNotification (void) { CBuffer * pBuffer = NULL; DWORD nBytes = 0; DWORD Error = 0;
TraceFunctEnterEx((LPARAM) this, "SMTP_DIRNOT::PendDirChangeNotification" );
_ASSERT(m_pInstance != NULL); _ASSERT(m_hDir != INVALID_HANDLE_VALUE);
//get a new buffer
pBuffer = new CBuffer(); if(pBuffer == NULL) { ErrorTrace((LPARAM) this, "pBuffer = new CBuffer()failed . err: %u", ERROR_NOT_ENOUGH_MEMORY); SetLastError (ERROR_NOT_ENOUGH_MEMORY); TraceFunctLeaveEx((LPARAM)this); return FALSE; }
if(pBuffer->GetData() == NULL) { ErrorTrace((LPARAM) this, "pBuffer = new CBuffer()->GetData failed . err: %u", ERROR_NOT_ENOUGH_MEMORY); delete pBuffer; SetLastError (ERROR_NOT_ENOUGH_MEMORY); TraceFunctLeaveEx((LPARAM)this); return FALSE; }
QuerySmtpInstance()->IncCBufferObjs ();
IncPendingIoCount(); IncDirChangeIoCount();
if(!AtqReadDirChanges(QueryAtqContext(),pBuffer->GetData(),QuerySmtpInstance()->GetDirBufferSize(), FALSE,FILE_NOTIFY_CHANGE_FILE_NAME, (OVERLAPPED *) &pBuffer->m_Overlapped.SeoOverlapped.Overlapped)) { Error = GetLastError(); ErrorTrace((LPARAM) this, "ReadDirectoryChangesW failed . err: %u", Error); delete pBuffer; QuerySmtpInstance()->DecCBufferObjs (); //decrement over all I/O count
DecPendingIoCount(); DecDirChangeIoCount(); TraceFunctLeaveEx((LPARAM)this); return FALSE;
}
TraceFunctLeaveEx((LPARAM)this); return TRUE; }
BOOL SMTP_DIRNOT::ProcessDirNotification( IN DWORD InputBufferLen, IN DWORD dwCompletionStatus, IN OUT OVERLAPPED * lpo) { CHAR FileName[MAX_PATH + 1]; PFILE_NOTIFY_INFORMATION Info = NULL; DWORD FileNameLength = 0; DWORD ServerState; CBuffer* pBuffer = ((DIRNOT_OVERLAPPED*)lpo)->pBuffer; HRESULT hr;
TraceFunctEnterEx((LPARAM)this, "SMTP_DIRNOT::ProcessDirNotification" );
_ASSERT(m_pInstance != NULL);
//
// pend a new notification to replace the one we are currently using... there will still be
// a couple outstanding unless we are shutting down.
//
if (!PendDirChangeNotification()) ErrorTrace((LPARAM)this, "PendDirChangeNotification() failed");
if (!QuerySmtpInstance()->GetAcceptConnBool()) { delete pBuffer; QuerySmtpInstance()->DecCBufferObjs (); TraceFunctLeaveEx((LPARAM)this); return TRUE; }
if(dwCompletionStatus != NO_ERROR) { ErrorTrace((LPARAM) this, "SMTP_DIRNOT::ProcessDirNotification received error %d", dwCompletionStatus); delete pBuffer; QuerySmtpInstance()->DecCBufferObjs (); TraceFunctLeaveEx((LPARAM)this); return TRUE; }
ServerState = QuerySmtpInstance()->QueryServerState( );
//if we are not running, just delete
if( (ServerState == MD_SERVER_STATE_STOPPED ) || (ServerState == MD_SERVER_STATE_INVALID)) { delete pBuffer; QuerySmtpInstance()->DecCBufferObjs (); TraceFunctLeaveEx((LPARAM)this); return TRUE; }
if(InputBufferLen) { Info = (PFILE_NOTIFY_INFORMATION) pBuffer->GetData(); while (1) { //we only care about files added to this
//directory
//if(Info->Action == FILE_ACTION_MODIFIED)
if(Info->Action == FILE_ACTION_ADDED) { IMailMsgProperties *pIMsg = NULL;
//convert the name to ascii
FileNameLength = WideCharToMultiByte(CP_ACP, 0, &Info->FileName[0], Info->FileNameLength, FileName, sizeof(FileName), NULL, NULL); FileName[FileNameLength/2] = '\0'; DebugTrace((LPARAM) this, "File %s was detected", FileName);
hr = CoCreateInstance(CLSID_MsgImp, NULL, CLSCTX_INPROC_SERVER, IID_IMailMsgProperties, (LPVOID *)&pIMsg);
// Next, check if we are over the inbound cutoff limit. If so, we will release the message
// and not proceed.
if (SUCCEEDED(hr)) { DWORD dwCreationFlags; hr = pIMsg->GetDWORD( IMMPID_MPV_MESSAGE_CREATION_FLAGS, &dwCreationFlags); if (FAILED(hr) || (dwCreationFlags & MPV_INBOUND_CUTOFF_EXCEEDED)) { // If we fail to get this property of if the inbound cutoff
// exceeded flag is set, discard the message and return failure
if (SUCCEEDED(hr)) { DebugTrace((LPARAM)this, "Failing because inbound cutoff reached"); hr = E_OUTOFMEMORY; } pIMsg->Release(); pIMsg = NULL; } }
if( (pIMsg == NULL) || FAILED(hr)) { // We are out of resources, there is absolutely nothing
// we can do: can't NDR, can't retry ...
// Just go on with the next mail ...
ErrorTrace((LPARAM) this, "new MAILQ_ENTRY failed for file: %s", FileName);
// will be mail left in pickup. queue a findfirst to take care of it.
SetDelayedFindNotification(TRUE); IncPendingIoCount (); AtqContextSetInfo(QueryAtqContext(), ATQ_INFO_TIMEOUT, TIMEOUT_INTERVAL); //retry after a while
ErrorTrace((LPARAM)this, "Failed to create message will retry later."); goto Exit; } else { pIMsg->PutStringA(IMMPID_MP_PICKUP_FILE_NAME, FileName); if(!ProcessFile(pIMsg)) { // will be mail left in pickup. queue a findfirst to take care of it.
SetDelayedFindNotification(TRUE); } } }
if(Info->NextEntryOffset == 0) { DebugTrace((LPARAM) this, "No more entries in this notification"); break; }
//get the next entry in the list to process
Info = (PFILE_NOTIFY_INFORMATION)( (PCHAR)Info + Info->NextEntryOffset); }
if (GetDelayedFindNotification()) { DebugTrace((LPARAM) this, "Doing FindFirstFile because max mail objects was hit in notification"); DoFindFirstFile(); }
} else { DebugTrace((LPARAM) this, "Doing FindFirstFile because InputBufferLen == 0"); DoFindFirstFile(); }
Exit: if(pBuffer) { delete pBuffer; pBuffer = NULL; QuerySmtpInstance()->DecCBufferObjs (); }
TraceFunctLeaveEx((LPARAM)this); return TRUE; }
/*++
Name : SMTP_DIRNOT::ProcessClient
Description:
Main function for this class. Processes the connection based on current state of the connection. It may invoke or be invoked by ATQ functions.
Arguments:
cbWritten count of bytes written
dwCompletionStatus Error Code for last IO operation
lpo Overlapped stucture
Returns:
TRUE when processing is incomplete. FALSE when the connection is completely processed and this object may be deleted.
--*/ BOOL SMTP_DIRNOT::ProcessClient( IN DWORD InputBufferLen, IN DWORD dwCompletionStatus, IN OUT OVERLAPPED * lpo) { PFILE_NOTIFY_INFORMATION Info = NULL; DWORD FileNameLength = 0; BOOL fRet; PSMTP_IIS_SERVICE pService;
TraceFunctEnterEx((LPARAM) this, "SMTP_DIRNOT::ProcessClient" );
_ASSERT(m_pInstance != NULL);
//
// increment the number of threads processing this client
//
IncThreadCount();
if(!QuerySmtpInstance()->IsShuttingDown()) {
//
// dhowell - if a gilbraltar thread, and size > 0 then it is a delivery thread we have created
// the SMTP_CONNECTION code will ensure that only the correct number run.
//
if (lpo == &(QueryAtqContext()->Overlapped) || dwCompletionStatus == ERROR_SEM_TIMEOUT) { if(InputBufferLen == 0) { DebugTrace( (LPARAM)this,"FindFirst called internally.");
//reset timeout to infinity in anticipation of having memory; DoFindFirstFile
//will set it to TIMEOUT_INTERVAL if it runs out of memory
AtqContextSetInfo(QueryAtqContext(), ATQ_INFO_TIMEOUT, INFINITE);
DoFindFirstFile();
//since this was a timeout completion, the atq context has been "disabled" from
//further IO processing. Restart the timeout processing.
AtqContextSetInfo(QueryAtqContext(), ATQ_INFO_RESUME_IO, 1); } } //
// A legit notification .
//
else { DecDirChangeIoCount(); DebugTrace( (LPARAM)this,"ReadDirectoryChange Notification"); fRet = ProcessDirNotification(InputBufferLen, dwCompletionStatus, lpo); } } else { if (lpo != &(QueryAtqContext()->Overlapped)) { //just cleanup up shop if we are shutting down.
CBuffer* pBuffer = ((DIRNOT_OVERLAPPED*)lpo)->pBuffer; delete pBuffer; pBuffer = NULL; QuerySmtpInstance()->DecCBufferObjs (); DecDirChangeIoCount(); }
//
// only applies to shutdown case
// these will not be decremented via SMTP_CONNECTION::ProcessMailQIO so we have to do it here.
//
if ((lpo == &(QueryAtqContext()->Overlapped)) && InputBufferLen) { pService = (PSMTP_IIS_SERVICE) g_pInetSvc;
QuerySmtpInstance()->DecRoutingThreads(); pService->DecSystemRoutingThreads(); } }
//
// decrement the number of threads processing this client
//
DecThreadCount();
if ( DecPendingIoCount() == 0 ) { DebugTrace( (LPARAM)this,"SMTP_DIRNOT::ProcessClient() shutting down - Pending IOs: %d",m_cPendingIoCount); DebugTrace( (LPARAM)this,"SMTP_DIRNOT::ProcessClient() shutting down - ActiveThreads: %d",m_cActiveThreads); _ASSERT( m_cActiveThreads == 0 ); fRet = FALSE; } else { DebugTrace( (LPARAM)this,"SMTP_DIRNOT::ProcessClient() - Pending IOs: %d",m_cPendingIoCount); DebugTrace( (LPARAM)this,"SMTP_DIRNOT::ProcessClient() - ActiveThreads: %d",m_cActiveThreads); fRet = TRUE; }
TraceFunctLeaveEx((LPARAM)this); return fRet; }
/*++
Name : ReadDirectoryCompletion
Description:
Handles a completed I/O for ReadDirectoryChanges
Arguments:
pvContext: the context pointer specified in the initial IO cbWritten: the number of bytes sent dwCompletionStatus: the status of the completion (usually NO_ERROR) lpo: the overlapped structure associated with the IO
Returns:
nothing.
--*/ VOID SMTP_DIRNOT::ReadDirectoryCompletion(PVOID pvContext, DWORD cbWritten, DWORD dwCompletionStatus, OVERLAPPED * lpo) { BOOL WasProcessed; SMTP_DIRNOT *pCC = (SMTP_DIRNOT *) pvContext;
TraceFunctEnterEx((LPARAM) pCC, "SMTP_DIRNOT::ReadDirectoryCompletion");
_ASSERT(pCC); _ASSERT(pCC->IsValid()); _ASSERT(pCC->QuerySmtpInstance() != NULL);
if (dwCompletionStatus != NO_ERROR && dwCompletionStatus != ERROR_SEM_TIMEOUT) ErrorTrace((LPARAM)pCC, "Error in Atq operation: %d\r\n", dwCompletionStatus);
WasProcessed = pCC->ProcessClient(cbWritten, dwCompletionStatus, lpo); if(!WasProcessed) { SetEvent(pCC->QuerySmtpInstance()->GetDirnotStopHandle()); }
TraceFunctLeaveEx((LPARAM) pCC); }
|