#include "precomp.h"

static int checkVerHelper(LPCTSTR pcszPrevVer, LPCTSTR pcszNewVer);
static void generateNewVersionStrHelper(LPCTSTR pcszInsFile, LPTSTR pszNewVersionStr);
static void setOrClearVersionInfoHelper(LPCTSTR pcszInsFile, DWORD dwCabType, LPCTSTR pcszCabName, 
                                        LPCTSTR pcszCabsURLPath, LPTSTR pszNewVersionStr, BOOL fSet);
static void getBaseFileNameHelper(LPCTSTR pcszFile, LPTSTR pszBaseFileName, INT cchSize);

// iRet >  0  ==>  PrevVer is higher than NewVersion
// iRet == 0  ==>  PrevVer is the same as NewVersion
// iRet <  0  ==>  PrevVer is lower than NewVersion

int WINAPI CheckVerA(LPCSTR pcszPrevVer, LPCSTR pcszNewVer)
{
    USES_CONVERSION;

    return checkVerHelper(A2CT(pcszPrevVer), A2CT(pcszNewVer));
}

int WINAPI CheckVerW(LPCWSTR pcwszPrevVer, LPCWSTR pcwszNewVer)
{
    USES_CONVERSION;

    return checkVerHelper(W2CT(pcwszPrevVer), W2CT(pcwszNewVer));
}

void WINAPI GenerateNewVersionStrA(LPCSTR pcszInsFile, LPSTR pszNewVersionStr)
{
    TCHAR szNewVerStr[32];

    USES_CONVERSION;

    generateNewVersionStrHelper(A2CT(pcszInsFile), szNewVerStr);
    T2Abux(szNewVerStr, pszNewVersionStr);
}

void WINAPI GenerateNewVersionStrW(LPCWSTR pcwszInsFile, LPWSTR pwszNewVersionStr)
{
    TCHAR szNewVerStr[32];

    USES_CONVERSION;

    generateNewVersionStrHelper(W2CT(pcwszInsFile), szNewVerStr);
    T2Wbux(szNewVerStr, pwszNewVersionStr);
}

void WINAPI SetOrClearVersionInfoA(LPCSTR pcszInsFile, DWORD dwCabType, LPCSTR pcszCabName, 
                            LPCSTR pcszCabsURLPath, LPSTR pszNewVersionStr, BOOL fSet)
{
    TCHAR szNewVerStr[32];

    USES_CONVERSION;

    A2Tbux(pszNewVersionStr, szNewVerStr);
    setOrClearVersionInfoHelper(A2CT(pcszInsFile), dwCabType, A2CT(pcszCabName),
        A2CT(pcszCabsURLPath), szNewVerStr, fSet);
    T2Abux(szNewVerStr, pszNewVersionStr);
}

void WINAPI SetOrClearVersionInfoW(LPCWSTR pcwszInsFile, DWORD dwCabType, LPCWSTR pcwszCabName, 
                            LPCWSTR pcwszCabsURLPath, LPWSTR pwszNewVersionStr, BOOL fSet)
{
    TCHAR szNewVerStr[32];

    USES_CONVERSION;

    W2Tbux(pwszNewVersionStr, szNewVerStr);
    setOrClearVersionInfoHelper(W2CT(pcwszInsFile), dwCabType, W2CT(pcwszCabName),
        W2CT(pcwszCabsURLPath), szNewVerStr, fSet);
    T2Wbux(szNewVerStr, pwszNewVersionStr);
}

void WINAPI GetBaseFileNameA(LPCSTR pcszFile, LPSTR pszBaseFileName, INT cchSize)
{
    LPTSTR pszBuf = (LPTSTR)CoTaskMemAlloc(StrCbFromCch(cchSize));

    USES_CONVERSION;

    if (pszBuf != NULL)
    {
        getBaseFileNameHelper(A2CT(pcszFile), pszBuf, cchSize);
        T2Abux(pszBuf, pszBaseFileName);
        CoTaskMemFree(pszBuf);
    }
}

void WINAPI GetBaseFileNameW(LPCWSTR pcwszFile, LPWSTR pwszBaseFileName, INT cchSize)
{
    LPTSTR pszBuf = (LPTSTR)CoTaskMemAlloc(StrCbFromCch(cchSize));

    USES_CONVERSION;

    if (pszBuf != NULL)
    {
        getBaseFileNameHelper(W2CT(pcwszFile), pszBuf, cchSize);
        T2Wbux(pszBuf, pwszBaseFileName);
        CoTaskMemFree(pszBuf);
    }
}

/* stolen from webcheck
// PRIVATE VERSION HANDLING CODE - REVIEW THIS CODE SHOULD HAVE BEEN STOLEN
// FROM SETUP
*/
typedef struct _MYVERSION
{
    DWORD dw1;  /* most sig version number  */
    DWORD dw2;
    DWORD dw3;
    DWORD dw4;  /* least sig version number  */
} MYVERSION;

static int compareDW(DWORD dw1, DWORD dw2)
{
    if (dw1 > dw2)
        return 1;
    if (dw1 < dw2)
        return -1;

    return 0;
}

static int compareVersion(MYVERSION * pv1, MYVERSION * pv2)
{
    int rv;

    rv = compareDW(pv1->dw1, pv2->dw1);

    if (rv == 0)
    {
        rv = compareDW(pv1->dw2, pv2->dw2);

        if (rv == 0)
        {
            rv = compareDW(pv1->dw3, pv2->dw3);

            if (rv == 0)
            {
                rv = compareDW(pv1->dw4, pv2->dw4);
            }
        }
    }

    return rv;
}

static void getDWORDFromStringAndAdvancePtr(DWORD *pdw, LPTSTR *psz)
{
    LPTSTR pszTemp;

    if ((!(*psz)) || (!StrToIntEx(*psz, 0, (int *)pdw)))
    {
        *pdw = TEXT('\0');
        return;
    }

    // look for period separator first, then comma

    pszTemp  = StrChr(*psz, TEXT('.'));

    if (pszTemp != NULL)
        *psz = pszTemp;
    else
        *psz = StrChr(*psz, TEXT(','));

    if (*psz)
        (*psz)++;

    return;
}

static void getVersionFromString(MYVERSION *pver, LPTSTR psz)
{
    getDWORDFromStringAndAdvancePtr(&pver->dw1, &psz);
    getDWORDFromStringAndAdvancePtr(&pver->dw2, &psz);
    getDWORDFromStringAndAdvancePtr(&pver->dw3, &psz);
    getDWORDFromStringAndAdvancePtr(&pver->dw4, &psz);
}

// end of stolen code

static int checkVerHelper(LPCTSTR pcszPrevVer, LPCTSTR pcszNewVer)
{
    MYVERSION verOldVer, verNewVer;

    getVersionFromString(&verOldVer, (LPTSTR)pcszPrevVer);
    getVersionFromString(&verNewVer, (LPTSTR)pcszNewVer);

    return compareVersion(&verOldVer, &verNewVer);
}

static void incrementDotVer(LPTSTR pszVerStr)
{
    LPTSTR pszT = pszVerStr;
    DWORD dwYear, dwMonth, dwDay, dwDotVer;
    
    getDWORDFromStringAndAdvancePtr(&dwYear, &pszT);
    getDWORDFromStringAndAdvancePtr(&dwMonth, &pszT);
    getDWORDFromStringAndAdvancePtr(&dwDay, &pszT);
    getDWORDFromStringAndAdvancePtr(&dwDotVer, &pszT);
    
    if (++dwDotVer > 99)
    {
        // this case should never arise
    }
    
    wnsprintf(pszVerStr, 32, TEXT("%04d.%02d.%02d.%02d"), dwYear, dwMonth, dwDay, dwDotVer);
}

static void generateNewVersionStrHelper(LPCTSTR pcszInsFile, LPTSTR pszNewVersionStr)
{
    TCHAR szPrevVerStr[32];
    SYSTEMTIME st;
    
    GetPrivateProfileString(BRANDING, INSVERKEY, TEXT(""), szPrevVerStr, ARRAYSIZE(szPrevVerStr), pcszInsFile);
    
    GetLocalTime(&st);
    wnsprintf(pszNewVersionStr, 32, TEXT("%04d.%02d.%02d.%02d"), st.wYear, st.wMonth, st.wDay, 0);

    if (ISNONNULL(szPrevVerStr))
    {
        int iRet = CheckVer(szPrevVerStr, pszNewVersionStr);
        
        // iRet >  0  ==>  PrevVer is higher than NewVersion
        // iRet == 0  ==>  PrevVer is the same as NewVersion
        // iRet <  0  ==>  PrevVer is lower than NewVersion
        
        if (iRet >= 0)
        {
            if (iRet > 0)
                StrCpy(pszNewVersionStr, szPrevVerStr);
            
            incrementDotVer(pszNewVersionStr);
        }
    }
}

static void setOrClearVersionInfoHelper(LPCTSTR pcszInsFile, DWORD dwCabType, LPCTSTR pcszCabName, 
                                        LPCTSTR pcszCabsURLPath, LPTSTR pszNewVersionStr, BOOL fSet)
{
    LPCTSTR pcszSection = NULL, pcszKey = NULL;
    TCHAR szCabInfoLine[INTERNET_MAX_URL_LENGTH + 128];
    LPTSTR pszCurField, pszNextField;
    TCHAR szCabURL[INTERNET_MAX_URL_LENGTH];
    TCHAR szExpiration[16];
    TCHAR szFlags[16];

    switch (dwCabType)
    {
    case CAB_TYPE_CONFIG:
        pcszSection = CUSTBRNDSECT;
        pcszKey = CUSTBRNDNAME;
        break;

    case CAB_TYPE_DESKTOP:
        pcszSection = CUSTDESKSECT;
        pcszKey = CUSTDESKNAME;
        break;

    case CAB_TYPE_CHANNELS:
        pcszSection = CUSTCHANSECT;
        pcszKey = CUSTCHANNAME;
        break;
    }

    if (pcszSection == NULL  ||  pcszKey == NULL)
        return;

    if (fSet)
    {
        // save the info before deleting it
        if (GetPrivateProfileString(pcszSection, pcszKey, TEXT(""), szCabInfoLine, ARRAYSIZE(szCabInfoLine), pcszInsFile) == 0)
            GetPrivateProfileString(CUSTOMVERSECT, pcszKey, TEXT(""), szCabInfoLine, ARRAYSIZE(szCabInfoLine), pcszInsFile);
    }

    // clear out the version information in the INS file
    WritePrivateProfileString(pcszSection, NULL, NULL, pcszInsFile);
    WritePrivateProfileString(CUSTOMVERSECT, pcszKey, NULL, pcszInsFile);

    if (!fSet)
        return;

    *szCabURL = *szExpiration = *szFlags = TEXT('\0');

    if (ISNONNULL(szCabInfoLine))
    {
        // parse the szCabInfoLine
        pszCurField = szCabInfoLine;
        if ((pszNextField = StrChr(pszCurField, TEXT(','))) != NULL)
        {
            *pszNextField++ = TEXT('\0');
            StrCpy(szCabURL, pszCurField);

            pszCurField = pszNextField;
            if ((pszNextField = StrChr(pszCurField, TEXT(','))) != NULL)
            {
                *pszNextField++ = TEXT('\0');

                pszCurField = pszNextField;
                if ((pszNextField = StrChr(pszCurField, TEXT(','))) != NULL)
                {
                    *pszNextField++ = TEXT('\0');
                    StrCpy(szExpiration, pszCurField);

                    pszCurField = pszNextField;
                    if ((pszNextField = StrChr(pszCurField, TEXT(','))) != NULL)
                    {
                        *pszNextField++ = TEXT('\0');
                        StrCpy(szFlags, pszCurField);
                    }
                    else
                        StrCpy(szFlags, pszCurField);
                }
                else
                    StrCpy(szExpiration, pszCurField);
            }
        }
        else
            StrCpy(szCabURL, pszCurField);
    }

    // initialize autoconfig URL
    StrCpy(szCabURL, pcszCabsURLPath);
    PathRemoveBackslash(szCabURL);
    if (ISNONNULL(szCabURL)  &&  szCabURL[StrLen(szCabURL) - 1] != TEXT('/'))
        StrCat(szCabURL, TEXT("/"));
    StrCat(szCabURL, pcszCabName);

    generateNewVersionStrHelper(pcszInsFile, pszNewVersionStr);

    if (ISNULL(szExpiration))
        StrCpy(szExpiration, TEXT("-1"));           // never expires

    // write the new version info line
    wnsprintf(szCabInfoLine, ARRAYSIZE(szCabInfoLine), TEXT("%s,%s,%s,%s"), szCabURL, pszNewVersionStr, szExpiration, szFlags);
    WritePrivateProfileString(pcszSection, pcszKey, szCabInfoLine, pcszInsFile);
}

static void getBaseFileNameHelper(LPCTSTR pcszFile, LPTSTR pszBaseFileName, INT cchSize)
{
    TCHAR szFile[MAX_PATH];
    LPTSTR lpszFileName = NULL;
    
    if (pcszFile == NULL  ||  ISNULL(pcszFile))
        return;
    
    StrCpy(szFile, pcszFile);
    lpszFileName = PathFindFileName(szFile);
    PathRemoveExtension(lpszFileName);
    StrCpyN(pszBaseFileName, lpszFileName, cchSize);
}