Windows2003-3790/inetsrv/iis/svcs/smtp/server/buildq.cxx
2020-09-30 16:53:55 +02:00

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;
}