2914 lines
91 KiB
C++
2914 lines
91 KiB
C++
#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 | GENERIC_WRITE,
|
|
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;
|
|
}
|
|
|
|
_snprintf(szPickupFilePath, sizeof(szPickupFilePath)-1, "%s%s", QuerySmtpInstance()->GetMailPickupDir(), FileName);
|
|
szPickupFilePath[sizeof(szPickupFilePath)-1]='\0';
|
|
|
|
// 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 + 9]; // for the "From: %s\r\n" below
|
|
|
|
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);
|
|
}
|
|
|
|
|