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