/* Copyright (C) 2004 Juan Lang
 *
 * This file implements thunks between wide char and multibyte functions for
 * SSPs that only provide one or the other.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "winnls.h"
#include "winreg.h"
#include "winternl.h"
#include "sspi.h"
#include "secur32_priv.h"
#include "thunks.h"

#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(secur32);

SECURITY_STATUS SEC_ENTRY thunk_AcquireCredentialsHandleA(
 SEC_CHAR *pszPrincipal, SEC_CHAR *pszPackage, ULONG fCredentialsUse,
 PLUID pvLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn,
 PVOID pvGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry)
{
    SECURITY_STATUS ret;

    TRACE("%s %s %ld %p %p %p %p %p %p\n", debugstr_a(pszPrincipal),
     debugstr_a(pszPackage), fCredentialsUse, pvLogonID, pAuthData, pGetKeyFn,
     pvGetKeyArgument, phCredential, ptsExpiry);
    if (pszPackage)
    {
        UNICODE_STRING principal, package;

        RtlCreateUnicodeStringFromAsciiz(&principal, pszPrincipal);
        RtlCreateUnicodeStringFromAsciiz(&package, pszPackage);
        ret = AcquireCredentialsHandleW(principal.Buffer, package.Buffer,
         fCredentialsUse, pvLogonID, pAuthData, pGetKeyFn, pvGetKeyArgument,
         phCredential, ptsExpiry);
        RtlFreeUnicodeString(&principal);
        RtlFreeUnicodeString(&package);
    }
    else
        ret = SEC_E_SECPKG_NOT_FOUND;
    return ret;
}

SECURITY_STATUS SEC_ENTRY thunk_AcquireCredentialsHandleW(
 SEC_WCHAR *pszPrincipal, SEC_WCHAR *pszPackage, ULONG fCredentialsUse,
 PLUID pvLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn,
 PVOID pvGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry)
{
    SECURITY_STATUS ret;

    TRACE("%s %s %ld %p %p %p %p %p %p\n", debugstr_w(pszPrincipal),
     debugstr_w(pszPackage), fCredentialsUse, pvLogonID, pAuthData, pGetKeyFn,
     pvGetKeyArgument, phCredential, ptsExpiry);
    if (pszPackage)
    {
        PSTR principal, package;

        principal = SECUR32_AllocMultiByteFromWide(pszPrincipal);
        package = SECUR32_AllocMultiByteFromWide(pszPackage);
        ret = AcquireCredentialsHandleA(principal, package, fCredentialsUse,
         pvLogonID, pAuthData, pGetKeyFn, pvGetKeyArgument, phCredential,
         ptsExpiry);
        if (principal)
            SECUR32_FREE(principal);
        if (package)
            SECUR32_FREE(package);
    }
    else
        ret = SEC_E_SECPKG_NOT_FOUND;
    return ret;
}

/* thunking is pretty dicey for these--the output type depends on ulAttribute,
 * so we have to know about every type the caller does
 */
SECURITY_STATUS SEC_ENTRY thunk_QueryCredentialsAttributesA(
 PCredHandle phCredential, unsigned long ulAttribute, void *pBuffer)
{
    SECURITY_STATUS ret;

    TRACE("%p %ld %p\n", phCredential, ulAttribute, pBuffer);
    if (phCredential)
    {
        SecurePackage *package = (SecurePackage *)phCredential->dwUpper;
        PCredHandle cred = (PCredHandle)phCredential->dwLower;

        if (package && package->provider)
        {
            if (package->provider->fnTableW.QueryCredentialsAttributesW)
            {
                ret = package->provider->fnTableW.QueryCredentialsAttributesW(
                 cred, ulAttribute, pBuffer);
                if (ret == SEC_E_OK)
                {
                    switch (ulAttribute)
                    {
                        case SECPKG_CRED_ATTR_NAMES:
                        {
                            PSecPkgCredentials_NamesW names =
                             (PSecPkgCredentials_NamesW)pBuffer;
                            SEC_WCHAR *oldUser = names->sUserName;

                            if (oldUser)
                            {
                                names->sUserName =
                                 (PWSTR)SECUR32_AllocMultiByteFromWide(oldUser);
                                package->provider->fnTableW.FreeContextBuffer(
                                 oldUser);
                            }
                            break;
                        }
                        default:
                            WARN("attribute type %ld unknown\n", ulAttribute);
                            ret = SEC_E_INTERNAL_ERROR;
                    }
                }
            }
            else
                ret = SEC_E_UNSUPPORTED_FUNCTION;
        }
        else
            ret = SEC_E_INVALID_HANDLE;
    }
    else
        ret = SEC_E_INVALID_HANDLE;
    return ret;
}

SECURITY_STATUS SEC_ENTRY thunk_QueryCredentialsAttributesW(
 PCredHandle phCredential, unsigned long ulAttribute, void *pBuffer)
{
    SECURITY_STATUS ret;

    TRACE("%p %ld %p\n", phCredential, ulAttribute, pBuffer);
    if (phCredential)
    {
        SecurePackage *package = (SecurePackage *)phCredential->dwUpper;
        PCredHandle cred = (PCredHandle)phCredential->dwLower;

        if (package && package->provider)
        {
            if (package->provider->fnTableA.QueryCredentialsAttributesA)
            {
                ret = package->provider->fnTableA.QueryCredentialsAttributesA(
                 cred, ulAttribute, pBuffer);
                if (ret == SEC_E_OK)
                {
                    switch (ulAttribute)
                    {
                        case SECPKG_CRED_ATTR_NAMES:
                        {
                            PSecPkgCredentials_NamesA names =
                             (PSecPkgCredentials_NamesA)pBuffer;
                            SEC_CHAR *oldUser = names->sUserName;

                            if (oldUser)
                            {
                                names->sUserName =
                                 (PSTR)SECUR32_AllocWideFromMultiByte(oldUser);
                                package->provider->fnTableA.FreeContextBuffer(
                                 oldUser);
                            }
                            break;
                        }
                        default:
                            WARN("attribute type %ld unknown\n", ulAttribute);
                            ret = SEC_E_INTERNAL_ERROR;
                    }
                }
            }
            else
                ret = SEC_E_UNSUPPORTED_FUNCTION;
        }
        else
            ret = SEC_E_INVALID_HANDLE;
    }
    else
        ret = SEC_E_INVALID_HANDLE;
    return ret;
}

SECURITY_STATUS SEC_ENTRY thunk_InitializeSecurityContextA(
 PCredHandle phCredential, PCtxtHandle phContext,
 SEC_CHAR *pszTargetName, unsigned long fContextReq,
 unsigned long Reserved1, unsigned long TargetDataRep, PSecBufferDesc pInput,
 unsigned long Reserved2, PCtxtHandle phNewContext, PSecBufferDesc pOutput,
 unsigned long *pfContextAttr, PTimeStamp ptsExpiry)
{
    SECURITY_STATUS ret;

    TRACE("%p %p %s %ld %ld %ld %p %ld %p %p %p %p\n", phCredential, phContext,
     debugstr_a(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput,
     Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry);
    if (phCredential)
    {
        SecurePackage *package = (SecurePackage *)phCredential->dwUpper;

        if (package && package->provider)
        {
            if (package->provider->fnTableW.InitializeSecurityContextW)
            {
                UNICODE_STRING target;

                RtlCreateUnicodeStringFromAsciiz(&target, pszTargetName);
                ret = package->provider->fnTableW.InitializeSecurityContextW(
                 phCredential, phContext, target.Buffer, fContextReq, Reserved1,
                 TargetDataRep, pInput, Reserved2, phNewContext, pOutput,
                 pfContextAttr, ptsExpiry);
                RtlFreeUnicodeString(&target);
            }
            else
                ret = SEC_E_UNSUPPORTED_FUNCTION;
        }
        else
            ret = SEC_E_INVALID_HANDLE;
    }
    else
        ret = SEC_E_INVALID_HANDLE;
    return ret;
}

SECURITY_STATUS SEC_ENTRY thunk_InitializeSecurityContextW(
 PCredHandle phCredential, PCtxtHandle phContext,
 SEC_WCHAR *pszTargetName, unsigned long fContextReq,
 unsigned long Reserved1, unsigned long TargetDataRep, PSecBufferDesc pInput,
 unsigned long Reserved2, PCtxtHandle phNewContext, PSecBufferDesc pOutput,
 unsigned long *pfContextAttr, PTimeStamp ptsExpiry)
{
    SECURITY_STATUS ret;

    TRACE("%p %p %s %ld %ld %ld %p %ld %p %p %p %p\n", phCredential, phContext,
     debugstr_w(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput,
     Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry);
    if (phCredential)
    {
        SecurePackage *package = (SecurePackage *)phCredential->dwUpper;

        if (package && package->provider)
        {
            if (package->provider->fnTableA.InitializeSecurityContextA)
            {
                PSTR target = SECUR32_AllocMultiByteFromWide(pszTargetName);

                ret = package->provider->fnTableA.InitializeSecurityContextA(
                 phCredential, phContext, target, fContextReq, Reserved1,
                 TargetDataRep, pInput, Reserved2, phNewContext, pOutput,
                 pfContextAttr, ptsExpiry);
                if (target)
                    SECUR32_FREE(target);
            }
            else
                ret = SEC_E_UNSUPPORTED_FUNCTION;
        }
        else
            ret = SEC_E_INVALID_HANDLE;
    }
    else
        ret = SEC_E_INVALID_HANDLE;
    return ret;
}

SECURITY_STATUS SEC_ENTRY thunk_AddCredentialsA(PCredHandle hCredentials,
 SEC_CHAR *pszPrincipal, SEC_CHAR *pszPackage, unsigned long fCredentialUse,
 void *pAuthData, SEC_GET_KEY_FN pGetKeyFn, void *pvGetKeyArgument,
 PTimeStamp ptsExpiry)
{
    SECURITY_STATUS ret;

    TRACE("%p %s %s %ld %p %p %p %p\n", hCredentials, debugstr_a(pszPrincipal),
     debugstr_a(pszPackage), fCredentialUse, pAuthData, pGetKeyFn,
     pvGetKeyArgument, ptsExpiry);
    if (hCredentials)
    {
        SecurePackage *package = (SecurePackage *)hCredentials->dwUpper;
        PCredHandle cred = (PCtxtHandle)hCredentials->dwLower;

        if (package && package->provider)
        {
            if (package->provider->fnTableW.AddCredentialsW)
            {
                UNICODE_STRING szPrincipal, szPackage;

                RtlCreateUnicodeStringFromAsciiz(&szPrincipal, pszPrincipal);
                RtlCreateUnicodeStringFromAsciiz(&szPackage, pszPackage);
                ret = package->provider->fnTableW.AddCredentialsW(
                 cred, szPrincipal.Buffer, szPackage.Buffer, fCredentialUse,
                 pAuthData, pGetKeyFn, pvGetKeyArgument, ptsExpiry);
                RtlFreeUnicodeString(&szPrincipal);
                RtlFreeUnicodeString(&szPackage);
            }
            else
                ret = SEC_E_UNSUPPORTED_FUNCTION;
        }
        else
            ret = SEC_E_INVALID_HANDLE;
    }
    else
        ret = SEC_E_INVALID_HANDLE;
    return ret;
}

SECURITY_STATUS SEC_ENTRY thunk_AddCredentialsW(PCredHandle hCredentials,
 SEC_WCHAR *pszPrincipal, SEC_WCHAR *pszPackage, unsigned long fCredentialUse,
 void *pAuthData, SEC_GET_KEY_FN pGetKeyFn, void *pvGetKeyArgument,
 PTimeStamp ptsExpiry)
{
    SECURITY_STATUS ret;

    TRACE("%p %s %s %ld %p %p %p %p\n", hCredentials, debugstr_w(pszPrincipal),
     debugstr_w(pszPackage), fCredentialUse, pAuthData, pGetKeyFn,
     pvGetKeyArgument, ptsExpiry);
    if (hCredentials)
    {
        SecurePackage *package = (SecurePackage *)hCredentials->dwUpper;
        PCredHandle cred = (PCtxtHandle)hCredentials->dwLower;

        if (package && package->provider)
        {
            if (package->provider->fnTableA.AddCredentialsA)
            {
                PSTR szPrincipal = SECUR32_AllocMultiByteFromWide(pszPrincipal);
                PSTR szPackage = SECUR32_AllocMultiByteFromWide(pszPackage);

                ret = package->provider->fnTableA.AddCredentialsA(
                 cred, szPrincipal, szPackage, fCredentialUse, pAuthData,
                 pGetKeyFn, pvGetKeyArgument, ptsExpiry);
                SECUR32_FREE(szPrincipal);
                SECUR32_FREE(szPackage);
            }
            else
                ret = SEC_E_UNSUPPORTED_FUNCTION;
        }
        else
            ret = SEC_E_INVALID_HANDLE;
    }
    else
        ret = SEC_E_INVALID_HANDLE;
    return ret;
}

static PSecPkgInfoA _copyPackageInfoFlatWToA(PSecPkgInfoW infoW)
{
    PSecPkgInfoA ret;

    if (infoW)
    {
        size_t bytesNeeded = sizeof(SecPkgInfoA);
        int nameLen = 0, commentLen = 0;

        if (infoW->Name)
        {
            nameLen = WideCharToMultiByte(CP_ACP, 0, infoW->Name, -1,
             NULL, 0, NULL, NULL);
            bytesNeeded += nameLen;
        }
        if (infoW->Comment)
        {
            commentLen = WideCharToMultiByte(CP_ACP, 0, infoW->Comment, -1,
             NULL, 0, NULL, NULL);
            bytesNeeded += commentLen;
        }
        ret = (PSecPkgInfoA)SECUR32_ALLOC(bytesNeeded);
        if (ret)
        {
            PSTR nextString = (PSTR)((PBYTE)ret + sizeof(SecPkgInfoA));

            memcpy(ret, infoW, sizeof(SecPkgInfoA));
            if (infoW->Name)
            {
                ret->Name = nextString;
                WideCharToMultiByte(CP_ACP, 0, infoW->Name, -1, nextString,
                 nameLen, NULL, NULL);
                nextString += nameLen;
            }
            else
                ret->Name = NULL;
            if (infoW->Comment)
            {
                ret->Comment = nextString;
                WideCharToMultiByte(CP_ACP, 0, infoW->Comment, -1, nextString,
                 nameLen, NULL, NULL);
            }
            else
                ret->Comment = NULL;
        }
    }
    else
        ret = NULL;
    return ret;
}

static SECURITY_STATUS thunk_ContextAttributesWToA(SecurePackage *package,
 unsigned long ulAttribute, void *pBuffer)
{
    SECURITY_STATUS ret = SEC_E_OK;

    if (package && pBuffer)
    {
        switch (ulAttribute)
        {
            case SECPKG_ATTR_NAMES:
            {
                PSecPkgContext_NamesW names = (PSecPkgContext_NamesW)pBuffer;
                SEC_WCHAR *oldUser = names->sUserName;

                if (oldUser)
                {
                    names->sUserName =
                     (PWSTR)SECUR32_AllocMultiByteFromWide(oldUser);
                    package->provider->fnTableW.FreeContextBuffer(oldUser);
                }
                break;
            }
            case SECPKG_ATTR_AUTHORITY:
            {
                PSecPkgContext_AuthorityW names =
                 (PSecPkgContext_AuthorityW)pBuffer;
                SEC_WCHAR *oldAuth = names->sAuthorityName;

                if (oldAuth)
                {
                    names->sAuthorityName =
                     (PWSTR)SECUR32_AllocMultiByteFromWide(oldAuth);
                    package->provider->fnTableW.FreeContextBuffer(oldAuth);
                }
                break;
            }
            case SECPKG_ATTR_KEY_INFO:
            {
                PSecPkgContext_KeyInfoW info = (PSecPkgContext_KeyInfoW)pBuffer;
                SEC_WCHAR *oldSigAlgName = info->sSignatureAlgorithmName;
                SEC_WCHAR *oldEncAlgName = info->sEncryptAlgorithmName;

                if (oldSigAlgName)
                {
                    info->sSignatureAlgorithmName =
                     (PWSTR)SECUR32_AllocMultiByteFromWide(oldSigAlgName);
                    package->provider->fnTableW.FreeContextBuffer(
                     oldSigAlgName);
                }
                if (oldEncAlgName)
                {
                    info->sEncryptAlgorithmName =
                     (PWSTR)SECUR32_AllocMultiByteFromWide(oldEncAlgName);
                    package->provider->fnTableW.FreeContextBuffer(
                     oldEncAlgName);
                }
                break;
            }
            case SECPKG_ATTR_PACKAGE_INFO:
            {
                PSecPkgContext_PackageInfoW info =
                 (PSecPkgContext_PackageInfoW)pBuffer;
                PSecPkgInfoW oldPkgInfo = info->PackageInfo;

                if (oldPkgInfo)
                {
                    info->PackageInfo = (PSecPkgInfoW)
                     _copyPackageInfoFlatWToA(oldPkgInfo);
                    package->provider->fnTableW.FreeContextBuffer(oldPkgInfo);
                }
                break;
            }
            case SECPKG_ATTR_NEGOTIATION_INFO:
            {
                PSecPkgContext_NegotiationInfoW info =
                 (PSecPkgContext_NegotiationInfoW)pBuffer;
                PSecPkgInfoW oldPkgInfo = info->PackageInfo;

                if (oldPkgInfo)
                {
                    info->PackageInfo = (PSecPkgInfoW)
                     _copyPackageInfoFlatWToA(oldPkgInfo);
                    package->provider->fnTableW.FreeContextBuffer(oldPkgInfo);
                }
                break;
            }
            case SECPKG_ATTR_NATIVE_NAMES:
            {
                PSecPkgContext_NativeNamesW names =
                 (PSecPkgContext_NativeNamesW)pBuffer;
                PWSTR oldClient = names->sClientName;
                PWSTR oldServer = names->sServerName;

                if (oldClient)
                {
                    names->sClientName = (PWSTR)SECUR32_AllocMultiByteFromWide(
                     oldClient);
                    package->provider->fnTableW.FreeContextBuffer(oldClient);
                }
                if (oldServer)
                {
                    names->sServerName = (PWSTR)SECUR32_AllocMultiByteFromWide(
                     oldServer);
                    package->provider->fnTableW.FreeContextBuffer(oldServer);
                }
                break;
            }
            case SECPKG_ATTR_CREDENTIAL_NAME:
            {
                PSecPkgContext_CredentialNameW name =
                 (PSecPkgContext_CredentialNameW)pBuffer;
                PWSTR oldCred = name->sCredentialName;

                if (oldCred)
                {
                    name->sCredentialName =
                     (PWSTR)SECUR32_AllocMultiByteFromWide(oldCred);
                    package->provider->fnTableW.FreeContextBuffer(oldCred);
                }
                break;
            }
            /* no thunking needed: */
            case SECPKG_ATTR_ACCESS_TOKEN:
            case SECPKG_ATTR_DCE_INFO:
            case SECPKG_ATTR_FLAGS:
            case SECPKG_ATTR_LIFESPAN:
            case SECPKG_ATTR_PASSWORD_EXPIRY:
            case SECPKG_ATTR_SESSION_KEY:
            case SECPKG_ATTR_SIZES:
            case SECPKG_ATTR_STREAM_SIZES:
            case SECPKG_ATTR_TARGET_INFORMATION:
                break;
            default:
                WARN("attribute type %ld unknown\n", ulAttribute);
                ret = SEC_E_INTERNAL_ERROR;
        }
    }
    else
        ret = SEC_E_INVALID_TOKEN;
    return ret;
}

SECURITY_STATUS SEC_ENTRY thunk_QueryContextAttributesA(PCtxtHandle phContext,
 unsigned long ulAttribute, void *pBuffer)
{
    SECURITY_STATUS ret;

    TRACE("%p %ld %p\n", phContext, ulAttribute, pBuffer);
    if (phContext)
    {
        SecurePackage *package = (SecurePackage *)phContext->dwUpper;
        PCtxtHandle ctxt = (PCtxtHandle)phContext->dwLower;

        if (package && package->provider)
        {
            if (package->provider->fnTableW.QueryContextAttributesW)
            {
                ret = package->provider->fnTableW.QueryContextAttributesW(
                 ctxt, ulAttribute, pBuffer);
                if (ret == SEC_E_OK)
                    ret = thunk_ContextAttributesWToA(package, ulAttribute,
                     pBuffer);
            }
            else
                ret = SEC_E_UNSUPPORTED_FUNCTION;
        }
        else
            ret = SEC_E_INVALID_HANDLE;
    }
    else
        ret = SEC_E_INVALID_HANDLE;
    return ret;
}

static PSecPkgInfoW _copyPackageInfoFlatAToW(PSecPkgInfoA infoA)
{
    PSecPkgInfoW ret;

    if (infoA)
    {
        size_t bytesNeeded = sizeof(SecPkgInfoW);
        int nameLen = 0, commentLen = 0;

        if (infoA->Name)
        {
            nameLen = MultiByteToWideChar(CP_ACP, 0, infoA->Name, -1,
             NULL, 0);
            bytesNeeded += nameLen * sizeof(WCHAR);
        }
        if (infoA->Comment)
        {
            commentLen = MultiByteToWideChar(CP_ACP, 0, infoA->Comment, -1,
             NULL, 0);
            bytesNeeded += commentLen * sizeof(WCHAR);
        }
        ret = (PSecPkgInfoW)SECUR32_ALLOC(bytesNeeded);
        if (ret)
        {
            PWSTR nextString = (PWSTR)((PBYTE)ret + sizeof(SecPkgInfoW));

            memcpy(ret, infoA, sizeof(SecPkgInfoA));
            if (infoA->Name)
            {
                ret->Name = nextString;
                MultiByteToWideChar(CP_ACP, 0, infoA->Name, -1, nextString,
                 nameLen);
                nextString += nameLen;
            }
            else
                ret->Name = NULL;
            if (infoA->Comment)
            {
                ret->Comment = nextString;
                MultiByteToWideChar(CP_ACP, 0, infoA->Comment, -1, nextString,
                 commentLen);
            }
            else
                ret->Comment = NULL;
        }
    }
    else
        ret = NULL;
    return ret;
}

static SECURITY_STATUS thunk_ContextAttributesAToW(SecurePackage *package,
 unsigned long ulAttribute, void *pBuffer)
{
    SECURITY_STATUS ret = SEC_E_OK;

    if (package && pBuffer)
    {
        switch (ulAttribute)
        {
            case SECPKG_ATTR_NAMES:
            {
                PSecPkgContext_NamesA names = (PSecPkgContext_NamesA)pBuffer;
                SEC_CHAR *oldUser = names->sUserName;

                if (oldUser)
                {
                    names->sUserName =
                     (PSTR)SECUR32_AllocWideFromMultiByte(oldUser);
                    package->provider->fnTableW.FreeContextBuffer(oldUser);
                }
                break;
            }
            case SECPKG_ATTR_AUTHORITY:
            {
                PSecPkgContext_AuthorityA names =
                 (PSecPkgContext_AuthorityA)pBuffer;
                SEC_CHAR *oldAuth = names->sAuthorityName;

                if (oldAuth)
                {
                    names->sAuthorityName =
                     (PSTR)SECUR32_AllocWideFromMultiByte(oldAuth);
                    package->provider->fnTableW.FreeContextBuffer(oldAuth);
                }
                break;
            }
            case SECPKG_ATTR_KEY_INFO:
            {
                PSecPkgContext_KeyInfoA info = (PSecPkgContext_KeyInfoA)pBuffer;
                SEC_CHAR *oldSigAlgName = info->sSignatureAlgorithmName;
                SEC_CHAR *oldEncAlgName = info->sEncryptAlgorithmName;

                if (oldSigAlgName)
                {
                    info->sSignatureAlgorithmName =
                     (PSTR)SECUR32_AllocWideFromMultiByte(oldSigAlgName);
                    package->provider->fnTableW.FreeContextBuffer(
                     oldSigAlgName);
                }
                if (oldEncAlgName)
                {
                    info->sEncryptAlgorithmName =
                     (PSTR)SECUR32_AllocWideFromMultiByte(
                     oldEncAlgName);
                    package->provider->fnTableW.FreeContextBuffer(
                     oldEncAlgName);
                }
                break;
            }
            case SECPKG_ATTR_PACKAGE_INFO:
            {
                PSecPkgContext_PackageInfoA info =
                 (PSecPkgContext_PackageInfoA)pBuffer;
                PSecPkgInfoA oldPkgInfo = info->PackageInfo;

                if (oldPkgInfo)
                {
                    info->PackageInfo = (PSecPkgInfoA)
                     _copyPackageInfoFlatAToW(oldPkgInfo);
                    package->provider->fnTableW.FreeContextBuffer(oldPkgInfo);
                }
                break;
            }
            case SECPKG_ATTR_NEGOTIATION_INFO:
            {
                PSecPkgContext_NegotiationInfoA info =
                 (PSecPkgContext_NegotiationInfoA)pBuffer;
                PSecPkgInfoA oldPkgInfo = info->PackageInfo;

                if (oldPkgInfo)
                {
                    info->PackageInfo = (PSecPkgInfoA)
                     _copyPackageInfoFlatAToW(oldPkgInfo);
                    package->provider->fnTableW.FreeContextBuffer(oldPkgInfo);
                }
                break;
            }
            case SECPKG_ATTR_NATIVE_NAMES:
            {
                PSecPkgContext_NativeNamesA names =
                 (PSecPkgContext_NativeNamesA)pBuffer;
                PSTR oldClient = names->sClientName;
                PSTR oldServer = names->sServerName;

                if (oldClient)
                {
                    names->sClientName = (PSTR)SECUR32_AllocWideFromMultiByte(
                     oldClient);
                    package->provider->fnTableW.FreeContextBuffer(oldClient);
                }
                if (oldServer)
                {
                    names->sServerName = (PSTR)SECUR32_AllocWideFromMultiByte(
                     oldServer);
                    package->provider->fnTableW.FreeContextBuffer(oldServer);
                }
                break;
            }
            case SECPKG_ATTR_CREDENTIAL_NAME:
            {
                PSecPkgContext_CredentialNameA name =
                 (PSecPkgContext_CredentialNameA)pBuffer;
                PSTR oldCred = name->sCredentialName;

                if (oldCred)
                {
                    name->sCredentialName =
                     (PSTR)SECUR32_AllocWideFromMultiByte(oldCred);
                    package->provider->fnTableW.FreeContextBuffer(oldCred);
                }
                break;
            }
            /* no thunking needed: */
            case SECPKG_ATTR_ACCESS_TOKEN:
            case SECPKG_ATTR_DCE_INFO:
            case SECPKG_ATTR_FLAGS:
            case SECPKG_ATTR_LIFESPAN:
            case SECPKG_ATTR_PASSWORD_EXPIRY:
            case SECPKG_ATTR_SESSION_KEY:
            case SECPKG_ATTR_SIZES:
            case SECPKG_ATTR_STREAM_SIZES:
            case SECPKG_ATTR_TARGET_INFORMATION:
                break;
            default:
                WARN("attribute type %ld unknown\n", ulAttribute);
                ret = SEC_E_INTERNAL_ERROR;
        }
    }
    else
        ret = SEC_E_INVALID_TOKEN;
    return ret;
}

SECURITY_STATUS SEC_ENTRY thunk_QueryContextAttributesW(PCtxtHandle phContext,
 unsigned long ulAttribute, void *pBuffer)
{
    SECURITY_STATUS ret;

    TRACE("%p %ld %p\n", phContext, ulAttribute, pBuffer);
    if (phContext)
    {
        SecurePackage *package = (SecurePackage *)phContext->dwUpper;
        PCtxtHandle ctxt = (PCtxtHandle)phContext->dwLower;

        if (package && package->provider)
        {
            if (package->provider->fnTableA.QueryContextAttributesA)
            {
                ret = package->provider->fnTableA.QueryContextAttributesA(
                 ctxt, ulAttribute, pBuffer);
                if (ret == SEC_E_OK)
                    ret = thunk_ContextAttributesAToW(package, ulAttribute,
                     pBuffer);
            }
            else
                ret = SEC_E_UNSUPPORTED_FUNCTION;
        }
        else
            ret = SEC_E_INVALID_HANDLE;
    }
    else
        ret = SEC_E_INVALID_HANDLE;
    return ret;
}

SECURITY_STATUS SEC_ENTRY thunk_SetContextAttributesA(PCtxtHandle phContext,
 unsigned long ulAttribute, void *pBuffer, unsigned long cbBuffer)
{
    SECURITY_STATUS ret;

    TRACE("%p %ld %p %ld\n", phContext, ulAttribute, pBuffer, cbBuffer);
    if (phContext)
    {
        SecurePackage *package = (SecurePackage *)phContext->dwUpper;
        PCtxtHandle ctxt = (PCtxtHandle)phContext->dwLower;

        if (package && package->provider && pBuffer && cbBuffer)
        {
            if (package->provider->fnTableW.SetContextAttributesW)
            {
                /* TODO: gotta validate size too! */
                ret = thunk_ContextAttributesWToA(package, ulAttribute,
                 pBuffer);
                if (ret == SEC_E_OK)
                    ret = package->provider->fnTableW.SetContextAttributesW(
                     ctxt, ulAttribute, pBuffer, cbBuffer);
            }
            else
                ret = SEC_E_UNSUPPORTED_FUNCTION;
        }
        else
            ret = SEC_E_INVALID_HANDLE;
    }
    else
        ret = SEC_E_INVALID_HANDLE;
    return ret;
}

SECURITY_STATUS SEC_ENTRY thunk_SetContextAttributesW(PCtxtHandle phContext,
 unsigned long ulAttribute, void *pBuffer, unsigned long cbBuffer)
{
    SECURITY_STATUS ret;

    TRACE("%p %ld %p %ld\n", phContext, ulAttribute, pBuffer, cbBuffer);
    if (phContext)
    {
        SecurePackage *package = (SecurePackage *)phContext->dwUpper;
        PCtxtHandle ctxt = (PCtxtHandle)phContext->dwLower;

        if (package && package->provider && pBuffer && cbBuffer)
        {
            if (package->provider->fnTableA.SetContextAttributesA)
            {
                /* TODO: gotta validate size too! */
                ret = thunk_ContextAttributesAToW(package, ulAttribute,
                 pBuffer);
                if (ret == SEC_E_OK)
                    ret = package->provider->fnTableA.SetContextAttributesA(
                     ctxt, ulAttribute, pBuffer, cbBuffer);
            }
            else
                ret = SEC_E_UNSUPPORTED_FUNCTION;
        }
        else
            ret = SEC_E_INVALID_HANDLE;
    }
    else
        ret = SEC_E_INVALID_HANDLE;
    return ret;
}

SECURITY_STATUS SEC_ENTRY thunk_ImportSecurityContextA(
 SEC_CHAR *pszPackage, PSecBuffer pPackedContext, void *Token,
 PCtxtHandle phContext)
{
    SECURITY_STATUS ret;
    UNICODE_STRING package;

    TRACE("%s %p %p %p\n", debugstr_a(pszPackage), pPackedContext, Token,
     phContext);
    RtlCreateUnicodeStringFromAsciiz(&package, pszPackage);
    ret = ImportSecurityContextW(package.Buffer, pPackedContext, Token,
     phContext);
    RtlFreeUnicodeString(&package);
    return ret;
}

SECURITY_STATUS SEC_ENTRY thunk_ImportSecurityContextW(
 SEC_WCHAR *pszPackage, PSecBuffer pPackedContext, void *Token,
 PCtxtHandle phContext)
{
    SECURITY_STATUS ret;
    PSTR package = SECUR32_AllocMultiByteFromWide(pszPackage);

    TRACE("%s %p %p %p\n", debugstr_w(pszPackage), pPackedContext, Token,
     phContext);
    ret = ImportSecurityContextA(package, pPackedContext, Token, phContext);
    if (package)
        SECUR32_FREE(package);
    return ret;
}
