//
// Microsoft Corporation - Copyright 1997
//

//
// RESPONSE.CPP - Contains possible responses
//

#include "pch.h"


// Globals
const char g_cszTableHeader[] = "%s \
<TABLE ID=%s BORDER=1> \
<TR> \
  <TH WIDTH=40%%>Variable Name</TH> \
  <TH WIDTH=60%%>Value</TH> \
</TR>";

const char g_cszTableEnd[] = "</TABLE>";

// List of known server variables
LPSTR g_lpServerVars[] = {
        "AUTH_TYPE",
        "CONTENT_LENGTH",
        "CONTENT_TYPE",
        "GATEWAY_INTERFACE",
        "LOGON_USER",
        "PATH_INFO",
        "PATH_TRANSLATED",
        "QUERY_STRING",
        "REMOTE_ADDR",
        "REMOTE_HOST",
        "REQUEST_METHOD",
        "SCRIPT_MAP",
        "SCRIPT_NAME",
        "SERVER_NAME",
        "SERVER_PORT",
        "SERVER_PORT_SECURE",
        "SERVER_SOFTWARE",
        "URL" 
    };

#define FROMHEX( _v ) \
    ( ( _v >= 48 ) && ( _v <= 57 ) ? _v - 48 : \
    ( ( _v >= 65 ) && ( _v <= 70 ) ? _v + 10 - 65 : 0 ) )

//
// What:    UnCanonicalize
//
// Desc:    Un-escapes strings
//
BOOL UnCanonicalize( LPSTR lpszStart, LPSTR lpszUnURL , LPDWORD lpdwSize )
{
    LPSTR lps = lpszStart;
    DWORD cb  = 0;

    while (( *lps ) && ( cb < *lpdwSize ))
    {
        if ( *lps == '%' )
        {
            lps++;
            lpszUnURL[ cb ]  = 16 * FROMHEX( *lps);
            lps++;
            lpszUnURL[ cb ] += FROMHEX( *lps);
        } 
        else if ( *lps == '+' )
        {
            lpszUnURL[ cb ] = 32;
        }
        else 
        {
            lpszUnURL[ cb ] = *lps;
        }
        cb++;
        lps++;
    }

    lpszUnURL[ cb ] = 0;  // paranoid
    *lpdwSize = cb;

    return TRUE;

} // UnCanonicalize( )


//
// What:    DumpFormDataFromPost
//
// Desc:    Dumps the fields and values in a form from a POST.
//
BOOL DumpFormDataFromPost( LPECB lpEcb )
{
    BOOL  fReturn = TRUE;
    DWORD dwSize;

    static char szUnparsed[] = "The unparsed Form submit string is: <FONT FACE=\"Courier New\">";
    static char szUnFont[] = "</FONT>";

    TraceMsg( TF_FUNC | TF_RESPONSE, "DumpFormDataFromPost()" );

    if ( fReturn )
    {   // header and the beginning of the table
        CHAR szBuffer[ 512 ];
        dwSize = wsprintf( szBuffer, g_cszTableHeader, 
            "<H2>Form Data (METHOD=POST)</H2>", "TBLPOST" );
        fReturn = WriteClient( lpEcb->ConnID, szBuffer, &dwSize, HSE_IO_ASYNC );
    }

    if ( fReturn )
    {
        int i = 0;
        LPSTR lpszStart = (LPSTR) lpEcb->lpbData;
        while (( fReturn ) && ( lpszStart ))
        {
            CHAR  cTmpEnd;
            LPSTR lpszEquals = StrChr( lpszStart, '=' );
            if ( !lpszEquals )
                break;  // no more data

            LPSTR lpszEnd    = StrChr( lpszEquals, '&' );

            if ( lpszEnd )
            {
                cTmpEnd = *lpszEnd;     // save
                *lpszEnd = 0;
            }

            CHAR cTmp = *lpszEquals;    // save
            *lpszEquals = 0;

            CHAR szBuffer[ 4096 ];
            CHAR szUnURL[ 2048 ];
            dwSize = sizeof( szUnURL );
            fReturn = UnCanonicalize( lpszEquals + 1, szUnURL, &dwSize );
            if ( fReturn )
            {
                dwSize = wsprintf( szBuffer, 
                    "<TR ID=TR%u><TD ID=TD%u>%s</TD><TD ID=%s>%s</TD></TR>",
                    i, i, lpszStart, lpszStart, szUnURL );
                fReturn = WriteClient( lpEcb->ConnID, szBuffer, &dwSize, HSE_IO_ASYNC );
            }

            *lpszEquals = cTmp;         // restore

            if ( lpszEnd )
            {
                *lpszEnd = cTmpEnd;     // restore
                lpszEnd++;
            }

            lpszStart = lpszEnd;
            i++;
        }
    }

    // end of the table
    if ( fReturn )
    {
        dwSize = 1 + lstrlen( g_cszTableEnd );
        fReturn = WriteClient( lpEcb->ConnID, (LPVOID) g_cszTableEnd, &dwSize, HSE_IO_ASYNC );
    }

    // display unparsed information
    if ( fReturn )
    {
        dwSize = 1 + lstrlen( szUnparsed );
        fReturn = WriteClient( lpEcb->ConnID, szUnparsed, &dwSize, HSE_IO_ASYNC );
    }

    if ( fReturn )
    {
        dwSize = 1 + lstrlen( (LPSTR) lpEcb->lpbData );
        fReturn = WriteClient( lpEcb->ConnID, lpEcb->lpbData, &dwSize, HSE_IO_ASYNC );
    }

    // end FONT tag
    if ( fReturn )
    {
        dwSize = 1 + lstrlen( szUnFont );
        fReturn = WriteClient( lpEcb->ConnID, szUnFont, &dwSize, HSE_IO_ASYNC );
    }

    TraceMsg( TF_FUNC | TF_RESPONSE, "DumpFormDataFromPost() Exit = %s",
        BOOLTOSTRING( fReturn ) );
    return fReturn;

} // DumpFormDataFromPost( )

//
// What:    DumpQueryStringDataFromGet
//
// Desc:    Dumps the fields and values in a form from a GET.
//
BOOL DumpQueryStringDataFromGet( LPECB lpEcb )
{
    BOOL fReturn = TRUE;
    LPSTR lpszBuffer;
    DWORD dwSize;

    static char szUnparsed[] = "The unparsed QueryString is: <FONT FACE=\"Courier New\">";
    static char szUnFont[] = "</FONT>";

    TraceMsg( TF_FUNC | TF_RESPONSE, "DumpQueryStringDataFromGet()" );

    if ( fReturn )
        fReturn = GetServerVarString( lpEcb, "QUERY_STRING", &lpszBuffer, NULL );

    if ( fReturn )
    {   // header and the beginning of the table
        CHAR szBuffer[ 512 ];
        dwSize = wsprintf( szBuffer, g_cszTableHeader, 
            "<H2>QueryString Data(METHOD=GET)</H2>", "TBLGET" );
        fReturn = WriteClient( lpEcb->ConnID, szBuffer, &dwSize, HSE_IO_ASYNC );
    }

    if ( fReturn )
    {
        int i = 0;
        LPSTR lpszStart = lpszBuffer;
        while (( fReturn ) && ( lpszStart ))
        {
            CHAR cTmpEnd;
            LPSTR lpszEquals = StrChr( lpszStart, '=' );
            LPSTR lpszEnd    = StrChr( lpszEquals, '&' );

            if ( lpszEnd )
            {
                cTmpEnd = *lpszEnd;     // save
                *lpszEnd = 0;
            }

            CHAR cTmp = *lpszEquals;    // save
            *lpszEquals = 0;

            CHAR szBuffer[ 4096 ];
            CHAR szUnURL[ 2048 ];
            dwSize = sizeof( szUnURL );
            fReturn = UnCanonicalize( lpszEquals + 1, szUnURL, &dwSize );
            if ( fReturn )
            {
                dwSize = wsprintf( szBuffer, 
                    "<TR ID=TR%u><TD ID=TD%u>%s</TD><TD ID=%s>%s</TD></TR>",
                    i, i, lpszStart, lpszStart, szUnURL );
                fReturn = WriteClient( lpEcb->ConnID, szBuffer, &dwSize, HSE_IO_ASYNC );
            }

            *lpszEquals = cTmp;         // restore

            if ( lpszEnd )
            {
                *lpszEnd = cTmpEnd;     // restore
                lpszEnd++;
            }

            lpszStart = lpszEnd;
            i++;
        }
    }

    // end the table
    if ( fReturn )
    {
        dwSize = 1 + lstrlen( g_cszTableEnd );
        fReturn = WriteClient( lpEcb->ConnID, (LPVOID) g_cszTableEnd, &dwSize, HSE_IO_ASYNC );
    }

    // display unparsed information
    if ( fReturn )
    {
        dwSize = 1 + lstrlen( szUnparsed );
        fReturn = WriteClient( lpEcb->ConnID, szUnparsed, &dwSize, HSE_IO_ASYNC );
    }

    if ( fReturn )
    {
        dwSize = 1 + lstrlen( lpszBuffer );
        fReturn = WriteClient( lpEcb->ConnID, lpszBuffer, &dwSize, HSE_IO_ASYNC );
    }

    // end font tag
    if ( fReturn )
    {
        dwSize = 1 + lstrlen( szUnFont );
        fReturn = WriteClient( lpEcb->ConnID, szUnFont, &dwSize, HSE_IO_ASYNC );
    }

    // clean up
    if ( lpszBuffer )
        GlobalFree( lpszBuffer );

    TraceMsg( TF_FUNC | TF_RESPONSE, "DumpQueryStringDataFromGet() Exit = %s",
        BOOLTOSTRING( fReturn ) );
    return fReturn;

} // DumpQueryStringDataFromGet( )

//
// What:    DumpServerVariables
//
// Desc:    Dumps the current server variable settings from the result of
//          a form submission.
//
BOOL DumpServerVariables( LPECB lpEcb )
{
    BOOL  fReturn = TRUE;
    LPSTR lpszBuffer;
    DWORD dwSize;
    int   i = 0;    // counter

    TraceMsg( TF_FUNC | TF_RESPONSE, "DumpServerVariables()" );

    if ( fReturn )
    {   // header and the begging of the table
        CHAR szBuffer[ 512 ];
        dwSize = wsprintf( szBuffer, g_cszTableHeader, 
            "<H2>Server Variables</H2>", "TBLSERVERVARS" );
        fReturn = WriteClient( lpEcb->ConnID, szBuffer, &dwSize, HSE_IO_ASYNC );
    }

    // try the known ones
    for( ; ( fReturn ) && ( i < ARRAYOF( g_lpServerVars ) ); i++ )
    {
        GetServerVarString( lpEcb, g_lpServerVars[ i ], &lpszBuffer, NULL );

        if ( lpszBuffer )
        {
            CHAR szBuffer[ 512 ];
            dwSize = wsprintf( szBuffer, 
                "<TR ID=TR%u><TD ID=TD%u>%s</TD><TD ID=%s>%s</TD></TR>",
                i, i, g_lpServerVars[ i ], g_lpServerVars[ i ], lpszBuffer );
            fReturn = WriteClient( lpEcb->ConnID, szBuffer, &dwSize, HSE_IO_ASYNC );
        }

        GlobalFree( lpszBuffer );
        lpszBuffer = NULL;
    }

    // try the HTTP_ALL from header that the server ignores
    if ( fReturn )
        fReturn = GetServerVarString( lpEcb, "ALL_HTTP", &lpszBuffer, NULL );

    if (( fReturn ) && ( lpszBuffer ))
    {
        LPSTR lpszStart = lpszBuffer;
        while (( fReturn ) && ( lpszStart ))
        {
            char cTmpEnd;

            LPSTR lpszColon = StrChr( lpszStart, ':' );
            LPSTR lpszEnd   = StrStr( lpszColon, "HTTP_" );
            if ( lpszEnd )
            {
                cTmpEnd = *lpszEnd; // save
                *lpszEnd = 0;
            }

            char cTmp = *lpszColon; // save
            *lpszColon = 0;

            CHAR szBuffer[ 4096 ];
            dwSize = wsprintf( szBuffer, 
                "<TR ID=TR%u><TD ID=TD%u>%s</TD><TD ID=%s>%s</TD></TR>",
                i, i, lpszStart, lpszStart, lpszColon + 1 );
            fReturn = WriteClient( lpEcb->ConnID, szBuffer, &dwSize, HSE_IO_ASYNC );

            *lpszColon = cTmp;      // restore

            if ( lpszEnd )
                *lpszEnd = cTmpEnd; // restore

            lpszStart = lpszEnd;
            i++;
        }
    }

    // end the table
    if ( fReturn )
    {
        dwSize = 1 + lstrlen( g_cszTableEnd );
        fReturn = WriteClient( lpEcb->ConnID, (LPVOID) g_cszTableEnd, &dwSize, HSE_IO_ASYNC );
    }

    // clean up
    GlobalFree( lpszBuffer );
    lpszBuffer = NULL;

    TraceMsg( TF_FUNC | TF_RESPONSE, "DumpServerVariables() Exit = %s",
        BOOLTOSTRING( fReturn ) );
    return fReturn;

} // ( )

//
// What:    DumpOutput
//
// Desc:    Generate reponse body page after headers and the HTML header
//          has been.
//
BOOL DumpOutput( 
        QUERYMETHOD eMethod, 
        LPECB       lpEcb, 
        LPSTR       lpszOut, 
        LPSTR       lpszDebug )
{
    BOOL  fReturn;
    DWORD dwSize;
    char  szBuffer[ RESPONSE_BUF_SIZE ];

    TraceMsg( TF_FUNC | TF_RESPONSE, "DumpOutput()" );

    // Display form data
    switch ( eMethod )
    {
    case METHOD_POST:
        fReturn = DumpFormDataFromPost( lpEcb );
        break;

    case METHOD_GET:
        fReturn = DumpQueryStringDataFromGet( lpEcb );
        break;

    case METHOD_POSTTEXTPLAIN:
    case METHOD_POSTMULTIPART:
        if ( lpszOut )
        {
            dwSize = 1 + lstrlen( lpszOut );
            fReturn = WriteClient( lpEcb->ConnID, lpszOut, &dwSize, HSE_IO_ASYNC );
        }
        break;

    default:
        StrCpy( szBuffer, "<H2>???? (METHOD=UNKNOWN)</H2>" );
        dwSize = 1 + lstrlen( szBuffer );
        fReturn = WriteClient( lpEcb->ConnID, szBuffer, &dwSize, HSE_IO_ASYNC );
    }

    // Dump the server variables
    if ( fReturn )
        fReturn = DumpServerVariables( lpEcb );

    // Dump any debugging messages
    if (( fReturn ) && ( lpszDebug ))
    {
        StrCpy( szBuffer, "<H2>Debugging Output</H2>" );
        dwSize = 1 + lstrlen( szBuffer );
        fReturn = WriteClient( lpEcb->ConnID, szBuffer, &dwSize, HSE_IO_ASYNC );

        if ( fReturn )
            fReturn = OutputHTMLString( lpEcb, lpszDebug );
    }

    // Dump any server log entries
    if (( fReturn ) && ( lpEcb->lpszLogData[ 0 ] ))
    {
        StrCpy( szBuffer, "<H2>Server Log Entry</H2>" );
        dwSize = 1 + lstrlen( szBuffer );
        fReturn = WriteClient( lpEcb->ConnID, szBuffer, &dwSize, HSE_IO_ASYNC );
        if ( fReturn )
        {
            dwSize = 1 + lstrlen( lpEcb->lpszLogData );
            fReturn = WriteClient( lpEcb->ConnID, lpEcb->lpszLogData, &dwSize, HSE_IO_ASYNC );
        }
    }

    TraceMsg( TF_FUNC | TF_RESPONSE, "DumpOutput() Exit = %s",
        BOOLTOSTRING( fReturn ) );
    return fReturn;

} // DumpOutput( )

// 
// What:    SendSuccess
//
// Desc:    Sends client an HTML response for a successful upload. Just a
//          green screen with "SUCCESS!" in a big header.
//
// In:      lpEcb is the EXTENDED_CONTROL_BLOCK that the server sent us.
//
BOOL SendSuccess( 
    QUERYMETHOD eMethod, 
    LPECB       lpEcb, 
    LPSTR       lpszOut, 
    LPSTR       lpszDebug,
    LPBYTE      lpbData,
    DWORD       dwParsed,
    LPDUMPTABLE lpDT ) 
{
    BOOL  fReturn = TRUE;    // assume success
    CHAR  szBuffer[ RESPONSE_BUF_SIZE ];
    DWORD dwSize;
    BOOL  fDebug = FALSE;

    TraceMsg( TF_FUNC | TF_RESPONSE, "SendSuccess( )" );

    // Generate on the fly so
    // fReturn = SendServerHeader( lpEcb );

    if ( fReturn )
    {
        StrCpy( szBuffer, "\
            <HTML>\
            <HEAD>\
            <TITLE>Form Submission Reflector</TITLE>\
            </HEAD>\
            <BODY bgcolor=#00FF00>\
            <FONT FACE=\"ARIAL,HELVETICA\" SIZE=2>\
            Submission Result <INPUT TYPE=TEXT NAME=RESULT VALUE=\"SUCCESS!\">" );
        dwSize = 1 + lstrlen( szBuffer );
        fReturn = WriteClient( lpEcb->ConnID, szBuffer, &dwSize, HSE_IO_ASYNC );
    }

    if ( fReturn )
        fReturn = CheckForDebug( lpEcb, &fDebug );

    if ( fReturn )
    {
        if ( fDebug )
            fReturn = DumpOutput( eMethod, lpEcb, lpszOut, lpszDebug );
        else
            fReturn = DumpOutput( eMethod, lpEcb, lpszOut, NULL );
    }

    if (( fReturn ) && ( fDebug ))
        fReturn = HexDump( lpEcb, lpbData, dwParsed, lpDT );

    if ( fReturn )
    {
        StrCpy( szBuffer, "</BODY></HTML>" );
        dwSize = 1 + lstrlen( szBuffer );
        fReturn = WriteClient( lpEcb->ConnID, szBuffer, &dwSize, HSE_IO_ASYNC );
    }

    TraceMsg( TF_FUNC | TF_RESPONSE, "SendSuccess( ) Exit = %s",
        BOOLTOSTRING( fReturn ) );
    return fReturn;
} // SendSuccess( )

// 
// What:    SendFailure
//
// Desc:    Sends client an HTML response for a failed upload. Just a
//          red screen with "FAILED!" in a big header.
//
// In:      lpEcb is the EXTENDED_CONTROL_BLOCK that the server sent us.
//
BOOL SendFailure( 
    QUERYMETHOD eMethod, 
    LPECB       lpEcb, 
    LPSTR       lpszOut, 
    LPSTR       lpszDebug,
    LPBYTE      lpbData,
    DWORD       dwParsed,
    LPDUMPTABLE lpDT ) 
{
    BOOL  fReturn = TRUE;    // assume success
    CHAR  szBuffer[ RESPONSE_BUF_SIZE ];
    DWORD dwSize;
    BOOL  fDebug = FALSE;

    TraceMsg( TF_FUNC | TF_RESPONSE, "SendFailure( )" );

    // fReturn = SendServerHeader( lpEcb );

    if ( fReturn )
    {
        StrCpy( szBuffer, "\
            <HTML>\
            <HEAD>\
            <TITLE>Form Submission Reflector</TITLE>\
            </HEAD>\
            <BODY bgcolor=#FF0000>\
            <FONT FACE=\"ARIAL,HELVETICA\" SIZE=2>\
            Submission Result <INPUT TYPE=TEXT NAME=RESULT VALUE=\"FAILED!\">" );
        dwSize = 1 + lstrlen( szBuffer );
        fReturn = WriteClient( lpEcb->ConnID, szBuffer, &dwSize, HSE_IO_ASYNC );
    }

    if ( fReturn )
        fReturn = CheckForDebug( lpEcb, &fDebug );

    if ( fReturn )
    {
        if ( fDebug )
            fReturn = DumpOutput( eMethod, lpEcb, lpszOut, lpszDebug );
        else
            fReturn = DumpOutput( eMethod, lpEcb, lpszOut, NULL );
    }

    if (( fReturn ) && ( fDebug ))
        fReturn = HexDump( lpEcb, lpbData, dwParsed, lpDT );

    if ( fReturn )
    {
        StrCpy( szBuffer, "</BODY></HTML>" );
        dwSize = 1 + lstrlen( szBuffer );
        fReturn = WriteClient( lpEcb->ConnID, szBuffer, &dwSize, HSE_IO_ASYNC );
    }

    TraceMsg( TF_FUNC | TF_RESPONSE, "SendFailure( ) Exit = %s",
        BOOLTOSTRING( fReturn ) );
    return fReturn;
} // SendSuccess( )


// 
// What:    SendRedirect
//
// Desc:    Redirects to another URL.
//
// In:      lpEcb is the EXTENDED_CONTROL_BLOCK that the server sent us.
//
BOOL SendRedirect( LPECB lpEcb, LPSTR lpszURL ) 
{
    BOOL fReturn = TRUE;    // assume success
    CHAR szBuffer[ RESPONSE_BUF_SIZE ];
    DWORD dwSize = 1 + lstrlen( lpszURL );
    DWORD dw;

    TraceMsg( TF_FUNC | TF_RESPONSE, "SendRedirect( )" );

    fReturn = ServerSupportFunction( lpEcb->ConnID, HSE_REQ_SEND_URL_REDIRECT_RESP,
        lpszURL, &dwSize, &dw );

    TraceMsg( TF_FUNC | TF_RESPONSE, "SendRedirect( ) Exit = %s",
        BOOLTOSTRING( fReturn ) );
    return fReturn;
} // SendSuccess( )


//
// What:    SendEcho
//
// Desc:    Sends client an echo for everything that it sent to us.
//
// In:      lpEcb is the EXTENDED_CONTROL_BLOCK
//
BOOL SendEcho( LPECB lpEcb )
{
    BOOL fReturn = TRUE;    // assume success
    CHAR szBuffer[ RESPONSE_BUF_SIZE ];
    DWORD dwSize;
    LPVOID  lpMoreData = NULL;

    TraceMsg( TF_FUNC | TF_RESPONSE, "SendEcho( )" );

    BOOL fDownloadComplete = (lpEcb->cbTotalBytes == lpEcb->cbAvailable );

    TraceMsg( TF_RESPONSE, "Does cbTotalBytes(%u) == cbAvailable(%u)? %s",
        lpEcb->cbTotalBytes, lpEcb->cbAvailable, 
        BOOLTOSTRING( fDownloadComplete ) );

    if ( !fDownloadComplete )
    {   // Get the rest of the data
        dwSize = lpEcb->cbTotalBytes - lpEcb->cbAvailable;
        lpMoreData = (LPVOID) GlobalAlloc( GPTR, dwSize );
        fReturn = ReadData( lpEcb, lpMoreData, dwSize );
        if ( !fReturn )
            goto Cleanup;
    }

    TraceMsg( TF_RESPONSE, "Total Bytes: %u\n%s", 
        lpEcb->cbAvailable, lpEcb->lpbData );

    // Server header info...
    fReturn = SendServerHeader( lpEcb );
    if ( !fReturn )
        goto Cleanup;

    // Create top part of response and send it
    StrCpy( szBuffer, "<HTML><BODY bgcolor=#0000FF><PRE>" );
    dwSize = 1 + lstrlen( szBuffer );
    fReturn = WriteClient( lpEcb->ConnID, szBuffer, &dwSize, HSE_IO_ASYNC );
    if ( !fReturn )
        goto Cleanup;

    // and echo it back...
    dwSize = lpEcb->cbAvailable;
    fReturn = WriteClient( lpEcb->ConnID, lpEcb->lpbData, &dwSize, HSE_IO_ASYNC );
    if ( !fReturn )
        goto Cleanup;

    // and anything else that was sent...
    if ( lpMoreData ) 
    {
        dwSize = lpEcb->cbTotalBytes - lpEcb->cbAvailable;
        fReturn = WriteClient( lpEcb->ConnID, lpMoreData, &dwSize, HSE_IO_ASYNC );
        if ( !fReturn )
            goto Cleanup;
    }

    // Create bottom part of response and send it
    StrCpy( szBuffer, "</PRE></BODY></HTML>" );
    dwSize = 1 + lstrlen( szBuffer );
    fReturn = WriteClient( lpEcb->ConnID, szBuffer, &dwSize, HSE_IO_ASYNC );
    if ( !fReturn )
        goto Cleanup;

Cleanup:
    if ( lpMoreData )
        GlobalFree( lpMoreData );

    TraceMsg( TF_FUNC | TF_RESPONSE, "SendEcho( ) Exit = %s",
        BOOLTOSTRING( fReturn ) );
    return fReturn;
} // SendEcho( )


//
// What:    SendServerHeader
//
// Desc:    This sends a complete HTTP server response header including the 
//          status, server version, message time, and MIME version. The ISAPI 
//          application should append other HTTP headers such as the content 
//          type and content length, followed by an extra "\r\n". This function 
//          only takes textual data, up to the first '\0' terminator
//
// In:      lpEcb is the EXTENDED_CONTROL_BLOCK
//
BOOL SendServerHeader( LPECB lpEcb )
{
    BOOL fReturn;
    CHAR szBuffer[ RESPONSE_BUF_SIZE ];
    DWORD dwSize, dwParam;

    TraceMsg( TF_FUNC | TF_RESPONSE, "SendServerHeader( )" );


    TraceMsg( TF_RESPONSE, "Sending Pre Header: %s", szBuffer );

    dwSize = wsprintf( szBuffer, "200 OK" );
    dwParam = 0;
    fReturn = ServerSupportFunction( lpEcb->ConnID, HSE_REQ_SEND_RESPONSE_HEADER,
        szBuffer, &dwSize, &dwParam );
    if ( !fReturn )
        goto Cleanup;

    TraceMsg( TF_RESPONSE, "Sending Post Header: %s", szBuffer );

    dwSize = wsprintf( szBuffer, "Content-Type: text/html\r\n" );

    fReturn = WriteClient( lpEcb->ConnID, szBuffer, &dwSize, HSE_IO_ASYNC );
    if ( !fReturn )
        goto Cleanup;


Cleanup:
    TraceMsg( TF_FUNC | TF_RESPONSE, "SendServerHeader( ) Exit = %s",
        BOOLTOSTRING( fReturn ) );
    return fReturn;

} // SendServerHeader( )

//
// What:    OutputHTMLString
//
// Desc:    Outputs HTML to client. Simply changes '\n's to <BR>s.
//
// In:      lpEcb is the EXTENDED_CONTROL_BLOCK
//          lpszOut string to translate to HTML.
//
BOOL OutputHTMLString( LPECB lpEcb, LPSTR lpszOut )
{
    BOOL  fReturn = TRUE;
    LPSTR lpstr;
    DWORD dwSize;

    TraceMsg( TF_FUNC | TF_RESPONSE, "OutputHTMLString()" );

    if( *lpszOut )
    {
        lpstr = lpszOut;
        while (( *lpstr ) && ( fReturn))
        {
            lpszOut = lpstr;
            lpstr++;

            while (( *lpstr ) && ( *lpstr != '\n' ))
                lpstr++;

            CHAR cTmp = *lpstr;     // save
            *lpstr = 0;

            dwSize = 1 + lstrlen( lpszOut );
            fReturn = WriteClient( lpEcb->ConnID, lpszOut, &dwSize, HSE_IO_ASYNC );

            *lpstr = cTmp;          // restore

            if ( fReturn )
            {
                dwSize  = 4;
                fReturn = WriteClient( lpEcb->ConnID, "<BR>", &dwSize, HSE_IO_ASYNC );
            }
        }
    }

    TraceMsg( TF_FUNC | TF_RESPONSE, "OutputHTMLString() Exit = %s",
        BOOLTOSTRING( fReturn ) );
    return fReturn;
} // OutputHTMLString( )

// 
// What:    HexDump
//
// Desc:    Dumps a HEX dump to the client using HTML. (16-byte rows)
//
// In:      lpEcb is the EXTENDED_CONTROL_BLOCK
//          lpbData is the data to be dumped
//          dwLength is the length of the dump.
//          lpDT is DUMPTABLE which contains information about what the
//              parser found while parsing. The end of the DT is indicated
//              by a NULL lpAddr.
//
// Return:  Return a pointer to the formatted output buffer.
//
BOOL HexDump( 
        LPECB lpEcb,
        LPBYTE lpbData, 
        DWORD dwLength, 
        LPDUMPTABLE lpDT )
{
    BOOL   fReturn;
    DWORD  cb           = 0;            // number of bytes processed
    DWORD  cbDT         = 0;            // number of DTs processed
    DWORD  cbComment    = 0;            // number of DT comments processed
    LPBYTE lpb          = lpbData;      // current data byte to be processed
    DWORD  dwColor      = 0x000000;     // color to output text in
    LPBYTE lpbStartLine = lpb;          // keeps track of where this line begins
                                        // for the string dump
    BOOL   fKeepGoing   = TRUE;         // exit flag
    CHAR   szBuffer[ 4096 ];            // output buffer
    CHAR   szString[ 512 ];             // "String Dump" output buffer
    DWORD  cbBuf, cbStr;                // helper count bytes for buffers

    // Output Dump Header
    cbBuf = wsprintf( szBuffer, "\
        <H2>Parser Commented Request Dump</H2>\
        <FONT FACE=\"Courier\">\
        <TABLE colpadding=2>\
        <TR><TD>Offset</TD><TD>Hex Dump</TD><TD>String Dump</TD><TD>Comments</TD></TR>" );

    fReturn = WriteClient( lpEcb->ConnID, szBuffer, &cbBuf, HSE_IO_ASYNC );

    while (( fReturn ) && ( fKeepGoing ))
    {
        if ( cb == 0 )
        {   // beginning of every row....
            if (( cbDT ) 
               && ( ( lpDT[ cbDT ].lpAddr - lpb ) > 64 ))
            {
                // eliminates "big" body data (like binary files)
                cbBuf = wsprintf( szBuffer, "<TR><TD>Skipping...</TD><TD>.</TD><TD>.</TD><TD>.</TD></TR>" );   

                // jump to one row before the next DT point
                lpb = (LPBYTE) ( ( ( (DWORD) lpDT[ cbDT ].lpAddr / 16 ) - 1 ) * 16 );
            } 
            else 
            {
                cbBuf = 0;
            }

            // Output 0xXXXXXXXX (NNNNNN): and change the color
            cbBuf += wsprintf( &szBuffer[ cbBuf ], 
                "<TR><TD>0x%-8.8x (%-6.6u):</TD><TD><FONT COLOR=%-6.6x>",
                lpb - lpbData, lpb - lpbData, dwColor );

            // starting color on "String Dump"
            cbStr = wsprintf( szString, "<FONT COLOR=%-6.6x>", dwColor );
        }

        if ( lpb < lpbData + dwLength )
        {   // middle of every row...

            // color change if needed
            while (( lpDT[ cbDT ].lpAddr )
               && ( lpb >= lpDT[ cbDT ].lpAddr ))
            {
                dwColor = lpDT[ cbDT ].dwColor;
                cbBuf += wsprintf( &szBuffer[ cbBuf ], "</FONT><FONT COLOR=%-6.6x>", dwColor );

                cbStr += wsprintf( &szString[ cbStr ], "</FONT><FONT COLOR=%-6.6x>", dwColor );

                cbDT++;
            }

            // output hex number
            cbBuf += wsprintf( &szBuffer[ cbBuf ], "%-2.2x ", *lpb );

            // output "String Dump" character
            cbStr += wsprintf( &szString[ cbStr ], "%c", 
                ( ( *lpb < 32 || *lpb == 127 ) ? '.' : ( ( *lpb == 32 ) ? '_' : *lpb ) ) );

            lpb++;

        }

        cb++;   // always count even if there is no more data

        if ( cb == 16 )
        {   // end of every row...

            // terminate FONT tags and append "String Dump"
            cbStr += wsprintf( &szString[ cbStr ], "</FONT>" );
            cbBuf += wsprintf( &szBuffer[ cbBuf ], "</FONT></TD><TD>%s</TD>",
                szString );

            // skip NULL comments
            while (( lpDT[ cbComment ].lpAddr ) 
                  && ( !lpDT[ cbComment ].lpszComment ))
                  cbComment++;  

            // don't allow comments to get ahead of the bits
            if (( lpDT[ cbComment ].lpAddr ) && ( cbComment < cbDT )) 
            {
                cbBuf += wsprintf( &szBuffer[ cbBuf ], 
                    "<TD><FONT COLOR=%-6.6x>%s</FONT></TD></TR>",
                    lpDT[ cbComment ].dwColor, lpDT[ cbComment ].lpszComment );
                cbComment++;
            } 
            else 
            {
                cbBuf += wsprintf( &szBuffer[ cbBuf ], "<TD><BR></TD></TR>" );
            }

            fReturn = WriteClient( lpEcb->ConnID, szBuffer, &cbBuf, HSE_IO_ASYNC );

            cb = 0;
            lpbStartLine = lpb;

            if ( lpb >= lpbData + dwLength )
                fKeepGoing = FALSE;
        }
    }

    // end the table
    if ( fReturn )
    {
        cbBuf = wsprintf( szBuffer, "</TABLE></FONT>" );
        fReturn = WriteClient( lpEcb->ConnID, szBuffer, &cbBuf, HSE_IO_ASYNC );
    }

    return fReturn;
} // HexDump( )