//*********************************************************************
//*                  Microsoft Windows                               **
//*            Copyright(c) Microsoft Corp., 1995                    **
//*********************************************************************
//
//    HELPERS.CPP
//
//    HISTORY:
//    
//    9/01/95        philco        Created.
//
//

#include <cdlpch.h>

BOOL GetEXEName(LPSTR szCmdLine);


///////***********       Helper functions        *********/////////////


/*******************************************************************

    NAME:        Unicode2Ansi
        
    SYNOPSIS:    Converts a unicode widechar string to ansi (MBCS)

    NOTES:        Caller must free out parameter using delete
                    
********************************************************************/
HRESULT Unicode2Ansi(const wchar_t *src, char ** dest)
{
    if ((src == NULL) || (dest == NULL))
        return E_INVALIDARG;

    // find out required buffer size and allocate it.
    int len = WideCharToMultiByte(CP_ACP, 0, src, -1, NULL, 0, NULL, NULL);
    *dest = new char [len*sizeof(char)];
    if (!*dest)
        return E_OUTOFMEMORY;

    // Now do the actual conversion
    if ((WideCharToMultiByte(CP_ACP, 0, src, -1, *dest, len*sizeof(char), 
                                                            NULL, NULL)) != 0)
        return S_OK; 
    else
        return E_FAIL;
}


/*******************************************************************

    NAME:        Ansi2Unicode
        
    SYNOPSIS:    Converts an ansi (MBCS) string to unicode.

    Notes:        Caller must free out parameter using delete
                        
********************************************************************/
HRESULT Ansi2Unicode(const char * src, wchar_t **dest)
{
    if ((src == NULL) || (dest == NULL))
        return E_INVALIDARG;

    // find out required buffer size and allocate it
    int len = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, src, -1, NULL, 0);
    *dest = new WCHAR [len*sizeof(WCHAR)];
    if (!*dest)
        return E_OUTOFMEMORY;

    // Do the actual conversion.
    if ((MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, src, -1, *dest, 
                                                    len*sizeof(wchar_t))) != 0)
        return S_OK; 
    else
        return E_FAIL;
}


/*******************************************************************

    NAME:        ConvertANSItoCLSID
        
    SYNOPSIS:    Converts an ANSI string to a CLSID structure (address
                of which is passed by caller as an [out] parameter)
                    
********************************************************************/
HRESULT ConvertANSItoCLSID(const char *pszCLSID, CLSID * clsid)
{
    ASSERT(pszCLSID != NULL);
    ASSERT(clsid != NULL);

    HRESULT hr = S_OK;
    LPOLESTR wcstr = NULL;

    // Since OLE is Unicode only, we need to convert pszClsid to Unicode.
    hr = Ansi2Unicode(pszCLSID, &wcstr);
    if (FAILED(hr))
        goto cleanup;

    // Get CLSID from string
    hr = CLSIDFromString(wcstr, clsid);
    if (FAILED(hr))
        goto cleanup;

cleanup:
    if (wcstr != NULL) 
        delete wcstr;   // Delete unicode string.  We're done.
    return hr;
}

/*******************************************************************

    NAME:        ConvertFriendlyANSItoCLSID
        
    SYNOPSIS:    Works like ConvertANSItoCLSID but allows prefix
                 of "clsid:" CLSID is passed as [out]
                 parameter.
   
********************************************************************/
HRESULT ConvertFriendlyANSItoCLSID(char *pszCLSID, CLSID * clsid)
{
    static const char *szClsid = "clsid:";
    ASSERT(pszCLSID != NULL);
    ASSERT(clsid != NULL);
    
    HRESULT hr = S_OK;

    // try OLE form.
    hr = ConvertANSItoCLSID(pszCLSID,clsid);
    if (SUCCEEDED(hr))
        goto cleanup;

    // try for prefix "clsid:"

    if (StrCmpNA(pszCLSID, szClsid, lstrlenA(szClsid)) == 0) {

        hr = ConvertANSItoCLSID(pszCLSID + lstrlenA(szClsid), clsid);
        if (SUCCEEDED(hr))
            goto cleanup;

        // construct COM form of clsid:
        LPSTR szTmp = NULL;
        szTmp = new char[lstrlenA(pszCLSID) - lstrlenA(szClsid) + 2 + 1];
        if (!szTmp) {
            hr = E_OUTOFMEMORY;
            goto cleanup;
        }
        lstrcpyA(szTmp,"{");
        lstrcatA(szTmp,pszCLSID + lstrlenA(szClsid));
        lstrcatA(szTmp,"}");

        hr = ConvertANSItoCLSID(szTmp,clsid);
        delete [] szTmp;
        if (SUCCEEDED(hr))
            goto cleanup;
    }
    
    // error code passes through
    
cleanup:

    return hr;
}

// ---------------------------------------------------------------------------
// %%Function: GetExtnAndBaseFileName
//
// Parameters:
//    in szName: a filename or URL
//  out update pbasefilename to point into this szName
//
//    Returns:
//    out: extn
// ---------------------------------------------------------------------------
FILEXTN GetExtnAndBaseFileName( char *szName, char **plpBaseFileName)
{
    char *pCur = szName;
    char *pExt = NULL;
    char szExtCopy[4];
    FILEXTN extn = FILEXTN_UNKNOWN;

    const static char *szCAB = "CAB";
    const static char *szINF = "INF";
    const static char *szDLL = "DLL";
    const static char *szOCX = "OCX";
    const static char *szEXE = "EXE";
    const static char *szOSD = "OSD";
    const static char *szCAT = "CAT";

    *plpBaseFileName = szName;

    // find location of last '.' and '/' in name
    for (;*pCur; *pCur++) {
        if (*pCur == '.')
            pExt = pCur+1;
        if ((*pCur == '/') || (*pCur == '\\'))
            *plpBaseFileName = pCur+1;
    }

    if (!pExt || !*pExt) { // if no '.' or as last char
        extn = FILEXTN_NONE;
        goto Exit;
    }

    StrNCpy(szExtCopy, pExt, 4);

    CharUpperBuff(szExtCopy, 3);

    if (lstrcmp(szExtCopy, szCAB)     == 0) {
        extn = FILEXTN_CAB;
        goto Exit;
    }

    if (lstrcmp(szExtCopy, szOCX)     == 0) {
        extn = FILEXTN_OCX;
        goto Exit;
    }

    if (lstrcmp(szExtCopy, szDLL)     == 0) {
        extn = FILEXTN_DLL;
        goto Exit;
    }

    if (lstrcmp(szExtCopy, szEXE)     == 0) {
        extn = FILEXTN_EXE;
        goto Exit;
    }

    if (lstrcmp(szExtCopy, szINF)     == 0) {
        extn = FILEXTN_INF;
        goto Exit;
    }

    if (lstrcmp(szExtCopy, szOSD)     == 0) {
        extn = FILEXTN_OSD;
        goto Exit;
    }

    if (lstrcmp(szExtCopy, szCAT)     == 0) {
        extn = FILEXTN_CAT;
        goto Exit;
    }

Exit:

    return extn;
}

// ---------------------------------------------------------------------------
// %%Function: GetVersionFromString
//
//    converts version in text format (a,b,c,d) into two dwords (a,b), (c,d)
//    The printed version number is of format a.b.d (but, we don't care)
// ---------------------------------------------------------------------------
HRESULT
GetVersionFromString(const char *szBuf, LPDWORD pdwFileVersionMS, LPDWORD pdwFileVersionLS, char cSeperator)
{
    // cdl.h defines cSeperator as a default parameter: ','
    const char *pch = szBuf;
    char ch;
    char szMaxString[30];

    *pdwFileVersionMS = 0;
    *pdwFileVersionLS = 0;

    if (!pch)            // default to zero if none provided
        return S_OK;
    wsprintf(szMaxString, "-1%c-1%c-1%c-1", cSeperator, cSeperator, cSeperator);

    if (lstrcmp(pch, szMaxString) == 0) {
        *pdwFileVersionMS = 0xffffffff;
        *pdwFileVersionLS = 0xffffffff;
        return S_OK;
    }

    USHORT n = 0;

    USHORT a = 0;
    USHORT b = 0;
    USHORT c = 0;
    USHORT d = 0;

    enum HAVE { HAVE_NONE, HAVE_A, HAVE_B, HAVE_C, HAVE_D } have = HAVE_NONE;


    for (ch = *pch++;;ch = *pch++) {

        if ((ch == cSeperator) || (ch == '\0')) {

            switch (have) {

            case HAVE_NONE:
                a = n;
                have = HAVE_A;
                break;

            case HAVE_A:
                b = n;
                have = HAVE_B;
                break;

            case HAVE_B:
                c = n;
                have = HAVE_C;
                break;

            case HAVE_C:
                d = n;
                have = HAVE_D;
                break;

            case HAVE_D:
                return E_INVALIDARG; // invalid arg
            }

            if (ch == '\0') {
                // all done convert a,b,c,d into two dwords of version

                *pdwFileVersionMS = ((a << 16)|b);
                *pdwFileVersionLS = ((c << 16)|d);

                return S_OK;
            }

            n = 0; // reset

        } else if ( (ch < '0') || (ch > '9'))
            return E_INVALIDARG;    // invalid arg
        else
            n = n*10 + (ch - '0');


    } /* end forever */

    // NEVERREACHED
}

//************************************************************
// CheckFileImplementsCLSID
//
// checks if a clsid is implemented by the given file
// Return S_OK on success.
//
// pszFileName:  Full path to the file whose clsid we
//               desire.
//  rClsid:    clsid to check
//

HRESULT CheckFileImplementsCLSID(const char *pszFileName, REFCLSID rClsid)
{
    LPTYPELIB ptlib = NULL;
    LPTYPEINFO lpTypeInfo = NULL;
    LPTYPEATTR lpTypeAttr = NULL;
    WCHAR puszFileName[MAX_PATH];  // unicode string
    HRESULT hr = S_OK;

    //
    // LoadTypeLib() needs the string in unicode.
    //
    MultiByteToWideChar(CP_ACP, 0, pszFileName, -1, puszFileName, sizeof(puszFileName) / sizeof(wchar_t));

    hr = LoadTypeLib(puszFileName, &ptlib);

    if (FAILED(hr))
        return hr;

    hr = ptlib->GetTypeInfoOfGuid(rClsid, &lpTypeInfo);

    if (SUCCEEDED(hr)) {

        if (S_OK != (hr = lpTypeInfo->GetTypeAttr(&lpTypeAttr))) {
            goto Exit;
        }

        if (TKIND_COCLASS == lpTypeAttr->typekind) {

            Assert(IsEqualGUID( lpTypeAttr->guid, rClsid));

            // success
            goto Exit;
        }

    
    }


Exit:

    if (lpTypeAttr != NULL) lpTypeInfo->ReleaseTypeAttr(lpTypeAttr);
    if (lpTypeInfo != NULL) lpTypeInfo->Release();
    if (ptlib != NULL)      ptlib->Release();

    return hr;
}

HRESULT CDLDupWStr( LPWSTR *pszwstrDst, LPCWSTR szwSrc )
{
    HRESULT hr = S_OK;

    *pszwstrDst = new WCHAR[lstrlenW(szwSrc)+1];
    if (*pszwstrDst)
        StrCpyW( *pszwstrDst, szwSrc );
    else
        hr = E_OUTOFMEMORY;

    return hr;
}

extern CMutexSem g_mxsCodeDownloadGlobals;

HRESULT 
MakeUniqueTempDirectory(LPCSTR szTempDir, LPSTR szUniqueTempDir, int iLen)
{
    int n = 1;
    HRESULT hr = S_OK;

    //execute entire function under critical section
    CLock lck(g_mxsCodeDownloadGlobals);

    do {

        if (n > 100)    // avoid infinite loop!
            break;

        wnsprintf(szUniqueTempDir, iLen-1, "%s%s%d.tmp", szTempDir, "ICD", n++);


    } while (GetFileAttributes(szUniqueTempDir) != -1);

    if (!CreateDirectory(szUniqueTempDir, NULL)) {
        hr = HRESULT_FROM_WIN32(GetLastError());
    }

    return hr;
}

HRESULT
ComposeHackClsidFromMime(LPSTR szHackMimeType, int iLen, LPCSTR szClsid)
{
    HRESULT hr = S_OK;
    char szID[MAX_PATH];
    LPSTR pchDest = szID;

    for (LPCSTR pchSrc = szClsid; *pchDest = *pchSrc; pchSrc++,pchDest++) {

        if (*pchSrc == '/') {
            *pchDest++ = '_';   
            *pchDest++ = '2';   
            *pchDest++ = 'F';   
            *pchDest = '_'; 
            
        }
    }

    wnsprintf(szHackMimeType, iLen-1, "&CLSID=%s", szID);

    return hr;
}


HRESULT
RemoveDirectoryAndChildren(LPCSTR szDir)
{
    HRESULT hr = S_OK;
    HANDLE hf = INVALID_HANDLE_VALUE;
    char szBuf[MAX_PATH];
    WIN32_FIND_DATA fd;

    if (RemoveDirectory(szDir))
        goto Exit;

    // ha! we have a case where the directory is probbaly not empty

    StrNCpy(szBuf, szDir, sizeof(szBuf));
    StrCatBuff(szBuf, "\\*", sizeof(szBuf));

    if ((hf = FindFirstFile(szBuf, &fd)) == INVALID_HANDLE_VALUE) {
        hr = HRESULT_FROM_WIN32(GetLastError());
        goto Exit;
    }

    do {

        if ( (lstrcmp(fd.cFileName, ".") == 0) || 
             (lstrcmp(fd.cFileName, "..") == 0))
            continue;

        wnsprintf(szBuf, sizeof(szBuf)-1, "%s\\%s", szDir, fd.cFileName);
        if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {

            SetFileAttributes(szBuf, 
                FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_NORMAL);

            if (FAILED((hr=RemoveDirectoryAndChildren(szBuf)))) {
                goto Exit;
            }

        } else {

            SetFileAttributes(szBuf, FILE_ATTRIBUTE_NORMAL);
            if (!DeleteFile(szBuf)) {
                hr = HRESULT_FROM_WIN32(GetLastError());
                goto Exit;
            }
        }


    } while (FindNextFile(hf, &fd));


    if (GetLastError() != ERROR_NO_MORE_FILES) {

        hr = HRESULT_FROM_WIN32(GetLastError());
        goto Exit;
    }

    if (hf != INVALID_HANDLE_VALUE) {
        FindClose(hf);
        hf = INVALID_HANDLE_VALUE;
    }

    // here if all subdirs/children removed
    /// re-attempt to remove the main dir
    if (!RemoveDirectory(szDir)) {
        hr = HRESULT_FROM_WIN32(GetLastError());
        goto Exit;
    }

Exit:

    if (hf != INVALID_HANDLE_VALUE)
        FindClose(hf);

    return hr;
}


HRESULT IsExtnHandled(LPSTR pszExt)
{
    DEBUG_ENTER((DBG_DOWNLOAD,
                Hresult,
                "IsExtnHandled",
                "%.80q",
                pszExt
                ));
                
    HRESULT hr = REGDB_E_CLASSNOTREG;

    HKEY        hkRoot = HKEY_CLASSES_ROOT;
    char        szFileExt[MAX_PATH];
    DWORD       cbFileExt = sizeof(szFileExt);

    char szCmdLine[2*MAX_PATH];
    char szProgID[2*MAX_PATH];
    LONG  cbProgID = sizeof(szProgID);
    DWORD dwType;
    DWORD dwLen = sizeof(szCmdLine);
    HKEY hkeyCmd = 0;

    if (pszExt[0] == '\0')
    {
        goto Exit;
    }

    StrNCpy(szFileExt,pszExt, sizeof(szFileExt));

    Assert((szFileExt[0] == '.'));

    // the entry begins with '.' so it may be a file extension
    // query the value (which is the ProgID)

    if (RegQueryValue(hkRoot, szFileExt, szProgID, &cbProgID) == ERROR_SUCCESS)
    {
        // we got the value (ProgID), now query for the CLSID
        // string and convert it to a CLSID
        StrCatBuff(szProgID, "\\Shell\\Open\\Command", sizeof(szProgID));

        if ( (RegOpenKeyEx(HKEY_CLASSES_ROOT, szProgID, 0, KEY_QUERY_VALUE, &hkeyCmd) == ERROR_SUCCESS) &&
             (SHQueryValueEx(hkeyCmd, NULL, NULL, &dwType, &szCmdLine, &dwLen) == ERROR_SUCCESS) && dwLen)

        {
            if (GetEXEName(szCmdLine)) {
                hr = S_OK;
            }
        }
    }

Exit:

    if (hkeyCmd)
        RegCloseKey(hkeyCmd);

    DEBUG_LEAVE(hr);
    return hr;
}

#define SZMIMESIZE_MAX  128

HRESULT IsMimeHandled(LPCWSTR pwszMime)
{
    DEBUG_ENTER((DBG_DOWNLOAD,
                Hresult,
                "IsMimeHandled",
                "%.80wq",
                pwszMime
                ));
                
    HRESULT hr = S_OK;
    HKEY hMimeKey = NULL;
    DWORD dwType;
    char szValue[MAX_PATH];
    DWORD dwValueLen = MAX_PATH;
    static char szMimeKey[]     = "MIME\\Database\\Content Type\\";
    static char szExtension[]     = "Extension";
    const ULONG ulMimeKeyLen    = ((sizeof(szMimeKey)/sizeof(char))-1);
    char szKey[SZMIMESIZE_MAX + ulMimeKeyLen];
    LPSTR pszMime = NULL;

    if (SUCCEEDED((hr=::Unicode2Ansi(pwszMime, &pszMime)))) {

        StrNCpy(szKey, szMimeKey, sizeof(szKey));
        StrCatBuff(szKey, pszMime, sizeof(szKey));

        hr = REGDB_E_READREGDB;

        if (RegOpenKeyEx(HKEY_CLASSES_ROOT, szKey, 0, KEY_QUERY_VALUE, &hMimeKey) == ERROR_SUCCESS) {

            if (RegQueryValueEx(hMimeKey
                    , szExtension
                    , NULL
                    , &dwType
                    , (LPBYTE)szValue
                    , &dwValueLen) == ERROR_SUCCESS) {

                hr = IsExtnHandled(szValue);
            }
        }
    }

    if (hMimeKey) {
        RegCloseKey(hMimeKey);
    }

    SAFEDELETE(pszMime);

    DEBUG_LEAVE(hr);
    return hr;
}