///////////////////////////////////////////////////////////////////////////////
//
//  File Name 
//      NOTIFY.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

typedef struct _WINDS_NOTIF_CLIENT
{
    struct _WINDS_NOTIF_CLIENT *    pNext;
    TCHAR                           szComputerName[MAX_COMPUTERNAME_LENGTH + 1];
    TCHAR                           szObjectName[MAX_ALIAS_SIZE+1];
    DWORD                           dwFlags;
    DWORD                           dwNotifMask;
} WINDS_NOTIF_CLIENT, *PWINDS_NOTIF_CLIENT;

PWINDS_NOTIF_CLIENT gpNotifLinks = NULL;

///////////////////////////////////////////////////////////////////////////////
//    RemoteLogonMailBoxAndSetNotifA()
//
//    Parameters
//
//    Purpose
//
//    Return Value
//
long RemoteLogonMailBoxAndSetNotifA (unsigned char *    szMailbox,
                                     unsigned char *    szPassword,
                                     unsigned char *    szFullName,
                                     unsigned long *    pdwMailboxID,
                                     unsigned char *    szComputerName,
                                     unsigned long      ulNotifMask,
                                     unsigned long *    pulConnectionID)
{
    long lResult = RemoteLogonMailBoxA (szMailbox, szPassword, szFullName, pdwMailboxID);
    if (lResult)
    {
        return lResult;
    }
    RemoteValidateNotifA (szComputerName, szMailbox, ulNotifMask, pulConnectionID);
    return S_OK;
}

///////////////////////////////////////////////////////////////////////////////
//    RemoteValidateNotifA()
//
//    Parameters
//
//    Purpose
//
//    Return Value
//
long RemoteValidateNotifA (unsigned char *  szComputerName,
                           unsigned char *  szMailbox,
                           unsigned long    ulNotifMask,
                           unsigned long *  pulConnectionID)
{
    long lResult = GetServiceState();
    if (lResult)
    {
        return lResult;
    }
    PWINDS_NOTIF_CLIENT pNode, pNewNode = (PWINDS_NOTIF_CLIENT)HeapAlloc (ghHeap,
                                                                          HEAP_ZERO_MEMORY,
                                                                          sizeof(WINDS_NOTIF_CLIENT));
    if (pNewNode)
    {
        pNewNode->dwNotifMask = ulNotifMask;
        lstrcpy (pNewNode->szComputerName, (LPSTR)szComputerName);
        lstrcpy (pNewNode->szObjectName, (LPSTR)szMailbox);
        
        EnterCriticalSection (&g_csNotifLinks);
        if (gpNotifLinks)
        {
            pNode = gpNotifLinks;
            while (pNode)
            {
                if (NULL == pNode->pNext)
                {
                    pNode->pNext = pNewNode;
                    break; // Out of the WHILE() loop
                }
                pNode = pNode->pNext;
            }
        }
        else
        {
            gpNotifLinks = pNewNode;
        }
        LeaveCriticalSection (&g_csNotifLinks);

        *pulConnectionID = (unsigned long)pNewNode;
    }
    TraceMessageIf ("RemoteValidateNotifA: Failed to allocated new node", 0 == *pulConnectionID);
    return S_OK;
}

///////////////////////////////////////////////////////////////////////////////
//    RemoteTerminateNotifA()
//
//    Parameters
//
//    Purpose
//
//    Return Value
//
long RemoteTerminateNotifA (unsigned char * szComputerName,
                            unsigned long ulConnectionID)
{
    long lResult = GetServiceState();
    if (lResult)
    {
        return lResult;
    }

    long cbSize = lstrlen ((LPSTR)szComputerName);
    EnterCriticalSection (&g_csNotifLinks);
    PWINDS_NOTIF_CLIENT pNext, pPrev = NULL, pNode = gpNotifLinks;
    while (pNode)
    {
        if (ulConnectionID == (unsigned long)pNode && (0 == memcmp (pNode->szComputerName, szComputerName, cbSize)))
        {
            // If the node is the head of the list, make the head point to the next node
            if (gpNotifLinks == pNode)
            {
                gpNotifLinks = gpNotifLinks->pNext;
            }
            // Save the next pointer so that we may delete the current node
            pNext = pNode->pNext;
            HeapFree (ghHeap, 0, pNode);
            // If we have a previous node, we are doing a delete in the middle of a single link list
            if (pPrev)
            {
                pPrev->pNext = pNext;
            }
            break; // Out of the WHILE() loop
        }
        else
        {
            // Don't delete this node, save is as the "previous" node in the linear list
            pPrev = pNode;
            pNode = pNode->pNext;
        }
    }
    LeaveCriticalSection (&g_csNotifLinks);
    return S_OK;
}

///////////////////////////////////////////////////////////////////////////////
//    RemoveAllNotifLinks()
//
//    Parameters
//
//    Purpose
//
//    Return Value
//      None.
//
void WINAPI RemoveAllNotifLinks()
{
    EnterCriticalSection (&g_csNotifLinks);
    PWINDS_NOTIF_CLIENT pNode = gpNotifLinks;
    while (gpNotifLinks)
    {
        pNode = gpNotifLinks->pNext;
        HeapFree (ghHeap, 0, gpNotifLinks);
        gpNotifLinks = pNode;
    }
    gpNotifLinks = NULL;
    LeaveCriticalSection (&g_csNotifLinks);
}

///////////////////////////////////////////////////////////////////////////////
//    NotifyClients()
//
//    Parameters
//
//    Purpose
//
//    Return Value
//      None.
//
void WINAPI NotifyClients (PWINDS_NOTIFICATION pNotif)
{
    DWORD dwThreadID;
    HANDLE hThread = CreateThread (NULL,
                                   0,
                                   (LPTHREAD_START_ROUTINE)ClientNotifThreadProc,
                                   (LPVOID)pNotif,
                                   CREATE_SUSPENDED,
                                   &dwThreadID);
    if (hThread)
    {
        TraceDebugger ("NotifyClients: Notif dispatcher thread spawned. ID: %X", dwThreadID);
        ResumeThread (hThread);
        CloseHandle (hThread);
    }
    else
    {
        TraceResult ("NotifyClients: Failed to create notification dispatching thread", HRESULT_FROM_WIN32(GetLastError()));
        HeapFree (ghHeap, 0, pNotif);
    }
}

///////////////////////////////////////////////////////////////////////////////
//    ClientNotifThreadProc()
//
//    Parameters
//
//    Purpose
//
//    Return Value
//      None.
//
DWORD WINAPI ClientNotifThreadProc (PWINDS_NOTIFICATION pNotif)
{
    TCHAR szTargetMailslot[MAX_PATH] = { 0 };
    DWORD dwWritten, dwNotifEvent = 0;
    HANDLE hClientMailSlot;
    LPTSTR szMailSlotSink = NULL;

    SYSTEMTIME st;
    GetSystemTime (&st);
    SystemTimeToFileTime (&st, &(pNotif->ftEventTime));

    if (pNotif->Event >= AB_NOTIF_MIN  && pNotif->Event <= AB_NOTIF_MAX)
    {
        dwNotifEvent = WINDS_NOTIF_ON_AB | WINDS_NOTIF_ON_USER;
        szMailSlotSink = AB_WINDS_NOTIFICATION_MAILSLOT;
    }
    else
    {
        if (pNotif->Event >= XP_NOTIF_MIN  && pNotif->Event <= XP_NOTIF_MAX)
        {
            dwNotifEvent = WINDS_NOTIF_ON_XP;
            szMailSlotSink = XP_WINDS_NOTIFICATION_MAILSLOT;
        }
        else
        {
            if (pNotif->Event >= MS_NOTIF_MIN  && pNotif->Event <= MS_NOTIF_MAX)
            {
                dwNotifEvent = WINDS_NOTIF_ON_MS;
                szMailSlotSink = MS_WINDS_NOTIFICATION_MAILSLOT;
            }
            else
            {
                if (pNotif->Event >= GENERAL_NOTIF_MIN  && pNotif->Event <= GENERAL_NOTIF_MAX)
                {
                    dwNotifEvent = WINDS_NOTIF_ON_USER;
                }
            }
        }
    }

    EnterCriticalSection (&g_csNotifLinks);
    PWINDS_NOTIF_CLIENT pNode = gpNotifLinks;
    while (pNode)
    {
        if (dwNotifEvent & pNode->dwNotifMask)
        {
            if (WINDS_NOTIF_ON_USER & dwNotifEvent)
            {
                if (WINDS_NOTIF_ON_AB & pNode->dwNotifMask)
                {
                    szMailSlotSink = AB_WINDS_NOTIFICATION_MAILSLOT;
                }
                else
                {
                    if (WINDS_NOTIF_ON_XP & pNode->dwNotifMask)
                    {
                        szMailSlotSink = XP_WINDS_NOTIFICATION_MAILSLOT;
                    }
                    else
                    {
                        szMailSlotSink = MS_WINDS_NOTIFICATION_MAILSLOT;
                    }
                }
            }
            if (WINDS_NOTIF_ON_XP == dwNotifEvent && XP_NEW_MAIL_ARRIVED == pNotif->Event)
            {
                if (lstrcmpi (pNode->szObjectName, pNotif->Info.MB.szMailboxName))
                {
                    goto NextNode;
                }
            }
            if (WINDS_ADMINISTRATOR & pNode->dwNotifMask)
            {
                szMailSlotSink = ADMIN_WINDS_NOTIFICATION_MAILSLOT;
            }
            wsprintf (szTargetMailslot,
                      SERVER_MAILSLOT_SINK_NAME_FORMAT,
                      pNode->szComputerName,
                      szMailSlotSink,
                      pNode); // the connection number is the pointer address in the server side (this is unique within a given process)

            hClientMailSlot = CreateFile (szTargetMailslot,
                                          GENERIC_WRITE,
                                          FILE_SHARE_READ,
                                          NULL,
                                          OPEN_EXISTING,
                                          FILE_ATTRIBUTE_NORMAL,
                                          NULL); 
            if (INVALID_HANDLE_VALUE == hClientMailSlot)
            {
                TraceResult ("ClientNotifThreadProc: Failed to open client mailslot", HRESULT_FROM_WIN32(GetLastError()));
            }
            else
            {
                if (!WriteFile (hClientMailSlot, pNotif, sizeof(WINDS_NOTIFICATION), &dwWritten, NULL))
                {
                    TraceResult ("ClientNotifThreadProc: Failed to write to the client mailslot", HRESULT_FROM_WIN32(GetLastError()));
                }
                CloseHandle (hClientMailSlot); 
            }
        }
NextNode:
        pNode = pNode->pNext;
    }

#define CLIENTS_NOTIF_CACHE_FILE    TEXT("WINDS Service - Last Session Clients.WINDS")
    if (SERVER_IS_SHUTTING_DOWN == pNotif->Event && gpNotifLinks)
    {
        DWORD dwBytesWritten;
        WINDS_NOTIF_CLIENT tmpNode;
        HANDLE hFile = CreateFile (CLIENTS_NOTIF_CACHE_FILE,
                                   GENERIC_WRITE,
                                   0,
                                   NULL,
                                   CREATE_ALWAYS,
                                   FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
                                   NULL);
        if (INVALID_HANDLE_VALUE == hFile)
        {
            TraceResult ("ClientNotifThreadProc: Failed to create clients cache file", HRESULT_FROM_WIN32(GetLastError()));
        }
        else
        {
            pNode = gpNotifLinks;
            while (pNode)
            {
                tmpNode = *pNode;
                tmpNode.pNext = pNode;
                // This is so that the node address gets written to the disk. This is the
                // connection number and the name of the mailslot in the client side.
                WriteFile (hFile, &tmpNode, sizeof(WINDS_NOTIF_CLIENT), &dwBytesWritten, NULL);
                pNode = pNode->pNext;
            }
        }
    }
    if (SERVER_HAS_RESTARTED == pNotif->Event)
    {
        DWORD dwBytesRead;
        WINDS_NOTIF_CLIENT tmpNode = { 0 };
        HANDLE hFile = CreateFile (CLIENTS_NOTIF_CACHE_FILE,
                                   GENERIC_READ,
                                   0,
                                   NULL,
                                   OPEN_EXISTING,
                                   FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_DELETE_ON_CLOSE,
                                   NULL);
        if (INVALID_HANDLE_VALUE != hFile)
        {
            do
            {
                if (TRUE == ReadFile (hFile, &tmpNode, sizeof(WINDS_NOTIF_CLIENT), &dwBytesRead, NULL))
                {
                    if (WINDS_NOTIF_ON_AB & tmpNode.dwNotifMask)
                    {
                        szMailSlotSink = AB_WINDS_NOTIFICATION_MAILSLOT;
                    }
                    else
                    {
                        if (WINDS_NOTIF_ON_XP & tmpNode.dwNotifMask)
                        {
                            szMailSlotSink = XP_WINDS_NOTIFICATION_MAILSLOT;
                        }
                        else
                        {
                            szMailSlotSink = MS_WINDS_NOTIFICATION_MAILSLOT;
                        }
                    }
                    if (WINDS_ADMINISTRATOR & tmpNode.dwNotifMask)
                    {
                        szMailSlotSink = ADMIN_WINDS_NOTIFICATION_MAILSLOT;
                    }
                    wsprintf (szTargetMailslot,
                              SERVER_MAILSLOT_SINK_NAME_FORMAT,
                              tmpNode.szComputerName,
                              szMailSlotSink,
                              tmpNode.pNext); // The connection number is the pointer address in the server side (this is unique within a process address space)
                    hClientMailSlot = CreateFile (szTargetMailslot,
                                                  GENERIC_WRITE,
                                                  FILE_SHARE_READ,
                                                  NULL,
                                                  OPEN_EXISTING,
                                                  FILE_ATTRIBUTE_NORMAL,
                                                  NULL); 
                    if (INVALID_HANDLE_VALUE != hClientMailSlot)
                    {
                        pNotif->Event = SERVER_HAS_RESTARTED;
                        pNotif->ftEventTime.dwLowDateTime++;
                        pNotif->ftEventTime.dwHighDateTime++;
                        WriteFile (hClientMailSlot, pNotif, sizeof(WINDS_NOTIFICATION), &dwWritten, NULL);
                        pNotif->Event = RESET_LINKS_WITH_SERVER;
                        pNotif->ftEventTime.dwLowDateTime++;
                        pNotif->ftEventTime.dwHighDateTime++;
                        WriteFile (hClientMailSlot, pNotif, sizeof(WINDS_NOTIFICATION), &dwWritten, NULL);
                        CloseHandle (hClientMailSlot);
                    }
                }
            } while (dwBytesRead);
            CloseHandle (hFile);
        }
    }

    LeaveCriticalSection (&g_csNotifLinks);
    HeapFree (ghHeap, 0, pNotif);
    return S_OK;
}

// End of file for NOTIFY.CPP
