/*===================================================================
Microsoft Denali

Microsoft Confidential.
Copyright 1996 Microsoft Corporation. All Rights Reserved.

Component: Request, Response objects

File: clcert.cpp

Owner: DGottner

This file contains the code for the implementation of the 
Request.ClientCertificate
===================================================================*/

#include "denpre.h"
#pragma hdrstop

#include <schnlsp.h>

#include "objbase.h"
#include "request.h"
#include "clcert.h"

#include "memchk.h"

#pragma warning (disable: 4355)  // ignore: "'this' used in base member init

#define UUENCODEDSIZE(a)  ((((a)+3)*4)/3+1)

#define BLOB_AS_ARRAY

HRESULT
SetVariantAsByteArray(
    VARIANT*    pvarReturn, 
    DWORD       cbLen,
    LPBYTE      pbIn 
    );

//
//  Taken from NCSA HTTP and wwwlib.
//
//  NOTE: These conform to RFC1113, which is slightly different then the Unix
//        uuencode and uudecode!
//

const int _pr2six[256]={
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,62,64,64,64,63,
    52,53,54,55,56,57,58,59,60,61,64,64,64,64,64,64,64,0,1,2,3,4,5,6,7,8,9,
    10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,64,64,64,64,64,64,26,27,
    28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64
};

char _six2pr[64] = {
    'A','B','C','D','E','F','G','H','I','J','K','L','M',
    'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
    'a','b','c','d','e','f','g','h','i','j','k','l','m',
    'n','o','p','q','r','s','t','u','v','w','x','y','z',
    '0','1','2','3','4','5','6','7','8','9','+','/'
};

const int _pr2six64[256]={
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,
    16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,
    40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
     0,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
    64,64,64,64,64,64,64,64,64,64,64,64,64
};
     
char _six2pr64[64] = {
    '`','!','"','#','$','%','&','\'','(',')','*','+',',',
    '-','.','/','0','1','2','3','4','5','6','7','8','9',
    ':',';','<','=','>','?','@','A','B','C','D','E','F',
    'G','H','I','J','K','L','M','N','O','P','Q','R','S',
    'T','U','V','W','X','Y','Z','[','\\',']','^','_'
};

/*------------------------------------------------------------------
 * X B F
 */

BOOL XBF::Extend( int cA )
{
    if ( cA > m_cAlloc )
    {
        int cNew = (( cA + XBF_EXTEND )/XBF_EXTEND)*XBF_EXTEND;
        LPSTR pN = (LPSTR)malloc( cNew );
        if ( pN == NULL )
        {
            return FALSE;
        }
        if ( m_cSize )
        {
            memcpy( pN, m_pV, m_cSize );
        }
        if ( m_cAlloc )
        {
            free( m_pV );
        }
        m_pV = pN;
        m_cAlloc = cNew;
    }
    return TRUE;
}

/*------------------------------------------------------------------
 * C C l C e r t S u p p o r t E r r
 */

/*===================================================================
CClCertSupportErr::CClCertSupportErr

constructor
===================================================================*/

CClCertSupportErr::CClCertSupportErr(CClCert *pClCert)
{
    m_pClCert = pClCert;
}



/*===================================================================
CClCertSupportErr::QueryInterface
CClCertSupportErr::AddRef
CClCertSupportErr::Release

Delegating IUnknown members for CClCertSupportErr object.
===================================================================*/

STDMETHODIMP CClCertSupportErr::QueryInterface(const IID &idInterface, void **ppvObj)
{
    return m_pClCert->QueryInterface(idInterface, ppvObj);
}

STDMETHODIMP_(ULONG) CClCertSupportErr::AddRef()
{
    return m_pClCert->AddRef();
}

STDMETHODIMP_(ULONG) CClCertSupportErr::Release()
{
    return m_pClCert->Release();
}



/*===================================================================
CClCertSupportErr::InterfaceSupportsErrorInfo

Report back to OA about which interfaces we support that return
error information
===================================================================*/

STDMETHODIMP CClCertSupportErr::InterfaceSupportsErrorInfo(const GUID &idInterface)
{
    if (idInterface == IID_IDispatch)
        return S_OK;

    return S_FALSE;
}



/*------------------------------------------------------------------
 * C R e a d C l C e r t
 */

/*===================================================================
CReadClCert::CReadClCert

constructor
===================================================================*/

CReadClCert::CReadClCert(CClCert *pClCert)
{
    m_pClCert = pClCert;
    CDispatch::Init(IID_IRequestDictionary);
}



/*===================================================================
CReadClCert::QueryInterface
CReadClCert::AddRef
CReadClCert::Release

Delegating IUnknown members for CReadClCert object.
===================================================================*/

STDMETHODIMP CReadClCert::QueryInterface(const IID &idInterface, void **ppvObj)
{
    return m_pClCert->QueryInterface(idInterface, ppvObj);
}

STDMETHODIMP_(ULONG) CReadClCert::AddRef()
{
    return m_pClCert->AddRef();
}

STDMETHODIMP_(ULONG) CReadClCert::Release()
{
    return m_pClCert->Release();
}


/*===================================================================
CReadClCert::get_Item

Retrieve a value in the clcert dictionary.
===================================================================*/

STDMETHODIMP CReadClCert::get_Item(VARIANT varKey, VARIANT *pVarReturn)
{
    VariantInit(pVarReturn);                // default return value is Empty
    VARIANT *pvarKey = &varKey;
    HRESULT hres;

    // BUG 937: VBScript passes VT_VARIANT|VT_BYREF when passing obect
    //          produced by IEnumVariant
    //
    // Use VariantResolveDispatch which will:
    //
    //     *  Copy BYREF variants for us using VariantCopyInd
    //     *  handle E_OUTOFMEMORY for us
    //     *  get the default value from an IDispatch, which seems
    //        like an appropriate conversion.
    //
    VARIANT varKeyCopy;
    VariantInit(&varKeyCopy);
    if (V_VT(pvarKey) != VT_BSTR) {
        if (FAILED(VariantResolveDispatch(&varKeyCopy, &varKey, IID_IRequestDictionary, IDE_REQUEST)))
            goto LExit;

        pvarKey = &varKeyCopy;
    }

    switch (V_VT(pvarKey)) {
        case VT_BSTR:
            break;
        
        case VT_ERROR:
            if (V_ERROR(pvarKey) == DISP_E_PARAMNOTFOUND) {
                // simple value, URLEncoding NOT a good idea in this case
                if (m_pClCert->m_szValue) {
                    V_VT(pVarReturn) = VT_BSTR;
                    switch( m_pClCert->m_veType ) {
                        case VT_BSTR: {
                            BSTR bstrT;
                            if ( FAILED(SysAllocStringFromSz(m_pClCert->m_szValue, 0, &bstrT )) ) {
                                ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
                                VariantClear(&varKeyCopy);
                                return E_FAIL;
                            }
                            V_BSTR(pVarReturn) = bstrT;
                            break;
                        }
                                                
                        case VT_DATE:
                            V_VT(pVarReturn) = VT_DATE;
                            V_DATE(pVarReturn) = *(UNALIGNED64 DATE*)m_pClCert->m_szValue;
                            break;

                        case VT_I4:
                            V_VT(pVarReturn) = VT_I4;
                            V_I4(pVarReturn) = *(UNALIGNED64 DWORD*)m_pClCert->m_szValue;
                            break;

                        case VT_BLOB:
#if defined(BLOB_AS_ARRAY)
                            if ( FAILED( hres = SetVariantAsByteArray( pVarReturn, 
                                                                       m_pClCert->m_cLen, 
                                                                       (LPBYTE)m_pClCert->m_szValue ) ) ) {
                                ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
                                VariantClear(&varKeyCopy);
                                return hres;
                            }
#else
                            V_BSTR(pVarReturn) = SysAllocStringByteLen(m_pClCert->m_szValue, m_pClCert->m_cLen );
#endif
                            break;

                        default:
                            Assert( FALSE );
                    }
                }
                        
                // dictionary value, must URLEncode to prevent '&', '=' from being misinterpreted
                else {
                }

                VariantClear(&varKeyCopy);
                return S_OK;
            }

        default:
            ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_EXPECTING_STR);
            VariantClear(&varKeyCopy);
            return E_FAIL;
    }
LExit:
    VariantClear(&varKeyCopy);
    return S_OK;
}

/*===================================================================
CReadClCert::get_Key

Function called from DispInvoke to get keys from the QueryString collection.

Parameters:
        vKey            VARIANT [in], which parameter to get the key of
        pvarReturn      VARIANT *, [out] value of the requested parameter

Returns:
        S_OK on success, E_FAIL on failure.
===================================================================*/

HRESULT CReadClCert::get_Key(VARIANT varKey, VARIANT *pVar)
{
    return E_NOTIMPL;
}

/*===================================================================
CReadClCert::get_Count

Parameters:
        pcValues - count is stored in *pcValues
===================================================================*/

STDMETHODIMP CReadClCert::get_Count(int *pcValues)
{
    HRESULT hrReturn = S_OK;

    *pcValues = 0;

    return hrReturn;
}

/*===================================================================
CReadClCert::get__NewEnum

Return an enumerator object.
===================================================================*/

STDMETHODIMP CReadClCert::get__NewEnum(IUnknown **ppEnum)
{
    *ppEnum = NULL;
    return E_NOTIMPL;
}



/*------------------------------------------------------------------
 * C C l C e r t
 */

/*===================================================================
CClCert::CClCert

constructor
===================================================================*/

CClCert::CClCert(IUnknown *pUnkOuter, PFNDESTROYED pfnDestroy)
    : m_ReadClCertInterface(this),
      m_ClCertSupportErrorInfo(this)
{
    m_szValue    = NULL;
    m_veType     = VT_BSTR;
    m_pfnDestroy = pfnDestroy;
    m_cRefs      = 1;
}



/*===================================================================
CClCert::~CClCert

Destructor
===================================================================*/

CClCert::~CClCert()
{
}



/*===================================================================
CClCert::Init

initialize the clcert. This initializes the clcert's value hashing
table
===================================================================*/

HRESULT CClCert::Init()
{
    return S_OK;
}



/*===================================================================
CClCert::QueryInterface
CClCert::AddRef
CClCert::Release

IUnknown members for CClCert object.

Note on CClCert::QueryInterface: The Query for IDispatch is
ambiguous because it can either refer to DIRequestDictionary or
DIWriteClCert.  To resolve this, we resolve requests for IDispatch
to IRequestDictionary.
===================================================================*/

STDMETHODIMP CClCert::QueryInterface(const IID &idInterface, void **ppvObj)
{
    if (idInterface == IID_IUnknown)
        *ppvObj = this;

    else if (idInterface == IID_IRequestDictionary || idInterface == IID_IDispatch)
        *ppvObj = &m_ReadClCertInterface;

    else if (idInterface == IID_ISupportErrorInfo)
        *ppvObj = &m_ClCertSupportErrorInfo;

    else
        *ppvObj = NULL;

    if (*ppvObj != NULL)
    {
        static_cast<IUnknown *>(*ppvObj)->AddRef();
        return S_OK;
    }

    return ResultFromScode(E_NOINTERFACE);
}


STDMETHODIMP_(ULONG) CClCert::AddRef()
{
    return ++m_cRefs;
}


STDMETHODIMP_(ULONG) CClCert::Release(void)
{
    if (--m_cRefs != 0)
        return m_cRefs;

    if (m_pfnDestroy != NULL)
        (*m_pfnDestroy)();

    delete this;
    return 0;
}



/*===================================================================
CClCert::AddValue

Set the clcert's primary value. One you set the primary value,
you can't reset it.
===================================================================*/

HRESULT CClCert::AddValue(char *szValue, VARENUM ve, UINT l )
{
    if (m_szValue != NULL)          // clcert already is marked as single-valued
        return E_FAIL;

    m_szValue = szValue;

    m_veType = ve;
    m_cLen = l;
    return S_OK;
}



/*===================================================================
CClCert::GetHTTPClCertSize

Return the number of bytes required for the expansion of the clcert
===================================================================*/

size_t CClCert::GetHTTPClCertSize()
        {
        if (m_szValue)
                return URLEncodeLen(m_szValue);
    else
        return 1;
        }


/*===================================================================
CClCert::GetHTTPClCert

Return the URL Encoded value a single clcert

Parameters:
        szBuffer -  pointer to the destination buffer to store the
                                URL encoded value

Returns:
        Returns a pointer to the terminating NUL character.
===================================================================*/

char *CClCert::GetHTTPClCert(char *szBuffer)
{
    if (m_szValue)
        return URLEncode(szBuffer, m_szValue);

    else
    {
        char *szDest = szBuffer;
        *szDest = '\0';

        return szDest;
    }
}



/*===================================================================
CClCert::GetClCertHeaderSize

Return the number of bytes required to allocate for the "Set-ClCert" header.

Parameters:
        szName - the name of the cookie (the size of the name is added to the value)

Returns:
        Returns 0 if *this does not contain a cookie value.
===================================================================*/

size_t CClCert::GetClCertHeaderSize(const char *szName)
{
    int cbClCert = sizeof "Set-ClCert: ";           // initialize and add NUL terminator now

    // Add size of the URL Encoded name, a character for the '=', and the size
    // of the URL Encoded cookie value.  URLEncodeLen, and GetHttpClCertSize
    // compensate for the NUL terminator, so we actually SUBTRACT 1. (-2 for
    // these two function calls, +1 for the '=' sign
    //
    cbClCert += URLEncodeLen(szName) + GetHTTPClCertSize() - 1;
    
    return cbClCert;
}



/*===================================================================
CClCert::GetClCertHeader

Construct the appropriate "Set-ClCert" header for a clcert.

Parameters:
        szName - the name of the clcert (the size of the name is added to the value)

Returns:
        Returns 0 if *this does not contain a clcert value.
===================================================================*/

char *CClCert::GetClCertHeader(const char *szName, char *szBuffer)
{
    // write out the clcert name and value
    //
    char *szDest = strcpyExA(szBuffer, "Set-ClCert: ");
    szDest = URLEncode(szDest, szName);
    szDest = strcpyExA(szDest, "=");
    szDest = GetHTTPClCert(szDest);
    
    return szDest;
}



/*------------------------------------------------------------------
 * C C e r t R e q u e s t
 */


/*===================================================================
CCertRequest::AddStringPair

Add a string element in the collection

Parameters:
        Source - variable type ( CLCERT, COOKIE, ... )
    szName - name of element
    szValue - ptr to value as string
    pxbf - ptr to buffer where to store name

Returns:
        S_OK if success, E_OUTOFMEMORY or E_FAIL otherwise
===================================================================*/

HRESULT 
CCertRequest::AddStringPair( 
    CollectionType Source, 
    LPSTR szName, 
    LPSTR szValue,
    XBF *pxbf,
    BOOL fDuplicate,
    UINT lCodePage
    )
{
    HRESULT hResult;
    CRequestHit *pReqHit;

    if ( fDuplicate )
    {
        if ( (szValue = pxbf->AddStringZ( szValue )) == NULL )
        {
            return E_OUTOFMEMORY;
        }
    }

    if (FAILED(hResult = AddName( szName, &pReqHit, pxbf)))
    {
        if ( hResult == E_FAIL )
        {
            // assume duplicate value found
            // if out of memore, OUT_OF_MEMORY would have been returned

            hResult = S_OK;
        }
        return hResult;
    }

    if (FAILED(hResult = pReqHit->AddValue( Source, szValue, NULL, lCodePage )))
    {
        return hResult;
    }

    return S_OK;
}


/*===================================================================
CCertRequest::AddDatePair

Add a date element in the collection

Parameters:
        Source - variable type ( CLCERT, COOKIE, ... )
    szName - name of element
    pValue - ptr to date as FILETIME
    pxbf - ptr to buffer where to store name

Returns:
        S_OK if success, E_OUTOFMEMORY or E_FAIL otherwise
===================================================================*/

HRESULT 
CCertRequest::AddDatePair( 
    CollectionType Source, 
    LPSTR szName, 
    FILETIME* pValue,
    XBF *pxbf
    )
{
    HRESULT hResult;
    CRequestHit *pReqHit;
    DATE Date;
    SYSTEMTIME st;
    LPBYTE pVal;

    if ( !FileTimeToSystemTime( pValue, &st ) )
    {
        return E_FAIL;
    }

    SystemTimeToVariantTime( &st, &Date );

    if ( (pVal = (LPBYTE)pxbf->AddBlob( (LPSTR)&Date, sizeof(Date) )) == NULL )
    {
        return E_OUTOFMEMORY;
    }

    if (FAILED(hResult = AddName( szName, &pReqHit, pxbf)))
    {
        return hResult;
    }

    if (FAILED(hResult = pReqHit->AddCertValue( VT_DATE, pVal, sizeof(Date) )))
    {
        return hResult;
    }

    return S_OK;
}




/*===================================================================
CCertRequest::AddDwordPair

Add a DWORD element in the collection

Parameters:
        Source - variable type ( CLCERT, COOKIE, ... )
    szName - name of element
    pValue - ptr to date as DWORD
    pxbf - ptr to buffer where to store name

Returns:
        S_OK if success, E_OUTOFMEMORY or E_FAIL otherwise
===================================================================*/

HRESULT 
CCertRequest::AddDwordPair( 
    CollectionType Source, 
    LPSTR szName, 
    DWORD* pValue,
    XBF *pxbf
    )
{
    HRESULT hResult;
    CRequestHit *pReqHit;
    LPBYTE pVal;

    if ( (pVal = (LPBYTE)pxbf->AddBlob( (LPSTR)pValue, sizeof(DWORD) )) == NULL )
    {
        return E_OUTOFMEMORY;
    }

    if (FAILED(hResult = AddName( szName, &pReqHit, pxbf)))
    {
        return hResult;
    }

    if (FAILED(hResult = pReqHit->AddCertValue( VT_I4, pVal, sizeof(DWORD) )))
    {
        return hResult;
    }

    return S_OK;
}




/*===================================================================
CCertRequest::AddBinaryPair

Add a binary element in the collection
Each byte is converted to UNICODE character so that mid() & asc() work

Parameters:
        Source - variable type ( CLCERT, COOKIE, ... )
    szName - name of element
    pValue - ptr to value as byte array
    cValue - # of bytes pointed to by pValue
    pxbf - ptr to buffer where to store name

Returns:
        S_OK if success, E_OUTOFMEMORY or E_FAIL otherwise
===================================================================*/

HRESULT 
CCertRequest::AddBinaryPair( 
    CollectionType Source, 
    LPSTR szName, 
    LPBYTE pValue,
    DWORD cValue,
    XBF *pxbf,
    UINT lCodePage
    )
{
    HRESULT hResult;
    CRequestHit *pReqHit;
    LPBYTE pVal;

#if defined(BLOB_AS_ARRAY)

    if ( (pVal = (LPBYTE)pxbf->ReserveRange( cValue )) == NULL )
    {
        return E_OUTOFMEMORY;
    }

    memcpy( pVal, pValue, cValue );

    pxbf->SkipRange( cValue );

    if (FAILED(hResult = AddName( szName, &pReqHit, pxbf)))
    {
        return hResult;
    }

    if (FAILED(hResult = pReqHit->AddCertValue( VT_BLOB, pVal, cValue )))
    {
        return hResult;
    }

#else

    if ( (pVal = (LPBYTE)pxbf->ReserveRange( cValue * sizeof(WCHAR), sizeof(WCHAR))) == NULL )
    {
        return E_OUTOFMEMORY;
    }

    if ( !(cValue = MultiByteToWideChar( lCodePage, 0, (LPSTR)pValue, cValue, (WCHAR*)pVal, cValue)) )
    {
        return E_FAIL;
    }

    pxbf->SkipRange( cValue * sizeof(WCHAR), sizeof(WCHAR));

    if (FAILED(hResult = AddName( szName, &pReqHit, pxbf)))
    {
        return hResult;
    }

    if (FAILED(hResult = pReqHit->AddCertValue( VT_BLOB, pVal, cValue * sizeof(WCHAR) )))
    {
            return hResult;
    }

#endif

    return S_OK;
}


BOOL IISuuencode( BYTE *   bufin,
               DWORD    nbytes,
               BYTE *   outptr,
               BOOL     fBase64 )
{
   unsigned int i;
   char *six2pr = fBase64 ? _six2pr64 : _six2pr;

   for (i=0; i<nbytes; i += 3) {
      *(outptr++) = six2pr[*bufin >> 2];            /* c1 */
      *(outptr++) = six2pr[((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)]; /*c2*/
      *(outptr++) = six2pr[((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03)];/*c3*/
      *(outptr++) = six2pr[bufin[2] & 077];         /* c4 */

      bufin += 3;
   }

   /* If nbytes was not a multiple of 3, then we have encoded too
    * many characters.  Adjust appropriately.
    */
   if(i == nbytes+1) {
      /* There were only 2 bytes in that last group */
      outptr[-1] = '=';
   } else if(i == nbytes+2) {
      /* There was only 1 byte in that last group */
      outptr[-1] = '=';
      outptr[-2] = '=';
   }

   *outptr = '\0';

   return TRUE;
}

/*===================================================================
CCertRequest::AddUuBinaryPair

Add a binary element in the collection
buffer is uuencoded then converted to UNICODE character so that mid() & asc() work

Parameters:
        Source - variable type ( CLCERT, COOKIE, ... )
    szName - name of element
    pValue - ptr to value as byte array
    cValue - # of bytes pointed to by pValue
    pxbf - ptr to buffer where to store name

Returns:
        S_OK if success, E_OUTOFMEMORY or E_FAIL otherwise
===================================================================*/

HRESULT 
CCertRequest::AddUuBinaryPair( 
    CollectionType Source, 
    LPSTR szName, 
    LPBYTE pValue,
    DWORD cValue,
    XBF *pxbf,
    UINT lCodePage
    )
{
    HRESULT hResult;
    CRequestHit *pReqHit;
    LPBYTE pVal;

    if ( (pVal = (LPBYTE)pxbf->ReserveRange( UUENCODEDSIZE(cValue) )) == NULL )
    {
        return E_OUTOFMEMORY;
    }

    if ( !IISuuencode( (LPBYTE)pValue, cValue, pVal, FALSE ) )
    {
        return E_FAIL;
    }

    Assert( (strlen((LPSTR)pVal)+1) <= UUENCODEDSIZE(cValue) );

    pxbf->SkipRange( strlen((LPSTR)pVal)+1 );

        if (FAILED(hResult = AddName( szName, &pReqHit, pxbf)))
    {
                return hResult;
    }

        if (FAILED(hResult = pReqHit->AddValue( Source, (LPSTR)pVal, NULL, lCodePage )))
    {
                return hResult;
    }

    return S_OK;
}


/*===================================================================
CCertRequest::AddName

Add a named entry to the collection

Parameters:
        szName - name of entry
    ppReqHit - updated with ptr to created entry
    pxbf - ptr to buffer where to store name

Returns:
        S_OK if success, E_OUTOFMEMORY or E_FAIL otherwise
===================================================================*/

HRESULT 
CCertRequest::AddName( 
    LPSTR szName, 
    CRequestHit **ppReqHit,
    XBF *pxbf
    )
{
    if ( (szName = pxbf->AddStringZ( szName )) == NULL )
    {
        return E_OUTOFMEMORY;
    }

    // Add this object to the Request
    CRequestHit *pRequestHit = (CRequestHit *)(pReq->CertStoreFindElem(szName, strlen(szName)));
    if (pRequestHit == NULL)
    {
        pRequestHit = new CRequestHit;
        if (pRequestHit == NULL)
        {
            return E_OUTOFMEMORY;
        }

        if (FAILED(pRequestHit->Init(szName)))
        {
            delete pRequestHit;
            return E_FAIL;
        }

        pReq->CertStoreAddElem( (CLinkElem*) pRequestHit );
    }
    else if (pRequestHit->m_pClCertData)    // a clcert by this name already exists
    {
        return E_FAIL;
    }

    if (!pReq->m_pData->m_ClCerts.AddRequestHit(pRequestHit))
    {
        return E_OUTOFMEMORY;
    }

    *ppReqHit = pRequestHit;

    return S_OK;
}


typedef struct _MAP_ASN {
    LPSTR pAsnName;
    LPSTR pTextName;
} MAP_ASN;


//
// definition of ASN.1 <> X.509 name conversion
//

MAP_ASN aMapAsn[] = {
    { szOID_COUNTRY_NAME, "C" },
    { szOID_ORGANIZATION_NAME, "O" },
    { szOID_ORGANIZATIONAL_UNIT_NAME, "OU" },
    { szOID_COMMON_NAME, "CN" },
    { szOID_LOCALITY_NAME, "L" },
    { szOID_STATE_OR_PROVINCE_NAME, "S" },
    { szOID_TITLE, "T" },
    { szOID_GIVEN_NAME, "GN" },
    { szOID_INITIALS, "I" },
    { "1.2.840.113549.1.9.1", "EMAIL" },
} ;


LPSTR MapAsnName(
    LPSTR pAsnName
    )
/*++

Routine Description:

    Convert ASN.1 name ( as ANSI string ) to X.509 member name

Arguments:

    pAsnName - ASN.1 name

Return Value:

    ptr to converted name if ASN.1 name was recognized, else ASN.1 name

--*/
{
    UINT x;
    for ( x = 0 ; x < sizeof(aMapAsn)/sizeof(MAP_ASN) ; ++x )
    {
        if ( !strcmp( pAsnName, aMapAsn[x].pAsnName ) )
        {
            return aMapAsn[x].pTextName;
        }
    }

    return pAsnName;
}


BOOL
DecodeRdn( 
    CERT_NAME_BLOB* pNameBlob,
    PCERT_NAME_INFO* ppNameInfo
    )
/*++

Routine Description:

    Create a PNAME_INFO from PNAME_BLOB

Arguments:

    pNameBlob - ptr to name blob to decode
    ppNameInfo - updated with ptr to name info

Return Value:

    TRUE on success, FALSE on failure

--*/
{
    PCERT_NAME_INFO     pNameInfo = NULL;
    DWORD               cbNameInfo;

    if (!CryptDecodeObject(X509_ASN_ENCODING,
                          (LPCSTR)X509_NAME,
                          pNameBlob->pbData,
                          pNameBlob->cbData,
                          0,
                          NULL,
                          &cbNameInfo))
    {
        ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_CERTIFICATE_BAD_CERT);
        return FALSE;
    }

    if (NULL == (pNameInfo = (PCERT_NAME_INFO)malloc(cbNameInfo)))
    {
        ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
        return FALSE;
    }

    if (!CryptDecodeObject(X509_ASN_ENCODING,
                           (LPCSTR)X509_NAME,
                           pNameBlob->pbData,
                           pNameBlob->cbData,
                           0,
                           pNameInfo,
                           &cbNameInfo))
    {
        ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_CERTIFICATE_BAD_CERT);
        free( pNameInfo );
        return FALSE;
    }

    *ppNameInfo = pNameInfo;

    return TRUE;
}


VOID
FreeDecodedRdn(
    PCERT_NAME_INFO pNameInfo
    )
/*++

Routine Description:

    Free a PNAME_BLOB created by DecodeRdn()

Arguments:

    pNameInfo - ptr to name info created by DecodeRdn()

Return Value:

    None

--*/
{
    free( pNameInfo );
}


BOOL
BuildRdnList(
    PCERT_NAME_INFO pNameInfo,
    XBF* pxbf,
    BOOL fXt
    )
/*++

Routine Description:

    Build a clear text representation of the Rdn list in pNameInfo
    Format as "C=US, O=Ms, CN=name"

Arguments:

    pNameInfo - ptr to name info
    pxbf - ptr to buffer receiving output
    fXt - TRUE if buffer to be extended, FALSE does not extend ( buffer 
          must be big enough before calling this function or FALSE will
          be returned )

Return Value:

    TRUE on success, FALSE on failure

--*/
{
    DWORD               cRDN;
    DWORD               cAttr;
    PCERT_RDN           pRDN;
    PCERT_RDN_ATTR      pAttr;
    BOOL                fFirst = TRUE;

    for (cRDN = pNameInfo->cRDN, pRDN = pNameInfo->rgRDN; cRDN > 0; cRDN--, pRDN++)
    {
        for ( cAttr = pRDN->cRDNAttr, pAttr = pRDN->rgRDNAttr ; cAttr > 0 ; cAttr--, ++pAttr )
        {
            if ( !fFirst )
            {
                if ( !pxbf->AddBlob( ", ", sizeof(", ")-1, fXt ) )
                {
                    return FALSE;
                }
            }
            else
            {
                fFirst = FALSE;
            }

            if ( pAttr->dwValueType == CERT_RDN_UNICODE_STRING )
            {
                INT                 iRet;
                BYTE                abBuffer[ 512 ];
                DWORD               cbNameBuffer;
                PBYTE               pNameBuffer = NULL;
                
                //
                // Need to convert unicode string to MBCS :(
                //

                iRet = WideCharToMultiByte( CP_ACP,
                                            0,
                                            (LPWSTR) pAttr->Value.pbData,
                                            -1,
                                            NULL,
                                            0,
                                            NULL,
                                            NULL );

                if ( !iRet )
                {
                    return FALSE;
                }
                else
                {
                    cbNameBuffer = (DWORD) iRet;
                    if ( (DWORD) iRet > sizeof( abBuffer ) )
                    {
                        pNameBuffer = (PBYTE) LocalAlloc( LPTR,
                                                          (DWORD) iRet );
                        if ( !pNameBuffer )
                        {
                            return FALSE;
                        }
                    }
                    else
                    {
                        pNameBuffer = abBuffer;
                    }
                }

                iRet = WideCharToMultiByte( CP_ACP,
                                            0,
                                            (LPWSTR) pAttr->Value.pbData,
                                            -1,
                                            (LPSTR) pNameBuffer,
                                            cbNameBuffer,
                                            NULL,
                                            NULL );

                if ( !iRet )
                {
                    if ( pNameBuffer != abBuffer )
                    {
                        LocalFree( pNameBuffer );
                    }
                    return FALSE;
                }

                //
                // Now stuff the MBCS string back into the blob.  I do this
                // because there is other code that re-reads and re-processes
                // the CRYPTAPI blob.  
                //

                if ( cbNameBuffer <= pAttr->Value.cbData )
                {
                    memcpy( pAttr->Value.pbData,
                            pNameBuffer,
                            cbNameBuffer );
                    pAttr->Value.cbData = cbNameBuffer;
                    pAttr->dwValueType = CERT_RDN_OCTET_STRING;
                }

                if ( pNameBuffer != abBuffer )
                {
                    LocalFree( pNameBuffer );
                    pNameBuffer = NULL;
                }
            }
            
            if ( !pxbf->AddString( MapAsnName( pAttr->pszObjId ), fXt ) ||
                 !pxbf->AddBlob( "=", sizeof("=")-1, fXt ) ||
                 !pxbf->AddString( (LPSTR) pAttr->Value.pbData, fXt ) )
            {
                return FALSE;
            }
        }
    }

    return pxbf->AddBlob( "", sizeof(""), fXt ) != NULL;
}
        

/*===================================================================
CCertRequest::ParseRDNS

Function called to parse a certificate into a OA collection

Parameters:
        pNameInfo - ptr to name structure ( cf. CAPI 2 )
    pszPrefix - prefix to prepend to members name
    pxbf - ptr to buffer to hold result

Returns:
        S_OK on success, E_OUTOFMEMORY if out of memory or E_FAIL for
    other errors
===================================================================*/

HRESULT
CCertRequest::ParseRDNS( 
    PCERT_NAME_INFO pNameInfo,
    LPSTR pszPrefix,
    XBF *pxbf,
    UINT lCodePage
    )
{
    DWORD               cRDN;
    DWORD               cAttr;
    PCERT_RDN           pRDN;
    PCERT_RDN_ATTR      pAttr;
    DWORD               cRDNs;
    DWORD               cAttrs;
    PCERT_RDN           pRDNs;
    PCERT_RDN_ATTR      pAttrs;
    LPSTR               pszFullName = NULL;
    HRESULT             hRes = S_OK;
    LPSTR               pName;
    UINT                cL;
    LPSTR               pVal = NULL;


    for (cRDN = pNameInfo->cRDN, pRDN = pNameInfo->rgRDN; cRDN > 0; cRDN--, pRDN++)
    {
        for ( cAttr = pRDN->cRDNAttr, pAttr = pRDN->rgRDNAttr ; cAttr > 0 ; cAttr--, ++pAttr )
        {
            if ( pAttr->dwValueType & 0x80000000 )
            {
                continue;
            }

            // scan for attr of same name

            pAttr->dwValueType |= 0x80000000;
            cL = 0;
            pVal = NULL;
            for ( cRDNs = cRDN, pRDNs = pRDN; 
                  cRDNs > 0; 
                  cRDNs--, pRDNs++)
            {
                for ( cAttrs = pRDNs->cRDNAttr, pAttrs = pRDNs->rgRDNAttr ; 
                      cAttrs > 0 ; 
                      cAttrs--, ++pAttrs )
                {
                    if ( !(pAttrs->dwValueType & 0x80000000) &&
                         !strcmp( pAttr->pszObjId, pAttrs->pszObjId ) )
                    {
                        cL += strlen( (LPSTR)pAttrs->Value.pbData ) + 1;
                    }                    
                }
            }

            //
            // if attributes of the same name found, concatenate their
            // values separated by ';'
            //

            if ( cL )
            {
                pVal = (LPSTR)malloc( cL + strlen((LPSTR)pAttr->Value.pbData) + 1 );
                if ( pVal == NULL )
                {
                    return E_OUTOFMEMORY;
                }
                strcpy( pVal, (LPSTR)pAttr->Value.pbData );
                for ( cRDNs = cRDN, pRDNs = pRDN; 
                      cRDNs > 0; 
                      cRDNs--, pRDNs++)
                {
                    for ( cAttrs = pRDNs->cRDNAttr, pAttrs = pRDNs->rgRDNAttr ; 
                          cAttrs > 0 ; 
                          cAttrs--, ++pAttrs )
                    {
                        if ( !(pAttrs->dwValueType & 0x80000000) &&
                             !strcmp( pAttr->pszObjId, pAttrs->pszObjId ) )
                        {
                            strcat( pVal, ";" );
                            strcat( pVal, (LPSTR)pAttrs->Value.pbData );
                            pAttrs->dwValueType |= 0x80000000;
                        }                    
                    }
                }
            }

            pName = MapAsnName( pAttr->pszObjId );
            if ( (pszFullName = (LPSTR)malloc( strlen(pszPrefix)+strlen(pName)+1 )) == NULL )
            {
                hRes = E_OUTOFMEMORY;
                goto cleanup;
            }
            strcpy( pszFullName, pszPrefix );
            strcat( pszFullName, pName );
            if ( (hRes = AddStringPair( CLCERT, 
                                        pszFullName,
                                        pVal ? pVal : (LPSTR)pAttr->Value.pbData, 
                                        pxbf,
                                        TRUE,
                                        lCodePage )) != S_OK )
            {
                if ( pVal != NULL )
                {
                    free( pVal );
                }
                goto cleanup;
            }
            if ( pVal != NULL )
            {
                free( pVal );
                pVal = NULL;
            }
            free( pszFullName );
            pszFullName = NULL;
        }
    }

cleanup:
    if ( pszFullName != NULL )
    {
        free( pszFullName );
    }

    return hRes;
}
        

/*===================================================================
CCertRequest::ParseCertificate

Function called to parse a certificate into a OA collection

Parameters:
        pspcRCI - client certificate structure

Returns:
        S_OK on success, E_OUTOFMEMORY if out of memory or E_FAIL for
    other errors
===================================================================*/

HRESULT
CCertRequest::ParseCertificate( 
    LPBYTE      pbCert,
    DWORD       cCert,
    DWORD       dwEncoding,
    DWORD       dwFlags,
    UINT        lCodePage
    )
{
    XBF                 xbf( pReq->GetCertStoreBuf(), pReq->GetCertStoreSize() );
    HRESULT             hRes = S_OK;
    PCERT_NAME_INFO     pNameInfo = NULL;
    UINT                cStore;
    UINT                x;
    LPSTR               pVal;
    PCCERT_CONTEXT      pCert;


    if (NULL == (pCert = CertCreateCertificateContext(dwEncoding,
                                                      pbCert,
                                                      cCert))) {
        ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
        hRes = E_FAIL;
        goto cleanup;
    }

    // estimate size of buffer holding values

    cStore = pCert->cbCertEncoded +          // for clear text format
             sizeof("ISSUER") +
             sizeof("BINARYISSUER") + (UUENCODEDSIZE(pCert->pCertInfo->Issuer.cbData)*sizeof(WCHAR)) +
             sizeof("BINARYSUBJECT") + (UUENCODEDSIZE(pCert->pCertInfo->Subject.cbData)*sizeof(WCHAR)) +
             sizeof("SUBJECT") + ((pCert->cbCertEncoded + 2) * 2 * sizeof(WCHAR)) +     // store fields
             sizeof("CERTIFICATE") + ((pCert->cbCertEncoded +2)* sizeof(WCHAR)) +
             sizeof("SERIALNUMBER") + (pCert->pCertInfo->SerialNumber.cbData * 3) +
             sizeof("PUBLICKEY") + ((pCert->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData+2) * sizeof(WCHAR)) +
             sizeof("VALIDFROM") + sizeof(DATE) +
             sizeof("VALIDUNTIL") + sizeof(DATE) +
             sizeof("FLAGS") + sizeof(DWORD) +
             sizeof("ENCODING") + sizeof(DWORD);
           ;

    if ( !xbf.Extend( cStore ) ) {
        ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
        hRes = E_OUTOFMEMORY;
        goto cleanup;
    }

    //
    // Build Issuer clear text format & fields collection
    //

    if ( !DecodeRdn( &pCert->pCertInfo->Issuer, &pNameInfo ) )
    {
        hRes = E_FAIL;
        goto cleanup;
    }
    pVal = xbf.ReserveRange( 0 );
    if ( !BuildRdnList( pNameInfo, &xbf, FALSE ) )
    {
        ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
        hRes = E_FAIL;
        goto cleanup;
    }
    if ( (hRes = AddStringPair( CLCERT, "ISSUER", pVal, &xbf, FALSE, lCodePage ))
         != S_OK )
    {
        goto cleanup;
    }

    if ( (hRes=AddUuBinaryPair( CLCERT, 
                                "BINARYISSUER", 
                                pCert->pCertInfo->Issuer.pbData, 
                                pCert->pCertInfo->Issuer.cbData, 
                                &xbf,
                                lCodePage ))!=S_OK ) {
        ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
        goto cleanup;
    }

    if ( (hRes=ParseRDNS( pNameInfo, "ISSUER", &xbf, lCodePage )) != S_OK ) {
        ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
        goto cleanup;
    }
    FreeDecodedRdn( pNameInfo );

    //
    // Build Subject clear text format & fields collection
    //

    if ( !DecodeRdn( &pCert->pCertInfo->Subject, &pNameInfo ) )
    {
        ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
        return E_FAIL;
    }
    pVal = xbf.ReserveRange( 0 );
    if ( !BuildRdnList( pNameInfo, &xbf, FALSE ) )
    {
        ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
        hRes = E_FAIL;
        goto cleanup;
    }
    if ( (hRes = AddStringPair( CLCERT, "SUBJECT", pVal, &xbf, FALSE, lCodePage ))
         != S_OK )
    {
        ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
        goto cleanup;
    }

    if ( (hRes=AddUuBinaryPair( CLCERT, 
                                "BINARYSUBJECT", 
                                pCert->pCertInfo->Subject.pbData, 
                                pCert->pCertInfo->Subject.cbData, 
                                &xbf,
                                lCodePage ))!=S_OK )
    {
        ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
        goto cleanup;
    }

    if ( (hRes=ParseRDNS( pNameInfo, "SUBJECT", &xbf, lCodePage )) != S_OK )
    {
        ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
        goto cleanup;
    }
    FreeDecodedRdn( pNameInfo );

    if ( (hRes=AddBinaryPair( CLCERT, "CERTIFICATE", pCert->pbCertEncoded, pCert->cbCertEncoded, &xbf, lCodePage ))!=S_OK )
    {
        ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
        goto cleanup;
    }

    //
    //  SerialNumber 
    //  The certificate's serial number. (Decoded as a multiple byte integer. 
    //  SerialNumber.pbData[0] is the least significant byte. SerialNumber.pbData[
    //  SerialNumber.cbData - 1] is the most significant byte.)
    //
    char achSerNum[128];
    UINT cbSN;

    DBG_ASSERT(pCert->pCertInfo->SerialNumber.cbData > 1);
    cbSN = pCert->pCertInfo->SerialNumber.cbData;
    if (cbSN > 0)
    {
        cbSN--;
    }
    else
    {
        ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
        goto cleanup;
    }
    
    UINT iOffSet;
    for ( x = 0, iOffSet = 0; x < pCert->pCertInfo->SerialNumber.cbData ; ++x )
    {
        iOffSet = (cbSN-x)*3;   // start with the least significant byte
        achSerNum[iOffSet] = "0123456789abcdef"[((LPBYTE)pCert->pCertInfo->SerialNumber.pbData)[x]>>4];
        achSerNum[iOffSet+1] = "0123456789abcdef"[pCert->pCertInfo->SerialNumber.pbData[x]&0x0f];
        if ( x != 0 ) {
            achSerNum[iOffSet+2] = '-';
        }
        else
        {
            achSerNum[iOffSet+2] = '\0';
        }
    }

    if ( (hRes=AddStringPair( CLCERT, "SERIALNUMBER", achSerNum, &xbf, TRUE, lCodePage ))!=S_OK )
    {
        ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
        goto cleanup;
    }

    if ( (hRes=AddBinaryPair( CLCERT, "PUBLICKEY", pCert->pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData, pCert->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData, &xbf, lCodePage ))!=S_OK )
    {
        ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
        goto cleanup;
    }

    if ( (hRes=AddDatePair( CLCERT, "VALIDFROM", &pCert->pCertInfo->NotBefore, &xbf ))!=S_OK )
    {
        ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
        goto cleanup;
    }

    if ( (hRes=AddDatePair( CLCERT, "VALIDUNTIL", &pCert->pCertInfo->NotAfter, &xbf ))!=S_OK )
    {
        ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
        goto cleanup;
    }

    if ( (hRes=AddDwordPair( CLCERT, "FLAGS", &dwFlags, &xbf ))!=S_OK )
    {
        ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
        goto cleanup;
    }

    if ( (hRes=AddDwordPair( CLCERT, "ENCODING", &dwEncoding, &xbf ))!=S_OK )
    {
        ExceptionId(IID_IRequestDictionary, IDE_REQUEST, IDE_OOM);
        goto cleanup;
    }

cleanup:
    if ( pCert )
    {
        CertFreeCertificateContext( pCert );
    }
    
    pReq->SetCertStore( xbf.QueryBuf(), xbf.QueryAllocSize() );

    xbf.Reset();

    return hRes;
}


/*===================================================================
CCertRequest::NoCertificate

Function called to create NULL certificate info into a OA collection

Parameters:
        None

Returns:
        S_OK on success, E_OUTOFMEMORY if out of memory or E_FAIL for
    other errors
===================================================================*/

HRESULT
CCertRequest::NoCertificate( 
    )
{
#if 1

    return S_OK;

#else

    XBF                 xbf( pReq->GetCertStoreBuf(), pReq->GetCertStoreSize() );
    HRESULT             hRes = S_OK;
    UINT                cStore;
    FILETIME            ft;

    // estimate size of buffer holding values

    cStore = 
             sizeof("ISSUER") + 2*sizeof(WCHAR) +
             sizeof("BINARYISSUER") + 2*sizeof(WCHAR) +
             sizeof("BINARYSUBJECT") + 2*sizeof(WCHAR) +
             sizeof("SUBJECT") + 2*sizeof(WCHAR) +
             sizeof("CERTIFICATE") + 2 * sizeof(WCHAR) +
             sizeof("SERIALNUMBER") + 2 * sizeof(WCHAR) +
             sizeof("PUBLICKEY") + 2 * sizeof(WCHAR) +
             sizeof("VALIDFROM") + sizeof(DATE) +
             sizeof("VALIDUNTIL") + sizeof(DATE)
           ;

    if ( !xbf.Extend( cStore ) )
    {
        hRes = E_OUTOFMEMORY;
        goto cleanup;
    }

    ft.dwLowDateTime = 0;
    ft.dwHighDateTime = 0;

    //
    // Build Issuer clear text format & fields collection
    //

    if ( (hRes = AddStringPair( CLCERT, "ISSUER", "", &xbf, FALSE, lCodePage ))
         != S_OK )
    {
        goto cleanup;
    }

    if ( (hRes = AddStringPair( CLCERT, "BINARYISSUER", "", &xbf, FALSE, lCodePage ))
         != S_OK )
    {
        goto cleanup;
    }

    if ( (hRes = AddStringPair( CLCERT, "SUBJECT", "", &xbf, FALSE, lCodePage ))
         != S_OK )
    {
        goto cleanup;
    }

    if ( (hRes = AddStringPair( CLCERT, "BINARYSUBJECT", "", &xbf, FALSE, lCodePage ))
         != S_OK )
    {
        goto cleanup;
    }

    if ( (hRes = AddStringPair( CLCERT, "CERTIFICATE", "", &xbf, FALSE, lCodePage ))
         != S_OK )
    {
        goto cleanup;
    }

    if ( (hRes=AddStringPair( CLCERT, "SERIALNUMBER", "", &xbf, TRUE, lCodePage ))!=S_OK )
    {
        goto cleanup;
    }

    if ( (hRes=AddStringPair( CLCERT, "PUBLICKEY", "", &xbf, TRUE, lCodePage ))!=S_OK )
    {
        goto cleanup;
    }

    if ( (hRes=AddDatePair( CLCERT, "VALIDFROM", &ft, &xbf ))!=S_OK )
    {
        goto cleanup;
    }

    if ( (hRes=AddDatePair( CLCERT, "VALIDUNTIL", &ft, &xbf ))!=S_OK )
    {
        goto cleanup;
    }

cleanup:

    pReq->SetCertStore( xbf.QueryBuf(), xbf.QueryAllocSize() );

    xbf.Reset();

    return hRes;

#endif
}


/*===================================================================
RequestSupportTerminate

Function called to initialize certificate support

Parameters:
        None

Returns:
        TRUE on success, otherwise FALSE
===================================================================*/

BOOL 
RequestSupportInit(
    )
{
    return TRUE;
}


/*===================================================================
RequestSupportTerminate

Function called to terminate certificate support

Parameters:
        None

Returns:
        Nothing
===================================================================*/

VOID
RequestSupportTerminate(
    )
{
}


HRESULT
SetVariantAsByteArray(
    VARIANT*    pvarReturn, 
    DWORD       cbLen,
    LPBYTE      pbIn 
    )
/*++

Routine Description:

    Create variant as byte array

Arguments:

    pVarReturn - ptr to created variant
    cbLen - byte count
    pbIn - byte array

Returns:

    COM status

--*/
{
    HRESULT         hr;
    SAFEARRAYBOUND  rgsabound[1];
    BYTE *          pbData = NULL;

    // Set the variant type of the output parameter

    V_VT(pvarReturn) = VT_ARRAY|VT_UI1;
    V_ARRAY(pvarReturn) = NULL;

    // Allocate a SafeArray for the data

    rgsabound[0].lLbound = 0;
    rgsabound[0].cElements = cbLen;

    V_ARRAY(pvarReturn) = SafeArrayCreate(VT_UI1, 1, rgsabound);
    if (V_ARRAY(pvarReturn) == NULL)
    {
        return E_OUTOFMEMORY;
    }

    if (FAILED(SafeArrayAccessData(V_ARRAY(pvarReturn), (void **) &pbData)))
    {
        return E_UNEXPECTED;
    }

    memcpy(pbData, pbIn, cbLen );

    SafeArrayUnaccessData(V_ARRAY(pvarReturn));

    return S_OK;
}