/*++

Copyright (c) 2001  Microsoft Corporation

Module Name:

    utils.cpp

Abstract:

    Utilities for the BITS server extensions

--*/

#include "precomp.h"

const char *LookupHTTPStatusCodeText(
    DWORD HttpCode
    )
{
    switch( HttpCode )
        {
        case 100:   return "100 Continue";
        case 101:   return "101 Switching Protocols";
        case 200:   return "200 OK";
        case 201:   return "201 Created";
        case 202:   return "202 Accepted";
        case 203:   return "203 Non-Authoritative Information";
        case 204:   return "204 No Content";
        case 205:   return "205 Reset Content";
        case 206:   return "206 Partial Content";
        case 300:   return "300 Multiple Choices";
        case 301:   return "301 Moved Permanently";
        case 302:   return "302 Found";
        case 303:   return "303 See Other";
        case 304:   return "304 Not Modified";
        case 305:   return "305 Use Proxy";
        case 306:   return "306 (Unused)";
        case 307:   return "307 Temporary Redirect";
        case 400:   return "400 Bad Request";
        case 401:   return "401 Unauthorized";
        case 402:   return "402 Payment Required";
        case 403:   return "403 Forbidden";
        case 404:   return "404 Not Found";
        case 405:   return "405 Method Not Allowed";
        case 406:   return "406 Not Acceptable";
        case 407:   return "407 Proxy Authentication Required";
        case 408:   return "408 Request Timeout";
        case 409:   return "409 Conflict";
        case 410:   return "410 Gone";
        case 411:   return "411 Length Required";
        case 412:   return "412 Precondition Failed";
        case 413:   return "413 Request Entity Too Large";
        case 414:   return "414 Request-URI Too Long";
        case 415:   return "415 Unsupported Media Type";
        case 416:   return "416 Requested Range Not Satisfiable";
        case 417:   return "417 Expectation Failed";
        case 500:   return "500 Internal Server Error";
        case 501:   return "501 Not Implemented";
        case 502:   return "502 Bad Gateway";
        case 503:   return "503 Service Unavailable";
        case 504:   return "504 Gateway Timeout";
        case 505:   return "505 HTTP Version Not Supported";

        default:
            return "";

        }

}

void
ServerException::SendErrorResponse(
    EXTENSION_CONTROL_BLOCK * ExtensionControlBlock
    ) const
{

    char Headers[255];
    StringCbPrintfA( 
        Headers,
        sizeof( Headers ),
        "Pragma: no-cache\r\n"
        "BITS-packet-type: Ack\r\n"
        "BITS-Error: 0x%8.8X\r\n"
        "\r\n", 
        m_Code );

    ExtensionControlBlock->dwHttpStatusCode = m_HttpCode;

    BOOL Result;
    BOOL KeepConnection;

    Result =
        (ExtensionControlBlock->ServerSupportFunction)(
            ExtensionControlBlock->ConnID,
            HSE_REQ_IS_KEEP_CONN,
            &KeepConnection,
            NULL,
            NULL );

    if ( !Result )
        throw ServerException( HRESULT_FROM_WIN32( GetLastError() ) );

    // IIS5.0(Win2k) has a bug where KeepConnect is returned as -1
    // to keep the connection alive.   Apparently, this confuses the
    // HSE_REQ_SEND_RESPONSE_HEADER_EX call.   Bash the value into a real bool.

    KeepConnection = KeepConnection ? 1 : 0;

    HSE_SEND_HEADER_EX_INFO HeaderInfo;
    HeaderInfo.pszStatus = LookupHTTPStatusCodeText( m_HttpCode );
    HeaderInfo.cchStatus = strlen( HeaderInfo.pszStatus );
    HeaderInfo.pszHeader = Headers;
    HeaderInfo.cchHeader = strlen( Headers );
    HeaderInfo.fKeepConn = KeepConnection;

    Result =
        (ExtensionControlBlock->ServerSupportFunction)(
            ExtensionControlBlock->ConnID,
            HSE_REQ_SEND_RESPONSE_HEADER_EX,
            &HeaderInfo,
            NULL,
            NULL );

    if ( !Result )
        throw ServerException( HRESULT_FROM_WIN32( GetLastError() ) );

}

DWORD
ServerException::MapStatus( HRESULT Hr ) const
{
    switch( Hr )
        {
        case E_INVALIDARG:                                  return 400;
        // case HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ): return 400;
        case HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ):    return 404;
        case HRESULT_FROM_WIN32( ERROR_PATH_NOT_FOUND ):    return 404;
        case HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED ):     return 403;
        // case E_ACCESSDENIED:                                return 401;
        default:                                            return 500;
        }
}

void * _cdecl ::operator new( size_t Size )
{
    void *Memory = HeapAlloc( GetProcessHeap(), 0, Size );
    if ( !Memory )
        {
        Log( LOG_ERROR, "Out of memory!\n" ); 
        throw ServerException( ERROR_NOT_ENOUGH_MEMORY );        
        }
    return Memory;
}

void _cdecl ::operator delete( void *Memory )
{
    if ( !Memory )
        return;

    HeapFree( GetProcessHeap(), 0, Memory );
}


StringHandle 
GetMetaDataString(
    IMSAdminBase        *IISAdminBase,
    METADATA_HANDLE     Handle,
    LPCWSTR             Path,
    DWORD               dwIdentifier,
    LPCSTR              DefaultValue )
{
    HRESULT Hr;
    DWORD BufferRequired = 0;

    StringHandleW Data;

    METADATA_RECORD MdRecord;
    MdRecord.dwMDAttributes = METADATA_INHERIT;
    MdRecord.dwMDUserType   = ALL_METADATA;
    MdRecord.dwMDDataType   = STRING_METADATA;
    MdRecord.dwMDIdentifier = dwIdentifier;
    MdRecord.dwMDDataLen    = 0;
    MdRecord.pbMDData       = (PBYTE)NULL;
    MdRecord.dwMDDataTag    = 0;

    Hr =
        IISAdminBase->GetData(
            Handle,
            Path,
            &MdRecord,
            &BufferRequired );

    if ( SUCCEEDED( Hr ) )
        return (StringHandle)Data;

    if ( MD_ERROR_DATA_NOT_FOUND == Hr ||
        HRESULT_FROM_WIN32( ERROR_PATH_NOT_FOUND ) == Hr )
        return (StringHandle)DefaultValue;

    if ( Hr != HRESULT_FROM_WIN32( ERROR_INSUFFICIENT_BUFFER ) )
        throw ServerException( Hr );

    MdRecord.pbMDData        = (PBYTE)Data.AllocBuffer( BufferRequired / 2 );
    MdRecord.dwMDDataLen     = BufferRequired;

    Hr =
        IISAdminBase->GetData(
            Handle,
            Path,
            &MdRecord,
            &BufferRequired );

    if ( FAILED( Hr ) )
        throw ServerException( Hr );

    Data.SetStringSize();
    return (StringHandle)Data;

}

DWORD 
GetMetaDataDWORD(
    IMSAdminBase        *IISAdminBase,
    METADATA_HANDLE     Handle,
    LPCWSTR             Path,
    DWORD               dwIdentifier,
    DWORD               DefaultValue )
{

    DWORD BufferRequired;
    DWORD MetabaseValue;

    METADATA_RECORD MdRecord;
    memset( &MdRecord, 0, sizeof( MdRecord ) );

    MdRecord.dwMDAttributes = METADATA_INHERIT;
    MdRecord.dwMDUserType   = ALL_METADATA;
    MdRecord.dwMDDataType   = DWORD_METADATA;
    MdRecord.dwMDIdentifier = dwIdentifier;
    MdRecord.dwMDDataLen    = sizeof(MetabaseValue);
    MdRecord.pbMDData       = (PBYTE)&MetabaseValue;

    HRESULT Hr =
        IISAdminBase->GetData(
            Handle,
            Path,
            &MdRecord,
            &BufferRequired );
    
    if ( MD_ERROR_DATA_NOT_FOUND == Hr ||
         HRESULT_FROM_WIN32( ERROR_PATH_NOT_FOUND ) == Hr )
        return DefaultValue;

	if ( FAILED( Hr ) )
		throw ServerException( Hr );

    return MetabaseValue;
}

StringHandle
BITSUnicodeToStringHandle( const WCHAR *pStr )
{

    StringHandle RetValString;

    int RetVal =
        WideCharToMultiByte(
            CP_ACP,
            0,
            pStr,
            -1,
            NULL,
            0,
            NULL,
            NULL );

    if ( !RetVal )
        throw ServerException( HRESULT_FROM_WIN32( GetLastError() ) );

    char *pRetBuffer = RetValString.AllocBuffer( RetVal );

    RetVal =
        WideCharToMultiByte(
                CP_ACP,
                0,
                pStr,
                -1,
                pRetBuffer,
                RetVal,
                NULL,
                NULL );

    if ( !RetVal )
        throw ServerException( HRESULT_FROM_WIN32( GetLastError() ) );

    RetValString.SetStringSize();

    return RetValString;
}

StringHandle
BITSUrlCombine(
    const char *Base,
    const char *Relative,
    DWORD dwFlags )
{

    DWORD dwCombined = 0;

    HRESULT Hr =
        UrlCombine(
            Base,
            Relative,
            NULL,
            &dwCombined,
            dwFlags );

    if ( FAILED(Hr) && ( Hr != E_POINTER ) )
        throw ServerException( Hr );

    StringHandle RetVal;
    char *Buffer = RetVal.AllocBuffer( dwCombined + 1 );

    Hr = 
        UrlCombine(
            Base,
            Relative,
            Buffer,
            &dwCombined,
            dwFlags );

    if ( FAILED(Hr) )
        throw ServerException( Hr );

    RetVal.SetStringSize();

    return RetVal;

}

StringHandle
BITSUrlCanonicalize(
    const char *URL,
    DWORD dwFlags )
{

    return 
        BITSUrlCombine(
            "",
            URL,
            dwFlags );

}