PROCESS.C

// --process.c------------------------------------------------------------------ 
//
// Functions to start a thread to handle processing of new mail or files.
//
// Copyright (C) Microsoft Corp. 1986-1996. All Rights Reserved.
// -----------------------------------------------------------------------------

#include "edk.h"
#include "gwmain.h"

#define SLEEP_TIME 3000

extern HANDLE hNewMailThread;
extern HANDLE hNewFileThread;

extern DWORD dwNewMailTimeout;
extern DWORD dwNewFileTimeout;

extern CRITICAL_SECTION csNewMailList;
extern CRITICAL_SECTION csNewFileList;

static HANDLE hFindData = INVALID_HANDLE_VALUE; // change notification handle
static HANDLE PollHandle = INVALID_HANDLE_VALUE; // poll handle

//$--_GWThreadContext-----------------------------------------------------------
// Type for gateway thread context information.
// -----------------------------------------------------------------------------
typedef struct _GWThreadContext
{
BOOL fUseFindData; // if TRUE use lpFindData

CHAR szAddrType[MAX_PATH+1]; // address type

DWORD dwSubmitInterval; // submit interval
DWORD dwPollingInterval; // polling interval
DWORD cMessages; // #messages/thread
DWORD cThreads; // #threads

ULONG ulThreadNumber; // thread#

CHAR szFindPath[MAX_PATH+1]; // path
CHAR szFindMask[MAX_PATH+1]; // find mask
HANDLE *lphFindData; // change notifcation handle
ULONG cFindData; // find data count
LPWIN32_FIND_DATA lpFindData; // find data
HANDLE *lpPollHandle; // poll handle

LPSRowSet lpRows; // rows
LPMAPITABLE lpTable; // contents table

LPTHREAD_START_ROUTINE lpStartAddress; // thread function

} GWTHREADCONTEXT, *LPGWTHREADCONTEXT;

#include "process.chk"

//$--GetGWPath------------------------------------------------------------------
// Get a gateway path pointer.
// -----------------------------------------------------------------------------
LPSTR GetGWPath( // RETURNS: gateway path
IN LPVOID lpParameter) // parameter
{
HRESULT hr = NOERROR;

DEBUGPUBLIC("GetGWPath()");

hr = CHK_HrCheckParameter(lpParameter);

if(FAILED(hr))
return(NULL);

if(((LPGWTHREADCONTEXT)lpParameter)->fUseFindData == FALSE)
{
return(NULL);
}

return(((LPGWTHREADCONTEXT)lpParameter)->szFindPath);
}

//$--GetGWSRowSet-----------------------------------------------------------------
// Get a SRowSet pointer.
// -----------------------------------------------------------------------------
LPSRowSet GetGWSRowSet( // RETURNS: SRowSet
IN LPVOID lpParameter) // parameter
{
HRESULT hr = NOERROR;

DEBUGPUBLIC("GetGWSRowSet()");

hr = CHK_HrCheckParameter(lpParameter);

if(FAILED(hr))
return(NULL);

if(((LPGWTHREADCONTEXT)lpParameter)->fUseFindData == TRUE)
{
return(NULL);
}

return(((LPGWTHREADCONTEXT)lpParameter)->lpRows);
}

//$--GetGWFindData-----------------------------------------------------------
// Get a WIN32_FIND_DATA pointer.
// -----------------------------------------------------------------------------
LPWIN32_FIND_DATA GetGWFindData( // RETURNS: WIN32_FIND_DATA
IN LPVOID lpParameter) // parameter
{
HRESULT hr = NOERROR;

DEBUGPUBLIC("GetGWFindData()");

hr = CHK_HrCheckParameter(lpParameter);

if(FAILED(hr))
return(NULL);

if(((LPGWTHREADCONTEXT)lpParameter)->fUseFindData == FALSE)
{
return(NULL);
}

return(((LPGWTHREADCONTEXT)lpParameter)->lpFindData);
}

//$--GetGWFindDataSize-------------------------------------------------------
// Get a WIN32_FIND_DATA size.
// -----------------------------------------------------------------------------
ULONG GetGWFindDataSize( // RETURNS: count of WIN32_FIND_DATA
IN LPVOID lpParameter) // parameter
{
HRESULT hr = NOERROR;

DEBUGPUBLIC("GetGWFindDataSize()");

hr = CHK_HrCheckParameter(lpParameter);

if(FAILED(hr))
return(0);

if(((LPGWTHREADCONTEXT)lpParameter)->fUseFindData == FALSE)
{
return(0);
}

return(((LPGWTHREADCONTEXT)lpParameter)->cFindData);
}

//$--GetGWSubmitInterval----------------------------------------------------------
// Get the submit interval.
// -----------------------------------------------------------------------------
DWORD GetGWSubmitInterval( // RETURNS: submit interval
IN LPVOID lpParameter) // parameter
{
HRESULT hr = NOERROR;

DEBUGPUBLIC("GetGWSubmitInterval()");

hr = CHK_HrCheckParameter(lpParameter);

if(FAILED(hr))
return(0);

return(((LPGWTHREADCONTEXT)lpParameter)->dwSubmitInterval);
}

//$--FreeThreadParameter--------------------------------------------------------
// Free the thread parameter.
// -----------------------------------------------------------------------------
VOID FreeThreadParameter( // RETURNS: nothing
IN LPVOID lpParameter) // parameter
{
HRESULT hr = NOERROR;

DEBUGPUBLIC("FreeThreadParameter()");

hr = CHK_HrCheckParameter(lpParameter);

if(FAILED(hr))
return;

if(((LPGWTHREADCONTEXT)lpParameter)->lpRows != NULL)
{
FREEPROWS(((LPGWTHREADCONTEXT)lpParameter)->lpRows);
}

if(((LPGWTHREADCONTEXT)lpParameter)->lpFindData != NULL)
{
MAPIFREEBUFFER(((LPGWTHREADCONTEXT)lpParameter)->lpFindData);
}

MAPIFREEBUFFER(lpParameter);
}

//$--HrGetNewMailList-----------------------------------------------------------
// Get list of new mail.
// -----------------------------------------------------------------------------
static HRESULT HrGetNewMailList( // RETURNS: return code
IN LPMAPITABLE lpTable, // contents table
IN ULONG cMessages, // maximum number of messages to get
IN DWORD dwPollingInterval, // polling interval
OUT LPSRowSet *lppRows) // new mail list
{
HRESULT hr = NOERROR;
LPSRowSet lpRows = NULL;

DEBUGPRIVATE("HrGetNewMailList()");

EnterCriticalSection(&csNewMailList);

START:

//
// Check for service control
//

hr = HrServiceProcessControl();

if(FAILED(hr))
{
goto cleanup;
}

hr = MAPICALL(lpTable)->QueryRows(
lpTable,
cMessages,
0,
&lpRows);

if(FAILED(hr))
{
goto cleanup;
}

if((lpRows == NULL) || (lpRows->cRows == 0))
{
FREEPROWS(lpRows);
}
else
{
goto cleanup;
}

if((dwPollingInterval == ((DWORD)-1)) && (GetGWNewMailEvent() != NULL))
{
DWORD dw = 0;
HANDLE hObjects[2] = {0};

hObjects[0] = GetServiceStopEvent();
hObjects[1] = GetGWNewMailEvent();

dw = WaitForMultipleObjects(2, hObjects, FALSE, dwNewMailTimeout);

switch(dw)
{
case WAIT_OBJECT_0:
case (WAIT_ABANDONED_0):

//
// Service is stopping.
//

hr = HR_LOG(EDK_E_SHUTDOWN_SERVICE);

goto cleanup;
break;
case (WAIT_OBJECT_0+1):
case (WAIT_ABANDONED_0+1):
break;
case (WAIT_TIMEOUT):
break;
default:
hr = HR_LOG(E_FAIL);
goto cleanup;
}
}
else
{
if(dwPollingInterval == ((DWORD)-1))
{
dwPollingInterval = SLEEP_TIME;
}

Sleep(dwPollingInterval);
}

// Go to the beginning of the contents table for the folder
hr = MAPICALL(lpTable)->SeekRow(lpTable, BOOKMARK_BEGINNING, 0, NULL);

if(FAILED(hr))
{
goto cleanup;
}

goto START;

cleanup:

if(SUCCEEDED(hr))
{
*lppRows = lpRows;
}
else
{
FREEPROWS(lpRows);
}

LeaveCriticalSection(&csNewMailList);

RETURN(hr);
}

//$--FnNewMailPool--------------------------------------------------------------
// New mail thread pool function
// -----------------------------------------------------------------------------
static VOID FnNewMailPool( // RETURNS: nothing
IN LPVOID lpParameter) // parameter
{
HRESULT hr = NOERROR;

DEBUGPRIVATE("FnNewMailPool()");

__try
{
for(;;)
{
hr = HrGetNewMailList(
((LPGWTHREADCONTEXT)lpParameter)->lpTable,
((LPGWTHREADCONTEXT)lpParameter)->cMessages,
((LPGWTHREADCONTEXT)lpParameter)->dwPollingInterval,
&((LPGWTHREADCONTEXT)lpParameter)->lpRows);

if(FAILED(hr))
{
break;
}

(*((LPGWTHREADCONTEXT)lpParameter)->lpStartAddress)(lpParameter);

FREEPROWS(((LPGWTHREADCONTEXT)lpParameter)->lpRows);
}

FreeThreadParameter(lpParameter);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
SetLastError(_exception_code());

hr = HR_LOG(E_FAIL);

MODULE_WARNING("**** Stopping gateway ****");

SetServiceExitCode( ERROR_INTERNAL_ERROR, NOERROR);

ServiceStop();

}
}

//$--FnNewMailMain--------------------------------------------------------------
// New mail handler main loop.
// -----------------------------------------------------------------------------
static VOID FnNewMailMain( // RETURNS: nothing
IN LPVOID lpParameter) // parameter
{
HRESULT hr = NOERROR;
SCODE sc = 0;
DWORD dw = 0;
ULONG i = 0;
BOOL fStatus = FALSE;

LPMAPITABLE lpTable = NULL;

DWORD dwThreadId = 0;
HANDLE hThread = NULL;

LONG NumStartThreads = 0;
LONG NumStopThreads = 0;

LONG cThreads = 0;

LONG cRunningThreads = 0;

LPMAPIFOLDER lpMtsOutFolder = NULL;

LPGWTHREADCONTEXT lpContext = NULL;
LPGWTHREADCONTEXT lpThreadContext = NULL;

SizedSPropTagArray(1, rgPropTag) =
{
1,
{
PR_ENTRYID
}
};

DEBUGPRIVATE("FnNewMailMain()");

hr = CHK_HrCheckParameter(lpParameter);

if(FAILED(hr))
return;

lpContext = (LPGWTHREADCONTEXT)lpParameter;

cThreads = lpContext->cThreads;

lpMtsOutFolder = GetGWMtsOutFolder();

//--------------------------------------------------------------------------

hr = MAPICALL(lpMtsOutFolder)->GetContentsTable(
lpMtsOutFolder,
MAPI_DEFERRED_ERRORS,
&lpTable);

if(FAILED(hr))
{
goto cleanup;
}

hr = MAPICALL(lpTable)->SetColumns(
lpTable,
(LPSPropTagArray)&rgPropTag,
TBL_BATCH);

if(FAILED(hr))
{
goto cleanup;
}

//
// Check for service control
//

hr = HrServiceProcessControl();

if(FAILED(hr))
{
goto cleanup;
}

NumStartThreads = 0;
NumStopThreads = 0;

cRunningThreads = 0;

for(i = 0 ; i < ((ULONG)cThreads) ; i++)
{
lpThreadContext = NULL;

sc = MAPIAllocateBuffer(sizeof(GWTHREADCONTEXT), (void **)&lpThreadContext);

if(FAILED(sc))
{
hr = HR_LOG(E_OUTOFMEMORY);
goto cleanup;
}

CopyMemory(lpThreadContext, lpContext, sizeof(GWTHREADCONTEXT));

lpThreadContext->ulThreadNumber = i;
lpThreadContext->lpTable = lpTable;
lpThreadContext->lpRows = NULL;

hThread = HServiceCreateThread(
&NumStartThreads,
&NumStopThreads,
NULL,
0,
(LPTHREAD_START_ROUTINE)FnNewMailPool,
(LPVOID)lpThreadContext,
0,
&dwThreadId);

if(hThread == NULL)
{
hr = HR_LOG(E_FAIL);

if(cRunningThreads == 0)
goto cleanup;
else
goto WAIT;
}

CloseHandle(hThread);

cRunningThreads = i;
cRunningThreads++;
}

WAIT:

// Wait for all threads to start
while(NumStartThreads != cRunningThreads)
{
Sleep(SLEEP_TIME);
}

// Wait for all threads to exit
while(NumStopThreads != cRunningThreads)
{
Sleep(SLEEP_TIME);
}

cleanup:

ULRELEASE(lpTable);

if(FAILED(hr))
{
MODULE_WARNING("**** Stopping gateway ****");

SetServiceExitCode( ERROR_INTERNAL_ERROR, NOERROR);

ServiceStop();
}

return;
}

//$--FnNewMailThread------------------------------------------------------------
// New mail thread function
// -----------------------------------------------------------------------------
static VOID FnNewMailThread( // RETURNS: nothing
IN LPVOID lpParameter) // parameter
{
HRESULT hr = NOERROR;
HGLOBAL hglobal = NULL;

DEBUGPRIVATE("FnNewMailThread()");

hr = CHK_HrCheckParameter(lpParameter);

if(FAILED(hr))
ExitThread(0);

__try
{
FnNewMailMain(lpParameter);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
SetLastError(_exception_code());

hr = HR_LOG(E_FAIL);

MODULE_WARNING("**** Stopping gateway ****");

SetServiceExitCode( ERROR_INTERNAL_ERROR, NOERROR);

ServiceStop();

}

MAPIFREEBUFFER( lpParameter);

ExitThread(0);
}

//$--HrGWStartNewMailHandler@--------------------------------------------------
// Start a thread to handle processing of new mail.
// -----------------------------------------------------------------------------
HRESULT HrGWStartNewMailHandlerW(
IN DWORD dwSubmitInterval, // submit interval
IN DWORD dwPollingInterval, // polling interval
IN DWORD cMessages, // #messages/thread
IN DWORD cThreads, // #threads
IN LPCWSTR lpszAddrType, // address type
IN LPCWSTR lpszPath, // path
IN LPTHREAD_START_ROUTINE lpStartAddress) // thread function
{
HRESULT hr = NOERROR;
HGLOBAL hglobal = NULL;
LPSTR lpszAddrTypeA = NULL;
LPSTR lpszPathA = NULL;
DWORD dwThreadId = 0;

LPGWTHREADCONTEXT lpContext = NULL;

DEBUGPUBLIC("HrGWStartNewMailHandlerW()");

hr = CHK_HrGWStartNewMailHandlerW(
dwSubmitInterval,
dwPollingInterval,
cMessages,
cThreads,
lpszAddrType,
lpszPath,
lpStartAddress);
if(FAILED(hr))
RETURN(hr);

hr = MAPIAllocateBuffer( sizeof(GWTHREADCONTEXT), (PVOID)&lpContext);
if( FAILED( hr))
goto cleanup;

ZeroMemory(lpContext, sizeof(GWTHREADCONTEXT));

lpContext->fUseFindData = FALSE;
lpContext->dwSubmitInterval = dwSubmitInterval;
lpContext->dwPollingInterval = dwPollingInterval;
lpContext->lpStartAddress = lpStartAddress;
lpContext->cMessages = cMessages;
lpContext->cThreads = cThreads;
lpContext->lpStartAddress = lpStartAddress;

hr = HrStrWToStrA( lpszAddrType, &lpszAddrTypeA);
if( FAILED( hr))
goto cleanup;

hr = HrStrWToStrA( lpszPath, &lpszPathA);
if( FAILED( hr))
goto cleanup;

lstrcpyA(lpContext->szAddrType, lpszAddrTypeA);
lstrcpyA(lpContext->szFindPath, lpszPathA);

hNewMailThread = CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE)FnNewMailThread,
(LPVOID)lpContext,
0,
&dwThreadId);

if(hNewMailThread == NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

cleanup:
MAPIFREEBUFFER( lpszAddrTypeA);
MAPIFREEBUFFER( lpszPathA);

if(FAILED(hr))
MAPIFREEBUFFER( lpContext);

RETURN(hr);
}

HRESULT HrGWStartNewMailHandlerA(
IN DWORD dwSubmitInterval, // submit interval
IN DWORD dwPollingInterval, // polling interval
IN DWORD cMessages, // #messages/thread
IN DWORD cThreads, // #threads
IN LPCSTR lpszAddrType, // address type
IN LPCSTR lpszPath, // path
IN LPTHREAD_START_ROUTINE lpStartAddress) // thread function
{
HRESULT hr = NOERROR;
HGLOBAL hglobal = NULL;
DWORD dwThreadId = 0;

LPGWTHREADCONTEXT lpContext = NULL;

DEBUGPUBLIC("HrGWStartNewMailHandlerA()");

hr = CHK_HrGWStartNewMailHandlerA(
dwSubmitInterval,
dwPollingInterval,
cMessages,
cThreads,
lpszAddrType,
lpszPath,
lpStartAddress);
if(FAILED(hr))
RETURN(hr);

hr = MAPIAllocateBuffer( sizeof(GWTHREADCONTEXT), (PVOID)&lpContext);
if( FAILED( hr))
goto cleanup;

ZeroMemory(lpContext, sizeof(GWTHREADCONTEXT));

lpContext->fUseFindData = FALSE;
lpContext->dwSubmitInterval = dwSubmitInterval;
lpContext->dwPollingInterval = dwPollingInterval;
lpContext->lpStartAddress = lpStartAddress;
lpContext->cMessages = cMessages;
lpContext->cThreads = cThreads;
lpContext->lpStartAddress = lpStartAddress;

lstrcpyA( lpContext->szAddrType, lpszAddrType);
lstrcpyA( lpContext->szFindPath, lpszPath);

hNewMailThread = CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE)FnNewMailThread,
(LPVOID)lpContext,
0,
&dwThreadId);

if(hNewMailThread == NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

cleanup:
if(FAILED(hr))
MAPIFREEBUFFER( lpContext);

RETURN(hr);
}

//$--HrPollFile-----------------------------------------------------------------
// Poll for a file.
// -----------------------------------------------------------------------------
static HRESULT HrPollFile( // RETURNS: return code
IN LPSTR lpszDirectory, // pointer to directory
IN LPSTR lpszFindMask, // pointer to find mask
OUT HANDLE *lpPollHandle, // pointer to find data handle
OUT LPWIN32_FIND_DATA lpFindData) // pointer to find data
{
HRESULT hr = NOERROR;
BOOL fFound = FALSE;
DWORD dwIgnoreAttrs =
FILE_ATTRIBUTE_READONLY |
FILE_ATTRIBUTE_SYSTEM |
FILE_ATTRIBUTE_DIRECTORY |
FILE_ATTRIBUTE_TEMPORARY;

DEBUGPRIVATE("HrPollFile()");

hr = CHK_HrPollFile(
lpszDirectory,
lpszFindMask,
lpPollHandle,
lpFindData);

if(FAILED(hr))
RETURN(hr);

//One of the following is now true:
//
//A)We have a search already active, use FindNextFile.
//If it fails, we will want to restart the search
//(in case something appeared in the search behind
//us.
//
//B)We don't have a search active, use FindFirstFile.

// Try the active search

if (*lpPollHandle != INVALID_HANDLE_VALUE)
{
fFound = FindNextFile(*lpPollHandle, lpFindData);

// If we fail, close the old search so we can start
// a new one below.

if (!fFound)
{
FindClose(*lpPollHandle);
*lpPollHandle = INVALID_HANDLE_VALUE;

//
// If we don't find a file, then return.
//

if(GetLastError() == ERROR_NO_MORE_FILES)
{
hr = HR_LOG(EDK_E_NOT_FOUND);
}
else
{
hr = HR_LOG(HRESULT_FROM_WIN32(GetLastError()));
}

goto cleanup;
}
}

// If there was no search or if the old one was just closed above

if (*lpPollHandle == INVALID_HANDLE_VALUE)
{
HANDLE hFindT;
CHAR rgchFileName[MAX_PATH];

// Copy the directory name. Note that we trust the value in the
// profile to be correct, since the UI should have enforced a
// syntax that included a trailing : or \ in the spec.

lstrcpy(rgchFileName, lpszDirectory);

lstrcat(rgchFileName, lpszFindMask);

hFindT = FindFirstFile(rgchFileName, lpFindData);

// If nothing's found, we're done here.

if (hFindT == INVALID_HANDLE_VALUE)
{
hr = HR_LOG(EDK_E_NOT_FOUND);
goto cleanup;
}

// Found something, continue along.

fFound = TRUE;
*lpPollHandle = hFindT;
}


//Here on a match. Exclude unwanted files.
//
//Any match with DIRECTORY, READONLY, SYSTEM or TEMPORARY attribute
//is ignored. Keep trying until we exhaust the current supply or we
//find a file without these attributes. Also, ignore files smaller
//than some arbitrary size, they're probably trash.

while (fFound)
{
//We found a file. Does it have any of the attributes we
//want to ignore? If not, get out. If so, try another.

#define MIN_USEFUL_FILESIZE ((DWORD) 64)

if ((!((lpFindData)->dwFileAttributes & dwIgnoreAttrs)) &&
((lpFindData->nFileSizeHigh != 0) ||
(lpFindData->nFileSizeLow >= MIN_USEFUL_FILESIZE)))
break;

fFound = FindNextFile(*lpPollHandle, lpFindData);
}

if(!fFound)
{
FindClose(*lpPollHandle);
*lpPollHandle = INVALID_HANDLE_VALUE;

hr = HR_LOG(EDK_E_NOT_FOUND);
goto cleanup;
}

cleanup:

RETURN(hr);
}

//$--HrGetNewFileList-----------------------------------------------------------
// Get list of new files.
// -----------------------------------------------------------------------------
static HRESULT HrGetNewFileList( // RETURNS: return code
IN ULONG cMessages, // maximum number of messages to get
IN DWORD dwPollingInterval, // polling interval
IN LPSTR lpszFindPath, // find path
IN LPSTR lpszFindMask, // find mask
IN OUT HANDLE *lpPollHandle, // poll handle
IN OUT HANDLE *lphFindData, // change notifcation handle
OUT ULONG *lpcFindData, // count of find data
OUT LPWIN32_FIND_DATA *lppFindData) // new file list
{
HRESULT hr = NOERROR;
LPWIN32_FIND_DATA lpFindData = NULL;
ULONG cBytes = 0;
SCODE sc = NOERROR;
ULONG j = 0;
BOOL fStatus = FALSE;

DEBUGPRIVATE("HrGetNewFileList()");

*lpcFindData = 0;
*lppFindData = NULL;

EnterCriticalSection(&csNewFileList);

START:

//
// Check for service control
//

hr = HrServiceProcessControl();

if(FAILED(hr))
{
goto cleanup;
}

lpFindData = NULL;

cBytes = cMessages*(sizeof(WIN32_FIND_DATA) + MAX_ALIGN);

sc = MAPIAllocateBuffer(cBytes, (void **)&lpFindData);

ZeroMemory(lpFindData, cBytes);

// An error occured allocating the messages buffer

if(FAILED(sc))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

for(j = 0 ; j < ((ULONG)cMessages) ; j++)
{
hr = HrPollFile(
lpszFindPath,
lpszFindMask,
lpPollHandle,
&lpFindData[j]);

if(hr == EDK_E_NOT_FOUND)
{
hr = NOERROR;

//
// Don't start any more threads after the current one is
// started since there are no more files to process.
//

break;
}

if(FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
}

if(j == 0)
{
MAPIFREEBUFFER(lpFindData);
}
else
{
*lpcFindData = j;
*lppFindData = lpFindData;
goto cleanup;
}

if((dwPollingInterval == ((DWORD)-1)) && (*lphFindData != NULL))
{
DWORD dw = 0;
HANDLE hObjects[2] = {0};

hObjects[0] = GetServiceStopEvent();
hObjects[1] = *lphFindData;

dw = WaitForMultipleObjects(2, hObjects, FALSE, dwNewFileTimeout);

switch(dw)
{
case WAIT_OBJECT_0:
case (WAIT_ABANDONED_0):

//
// Service is stopping.
//

hr = HR_LOG(EDK_E_SHUTDOWN_SERVICE);

goto cleanup;
break;
case (WAIT_OBJECT_0+1):
case (WAIT_ABANDONED_0+1):
break;
case (WAIT_TIMEOUT):
goto START;
break;
default:
hr = HR_LOG(E_FAIL);
goto cleanup;
}

fStatus = FindNextChangeNotification(*lphFindData);

if(fStatus == FALSE)
{
hr = HR_LOG(E_FAIL);

FindCloseChangeNotification(*lphFindData);
*lphFindData = NULL;
}
}
else
{
if(dwPollingInterval == ((DWORD)-1))
{
dwPollingInterval = SLEEP_TIME;
}

Sleep(dwPollingInterval);
}

goto START;

cleanup:

if(FAILED(hr))
{
*lpcFindData = 0;
*lppFindData = NULL;

MAPIFREEBUFFER(lpFindData);
}

LeaveCriticalSection(&csNewFileList);

RETURN(hr);
}

//$--FnNewFilePool--------------------------------------------------------------
// New file thread pool function
// -----------------------------------------------------------------------------
static VOID FnNewFilePool( // RETURNS: nothing
IN LPVOID lpParameter) // parameter
{
HRESULT hr = NOERROR;

DEBUGPRIVATE("FnNewFilePool()");

__try
{
for(;;)
{
hr = HrGetNewFileList(
((LPGWTHREADCONTEXT)lpParameter)->cMessages,
((LPGWTHREADCONTEXT)lpParameter)->dwPollingInterval,
((LPGWTHREADCONTEXT)lpParameter)->szFindPath,
((LPGWTHREADCONTEXT)lpParameter)->szFindMask,
((LPGWTHREADCONTEXT)lpParameter)->lpPollHandle,
((LPGWTHREADCONTEXT)lpParameter)->lphFindData,
&((LPGWTHREADCONTEXT)lpParameter)->cFindData,
&((LPGWTHREADCONTEXT)lpParameter)->lpFindData);

if(FAILED(hr))
{
break;
}

(*((LPGWTHREADCONTEXT)lpParameter)->lpStartAddress)(lpParameter);

MAPIFREEBUFFER(((LPGWTHREADCONTEXT)lpParameter)->lpFindData);
}

FreeThreadParameter(lpParameter);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
SetLastError(_exception_code());

hr = HR_LOG(E_FAIL);

MODULE_WARNING("**** Stopping gateway ****");

SetServiceExitCode( ERROR_INTERNAL_ERROR, NOERROR);

ServiceStop();

}
}

//$--FnNewFileMain--------------------------------------------------------------
// New file handler main loop.
// -----------------------------------------------------------------------------
static VOID FnNewFileMain( // RETURNS: nothing
IN LPVOID lpParameter) // parameter
{
HRESULT hr = NOERROR;
HRESULT hrT = NOERROR;
SCODE sc = 0;
DWORD dw = 0;

BOOL    fStatus           = 0; 
ULONG i = 0;

ULONG cBytes = 0;

DWORD dwThreadId = 0;
HANDLE hThread = NULL;

LONG NumStartThreads = 0;
LONG NumStopThreads = 0;

LONG cThreads = 0;
ULONG cMessages = 0;

LONG cRunningThreads = 0;

DWORD dwPollingInterval = 0;

LPGWTHREADCONTEXT lpContext = NULL;
LPGWTHREADCONTEXT lpThreadContext = NULL;

DEBUGPRIVATE("FnNewFileMain()");

hr = CHK_HrCheckParameter(lpParameter);

if(FAILED(hr))
return;

lpContext = (LPGWTHREADCONTEXT)lpParameter;

cThreads = lpContext->cThreads;

//--------------------------------------------------------------------------

//
// Check for service control
//

hr = HrServiceProcessControl();

if(FAILED(hr))
{
goto cleanup;
}

NumStartThreads = 0;
NumStopThreads = 0;

cRunningThreads = 0;

for(i = 0 ; i < ((ULONG)cThreads) ; i++)
{
lpThreadContext = NULL;

sc = MAPIAllocateBuffer(sizeof(GWTHREADCONTEXT), (void **)&lpThreadContext);

if(FAILED(sc))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

CopyMemory(lpThreadContext, lpContext, sizeof(GWTHREADCONTEXT));

lpThreadContext->ulThreadNumber = i;
lpThreadContext->cFindData = 0;
lpThreadContext->lpFindData = NULL;

hThread = HServiceCreateThread(
&NumStartThreads,
&NumStopThreads,
NULL,
0,
(LPTHREAD_START_ROUTINE)FnNewFilePool,
(LPVOID)lpThreadContext,
0,
&dwThreadId);

if(hThread == NULL)
{
hr = HR_LOG(E_FAIL);

if(cRunningThreads == 0)
goto cleanup;
else
goto WAIT;
}

CloseHandle(hThread);

cRunningThreads = i;
cRunningThreads++;
}

WAIT:

// Wait for all threads to start
while(NumStartThreads != cRunningThreads)
{
Sleep(SLEEP_TIME);
}

// Wait for all threads to exit
while(NumStopThreads != cRunningThreads)
{
Sleep(SLEEP_TIME);
}

cleanup:

if(FAILED(hr))
{
MODULE_WARNING("**** Stopping gateway ****");

SetServiceExitCode( ERROR_INTERNAL_ERROR, NOERROR);

ServiceStop();
}

return;
}

//$--FnNewFileThread------------------------------------------------------------
// New file thread function
// -----------------------------------------------------------------------------
static VOID FnNewFileThread( // RETURNS: nothing
IN LPVOID lpParameter) // parameter
{
HRESULT hr = NOERROR;
BOOL fStatus = FALSE;

DEBUGPRIVATE("FnNewFileThread()");

hr = CHK_HrCheckParameter(lpParameter);

if(FAILED(hr))
ExitThread(0);

__try
{
FnNewFileMain(lpParameter);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
SetLastError(_exception_code());

hr = HR_LOG(E_FAIL);

MODULE_WARNING("**** Stopping gateway ****");

SetServiceExitCode( ERROR_INTERNAL_ERROR, NOERROR);

ServiceStop();
}

__try
{
if(*((LPGWTHREADCONTEXT)lpParameter)->lphFindData != NULL)
{
fStatus = FindCloseChangeNotification(
*((LPGWTHREADCONTEXT)lpParameter)->lphFindData);

if(fStatus == FALSE)
{
hr = HR_LOG(E_FAIL);
}
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
SetLastError(_exception_code());

hr = HR_LOG(E_FAIL);
}

MAPIFREEBUFFER( lpParameter);

ExitThread(0);
}

//$--HrGWStartNewFileHandler@--------------------------------------------------
// Start a thread to handle processing of new files.
// -----------------------------------------------------------------------------
HRESULT HrGWStartNewFileHandlerW(
IN DWORD dwSubmitInterval, // submit interval
IN DWORD dwPollingInterval, // polling interval
IN DWORD cMessages, // #messages/thread
IN DWORD cThreads, // #threads
IN LPCWSTR lpszAddrType, // address type
IN LPCWSTR lpszFileMask, // file mask
IN LPCWSTR lpszPath, // path
IN LPTHREAD_START_ROUTINE lpStartAddress) // thread function
{
HRESULT hr = NOERROR;
HANDLE hThread = NULL;
LPSTR lpszAddrTypeA = NULL;
LPSTR lpszFileMaskA = NULL;
LPSTR lpszPathA = NULL;
DWORD dwThreadId = 0;

LPGWTHREADCONTEXT lpContext = NULL;

DEBUGPUBLIC("HrGWStartNewFileHandlerW()");

hr = CHK_HrGWStartNewFileHandlerW(
dwSubmitInterval,
dwPollingInterval,
cMessages,
cThreads,
lpszAddrType,
lpszFileMask,
lpszPath,
lpStartAddress);
if(FAILED(hr))
RETURN(hr);

hr = MAPIAllocateBuffer( sizeof(GWTHREADCONTEXT), (PVOID)&lpContext);
if( FAILED( hr))
goto cleanup;

ZeroMemory(lpContext, sizeof(GWTHREADCONTEXT));

lpContext->fUseFindData = TRUE;
lpContext->dwSubmitInterval = dwSubmitInterval;
lpContext->dwPollingInterval = dwPollingInterval;
lpContext->lpStartAddress = lpStartAddress;
lpContext->cMessages = cMessages;
lpContext->cThreads = cThreads;
lpContext->lpStartAddress = lpStartAddress;

hr = HrStrWToStrA( lpszAddrType, &lpszAddrTypeA);
if( FAILED( hr))
goto cleanup;

hr = HrStrWToStrA( lpszFileMask, &lpszFileMaskA);
if( FAILED( hr))
goto cleanup;

hr = HrStrWToStrA( lpszPath, &lpszPathA);
if( FAILED( hr))
goto cleanup;

lstrcpyA(lpContext->szAddrType, lpszAddrTypeA);
lstrcpyA(lpContext->szFindMask, lpszFileMaskA);
lstrcpyA(lpContext->szFindPath, lpszPathA);

hFindData = FindFirstChangeNotification(
lpContext->szFindPath,
FALSE,
FILE_NOTIFY_CHANGE_ATTRIBUTES);

if(hFindData == INVALID_HANDLE_VALUE)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

lpContext->lphFindData = &hFindData;
lpContext->lpPollHandle = &PollHandle;

hNewFileThread = CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE)FnNewFileThread,
(LPVOID)lpContext,
0,
&dwThreadId);

if(hNewFileThread == NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

cleanup:
MAPIFREEBUFFER( lpszAddrTypeA);
MAPIFREEBUFFER( lpszFileMaskA);
MAPIFREEBUFFER( lpszPathA);

if(FAILED(hr))
MAPIFREEBUFFER( lpContext);

RETURN(hr);
}

HRESULT HrGWStartNewFileHandlerA(
IN DWORD dwSubmitInterval, // submit interval
IN DWORD dwPollingInterval, // polling interval
IN DWORD cMessages, // #messages/thread
IN DWORD cThreads, // #threads
IN LPCSTR lpszAddrType, // address type
IN LPCSTR lpszFileMask, // file mask
IN LPCSTR lpszPath, // path
IN LPTHREAD_START_ROUTINE lpStartAddress) // thread function
{
HRESULT hr = NOERROR;
HANDLE hThread = NULL;
DWORD dwThreadId = 0;

LPGWTHREADCONTEXT lpContext = NULL;

DEBUGPUBLIC("HrGWStartNewFileHandlerA()");

hr = CHK_HrGWStartNewFileHandlerA(
dwSubmitInterval,
dwPollingInterval,
cMessages,
cThreads,
lpszAddrType,
lpszFileMask,
lpszPath,
lpStartAddress);
if(FAILED(hr))
RETURN(hr);

hr = MAPIAllocateBuffer( sizeof(GWTHREADCONTEXT), (PVOID)&lpContext);
if( FAILED( hr))
goto cleanup;

ZeroMemory(lpContext, sizeof(GWTHREADCONTEXT));

lpContext->fUseFindData = TRUE;
lpContext->dwSubmitInterval = dwSubmitInterval;
lpContext->dwPollingInterval = dwPollingInterval;
lpContext->lpStartAddress = lpStartAddress;
lpContext->cMessages = cMessages;
lpContext->cThreads = cThreads;
lpContext->lpStartAddress = lpStartAddress;

lstrcpy(lpContext->szAddrType, lpszAddrType);
lstrcpy(lpContext->szFindMask, lpszFileMask);
lstrcpy(lpContext->szFindPath, lpszPath);

hFindData = FindFirstChangeNotification(
lpContext->szFindPath,
FALSE,
FILE_NOTIFY_CHANGE_ATTRIBUTES);

if(hFindData == INVALID_HANDLE_VALUE)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

lpContext->lphFindData = &hFindData;
lpContext->lpPollHandle = &PollHandle;

hNewFileThread = CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE)FnNewFileThread,
(LPVOID)lpContext,
0,
&dwThreadId);

if(hNewFileThread == NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

cleanup:
if(FAILED(hr))
MAPIFREEBUFFER( lpContext);

RETURN(hr);
}