/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    clirpc.c

Abstract:

    This module contains the client side RPC
    functions.  These functions are used when the
    WINFAX client runs as an RPC server too.  These
    functions are the ones available for the RPC
    clients to call.  Currently the only client
    of these functions is the fax service.

Author:

    Wesley Witt (wesw) 29-Nov-1996


Revision History:

--*/

#include "faxapi.h"
#pragma hdrstop

BOOL RpcServerStarted;



VOID
WINAPI
FaxFreeBuffer(
    LPVOID Buffer
    )
{
    MemFree( Buffer );
}


void *
MIDL_user_allocate(
    IN size_t NumBytes
    )
{
    return MemAlloc( NumBytes );
}


void
MIDL_user_free(
    IN void *MemPointer
    )
{
    MemFree( MemPointer );
}


DWORD
FaxServerThread(
    LPVOID UnUsed
    )

/*++

Routine Description:

    Thread to process RPC messages from the various fax servers.

Arguments:

    AsyncInfo       - Packet of data necessary for processing this thread

Return Value:

    Always zero.

--*/

{
    error_status_t ec;

    ec = RpcMgmtWaitServerListen();
    if (ec != 0) {
        return ec;
    }

    return 0;
}


BOOL
WINAPI
FaxInitializeEventQueue(
    IN HANDLE FaxHandle,
    IN HANDLE CompletionPort,
    IN ULONG_PTR CompletionKey,
    IN HWND hWnd,
    IN UINT MessageStart
    )

/*++

Routine Description:

    Initializes the client side event queue.  There can be one event
    queue initialized for each fax server that the client app is
    connected to.

Arguments:

    FaxHandle       - FAX handle obtained from FaxConnectFaxServer.
    CompletionPort  - Handle of an existing completion port opened using CreateIoCompletionPort.
    CompletionKey   - A value that will be returned through the lpCompletionKey parameter of GetQueuedCompletionStatus.
    hWnd            - Window handle to post events to
    MessageStart    - Starting message number, message range used is MessageStart + FEI_NEVENTS

Return Value:

    TRUE    - Success
    FALSE   - Failure, call GetLastError() for more error information.

--*/

{
    DWORD ThreadId;
    HANDLE hThread;
    PASYNC_EVENT_INFO AsyncInfo;
    DWORD Size;
    error_status_t ec;
    TCHAR ComputerName[64];
    TCHAR ClientName[64];
    DWORD FaxSvcProcessId;

    if (CompletionPort && hWnd) {
        SetLastError( ERROR_INVALID_PARAMETER );
        return FALSE;
    }

    if (!ValidateFaxHandle(FaxHandle, FHT_SERVICE)) {
       SetLastError(ERROR_INVALID_HANDLE);
       return FALSE;
    }        

    if (hWnd && !IsLocalFaxConnection(FaxHandle)) {
        SetLastError( ERROR_INVALID_PARAMETER );
        return FALSE;
    }

    if (hWnd) {
        if (MessageStart < WM_USER) {
            SetLastError( ERROR_INVALID_PARAMETER );
            return FALSE;
        }
        if (CompletionKey == (ULONG_PTR)-1) {
            //
            // this means that we want the fax service to close it's token so we can logoff
            // 
            ec = FAX_RegisterEventWindow( FH_FAX_HANDLE(FaxHandle), (ULONG64)hWnd, 0, NULL , NULL , &FaxSvcProcessId );
        } else {
            //
            // normal registration of event window
            // 
            HWINSTA hWinStation;
            HDESK hDesktop;
            WCHAR StationName[100];
            WCHAR DesktopName[100];
            DWORD dwNeeded;

            if (!IsWindow(hWnd)) {
                SetLastError( ERROR_INVALID_PARAMETER );
                return FALSE;
            }
            
            hWinStation = GetProcessWindowStation();
            if (!GetUserObjectInformation(hWinStation,
                                          UOI_NAME,
                                          StationName,
                                          sizeof(StationName),
                                          &dwNeeded) ) {
               return FALSE;
            }
            hDesktop = GetThreadDesktop( GetCurrentThreadId() );
            if (! GetUserObjectInformation(hDesktop,
                                           UOI_NAME,
                                           DesktopName,
                                           sizeof(DesktopName),
                                           &dwNeeded) ) {
               return FALSE;
            }

            ec = FAX_RegisterEventWindow( FH_FAX_HANDLE(FaxHandle), 
                                          (ULONG64)hWnd, 
                                          MessageStart,
                                          StationName,
                                          DesktopName,
                                          &FaxSvcProcessId );
        }
        if (ec) {
            SetLastError( ec );
            return FALSE;
        }
        return FaxSvcProcessId;
    } else if (!CompletionPort) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

    AsyncInfo = (PASYNC_EVENT_INFO) MemAlloc( sizeof(ASYNC_EVENT_INFO) );
    if (!AsyncInfo) {
        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
        return FALSE;
    }

    AsyncInfo->FaxData        = FH_DATA(FaxHandle);
    AsyncInfo->CompletionPort = CompletionPort;
    AsyncInfo->CompletionKey  = CompletionKey;


    Size = sizeof(ComputerName) / sizeof(TCHAR);
    if (!GetComputerName( ComputerName, &Size )) {
        goto error_exit;
    }

    _stprintf( ClientName, TEXT("FaxClient$%d"), GetCurrentProcessId() );

    //
    // timing: get the server thread up and running before
    // registering with the fax service (our client)
    // 
    ec = RpcpStartRpcServer( ClientName, faxclient_ServerIfHandle );
    if (ec != 0) {
        SetLastError( ec );
        goto error_exit;        
    }

    FH_DATA(FaxHandle)->EventInit = TRUE;

    if (!RpcServerStarted) {
        hThread = CreateThread(
            NULL,
            1024*100,
            FaxServerThread,
            NULL,
            0,
            &ThreadId
            );

        if (!hThread) {
            goto error_exit;            
        } else {
            RpcServerStarted = TRUE;
            CloseHandle(hThread);            
        }
    }

    ec = FAX_StartClientServer( FH_FAX_HANDLE(FaxHandle), ComputerName, ClientName, (ULONG64) AsyncInfo );
    if (ec) {
        SetLastError( ec );
        goto error_exit;
    }

    

    return TRUE;

error_exit:
    if (AsyncInfo) {
        MemFree(AsyncInfo);
    }
   
    if (RpcServerStarted) {
        FH_DATA(FaxHandle)->EventInit = FALSE;
        // this should also terminate FaxServerThread
        RpcpStopRpcServer( faxclient_ServerIfHandle );
        RpcServerStarted = FALSE;
    }

    return FALSE;
}


error_status_t
FAX_OpenConnection(
   IN handle_t hBinding,
   IN ULONG64 Context,
   OUT LPHANDLE FaxHandle
   )
{
    *FaxHandle = (HANDLE) Context;
    return 0;
}


error_status_t
FAX_CloseConnection(
   OUT LPHANDLE FaxHandle
   )
{
    *FaxHandle = NULL;
    return 0;
}


error_status_t
FAX_ClientEventQueue(
    IN HANDLE FaxHandle,
    IN FAX_EVENT FaxEvent
    )

/*++

Routine Description:

    This function is called when the a fax server wants
    to deliver a fax event to this client.

Arguments:

    FaxHandle       - FAX handle obtained from FaxConnectFaxServer.
    FaxEvent        - FAX event structure.
    Context         - Context token, really a ASYNC_EVENT_INFO structure pointer

Return Value:

    TRUE    - Success
    FALSE   - Failure, call GetLastError() for more error information.

--*/

{
    PASYNC_EVENT_INFO AsyncInfo = (PASYNC_EVENT_INFO) FaxHandle;
    PFAX_EVENT FaxEventPost = NULL;


    FaxEventPost = (PFAX_EVENT) LocalAlloc( LMEM_FIXED, sizeof(FAX_EVENT) );
    if (!FaxEventPost) {
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    CopyMemory( FaxEventPost, &FaxEvent, sizeof(FAX_EVENT) );

    PostQueuedCompletionStatus(
        AsyncInfo->CompletionPort,
        sizeof(FAX_EVENT),
        AsyncInfo->CompletionKey,
        (LPOVERLAPPED) FaxEventPost
        );

    return ERROR_SUCCESS;
}


VOID
RPC_FAX_HANDLE_rundown(
    IN HANDLE FaxHandle
    )
{
    PASYNC_EVENT_INFO AsyncInfo = (PASYNC_EVENT_INFO) FaxHandle;
    PFAX_EVENT FaxEvent;


    FaxEvent = (PFAX_EVENT) LocalAlloc( LMEM_FIXED, sizeof(FAX_EVENT) );
    if (!FaxEvent) {
        goto exit;        
    }

    FaxEvent->SizeOfStruct      = sizeof(ASYNC_EVENT_INFO);
    GetSystemTimeAsFileTime( &FaxEvent->TimeStamp );
    FaxEvent->DeviceId = 0;
    FaxEvent->EventId  = FEI_FAXSVC_ENDED;
    FaxEvent->JobId    = 0;

    PostQueuedCompletionStatus(
        AsyncInfo->CompletionPort,
        sizeof(FAX_EVENT),
        AsyncInfo->CompletionKey,
        (LPOVERLAPPED) FaxEvent
        );

exit:
    RpcpStopRpcServer( faxclient_ServerIfHandle );

    return;
}