static void
parseBase64(xmlrpc_env *    const envP,
            const char *    const str,
            size_t          const strLength,
            xmlrpc_value ** const valuePP) {
/*----------------------------------------------------------------------------
   Parse the content of a <base64> XML-RPC XML element, e.g. "FD32YY".

   'str' is that content.
-----------------------------------------------------------------------------*/
    xmlrpc_mem_block * decoded;
    
    XMLRPC_ASSERT_ENV_OK(envP);
    XMLRPC_ASSERT_PTR_OK(str);

    decoded = xmlrpc_base64_decode(envP, str, strLength);
    if (!envP->fault_occurred) {
        unsigned char * const bytes =
            XMLRPC_MEMBLOCK_CONTENTS(unsigned char, decoded);
        size_t const byteCount =
            XMLRPC_MEMBLOCK_SIZE(unsigned char, decoded);

        *valuePP = xmlrpc_base64_new(envP, byteCount, bytes);
        
        XMLRPC_MEMBLOCK_FREE(unsigned char, decoded);
    }
/*
 * This is a blocking function that merely sits on the request queue
 * for our URI and processes them one at a time.  Once a request comes
 * in, we check it for content-type, content-length, and verb.  As long
 * as the initial validations are done, we pass the request to the 
 * processRPCCall() function, which collects the body of the request
 * and processes it.  If we get an error back other than network type,
 * we are responsible for notifing the client.
 */
DWORD
DoReceiveRequests(
    IN HANDLE hReqQueue,
    const xmlrpc_server_httpsys_parms * const parmsP
    )
{
    ULONG              result;
    HTTP_REQUEST_ID    requestId;
    DWORD              bytesRead;
    PHTTP_REQUEST      pRequest;
    PCHAR              pRequestBuffer;
    ULONG              RequestBufferLength;
    xmlrpc_env          env;
    char                szHeaderBuf[255];
    long                lContentLength;

    // Allocate a 2K buffer. Should be good for most requests, we'll grow 
    // this if required. We also need space for a HTTP_REQUEST structure.
    RequestBufferLength = sizeof(HTTP_REQUEST) + 2048;
    pRequestBuffer      = (PCHAR) ALLOC_MEM( RequestBufferLength );
    if (pRequestBuffer == NULL)
    {
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    pRequest = (PHTTP_REQUEST)pRequestBuffer;

    // Wait for a new request -- This is indicated by a NULL request ID.
    HTTP_SET_NULL_ID( &requestId );
    for(;;)
    {
        RtlZeroMemory(pRequest, RequestBufferLength);

        result = HttpReceiveHttpRequest(
            hReqQueue,          // Req Queue
            requestId,          // Req ID
            0,                  // Flags
            pRequest,           // HTTP request buffer
            RequestBufferLength,// req buffer length
            &bytesRead,         // bytes received
            NULL                // LPOVERLAPPED
            );

        if(NO_ERROR == result)
        {
            // Got a request with a filled buffer.
            switch(pRequest->Verb)
            {
                case HttpVerbPOST:

                    TraceW(L"Got a POST request for %ws",
                           pRequest->CookedUrl.pFullUrl);              
                    
                    //Check if we need use authorization.
                    if(parmsP->authfn)
                    {
                        xmlrpc_env_init(&env);
                        if(pRequest->Headers.KnownHeaders[
                            HttpHeaderAuthorization].RawValueLength
                           < 6)
                        {
                            xmlrpc_env_set_fault(
                                &env, XMLRPC_REQUEST_REFUSED_ERROR, 
                                "Authorization header too short.");
                        }
                        else
                        {
                            //unencode the headers
                            if(_strnicmp(
                                "basic",
                                pRequest->Headers.KnownHeaders[
                                    HttpHeaderAuthorization].pRawValue,5)
                               !=0)
                            {
#ifndef  NDEBUG
                                PCHAR pTmp = (PCHAR)
                                    ALLOC_MEM(pRequest->Headers.KnownHeaders[
                                        HttpHeaderAuthorization
                                        ].RawValueLength + 1 );
                                if( pTmp ) {
                                    strncpy(pTmp,
                                            pRequest->Headers.KnownHeaders[
                                                HttpHeaderAuthorization
                                                ].pRawValue,
                                            pRequest->Headers.KnownHeaders[
                                                HttpHeaderAuthorization
                                                ].RawValueLength );
                                    pTmp[pRequest->Headers.KnownHeaders[
                                        HttpHeaderAuthorization
                                        ].RawValueLength] = 0;
                                    TraceA("Got HEADER [%s]",pTmp);
                                    FREE_MEM(pTmp);
                                }
#endif   /* #ifndef NDEBUG */
                                xmlrpc_env_set_fault(
                                    &env, XMLRPC_REQUEST_REFUSED_ERROR, 
                                    "Authorization header does not start "
                                    "with type 'basic'!");
                            }
                            else
                            {
                                xmlrpc_mem_block * decoded;
                                
                                decoded =
                                    xmlrpc_base64_decode(
                                        &env,
                                        pRequest->Headers.KnownHeaders[
                                            HttpHeaderAuthorization
                                            ].pRawValue+6,
                                        pRequest->Headers.KnownHeaders[
                                            HttpHeaderAuthorization
                                            ].RawValueLength-6);
                                if(!env.fault_occurred)
                                {
                                    char *pDecodedStr;
                                    char *pUser;
                                    char *pPass;
                                    char *pColon;
                                    
                                    pDecodedStr = (char*)
                                        malloc(decoded->_size+1);
                                    memcpy(pDecodedStr,
                                           decoded->_block,
                                           decoded->_size);
                                    pDecodedStr[decoded->_size]='\0';
                                    pUser = pPass = pDecodedStr;
                                    pColon=strchr(pDecodedStr,':');
                                    if(pColon)
                                    {
                                        *pColon='\0';
                                        pPass=pColon+1;
                                        //The authfn should set env to
                                        //fail if auth is denied.
                                        parmsP->authfn(&env,pUser,pPass);
                                    }
                                    else
                                    {
                                        xmlrpc_env_set_fault(
                                            &env,
                                            XMLRPC_REQUEST_REFUSED_ERROR, 
                                            "Decoded auth not of the correct "
                                            "format.");
                                    }
                                    free(pDecodedStr);
                                }
                                if(decoded)
                                    XMLRPC_MEMBLOCK_FREE(char, decoded);
                            }
                        }
                        if(env.fault_occurred)
                        {
                            //request basic authorization, as the user
                            //did not provide it.
                            xmlrpc_env_clean(&env);
                            TraceW(L"POST request did not provide valid "
                                   L"authorization header.");
                            result =
                                SendHttpResponseAuthRequired( hReqQueue,
                                                              pRequest);
                            break;
                        }
                        xmlrpc_env_clean(&env);
                    }
                    
                    //Check content type to make sure it is text/xml.
                    memcpy(szHeaderBuf,
                           pRequest->Headers.KnownHeaders[
                               HttpHeaderContentType
                               ].pRawValue,
                           pRequest->Headers.KnownHeaders[
                               HttpHeaderContentType
                               ].RawValueLength);
                    szHeaderBuf[pRequest->Headers.KnownHeaders[
                        HttpHeaderContentType
                        ].RawValueLength] = '\0';
                    if (_stricmp(szHeaderBuf,"text/xml")!=0)
                    {
                        //We handle only text/xml data.  Anything else
                        //is not valid.
                        TraceW(L"POST request had an unrecognized "
                               L"content-type: %s", szHeaderBuf);
                        result = SendHttpResponse(
                            hReqQueue, 
                            pRequest,
                            400,
                            "Bad Request",
                            NULL
                            );
                        break;
                    }

                    //Check content length to make sure it exists and
                    //is not too big.
                    memcpy(szHeaderBuf,
                           pRequest->Headers.KnownHeaders[
                               HttpHeaderContentLength
                               ].pRawValue,
                           pRequest->Headers.KnownHeaders[
                               HttpHeaderContentLength
                               ].RawValueLength);
                    szHeaderBuf[pRequest->Headers.KnownHeaders[
                        HttpHeaderContentLength
                        ].RawValueLength]='\0';
                    lContentLength = atol(szHeaderBuf);
                    if (lContentLength<=0)
                    {
                        //Make sure a content length was supplied.
                        TraceW(L"POST request did not include a "
                               L"content-length", szHeaderBuf);
                        result = SendHttpResponse(
                            hReqQueue, 
                            pRequest,
                            411,
                            "Length Required",
                            NULL
                            );
                        break;
                    }                       
                    if((size_t) lContentLength >
                       xmlrpc_limit_get(XMLRPC_XML_SIZE_LIMIT_ID))
                    {
                        //Content-length is too big for us to handle
                        TraceW(L"POST request content-length is too big "
                               L"for us to handle: %d bytes",
                               lContentLength);
                        result = SendHttpResponse(
                            hReqQueue, 
                            pRequest,
                            500,
                            "content-length too large",
                            NULL
                            );
                        break;
                    }

                    //our initial validations of POST, content-type,
                    //and content-length all check out.  Collect and
                    //pass the complete buffer to the XMLRPC-C library
                    
                    xmlrpc_env_init(&env);
                    processRPCCall(&env,hReqQueue, pRequest);
                    if (env.fault_occurred) 
                    {
                        //if we fail and it is anything other than a
                        //network error, we should return a failure
                        //response to the client.
                        if (env.fault_code != XMLRPC_NETWORK_ERROR)
                        {
                            if (env.fault_string)
                                result = SendHttpResponse(
                                    hReqQueue, 
                                    pRequest,
                                    500,
                                    env.fault_string,
                                    NULL
                                    );
                            else
                                result = SendHttpResponse(
                                    hReqQueue, 
                                    pRequest,
                                    500,
                                    "Unknown Error",
                                    NULL
                                    );
                        }
                    }
                    
                    xmlrpc_env_clean(&env);
                    break;

                default:
                    //We handle only POST data.  Anything else is not valid.
                    TraceW(L"Got an unrecognized Verb request for URI %ws",
                           pRequest->CookedUrl.pFullUrl);
            
                    result = SendHttpResponse(
                        hReqQueue, 
                        pRequest,
                        405,
                        "Method Not Allowed",
                        NULL
                        );
                    break;
            }
            if(result != NO_ERROR)
            {
                break;
            }

            // Reset the Request ID so that we pick up the next request.
            HTTP_SET_NULL_ID( &requestId );
        }
        else if(result == ERROR_MORE_DATA)
        {
            // The input buffer was too small to hold the request headers
            // We have to allocate more buffer & call the API again. 
            //
            // When we call the API again, we want to pick up the request
            // that just failed. This is done by passing a RequestID.
            // This RequestID is picked from the old buffer.
            requestId = pRequest->RequestId;

            // Free the old buffer and allocate a new one.
            RequestBufferLength = bytesRead;
            FREE_MEM( pRequestBuffer );
            pRequestBuffer = (PCHAR) ALLOC_MEM( RequestBufferLength );

            if (pRequestBuffer == NULL)
            {
                result = ERROR_NOT_ENOUGH_MEMORY;
                break;
            }

            pRequest = (PHTTP_REQUEST)pRequestBuffer;

        }
        else if(ERROR_CONNECTION_INVALID == result && 
                !HTTP_IS_NULL_ID(&requestId))
        {
            // The TCP connection got torn down by the peer when we were
            // trying to pick up a request with more buffer. We'll just move
            // onto the next request.            
            HTTP_SET_NULL_ID( &requestId );
        }
        else
        {
            break;
        }

    } // for(;;)

    if(pRequestBuffer)
    {
        FREE_MEM( pRequestBuffer );
    }

    return result;
}
static void
test_base64_conversion(void) {
    xmlrpc_env env;
    char ** triplet;

    xmlrpc_env_init(&env);

    for (triplet = base64_triplets; *triplet != NULL; triplet += 3) {
        char * bin_data;
        char * nocrlf_ascii_data;
        char * ascii_data;
        xmlrpc_mem_block * output;

        bin_data = *triplet;
        nocrlf_ascii_data = *(triplet + 1);
        ascii_data = *(triplet + 2);

        /* Test our encoding routine. */
        output = xmlrpc_base64_encode(&env,
                                      (unsigned char*) bin_data,
                                      strlen(bin_data));
        TEST_NO_FAULT(&env);
        TEST(output != NULL);
        TEST(xmlrpc_mem_block_size(output) == strlen(ascii_data));
        TEST(memcmp(xmlrpc_mem_block_contents(output), ascii_data,
                    strlen(ascii_data)) == 0);
        xmlrpc_mem_block_free(output);

        /* Test our newline-free encoding routine. */
        output =
            xmlrpc_base64_encode_without_newlines(&env,
                                                  (unsigned char*) bin_data,
                                                  strlen(bin_data));
        TEST_NO_FAULT(&env);
        TEST(output != NULL);
        TEST(xmlrpc_mem_block_size(output) == strlen(nocrlf_ascii_data));
        TEST(memcmp(xmlrpc_mem_block_contents(output), nocrlf_ascii_data,
                    strlen(nocrlf_ascii_data)) == 0);
        xmlrpc_mem_block_free(output);

        /* Test our decoding routine. */
        output = xmlrpc_base64_decode(&env, ascii_data, strlen(ascii_data));
        TEST_NO_FAULT(&env);
        TEST(output != NULL);
        TEST(xmlrpc_mem_block_size(output) == strlen(bin_data));
        TEST(memcmp(xmlrpc_mem_block_contents(output), bin_data,
                    strlen(bin_data)) == 0);
        xmlrpc_mem_block_free(output);
    }

    /* Now for something broken... */
    {
        xmlrpc_env env2;
        xmlrpc_mem_block * output;

        xmlrpc_env_init(&env2);
        output = xmlrpc_base64_decode(&env2, "====", 4);
        TEST(output == NULL);
        TEST_FAULT(&env2, XMLRPC_PARSE_ERROR);
        xmlrpc_env_clean(&env2);
    }
    /* Now for something broken in a really sneaky way... */
    {
        xmlrpc_env env2;
        xmlrpc_mem_block * output;
        xmlrpc_env_init(&env2);
        output = xmlrpc_base64_decode(&env2, "a==", 4);
        TEST(output == NULL);
        TEST_FAULT(&env2, XMLRPC_PARSE_ERROR);
        xmlrpc_env_clean(&env2);
    }
    xmlrpc_env_clean(&env);
}