/*++

   Copyright    (c)    1996    Microsoft Corporation

   Module  Name :

       atqxmit.cxx

   Abstract:

        Contains internal support routines for transmit file

   Author:
        Johnson Apacible     (johnsona)      26-Mar-1996

--*/

#include "isatq.hxx"


//
// Size of buffers for fake xmits
//

DWORD g_cbXmitBufferSize = ATQ_REG_DEF_NONTF_BUFFER_SIZE;

VOID
I_CleanupFakeTransmitFile(
        IN PATQ_CONT pContext
        )
{
    //
    // Put the old completion routine back and free allocated buffers
    //

    pContext->pfnCompletion = pContext->arInfo.uop.opFakeXmit.pfnCompletion;
    pContext->ClientContext = pContext->arInfo.uop.opFakeXmit.ClientContext;
    if ( pContext->arInfo.uop.opFakeXmit.pBuffer != NULL ) {
        LocalFree(pContext->arInfo.uop.opFakeXmit.pBuffer);
        pContext->arInfo.uop.opFakeXmit.pBuffer = NULL;
    }

    //
    // Clean up the event
    //

    if ( pContext->arInfo.uop.opFakeXmit.hOvEvent != NULL ) {
        CloseHandle( pContext->arInfo.uop.opFakeXmit.hOvEvent );
        pContext->arInfo.uop.opFakeXmit.hOvEvent = NULL;
    }
    return;

} // I_CleanupFakeTransmitFile

VOID
I_FakeTransmitFileCompletion(
            IN PVOID ClientContext,
            IN DWORD BytesWritten,
            IN DWORD CompletionStatus,
            IN OVERLAPPED * lpo
            )
{
    PATQ_CONT pContext;
    DWORD nWrite = 0;
    DWORD nRead;
    PCHAR buffer;
    INT err;
    PVOID tail;
    OVERLAPPED ov;
    OVERLAPPED *pov = &ov;

    pContext = (PATQ_CONT)ClientContext;
    pContext->arInfo.uop.opFakeXmit.BytesWritten += BytesWritten;

    if ( CompletionStatus != NO_ERROR ) {

        //
        // An error occured, call the completion routine
        //

        goto call_completion;
    }

    //
    // We already have a buffer of size g_cbXmitBufferSize
    //

    nRead = pContext->arInfo.uop.opFakeXmit.BytesLeft;
    buffer = pContext->arInfo.uop.opFakeXmit.pBuffer;
    ATQ_ASSERT(buffer != NULL);

    if ( nRead > 0 ) {

        //
        // Do the read at the specified offset
        //

        pov->OffsetHigh = 0;
        pov->Offset = pContext->arInfo.uop.opFakeXmit.FileOffset;
        pov->hEvent = pContext->arInfo.uop.opFakeXmit.hOvEvent;
        ATQ_ASSERT(pov->hEvent != NULL);
        ResetEvent(pov->hEvent);

        if (!ReadFile(
                    pContext->arInfo.uop.opFakeXmit.hFile,
                    buffer,
                    g_cbXmitBufferSize,
                    &nRead,
                    pov
                    ) ) {

            err = GetLastError();
            if ( (err != ERROR_IO_PENDING) ||
                 !GetOverlappedResult(
                        pContext->arInfo.uop.opFakeXmit.hFile,
                        pov,
                        &nRead,
                        TRUE )) {

                CompletionStatus = GetLastError();
                ATQ_PRINTF(( DBG_CONTEXT,"ReadFile error %d\n",CompletionStatus));
                goto call_completion;
            }
        }

        //
        // if nRead is zero, we reached the EOF.
        //

        if ( nRead > 0 ) {

            //
            // Update for next read
            //

            pContext->arInfo.uop.opFakeXmit.BytesLeft -= nRead;
            pContext->arInfo.uop.opFakeXmit.FileOffset += nRead;

            //
            // Do the write
            //

            I_SetNextTimeout(pContext);
            pContext->BytesSent = nRead;

            //
            // Write to the socket
            //

            if ( !WriteFile(
                    pContext->hAsyncIO,
                    buffer,
                    nRead,
                    &nWrite,
                    &pContext->Overlapped
                    ) &&
                (GetLastError() != ERROR_IO_PENDING) ) {

                CompletionStatus = GetLastError();
                ATQ_PRINTF(( DBG_CONTEXT,
                             "WriteFile error %d\n",CompletionStatus));
                goto call_completion;
            }

            return;
        }
    }

    //
    // Time for the tail.  If one exist, send it synchronously and then
    // call the completion routine
    //

    tail = pContext->arInfo.uop.opFakeXmit.Tail;
    if ( tail != NULL ) {

        DWORD tailLength = pContext->arInfo.uop.opFakeXmit.TailLength;

        ATQ_ASSERT(tailLength > 0);

        //
        // Send it synchronously
        //

        err = send(
                HANDLE_TO_SOCKET(pContext->hAsyncIO),
                (PCHAR)tail,
                tailLength,
                0
                );

        if ( err == SOCKET_ERROR ) {
            CompletionStatus = GetLastError();
        } else {
            pContext->arInfo.uop.opFakeXmit.BytesWritten += err;
        }
    }

call_completion:

    //
    // cleanup and call real completion routine
    //

    I_CleanupFakeTransmitFile( pContext );
    if ( pContext->pfnCompletion != NULL ) {

        pContext->pfnCompletion(
                    pContext->ClientContext,
                    pContext->arInfo.uop.opFakeXmit.BytesWritten,
                    CompletionStatus,
                    lpo
                    );
    }

    return;

} // I_FakeTransmitFileCompletion

BOOL
I_DoFakeTransmitFile(
    IN PATQ_CONT                pContext,
    IN HANDLE                   hFile,
    IN DWORD                    dwBytesInFile,
    IN LPTRANSMIT_FILE_BUFFERS  lpTransmitBuffers
    )
/*++

Routine Description:

    Posts a completion status on the completion port queue

    An IO pending error code is treated as a success error code

Arguments:

    patqContext - pointer to ATQ context
    hFile - Handle to the file to be read.
    dwBytesInFile - Number of bytes to read in the file
    lpTransmitBuffers - the transmitfile structure

Return Value:

    TRUE if successful, FALSE on error (call GetLastError)

--*/
{

    PCHAR buffer = NULL;
    DWORD nWrite;
    DWORD nRead = 0;
    OVERLAPPED ov;
    INT err;
    DWORD cBuffer = 0;
    OVERLAPPED *pov = &ov;
    HANDLE hOvEvent = NULL;

    INC_ATQ_COUNTER( g_cTotalAllowedRequests);

    //
    // See if we need to send a header
    //

    pContext->arInfo.uop.opFakeXmit.BytesWritten = 0;
    if ( lpTransmitBuffers != NULL ) {

        //
        // Store the tail
        //

        pContext->arInfo.uop.opFakeXmit.Tail = lpTransmitBuffers->Tail;
        pContext->arInfo.uop.opFakeXmit.TailLength = lpTransmitBuffers->TailLength;

        if (lpTransmitBuffers->HeadLength > 0) {
            ATQ_ASSERT(lpTransmitBuffers->Head != NULL);

            //
            // Send it synchronously
            //

            err = send(
                    HANDLE_TO_SOCKET(pContext->hAsyncIO),
                    (PCHAR)lpTransmitBuffers->Head,
                    lpTransmitBuffers->HeadLength,
                    0
                    );

            if ( err == SOCKET_ERROR ) {
                ATQ_PRINTF(( DBG_CONTEXT, "Error %d in send.\n",err));
                return(FALSE);
            }
            pContext->arInfo.uop.opFakeXmit.BytesWritten += err;
        }
    }

    //
    // Check the number of bytes to send
    //

    if ( dwBytesInFile == 0 ) {

        //
        // Send the whole file.
        //

        dwBytesInFile = GetFileSize( hFile, NULL );
        ATQ_ASSERT(dwBytesInFile >= pContext->Overlapped.Offset);
        dwBytesInFile -= pContext->Overlapped.Offset;
    }

    //
    // Allocate the io buffer
    //

    cBuffer = min( dwBytesInFile, g_cbXmitBufferSize );
    if ( cBuffer > 0 ) {

        //
        // Read the first chunk of the body
        //

        buffer = (PCHAR)LocalAlloc( 0, cBuffer );
        if ( buffer == NULL ) {
            ATQ_PRINTF(( DBG_CONTEXT,
                         "Cannot allocate %d bytes for xmitfile\n",cBuffer));
            return(FALSE);
        }

        //
        // Do the read at the specified offset
        //

        hOvEvent = pov->hEvent = IIS_CREATE_EVENT(
                                     "OVERLAPPED::hEvent",
                                     pov,
                                     TRUE,
                                     FALSE
                                     );

        if ( hOvEvent == NULL ) {
            ATQ_PRINTF(( DBG_CONTEXT,
                         "Create event failed with %d\n",GetLastError()));
            LocalFree( buffer );
            return(FALSE);
        }

        pov->OffsetHigh = 0;
        pov->Offset = pContext->Overlapped.Offset;

        if (!ReadFile(
                    hFile,
                    buffer,
                    cBuffer,
                    &nRead,
                    pov
                    ) ) {

            err = GetLastError();
            if ( (err != ERROR_IO_PENDING) ||
                 !GetOverlappedResult( hFile, pov, &nRead, TRUE )) {

                err = GetLastError();
                CloseHandle( hOvEvent );
                LocalFree( buffer );
                SetLastError(err);
                ATQ_PRINTF(( DBG_CONTEXT,
                             "Error %d in readfile\n",err));
                return(FALSE);
            }
        }
    }

    //
    // are we done reading the body?
    //

    if ( nRead < g_cbXmitBufferSize ) {

        //
        // Done.
        //

        pContext->arInfo.uop.opFakeXmit.BytesLeft = 0;
    } else {

        pContext->arInfo.uop.opFakeXmit.BytesLeft = dwBytesInFile - nRead;
        pContext->arInfo.uop.opFakeXmit.FileOffset =
                                    pContext->Overlapped.Offset + nRead;
    }

    //
    // store data for restarting the operation.
    //

    pContext->arInfo.uop.opFakeXmit.pBuffer = buffer;
    pContext->arInfo.uop.opFakeXmit.hOvEvent = hOvEvent;
    pContext->arInfo.uop.opFakeXmit.hFile = hFile;

    //
    // replace the completion function with our own
    //

    pContext->arInfo.uop.opFakeXmit.pfnCompletion = pContext->pfnCompletion;
    pContext->arInfo.uop.opFakeXmit.ClientContext = pContext->ClientContext;
    pContext->pfnCompletion = I_FakeTransmitFileCompletion;
    pContext->ClientContext = pContext;

    //
    // Set the timeout
    //

    I_SetNextTimeout(pContext);
    pContext->BytesSent = nRead;

    //
    // Write to the socket
    //

    if ( !WriteFile(
            pContext->hAsyncIO,
            buffer,
            nRead,
            &nWrite,
            &pContext->Overlapped
            ) &&
        (GetLastError() != ERROR_IO_PENDING)) {

        err = GetLastError();
        I_CleanupFakeTransmitFile( pContext );
        SetLastError(err);
        return(FALSE);
    }

    SetLastError(NO_ERROR);
    return(TRUE);

} // I_DoFakeTransmitFile