RXPRPC.CPP

/////////////////////////////////////////////////////////////////////////////// 
//
// File Name
// RXPRPC.CPP
//
// Description
// This file implements all the remote functions available to client
// WINDS message transport providers.
//
// Authors
// Irving De la Cruz
// Les Thaler
//
// Revision: 1.7
//
// Written for Microsoft Windows Developer Support
// Copyright (c) 1995-1996 Microsoft Corporation. All rights reserved.
//
#include "_WINDS.H"
#include <RPC.H>
#include "WINDS.H" // Header file generated by the MIDL compiler
#include "WDSADM.H" // Header file generated by the MIDL compiler

typedef struct tagTHREAD_PROC_INFO
{
HANDLE hPipe;
HANDLE hTmpFile;
TCHAR szTmpFile[MAX_PATH];
TCHAR szMailbox[MAX_ALIAS_SIZE+1];
} THREAD_PROC_INFO, *PTHREAD_PROC_INFO;

typedef struct tagDL_DELIVERY_PROC_INFO
{
TCHAR szTmpFile[MAX_PATH];
TCHAR szHeader[1024];
DIST_LIST_INFO DLInfo;
DWORD dwAliasDelivered;
LPTSTR * ppszAliasList;
HANDLE hWaitEvent;
HANDLE hFile;
BOOL fDeleteInfoStruct;
} DL_DELIVERY_PROC_INFO, *PDL_DELIVERY_PROC_INFO;

extern "C"
{
HRESULT WINAPI MsgUploadPipeThread
(PTHREAD_PROC_INFO pInfo);
HRESULT WINAPI MsgDownloadThread
(PTHREAD_PROC_INFO pInfo);
HRESULT WINAPI OneMsgDownloadPipeThread
(PTHREAD_PROC_INFO pInfo);
HRESULT WINAPI HeaderDLPipeThread
(PTHREAD_PROC_INFO pInfo);
DWORD WINAPI DistListDeliveryThread
(PDL_DELIVERY_PROC_INFO pDLInfo);
BOOL WINAPI DeleteMessage
(HANDLE hResumeEvt,
LPTSTR szMBox,
LPBYTE pEID);
HANDLE WINAPI OpenMessage
(HANDLE hResumeEvt,
LPTSTR pszMailBox,
LPBYTE pEID,
ULONG ulCmd,
ULONG * pulMsgLen);
HRESULT WINAPI ObjectDelivered
(PDL_DELIVERY_PROC_INFO pInfo,
LPTSTR szObject);
}

///////////////////////////////////////////////////////////////////////////////
// RemoteOpenMsgUploadPipeA()
//
// Parameters
//
// Purpose
//
// Return Value
//
long RemoteOpenMsgUploadPipeA (unsigned char * szSenderMailbox,
long * pPipeNumber,
unsigned char * szCookie)
{
LPTSTR pStrTok;
TCHAR szBuffer[_MAX_PATH];
long lResult = GetServiceState();
if (lResult)
{
return lResult;
}
DWORD dwMailboxID;
if (S_OK != GlobalObjectMap.FindObjFromName (SERVER_USER_MAILBOX, (LPSTR)szSenderMailbox, &dwMailboxID))
{
TraceMessage ("RemoteOpenUploadPipeA: The originator no longer has an account in this server");
return HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER);
}

PTHREAD_PROC_INFO pInfo = (PTHREAD_PROC_INFO)HeapAlloc (ghHeap,
HEAP_ZERO_MEMORY,
sizeof(THREAD_PROC_INFO));
if (NULL == pInfo)
{
TraceMessage ("RemoteOpenMsgUploadPipeA: Failed to allocate info structure");
return E_OUTOFMEMORY;
}

lResult = GetLocalTempFileName (szBuffer);
if (lResult)
{
goto TerminateAndCleanup;
}
lstrcpy (pInfo->szTmpFile, szBuffer);
pInfo->hTmpFile = CreateFile (szBuffer,
GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if (INVALID_HANDLE_VALUE == pInfo->hTmpFile)
{
lResult = HRESULT_FROM_WIN32(GetLastError());
TraceResult ("RemoteOpenMsgUploadPipeA: Failed to create the temp file", lResult);
goto TerminateAndCleanup;
}

pStrTok = &szBuffer[lstrlen(szBuffer)];
while ('\\' != *pStrTok)
{
pStrTok--;
}
pStrTok++;
lstrcpy ((LPSTR)szCookie, pStrTok);

*pPipeNumber = GetNextPipeID();

SECURITY_ATTRIBUTES sa;
SECURITY_DESCRIPTOR sd;

// Initialize the new security descriptor.
InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION);

// Add a NULL descriptor ACL to the security descriptor.
SetSecurityDescriptorDacl (&sd, TRUE, (PACL)NULL, FALSE);

sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = &sd;
sa.bInheritHandle = TRUE;

// Create a pipe where we will expect the transport to send the data
wsprintf (szBuffer, SERVER_PIPE_NAME_FORMAT, *pPipeNumber);
pInfo->hPipe = CreateNamedPipe (szBuffer,
PIPE_ACCESS_INBOUND,
PIPE_WAIT | PIPE_READMODE_BYTE | PIPE_TYPE_BYTE,
1,
IO_BUFFERSIZE,
IO_BUFFERSIZE,
0,
&sa);
if (INVALID_HANDLE_VALUE == pInfo->hPipe || ERROR_INVALID_PARAMETER == (DWORD)pInfo->hPipe)
{
lResult = HRESULT_FROM_WIN32(GetLastError());
TraceResult ("RemoteOpenMsgUploadPipeA: Failed to create pipe", lResult);
}
else
{
DWORD dwThreadID;
HANDLE hThread = CreateThread (NULL,
0,
(LPTHREAD_START_ROUTINE)MsgUploadPipeThread,
(LPVOID)pInfo,
0,
&dwThreadID);
if (hThread)
{
TraceDebugger ("RemoteOpenMsgUploadPipeA: Message Upload thread spawned. ID: %X", dwThreadID);
CloseHandle (hThread);
pInfo = NULL; // The thread will free this memory
}
else
{
lResult = HRESULT_FROM_WIN32(GetLastError());
TraceResult ("RemoteOpenMsgUploadPipeA: Failed to create pipe thread", lResult);
}
}
TerminateAndCleanup :
if (pInfo)
{
if (pInfo->hTmpFile && (ERROR_INVALID_HANDLE != (DWORD)pInfo->hTmpFile))
{
CloseHandle (pInfo->hTmpFile);
}
if (pInfo->hPipe)
{
CloseHandle (pInfo->hPipe);
}
HeapFree (ghHeap, 0, pInfo);
}
if (lResult)
{
if (FACILITY_NULL == HRESULT_FACILITY(lResult))
{
lResult = HRESULT_FROM_WIN32 (lResult);
}
}
return lResult;
}

///////////////////////////////////////////////////////////////////////////////
// RemoteSendMsgToAccountA()
//
// Parameters
//
// Purpose
//
// Return Value
//
long RemoteSendMsgToAccountA (unsigned char * szRecipAccount,
unsigned char * szHeaderInfo,
unsigned char * szCookie)
{
long lResult = GetServiceState();
if (lResult)
{
return lResult;
}
DWORD dwMailboxID;
WINDS_AB_OBJTYPE Type;
if (S_OK != GlobalObjectMap.FindObjAndTypeFromName ((LPSTR)szRecipAccount, &Type, &dwMailboxID))
{
TraceMessage ("RemoteSendMsgToAccountA: Rejecting message for an non-existing recipient");
return HRESULT_FROM_WIN32(ERROR_INVALID_ACCOUNT_NAME);
}

TCHAR szTmpPath[_MAX_PATH];
if (!GetTempPath (_MAX_PATH, szTmpPath))
{
lResult = HRESULT_FROM_WIN32(GetLastError());
TraceResult ("RemoteSendMsgToAccountA: Failed to get temp path", lResult);
return HRESULT_FROM_WIN32 (lResult);
}

lstrcat (szTmpPath, (LPSTR)szCookie);
PTHREAD_PROC_INFO pInfo = NULL;
PDL_DELIVERY_PROC_INFO pDLInfo = NULL;
HANDLE hFile = NULL, hWaitEvent;
RetryOpenFile:
hFile = CreateFile (szTmpPath,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if (INVALID_HANDLE_VALUE == hFile)
{
lResult = HRESULT_FROM_WIN32(GetLastError());
if (HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION) == lResult)
{
lResult = S_OK;
TraceMessage ("RemoteSendMsgToAccountA: Sharing Violation, waiting before retry");
SleepEx (200, FALSE);
goto RetryOpenFile;
}
TraceResult ("RemoteSendMsgToAccountA: Failed to open the temp file", lResult);
return lResult;
}
hWaitEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
if (!hWaitEvent)
{
lResult = HRESULT_FROM_WIN32(GetLastError());
TraceResult ("RemoteSendMsgToAccountA: Failed to create event for I/O thread", lResult);
goto ErrorExit;
}
if (SERVER_USER_MAILBOX == Type)
{
EnterCriticalSection (&g_csIOInfo);
SetEvent (g_IOInfo.hResumeEvent);
lstrcpy (g_IOInfo.szObject, (LPSTR)szRecipAccount);
lstrcpy (g_IOInfo.szHeader, (LPSTR)szHeaderInfo);
g_IOInfo.Action = IO_COPY_MSG_FROM_FILE;
g_IOInfo.hActionCompleted = hWaitEvent;
g_IOInfo.hTmpFile = hFile;
g_IOInfo.fCloseHandle = TRUE;
hFile = NULL;
g_IOInfo.phLastError = &lResult;
LeaveCriticalSection (&g_csIOInfo);
WaitForSingleObject (hWaitEvent, GENERAL_TIME_OUT);
}
else
{
if (SERVER_DISTRIBUTION_LIST == Type)
{
pDLInfo = (PDL_DELIVERY_PROC_INFO)HeapAlloc (ghHeap,
HEAP_ZERO_MEMORY,
sizeof(DL_DELIVERY_PROC_INFO));
if (NULL == pDLInfo)
{
TraceMessage ("RemoteSendMsgToAccountA: Failed to allocate info structure");
lResult = E_OUTOFMEMORY;
goto ErrorExit;
}
pDLInfo->fDeleteInfoStruct = TRUE;
lstrcpy (pDLInfo->DLInfo.szDLAlias, (LPSTR)szRecipAccount);
lstrcpy (pDLInfo->szHeader, (LPSTR)szHeaderInfo);
GetLocalTempFileName (pDLInfo->szTmpFile);
if (FALSE == CopyFile (szTmpPath, pDLInfo->szTmpFile, FALSE))
{
lResult = HRESULT_FROM_WIN32(GetLastError());
TraceResult ("RemoteSendMsgToAccountA: Failed to create duplicate tmp file for DL delivery", lResult);
goto ErrorExit;
}

EnterCriticalSection (&g_csIOInfo);
SetEvent (g_IOInfo.hResumeEvent);
g_IOInfo.Action = IO_GET_DL_PROPERTIES;
g_IOInfo.hActionCompleted = hWaitEvent;
g_IOInfo.phLastError = &lResult;
g_IOInfo.pDLInfo = &(pDLInfo->DLInfo);
LeaveCriticalSection (&g_csIOInfo);
WaitForSingleObject (hWaitEvent, GENERAL_TIME_OUT);
if (lResult)
{
goto ErrorExit;
}
DWORD dwThreadID;
HANDLE hThread = CreateThread (NULL,
0,
(LPTHREAD_START_ROUTINE)DistListDeliveryThread,
(LPVOID)pDLInfo,
CREATE_SUSPENDED,
&dwThreadID);
if (hThread)
{
TraceDebugger ("RemoteSendMsgToAccountA: Server DL delivery thread spawned. ID: %X", dwThreadID);
SetThreadPriority (hThread, THREAD_PRIORITY_BELOW_NORMAL);

ResumeThread (hThread);
CloseHandle (hThread);
pDLInfo = NULL; // The thread will free this memory
}
else
{
lResult = HRESULT_FROM_WIN32(GetLastError());
TraceResult ("RemoteSendMsgToAccountA: Failed to create Server DL delivery thread", lResult);
}
}
else
{
TraceMessage ("RemoteSendMsgToAccountA: This type has not been implemented yet");
lResult = HRESULT_FROM_WIN32(ERROR_INVALID_ACCOUNT_NAME);
}
}
ErrorExit:
if (lResult)
{
if (pDLInfo)
{
DeleteFile (pDLInfo->szTmpFile);
HeapFree (ghHeap, 0, pDLInfo);
}
if (pInfo)
{
DeleteFile (pInfo->szTmpFile);
HeapFree (ghHeap, 0, pInfo);
}
}
if (hFile)
{
CloseHandle (hFile);
}
if (hWaitEvent)
{
CloseHandle (hWaitEvent);
}
return lResult;
}

///////////////////////////////////////////////////////////////////////////////
// RemoteFinishUpload()
//
// Parameters
//
// Purpose
//
// Return Value
//
long RemoteFinishUpload (unsigned char * szCookie)
{
long lResult = GetServiceState();
if (lResult)
{
return lResult;
}
TCHAR szTmpPath[_MAX_PATH];
if (!GetTempPath (_MAX_PATH, szTmpPath))
{
lResult = HRESULT_FROM_WIN32(GetLastError());
TraceResult ("RemoteFinishUpload: Failed to get temp path", lResult);
lResult = lResult;
}
else
{
lstrcat (szTmpPath, (LPSTR)szCookie);
DeleteFile (szTmpPath);
}
return lResult;
}

///////////////////////////////////////////////////////////////////////////////
// RemoteOpenHeaderDownloadPipeA()
//
// Parameters
//
// Purpose
//
// Return Value
//
long RemoteOpenHeaderDownloadPipeA (unsigned char * szMailbox, long * pPipeNumber)
{
long lResult = GetServiceState();
if (lResult)
{
return lResult;
}
DWORD dwMailboxID;
if (S_OK != GlobalObjectMap.FindObjFromName (SERVER_USER_MAILBOX, (LPSTR)szMailbox, &dwMailboxID))
{
TraceMessage ("RemoteOpenHeaderDownloadPipeA: Invalid user mailbox");
return HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER);
}

*pPipeNumber = GetNextPipeID();

TCHAR szBuffer[_MAX_PATH];
lResult = GetLocalTempFileName (szBuffer);
if (lResult)
{
return lResult;
}
HANDLE hFile = CreateFile (szBuffer,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
NULL);
if (INVALID_HANDLE_VALUE == hFile)
{
lResult = HRESULT_FROM_WIN32(GetLastError());
TraceResult ("RemoteOpenHeaderDownloadPipeA: Failed to temp file", lResult);
return lResult;
}

SECURITY_ATTRIBUTES sa;
SECURITY_DESCRIPTOR sd;

// Initialize the new security descriptor.
InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION);

// Add a NULL descriptor ACL to the security descriptor.
SetSecurityDescriptorDacl (&sd, TRUE, (PACL)NULL, FALSE);

sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = &sd;
sa.bInheritHandle = TRUE;

wsprintf (szBuffer, SERVER_PIPE_NAME_FORMAT, *pPipeNumber);
HANDLE hPipe = CreateNamedPipe (szBuffer,
PIPE_ACCESS_OUTBOUND,
PIPE_WAIT | PIPE_READMODE_BYTE | PIPE_TYPE_BYTE,
1,
IO_BUFFERSIZE,
IO_BUFFERSIZE,
0,
&sa);
if (INVALID_HANDLE_VALUE == hPipe || ERROR_INVALID_PARAMETER == (DWORD)hPipe)
{
lResult = HRESULT_FROM_WIN32(GetLastError());
TraceResult ("RemoteOpenHeaderDownloadPipeA: Failed to create pipe", lResult);
}
else
{
PTHREAD_PROC_INFO pInfo = (PTHREAD_PROC_INFO)HeapAlloc (ghHeap,
HEAP_ZERO_MEMORY,
sizeof(THREAD_PROC_INFO));
if (pInfo)
{
lstrcpy (pInfo->szMailbox, (LPSTR)szMailbox);
pInfo->hPipe = hPipe;
pInfo->hTmpFile = hFile;
DWORD dwThreadID;
HANDLE hThread = CreateThread (NULL,
0,
(LPTHREAD_START_ROUTINE)HeaderDLPipeThread,
(LPVOID)pInfo,
0,
&dwThreadID);
if (hThread)
{
TraceDebugger ("RemoteOpenHeaderDownloadPipeA: Header download thread spawned. ID: %X", dwThreadID);
CloseHandle (hThread);
}
else
{
lResult = HRESULT_FROM_WIN32(GetLastError());
TraceResult ("RemoteOpenHeaderDownloadPipeA: Failed to create pipe thread", lResult);
}
}
else
{
TraceMessage ("RemoteOpenHeaderDownloadPipeA: Failed to allocate info structure");
lResult = E_OUTOFMEMORY;
}
}
if (lResult)
{
CloseHandle (hPipe);
CloseHandle (hFile);
}
return lResult;
}

///////////////////////////////////////////////////////////////////////////////
// RemoteOpenMsgDownloadPipeA()
//
// Parameters
//
// Purpose
//
// Return Value
//
long RemoteOpenMsgDownloadPipeA (unsigned char * szMailbox,
unsigned long * pPipeNumber)
{
long lResult = GetServiceState();
if (lResult)
{
return lResult;
}
DWORD dwMailboxID;
if (S_OK != GlobalObjectMap.FindObjFromName (SERVER_USER_MAILBOX, (LPSTR)szMailbox, &dwMailboxID))
{
TraceMessage ("RemoteOpenMsgDownloadPipeA: Invalid user mailbox");
return HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER);
}

PTHREAD_PROC_INFO pInfo = (PTHREAD_PROC_INFO)HeapAlloc (ghHeap,
HEAP_ZERO_MEMORY,
sizeof(THREAD_PROC_INFO));
if (!pInfo)
{
TraceMessage ("RemoteOpenMsgDownloadPipeA: Failed to allocate info structure");
return E_OUTOFMEMORY;
}
lstrcpy (pInfo->szMailbox, (LPSTR)szMailbox);

*pPipeNumber = GetNextPipeID();

SECURITY_ATTRIBUTES sa;
SECURITY_DESCRIPTOR sd;

// Initialize the new security descriptor.
InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION);

// Add a NULL descriptor ACL to the security descriptor.
SetSecurityDescriptorDacl (&sd, TRUE, (PACL)NULL, FALSE);

sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = &sd;
sa.bInheritHandle = TRUE;

TCHAR szPipeName[64];
wsprintf (szPipeName, SERVER_PIPE_NAME_FORMAT, *pPipeNumber);
pInfo->hPipe = CreateNamedPipe (szPipeName,
PIPE_ACCESS_DUPLEX,
PIPE_WAIT | PIPE_READMODE_BYTE | PIPE_TYPE_BYTE,
1,
IO_BUFFERSIZE,
IO_BUFFERSIZE,
0,
&sa);
if (INVALID_HANDLE_VALUE == pInfo->hPipe || ERROR_INVALID_PARAMETER == (DWORD)pInfo->hPipe)
{
lResult = HRESULT_FROM_WIN32(GetLastError());
TraceResult ("RemoteOpenMsgDownloadPipeA: Failed to create pipe", lResult);
}
else
{
DWORD dwThreadID;
HANDLE hThread = CreateThread (NULL,
0,
(LPTHREAD_START_ROUTINE)MsgDownloadThread,
(LPVOID)pInfo,
0,
&dwThreadID);
if (hThread)
{
TraceDebugger ("RemoteOpenMsgDownloadPipeA: Header download thread spawned. ID: %X", dwThreadID);
CloseHandle (hThread);
}
else
{
lResult = HRESULT_FROM_WIN32(GetLastError());
TraceResult ("RemoteOpenMsgDownloadPipeA: Failed create doanload thread", lResult);
}
}

if (lResult)
{
if (FACILITY_NULL == HRESULT_FACILITY(lResult))
{
lResult = HRESULT_FROM_WIN32 (lResult);
}
HeapFree (ghHeap, 0, pInfo);
}
return lResult;
}

///////////////////////////////////////////////////////////////////////////////
// MsgUploadPipeThread()
//
// Parameters
//
// Purpose
//
// Return Value
//
HRESULT WINAPI MsgUploadPipeThread (PTHREAD_PROC_INFO pInfo)
{
BYTE abBuffer[IO_BUFFERSIZE];
DWORD dwBytesWrite, dwBytesRead = 0;

ConnectNamedPipe (pInfo->hPipe, NULL);
HRESULT hResult = S_OK;
do
{
// Wait until the client writes to the pipe
if (!ReadFile (pInfo->hPipe, abBuffer, IO_BUFFERSIZE, &dwBytesRead, NULL))
{
// There was an error and we can't continue
hResult = HRESULT_FROM_WIN32(GetLastError());
if (hResult != HRESULT_FROM_WIN32(ERROR_BROKEN_PIPE))
{
TraceResult ("MsgUploadPipeThread: Failed to read from the msg upload pipe", hResult);
}
else
{
hResult = 0;
}
}
if (dwBytesRead && !hResult)
{
if (!WriteFile (pInfo->hTmpFile, abBuffer, dwBytesRead, &dwBytesWrite, NULL))
{
hResult = HRESULT_FROM_WIN32(GetLastError());
TraceResult ("MsgUploadPipeThread: Failed to write to the local tmp file", hResult);
}
}
} while (dwBytesRead && !hResult);

TraceResult ("MsgUploadPipeThread", hResult);
CloseHandle (pInfo->hTmpFile);
CloseHandle (pInfo->hPipe);
if (hResult)
{
DeleteFile (pInfo->szTmpFile);
}
HeapFree (ghHeap, 0, pInfo);
return hResult;
}

///////////////////////////////////////////////////////////////////////////////
// HeaderDLPipeThread()
//
// Parameters
//
// Purpose
//
// Return Value
//
HRESULT WINAPI HeaderDLPipeThread (PTHREAD_PROC_INFO pInfo)
{
BYTE abBuffer[IO_BUFFERSIZE];
DWORD dwBytesRead, dwBytesWrite;
HRESULT hResult;
HANDLE hWaitEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
if (!hWaitEvent)
{
hResult = HRESULT_FROM_WIN32(GetLastError());
TraceResult ("HeaderDLPipeThread: Failed to create event for I/O thread", hResult);
goto Error;
}

EnterCriticalSection (&g_csIOInfo);
SetEvent (g_IOInfo.hResumeEvent);
lstrcpy (g_IOInfo.szObject, pInfo->szMailbox);
g_IOInfo.Action = IO_COPY_HEADERS_TO_FILE;
g_IOInfo.hTmpFile = pInfo->hTmpFile;
g_IOInfo.hActionCompleted = hWaitEvent;
g_IOInfo.phLastError = &hResult;
LeaveCriticalSection (&g_csIOInfo);

WaitForSingleObject (hWaitEvent, GENERAL_TIME_OUT);
CloseHandle (hWaitEvent);
if (hResult)
{
goto Error;
}

// Wait until the client connects to the pipe
ConnectNamedPipe (pInfo->hPipe, NULL);

do
{
if (!ReadFile (pInfo->hTmpFile, abBuffer, IO_BUFFERSIZE, &dwBytesRead, NULL))
{
hResult = HRESULT_FROM_WIN32(GetLastError());
TraceResult ("HeaderDLPipeThread: Failed to read the tmp file", hResult);
}
if (dwBytesRead && !hResult)
{
if (!WriteFile (pInfo->hPipe, abBuffer, dwBytesRead, &dwBytesWrite, NULL))
{
hResult = HRESULT_FROM_WIN32(GetLastError());
TraceResult ("HeaderDLPipeThread: Failed to write to the pipe", hResult);
}
}
} while (dwBytesRead && !hResult);

Error:
CloseHandle (pInfo->hTmpFile);
CloseHandle (pInfo->hPipe);
HeapFree (ghHeap, 0, pInfo);
return hResult;
}

////////////////////////////////////////////////////////////////////////////
// MsgDownloadThread()
//
// Parameters
// pInfo pointer to a THREAD_PROC_INFO containing the handle to
// the open pipe for data transmission to the remote XP
//
// Purpose
// This is the worker thread that is spawned when a request to
// messages arrives from the remote XP via RemoteOpenMsgDownloadPipe.
// Once the pipe has been opened the remote XP sends command messages
// on the pipe to request data from the server. These command messages
// are of 3 types: MSG_MOVE, MSG_DOWNLOAD, and MSG_DELETE. The command
// messages have a msg ID that tells the requested operation. When this
// thread is started, it creates an event object for synchronization
// with the storage thread that accesses the message database then
// immediately blocks waiting for the XP to connect to the pipe. We
// unblock and start reading command requests. Each mail message we
// process goes through the following cycle:
//
// 1. read the command message, this contains the operation
// requested by the XP and the mail message's entry ID
//
// 2. if the command is to HANG UP, quit, otherwise:
//
// look up the entry ID of the mail message in the storage.
// if unsuccessful, send a NAK message back to the XP
//
// if the request is to DELETE, delete the message and if
// successful send an ACK message back to the XP, otherwise
// send a NAK message back, then start on the next request
//
// if the request is to MOVE or DOWNLOAD, get the mail message's
// size, stuff it in an ACK message and write it to the XP.then
// stream the mail message over the pipe
//
// 3. read the next command
//
// We treat write errors to the pipe as fatal and disconnect.
//
// Return Value
//
HRESULT WINAPI MsgDownloadThread(PTHREAD_PROC_INFO pInfo)
{
MSG_HDR MsgHdr;
HANDLE hTmpFile;
DWORD dwBytesRead, dwBytesWritten;
HRESULT hResult = S_OK;
HANDLE hResumeEvt = CreateEvent(NULL, FALSE, FALSE, NULL);

if (!hResumeEvt)
{
hResult = HRESULT_FROM_WIN32(GetLastError());
TraceResult ("MsgDownloadThread: Failed to create event", hResult);
goto Quit;
}

// block until XP connects to us
ConnectNamedPipe (pInfo->hPipe, NULL);

while (TRUE)
{
if (!ReadFile(pInfo -> hPipe, &MsgHdr, sizeof(MSG_HDR), &dwBytesRead, NULL))
break; // Out of the WHILE() loop

ASSERT (dwBytesRead == sizeof(MSG_HDR));

switch (MsgHdr.ulMID)
{
case MSG_MOVE: // Fall through
case MSG_DOWNLOAD:

// tell IO thread to open msg's container, write it
// to a temp file and return us its size and handle

hTmpFile = OpenMessage(hResumeEvt,
pInfo->szMailbox,
MsgHdr.Info.EID,
MsgHdr.ulMID,
&MsgHdr.Info.
ulMsgLen);
if (!hTmpFile)
{
MsgHdr.ulMID = OP_FAILED; // send a NAK back to XP
MsgHdr.Info.ulMsgLen = 0;
if (!WriteFile(pInfo->hPipe, &MsgHdr, sizeof(MSG_HDR), &dwBytesWritten, NULL))
{
goto Quit;
}
}
else

{    
MsgHdr.ulMID = OP_STARTED; // send an ACK to XP
// Bail on any write errors, the connection is down
if (!WriteFile(pInfo->hPipe, &MsgHdr, sizeof(MSG_HDR), &dwBytesWritten, NULL))
{
goto Quit;
}

// set seek pointer to beginning of temp file
if ((DWORD)-1 == SetFilePointer(hTmpFile, 0, NULL, FILE_BEGIN))
break;

if (NO_ERROR != FileCopy(pInfo -> hPipe, hTmpFile, MsgHdr.Info.ulMsgLen))
goto Quit;

}
break;

case MSG_DELETE:
// tell storage thread to delete msg's substorage
MsgHdr.ulMID = (DeleteMessage (hResumeEvt, pInfo->szMailbox, MsgHdr.Info.EID) ? OP_COMPLETE : OP_FAILED);
MsgHdr.Info.ulMsgLen = 0;

// send ACK/NAK to XP
if (!WriteFile (pInfo->hPipe, &MsgHdr, sizeof(MSG_HDR), &dwBytesWritten, NULL))
{
goto Quit;
}
break;

case GOODBYE: // HANG UP
goto Quit;

default :
// don't attempt to resynchronize
goto Quit;
}
}

Quit:
CloseHandle (pInfo->hPipe);
CloseHandle (hResumeEvt);
HeapFree (ghHeap, 0, pInfo);
return hResult;
}

////////////////////////////////////////////////////////////////////////////
// DeleteMessage()
//
// Parameters
// hResumeEvt handle to event object for signaling completion
// of storage operation
// szMailBox Name of mailbox for the mail msg we're handling
// pEID entry ID of msg we're handling
// Purpose
// Sends a DELETE request to the storage thread.The request, along
// with the message's EID, and the mailbox name where the message
// resides are passed to the storage thread in g_IOInfo. We then block
// on an event from the storage thread signaling completetion of the
// requested operations. The storage finds the mail message and
// deletes it.
//
// Return Value
// TRUE on success,FALSE otherwise
//
BOOL WINAPI DeleteMessage(HANDLE hResumeEvt, LPTSTR pszMailBox, LPBYTE pEID)
{
HRESULT hResult = S_OK;
// In Windows NT 3.5 OLE storages can only be accessed by a single thread, so use
// this global data struct to pass data to the storage thread
EnterCriticalSection (&g_csIOInfo);
SetEvent(g_IOInfo.hResumeEvent);
g_IOInfo.Action = IO_DELETE_MSG_IN_MAILBOX;
g_IOInfo.hActionCompleted = hResumeEvt;
g_IOInfo.phLastError = &hResult;
g_IOInfo.dwObjID = (DWORD)atol((LPCSTR) &pEID[4]);
lstrcpy (g_IOInfo.szObject, pszMailBox);
LeaveCriticalSection (&g_csIOInfo);

// Block until the storage thread finishes
WaitForSingleObject(hResumeEvt, GENERAL_TIME_OUT);
return (S_OK == hResult ? TRUE : FALSE);
}

////////////////////////////////////////////////////////////////////////////
// OpenMessage()
//
// Parameters
// hResumeEvt handle to event object for signaling completion
// of storage operation
// pszMailBox name of mailbox for the mail msg we're handling
// pEID entry ID of msg we're handling
// ulCmd operation to perform on the message
// pulMsgLen to pass back the size of the data stream
// Purpose
// Create a temporary file to hold the mail message, then send
// a request for the operation passed in ulCmd to the storage
// thread.The request, along with the message's EID, the mailbox
// name where the message resides, and the handle to the tempfile
// are passed to the storage thread in g_IOInfo. We then block on
// an event from the storage thread signaling completetion of the
// requested operations. The storage finds the mail message and
// copies it into the tempfile passing back the size in pulMsgLen.
//
// Return Value
// Handle of temp file on success, NULL otherwise
//
HANDLE WINAPI OpenMessage(HANDLE hResumeEvt,
LPTSTR pszMailBox,
LPBYTE pEID,
ULONG ulCmd,
ULONG * pulMsgLen)
{
HANDLE hTempFile = NULL;
HRESULT hResult;
TCHAR szTempFile[MAX_PATH];
ACTION Action;
// creates a unique tempfile name
if (GetLocalTempFileName (szTempFile))
{
return NULL;
}

switch (ulCmd)
{
case MSG_DOWNLOAD :
Action = IO_COPY_MSG_TO_FILE;
break;

case MSG_MOVE :
Action = IO_MOVE_MSG_TO_FILE;
break;

default :
TraceMessage ("OpenMessage: Invalid command received");
ASSERT (FALSE);
return NULL;
}

// create tempfile to hold message
if (! (hTempFile = CreateFile(szTempFile,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
NULL)))
return NULL;

// OLE storages can only be accessed by a single thread, so use
// this global data struct to pass data to the storage thread

EnterCriticalSection (&g_csIOInfo);
SetEvent (g_IOInfo.hResumeEvent);
g_IOInfo.Action = Action; // requested operation
g_IOInfo.hActionCompleted = hResumeEvt; // signal us on this event
g_IOInfo.phLastError = &hResult;
g_IOInfo.dwObjID = (DWORD) atol((const char *) &pEID[4]);
g_IOInfo.hTmpFile = hTempFile; // storage thread writes msg here
g_IOInfo.pdwData = pulMsgLen; // storage thread writes length here
lstrcpy (g_IOInfo.szObject, pszMailBox);
LeaveCriticalSection (&g_csIOInfo);

// block until storage thread finishes
WaitForSingleObject(hResumeEvt, GENERAL_TIME_OUT);
return (hResult ? NULL : hTempFile);
}

////////////////////////////////////////////////////////////////////////////
// DistListDeliveryThread()
//
// Parameters
//
// Purpose
// This function is RECURSIVE
//
// Return Value
//
DWORD WINAPI DistListDeliveryThread (PDL_DELIVERY_PROC_INFO pDLInfo)
{
DLM_LIST_A * pNode, *pSubNode;
DL_DELIVERY_PROC_INFO SubDLInfo = { 0 };
DWORD j, dwResult = S_OK;
HRESULT hResult;
HANDLE hWaitEvent, hFile;
ObjectDelivered (pDLInfo, pDLInfo->DLInfo.szDLAlias);
if (NULL == pDLInfo->hFile)
{
hFile = CreateFile (pDLInfo->szTmpFile,
GENERIC_READ,
0,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE,
NULL);
if (INVALID_HANDLE_VALUE == hFile)
{
dwResult = HRESULT_FROM_WIN32(GetLastError());
TraceResult ("DistListDeliveryThread: Failed to open the temp file", dwResult);
goto ErrorExit;
}
pDLInfo->hFile = hFile;
}
else
{
hFile = pDLInfo->hFile;
}
if (NULL == pDLInfo->hWaitEvent)
{
hWaitEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
if (!hWaitEvent)
{
TraceResult ("DistListDeliveryThread: Failed to create event for I/O thread", HRESULT_FROM_WIN32(GetLastError()));
}
pDLInfo->hWaitEvent = hWaitEvent;
}
else
{
hWaitEvent = pDLInfo->hWaitEvent;
}
// Process the USER mailboxes first
pNode = (DLM_LIST_A *)pDLInfo->DLInfo.pMembers;
while (pNode)
{
if (SERVER_USER_MAILBOX != (WINDS_AB_OBJTYPE)pNode->Info.dwMemberType)
{
continue;
}
SetFilePointer (hFile, 0, NULL, FILE_BEGIN);
EnterCriticalSection (&g_csIOInfo);
SetEvent (g_IOInfo.hResumeEvent);
lstrcpy (g_IOInfo.szObject, (LPSTR)pNode->Info.szMemberAlias);
lstrcpy (g_IOInfo.szHeader, pDLInfo->szHeader);
g_IOInfo.Action = IO_COPY_MSG_FROM_FILE;
g_IOInfo.hActionCompleted = hWaitEvent;
g_IOInfo.hTmpFile = hFile;
g_IOInfo.fCloseHandle = FALSE;
g_IOInfo.phLastError = &hResult;
LeaveCriticalSection (&g_csIOInfo);
WaitForSingleObject (hWaitEvent, GENERAL_TIME_OUT);

// The message was successfully delivered to the recipient, so add it to the "DELIVERED" list
if (S_OK == hResult)
{
ObjectDelivered (pDLInfo, (LPSTR)pNode->Info.szMemberAlias);
}
pNode = pNode->pNext;
}
lstrcpy (SubDLInfo.szHeader, pDLInfo->szHeader);
SubDLInfo.hFile = hFile;
SubDLInfo.hWaitEvent = hWaitEvent;

// Process the Distribution lists second
pNode = (DLM_LIST_A *)pDLInfo->DLInfo.pMembers;
while (pNode)
{
if (SERVER_DISTRIBUTION_LIST != (WINDS_AB_OBJTYPE)pNode->Info.dwMemberType)
{
continue;
}
for (j=0; j<pDLInfo->dwAliasDelivered; j++)
{
// If the message has already been delivered to this sub DL, skip it
if (0 == lstrcmp ((LPSTR)pNode->Info.szMemberAlias, pDLInfo->ppszAliasList[j]))
{
continue;
}
}

lstrcpy (SubDLInfo.DLInfo.szDLAlias, (LPSTR)pNode->Info.szMemberAlias);
SubDLInfo.fDeleteInfoStruct = FALSE;
SubDLInfo.DLInfo.pMembers = NULL;
SubDLInfo.dwAliasDelivered = pDLInfo->dwAliasDelivered;
SubDLInfo.ppszAliasList = pDLInfo->ppszAliasList;

EnterCriticalSection (&g_csIOInfo);
SetEvent (g_IOInfo.hResumeEvent);
g_IOInfo.Action = IO_GET_DL_PROPERTIES;
g_IOInfo.hActionCompleted = hWaitEvent;
g_IOInfo.phLastError = &hResult;
g_IOInfo.pDLInfo = &(SubDLInfo.DLInfo);
LeaveCriticalSection (&g_csIOInfo);
WaitForSingleObject (hWaitEvent, GENERAL_TIME_OUT);
if (hResult)
{
TraceResult ("DistListDeliveryThread: Could not get DL properties", hResult);
continue;
}

pSubNode = (DLM_LIST_A *)SubDLInfo.DLInfo.pMembers;
while (pSubNode)
{
for (j=0; j<pDLInfo->dwAliasDelivered; j++)
{
// If the message has already been delivered to a member of the Sub DL
// mark the entry in the sub DL so that it won't deliver to that recipient.
if (0 == lstrcmp ((LPSTR)pSubNode->Info.szMemberAlias, pDLInfo->ppszAliasList[j]))
{
pSubNode->Info.dwMemberType = (DWORD)UNDEFINED_OBJECT_TYPE;
}
}
pSubNode = pSubNode->pNext;
}
// Deliver to the members of the sub-distribution list
hResult = DistListDeliveryThread (&SubDLInfo);
// Remember how many recipients were handled in this sub DL
pDLInfo->dwAliasDelivered = SubDLInfo.dwAliasDelivered;
pDLInfo->ppszAliasList = SubDLInfo.ppszAliasList;
if (SubDLInfo.DLInfo.pMembers)
{
HeapFree (ghHeap, 0, SubDLInfo.DLInfo.pMembers);
}
pNode = pNode->pNext;
}

ErrorExit:
if (pDLInfo->fDeleteInfoStruct)
{
CloseHandle (pDLInfo->hWaitEvent);
CloseHandle (pDLInfo->hFile);
DeleteFile (pDLInfo->szTmpFile);
if (pDLInfo->DLInfo.pMembers)
{
HeapFree (ghHeap, 0, pDLInfo->DLInfo.pMembers);
}
if (pDLInfo->ppszAliasList)
{
for (j=0; j<pDLInfo->dwAliasDelivered; j++)
{
HeapFree (ghHeap, 0, pDLInfo->ppszAliasList[j]);
}
HeapFree (ghHeap, 0, pDLInfo->ppszAliasList);
}
HeapFree (ghHeap, 0, pDLInfo);
}
return dwResult;
}

////////////////////////////////////////////////////////////////////////////
// ObjectDelivered()
//
// Parameters
//
// Purpose
//
// Return Value
//
HRESULT WINAPI ObjectDelivered (PDL_DELIVERY_PROC_INFO pDLInfo, LPTSTR szObject)
{
DWORD dwIndex = pDLInfo->dwAliasDelivered;
if (NULL == pDLInfo->ppszAliasList)
{
ASSERT (0 == pDLInfo->dwAliasDelivered);
pDLInfo->ppszAliasList = (LPTSTR *)HeapAlloc (ghHeap, 0, sizeof(LPTSTR));
if (NULL == pDLInfo->ppszAliasList)
{
TraceMessage ("ObjectDelivered: Failed to allocate Delivered Alias List");
return E_OUTOFMEMORY;
}
}
else
{
pDLInfo->ppszAliasList = (LPTSTR *)HeapReAlloc (ghHeap,
0,
pDLInfo->ppszAliasList,
sizeof(LPTSTR) * (pDLInfo->dwAliasDelivered + 1));
if (NULL == pDLInfo->ppszAliasList[dwIndex])
{
TraceMessage ("ObjectDelivered: Failed to re-allocate Delivered Alias List");
return E_OUTOFMEMORY;
}
}

pDLInfo->ppszAliasList[dwIndex] = (LPTSTR)HeapAlloc (ghHeap, 0, (sizeof (TCHAR)*(lstrlen(szObject)+1)));
if (NULL == pDLInfo->ppszAliasList[dwIndex])
{
TraceMessage ("ObjectDelivered: Failed to allocate object alias string");
return E_OUTOFMEMORY;
}
lstrcpy (pDLInfo->ppszAliasList[dwIndex], szObject);
pDLInfo->dwAliasDelivered++;
return S_OK;
}

///////////////////////////////////////////////////////////////////////////////
// RemoteCheckNewMailA()
//
// Parameters
//
// Purpose
//
// Return Value
//
long RemoteCheckNewMailA (unsigned char * szMailbox, unsigned long * pulPending)
{
long lResult = GetServiceState();
if (lResult)
{
return lResult;
}
HANDLE hWaitEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
if (!hWaitEvent)
{
lResult = HRESULT_FROM_WIN32(GetLastError());
TraceResult ("RemoteCheckNewMailA: Failed to create event for I/O thread", lResult);
return lResult;
}
*pulPending = 0;
EnterCriticalSection (&g_csIOInfo);
SetEvent (g_IOInfo.hResumeEvent);
lstrcpy (g_IOInfo.szObject, (LPSTR)szMailbox);
g_IOInfo.Action = IO_CHECK_PENDING_MESSAGES;
g_IOInfo.hActionCompleted = hWaitEvent;
g_IOInfo.phLastError = &lResult;
g_IOInfo.pdwData = pulPending;
LeaveCriticalSection (&g_csIOInfo);
WaitForSingleObject (hWaitEvent, GENERAL_TIME_OUT);
CloseHandle (hWaitEvent);
return lResult;
}

///////////////////////////////////////////////////////////////////////////////
// RemoteOpenOneMsgDownloadPipeA()
//
// Parameters
//
// Purpose
//
// Return Value
//
long RemoteOpenOneMsgDownloadPipeA (unsigned char * szMailbox,
long * pPipeNumber)
{
long lResult = GetServiceState();
if (lResult)
{
return lResult;
}
TCHAR szTmpFile[_MAX_PATH], szPipeName[64];
lResult = GetLocalTempFileName (szTmpFile);
if (lResult)
{
return lResult;
}

PTHREAD_PROC_INFO pInfo = (PTHREAD_PROC_INFO)HeapAlloc (ghHeap,
HEAP_ZERO_MEMORY,
sizeof(THREAD_PROC_INFO));
if (NULL == pInfo)
{
TraceMessage ("RemoteOpenOneMsgDownloadPipeA: Failed to allocate info structure");
return E_OUTOFMEMORY;
}
DWORD dwThreadID, dwMsgID;
HANDLE hThread, hWaitEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
if (!hWaitEvent)
{
lResult = HRESULT_FROM_WIN32(GetLastError());
TraceResult ("RemoteOpenOneMsgDownloadPipeA: Failed to create event for I/O thread", lResult);
goto ErrorExit;
}

pInfo->hTmpFile = CreateFile (szTmpFile,
GENERIC_WRITE | GENERIC_READ,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_DELETE_ON_CLOSE,
NULL);
if (INVALID_HANDLE_VALUE == pInfo->hTmpFile)
{
lResult = HRESULT_FROM_WIN32(GetLastError());
TraceResult ("RemoteOpenOneMsgDownloadPipeA: Failed to create the temp file", lResult);
goto ErrorExit;
}

*pPipeNumber = GetNextPipeID();

SECURITY_ATTRIBUTES sa;
SECURITY_DESCRIPTOR sd;

// Initialize the new security descriptor.
InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION);

// Add a NULL descriptor ACL to the security descriptor.
SetSecurityDescriptorDacl (&sd, TRUE, (PACL)NULL, FALSE);

sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = &sd;
sa.bInheritHandle = TRUE;

wsprintf (szPipeName, SERVER_PIPE_NAME_FORMAT, *pPipeNumber);
pInfo->hPipe = CreateNamedPipe (szPipeName,
PIPE_ACCESS_OUTBOUND,
PIPE_WAIT | PIPE_READMODE_BYTE | PIPE_TYPE_BYTE,
1,
IO_BUFFERSIZE,
IO_BUFFERSIZE,
0,
&sa);
if (INVALID_HANDLE_VALUE == pInfo->hPipe || ERROR_INVALID_PARAMETER == (DWORD)pInfo->hPipe)
{
lResult = HRESULT_FROM_WIN32(GetLastError());
TraceResult ("RemoteOpenMsgDownloadPipeA: Failed to create pipe", lResult);
goto ErrorExit;
}

EnterCriticalSection (&g_csIOInfo);
SetEvent (g_IOInfo.hResumeEvent);
lstrcpy (g_IOInfo.szObject, (LPSTR)szMailbox);
g_IOInfo.Action = IO_MOVE_NEXT_MSG_TO_FILE;
g_IOInfo.hActionCompleted = hWaitEvent;
g_IOInfo.hTmpFile = pInfo->hTmpFile;
g_IOInfo.pdwData = &dwMsgID;
g_IOInfo.phLastError = &lResult;
LeaveCriticalSection (&g_csIOInfo);
WaitForSingleObject (hWaitEvent, GENERAL_TIME_OUT);
if (lResult)
{
goto ErrorExit;
}

hThread = CreateThread (NULL,
0,
(LPTHREAD_START_ROUTINE)OneMsgDownloadPipeThread,
(LPVOID)pInfo,
0,
&dwThreadID);
if (hThread)
{
TraceDebugger ("RemoteOpenOneMsgDownloadPipeA: Message Download thread spawned. ID: %X", dwThreadID);
CloseHandle (hThread);
pInfo = NULL; // The thread will free this memory
EnterCriticalSection (&g_csIOInfo);
SetEvent (g_IOInfo.hResumeEvent);
lstrcpy (g_IOInfo.szObject, (LPSTR)szMailbox);
g_IOInfo.Action = IO_DELETE_MSG_IN_MAILBOX;
g_IOInfo.hActionCompleted = NULL;
g_IOInfo.dwObjID = dwMsgID;
g_IOInfo.phLastError = NULL;
LeaveCriticalSection (&g_csIOInfo);
}
else
{
lResult = HRESULT_FROM_WIN32(GetLastError());
TraceResult ("RemoteOpenOneMsgDownloadPipeA: Failed to create pipe thread", lResult);
}

ErrorExit:
if (pInfo)
{
if (pInfo->hTmpFile)
{
CloseHandle (pInfo->hTmpFile);
}
if (pInfo->hPipe)
{
CloseHandle (pInfo->hPipe);
}
HeapFree (ghHeap, 0, pInfo);
}
if (hWaitEvent)
{
CloseHandle (hWaitEvent);
}
#ifdef _DEBUG
if (lResult && S_FALSE != lResult)
{
TraceResult ("RemoteOpenOneMsgDownloadPipeA", lResult);
}
#endif // _DEBUG
return lResult;
}

///////////////////////////////////////////////////////////////////////////////
// OneMsgDownloadPipeThread()
//
// Parameters
//
// Purpose
//
// Return Value
//
HRESULT WINAPI OneMsgDownloadPipeThread (PTHREAD_PROC_INFO pInfo)
{
DWORD dwBytesRead, dwBytesWritten;
BYTE abBuffer[IO_BUFFERSIZE];
HRESULT hResult = S_OK;

SetFilePointer (pInfo->hTmpFile, 0, NULL, FILE_BEGIN);

ConnectNamedPipe (pInfo->hPipe, NULL);

do
{
if (!ReadFile (pInfo->hTmpFile, abBuffer, IO_BUFFERSIZE, &dwBytesRead, NULL))
{
hResult = HRESULT_FROM_WIN32(GetLastError());
TraceResult ("OneMsgDownloadPipeThread: Failed to read the tmp file", hResult);
}
if (dwBytesRead && !hResult)
{
if (!WriteFile (pInfo->hPipe, abBuffer, dwBytesRead, &dwBytesWritten, NULL))
{
hResult = HRESULT_FROM_WIN32(GetLastError());
TraceResult ("OneMsgDownloadPipeThread: Failed to write to the pipe", hResult);
}
}
} while (dwBytesRead && !hResult);

TraceResult ("OneMsgDownloadPipeThread", hResult);
CloseHandle (pInfo->hPipe);
CloseHandle (pInfo->hTmpFile);
HeapFree (ghHeap, 0, pInfo);
return hResult;
}

// End of file for RXPRPC.CPP