520 lines
15 KiB
C++
520 lines
15 KiB
C++
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name :
|
|
|
|
buildq.cxx
|
|
|
|
Abstract:
|
|
Builds the initial queue during service startup
|
|
|
|
Author:
|
|
|
|
KeithLau 10/9/96
|
|
|
|
Project:
|
|
|
|
SMTP Server DLL
|
|
|
|
Functions Exported:
|
|
|
|
Revision History:
|
|
|
|
dhowell 26/5/97 Added MCIS to K2 upgrade... rewrite envelope if required logic.
|
|
|
|
|
|
--*/
|
|
|
|
|
|
/************************************************************
|
|
* Include Headers
|
|
************************************************************/
|
|
#define INCL_INETSRV_INCS
|
|
#include "smtpinc.h"
|
|
#include "buildq.hxx"
|
|
#include "headers.hxx"
|
|
//#include "pkuprtyq.hxx"
|
|
#include "timeconv.h"
|
|
|
|
#define MAX_INITIAL_THREADS 8
|
|
|
|
|
|
/************************************************************
|
|
* Threadprocs
|
|
************************************************************/
|
|
|
|
//
|
|
// Threadproc which builds up the initial queue of messages
|
|
//
|
|
DWORD BuildInitialQueueProc(void *lpThis);
|
|
|
|
//
|
|
// Threadproc which delivers the queued messages
|
|
//
|
|
VOID ProcessInitialQueueObjects(PVOID pvContext,
|
|
DWORD cbWritten,
|
|
DWORD dwCompletionStatus,
|
|
OVERLAPPED *lpo);
|
|
|
|
/************************************************************
|
|
* Globals
|
|
************************************************************/
|
|
|
|
/************************************************************
|
|
* Implementation of SMTP_BUILDQ class
|
|
************************************************************/
|
|
|
|
/*++
|
|
|
|
Name:
|
|
|
|
SMTP_BUILDQ::SMTP_BUILDQ
|
|
|
|
Constructs a new SMTP_BUILDQ object
|
|
|
|
--*/
|
|
SMTP_BUILDQ::SMTP_BUILDQ(SMTP_SERVER_INSTANCE * pInst)
|
|
{
|
|
TraceFunctEnterEx((LPARAM)this, "SMTP_BUILDQ::SMTP_BUILDQ");
|
|
|
|
m_hAtq = INVALID_HANDLE_VALUE;
|
|
m_pAtqContext = NULL;
|
|
m_cPendingIoCount = 0;
|
|
m_cActiveThreads = 0;
|
|
m_Entries = 0;
|
|
m_Signature = SMTP_BUILDQ_SIGNATURE_VALID;
|
|
m_pInstance = pInst;
|
|
|
|
InitializeListHead(&m_InitalQListHead);
|
|
InitializeCriticalSection (&m_CritSec);
|
|
|
|
TraceFunctLeaveEx((LPARAM)this);
|
|
}
|
|
|
|
SMTP_BUILDQ::~SMTP_BUILDQ(void)
|
|
{
|
|
PATQ_CONTEXT pAtqContext = NULL;
|
|
|
|
TraceFunctEnterEx((LPARAM)this, "SMTP_BUILDQ::~SMTP_BUILDQ");
|
|
|
|
_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 );
|
|
}
|
|
|
|
// Close the directory handle if for some reason it is
|
|
// not already closed
|
|
if (m_hAtq != INVALID_HANDLE_VALUE)
|
|
{
|
|
_VERIFY(CloseHandle(m_hAtq));
|
|
m_hAtq = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
// Invalidate the signature. since this connection is trashed.
|
|
m_Signature = SMTP_BUILDQ_SIGNATURE_FREE;
|
|
|
|
DeleteCriticalSection (&m_CritSec);
|
|
|
|
TraceFunctLeaveEx((LPARAM)this);
|
|
}
|
|
|
|
BOOL SMTP_BUILDQ::InitializeObject(ATQ_COMPLETION pfnCompletion)
|
|
{
|
|
//PATQ_CONT pContext;
|
|
BOOL fReturn = FALSE;
|
|
HANDLE hStop;
|
|
|
|
TraceFunctEnterEx((LPARAM)this, "SMTP_BUILDQ::InitializeObject");
|
|
|
|
// Open the queue directory, this is just to keep AtqAddAsyncHandle happy
|
|
m_hAtq = CreateFile(QuerySmtpInstance()->GetMailQueueDir(),
|
|
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_hAtq == INVALID_HANDLE_VALUE)
|
|
{
|
|
ErrorTrace((LPARAM)this,
|
|
"CreateFile on %s failed with error %d ",
|
|
QuerySmtpInstance()->GetMailQueueDir(),
|
|
GetLastError());
|
|
goto ExitFunc;
|
|
}
|
|
|
|
// Create an event to fire when we stop
|
|
hStop = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
if(hStop == NULL)
|
|
{
|
|
ErrorTrace((LPARAM)this,
|
|
"CreateEvent() for hStop failed with error %d ",
|
|
GetLastError());
|
|
goto ExitFunc;
|
|
}
|
|
|
|
QuerySmtpInstance()->SetBuildQStopHandle(hStop);
|
|
|
|
// Add it to our Gibraltar interface
|
|
if (!AtqAddAsyncHandle(&m_pAtqContext, NULL, this, pfnCompletion,
|
|
INFINITE, m_hAtq))
|
|
{
|
|
ErrorTrace((LPARAM)this,
|
|
"AtqAddAsyncHandle failed with error %d ",
|
|
GetLastError());
|
|
goto ExitFunc;
|
|
}
|
|
|
|
// Hack to get around ATQ assert
|
|
//pContext = (PATQ_CONT)m_pAtqContext;
|
|
//pContext->lSyncTimeout = AtqProcessingIo;
|
|
|
|
// Indicate success
|
|
fReturn = TRUE;
|
|
|
|
ExitFunc:
|
|
if (!fReturn)
|
|
{
|
|
if (m_hAtq != INVALID_HANDLE_VALUE)
|
|
{
|
|
_VERIFY(CloseHandle(m_hAtq));
|
|
m_hAtq = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
QuerySmtpInstance()->CloseBuildQStopHandle();
|
|
}
|
|
TraceFunctLeaveEx((LPARAM)this);
|
|
return(fReturn);
|
|
}
|
|
|
|
void SMTP_BUILDQ::CloseAtqHandleAndFlushQueue (void)
|
|
{
|
|
HANDLE hAtq = INVALID_HANDLE_VALUE;
|
|
PQUEUE_ENTRY pQEntry;
|
|
|
|
TraceFunctEnterEx((LPARAM)this, "SMTP_BUILDQ::CloseAtqHandle");
|
|
|
|
// We close the Atq (directory) handle.
|
|
hAtq = (HANDLE)InterlockedExchangePointer((PVOID *)&m_hAtq,
|
|
(PVOID)INVALID_HANDLE_VALUE);
|
|
if (hAtq != INVALID_HANDLE_VALUE)
|
|
_VERIFY(CloseHandle(hAtq));
|
|
|
|
QuerySmtpInstance()->SetStopHint(2);
|
|
WaitForSingleObject(QuerySmtpInstance()->QueryBuildQStopHandle(), INFINITE);
|
|
QuerySmtpInstance()->CloseBuildQStopHandle();
|
|
|
|
// Flush the queue, since we are all shuttin gdown
|
|
LockQ();
|
|
while(!IsListEmpty(&m_InitalQListHead))
|
|
{
|
|
QuerySmtpInstance()->StopHint();
|
|
pQEntry = PopQEntry();
|
|
delete pQEntry;
|
|
}
|
|
UnLockQ();
|
|
|
|
QuerySmtpInstance()->SetStopHint(2);
|
|
|
|
TraceFunctLeaveEx((LPARAM)this);
|
|
}
|
|
|
|
BOOL SMTP_BUILDQ::InsertIntoInitialQueue(IN OUT PERSIST_QUEUE_ENTRY * pEntry)
|
|
{
|
|
return(TRUE);
|
|
}
|
|
|
|
PQUEUE_ENTRY SMTP_BUILDQ::PopQEntry(void)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
|
|
BOOL SMTP_BUILDQ::PostCompletionStatus(DWORD BytesTransferred)
|
|
{
|
|
BOOL fRet;
|
|
|
|
TraceFunctEnterEx((LPARAM)this, "PostCompletionStatus");
|
|
|
|
_ASSERT(QueryAtqContext() != NULL);
|
|
|
|
// Ask for more threads only if we are less than the preset
|
|
// maximum number of threads
|
|
IncPendingIoCount();
|
|
|
|
if(!(fRet = AtqPostCompletionStatus(QueryAtqContext(),
|
|
BytesTransferred)))
|
|
{
|
|
DecPendingIoCount();
|
|
ErrorTrace((LPARAM) this,"AtqPostCompletionStatus() failed with error %d", GetLastError());
|
|
}
|
|
|
|
TraceFunctLeaveEx((LPARAM) this);
|
|
return(fRet);
|
|
}
|
|
|
|
/*++
|
|
|
|
Name :
|
|
DWORD BuildInitialQueueProc
|
|
|
|
Description:
|
|
Wrapper for the SMTP_BUILDQ::BuildInitialQueue workhorse
|
|
funciton.
|
|
|
|
Arguments:
|
|
lpThis - points to the SMTP_BUILDQ object
|
|
|
|
Returns:
|
|
NO_ERROR
|
|
|
|
--*/
|
|
DWORD BuildInitialQueueProc(void *lpThis)
|
|
{
|
|
SMTP_BUILDQ *pBuildQ = (SMTP_BUILDQ *)lpThis;
|
|
|
|
_ASSERT(pBuildQ);
|
|
_ASSERT(pBuildQ->IsValid());
|
|
|
|
// Call the main workhorse function
|
|
pBuildQ->BuildInitialQueue();
|
|
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
/*++
|
|
|
|
Name :
|
|
SMTP_BUILDQ::BuildInitialQueue
|
|
|
|
Description:
|
|
This function reads all the mail files
|
|
that were not sent, for what ever reason,
|
|
opens the NTFS stream that holds the envelope,
|
|
and builds up the internal send queue. The
|
|
envelope for each message resides in an NTFS
|
|
stream in that message file. The envelope has
|
|
a header that looks like the following :
|
|
|
|
struct ENVELOPE_HEADER
|
|
{
|
|
DWORD Version;
|
|
DWORD Dummy1; // To be used later
|
|
DWORD Dummy2; // To be used later
|
|
DWORD LocalOffset; // Local rcpt. list offset
|
|
DWORD RemoteOffset; // Remote rcpt. list offset
|
|
DWORD LocalSize; // Size of local rcpt list
|
|
DWORD RemoteSize; // Size of remote rcpt list
|
|
};
|
|
|
|
Right after the envelope header is the address
|
|
that was in the "Mail From" line. This address
|
|
is stored like "Srohanp@microsoft.com\n". The "S"
|
|
stands for SENDER. In the code below, the first
|
|
byte is always removed when reading the address.
|
|
The '\n' is also replaced with a '\0';
|
|
|
|
In this version the Local recipient list, if any,
|
|
comes right after the senders' address. You can
|
|
also find it by seeking LocalOffset bytes from the
|
|
beginning of the file. Once LocalOffset is reached,
|
|
the code reads LocalSize bytes of data. This is the
|
|
total size in bytes of the local recipient list.
|
|
Each recipient address is stored on a line by itself,
|
|
with the first letter "R" as in the example below:
|
|
|
|
Rrohanp@microsoft.com\n
|
|
Rtoddch@microsoft.com\n
|
|
etc.
|
|
|
|
The remote addresses have the same format. The first byte,
|
|
'R' stands for recipient and is always removed when building
|
|
the address. The '\n' is also removed.
|
|
|
|
Arguments:
|
|
A pointer to a PERSIST_QUEUE
|
|
|
|
Returns:
|
|
|
|
--*/
|
|
|
|
DWORD SMTP_BUILDQ::BuildInitialQueue(void)
|
|
{
|
|
|
|
SMTP_SERVER_INSTANCE * pInst;
|
|
|
|
pInst = QuerySmtpInstance();
|
|
|
|
SetEvent(QuerySmtpInstance()->GetBuildQStopHandle());
|
|
|
|
// Log an event saying that the server is ready to accept connections,
|
|
// only if we are not shutting down.
|
|
if (!pInst->IsShuttingDown())
|
|
{
|
|
char IntBuffer [20];
|
|
|
|
pInst->SetAcceptConnBool();
|
|
|
|
_itoa(pInst->QueryInstanceId(), IntBuffer, 10);
|
|
SmtpLogEventEx(SMTP_EVENT_ACCEPTING_CONNECTIONS, IntBuffer, 0);
|
|
}
|
|
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
|
|
BOOL SMTP_BUILDQ::ProcessMailQIO(DWORD InputBufferLen,
|
|
DWORD dwCompletionStatus,
|
|
OVERLAPPED * lpo)
|
|
{
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Name :
|
|
SMTP_BUILDQ::ProcessQueueObject
|
|
|
|
Description:
|
|
|
|
Main function for this class. Processes the objects in the
|
|
initial queue
|
|
|
|
Arguments:
|
|
|
|
cbWritten count of bytes written
|
|
dwCompletionStatus Error Code for last IO operation
|
|
lpo Overlapped stucture
|
|
|
|
Returns:
|
|
|
|
FALSE when processing is incomplete.
|
|
TRUE when the connection is completely processed and this
|
|
object may be deleted.
|
|
|
|
--*/
|
|
BOOL SMTP_BUILDQ::ProcessQueueObject(IN DWORD InputBufferLen, IN DWORD dwCompletionStatus, IN OUT OVERLAPPED * lpo)
|
|
{
|
|
BOOL fRet;
|
|
|
|
TraceFunctEnterEx((LPARAM)this, "SMTP_BUILDQ::ProcessQueueObject" );
|
|
|
|
//
|
|
// Increment the number of threads processing this client
|
|
//
|
|
IncThreadCount();
|
|
|
|
if(!QuerySmtpInstance()->IsShuttingDown())
|
|
{
|
|
// We want to make sure we are called from the Gibraltar
|
|
// context since that should be the only case ...
|
|
if (lpo == &(QueryAtqContext()->Overlapped))
|
|
{
|
|
DebugTrace((LPARAM)this,"About to process queue object");
|
|
fRet = ProcessMailQIO (InputBufferLen, dwCompletionStatus, lpo);
|
|
}
|
|
else
|
|
{
|
|
// We are not called with the correct context, we can't
|
|
// do much about it; so we skip in in retail and break
|
|
// in debug.
|
|
FatalTrace((LPARAM)this,"Bad overlapped context");
|
|
_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Decrement the number of threads processing this client
|
|
//
|
|
DecThreadCount();
|
|
|
|
// If our pending IO count ever hits zero, we know we have handled
|
|
// all initial mail messages. If this is the case, we will return
|
|
// FALSE, and the calling function will then fire the Stop event.
|
|
if (DecPendingIoCount() == 0)
|
|
{
|
|
DebugTrace((LPARAM)this,
|
|
"SMTP_BUILDQ::ProcessQueueObject() shutting down - Pending IOs: %d",
|
|
m_cPendingIoCount);
|
|
DebugTrace((LPARAM)this,
|
|
"SMTP_BUILDQ::ProcessQueueObject() shutting down - ActiveThreads: %d",
|
|
m_cActiveThreads);
|
|
_ASSERT(m_cActiveThreads == 0);
|
|
fRet = TRUE;
|
|
}
|
|
else
|
|
{
|
|
DebugTrace((LPARAM)this,
|
|
"SMTP_BUILDQ::ProcessClient() - Pending IOs: %d",
|
|
m_cPendingIoCount);
|
|
DebugTrace((LPARAM)this,
|
|
"SMTP_BUILDQ::ProcessClient() - ActiveThreads: %d",
|
|
m_cActiveThreads);
|
|
fRet = FALSE;
|
|
}
|
|
|
|
TraceFunctLeaveEx((LPARAM)this);
|
|
return(fRet);
|
|
}
|
|
|
|
/*++
|
|
Name :
|
|
ProcessInitialQueueObjects
|
|
|
|
Description:
|
|
|
|
Handles a completed I/O for delivering queued mail objects
|
|
|
|
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 ProcessInitialQueueObjects(PVOID pvContext,
|
|
DWORD cbWritten,
|
|
DWORD dwCompletionStatus,
|
|
OVERLAPPED *lpo)
|
|
{
|
|
BOOL fCompleted;
|
|
SMTP_BUILDQ *pBuildQ = (SMTP_BUILDQ *)pvContext;
|
|
|
|
TraceFunctEnterEx((LPARAM)pBuildQ, "ProcessInitialQueueObjects");
|
|
|
|
_ASSERT(pBuildQ);
|
|
_ASSERT(pBuildQ->IsValid());
|
|
|
|
// Call the in-context function to process the object
|
|
fCompleted =
|
|
pBuildQ->ProcessQueueObject(cbWritten, dwCompletionStatus, lpo);
|
|
if (fCompleted)
|
|
{
|
|
// If fCompleted is TRUE, we know that we either finished
|
|
// delivering the entire initial queue, or that we are already
|
|
// signalled to shut down, and the last pending IO has just
|
|
// been serviced. We fire the Stop event to allow CloseAtqHandle
|
|
// to proceed through the wait.
|
|
_VERIFY(SetEvent(pBuildQ->QuerySmtpInstance()->GetBuildQStopHandle()));
|
|
}
|
|
|
|
TraceFunctLeaveEx((LPARAM)pBuildQ);
|
|
}
|
|
|
|
BOOL SMTP_BUILDQ::MakeAllAddrsLocal (HANDLE hFile, char * szBuffer, ENVELOPE_HEADER * pMyHeader)
|
|
{
|
|
return TRUE;
|
|
}
|