#include "private.h"
#ifdef UNIX
/* Convert from little endian to big endian format */
#define CONVERTLONG(a,b,c,d) (((unsigned long )a) + \
                           ((unsigned long )b << 8) + \
                           ((unsigned long )c << 16) + \
                           ((unsigned long )d << 24))
#endif /* UNIX */

//
//  Globals
//
CMimeDatabaseReg *g_pMimeDatabaseReg = NULL;

//
//  Globals
//
PRFC1766INFOA   g_pRfc1766Reg = NULL;
UINT            g_cRfc1766Reg = 0, g_cMaxRfc1766 = 0;

//
//  Functions
//
void CMimeDatabaseReg::BuildRfc1766Table(void)
{
    HKEY hKey = NULL;
    DWORD dwIndex, dwType, cInfo, cbMaxValueLen, cbLCID, cb;
    TCHAR szLCID[8], sz[MAX_RFC1766_NAME +  MAX_LOCALE_NAME + 1];

    DebugMsg(DM_TRACE, TEXT("CRfc1766::BuildRfc1766Table called."));
    EnterCriticalSection(&g_cs);
    if (NULL == g_pRfc1766Reg)
    {
        if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, REGSTR_KEY_MIME_DATABASE_RFC1766, 0, KEY_READ, &hKey))
        {
            ASSERT(NULL != hKey);
            if (ERROR_SUCCESS == RegQueryInfoKey(hKey, NULL, NULL, 0, NULL, NULL, NULL, &cInfo, &cbMaxValueLen, NULL, NULL, NULL))
            {
                g_pRfc1766Reg = (PRFC1766INFOA)LocalAlloc(LPTR, sizeof(RFC1766INFOA) * cInfo);
                if (NULL != g_pRfc1766Reg)
                {
                    g_cRfc1766Reg = 0;
                    g_cMaxRfc1766 = cInfo;
                    dwIndex = 0;
                    while (g_cRfc1766Reg < g_cMaxRfc1766)
                    {
                        LONG lRet;

                        cbLCID = ARRAYSIZE(szLCID) - 2;
                        cb = sizeof(sz);
                        lRet = RegEnumValue(hKey, dwIndex++, szLCID + 2, &cbLCID, 0, &dwType, (LPBYTE)sz, &cb);
                        if (ERROR_SUCCESS == lRet)
                        {
                            int iLCID;

                            szLCID[0] = TEXT('0');
                            szLCID[1] = TEXT('x');
                            // StrToInt
                            if (iLCID = HexToNum(szLCID + 2))
                            {
                                g_pRfc1766Reg[g_cRfc1766Reg].lcid = (LCID)iLCID;
                                if (REG_SZ == dwType)
                                {
                                    TCHAR *psz = sz;

                                    while (*psz)
                                    {
                                        if (TEXT(';') == *psz)
                                        {
                                            *psz = TEXT('\0');
                                            break;
                                        }
                                        psz = CharNext(psz);
                                    }
                                    lstrcpyn(g_pRfc1766Reg[g_cRfc1766Reg].szRfc1766, sz, MAX_RFC1766_NAME);
                                    lstrcpyn(g_pRfc1766Reg[g_cRfc1766Reg].szLocaleName, psz + 1, MAX_LOCALE_NAME);
                                    g_cRfc1766Reg++;
                                }
                            }
                        }
                        else if (ERROR_NO_MORE_ITEMS == lRet)
                            break;
                    }
                }
            }
            RegCloseKey(hKey);
        }
    }
    LeaveCriticalSection(&g_cs);
}

void CMimeDatabaseReg::FreeRfc1766Table(void)
{
    DebugMsg(DM_TRACE, TEXT("CRfc1766::FreeRfc1766Table called."));
    EnterCriticalSection(&g_cs);
    if (NULL != g_pRfc1766Reg)
    {
        LocalFree(g_pRfc1766Reg);
        g_pRfc1766Reg = NULL;
        g_cRfc1766Reg = g_cMaxRfc1766 = 0;
    }
    LeaveCriticalSection(&g_cs);
}

void CMimeDatabaseReg::EnsureRfc1766Table(void)
{
    // Ensure g_pRfc1766 is initialized
    if (NULL == g_pRfc1766Reg)
        BuildRfc1766Table();
}


STDAPI CMimeDatabaseReg::LcidToRfc1766A(LCID Locale, LPSTR pszRfc1766, int iMaxLength)
{
    UINT i;
    HRESULT hr = E_INVALIDARG;

    EnsureRfc1766Table();
    if (NULL != pszRfc1766 && 0 < iMaxLength)
    {
        for (i = 0; i < g_cRfc1766Reg; i++)
        {
            if (g_pRfc1766Reg[i].lcid == Locale)
                break;
        }
        if (i < g_cRfc1766Reg)
        {
            lstrcpyn(pszRfc1766, g_pRfc1766Reg[i].szRfc1766, iMaxLength);
            hr = S_OK;
        }
        else
        {
            TCHAR sz[MAX_RFC1766_NAME];

            if (GetLocaleInfoA(Locale, LOCALE_SABBREVLANGNAME, sz, ARRAYSIZE(sz)))
            {
                CharLowerA(sz);
                if (!lstrcmpA(sz, TEXT("cht")))
                    lstrcpynA(pszRfc1766, TEXT("zh-cn"), iMaxLength);
                else if (!lstrcmpA(sz, TEXT("chs")))
                    lstrcpynA(pszRfc1766, TEXT("zh-tw"), iMaxLength);
                else if (!lstrcmpA(sz, TEXT("jpn")))
                    lstrcpynA(pszRfc1766, TEXT("ja"), iMaxLength);
                else
                {
                    sz[2] = TEXT('\0');
                    lstrcpynA(pszRfc1766, sz, iMaxLength);
                }
                hr = S_OK;
            }
            else
                hr = E_FAIL;
        }
    }
    return hr;
}    

STDAPI CMimeDatabaseReg::LcidToRfc1766W(LCID Locale, LPWSTR pwszRfc1766, int nChar)
{
    HRESULT hr = E_INVALIDARG;

    if (NULL != pwszRfc1766 && 0 < nChar)
    {
        TCHAR sz[MAX_RFC1766_NAME];

        hr = LcidToRfc1766A(Locale, (LPSTR)sz, ARRAYSIZE(sz));
        if (S_OK == hr)
        {
            int i;

            for (i = 0; i < nChar - 1; i++)
            {
                pwszRfc1766[i] = (WCHAR)sz[i];
                if (L'\0' == pwszRfc1766[i])
                    break;
            }
            if (i == nChar - 1)
                pwszRfc1766[i] = L'\0';            
        }
    }
    return hr;
}    

STDAPI CMimeDatabaseReg::Rfc1766ToLcidA(PLCID pLocale, LPCSTR pszRfc1766)
{
    UINT i;
    HRESULT hr = E_INVALIDARG;

    EnsureRfc1766Table();
    if (NULL != pLocale && NULL != pszRfc1766)
    {
        for (i = 0; i < g_cRfc1766Reg; i++)
        {
            if (!lstrcmpi(g_pRfc1766Reg[i].szRfc1766, pszRfc1766))
                break;
        }
        if (i < g_cRfc1766Reg)
        {
            *pLocale = g_pRfc1766Reg[i].lcid;
            hr = S_OK;
        }
        else
        {
            if (2 < lstrlen(pszRfc1766))
            {
                TCHAR sz[3];

                sz[0] = pszRfc1766[0];
                sz[1] = pszRfc1766[1];
                sz[2] = TEXT('\0');
                for (i = 0; i < g_cRfc1766Reg; i++)
                {
                    if (!lstrcmpi(g_pRfc1766Reg[i].szRfc1766, sz))
                        break;                
                }
                if (i < g_cRfc1766Reg)
                {
                    *pLocale = g_pRfc1766Reg[i].lcid;
                    hr = S_FALSE;
                }
                else
                    hr = E_FAIL;
            }
            else
                hr = E_FAIL;
        }
    }
    return hr;
}

STDAPI CMimeDatabaseReg::Rfc1766ToLcidW(PLCID pLocale, LPCWSTR pwszRfc1766)
{
    HRESULT hr = E_INVALIDARG;

    if (NULL != pLocale && NULL != pwszRfc1766)
    {
        int i;
        TCHAR sz[MAX_RFC1766_NAME];

        for (i = 0; i < MAX_RFC1766_NAME - 1; i++)
        {
            sz[i] = (TCHAR)pwszRfc1766[i];
            if (TEXT('\0') == sz[i])
                break;
        }
        if (i == MAX_RFC1766_NAME -1)
            sz[i] = TEXT('\0');

        hr = Rfc1766ToLcidA(pLocale, (LPCSTR)sz);
    }
    return hr;
}


//
//  CMimeDatabase implementation
//
CMimeDatabaseReg::CMimeDatabaseReg()
{
    DebugMsg(DM_TRACE, TEXT("constructor of CMimeDatabase 0x%08x"), this);
    _pCodePage = NULL;
    _cCodePage = _cMaxCodePage = 0;
    _pCharset = NULL;
    _cCharset = _cMaxCharset = 0;
    _fAllCPCached = FALSE;
    InitializeCriticalSection(&_cs);
}

CMimeDatabaseReg::~CMimeDatabaseReg()
{
    DebugMsg(DM_TRACE, TEXT("destructor of CMimeDatabase 0x%08x"), this);
    FreeMimeDatabase();
    DeleteCriticalSection(&_cs);
}

void CMimeDatabaseReg::BuildCodePageMimeDatabase(void)
{
    HKEY hKey = NULL;
    DWORD cInfo, cbMaxSubKeyLen;

    DebugMsg(DM_TRACE, TEXT("CMimeDatabase::BuildCodePageMimeDatabase called."));
    // Open CodePage Mime Database Key
    if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, REGSTR_KEY_MIME_DATABASE_CODEPAGE, 0, KEY_READ, &hKey))
    {
        ASSERT(NULL != hKey);
        if (ERROR_SUCCESS == RegQueryInfoKey(hKey, NULL, NULL, 0, &cInfo, &cbMaxSubKeyLen, NULL, NULL, NULL, NULL, NULL, NULL))
        {
            if (NULL == _pCodePage)
            {
                _pCodePage = (PMIMECPINFO)LocalAlloc(LPTR, sizeof(MIMECPINFO) * cInfo);
                if (NULL != _pCodePage)
                    _cMaxCodePage = cInfo;

            }
        }
        RegCloseKey(hKey);
        hKey = NULL;
    }

}

void CMimeDatabaseReg::BuildCharsetMimeDatabase(void)
{
    HKEY hKey = NULL;
    DWORD cInfo, cbMaxSubKeyLen;

    DebugMsg(DM_TRACE, TEXT("CMimeDatabase::BuildCharsetMimeDatabase called."));
    // Open Charset Mime Database Key
    if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, REGSTR_KEY_MIME_DATABASE_CHARSET, 0, KEY_READ, &hKey))
    {
        ASSERT(NULL != hKey);
        if (ERROR_SUCCESS == RegQueryInfoKey(hKey, NULL, NULL, 0, &cInfo, &cbMaxSubKeyLen, NULL, NULL, NULL, NULL, NULL, NULL))
        {
            if (NULL == _pCharset)
            {
                _pCharset = (PMIMECSETINFO)LocalAlloc(LPTR, sizeof(MIMECSETINFO) * cInfo);
                if (NULL != _pCharset)
                    _cMaxCharset = cInfo;
            }
        }
        RegCloseKey(hKey);
        hKey = NULL;
    }
}

void CMimeDatabaseReg::FreeMimeDatabase(void)
{
    DebugMsg(DM_TRACE, TEXT("CMimeDatabase::FreeMimeDatabase called."));
    EnterCriticalSection(&_cs);
    if (NULL != _pCodePage)
    {
        LocalFree(_pCodePage);
        _pCodePage = NULL;
        _cCodePage = _cMaxCodePage = 0;
    }
    if (NULL != _pCharset)
    {
        LocalFree(_pCharset);
        _pCharset = NULL;
        _cCharset = _cMaxCharset = 0;
    }
    LeaveCriticalSection(&_cs);
    FreeRfc1766Table();
}

STDAPI CMimeDatabaseReg::EnumCodePageInfo(void)
{
    HKEY hKey = NULL;
    DWORD dwIndex = 0;
    MIMECPINFO CPInfo;
    TCHAR szCodePage[15];
    HRESULT hr = S_OK;

    DebugMsg(DM_TRACE, TEXT("CMimeDatabase::EnumCodePageInfo called."));
    EnterCriticalSection(&_cs);
    if (FALSE == _fAllCPCached)
    {
        if (NULL == _pCodePage)
            BuildCodePageMimeDatabase();
        if (_pCodePage)
        {
            // Open CodePage Mime Database Key
            if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, REGSTR_KEY_MIME_DATABASE_CODEPAGE, 0, KEY_READ, &hKey))
            {
                ASSERT(NULL != hKey);
                while (ERROR_SUCCESS == RegEnumKey(hKey, dwIndex++, szCodePage, ARRAYSIZE(szCodePage)))
                {
                    UINT uiCodePage = MLStrToInt(szCodePage);
    
                    if (0 <= FindCodePageFromCache(uiCodePage))
                        continue;

                    if (TRUE == FindCodePageFromRegistry(uiCodePage, &CPInfo))
                    {
                        _pCodePage[_cCodePage] = CPInfo;
                        _cCodePage++;
                    }
                }
                _fAllCPCached = TRUE;
                RegCloseKey(hKey);
                hKey = NULL;
            }
            if (0 < _cCodePage)
                QSortCodePageInfo(0, _cCodePage-1);

            // Fill empty font face field base on its FamilyCodePage
            for (UINT i = 0; i < _cCodePage; i++)
            {
                UINT uiFamily;
                WCHAR wszFixed[MAX_MIMEFACE_NAME], wszProp[MAX_MIMEFACE_NAME];

                uiFamily = 0;
                wszFixed[0] = wszProp[0] = TEXT('\0');

                if (TEXT('\0') == _pCodePage[i].wszFixedWidthFont[0] || TEXT('\0') == _pCodePage[i].wszProportionalFont[0])
                {
                    if (uiFamily != _pCodePage[i].uiFamilyCodePage)
                    {
                        for (UINT j = 0; j < _cCodePage; j++)
                        {
                            if (_pCodePage[i].uiFamilyCodePage == _pCodePage[j].uiCodePage)
                            {
                                uiFamily = _pCodePage[j].uiCodePage;
                                MLStrCpyNW(wszFixed, _pCodePage[j].wszFixedWidthFont, MAX_MIMEFACE_NAME);
                                MLStrCpyNW(wszProp, _pCodePage[j].wszProportionalFont, MAX_MIMEFACE_NAME);
                                break;
                            }
                        }                
                    }
                    MLStrCpyNW(_pCodePage[i].wszFixedWidthFont, wszFixed, MAX_MIMEFACE_NAME);
                    MLStrCpyNW(_pCodePage[i].wszProportionalFont, wszProp, MAX_MIMEFACE_NAME);
                }
            }
        }
        else
        {
            hr = E_OUTOFMEMORY;
        }
    }
    LeaveCriticalSection(&_cs);

    return hr;
}

STDAPI CMimeDatabaseReg::GetNumberOfCodePageInfo(UINT *pcCodePage)
{
    EnterCriticalSection(&_cs);
    if (NULL == _pCodePage)
        BuildCodePageMimeDatabase();
    *pcCodePage = _cMaxCodePage;
    LeaveCriticalSection(&_cs);
    return NOERROR;
}

STDAPI CMimeDatabaseReg::GetCodePageInfo(UINT uiCodePage, PMIMECPINFO pcpInfo)
{
    int idx;
    HRESULT hr = E_FAIL;

    DebugMsg(DM_TRACE, TEXT("CMimeDatabase::GetCodePageInfo called."));
    if (NULL != pcpInfo)
    {
        EnterCriticalSection(&_cs);
        if (NULL == _pCodePage)
            BuildCodePageMimeDatabase();

        if (_pCodePage)
        {
            idx = FindCodePageFromCache(uiCodePage);
            if (0 > idx)
            {
                MIMECPINFO CPInfo = {0};

                if (TRUE == FindCodePageFromRegistry(uiCodePage, &CPInfo))
                {
                    if (CPInfo.uiCodePage != CPInfo.uiFamilyCodePage)
                    {
                        idx = FindCodePageFromCache(CPInfo.uiFamilyCodePage);
                        if (0 > idx)
                        {
                            MIMECPINFO FamilyCPInfo;

                            if (TRUE == FindCodePageFromRegistry(CPInfo.uiFamilyCodePage, &FamilyCPInfo))
                            {
                                idx = _cCodePage;
                                _pCodePage[_cCodePage] = FamilyCPInfo;
                                _cCodePage++;
                            }
                        }
                        MLStrCpyNW(CPInfo.wszFixedWidthFont, _pCodePage[idx].wszFixedWidthFont, MAX_MIMEFACE_NAME);
                        MLStrCpyNW(CPInfo.wszProportionalFont, _pCodePage[idx].wszProportionalFont, MAX_MIMEFACE_NAME);
                    }
                    _pCodePage[_cCodePage] = CPInfo;
                    _cCodePage++;
                    QSortCodePageInfo(0, _cCodePage-1);
                    idx = FindCodePageFromCache(uiCodePage);
                }
            }
            if (0 <= idx)
            {
                *pcpInfo = _pCodePage[idx];
                hr = S_OK;
            }
            LeaveCriticalSection(&_cs);
        }
    }
    return hr;
}

int CMimeDatabaseReg::FindCodePageFromCache(UINT uiCodePage)
{
    UINT i;
    int iRet = -1;

    for (i = 0; i < _cCodePage; i++)
    {
        if (_pCodePage[i].uiCodePage == uiCodePage)
        {
            iRet = i;
            break;
        }
    }
    return iRet;
}

BOOL CMimeDatabaseReg::FindCodePageFromRegistry(UINT uiCodePage, PMIMECPINFO pcpInfo)
{
    HKEY hKey;
    DWORD dw, cb;
    TCHAR szKey[256],  sz[MAX_MIMECP_NAME];
    BOOL fRet = FALSE;

    wsprintf(szKey, TEXT("%s\\%d"), REGSTR_KEY_MIME_DATABASE_CODEPAGE, uiCodePage);
    if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, szKey, 0, KEY_READ, &hKey))
    {
        TCHAR *psz, *pszComma;
        CHARSETINFO rCharsetInfo;

        pcpInfo->uiCodePage = uiCodePage;

        cb = sizeof(dw);
        if (ERROR_SUCCESS == RegQueryValueEx(hKey, REGSTR_VAL_FAMILY, 0, NULL, (LPBYTE)&dw, &cb))
            pcpInfo->uiFamilyCodePage = (UINT)dw;
        else
            pcpInfo->uiFamilyCodePage = pcpInfo->uiCodePage;

        cb = sizeof(dw);
        if (ERROR_SUCCESS == RegQueryValueEx(hKey, REGSTR_VAL_LEVEL, 0, NULL, (LPBYTE)&dw, &cb))
#ifdef UNIX
        {
           BYTE* px = (BYTE*)&dw;
           pcpInfo->dwFlags = CONVERTLONG(px[0], px[1], px[2], px[3]);
        }
#else
            pcpInfo->dwFlags = dw;
#endif /* UNIX */
        else
            pcpInfo->dwFlags = 0;

        cb = sizeof(sz);
        if (ERROR_SUCCESS == RegQueryValueEx(hKey, REGSTR_VAL_DESCRIPTION, NULL, NULL, (LPBYTE)sz, &cb))
            MultiByteToWideChar(CP_ACP, 0, sz, -1, pcpInfo->wszDescription, ARRAYSIZE(pcpInfo->wszDescription));
        else
        {
            TCHAR szDef[MAX_MIMECP_NAME];

            LoadString(g_hInst, IDS_MIME_LANG_DEFAULT, szDef, ARRAYSIZE(szDef));
            wsprintf(sz, szDef, pcpInfo->uiCodePage);
            MultiByteToWideChar(CP_ACP, 0, sz, -1, pcpInfo->wszDescription, ARRAYSIZE(pcpInfo->wszDescription));
        }

        cb = sizeof(sz);
        if (ERROR_SUCCESS == RegQueryValueEx(hKey, REGSTR_VAL_FIXEDWIDTHFONT, NULL, NULL, (LPBYTE)sz, &cb))
        {
            psz = sz;
            pszComma =  MLStrChr(sz, TEXT(','));
            if (NULL != pszComma)               // If there are multiple font name
            {
                if (uiCodePage != g_uACP)
                    psz = pszComma + 1;         // Take right side(English) fontname for non-native codepage info
                else
                    *pszComma = TEXT('\0');     // Take left side(DBCS) fontname for native codepage info
            }
            if (lstrlen(psz) >= MAX_MIMEFACE_NAME)
                psz[MAX_MIMEFACE_NAME-1] = TEXT('\0');
            MultiByteToWideChar(CP_ACP, 0, psz, -1, pcpInfo->wszFixedWidthFont, ARRAYSIZE(pcpInfo->wszFixedWidthFont));
        }
        else
            pcpInfo->wszFixedWidthFont[0] = L'\0';

        cb = sizeof(sz);
        if (ERROR_SUCCESS == RegQueryValueEx(hKey, REGSTR_VAL_PROPORTIONALFONT, NULL, NULL, (LPBYTE)sz, &cb))
        {
            psz = sz;
            pszComma = MLStrChr(sz, TEXT(','));
            if (NULL != pszComma)               // If there are multiple font name
            {
                if (uiCodePage != g_uACP)
                    psz = pszComma + 1;         // Take right side(English) fontname for non-native codepage info
                else
                    *pszComma = TEXT('\0');     // Take left side(DBCS) fontname for native codepage info
            }
            if (lstrlen(psz) >= MAX_MIMEFACE_NAME)
                psz[MAX_MIMEFACE_NAME-1] = TEXT('\0');
            MultiByteToWideChar(CP_ACP, 0, psz, -1, pcpInfo->wszProportionalFont, ARRAYSIZE(pcpInfo->wszProportionalFont));
        }
        else
            pcpInfo->wszProportionalFont[0] = L'\0';

        cb = sizeof(sz);
        if (ERROR_SUCCESS == RegQueryValueEx(hKey, REGSTR_VAL_BODYCHARSET, NULL, NULL, (LPBYTE)sz, &cb))
            MultiByteToWideChar(CP_ACP, 0, sz, -1, pcpInfo->wszBodyCharset, ARRAYSIZE(pcpInfo->wszBodyCharset));
        else
            pcpInfo->wszBodyCharset[0] = L'\0';

        cb = sizeof(sz);
        if (ERROR_SUCCESS == RegQueryValueEx(hKey, REGSTR_VAL_HEADERCHARSET, NULL, NULL, (LPBYTE)sz, &cb))
            MultiByteToWideChar(CP_ACP, 0, sz, -1, pcpInfo->wszHeaderCharset, ARRAYSIZE(pcpInfo->wszHeaderCharset));
        else
            MLStrCpyNW(pcpInfo->wszHeaderCharset, pcpInfo->wszBodyCharset, MAX_MIMECSET_NAME);

        cb = sizeof(sz);
        if (ERROR_SUCCESS == RegQueryValueEx(hKey, REGSTR_VAL_WEBCHARSET, NULL, NULL, (LPBYTE)sz, &cb))
            MultiByteToWideChar(CP_ACP, 0, sz, -1, pcpInfo->wszWebCharset, ARRAYSIZE(pcpInfo->wszWebCharset));
        else
            MLStrCpyNW(pcpInfo->wszWebCharset, pcpInfo->wszBodyCharset, MAX_MIMECSET_NAME);

        cb = sizeof(sz);
        if (ERROR_SUCCESS == RegQueryValueEx(hKey, REGSTR_VAL_PRIVCONVERTER, NULL, NULL, (LPBYTE)sz, &cb))
            pcpInfo->dwFlags |= MIMECONTF_PRIVCONVERTER;

        if (0 != TranslateCharsetInfo(IntToPtr_(LPDWORD, pcpInfo->uiFamilyCodePage), &rCharsetInfo, TCI_SRCCODEPAGE))
            pcpInfo->bGDICharset = (BYTE)rCharsetInfo.ciCharset;
        else
            pcpInfo->bGDICharset = DEFAULT_CHARSET;


        if (1200 == pcpInfo->uiFamilyCodePage || 50000 == pcpInfo->uiFamilyCodePage || TRUE == _IsValidCodePage(pcpInfo->uiFamilyCodePage)) // 50000 means user defined
        {
            if (TRUE == CheckFont(pcpInfo->bGDICharset))
            {
                if (pcpInfo->uiCodePage == pcpInfo->uiFamilyCodePage || TRUE == _IsValidCodePage(pcpInfo->uiCodePage))
                    pcpInfo->dwFlags |= MIMECONTF_VALID|MIMECONTF_VALID;
                else if (S_OK == IsConvertINetStringAvailable(pcpInfo->uiCodePage, pcpInfo->uiFamilyCodePage))
                    pcpInfo->dwFlags |= MIMECONTF_VALID|MIMECONTF_VALID;
            }
            else
            {
                if (pcpInfo->uiCodePage == pcpInfo->uiFamilyCodePage || TRUE == _IsValidCodePage(pcpInfo->uiCodePage))
                    pcpInfo->dwFlags |= MIMECONTF_VALID_NLS;
                else if (S_OK == IsConvertINetStringAvailable(pcpInfo->uiCodePage, pcpInfo->uiFamilyCodePage))
                    pcpInfo->dwFlags |= MIMECONTF_VALID_NLS;
            }

        }
        RegCloseKey(hKey);
        fRet = TRUE;
    }
    return fRet;
}

STDAPI CMimeDatabaseReg::GetCodePageInfoWithIndex(UINT uidx, PMIMECPINFO pcpInfo)
{
    HRESULT hr = NOERROR;

    DebugMsg(DM_TRACE, TEXT("CMimeDatabase::GetCodePageInfoWithIndex called."));
    EnterCriticalSection(&_cs);
    if (NULL == _pCodePage)
        BuildCodePageMimeDatabase();
    if (uidx < _cCodePage && _pCodePage)
        *pcpInfo = _pCodePage[uidx];
    else
        hr = E_FAIL;
    LeaveCriticalSection(&_cs);
    return hr;
}

STDAPI CMimeDatabaseReg::GetCharsetInfo(BSTR Charset, PMIMECSETINFO pcsetInfo)
{
    int idx;
    HRESULT hr = E_FAIL;

    DebugMsg(DM_TRACE, TEXT("CMimeDatabase::GetCharsetInfo called."));
    if (NULL != pcsetInfo)
    {
        EnterCriticalSection(&_cs);
        if (NULL == _pCharset)
            BuildCharsetMimeDatabase();
        if (_pCharset)
        {
            idx = FindCharsetFromCache(Charset);
            if (0 > idx)
                idx = FindCharsetFromRegistry(Charset, FALSE);
            if (0 <= idx)
            {
                *pcsetInfo = _pCharset[idx];
                hr = S_OK;
            }
        }
        LeaveCriticalSection(&_cs);
    }
    return hr;
}

int CMimeDatabaseReg::FindCharsetFromCache(BSTR Charset)
{
    int iStart, iEnd, iMiddle, iCmpResult, iRet = -1;

    iStart = 0;
    iEnd = _cCharset - 1;
    while (iStart <= iEnd)
    {
        iMiddle = (iStart + iEnd) / 2;
        iCmpResult = MLStrCmpIW(Charset, _pCharset[iMiddle].wszCharset);
        if (iCmpResult < 0)
            iEnd = iMiddle - 1;
        else if (iCmpResult > 0)
            iStart = iMiddle + 1;
        else
        {
            iRet = iMiddle;
            break;
        }
    }
    return iRet;
}

int CMimeDatabaseReg::FindCharsetFromRegistry(BSTR Charset, BOOL fFromAlias)
{
    HKEY hKey;
    TCHAR szKey[256], szCharset[MAX_MIMECSET_NAME];
    int iRet = -1;

    WideCharToMultiByte(CP_ACP, 0, Charset, -1, szCharset, ARRAYSIZE(szCharset), NULL, NULL);
    lstrcpy(szKey, REGSTR_KEY_MIME_DATABASE_CHARSET);
    lstrcat(szKey, TEXT("\\"));
    lstrcat(szKey, szCharset);
    if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, szKey, 0, KEY_READ, &hKey))
    {
        DWORD cb, dw;
        TCHAR sz[MAX_MIMECSET_NAME];
        WCHAR wsz[MAX_MIMECSET_NAME];

        cb = sizeof(sz);
        if (FALSE == fFromAlias && ERROR_SUCCESS == RegQueryValueEx(hKey, REGSTR_VAL_ALIASTO, NULL, NULL, (LPBYTE)sz, &cb))
        {
            MultiByteToWideChar(CP_ACP, 0, sz, -1, wsz, ARRAYSIZE(wsz));
            iRet = FindCharsetFromCache(wsz);
            if (0 > iRet)
                iRet = FindCharsetFromRegistry(wsz, TRUE);
            if (0 <= iRet)
            {
                MLStrCpyNW(_pCharset[_cCharset].wszCharset, Charset, MAX_MIMECSET_NAME);
                _pCharset[_cCharset].uiCodePage = _pCharset[iRet].uiCodePage;
                _pCharset[_cCharset].uiInternetEncoding = _pCharset[iRet].uiInternetEncoding;
                _cCharset++;
                QSortCharsetInfo(0, _cCharset-1);
                iRet = FindCharsetFromCache(Charset);
            }
        }
        else
        {
            MLStrCpyNW(_pCharset[_cCharset].wszCharset, Charset, MAX_MIMECSET_NAME);
            cb = sizeof(dw);
            if (ERROR_SUCCESS == RegQueryValueEx(hKey, REGSTR_VAL_CODEPAGE, 0, NULL, (LPBYTE)&dw, &cb))
            {
                _pCharset[_cCharset].uiCodePage = (UINT)dw;
                cb = sizeof(dw);
                if (ERROR_SUCCESS == RegQueryValueEx(hKey, REGSTR_VAL_INETENCODING, 0, NULL, (LPBYTE)&dw, &cb))
                {
                    _pCharset[_cCharset].uiInternetEncoding = (UINT)dw;
                    _cCharset++;
                    QSortCharsetInfo(0, _cCharset-1);
                    iRet = FindCharsetFromCache(Charset);
                }
            }
        }
        RegCloseKey(hKey);
    }
    return iRet;
}


BOOL CMimeDatabaseReg::CheckFont(BYTE bGDICharset)
{
    BOOL fRet = FALSE;

    DebugMsg(DM_TRACE, TEXT("CMimeDatabase::CheckFont called."));
    if (DEFAULT_CHARSET == bGDICharset)
        fRet = TRUE;
    else
    {
        HDC     hDC;
        LOGFONT lf;
        HWND    hWnd;

        hWnd = GetTopWindow(GetDesktopWindow());
        hDC = GetDC(hWnd);

        if (NULL != hDC)
        {
            lf.lfFaceName[0] = TEXT('\0');
            lf.lfPitchAndFamily = 0;
            lf.lfCharSet = bGDICharset;
            EnumFontFamiliesEx(hDC, &lf, (FONTENUMPROC)EnumFontFamExProc, (LPARAM)&fRet, 0);
        }
        ReleaseDC(hWnd, hDC);
    }
    return fRet;
}

void CMimeDatabaseReg::QSortCodePageInfo(LONG left, LONG right)
{
    register LONG i, j;
    WCHAR k[MAX_MIMECP_NAME];
    MIMECPINFO t;

    DebugMsg(DM_TRACE, TEXT("CMimeDatabase::QSortCodePageInfo called."));
    i = left;
    j = right;
    MLStrCpyW(k, _pCodePage[(left + right) / 2].wszDescription);

    do  
    {
        while(MLStrCmpIW(_pCodePage[i].wszDescription, k) < 0 && i < right)
            i++;
        while (MLStrCmpIW(_pCodePage[j].wszDescription, k) > 0 && j > left)
            j--;

        if (i <= j)
        {
            t = _pCodePage[i];
            _pCodePage[i] = _pCodePage[j];
            _pCodePage[j] = t;
            i++; j--;
        }

    } while (i <= j);

    if (left < j)
        QSortCodePageInfo(left, j);
    if (i < right)
        QSortCodePageInfo(i, right);
}

void CMimeDatabaseReg::QSortCharsetInfo(LONG left, LONG right)
{
    register LONG i, j;
    WCHAR k[MAX_MIMECSET_NAME];
    MIMECSETINFO t;

    DebugMsg(DM_TRACE, TEXT("CMimeDatabase::QSortCharsetInfo called."));
    i = left;
    j = right;
    MLStrCpyW(k, _pCharset[(left + right) / 2].wszCharset);

    do  
    {
        while(MLStrCmpIW(_pCharset[i].wszCharset, k) < 0 && i < right)
            i++;
        while (MLStrCmpIW(_pCharset[j].wszCharset, k) > 0 && j > left)
            j--;

        if (i <= j)
        {
            t = _pCharset[i];
            _pCharset[i] = _pCharset[j];
            _pCharset[j] = t;
            i++; j--;
        }

    } while (i <= j);

    if (left < j)
        QSortCharsetInfo(left, j);
    if (i < right)
        QSortCharsetInfo(i, right);
}

// validates all cps that are in the same
// family of the given codepage
STDAPI CMimeDatabaseReg::ValidateCP(UINT uiCodePage)
{
    UINT i;

    if (NULL == _pCodePage)
        BuildCodePageMimeDatabase();
    //
    // just look into already cached codepages
    // 
    for (i = 0; i < _cCodePage; i++)
    {
        if (_pCodePage[i].uiFamilyCodePage == uiCodePage)
            _pCodePage[i].dwFlags |=  MIMECONTF_VALID|MIMECONTF_VALID_NLS;
    }
        
    return S_OK; // never fail?
}