//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1993.
//
//  File:       olereg.cpp
//
//  Contents:   Helper routines to interrogate the reg database
//
//  Classes:
//
//  Functions:  OleRegGetUserType
//              OleRegGetMiscStatus
//              OleGetAutoConvert
//              OleSetAutoConvert
//
//  History:    dd-mmm-yy Author    Comment
//              11-Jan-94 alexgo    added VDATEHEAP macros to every function
//              30-Nov-93 alexgo    32bit port
//              11-Nov-92 jasonful  author
//
//--------------------------------------------------------------------------

#include <le2int.h>
#pragma SEG(olereg)

#include <reterr.h>
#include "oleregpv.h"
#include <ctype.h>

ASSERTDATA
#define MAX_STR 512

// Reg Db Keys
static const OLECHAR szAuxUserTypeKey[]         = OLESTR("AuxUserType");
static const OLECHAR szMiscStatusKey[]          = OLESTR("MiscStatus") ;
static const OLECHAR szProgIDKey[]              = OLESTR("ProgID");
static const OLECHAR szClsidKey[]               = OLESTR("Clsid");
static const OLECHAR szAutoConverTo[]    = OLESTR("AutoConvertTo");

// this is really a global variable
const OLECHAR szClsidRoot[]     = OLESTR("CLSID\\");


static INTERNAL OleRegGetDword
        (HKEY           hkey,
        LPCOLESTR       szKey,
        DWORD FAR*      pdw);

static INTERNAL OleRegGetDword
        (HKEY           hkey,
        DWORD           dwKey,
        DWORD FAR*      pdw);

static INTERNAL OleRegGetString
        (HKEY           hkey,
        LPCOLESTR       szKey,
        LPOLESTR FAR*   pszValue);


static  INTERNAL OleRegGetString
        (HKEY           hkey,
        DWORD           dwKey,
        LPOLESTR FAR*   pszValue);

//+-------------------------------------------------------------------------
//
//  Function:   Atol (static)
//
//  Synopsis:   Converts string to integer
//
//  Effects:
//
//  Arguments:  [sz]    -- the string
//
//  Requires:
//
//  Returns:    LONG
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              30-Nov-93 alexgo    32bit port
//
//  Notes:      32bit OLE just uses wcstol as a #define
//
//              original 16bit comment:
//
//              Replacement for stdlib atol,
//              which didn't work and doesn't take far pointers.
//              Must be tolerant of leading spaces.
//
//--------------------------------------------------------------------------
#ifndef WIN32
#pragma SEG(Atol)
FARINTERNAL_(LONG) Atol
        (LPOLESTR sz)
{
        VDATEHEAP();

        signed int      sign = +1;
        UINT            base = 10;
        LONG            l = 0;

        if (NULL==sz)
        {
                Assert (0);
                return 0;
        }
        while (isspace(*sz))
        {
                sz++;
        }

        if (*sz== OLESTR('-'))
        {
                sz++;
                sign = -1;
        }
        if (sz[0]==OLESTR('0') && sz[1]==OLESTR('x'))
        {
                base = 16;
                sz+=2;
        }

        if (base==10)
        {
                while (isdigit(*sz))
                {
                        l = l * base + *sz - OLESTR('0');
                        sz++;
                }
        }
        else
        {
                Assert (base==16);
                while (isxdigit(*sz))
                {
                        l = l * base + isdigit(*sz) ? *sz - OLESTR('0') :
                                toupper(*sz) - OLESTR('A') + 10;
                        sz++;
                }
        }
        return l * sign;
}
#endif  //!WIN32



//+-------------------------------------------------------------------------
//
//  Function:   OleRegGetDword
//
//  Synopsis:   returns the value of subkey "szKey" as a DWORD
//
//  Effects:
//
//  Arguments:  [hkey]  -- handle to a key in the regdb
//              [szKey] -- the subkey to look for
//              [pdw]   -- where to put the dword
//
//  Requires:
//
//  Returns:    HRESULT
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              30-Nov-93 alexgo    32bit port
//
//  Notes:
//
//--------------------------------------------------------------------------

#pragma SEG(OleRegGetDword)
static INTERNAL OleRegGetDword
        (HKEY           hkey,
        LPCOLESTR       szKey,
        DWORD FAR*      pdw)
{
        VDATEHEAP();

        VDATEPTRIN (pdw, DWORD);

        LPOLESTR        szLong = NULL;

        HRESULT hresult = OleRegGetString (hkey, szKey, &szLong);
        if (hresult != NOERROR)
        {
                return hresult;
        }
        *pdw = Atol (szLong);
        PubMemFree(szLong);
        return NOERROR;
}

//+-------------------------------------------------------------------------
//
//  Function:   OleRegGetDword (overloaded)
//
//  Synopsis:   Gets a dword from a sub-key given as a dword
//
//  Effects:
//
//  Arguments:  [hkey]  -- handle to a key in the regdb
//              [dwKey] -- number to convert to a string key to lookup in
//                         the regdb
//              [pdw]   -- where to put the dword
//
//  Requires:
//
//  Returns:    HRESULT
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              30-Nov-93 alexgo    32bit port
//
//  Notes:      REVIEW32:  This deep layering is kinda strange, as each
//              overloaded function is used exactly once.  It might be
//              better just to inline the stuff and be done with it.
//
//--------------------------------------------------------------------------

#pragma SEG(OleRegGetDword)
static  INTERNAL OleRegGetDword
        (HKEY           hkey,
        DWORD           dwKey,
        DWORD FAR*      pdw)
{
        VDATEHEAP();

        OLECHAR szBuf[MAX_STR];
        wsprintf(szBuf, OLESTR("%ld"), dwKey);

        return OleRegGetDword (hkey, szBuf, pdw);
}

//+-------------------------------------------------------------------------
//
//  Function:   OleRegGetString
//
//  Synopsis:   Return the value of subkey [szKey] of key [hkey] as a string
//
//  Effects:
//
//  Arguments:  [hkey]          -- a handle to a key in the reg db
//              [szKey]         -- the subkey to get the value of
//              [ppszValue]     -- where to put the value string
//
//  Requires:
//
//  Returns:    HRESULT (NOERROR, E_OUTOFMEMORY, REGDB_E_KEYMISSING)
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              01-Dec-93 alexgo    32bit port
//              15-Dec-93 ChrisWe   cb is supposed to be the size of the
//                                  buffer in bytes; changed to use sizeof()
//
//  Notes:
//
//--------------------------------------------------------------------------

#pragma SEG(OleRegGetString)
static INTERNAL OleRegGetString
        (HKEY           hkey,
        LPCOLESTR       szKey,
        LPOLESTR FAR*   ppszValue)
{
        VDATEHEAP();

        OLECHAR         szBuf [MAX_STR];
        LONG            cb = sizeof(szBuf);

        *ppszValue = NULL;

        if (ERROR_SUCCESS == RegQueryValue (hkey, (LPOLESTR) szKey,
                szBuf, &cb))
        {
                *ppszValue = UtDupString (szBuf);
                return *ppszValue ? NOERROR : ResultFromScode (E_OUTOFMEMORY);
        }
        return ReportResult(0, REGDB_E_KEYMISSING, 0, 0);
}

//+-------------------------------------------------------------------------
//
//  Function:   OleRegGetString (overloaded)
//
//  Synopsis:   Gets the string value of the DWORD subkey
//
//  Effects:
//
//  Arguments:  [hkey]          -- handle to a key in the regdb
//              [dwKey]         -- the subkey value
//              [ppszValue]     -- where to put the return value
//
//  Requires:
//
//  Returns:    HRESULT
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              01-Dec-93 alexgo    32bit port
//
//  Notes:
//
//--------------------------------------------------------------------------

static INTERNAL OleRegGetString
        (HKEY           hkey,
        DWORD           dwKey,
        LPOLESTR FAR*   ppszValue)
{
        VDATEHEAP();

        OLECHAR szBuf[MAX_STR];
        wsprintf(szBuf, OLESTR("%ld"), dwKey);

        return OleRegGetString (hkey, szBuf, ppszValue);
}

//+-------------------------------------------------------------------------
//
//  Function:   OleRegGetUserType
//
//  Synopsis:   Returns the user type name for the class id.
//
//  Effects:
//
//  Arguments:  [clsid]         -- the class ID to look up
//              [dwFormOfType]  -- flag indicating whether the fullname,
//                                 shortname, or app name is desired
//              [ppszUserType]  -- where to put the type string
//
//  Requires:   returned string must be deleted
//
//  Returns:    HRESULT (NOERROR, OLE_E_CLSID)
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              01-Nov-93 alexgo    32bit port
//
//  Notes:
//
//--------------------------------------------------------------------------

#pragma SEG(OleRegGetUserType)
STDAPI OleRegGetUserType
        (REFCLSID       clsid,
        DWORD           dwFormOfType,  // as in IOleObject::GetUserType
        LPOLESTR FAR*   ppszUserType)   // out parm
{
        OLETRACEIN((API_OleRegGetUserType, PARAMFMT("clsid= %I, dwFormOfType= %x, ppszUserType= %p"),
                        &clsid, dwFormOfType, ppszUserType));

        VDATEHEAP();

        LPOLESTR    pszTemp;
        HKEY        hkeyClsid = NULL;
        HKEY        hkeyAux   = NULL;
        HRESULT     hresult   = NOERROR;

        VDATEPTROUT_LABEL (ppszUserType, LPOLESTR, safeRtn, hresult);
        *ppszUserType = NULL;

        ErrRtnH(CoOpenClassKey(clsid, FALSE, &hkeyClsid));

        if (dwFormOfType == USERCLASSTYPE_FULL ||
                ERROR_SUCCESS != RegOpenKey (hkeyClsid, szAuxUserTypeKey,
                &hkeyAux))
        {
                // use Main User Type Name (value of key CLSID(...))
                hresult = OleRegGetString(hkeyClsid, (LPOLESTR)NULL,
                                          &pszTemp);
                if (SUCCEEDED(hresult))
                {
                    // If no user type string is registered under the class key,
                    // OleRegGetString returns NOERROR and returns an empty string.
                    // We need to check for this and return the appropriate error.
                    if ( !pszTemp[0] )
                    {
                        PubMemFree(pszTemp);
                        hresult = ResultFromScode(REGDB_E_INVALIDVALUE);
                        goto errRtn;
                    }
                    *ppszUserType = pszTemp;
                }
        }
        else
        {
                // look under key AuxUserType
                if (NOERROR !=
                        OleRegGetString (hkeyAux, dwFormOfType, ppszUserType)
                        || NULL==*ppszUserType
                        || '\0'==(*ppszUserType)[0])
                {
                        // Couldn't find the particular FormOfType requested,
                        // so use Full User Type Name (value of main
                        // CLSID key), as per spec
                        ErrRtnH (OleRegGetString (hkeyClsid, (LPOLESTR)NULL,
                                ppszUserType));
                }
        }

  errRtn:

        CLOSE (hkeyClsid);
        CLOSE (hkeyAux);

  safeRtn:
        OLETRACEOUT((API_OleRegGetUserType, hresult));

        return hresult;
}

//+-------------------------------------------------------------------------
//
//  Function:   OleRegGetMiscStatus
//
//  Synopsis:   Retrieves misc status bits from the reg db
//
//  Effects:
//
//  Arguments:  [clsid]         -- the class ID
//              [dwAspect]      -- specify the aspect (used in querrying
//                                 the reg db)
//              [pdwStatus]     -- return to return the status bits
//
//  Requires:
//
//  Returns:    HRESULT
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              01-Dec-93 alexgo    32bit port
//
//  Notes:      Uses default (0) is the MiscStatus key is missing
//
//--------------------------------------------------------------------------

#pragma SEG(OleRegGetMiscStatus)
STDAPI OleRegGetMiscStatus
        (REFCLSID       clsid,
        DWORD           dwAspect,
        DWORD FAR*      pdwStatus)
{
        OLETRACEIN((API_OleRegGetMiscStatus, PARAMFMT("clsid= %I, dwAspect= %x, pdwStatus= %p"),
                        &clsid, dwAspect, pdwStatus));

        VDATEHEAP();

        HKEY            hkeyClsid       = NULL;
        HKEY            hkeyMisc        = NULL;
        HRESULT         hresult         = NOERROR;

        VDATEPTROUT_LABEL(pdwStatus, DWORD, safeRtn, hresult);
        *pdwStatus = 0;

        ErrRtnH(CoOpenClassKey(clsid, FALSE, &hkeyClsid));

        // Open MiscStatus key
        if (ERROR_SUCCESS != RegOpenKey (hkeyClsid, szMiscStatusKey,
                &hkeyMisc))
        {
                // MiscStatus key not there, so use default.
                hresult = NOERROR;
                goto errRtn;
        }
        if (OleRegGetDword (hkeyMisc, dwAspect, pdwStatus) != NOERROR)
        {
                // Get default value from main Misc key
                ErrRtnH (OleRegGetDword (hkeyMisc,
                        (LPOLESTR)NULL, pdwStatus));
                // Got default value
        }
        // Got value for dwAspect

  errRtn:
        CLOSE (hkeyMisc);
        CLOSE (hkeyClsid);

  safeRtn:
        OLETRACEOUT((API_OleRegGetMiscStatus, hresult));

        return hresult;
}

//+-------------------------------------------------------------------------
//
//  Function:   OleGetAutoConvert
//
//  Synopsis:   Retrieves the class ID that [clsidOld] should be converted
//              to via auto convert
//
//  Effects:
//
//  Arguments:  [clsidOld]      -- the original class ID
//              [pClsidNew]     -- where to put the new convert-to class ID
//
//  Requires:
//
//  Returns:    HRESULT
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              05-Apr-94 kevinro   removed bogus assert, restructured
//              01-Dec-93 alexgo    32bit port
//
//  Notes:
//
//--------------------------------------------------------------------------

#pragma SEG(OleGetAutoConvert)
STDAPI OleGetAutoConvert(REFCLSID clsidOld, LPCLSID pClsidNew)
{
        OLETRACEIN((API_OleGetAutoConvert, PARAMFMT("clsidOld= %I, pClsidNew= %p"),
                        &clsidOld, pClsidNew));

        VDATEHEAP();

        HRESULT hresult;
        HKEY hkeyClsid = NULL;
        LPOLESTR lpszClsid = NULL;
        VDATEPTROUT_LABEL (pClsidNew, CLSID, errRtn, hresult);
        *pClsidNew = CLSID_NULL;

        hresult = CoOpenClassKey(clsidOld, FALSE, &hkeyClsid);
        if (FAILED(hresult))
        {
            goto errRtn;
        }

        hresult = OleRegGetString(hkeyClsid, szAutoConverTo, &lpszClsid);

        if (SUCCEEDED(hresult))
        {
                // Its Possible there is an AutoConvert Key under the CLSID but it has not value

                if (OLESTR('\0') == lpszClsid[0])
                {
                        hresult = REGDB_E_KEYMISSING;
                }
                else
                {
                        // convert string into CLSID
                        hresult = CLSIDFromString(lpszClsid, pClsidNew);
                }
        }

        CLOSE(hkeyClsid);
        PubMemFree(lpszClsid);

errRtn:
        OLETRACEOUT((API_OleGetAutoConvert, hresult));

        return hresult;
}

//+-------------------------------------------------------------------------
//
//  Function:   OleSetAutoConvert
//
//  Synopsis:   Sets the autoconvert information in the regdb
//
//  Effects:
//
//  Arguments:  [clsidOld]      -- the original class id
//              [clsidNew]      -- that class id that [clsidOld] should be
//                                 auto-converted to
//
//  Requires:
//
//  Returns:    HRESULT
//
//  Signals:
//
//  Modifies:
//
//  Algorithm:
//
//  History:    dd-mmm-yy Author    Comment
//              01-Dec-93 alexgo    32bit port
//
//  Notes:
//
//--------------------------------------------------------------------------

#pragma SEG(OleSetAutoConvert)
STDAPI OleSetAutoConvert(REFCLSID clsidOld, REFCLSID clsidNew)
{
        OLETRACEIN((API_OleSetAutoConvert, PARAMFMT("clsidOld= %I, clsidNew= %I"),
                                &clsidOld, &clsidNew));

        VDATEHEAP();

        HRESULT         hresult;
        HKEY            hkeyClsid = NULL;

        ErrRtnH(CoOpenClassKey(clsidOld, TRUE, &hkeyClsid));

        if (IsEqualCLSID(clsidNew, CLSID_NULL))
        {
                // ignore error since there may not be a value at present
                (void)RegDeleteKey(hkeyClsid, szAutoConverTo);
        }
        else
        {
                OLECHAR szClsid[MAX_STR];
                Verify(StringFromCLSID2(clsidNew, szClsid, sizeof(szClsid))
                        != 0);

                if (RegSetValue(hkeyClsid, szAutoConverTo, REG_SZ, szClsid,
                        _xstrlen(szClsid)) != ERROR_SUCCESS)
                {
                        hresult = ResultFromScode(REGDB_E_WRITEREGDB);
                }
        }

errRtn:
        CLOSE(hkeyClsid);

        OLETRACEOUT((API_OleSetAutoConvert, hresult));

        return hresult;
}