/*++

Copyright (c) 1994  Microsoft Corporation

Module Name:

    inetapiw.cxx

Abstract:

    Contains the wide-character Internet APIs

    Contents:
        InternetCrackUrlW
        InternetCreateUrlW
        InternetCanonicalizeUrlW
        InternetCombineUrlW
        InternetOpenW
        InternetConnectW
        InternetOpenUrlW
        InternetReadFileExW
        InternetWriteFileExW
        InternetFindNextFileW
        InternetQueryOptionW
        InternetSetOptionW
        InternetGetLastResponseInfoW
        InternetSetStatusCallbackW

Author:

    Richard L Firth (rfirth) 02-Mar-1995

Environment:

    Win32(s) user-mode DLL

Revision History:

    02-Mar-1995 rfirth
        Created

--*/

#include <wininetp.h>

//  because wininet doesnt know about IStream
#define NO_SHLWAPI_STREAM
#include <shlwapi.h>
#include <shlwapip.h>

extern BOOL TransformFtpFindDataToW(LPWIN32_FIND_DATAA pfdA, LPWIN32_FIND_DATAW pfdW);
extern BOOL TransformGopherFindDataToW(LPGOPHER_FIND_DATAA pgfdA, LPGOPHER_FIND_DATAW pgfdW);

// -- FixStrings ------

//  Used in InternetCrackUrlW only.
//  Either
//  (a) If we have an ansi string, AND a unicode buffer, convert from ansi to unicode
//  (b) If we have an ansi string, but NO unicode buffer, determine where the ansi string
//         occurs in the unicode URL, and point the component there.

VOID
FixStrings(    
    LPSTR& pszA, 
    DWORD cbA, 
    LPWSTR& pszW, 
    DWORD& ccW, 
    LPSTR pszUrlA, 
    LPCWSTR pszUrlW)
{
    if (!pszA)
        return;

    if (pszW) 
    {
        ccW = MultiByteToWideChar(CP_ACP, 0, pszA, cbA+1, pszW, ccW) - 1; 
    } 
    else 
    { 
        pszW = (LPWSTR)(pszUrlW + MultiByteToWideChar(CP_ACP, 0, 
                pszUrlA, (int) (pszA-pszUrlA), NULL, 0)); 
        ccW = MultiByteToWideChar(CP_ACP, 0, pszA, cbA, NULL, 0); 
    } 
}

//
// functions
//


INTERNETAPI_(BOOL) InternetCrackUrlW(
    IN LPCWSTR pszUrlW,
    IN DWORD dwUrlLengthW,
    IN DWORD dwFlags,
    IN OUT LPURL_COMPONENTSW pUCW
    )

/*++

Routine Description:

    Cracks an URL into its constituent parts. Optionally escapes the url-path.
    We assume that the user has supplied large enough buffers for the various
    URL parts

Arguments:

    pszUrl         - pointer to URL to crack

    dwUrlLength     - 0 if pszUrl is ASCIIZ string, else length of pszUrl

    dwFlags         - flags controlling operation

    lpUrlComponents - pointer to URL_COMPONENTS

Return Value:

    BOOL
        Success - TRUE

        Failure - FALSE. Call GetLastError() for more info

--*/

{
    DEBUG_ENTER_API((DBG_API,
                     Bool,
                     "InternetCrackUrlW",
                     "%wq, %#x, %#x, %#x",
                     pszUrlW,
                     dwUrlLengthW,
                     dwFlags,
                     pUCW
                     ));

    INET_ASSERT(pszUrlW);
    INET_ASSERT(pUCW);

    DWORD dwErr = ERROR_SUCCESS;
    BOOL fResult = FALSE;
    BOOL fContinue;
    DWORD c;
    MEMORYPACKET mpUrlA, mpHostName, mpUserName, mpScheme, mpPassword, mpUrlPath, mpExtraInfo;
    URL_COMPONENTSA UCA;

    if (!(pszUrlW && pUCW))
    {
        dwErr = ERROR_INVALID_PARAMETER;
        goto cleanup;
    }

    UCA.dwStructSize = sizeof(URL_COMPONENTSA); 
    ALLOC_MB(pszUrlW, dwUrlLengthW, mpUrlA);
    if (!mpUrlA.psStr) {
            dwErr = ERROR_NOT_ENOUGH_MEMORY;
            goto cleanup;
    }
    UNICODE_TO_ANSI(pszUrlW, mpUrlA);

    for (c=0; c<=5; c++) {
        LPWSTR pszWorker;
        DWORD ccLen;
        MEMORYPACKET* pmpWorker;
        
        switch(c)
        {
        case 0:
            pszWorker = pUCW->lpszScheme;
            ccLen = pUCW->dwSchemeLength;
            pmpWorker = &mpScheme;
            break;

        case 1:
            pszWorker = pUCW->lpszHostName;
            ccLen = pUCW->dwHostNameLength;
            pmpWorker = &mpHostName;
            break;

        case 2:
            pszWorker = pUCW->lpszUserName;
            ccLen = pUCW->dwUserNameLength;
            pmpWorker = &mpUserName;
            break;

        case 3:
            pszWorker = pUCW->lpszPassword;
            ccLen = pUCW->dwPasswordLength;
            pmpWorker = &mpPassword;
            break;

        case 4:
            pszWorker = pUCW->lpszUrlPath;
            ccLen = pUCW->dwUrlPathLength;
            pmpWorker = &mpUrlPath;
            break;

        case 5:
            pszWorker = pUCW->lpszExtraInfo;
            ccLen = pUCW->dwExtraInfoLength;
            pmpWorker = &mpExtraInfo;
            break;
        }

        if (pszWorker) { 
            ALLOC_MB(pszWorker,ccLen,(*pmpWorker)); 
            if (!pmpWorker->psStr) {
                dwErr = ERROR_NOT_ENOUGH_MEMORY;
                goto cleanup;
            }
        } else { 
            pmpWorker->dwAlloc = ccLen; 
        }
    };

    REASSIGN_ALLOC(mpScheme,UCA.lpszScheme,UCA.dwSchemeLength);
    REASSIGN_ALLOC(mpHostName, UCA.lpszHostName,UCA.dwHostNameLength);
    REASSIGN_ALLOC(mpUserName, UCA.lpszUserName,UCA.dwUserNameLength);
    REASSIGN_ALLOC(mpPassword,UCA.lpszPassword,UCA.dwPasswordLength);
    REASSIGN_ALLOC(mpUrlPath,UCA.lpszUrlPath,UCA.dwUrlPathLength);
    REASSIGN_ALLOC(mpExtraInfo,UCA.lpszExtraInfo,UCA.dwExtraInfoLength);
                
    fResult = InternetCrackUrlA(mpUrlA.psStr, mpUrlA.dwSize, dwFlags, &UCA);
    if (fResult) {
        FixStrings(UCA.lpszScheme, UCA.dwSchemeLength, pUCW->lpszScheme, 
                    pUCW->dwSchemeLength, mpUrlA.psStr, pszUrlW);
        FixStrings(UCA.lpszHostName, UCA.dwHostNameLength, pUCW->lpszHostName, 
                    pUCW->dwHostNameLength, mpUrlA.psStr, pszUrlW);
        FixStrings(UCA.lpszUserName, UCA.dwUserNameLength, pUCW->lpszUserName, 
                    pUCW->dwUserNameLength, mpUrlA.psStr, pszUrlW);
        FixStrings(UCA.lpszPassword, UCA.dwPasswordLength, pUCW->lpszPassword, 
                    pUCW->dwPasswordLength, mpUrlA.psStr, pszUrlW);
        FixStrings(UCA.lpszUrlPath, UCA.dwUrlPathLength, pUCW->lpszUrlPath, 
                    pUCW->dwUrlPathLength, mpUrlA.psStr, pszUrlW);
        FixStrings(UCA.lpszExtraInfo, UCA.dwExtraInfoLength, pUCW->lpszExtraInfo, 
                    pUCW->dwExtraInfoLength, mpUrlA.psStr, pszUrlW);
        pUCW->nScheme = UCA.nScheme;
        pUCW->nPort = UCA.nPort;
        pUCW->dwStructSize = sizeof(URL_COMPONENTSW);
    }

cleanup:        
    if (dwErr!=ERROR_SUCCESS) { 
        SetLastError(dwErr); 
        DEBUG_ERROR(API, dwErr);
    }
    DEBUG_LEAVE_API(fResult);
    return fResult;
}


INTERNETAPI_(BOOL) InternetCreateUrlW(
    IN LPURL_COMPONENTSW pUCW,
    IN DWORD dwFlags,
    OUT LPWSTR pszUrlW,
    IN OUT LPDWORD pdwUrlLengthW
    )

/*++

Routine Description:

    Creates an URL from its constituent parts

Arguments:

Return Value:

    BOOL
        Success - URL written to pszUrl

        Failure - call GetLastError() for more info

--*/

{
    DEBUG_ENTER_API((DBG_API,
                     Bool,
                     "InternetCreateUrlW",
                     "%#x, %#x, %#x, %#x",
                     pUCW,
                     dwFlags,
                     pszUrlW,
                     pdwUrlLengthW
                     ));

    // INET_ASSERT(pszUrlW);
    INET_ASSERT(pUCW);

    DWORD dwErr = ERROR_SUCCESS;
    BOOL fResult = FALSE;
    MEMORYPACKET mpUrlA, mpHostName, mpUserName, mpScheme, mpPassword, mpUrlPath, mpExtraInfo;
    URL_COMPONENTSA UCA;

    if (!pdwUrlLengthW 
        || (pUCW==NULL)
        || IsBadWritePtr(pUCW, sizeof(*pUCW))
        || (pUCW->dwStructSize != sizeof(*pUCW))
        || (pszUrlW && IsBadWritePtr(pszUrlW, *pdwUrlLengthW*sizeof(WCHAR))))
    {
        dwErr = ERROR_INVALID_PARAMETER;
        goto cleanup;
    }
    if (pszUrlW)
    {
        ALLOC_MB(pszUrlW, *pdwUrlLengthW, mpUrlA);
        if (!mpUrlA.psStr)
        {
            dwErr = ERROR_NOT_ENOUGH_MEMORY;
            goto cleanup;
        }
    }
    mpUrlA.dwSize = mpUrlA.dwAlloc;
    UCA.dwStructSize = sizeof(URL_COMPONENTSA);

    UCA.nScheme = pUCW->nScheme;
    UCA.nPort = pUCW->nPort;
    if (pUCW->lpszScheme)
    {
        ALLOC_MB(pUCW->lpszScheme, pUCW->dwSchemeLength, mpScheme);
        if (!mpScheme.psStr)
        {
            dwErr = ERROR_NOT_ENOUGH_MEMORY;
            goto cleanup;
        }
        UNICODE_TO_ANSI(pUCW->lpszScheme, mpScheme);
    }
    REASSIGN_SIZE(mpScheme, UCA.lpszScheme, UCA.dwSchemeLength);
    if (pUCW->lpszHostName)
    {
        ALLOC_MB(pUCW->lpszHostName, pUCW->dwHostNameLength, mpHostName);
        if (!mpHostName.psStr)
        {
            dwErr = ERROR_NOT_ENOUGH_MEMORY;
            goto cleanup;
        }
        UNICODE_TO_ANSI(pUCW->lpszHostName, mpHostName);
    }
    REASSIGN_SIZE(mpHostName, UCA.lpszHostName, UCA.dwHostNameLength);
    if (pUCW->lpszUserName)
    {
        ALLOC_MB(pUCW->lpszUserName, pUCW->dwUserNameLength, mpUserName);
        if (!mpUserName.psStr)
        {
            dwErr = ERROR_NOT_ENOUGH_MEMORY;
            goto cleanup;
        }
        UNICODE_TO_ANSI(pUCW->lpszUserName, mpUserName);
    }
    REASSIGN_SIZE(mpUserName, UCA.lpszUserName, UCA.dwUserNameLength);
    if (pUCW->lpszPassword)
    {
        ALLOC_MB(pUCW->lpszPassword, pUCW->dwPasswordLength, mpPassword);
        if (!mpPassword.psStr)
        {
            dwErr = ERROR_NOT_ENOUGH_MEMORY;
            goto cleanup;
        }
        UNICODE_TO_ANSI(pUCW->lpszPassword, mpPassword);
    }
    REASSIGN_SIZE(mpPassword, UCA.lpszPassword, UCA.dwPasswordLength);
    if (pUCW->lpszUrlPath)
    {
        ALLOC_MB(pUCW->lpszUrlPath, pUCW->dwUrlPathLength, mpUrlPath); 
        if (!mpUrlPath.psStr)
        {
            dwErr = ERROR_NOT_ENOUGH_MEMORY;
            goto cleanup;
        }
        UNICODE_TO_ANSI(pUCW->lpszUrlPath, mpUrlPath);
    }
    REASSIGN_SIZE(mpUrlPath, UCA.lpszUrlPath, UCA.dwUrlPathLength);
    if (pUCW->lpszExtraInfo)
    {
        ALLOC_MB(pUCW->lpszExtraInfo, pUCW->dwExtraInfoLength, mpExtraInfo);
        if (!mpExtraInfo.psStr)
        {
            dwErr = ERROR_NOT_ENOUGH_MEMORY;
            goto cleanup;
        }
        UNICODE_TO_ANSI(pUCW->lpszExtraInfo, mpExtraInfo);
    }
    REASSIGN_SIZE(mpExtraInfo, UCA.lpszExtraInfo, UCA.dwExtraInfoLength);
    fResult = InternetCreateUrlA(&UCA, dwFlags, mpUrlA.psStr, &mpUrlA.dwSize);
    if (fResult)
    {
        MAYBE_COPY_ANSI(mpUrlA, pszUrlW, *pdwUrlLengthW);
    }
    else
    {
        *pdwUrlLengthW = mpUrlA.dwSize*sizeof(WCHAR);
    }

cleanup:        
    if (dwErr!=ERROR_SUCCESS) { 
        SetLastError(dwErr); 
        DEBUG_ERROR(API, dwErr);
    }

    DEBUG_LEAVE_API(fResult);
    return fResult;
}


// implemented in inetapia.cxx
DWORD ICUHrToWin32Error(HRESULT);


INTERNETAPI_(BOOL) InternetCanonicalizeUrlW(
    IN LPCWSTR pszUrl,
    OUT LPWSTR pszBuffer,
    IN OUT LPDWORD lpdwBufferLength,
    IN DWORD dwFlags
    )

/*++

Routine Description:

    Combines a relative URL with a base URL to form a new full URL.

Arguments:

    pszUrl             - pointer to URL to be canonicalize

    pszBuffer          - pointer to buffer where new URL is written

    lpdwBufferLength    - size of buffer on entry, length of new URL on exit

    dwFlags             - flags controlling operation

Return Value:

    BOOL                - TRUE if successful, FALSE if not

--*/

{
    DEBUG_ENTER_API((DBG_API,
                     Bool,
                     "InternetCanonicalizeUrlW",
                     "%wq, %#x, %#x [%d], %#x",
                     pszUrl,
                     pszBuffer,
                     lpdwBufferLength,
                     lpdwBufferLength ? *lpdwBufferLength : 0,
                     dwFlags
                     ));

    HRESULT hr ;
    BOOL bRet;

//    We don't need no stinkin asserts
//    INET_ASSERT(pszUrl);
//    INET_ASSERT(pszBuffer);
//    INET_ASSERT(lpdwBufferLength && (*lpdwBufferLength > 0));

    //
    //  the flags for the Url* APIs in shlwapi should be the same
    //  except that NO_ENCODE is on by default.  so we need to flip it
    //
    dwFlags ^= ICU_NO_ENCODE;

    // Check for invalid parameters

    if (!pszUrl || !pszBuffer || !lpdwBufferLength 
        || IsBadWritePtr(lpdwBufferLength, sizeof(lpdwBufferLength))
        || *lpdwBufferLength == 0
        || IsBadWritePtr(pszBuffer, *lpdwBufferLength*sizeof(WCHAR))
        || IsBadStringPtrW(pszUrl, INTERNET_MAX_URL_LENGTH))
    {
        hr = E_INVALIDARG;
    }
    else
    {
        hr = UrlCanonicalizeW(pszUrl, pszBuffer,
                    lpdwBufferLength, dwFlags | URL_WININET_COMPATIBILITY);
    }

    if(FAILED(hr))
    {
        DWORD dw = ICUHrToWin32Error(hr);

        bRet = FALSE;

        DEBUG_ERROR(INET, dw);

        SetLastError(dw);
    }
    else
        bRet = TRUE;

    DEBUG_LEAVE_API(bRet);
    return bRet;
}


INTERNETAPI_(BOOL) InternetCombineUrlW(
    IN LPCWSTR pszBaseUrl,
    IN LPCWSTR pszRelativeUrl,
    OUT LPWSTR pszBuffer,
    IN OUT LPDWORD lpdwBufferLength,
    IN DWORD dwFlags
    )

/*++

Routine Description:

    Combines a relative URL with a base URL to form a new full URL.

Arguments:

    pszBaseUrl         - pointer to base URL

    pszRelativeUrl     - pointer to relative URL

    pszBuffer          - pointer to buffer where new URL is written

    lpdwBufferLength    - size of buffer on entry, length of new URL on exit

    dwFlags             - flags controlling operation

Return Value:

    BOOL                - TRUE if successful, FALSE if not

--*/
{
    DEBUG_ENTER_API((DBG_API,
                     Bool,
                     "InternetCombineUrlW",
                     "%wq, %wq, %#x, %#x [%d], %#x",
                     pszBaseUrl,
                     pszRelativeUrl,
                     pszBuffer,
                     lpdwBufferLength,
                     lpdwBufferLength ? *lpdwBufferLength : 0,
                     dwFlags
                     ));

    HRESULT hr ;
    BOOL bRet;

//    We don't need no stinkin' asserts
//    INET_ASSERT(pszBaseUrl);
//    INET_ASSERT(pszRelativeUrl);
//    INET_ASSERT(lpdwBufferLength);

    //
    //  the flags for the Url* APIs in shlwapi should be the same
    //  except that NO_ENCODE is on by default.  so we need to flip it
    //
    dwFlags ^= ICU_NO_ENCODE;

    // Check for invalid parameters

    if (!pszBaseUrl || !pszRelativeUrl || !lpdwBufferLength 
        || IsBadWritePtr(lpdwBufferLength, sizeof(*lpdwBufferLength))
        || (pszBuffer && IsBadWritePtr(pszBuffer, *lpdwBufferLength*sizeof(WCHAR)))
        || IsBadStringPtrW(pszBaseUrl, INTERNET_MAX_URL_LENGTH)
        || IsBadStringPtrW(pszRelativeUrl, INTERNET_MAX_URL_LENGTH))
    {
        hr = E_INVALIDARG;
    }
    else
    {
        hr = UrlCombineW(pszBaseUrl, pszRelativeUrl, pszBuffer,
                    lpdwBufferLength, dwFlags | URL_WININET_COMPATIBILITY);
    }

    if(FAILED(hr))
    {
        DWORD dw = ICUHrToWin32Error(hr);

        bRet = FALSE;

        DEBUG_ERROR(INET, dw);

        SetLastError(dw);
    }
    else
        bRet = TRUE;

    DEBUG_LEAVE_API(bRet);
    return bRet;
}


INTERNETAPI_(HINTERNET) InternetOpenW(
    IN LPCWSTR pszAgentW,
    IN DWORD dwAccessType,
    IN LPCWSTR pszProxyW OPTIONAL,
    IN LPCWSTR pszProxyBypassW OPTIONAL,
    IN DWORD dwFlags
    )

/*++

Routine Description:

    description-of-function.

Arguments:

    pszAgent       -

    dwAccessType    -

    pszProxy       -

    pszProxyBypass -

    dwFlags         -

Return Value:

    HINTERNET

--*/

{
    DEBUG_ENTER_API((DBG_API,
                     Handle,
                     "InternetOpenW",
                     "%wq, %s (%d), %wq, %wq, %#x",
                     pszAgentW,
                     InternetMapOpenType(dwAccessType),
                     dwAccessType,
                     pszProxyW,
                     pszProxyBypassW,
                     dwFlags
                     ));

    DWORD dwErr = ERROR_SUCCESS;
    HINTERNET hInternet = NULL;
    MEMORYPACKET mpAgentA, mpProxyA, mpProxyBypassA;

    ALLOC_MB(pszAgentW,0,mpAgentA);
    if (!mpAgentA.psStr)
    {
            dwErr = ERROR_NOT_ENOUGH_MEMORY;
            goto cleanup;
    }
    UNICODE_TO_ANSI(pszAgentW,mpAgentA);
    if (pszProxyW)
    {
        ALLOC_MB(pszProxyW,0,mpProxyA);
        if (!mpProxyA.psStr)
        {
            dwErr = ERROR_NOT_ENOUGH_MEMORY;
            goto cleanup;
        }
        UNICODE_TO_ANSI(pszProxyW,mpProxyA);
    }
    if (pszProxyBypassW)
    {
        ALLOC_MB(pszProxyBypassW,0,mpProxyBypassA);
        if (!mpProxyBypassA.psStr)
        {
            dwErr = ERROR_NOT_ENOUGH_MEMORY;
            goto cleanup;
        }
        UNICODE_TO_ANSI(pszProxyBypassW,mpProxyBypassA);
    }

    hInternet = InternetOpenA(mpAgentA.psStr, dwAccessType, mpProxyA.psStr, 
                                        mpProxyBypassA.psStr, dwFlags);

                                        
cleanup:        
    if (dwErr!=ERROR_SUCCESS) { 
        SetLastError(dwErr); 
        DEBUG_ERROR(API, dwErr);
    }

    DEBUG_LEAVE_API(hInternet);
    return hInternet;
}


INTERNETAPI_(HINTERNET) InternetConnectW(
    IN HINTERNET hInternetSession,
    IN LPCWSTR pszServerNameW,
    IN INTERNET_PORT nServerPort,
    IN LPCWSTR pszUserNameW,
    IN LPCWSTR pszPasswordW,
    IN DWORD dwService,
    IN DWORD dwFlags,
    IN DWORD_PTR dwContext
    )

/*++

Routine Description:

    description-of-function.

Arguments:

    hInternetSession    -
    pszServerName      -
    nServerPort         -
    pszUserName        -
    pszPassword        -
    dwService           -
    dwFlags             -
    dwContext           -

Return Value:

    HINTERNET

--*/

{
    DEBUG_ENTER_API((DBG_API,
                     Handle,
                     "InternetConnectW",
                     "%#x, %wq, %d, %wq, %wq, %s (%d), %#08x, %#x",
                     hInternetSession,
                     pszServerNameW,
                     nServerPort,
                     pszUserNameW,
                     pszPasswordW,
                     InternetMapService(dwService),
                     dwService,
                     dwFlags,
                     dwContext
                     ));

    DWORD dwErr = ERROR_SUCCESS;
    MEMORYPACKET mpServerNameA, mpUserNameA, mpPasswordA;
    HINTERNET hInternet = NULL;

    if (!pszServerNameW)
    {
        dwErr = ERROR_INVALID_PARAMETER;
        goto cleanup;
    }
    ALLOC_MB(pszServerNameW, 0, mpServerNameA);
    if (!mpServerNameA.psStr)
    {
        dwErr = ERROR_NOT_ENOUGH_MEMORY;
        goto cleanup;
    }
    UNICODE_TO_ANSI(pszServerNameW, mpServerNameA);
    if (pszUserNameW)
    {
        ALLOC_MB(pszUserNameW, 0, mpUserNameA);
        if (!mpUserNameA.psStr)
        {
            dwErr = ERROR_NOT_ENOUGH_MEMORY;
            goto cleanup;
        }
        UNICODE_TO_ANSI(pszUserNameW, mpUserNameA);
    }
    if (pszPasswordW)
    {
        ALLOC_MB(pszPasswordW, 0, mpPasswordA);
        if (!mpPasswordA.psStr)
        {
            dwErr = ERROR_NOT_ENOUGH_MEMORY;
            goto cleanup;
        }
        UNICODE_TO_ANSI(pszPasswordW, mpPasswordA);
    }
    hInternet = InternetConnectA(hInternetSession, mpServerNameA.psStr, nServerPort,
                                mpUserNameA.psStr, mpPasswordA.psStr, dwService, dwFlags, dwContext);

cleanup:        
    if (dwErr!=ERROR_SUCCESS) { 
        SetLastError(dwErr); 
        DEBUG_ERROR(API, dwErr);
    }

    DEBUG_LEAVE_API(hInternet);
    return hInternet;
}


INTERNETAPI_(HINTERNET) InternetOpenUrlW(
    IN HINTERNET hInternetSession,
    IN LPCWSTR pszUrlW,
    IN LPCWSTR pszHeadersW,
    IN DWORD dwHeadersLengthW,
    IN DWORD dwFlags,
    IN DWORD_PTR dwContext
    )

/*++

Routine Description:

    description-of-function.

Arguments:

    hInternetSession    -
    pszUrl             -
    pszHeaders         -
    dwHeadersLength     -
    dwFlags             -
    dwContext           -

Return Value:

    HINTERNET

--*/

{
    DEBUG_ENTER_API((DBG_API,
                     Handle,
                     "InternetOpenUrlW",
                     "%#x, %wq, %.80wq, %d, %#08x, %#x",
                     hInternetSession,
                     pszUrlW,
                     pszHeadersW,
                     dwHeadersLengthW,
                     dwFlags,
                     dwContext
                     ));

    DWORD dwErr = ERROR_SUCCESS;
    MEMORYPACKET mpHeadersA, mpUrlA;
    HINTERNET hInternet = NULL;
    
    if (!pszUrlW || (*pszUrlW==L'\0'))
    {
        dwErr = ERROR_INVALID_PARAMETER;
        goto cleanup;
    }
    ALLOC_MB(pszUrlW, 0, mpUrlA);
    if (!mpUrlA.psStr)
    {
        dwErr = ERROR_NOT_ENOUGH_MEMORY;
        goto cleanup;
    }
    UNICODE_TO_ANSI(pszUrlW, mpUrlA);
    if (pszHeadersW)
    {
        ALLOC_MB(pszHeadersW, (dwHeadersLengthW==-1L? 0 : dwHeadersLengthW), mpHeadersA);
        if (!mpHeadersA.psStr)
        {
            dwErr = ERROR_NOT_ENOUGH_MEMORY;
            goto cleanup;
        }
        UNICODE_TO_ANSI(pszHeadersW, mpHeadersA);
    }
    hInternet = InternetOpenUrlA(hInternetSession, mpUrlA.psStr, mpHeadersA.psStr, 
                                    mpHeadersA.dwSize, dwFlags, dwContext);

cleanup:        
    if (dwErr!=ERROR_SUCCESS) { 
        SetLastError(dwErr); 
        DEBUG_ERROR(API, dwErr);
    }

    DEBUG_LEAVE_API(hInternet);
    return hInternet;
}


INTERNETAPI_(BOOL) InternetReadFileExW(
    IN HINTERNET hFile,
    OUT LPINTERNET_BUFFERSW lpBuffersOut,
    IN DWORD dwFlags,
    IN DWORD_PTR dwContext
    )
{
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    return FALSE;
}


INTERNETAPI_(BOOL) InternetWriteFileExW(
    IN HINTERNET hFile,
    IN LPINTERNET_BUFFERSW lpBuffersIn,
    IN DWORD dwFlags,
    IN DWORD_PTR dwContext
    )
{
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    return FALSE;
}


INTERNETAPI_(BOOL) InternetFindNextFileW(
    IN HINTERNET hFind,
    OUT LPVOID lpvFindData
    )

/*++

Routine Description:

    This function retrieves the next block of data from the server.
    Currently it supports the following protocol data :

    FtpFindNextFile
    GopherFindNext

Arguments:

    hFind       - handle that was obtained by a FindFirst call

    lpvFindData - pointer to buffer where the next block of data is copied

Return Value:

    TRUE if the function successfully returns next block of data.
    FALSE otherwise. GetLastError() will return the error code.

--*/

{
    DEBUG_ENTER_API((DBG_API,
                     Bool,
                     "InternetFindNextFileW",
                     "%#x, %#x",
                     hFind,
                     lpvFindData
                     ));

    DWORD dwErr = ERROR_SUCCESS;
    HINTERNET hFindMapped;
    HINTERNET_HANDLE_TYPE handleType;
    BOOL fResult = FALSE;
    
    dwErr = MapHandleToAddress(hFind, (LPVOID *)&hFindMapped, FALSE);
    if (dwErr!=ERROR_SUCCESS)
    {
        goto cleanup;
    }
    dwErr = RGetHandleType(hFindMapped, &handleType);
    DereferenceObject(hFindMapped);
    if (dwErr!=ERROR_SUCCESS)
    {
        goto cleanup;
    }
    if ((handleType != TypeFtpFindHandle) && (handleType != TypeGopherFindHandle))
    {
        dwErr = ERROR_INTERNET_INVALID_OPERATION;
        goto cleanup;
    }

    if (handleType == TypeFtpFindHandle)
    {
        WIN32_FIND_DATAA fdA;
        dwErr = ProbeWriteBuffer(lpvFindData, sizeof(WIN32_FIND_DATAW));
        if (dwErr!=ERROR_SUCCESS) 
        {
            goto cleanup;
        }
        if (InternetFindNextFileA(hFind,(LPVOID)&fdA))
            fResult = TransformFtpFindDataToW(&fdA,(LPWIN32_FIND_DATAW)lpvFindData);
    }
    else
    {
        GOPHER_FIND_DATAA gfdA;
        dwErr = ProbeWriteBuffer(lpvFindData, sizeof(GOPHER_FIND_DATAW));
        if (dwErr!=ERROR_SUCCESS) 
        {
            goto cleanup;
        }
        if (InternetFindNextFileA(hFind,(LPVOID)&gfdA))
            fResult = TransformGopherFindDataToW(&gfdA,(LPGOPHER_FIND_DATAW)lpvFindData);
    }

cleanup:        
    if (dwErr!=ERROR_SUCCESS) { 
        SetLastError(dwErr); 
        DEBUG_ERROR(API, dwErr);
    }
    DEBUG_LEAVE_API(fResult);
    return fResult;
}


INTERNETAPI_(BOOL) InternetGetLastResponseInfoW(
    OUT LPDWORD lpdwErrorCategory,
    IN LPWSTR pszBufferW,
    IN OUT LPDWORD lpdwBufferLengthW
    )

/*++

Routine Description:

    description-of-function.

Arguments:

    lpdwErrorCategory   -
    pszBuffer          -
    lpdwBufferLength    -

Return Value:

    BOOL

--*/

{
    DEBUG_ENTER_API((DBG_API,
                     Bool,
                     "InternetGetLastResponseInfoW",
                     "%#x, %ws, %#x [%d]",
                     lpdwErrorCategory,
                     pszBufferW,
                     lpdwBufferLengthW,
                     lpdwBufferLengthW ? *lpdwBufferLengthW : 0
                     ));

    DWORD dwErr = ERROR_SUCCESS;
    BOOL fResult = FALSE;
    MEMORYPACKET mpBufferA;

    if (pszBufferW)
    {
        ALLOC_MB(pszBufferW,*lpdwBufferLengthW,mpBufferA);
        if (!mpBufferA.psStr)
        {
            dwErr = ERROR_NOT_ENOUGH_MEMORY;
            goto cleanup;
        }
    }
        
    mpBufferA.dwSize = mpBufferA.dwAlloc;
    fResult = InternetGetLastResponseInfoA(lpdwErrorCategory, mpBufferA.psStr,
                                            &mpBufferA.dwSize);
    if (fResult) {
        MAYBE_COPY_ANSI(mpBufferA, pszBufferW, *lpdwBufferLengthW);
    } else {
        *lpdwBufferLengthW = mpBufferA.dwSize*sizeof(WCHAR);
    }

cleanup:    
    if (dwErr!=ERROR_SUCCESS) { 
        SetLastError(dwErr); 
        DEBUG_ERROR(API, dwErr);
    }

    DEBUG_LEAVE_API(fResult);
    return fResult;
}


INTERNETAPI_(BOOL) InternetCheckConnectionW(
    IN      LPCWSTR   pszUrlW,
    IN      DWORD   dwFlags,
    IN      DWORD   dwReserved
)

/*++

Routine Description:

    This routine tells the caller whether he can establish a connection to the
    network.


Arguments:

    pszUrl     this parameter is an indication to the API to attempt
                a specific host. The use of this parameter is based on the
                flags set in the dwFlags parameter

    dwFlags     a bitwise OR of the following flags

                    INTERNET_FLAG_ICC_FORCE_CONNECTION - force a connection if
                    cannot find one already established

                    INTERNET_FLAG_ICC_CONNECT_SPECIFIC_HOST - try the connection
                    to the specific host. If this flag is not set then the
                    host name is used if a quicker method is not avilable.

    dwReserved  reserved

Return Value:

    TRUE - Success
    FALSE - failure, GetLastError returns the error code

--*/
{
    DEBUG_ENTER_API((DBG_API,
                     Bool,
                     "InternetCheckConnectionW",
                     "%ws %x",
                     pszUrlW,
                     dwFlags
                     ));

    DWORD dwErr = ERROR_SUCCESS;
    MEMORYPACKET mpUrlA;
    BOOL fResult = FALSE;
    
    if (pszUrlW)
    {
        ALLOC_MB(pszUrlW,0,mpUrlA);
        if (!mpUrlA.psStr)
        {
            dwErr = ERROR_NOT_ENOUGH_MEMORY;
            goto cleanup;
        }
        UNICODE_TO_ANSI(pszUrlW,mpUrlA);
    }
    fResult = InternetCheckConnectionA(mpUrlA.psStr, dwFlags, dwReserved);

cleanup:        
    if (dwErr!=ERROR_SUCCESS) { 
        SetLastError(dwErr); 
        DEBUG_ERROR(API, dwErr);
    }

    DEBUG_LEAVE_API(fResult);
    return fResult;
}

INTERNETAPI_(INTERNET_STATUS_CALLBACK) InternetSetStatusCallbackCore(
    IN HINTERNET hInternet,
    IN INTERNET_STATUS_CALLBACK lpfnInternetCallback,
    IN BOOL fType
    );

INTERNETAPI_(INTERNET_STATUS_CALLBACK) InternetSetStatusCallbackW(
    IN HINTERNET hInternet,
    IN INTERNET_STATUS_CALLBACK lpfnInternetCallback
    )

/*++

Routine Description:

    Sets the status callback function for the DLL or the handle object

Arguments:

    hInternet               - handle of the object for which we wish to set the
                              status callback

    lpfnInternetCallback    - pointer to caller-supplied status function

Return Value:

    FARPROC
        Success - previous status callback function address

        Failure - INTERNET_INVALID_STATUS_CALLBACK. Call GetLastErrorInfo() for
                  more information:

                    ERROR_INVALID_PARAMETER
                        The callback function is invalid

                    ERROR_INTERNET_INCORRECT_HANDLE_TYPE
                        Cannot set the callback on the supplied handle (probably
                        a NULL handle - per-process callbacks no longer
                        supported)

--*/

{
    DEBUG_ENTER((DBG_INET,
                 Pointer,
                 "InternetSetStatusCallbackW",
                 "%#x, %#x",
                 hInternet,
                 lpfnInternetCallback
                 ));

    INTERNET_STATUS_CALLBACK pfn = InternetSetStatusCallbackCore(hInternet,lpfnInternetCallback,TRUE);

    DEBUG_LEAVE(pfn);
    return pfn;
}

#ifdef IGCURLW
INTERNETAPI_(BOOL) InternetGetCertByURLW(
    IN LPWSTR     lpszURL,
    IN OUT LPWSTR lpszCertText,
    OUT DWORD    dwcbCertText
    )
{
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    return FALSE;
    return InternetGetCertByURLA(lpszURL,lpszCertText,dwcbCertText);
}

#endif

INTERNETAPI_(BOOL) InternetShowSecurityInfoByURLW(
    IN LPWSTR    pszUrlW,
    IN HWND      hwndRootWindow
    )
/*++

Routine Description:

    Does a high-level lookup against the Certificate Cache.
    Searches by URL (broken down into hostname) for the Certificate,
    and returns it in a formatted (&localized) string.

Arguments:

    lpszUrl         - pointer to URL to crack

    lpszCertText    - Output of formatted certifcate

    dwcbCertText    - Size of lpszCertText

Return Value:

    BOOL
        Success - TRUE

        Failure - FALSE. Call GetLastError() for more info

--*/

{
    DEBUG_ENTER_API((DBG_INET,
                 Bool,
                 "InternetShowSecurityInfoW",
                 "%wq, %#x",
                 pszUrlW,
                 hwndRootWindow
                 ));

    BOOL fResult = FALSE;
    DWORD dwErr = ERROR_SUCCESS;
    MEMORYPACKET mpUrlA;
    
    if (pszUrlW)
    {
        ALLOC_MB(pszUrlW,0,mpUrlA);
        if (!mpUrlA.psStr)
        {
            dwErr = ERROR_NOT_ENOUGH_MEMORY;
            goto cleanup;
        }
        UNICODE_TO_ANSI(pszUrlW,mpUrlA);
    }
    fResult = InternetShowSecurityInfoByURLA(mpUrlA.psStr, hwndRootWindow);

cleanup:        
    if (dwErr!=ERROR_SUCCESS) { 
        SetLastError(dwErr); 
        DEBUG_ERROR(API, dwErr);
    }
    DEBUG_LEAVE_API(fResult);
    return fResult;
}

INTERNETAPI_(BOOL) InternetAlgIdToStringW(
    IN ALG_ID       ai,
    IN LPWSTR       lpstr,
    IN OUT LPDWORD  lpdwstrLength,
    IN DWORD        dwReserved /* Must be 0 */
    )
/*++

Routine Description:

    Converts a algid to a user-displayable string.

Arguments:
    
    ai - Algorithm identifiers ( defined in wincrypt.h)

    lpstr - Buffer to copy string into.

    lpdwstrLength - pass in num of characters, return no of characters copied if successful,
                       else no of chars required (including null terminator)
    
    dwReserved = Must be 0

Return Value:
    DWORD
        Win32 or WININET error code.
--*/
{
    DEBUG_ENTER_API((DBG_API,
                     Bool,
                     "InternetAlgIdToStringW",
                     "%#x, %wq, %#x, %#x",
                     ai,
                     lpstr,
                     lpdwstrLength,
                     dwReserved
                     ));

    DWORD dwErr = ERROR_SUCCESS;
    MEMORYPACKET mpBuffer;
    BOOL fResult = FALSE;

    if (dwReserved!=0)
    {
        dwErr = ERROR_INVALID_PARAMETER;
        goto cleanup;
    }
    
    if (lpstr)
    {
        mpBuffer.dwAlloc = mpBuffer.dwSize = *lpdwstrLength;
        mpBuffer.psStr = (LPSTR)ALLOC_BYTES(mpBuffer.dwAlloc*sizeof(CHAR));

        if (!mpBuffer.psStr)
        {
            dwErr = ERROR_NOT_ENOUGH_MEMORY;
            goto cleanup;
        }
    }

    if (InternetAlgIdToStringA(ai, (LPSTR)mpBuffer.psStr, &mpBuffer.dwSize, dwReserved))
    {
        *lpdwstrLength = MultiByteToWideChar(CP_ACP, 0, mpBuffer.psStr, mpBuffer.dwSize + 1,
                      NULL, 0);
        if (*lpdwstrLength*sizeof(WCHAR) <= mpBuffer.dwAlloc && lpstr)
        {
            MultiByteToWideChar(CP_ACP, 0, mpBuffer.psStr, mpBuffer.dwSize + 1,
                    lpstr, *lpdwstrLength);
            (*lpdwstrLength)--;
            fResult = TRUE;
        }
        else
        {
            dwErr = ERROR_INSUFFICIENT_BUFFER;
        }
    }
    else 
    {
        dwErr = GetLastError();

        if (dwErr == ERROR_INSUFFICIENT_BUFFER)
        {
            *lpdwstrLength = mpBuffer.dwSize * sizeof(WCHAR);
        }
    }

cleanup:        
    if (dwErr!=ERROR_SUCCESS) { 
        SetLastError(dwErr); 
        DEBUG_ERROR(API, dwErr);
    }
    DEBUG_LEAVE_API(fResult);
    return fResult;
}


INTERNETAPI_(BOOL) InternetSecurityProtocolToStringW(
    IN DWORD        dwProtocol,
    IN LPWSTR       lpstr,
    IN OUT LPDWORD  lpdwstrLength,
    IN DWORD        dwReserved /* Must be 0 */
    )
/*++

Routine Description:

    Converts a security protocol to a user-displayable string.

Arguments:
    
    dwProtocol - Security protocol identifier ( defined in wincrypt.h)

    lpstr - Buffer to copy string into.

    lpdwstrLength - pass in num of characters, return no of characters copied if successful,
                       else no of chars required (including null terminator)
    
    dwReserved = Must be 0

Return Value:
    DWORD
        Win32 or WININET error code.
--*/
{
    DEBUG_ENTER_API((DBG_API,
                     Bool,
                     "InternetSecurityProtocolToStringW",
                     "%d, %wq, %#x, %#x",
                     dwProtocol,
                     lpstr,
                     lpdwstrLength,
                     dwReserved
                     ));

    DWORD dwErr = ERROR_SUCCESS;
    MEMORYPACKET mpBuffer;
    BOOL fResult = FALSE;

    if (dwReserved!=0)
    {
        dwErr = ERROR_INVALID_PARAMETER;
        goto cleanup;
    }
    
    if (lpstr)
    {
        mpBuffer.dwAlloc = mpBuffer.dwSize = *lpdwstrLength;
        mpBuffer.psStr = (LPSTR)ALLOC_BYTES(mpBuffer.dwAlloc*sizeof(CHAR));
        if (!mpBuffer.psStr)
        {
            dwErr = ERROR_NOT_ENOUGH_MEMORY;
            goto cleanup;
        }
    }

    if (InternetSecurityProtocolToStringA(dwProtocol, (LPSTR)mpBuffer.psStr, &mpBuffer.dwSize, dwReserved))
    {
        *lpdwstrLength = MultiByteToWideChar(CP_ACP, 0, mpBuffer.psStr, mpBuffer.dwSize + 1,
                      NULL, 0);
        if (*lpdwstrLength*sizeof(WCHAR) <= mpBuffer.dwAlloc && lpstr)
        {
            MultiByteToWideChar(CP_ACP, 0, mpBuffer.psStr, mpBuffer.dwSize + 1,
                    lpstr, *lpdwstrLength);
            (*lpdwstrLength)--;
            fResult = TRUE;
        }
        else
        {
            dwErr = ERROR_INSUFFICIENT_BUFFER;
        }
    }
    else 
    {
        dwErr = GetLastError();

        if (dwErr == ERROR_INSUFFICIENT_BUFFER)
        {
            *lpdwstrLength = mpBuffer.dwSize * sizeof(WCHAR);
        }
    }

cleanup:        
    if (dwErr!=ERROR_SUCCESS) { 
        SetLastError(dwErr); 
        DEBUG_ERROR(API, dwErr);
    }
    DEBUG_LEAVE_API(fResult);
    return fResult;
}