///////////////////////////////////////////////////////////////////////////////
//
//  File Name 
//      RADMRPC.CPP
//
//  Description
//
//  Author
//      Irving De la Cruz
//
//  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
#include "COMMON.H"     // For the transmition of the DL members list

///////////////////////////////////////////////////////////////////////////////
//    RemoteAdmGetServerMailboxes()
//
//    Parameters
//
//    Purpose
//
//    Return Value
//
long RemoteAdmGetServerMailboxes (long * pPipeNumber)
{
    long lResult = GetServiceState();
    if (lResult)
    {
        return lResult;
    }

    *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(sa);
    sa.lpSecurityDescriptor = &sd;
    sa.bInheritHandle = TRUE;

    // Create a pipe where we will expect the transport to send the data
    TCHAR szPipeName[64];
    wsprintf (szPipeName, SERVER_PIPE_NAME_FORMAT, *pPipeNumber);
    HANDLE hPipe;
    hPipe = CreateNamedPipe (szPipeName,
                             PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED,
                             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 ("RemoteAdmGetServerMailboxes: Failed to create pipe", lResult);
        return lResult;
    }

    EnterCriticalSection (&g_csIOInfo);
    SetEvent (g_IOInfo.hResumeEvent);
    g_IOInfo.Action           = IO_ADMIN_GET_SERVER_MAILBOXES;
    g_IOInfo.hActionCompleted = NULL;
    g_IOInfo.phLastError      = NULL;
    g_IOInfo.hTmpFile         = hPipe;
    LeaveCriticalSection (&g_csIOInfo);

    return lResult;
}

///////////////////////////////////////////////////////////////////////////////
//    RemoteAdmGetServerDistLists()
//
//    Parameters
//
//    Purpose
//
//    Return Value
//
long RemoteAdmGetServerDistLists (long * pPipeNumber)
{
    long lResult = GetServiceState();
    if (lResult)
    {
        return lResult;
    }

    *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(sa);
    sa.lpSecurityDescriptor = &sd;
    sa.bInheritHandle = TRUE;

    TCHAR szPipeName[64];
    wsprintf (szPipeName, SERVER_PIPE_NAME_FORMAT, *pPipeNumber);
    HANDLE hPipe;
    hPipe = CreateNamedPipe (szPipeName,
                             PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED,
                             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 ("RemoteAdmGetServerDistLists: Failed to create pipe", lResult);
        return lResult;
    }

    EnterCriticalSection (&g_csIOInfo);
    SetEvent (g_IOInfo.hResumeEvent);
    g_IOInfo.Action           = IO_ADMIN_GET_SERVER_DISTLISTS;
    g_IOInfo.hActionCompleted = NULL;
    g_IOInfo.phLastError      = NULL;
    g_IOInfo.hTmpFile         = hPipe;
    LeaveCriticalSection (&g_csIOInfo);

    return lResult;
}

///////////////////////////////////////////////////////////////////////////////
//    RemoteAdmIsServerRunning()
//
//    Parameters
//
//    Purpose
//
//    Return Value
//
long RemoteAdmIsServerRunning()
{
    return GetServiceState();
}

///////////////////////////////////////////////////////////////////////////////
//    RemoteAdmCreateMailboxA()
//
//    Parameters
//
//    Purpose
//
//    Return Value
//
long RemoteAdmCreateMailboxA (ADM_MAILBOX_INFO_A * pMailboxInfo)
{
    long lResult = GetServiceState();
    if (lResult)
    {
        return lResult;
    }
    if (FALSE == GlobalObjectMap.IsAliasNameAvailable ((LPSTR)pMailboxInfo->szMailboxName))
    {
        return HRESULT_FROM_WIN32(ERROR_USER_EXISTS);
    }
    
    HANDLE hWaitEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
    if (!hWaitEvent)
    {
        lResult = HRESULT_FROM_WIN32(GetLastError());
        TraceResult ("RemoteAdmCreateMailboxA: Failed to create event for I/O thread", lResult);
        return lResult;
    }

    MAILBOX_INFO_A MBInfo = { 0 };
    CopyMemory (&MBInfo, pMailboxInfo, sizeof(MAILBOX_INFO_A));
    
    EnterCriticalSection (&g_csIOInfo);
    SetEvent (g_IOInfo.hResumeEvent);
    g_IOInfo.Action           = IO_CREATE_NEW_MAILBOX;
    g_IOInfo.hActionCompleted = hWaitEvent;
    g_IOInfo.phLastError      = &lResult;
    g_IOInfo.pMBInfo          = &MBInfo;
    LeaveCriticalSection (&g_csIOInfo);
    WaitForSingleObject (hWaitEvent, GENERAL_TIME_OUT);
    CloseHandle (hWaitEvent);
    if (lResult)
    {
        TraceResult ("RemoteAdmCreateMailboxA: Failed to create the mailbox", lResult);
        return lResult;
    }

    GlobalObjectMap.Insert (MBInfo.dwObjID, MBInfo.szMailboxName, SERVER_USER_MAILBOX);

    // Notify clients (i.e. address books) that a new mail user has been added
    PWINDS_NOTIFICATION pNotif = (PWINDS_NOTIFICATION)HeapAlloc (ghHeap,
                                                                 HEAP_ZERO_MEMORY,
                                                                 sizeof(WINDS_NOTIFICATION));
    if (pNotif)
    {
        pNotif->Event = AB_USER_ADDED;
        CopyMemory (&(pNotif->Info.MB), &MBInfo, sizeof(MAILBOX_INFO));
        NotifyClients (pNotif); // This will take care of freeing the notification structure
    }
    else
    {
        // This is non-fatal
        TraceResult ("RemoteAdmCreateMailboxA: Failed to allocate notification structure", E_OUTOFMEMORY);
    }
    return lResult;
}

///////////////////////////////////////////////////////////////////////////////
//    RemoteAdmDeleteObject()
//
//    Parameters
//
//    Purpose
//
//    Return Value
//
long RemoteAdmDeleteObject (unsigned long dwObjID)
{
    long lResult = GetServiceState();
    if (lResult)
    {
        return lResult;
    }

    TCHAR szObjAlias[MAX_ALIAS_SIZE+1];
    WINDS_AB_OBJTYPE Type = UNDEFINED_OBJECT_TYPE;
    if (S_OK != GlobalObjectMap.FindObjFromID (dwObjID, szObjAlias, &Type))
    {
        return HRESULT_FROM_WIN32 (ERROR_FILE_NOT_FOUND);
    }

    HANDLE hWaitEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
    if (!hWaitEvent)
    {
        lResult = HRESULT_FROM_WIN32(GetLastError());
        TraceResult ("RemoteAdmDeleteObject: Failed to create event for I/O thread", lResult);
        return lResult;
    }
    MAILBOX_INFO MBInfo = { 0 };
    DIST_LIST_INFO DLInfo = { 0 };

    EnterCriticalSection (&g_csIOInfo);
    SetEvent (g_IOInfo.hResumeEvent);
    g_IOInfo.hActionCompleted = hWaitEvent;
    g_IOInfo.phLastError = &lResult;

    if (SERVER_USER_MAILBOX == Type)
    {
        lstrcpy (MBInfo.szMailboxName, szObjAlias);
        g_IOInfo.Action = IO_REMOVE_MAILBOX;
        g_IOInfo.pMBInfo = &MBInfo; // On return this will have all the mailbox properties
    }
    else
    {
        if (SERVER_DISTRIBUTION_LIST == Type)
        {
            lstrcpy (DLInfo.szDLAlias, szObjAlias);
            g_IOInfo.Action = IO_DELETE_DISTRIBUTION_LIST;
            g_IOInfo.pDLInfo = &DLInfo;
        }
        else
        {
            TraceMessage ("RemoteAdmDeleteObject: Remove GATEWAY recipient NYI");
        }
    }

    LeaveCriticalSection (&g_csIOInfo);

    WaitForSingleObject (hWaitEvent, GENERAL_TIME_OUT);
    CloseHandle (hWaitEvent);

    if (lResult)
    {
        TraceResult ("RemoteAdmDeleteObject: Failed to delete the mailbox", lResult);
        return lResult;
    }

    GlobalObjectMap.Delete (dwObjID);
    // Notify client address books that a mail user has been deleted
    PWINDS_NOTIFICATION pNotif = (PWINDS_NOTIFICATION)HeapAlloc (ghHeap,
                                                                 HEAP_ZERO_MEMORY,
                                                                 sizeof(WINDS_NOTIFICATION));
    if (pNotif)
    {
        if (SERVER_USER_MAILBOX == Type)
        {
            pNotif->Event = AB_USER_DELETED;
            CopyMemory (&(pNotif->Info.MB), &MBInfo, sizeof(MAILBOX_INFO));
        }
        else
        {
            if (SERVER_DISTRIBUTION_LIST == Type)
            {
                pNotif->Event = AB_DL_DELETED;
                CopyMemory (&(pNotif->Info.DL), &DLInfo, sizeof(DIST_LIST_INFO));
            }
            else
            {
                // TODO: Implement this for gateways
            }
        }
        NotifyClients (pNotif); // This will take care of freeing the notification structure
    }
    else
    {
        // Non-fatal error
        TraceResult ("RemoteAdmDeleteObject: Failed to allocate notification structure", E_OUTOFMEMORY);
    }
    return lResult;
}

///////////////////////////////////////////////////////////////////////////////
//    RemoteAdmGetMailboxPropsA()
//
//    Parameters
//
//    Purpose
//
//    Return Value
//
long RemoteAdmGetMailboxPropsA (unsigned long           dwObjID,
                                unsigned long *         pdwObjType,
                                ADM_MAILBOX_INFO_A *    pMailboxInfo)
{
    long lResult = GetServiceState();
    if (lResult)
    {
        return lResult;
    }
    return GetObjectProp (dwObjID, pdwObjType, (MAILBOX_INFO*)pMailboxInfo);
}

///////////////////////////////////////////////////////////////////////////////
//    RemoteAdmSetMailboxPropsA()
//
//    Parameters
//
//    Purpose
//
//    Return Value
//
long RemoteAdmSetMailboxPropsA (ADM_MAILBOX_INFO_A * pMailboxInfo)
{
    long lResult = GetServiceState();
    if (lResult)
    {
        return lResult;
    }

    if (pMailboxInfo->dwObjID == 0)
    {
        TraceMessage ("RemoteAdmSetMailboxPropsA: Invoked with Object ID = 0, cannot accept");
        return HRESULT_FROM_WIN32 (ERROR_NO_SUCH_USER);
    }

    HANDLE hWaitEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
    if (!hWaitEvent)
    {
        lResult = HRESULT_FROM_WIN32(GetLastError());
        TraceResult ("RemoteAdmSetMailboxPropsA: Failed to create event for I/O thread", lResult);
        return lResult;
    }

    MAILBOX_INFO_A MBInfo = { 0 };
    CopyMemory (&MBInfo, pMailboxInfo, sizeof(MAILBOX_INFO_A));

    EnterCriticalSection (&g_csIOInfo);
    SetEvent (g_IOInfo.hResumeEvent);
    g_IOInfo.Action           = IO_SET_MAILBOX_PROPERTIES;
    g_IOInfo.hActionCompleted = hWaitEvent;
    g_IOInfo.phLastError      = &lResult;
    g_IOInfo.pMBInfo          = &MBInfo;
    LeaveCriticalSection (&g_csIOInfo);

    WaitForSingleObject (hWaitEvent, GENERAL_TIME_OUT);
    CloseHandle (hWaitEvent);
    if (!lResult)
    {
        // Notify client address books that a mail user has been modified
        PWINDS_NOTIFICATION pNotif = (PWINDS_NOTIFICATION)HeapAlloc (ghHeap,
                                                                     HEAP_ZERO_MEMORY,
                                                                     sizeof(WINDS_NOTIFICATION));
        if (pNotif)
        {
            pNotif->Event = AB_USER_MODIFIED;
            CopyMemory (&(pNotif->Info.MB), &MBInfo, sizeof(MAILBOX_INFO));
            NotifyClients (pNotif); // This will take care of freeing the notification structure
        }
        else
        {
            // This is non-fatal
            TraceResult ("RemoteAdmSetMailboxPropsA: Failed to allocate notification structure", E_OUTOFMEMORY);
        }
    }
    return lResult;
}

///////////////////////////////////////////////////////////////////////////////
//    RemoteAdmTerminateNotifA()
//
//    Parameters
//
//    Purpose
//
//    Return Value
//
long RemoteAdmTerminateNotifA (unsigned char *   szComputerName,
                              unsigned long     ulConnectionID)
{
    return RemoteTerminateNotifA (szComputerName, ulConnectionID);
}

///////////////////////////////////////////////////////////////////////////////
//    RemoteAdmValidateNotifA()
//
//    Parameters
//
//    Purpose
//
//    Return Value
//
long RemoteAdmValidateNotifA (unsigned char *    szComputerName,
                              unsigned long      ulNotifMask,
                              unsigned long *    pulConnectionID)
{
    return RemoteValidateNotifA (szComputerName,
                                 (unsigned char *)"WINDS ADMIN",
                                 ulNotifMask,
                                 pulConnectionID);
}

///////////////////////////////////////////////////////////////////////////////
//    RemoteAdmGetGALDirectory()
//
//    Parameters
//
//    Purpose
//
//    Return Value
//
long RemoteAdmGetGALDirectory (unsigned long dwFlags, long * pPipeNumber)
{
    return RemoteGetContainerRecipients (dwFlags, GAL_CONTAINER_ID, pPipeNumber);
}

///////////////////////////////////////////////////////////////////////////////
//    RemoteAdmEmptyMailbox()
//
//    Parameters
//
//    Purpose
//
//    Return Value
//
long RemoteAdmEmptyMailbox (unsigned long dwMailboxID)
{
    long lResult = GetServiceState();
    if (lResult)
    {
        return lResult;
    }

    TCHAR szObjAlias[MAX_ALIAS_SIZE+1];
    WINDS_AB_OBJTYPE Type = UNDEFINED_OBJECT_TYPE;
    if (S_OK != GlobalObjectMap.FindObjFromID (dwMailboxID, szObjAlias, &Type))
    {
        return HRESULT_FROM_WIN32 (ERROR_FILE_NOT_FOUND);
    }
    if (SERVER_USER_MAILBOX != Type)
    {
        return HRESULT_FROM_WIN32 (ERROR_NO_SUCH_USER);
    }

    HANDLE hWaitEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
    if (!hWaitEvent)
    {
        lResult = HRESULT_FROM_WIN32(GetLastError());
        TraceResult ("RemoteAdmEmptyMailbox: Failed to create event for I/O thread", lResult);
        return lResult;
    }
    
    EnterCriticalSection (&g_csIOInfo);
    SetEvent (g_IOInfo.hResumeEvent);
    lstrcpy (g_IOInfo.szObject, szObjAlias);
    g_IOInfo.Action           = IO_EMPTY_MAILBOX;
    g_IOInfo.hActionCompleted = hWaitEvent;
    g_IOInfo.phLastError      = &lResult;
    LeaveCriticalSection (&g_csIOInfo);

    WaitForSingleObject (hWaitEvent, GENERAL_TIME_OUT);
    CloseHandle (hWaitEvent);

    TraceResult ("RemoteAdmEmptyMailbox", lResult);
    return lResult;
}

///////////////////////////////////////////////////////////////////////////////
//    RemoteAdmGetDLPropsA()
//
//    Parameters
//
//    Purpose
//
//    Return Value
//
long RemoteAdmGetDLPropsA (unsigned long        dwObjID,
                           unsigned char *      szDLAlias,
                           unsigned char *      szDLFullName,
                           unsigned long *      pdwFlags,
                           unsigned char *      szOwnerAlias,
                           unsigned char *      szOwnerName,
                           unsigned long *      pdwOwnerID,
                           unsigned char *      szComments,
                           DLM_XMIT_LIST_A *    pMembers)
{
    long lResult = GetServiceState();
    if (lResult)
    {
        return lResult;
    }

    if (0 == dwObjID)
    {
        TraceMessage ("RemoteAdmGetDLPropsA: Invoked with Object ID = 0, cannot accept");
        return HRESULT_FROM_WIN32 (ERROR_NO_SUCH_GROUP);
    }

    DIST_LIST_INFO DLInfo = { 0 };
    WINDS_AB_OBJTYPE Type = UNDEFINED_OBJECT_TYPE;
    if ((S_OK != GlobalObjectMap.FindObjFromID (dwObjID, DLInfo.szDLAlias, &Type)) ||
        (SERVER_DISTRIBUTION_LIST != Type))
    {
        return HRESULT_FROM_WIN32 (ERROR_NO_SUCH_GROUP);
    }

    HANDLE hWaitEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
    if (!hWaitEvent)
    {
        lResult = HRESULT_FROM_WIN32(GetLastError());
        TraceResult ("RemoteAdmGetDLPropsA: Failed to create event for I/O thread", lResult);
        return lResult;
    }
    DLInfo.pMembers = (LPVOID)pMembers;

    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          = &DLInfo;
    LeaveCriticalSection (&g_csIOInfo);

    WaitForSingleObject (hWaitEvent, GENERAL_TIME_OUT);
    CloseHandle (hWaitEvent);
    if (!lResult)
    {
        lstrcpy ((LPSTR)szDLAlias, DLInfo.szDLAlias);
        lstrcpy ((LPSTR)szDLFullName, DLInfo.szDLFullName);
        *pdwFlags = DLInfo.dwFlags;
        lstrcpy ((LPSTR)szOwnerAlias, DLInfo.szOwnerAlias);
        lstrcpy ((LPSTR)szOwnerName, DLInfo.szOwnerName);
        *pdwOwnerID = DLInfo.dwOwnerID;
    }

    TraceResult ("RemoteAdmGetDLPropsA", lResult);
    return lResult;
}

///////////////////////////////////////////////////////////////////////////////
//    RemoteAdmSetDLPropsA()
//
//    Parameters
//
//    Purpose
//
//    Return Value
//
long RemoteAdmSetDLPropsA (unsigned long        dwObjID,
                           unsigned char *      szDLAlias,
                           unsigned char *      szDLFullName,
                           unsigned long        dwFlags,
                           unsigned char *      szOwnerAlias,
                           unsigned char *      szOwnerName,
                           unsigned long        dwOwnerID,
                           unsigned char *      szComments,
                           DLM_XMIT_LIST_A *    pMembers)
{
    long lResult = GetServiceState();
    if (lResult)
    {
        return lResult;
    }
    if (dwObjID == 0)
    {
        TraceMessage ("RemoteAdmSetDLPropsA: Invoked with Object ID = 0, cannot accept");
        return HRESULT_FROM_WIN32 (ERROR_NO_SUCH_GROUP);
    }

    DIST_LIST_INFO DLInfo = { 0 };
    lstrcpy (DLInfo.szDLAlias, (LPSTR)szDLAlias);
    lstrcpy (DLInfo.szDLFullName, (LPSTR)szDLFullName);
    DLInfo.dwObjID= dwObjID;
    DLInfo.dwFlags = dwFlags;
    lstrcpy (DLInfo.szOwnerAlias, (LPSTR)szOwnerAlias);
    lstrcpy (DLInfo.szOwnerName, (LPSTR)szOwnerName);
    DLInfo.dwOwnerID = dwOwnerID;
    DLInfo.pMembers = (LPVOID)pMembers;

    HANDLE hWaitEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
    if (!hWaitEvent)
    {
        lResult = HRESULT_FROM_WIN32(GetLastError());
        TraceResult ("RemoteAdmSetDLPropsA: Failed to create event for I/O thread", lResult);
        return lResult;
    }

    EnterCriticalSection (&g_csIOInfo);
    SetEvent (g_IOInfo.hResumeEvent);
    g_IOInfo.Action           = IO_SET_DL_PROPERTIES;
    g_IOInfo.hActionCompleted = hWaitEvent;
    g_IOInfo.phLastError      = &lResult;
    g_IOInfo.pDLInfo          = &DLInfo;
    LeaveCriticalSection (&g_csIOInfo);
    WaitForSingleObject (hWaitEvent, GENERAL_TIME_OUT);
    CloseHandle (hWaitEvent);

    if (!lResult)
    {
        PWINDS_NOTIFICATION pNotif = (PWINDS_NOTIFICATION)HeapAlloc (ghHeap,
                                                                     HEAP_ZERO_MEMORY,
                                                                     sizeof(WINDS_NOTIFICATION));
        if (pNotif)
        {
            pNotif->Event = AB_DL_MODIFIED;
            CopyMemory (&(pNotif->Info.DL), &DLInfo, sizeof(DIST_LIST_INFO));
            NotifyClients (pNotif); // This will take care of freeing the notification structure
        }
        else
        {
            // This is non-fatal
            TraceResult ("RemoteAdmSetDLPropsA: Failed to allocate the notification structure", E_OUTOFMEMORY);
        }
    }
    TraceResult ("RemoteAdmSetDLPropsA", lResult);
    return lResult;
}

///////////////////////////////////////////////////////////////////////////////
//    RemoteAdmCreateDistListA()
//
//    Parameters
//
//    Purpose
//
//    Return Value
//
long RemoteAdmCreateDistListA (unsigned char *   szDLAlias,
                               unsigned char *   szDLFullName,
                               unsigned long     dwFlags,
                               unsigned char *   szOwnerAlias,
                               unsigned char *   szOwnerName,
                               unsigned long     dwOwnerID,
                               DLM_XMIT_LIST_A * pMembers)
{
    long lResult = GetServiceState();
    if (lResult)
    {
        return lResult;
    }
    if (FALSE == GlobalObjectMap.IsAliasNameAvailable ((LPSTR)szDLAlias))
    {
        return HRESULT_FROM_WIN32(ERROR_GROUP_EXISTS);
    }
    
    HANDLE hWaitEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
    if (!hWaitEvent)
    {
        lResult = HRESULT_FROM_WIN32(GetLastError());
        TraceResult ("RemoteAdmCreateDistListA: Failed to create event for I/O thread", lResult);
        return lResult;
    }

    DIST_LIST_INFO DLInfo = { 0 };
    lstrcpy (DLInfo.szDLAlias, (LPSTR)szDLAlias);
    lstrcpy (DLInfo.szDLFullName, (LPSTR)szDLFullName);
    DLInfo.dwFlags = dwFlags;

    lstrcpy (DLInfo.szOwnerAlias, (LPSTR)szOwnerAlias);
    lstrcpy (DLInfo.szOwnerName, (LPSTR)szOwnerName);
    DLInfo.dwOwnerID = dwOwnerID;
    if (pMembers->Info.szMemberName[0])
    {
        DLInfo.pMembers = (LPVOID)pMembers;
    }
    else
    {
        DLInfo.pMembers = NULL;
    }

    EnterCriticalSection (&g_csIOInfo);
    SetEvent (g_IOInfo.hResumeEvent);
    g_IOInfo.Action           = IO_CREATE_DISTRIBUTION_LIST;
    g_IOInfo.hActionCompleted = hWaitEvent;
    g_IOInfo.phLastError      = &lResult;
    g_IOInfo.pDLInfo          = &DLInfo;
    LeaveCriticalSection (&g_csIOInfo);
    WaitForSingleObject (hWaitEvent, GENERAL_TIME_OUT);
    CloseHandle (hWaitEvent);
    
    if (lResult)
    {
        TraceResult ("RemoteAdmCreateDistListA", lResult);
        return lResult;
    }

    GlobalObjectMap.Insert (DLInfo.dwObjID, DLInfo.szDLAlias, SERVER_DISTRIBUTION_LIST);

    // Notify client address books that a distribution list has been added
    PWINDS_NOTIFICATION pNotif = (PWINDS_NOTIFICATION)HeapAlloc (ghHeap,
                                                                 HEAP_ZERO_MEMORY,
                                                                 sizeof(WINDS_NOTIFICATION));
    if (pNotif)
    {
        pNotif->Event = AB_DL_ADDED;
        CopyMemory (&(pNotif->Info.DL), &DLInfo, sizeof(DIST_LIST_INFO));
        NotifyClients (pNotif); // This will take care of freeing the notification structure
    }
    else
    {
        // This is non-fatal
        TraceResult ("RemoteAdmCreateDistListA: Failed to allocate the notification structure", E_OUTOFMEMORY);
    }
    return lResult;
}

// The UNICODE version of these function, has not been implemented yet.
long RemoteAdmTerminateNotifW (wchar_t *        szComputerName,
                               unsigned long    ulConnectionID)
{ return HRESULT_FROM_WIN32(E_NOTIMPL); }
long RemoteAdmValidateNotifW (wchar_t *         szComputerName,
                              unsigned long     ulNotifMask,
                              unsigned long *   pulConnectionID)
{ return HRESULT_FROM_WIN32(E_NOTIMPL); }
long RemoteAdmCreateMailboxW (ADM_MAILBOX_INFO_W * pMailboxInfo)
{ return HRESULT_FROM_WIN32(E_NOTIMPL); }
long RemoteAdmGetMailboxPropsW (unsigned long        dwObjID,
                                unsigned long *      pdwUserType,
                                ADM_MAILBOX_INFO_W * pMailboxInfo)
{ return HRESULT_FROM_WIN32(E_NOTIMPL); }
long RemoteAdmSetMailboxPropsW (ADM_MAILBOX_INFO_W * pMailboxInfo)
{ return HRESULT_FROM_WIN32(E_NOTIMPL); }
long RemoteAdmCreateDistListW (wchar_t *         szDLAlias,
                               wchar_t *         szDLFullName,
                               unsigned long     dwFlags,
                               wchar_t *         szOwnerAlias,
                               wchar_t *         szOwnerName,
                               unsigned long     dwOwnerID,
                               DLM_XMIT_LIST_W * pMembers)
{ return HRESULT_FROM_WIN32(E_NOTIMPL); }
long RemoteAdmGetDLPropsW (unsigned long        dwObjID,
                           wchar_t *            szDLAlias,
                           wchar_t *            szDLFullName,
                           unsigned long *      pdwFlags,
                           wchar_t *            szOwnerAlias,
                           wchar_t *            szOwnerName,
                           unsigned long *      pdwOwnerID,
                           wchar_t *            szComments,
                           DLM_XMIT_LIST_W *    pMembers)
{ return HRESULT_FROM_WIN32(E_NOTIMPL); }
long RemoteAdmSetDLPropsW (unsigned long        dwObjID,
                           wchar_t *            szDLAlias,
                           wchar_t *            szDLFullName,
                           unsigned long        dwFlags,
                           wchar_t *            szOwnerAlias,
                           wchar_t *            szOwnerName,
                           unsigned long        dwOwnerID,
                           wchar_t *            szComments,
                           DLM_XMIT_LIST_W *    pMembers)
{ return HRESULT_FROM_WIN32(E_NOTIMPL); }
// End of file for RADMRPC.CPP
