windows-nt/Source/XPSP1/NT/enduser/windows.com/wuau/wuaueng/selfupdate.cpp
2020-09-26 16:20:57 +08:00

1468 lines
48 KiB
C++

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 2000
//
// File: selfupdate.cpp
//
// Desc: This file contains all the functions necessary for self-update
//--------------------------------------------------------------------------
#include "pch.h"
#include <osdet.h>
#include <muiutil.h>
#define TCHAR_SCTRUCTURE_DELIMITER _T('|')
struct AU_FILEINCAB
{
TCHAR szFilePath[MAX_PATH + 1];
TCHAR szNewFilePath[MAX_PATH + 1];
TCHAR szBackupFilePath[MAX_PATH + 1];
TCHAR szExtractFilePath[MAX_PATH + 1];
BOOL fCreatedBackup;
BOOL fFileExists;
AU_FILEINCAB *pNextFileInCab;
};
struct AU_COMPONENT : AU_FILEINCAB
{
TCHAR *pszSectionName;
TCHAR szFileName[_MAX_FNAME + 1];
TCHAR szCabName[_MAX_FNAME + 1];
TCHAR szCabPath[MAX_PATH + 1];
CHAR a_szCabPath[MAX_PATH + 1];
TCHAR szInfName[_MAX_FNAME + 1];
CHAR a_szInfName[_MAX_FNAME + 1];
DWORD dwUpdateMS;
DWORD dwUpdateLS;
BOOL fDoUpgrade;
BOOL fNeedToCheckMui;
BOOL fMuiFile;
BOOL fHasHelpfile;
AU_COMPONENT *pNext;
};
// AU_UPDATE_VERSION should be updated when incompatible changes are made to the
// self update mechanism required AU to go to a new directory for update info.
const TCHAR IDENT_TXT[] = _T("iuident.txt");
const TCHAR WUAUCOMP_CAB[] = _T("wuaucomp.cab");
const TCHAR WUAUCOMP_CIF[] = _T("wuaucomp.cif");
const TCHAR WUAUENG_DLL[] = TEXT("wuaueng.dll");
const TCHAR AU_KEY_FILE_NAME[] = TEXT("file");
const TCHAR AU_KEY_FILE_VERSION[] = TEXT("version");
const TCHAR AU_KEY_CAB_NAME[] = TEXT("cab");
const TCHAR AU_KEY_INF_NAME[] = TEXT("inf");
const TCHAR AU_KEY_RESMOD_NAME[] = TEXT("resmodule");
const TCHAR AU_KEY_HELPFILE[] = TEXT("helpfile");
const DWORD MAX_SECTION = 30;
// main selfupdate keys
const TCHAR IDENT_SERVERURLEX[] = _T("ServerUrlEx");
const TCHAR IDENT_STRUCTUREKEYEX[] = _T("StructureKeyEx");
const TCHAR INIVALUE_NOTFOUND[] = _T("??");
BOOL fConvertVersionStrToDwords(LPTSTR pszVer, LPDWORD pdwVer, LPDWORD pdwBuild);
HRESULT InstallUpdatedComponents(LPCTSTR pszSelfUpdateUrl,
LPCTSTR pszMuiUpdateUrl,
LPCTSTR pszIdentTxt,
LPCTSTR pszFileCacheDir,
LPCTSTR pszCif,
BOOL *pfInstalledWUAUENG);
BOOL ReplaceFileInPath(LPCTSTR pszPath, LPCTSTR szNewFile, LPTSTR pszNewPathBuf, DWORD cchNewPathBuf);
BOOL MyGetPrivateProfileString( IN LPCWSTR lpAppName,
IN LPCWSTR lpKeyName,
OUT LPWSTR lpReturnedString,
IN DWORD nSize,
IN LPCWSTR lpFileName,
IN LPCTSTR lpDefault=_T(""));
inline BOOL fNewerFile(DWORD dwUpdateMS, DWORD dwUpdateLS, DWORD dwExistingMS, DWORD dwExistingLS)
{
return (dwUpdateMS > dwExistingMS) ||
((dwUpdateMS == dwExistingMS) && (dwUpdateLS > dwExistingLS));
}
inline HRESULT vAU_W2A(LPCWSTR lpWideCharStr, LPSTR lpMultiByteStr, int cbMultiByte)
{
if ( 0 != WideCharToMultiByte(CP_ACP, 0, lpWideCharStr, -1, lpMultiByteStr, cbMultiByte, NULL, NULL))
{
return S_OK;
}
else
{
return HRESULT_FROM_WIN32(GetLastError());
}
}
HRESULT SelfUpdate(void)
{
HRESULT hr;
BOOL fInstalledWUAUENG = FALSE;
DEBUGMSG("------------------------SELFUPDATE BEGINS---------------------------");
if( FAILED(hr = CheckForUpdatedComponents(&fInstalledWUAUENG)) )
{
goto lCleanUp;
}
if ( fInstalledWUAUENG )
{
DEBUGMSG("SELFUPDATE installed new wuaueng");
hr = S_FALSE;
goto lCleanUp;
}
lCleanUp:
return hr;
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// CleanFileCache()
//
//////////////////////////////////////////////////////////////////////////////////////////
BOOL CleanFileCache(LPCTSTR pszFileCacheDir)
{
BOOL fRet = TRUE;
TCHAR szFileCacheDir[MAX_PATH + 1];
TCHAR szFilePath[MAX_PATH + 1];
WIN32_FIND_DATA fd;
HANDLE hFindFile = INVALID_HANDLE_VALUE;
if (FAILED(StringCchCopyEx(szFileCacheDir, ARRAYSIZE(szFileCacheDir), pszFileCacheDir, NULL, NULL, MISTSAFE_STRING_FLAGS)))
{
fRet = FALSE;
goto done;
}
if (FAILED(PathCchAppend(szFileCacheDir, ARRAYSIZE(szFileCacheDir), TEXT("*.*"))))
{
fRet = FALSE;
goto done;
}
// Find the first file
hFindFile = FindFirstFile(szFileCacheDir, &fd);
if ( INVALID_HANDLE_VALUE == hFindFile )
{
goto done;
}
do
{
if ( !(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
{
// Make file path
if (FAILED(StringCchCopyEx(szFilePath, ARRAYSIZE(szFilePath), pszFileCacheDir, NULL, NULL, MISTSAFE_STRING_FLAGS)) ||
FAILED(PathCchAppend(szFilePath, ARRAYSIZE(szFilePath), fd.cFileName)) ||
!SetFileAttributes(szFilePath, FILE_ATTRIBUTE_NORMAL) ||
!DeleteFile(szFilePath))
{
fRet = FALSE;
DEBUGMSG("Couldn't delete file %S", szFilePath);
}
}
}
while ( FindNextFile(hFindFile, &fd) );// Find the next entry
done:
if ( INVALID_HANDLE_VALUE != hFindFile )
{
FindClose(hFindFile);
}
return fRet;
}
//////////////////////////////////////////////////////////////////////
//
// GetSelfUpdateUrl()
//
// Should be like:
//
// http://windowsupdate.microsoft.com/selfupdate/x86/XP/en
////////////////////////////////////////////////////////////////////////
HRESULT GetSelfUpdateUrl(LPCTSTR ptszName,
LPCTSTR ptszBaseUrl,
LPCTSTR pszIdentTxt,
LPTSTR pszSelfUpdateUrl,
DWORD cchSelfUpdateUrl,
LPTSTR pszMuiUpdateUrl,
DWORD cchMuiUpdateUrl)
{
LOG_Block("GetSelfUpdateUrl");
HRESULT hr;
TCHAR tszKey[MAX_SECTION]; // at least MAX_ISO_CODE_LENGTH
TCHAR tszValue[MAX_PATH];
BOOL fLangField;
if (FAILED(hr = StringCchCopyEx(tszKey, ARRAYSIZE(tszKey), ptszName, NULL, NULL, MISTSAFE_STRING_FLAGS)) ||
FAILED(hr = StringCchCatEx(tszKey, ARRAYSIZE(tszKey), _T("SelfUpdate"), NULL, NULL, MISTSAFE_STRING_FLAGS)))
{
goto lFinish;
}
if (NULL == ptszBaseUrl)
{
// Get SelfUpdate Server URL
if (MyGetPrivateProfileString(
tszKey,
IDENT_SERVERURLEX,
pszSelfUpdateUrl,
cchSelfUpdateUrl,
pszIdentTxt) == FALSE)
{
// no URL specified in iuident..
hr = E_FAIL;
goto lFinish;
}
else
{
if (FAILED(hr = StringCchCopyEx(pszMuiUpdateUrl, cchMuiUpdateUrl, pszSelfUpdateUrl, NULL, NULL, MISTSAFE_STRING_FLAGS)))
goto lFinish;
}
}
else
{
if (FAILED(hr = StringCchCopyEx(pszSelfUpdateUrl, cchSelfUpdateUrl, ptszBaseUrl, NULL, NULL, MISTSAFE_STRING_FLAGS)) ||
FAILED(hr = StringCchCopyEx(pszMuiUpdateUrl, cchMuiUpdateUrl, ptszBaseUrl, NULL, NULL, MISTSAFE_STRING_FLAGS)))
{
goto lFinish;
}
// Remove trailing _T('/') if present
int nBaseUrlLen = lstrlen(pszSelfUpdateUrl);
if(nBaseUrlLen <= 0)
{
hr = E_FAIL;
goto lFinish;
}
if (_T('/') == pszSelfUpdateUrl[nBaseUrlLen-1])
{
pszSelfUpdateUrl[nBaseUrlLen-1] = _T('\0');
pszMuiUpdateUrl[nBaseUrlLen-1] = _T('\0');
}
}
TCHAR tszStructure[MAX_PATH];
if (!MyGetPrivateProfileString(
tszKey,
IDENT_STRUCTUREKEYEX,
tszStructure,
ARRAYSIZE(tszStructure),
pszIdentTxt))
{
// no STRUCTYREKEY specified in iuident..
hr = E_FAIL;
goto lFinish;
}
// Parse the SelfUpdate Structure Key for Value Names to Read
LPTSTR ptszWalk = tszStructure;
while (_T('\0') != ptszWalk[0])
{
LPTSTR ptszDelim;
fLangField = FALSE;
if (NULL != (ptszDelim = StrChr(ptszWalk, TCHAR_SCTRUCTURE_DELIMITER)))
{
*ptszDelim = _T('\0');
}
if (_T('/') == ptszWalk[0])
{
if (FAILED(hr = StringCchCopyEx(tszValue, ARRAYSIZE(tszValue), ptszWalk, NULL, NULL, MISTSAFE_STRING_FLAGS)))
{
goto lFinish;
}
}
else
{
int nPrefixLength = lstrlen(ptszName);
LPCTSTR ptszToken = ptszWalk;
if (0 == StrCmpNI(ptszWalk, ptszName, nPrefixLength))
{
ptszToken += nPrefixLength;
}
if (0 == StrCmpI(ptszToken, IDENT_ARCH))
{
if (!MyGetPrivateProfileString(
ptszWalk,
#ifdef _IA64_
IDENT_IA64,
#else
IDENT_X86,
#endif
tszValue,
ARRAYSIZE(tszValue),
pszIdentTxt))
{
// insufficient buffer
hr = E_FAIL;
goto lFinish;
}
}
else if (0 == StrCmpI(ptszToken, IDENT_OS))
{
if (FAILED(hr = StringCchCopyEx(tszKey, ARRAYSIZE(tszKey), ptszWalk, NULL, NULL, MISTSAFE_STRING_FLAGS)) ||
FAILED(hr = StringCchCatEx(tszKey, ARRAYSIZE(tszKey), _T("NT"), NULL, NULL, MISTSAFE_STRING_FLAGS)))
{
goto lFinish;
}
if (S_OK != GetINIValueByOSVer(
pszIdentTxt,
tszKey,
tszValue,
ARRAYSIZE(tszValue)))
{
hr = E_FAIL;
goto lFinish;
}
}
else if (0 == StrCmpI(ptszToken, IDENT_LANG))
{
fLangField = TRUE;
// Get the Current Locale String
(void) LookupLocaleString(tszKey, ARRAYSIZE(tszKey), FALSE);
if (0 == StrCmp(tszKey, _T("Error")))
{
DEBUGMSG("GetSelfUpdateUrl() call to LookupLocaleString() failed.");
hr = E_FAIL;
goto lFinish;
}
if (!MyGetPrivateProfileString(
ptszWalk,
tszKey,
tszValue,
ARRAYSIZE(tszValue),
pszIdentTxt,INIVALUE_NOTFOUND))
{
hr = E_FAIL;
goto lFinish;
}
if (0 == StrCmp(tszValue, INIVALUE_NOTFOUND))
{
LPTSTR ptszDash = StrChr(tszKey, _T('-'));
if (NULL != ptszDash)
{
*ptszDash = _T('\0');
if (!MyGetPrivateProfileString(
ptszWalk,
tszKey,
tszValue,
ARRAYSIZE(tszValue),
pszIdentTxt))
{
hr = E_FAIL;
goto lFinish;
}
}
else
{
tszValue[0] = _T('\0');
}
}
}
else
{
LOG_Internet(_T("Found Unrecognized Token in SelfUpdate Structure String: Token was: %s"), ptszWalk);
tszValue[0] = _T('\0'); // ignore the unrecognized token
}
}
if (_T('\0') != tszValue[0])
{
LPCTSTR ptszMuiCopy;
if (FAILED(hr = StringCchCatEx(pszSelfUpdateUrl, cchSelfUpdateUrl, tszValue, NULL, NULL, MISTSAFE_STRING_FLAGS)))
goto lFinish;
if (fLangField)
ptszMuiCopy = MUI_WEBSUBPATH;
else
ptszMuiCopy = tszValue;
if (FAILED(hr = StringCchCatEx(pszMuiUpdateUrl, cchMuiUpdateUrl, ptszMuiCopy, NULL, NULL, MISTSAFE_STRING_FLAGS)))
goto lFinish;
}
if (NULL == ptszDelim)
{
break;
}
ptszWalk = ptszDelim + 1; // skip the previous token, and go to the next one in the string.
*ptszDelim = TCHAR_SCTRUCTURE_DELIMITER;
}
DEBUGMSG("GetSelfUpdateUrl() Self Update URL is %S", pszSelfUpdateUrl);
DEBUGMSG("GetSelfUpdateUrl() MUI Update URL is %S", pszMuiUpdateUrl);
hr = S_OK;
lFinish:
if (FAILED(hr))
{
if (cchMuiUpdateUrl > 0)
*pszMuiUpdateUrl = _T('\0');
if (cchSelfUpdateUrl > 0)
*pszSelfUpdateUrl = _T('\0');
}
return hr;
}
////////////////////////////////////////////////////////////////////////////////////////
//
// CheckForUpdatedComponents
//
////////////////////////////////////////////////////////////////////////////////////////
HRESULT CheckForUpdatedComponents(BOOL *pfInstalledWUAUENG)
{
HRESULT hr;
LPCTSTR ptszIdentServerUrl = NULL;
LPTSTR ptszSelfUpdateUrl = NULL;
LPTSTR ptszMuiUpdateUrl = NULL;
if (NULL != (ptszSelfUpdateUrl = (LPTSTR) malloc(sizeof(TCHAR) * INTERNET_MAX_URL_LENGTH)) &&
NULL != (ptszMuiUpdateUrl = (LPTSTR) malloc(sizeof(TCHAR) * INTERNET_MAX_URL_LENGTH)) &&
NULL != (ptszIdentServerUrl = gpState->GetIdentServerURL()))
{
TCHAR szFileCacheDir[MAX_PATH+1];
if ( FAILED(hr = MakeTempDownloadDir(szFileCacheDir, ARRAYSIZE(szFileCacheDir))) ||
!CleanFileCache(szFileCacheDir) )
{
DEBUGMSG("Couldn't fully clean file cache %S", szFileCacheDir);
hr = FAILED(hr) ? hr : E_FAIL;
goto done;
}
BOOL fInCorpWU = gpState->fInCorpWU();
if (IsConnected(ptszIdentServerUrl, !fInCorpWU))
{
DWORD dwFlags = 0;
if (fInCorpWU)
{
dwFlags |= WUDF_DONTALLOWPROXY;
}
if (SUCCEEDED(hr = DownloadIUIdent(
ghServiceFinished,
ptszIdentServerUrl,
szFileCacheDir,
dwFlags)))
{
TCHAR tszIdentTxt[MAX_PATH];
gPingStatus.ReadLiveServerUrlFromIdent();
hr = PathCchCombine(tszIdentTxt, ARRAYSIZE(tszIdentTxt),
szFileCacheDir, IDENT_TXT);
if (FAILED(hr))
goto done;
if (SUCCEEDED(hr = GetSelfUpdateUrl(
_T("AU"),
gpState->GetSelfUpdateServerURLOverride(),
tszIdentTxt,
ptszSelfUpdateUrl,
INTERNET_MAX_URL_LENGTH,
ptszMuiUpdateUrl,
INTERNET_MAX_URL_LENGTH)) &&
SUCCEEDED(hr = DownloadCab(
ghServiceFinished,
WUAUCOMP_CAB,
ptszSelfUpdateUrl,
szFileCacheDir,
dwFlags)))
{
TCHAR szWuaucompCif[MAX_PATH+1];
if (SUCCEEDED(hr = PathCchCombine(szWuaucompCif, ARRAYSIZE(szWuaucompCif), szFileCacheDir, WUAUCOMP_CIF)))
{
// install any updated components
hr = InstallUpdatedComponents(
ptszSelfUpdateUrl,
ptszMuiUpdateUrl,
tszIdentTxt,
szFileCacheDir,
szWuaucompCif,
pfInstalledWUAUENG);
#ifdef DBG
if (FAILED(hr))
{
DEBUGMSG("InstallUpdatedComponents failed");
}
#endif
}
}
}
}
else
{
DEBUGMSG("SelfUpdate: No connection found.");
hr = E_FAIL;
}
}
else
{
hr = E_OUTOFMEMORY;
}
done:
SafeFree(ptszSelfUpdateUrl);
SafeFree(ptszMuiUpdateUrl);
return hr;
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// SfcMoveFileEx
//
//////////////////////////////////////////////////////////////////////////////////////////
BOOL SfcMoveFileEx( IN LPCTSTR lpExistingFileName,
IN LPCTSTR lpNewFileName,
IN LPCTSTR lpSfcProtectedFile,
IN HANDLE SfcRpcHandle)
{
BOOL fRet = TRUE;
if ( SfcIsFileProtected(SfcRpcHandle, lpSfcProtectedFile) &&
(ERROR_SUCCESS != SfcFileException(SfcRpcHandle,
lpSfcProtectedFile,
SFC_ACTION_RENAMED_OLD_NAME)) )
{
fRet = FALSE;
goto done;
}
fRet = MoveFileEx(lpExistingFileName, lpNewFileName, MOVEFILE_REPLACE_EXISTING);
done:
if ( !fRet )
{
DEBUGMSG("Could not rename %S --> %S", lpExistingFileName, lpNewFileName);
}
return fRet;
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// Function BuildPaths()
//
//////////////////////////////////////////////////////////////////////////////////////////
HRESULT BuildPaths(AU_FILEINCAB *paufic, LPCTSTR pszFileName, LPCTSTR pszBasePath, LPCTSTR pszExtractBase,
AU_LANG *paul)
{
HRESULT hr = S_OK;
if (paufic == NULL || pszFileName == NULL || pszExtractBase == NULL)
{
hr = E_INVALIDARG;
goto done;
}
if (pszBasePath != NULL)
{
// build the full file path
hr = PathCchCombine(paufic->szFilePath, ARRAYSIZE(paufic->szFilePath),
pszBasePath, pszFileName);
if (FAILED(hr))
goto done;
paufic->fFileExists = fFileExists(paufic->szFilePath);
}
// file path we'll temporarily copy the original file to
if (ReplaceFileExtension(paufic->szFilePath, _T(".bak"),
paufic->szBackupFilePath,
ARRAYSIZE(paufic->szBackupFilePath)) == FALSE)
{
hr = E_FAIL;
goto done;
}
// file path we'll temporarily expand the new file to
if (ReplaceFileExtension(paufic->szFilePath, _T(".new"),
paufic->szNewFilePath,
ARRAYSIZE(paufic->szNewFilePath)) == FALSE)
{
hr = E_FAIL;
goto done;
}
if (ReplaceFileInPath(pszExtractBase, pszFileName,
paufic->szExtractFilePath,
ARRAYSIZE(paufic->szExtractFilePath)) == FALSE)
{
hr = E_FAIL;
goto done;
}
// if we are processing a language file, append the language name to
// the end of the extraction path to avoid collisions in this directory.
if (paul != NULL)
{
hr = StringCchCatEx(paufic->szExtractFilePath,
ARRAYSIZE(paufic->szExtractFilePath),
paul->szAUName,
NULL, NULL, MISTSAFE_STRING_FLAGS);
if (FAILED(hr))
goto done;
}
done:
return hr;
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// Function ProcessFile()
//
//////////////////////////////////////////////////////////////////////////////////////////
HRESULT ProcessFile(AU_COMPONENT *paucParent, AU_COMPONENT *paucCurr, LPCTSTR pszBasePath,
AU_LANG *paul, LPCTSTR pszCif)
{
USES_IU_CONVERSION;
HRESULT hr = NOERROR;
LPCTSTR pszIniFileVerToUse;
DWORD dwExistingMS = 0, dwExistingLS = 0;
TCHAR szValue[64], szIniFileVer[32];
BOOL fRet;
int cch, cchLang;
// validate params
if (paucCurr == NULL || pszBasePath == NULL || pszCif == NULL ||
((paucParent == NULL) != (paul == NULL)))
{
hr = E_INVALIDARG;
goto done;
}
// build the full file path
hr = PathCchCombine(paucCurr->szFilePath, ARRAYSIZE(paucCurr->szFilePath),
pszBasePath, paucCurr->szFileName);
if (FAILED(hr))
goto done;
// get the version of the file we should have
if (paul != NULL)
{
hr = StringCchPrintfEx(szIniFileVer, ARRAYSIZE(szIniFileVer),
NULL, NULL, MISTSAFE_STRING_FLAGS,
_T("%s%s"), AU_KEY_FILE_VERSION, paul->szAUName);
if (FAILED(hr))
goto done;
pszIniFileVerToUse = szIniFileVer;
}
else
{
pszIniFileVerToUse = AU_KEY_FILE_VERSION;
}
fRet = MyGetPrivateProfileString(paucCurr->pszSectionName,
pszIniFileVerToUse,
szValue, ARRAYSIZE(szValue),
pszCif);
if (fRet)
{
fRet = fConvertVersionStrToDwords(szValue, &paucCurr->dwUpdateMS,
&paucCurr->dwUpdateLS);
}
// if we couldn't find the version string in the ini file, get it from the
// parent AU_COMPONENT
else if (paucParent != NULL)
{
paucCurr->dwUpdateMS = paucParent->dwUpdateMS;
paucCurr->dwUpdateLS = paucParent->dwUpdateLS;
fRet = TRUE;
}
if (fRet == FALSE)
{
hr = E_FAIL;
goto done;
}
// see if we need to replace the file
paucCurr->fFileExists = fFileExists(paucCurr->szFilePath);
if (paucCurr->fFileExists)
{
LPSTR pszPathForVer;
// if the file exists, then check for the version
pszPathForVer = T2A(paucCurr->szFilePath);
if (pszPathForVer == NULL)
{
hr = E_OUTOFMEMORY;
goto done;
}
// this function will never return a failure code. Intstead, check if
// both return values are 0
hr = GetVersionFromFileEx(pszPathForVer, &dwExistingMS, &dwExistingLS,
TRUE);
if (FAILED(hr) || (dwExistingMS == 0 && dwExistingLS == 0))
{
hr = E_FAIL;
goto done;
}
paucCurr->fDoUpgrade = fNewerFile(paucCurr->dwUpdateMS,
paucCurr->dwUpdateLS,
dwExistingMS,
dwExistingLS);
}
else
{
// if the file doesn't exist, obviously gotta replace it
paucCurr->fDoUpgrade = TRUE;
}
// if we don't need to update the file and it's not a parent file with
// resources, then can just bail at this point.
if (paucCurr->fDoUpgrade == FALSE)
{
if (paul != NULL ||
(paul == NULL && paucCurr->fNeedToCheckMui == FALSE))
{
hr = S_FALSE;
goto done;
}
}
else
{
DEBUGMSG("PASS 1 -- newer file in section %S", paucCurr->pszSectionName);
}
// get the cab and inf name. For non-MUI files, we fetch this out of the ini.
if (paul == NULL)
{
if (MyGetPrivateProfileString(paucCurr->pszSectionName,
AU_KEY_CAB_NAME,
paucCurr->szCabName,
ARRAYSIZE(paucCurr->szCabName),
pszCif) == FALSE)
{
hr = E_FAIL;
goto done;
}
// if there is no inf, "" is value of field, so we're ok ignoring a
// failure here
MyGetPrivateProfileString(paucCurr->pszSectionName,
AU_KEY_INF_NAME,
paucCurr->szInfName,
ARRAYSIZE(paucCurr->szInfName),
pszCif);
}
// for MUI files, we base it on the name of the cab from the parent file.
else
{
LPTSTR pszExt;
DWORD cchExt, cchName;
// make sure the buffer is big enuf
cch = lstrlen(paucParent->szCabName);
cchLang = lstrlen(paul->szAUName);
if (cch + cchLang >= ARRAYSIZE(paucCurr->szCabName))
{
hr = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
goto done;
}
hr = StringCchCopyEx(paucCurr->szCabName, ARRAYSIZE(paucCurr->szCabName),
paucParent->szCabName,
NULL, NULL, MISTSAFE_STRING_FLAGS);
if (FAILED(hr))
goto done;
// paucCurr->szCabName
for (pszExt = paucCurr->szCabName + cch, cchExt = 0;
pszExt > paucCurr->szCabName && *pszExt != _T('\\') && *pszExt != _T('.');
pszExt--, cchExt++);
// if we hit a backslash or the beginning of the string, then move the
// extension pointer to the NULL terminator.
if (*pszExt == _T('\\') || pszExt <= paucCurr->szCabName)
{
pszExt = paucCurr->szCabName + cch;
cchExt = 0;
}
cchName = (DWORD)(pszExt - paucCurr->szCabName);
// append the language to where the extension (if any) currently exists
hr = StringCchCopyEx(pszExt, ARRAYSIZE(paucCurr->szCabName) - cchName,
paul->szAUName,
NULL, NULL, MISTSAFE_STRING_FLAGS);
if (FAILED(hr))
goto done;
// if there is an extension, copy it over from the original string in
// the parent AU_COMPONENT
if (cchExt > 0)
{
hr = StringCchCopyEx(&paucCurr->szCabName[cchName + cchLang],
ARRAYSIZE(paucCurr->szCabName) - cchName - cchLang,
&paucParent->szCabName[cchName],
NULL, NULL, MISTSAFE_STRING_FLAGS);
if (FAILED(hr))
goto done;
}
}
if (ReplaceFileInPath(pszCif, paucCurr->szCabName,
paucCurr->szCabPath,
ARRAYSIZE(paucCurr->szCabPath)) == FALSE)
{
hr = E_FAIL;
goto done;
}
hr = BuildPaths(paucCurr, paucCurr->szFileName, NULL, pszCif, paul);
if (FAILED(hr))
goto done;
done:
return hr;
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// Function InstallUpdatedComponents()
//
//////////////////////////////////////////////////////////////////////////////////////////
HRESULT InstallUpdatedComponents(LPCTSTR pszSelfUpdateUrl,
LPCTSTR pszMuiUpdateUrl,
LPCTSTR pszIdentTxt,
LPCTSTR pszFileCacheDir,
LPCTSTR pszCif,
BOOL *pfInstalledWUAUENG)
{
USES_IU_CONVERSION;
AU_COMPONENT *paucRoot = NULL;
AU_COMPONENT *paucCurr = NULL;
AU_COMPONENT *paucParent = NULL;
AU_COMPONENT *paucMui = NULL;
AU_FILEINCAB *paufic = NULL;
HRESULT hr = S_OK;
HANDLE SfcRpcHandle = NULL;
LPTSTR pszSection = NULL;
TCHAR szSectionNames[1024];
TCHAR szSysDir[MAX_PATH + 1];
TCHAR szSrcPath[MAX_PATH + 1];
TCHAR szHelpFile[_MAX_FNAME + 1];
DWORD cchSectionNames, cch;
BOOL fFailedInstall = FALSE;
// MUI stuff
AU_LANGLIST aull;
DWORD cchMuiDir = 0, cchMuiDirAvail = 0;
DWORD cchHelpMuiDir = 0, cchHelpMuiDirAvail = 0;
TCHAR szMuiDir[MAX_PATH + 1];
TCHAR szHelpMuiDir[MAX_PATH + 1];
ZeroMemory(&aull, sizeof(aull));
aull.pszIdentFile = pszIdentTxt;
szMuiDir[0] = _T('\0');
szHelpMuiDir[0] = _T('\0');
*pfInstalledWUAUENG = FALSE;
SfcRpcHandle = SfcConnectToServer(NULL);
if (NULL == SfcRpcHandle)
{
hr = E_FAIL;
goto done;
}
// determine how many components there are to update.
cchSectionNames = GetPrivateProfileSectionNames(szSectionNames,
ARRAYSIZE(szSectionNames),
pszCif);
if ((ARRAYSIZE(szSectionNames) - 2) == cchSectionNames)
{
hr = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
goto done;
}
cchMuiDir = ARRAYSIZE(szMuiDir);
cchHelpMuiDir = ARRAYSIZE(szHelpMuiDir);
hr = GetMuiLangList(&aull, szMuiDir, &cchMuiDir, szHelpMuiDir, &cchHelpMuiDir);
if (FAILED(hr))
goto done;
cchMuiDirAvail = ARRAYSIZE(szMuiDir) - cchMuiDir;
cchHelpMuiDirAvail = ARRAYSIZE(szHelpMuiDir) - cchHelpMuiDir;
cch = GetSystemDirectory(szSysDir, ARRAYSIZE(szSysDir));
if (cch == 0 || cch >= ARRAYSIZE(szSysDir))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
// PASS 1: figure out which files to upgrade
for (pszSection = szSectionNames;
*pszSection != _T('\0');
pszSection += lstrlen(pszSection) + 1)
{
szHelpFile[0] = _T('\0');
// if we didn't need to upgrade the parent file from the previous pass
// then we don't need to alloc a new blob- just reuse the one from the
// previous pass. To signal this, we'll set paucParent to NULL if we
// add it to the linked list- note this covers us for the first time
// thru the loop cuz we initialize paucParent to NULL.
if (paucParent == NULL)
{
paucParent = (AU_COMPONENT *)malloc(sizeof(AU_COMPONENT));
if (paucParent == NULL)
{
hr = E_OUTOFMEMORY;
goto done;
}
}
ZeroMemory(paucParent, sizeof(AU_COMPONENT));
paucParent->fMuiFile = FALSE;
DEBUGMSG("PASS 1 -- section %S", pszSection);
paucParent->pszSectionName = pszSection;
if (MyGetPrivateProfileString(paucParent->pszSectionName,
AU_KEY_FILE_NAME,
paucParent->szFileName,
ARRAYSIZE(paucParent->szFileName),
pszCif) == FALSE)
{
hr = E_FAIL;
goto done;
}
if (aull.cLangs > 0)
{
UINT uiHasResources;
// see if we need to test for MUI file updates
uiHasResources = GetPrivateProfileInt(paucParent->pszSectionName,
AU_KEY_RESMOD_NAME,
0,
pszCif);
// if we do have resources, then check if we also have a helpfile
if (uiHasResources == 1)
{
paucParent->fNeedToCheckMui = TRUE;
if (MyGetPrivateProfileString(paucParent->pszSectionName,
AU_KEY_HELPFILE,
szHelpFile, ARRAYSIZE(szHelpFile),
pszCif) == FALSE)
{
szHelpFile[0] = _T('\0');
}
}
else
{
paucParent->fNeedToCheckMui = FALSE;
}
}
else
{
paucParent->fNeedToCheckMui = FALSE;
}
hr = ProcessFile(NULL, paucParent, szSysDir, NULL, pszCif);
if (FAILED(hr))
goto done;
if (paucParent->fNeedToCheckMui)
{
DWORD iLang;
DWORD cchParentFile;
cchParentFile = lstrlen(paucParent->szFileName);
if (cchParentFile + ARRAYSIZE(MUI_EXT) > ARRAYSIZE(paucParent->szFileName))
{
hr = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
goto done;
}
for (iLang = 0; iLang < aull.cLangs; iLang++)
{
// if we didn't need to upgrade the file from the previous
// pass then we don't need to alloc a new blob- just reuse
// the one from the previous pass.
if (paucMui == NULL)
{
paucMui = (AU_COMPONENT *)malloc(sizeof(AU_COMPONENT));
if (paucMui == NULL)
{
hr = E_OUTOFMEMORY;
goto done;
}
}
ZeroMemory(paucMui, sizeof(AU_COMPONENT));
paucMui->pszSectionName = paucParent->pszSectionName;
paucMui->fMuiFile = TRUE;
// ProcessFile does not expect a trailing backslash, so be sure
// not to add one. Note that we've checked the size of the
// buffer against the largest possible string it will contain
// above, so this should not fail.
// The directory is build with the MUI langauge name (4 hex chars)
hr = StringCchCopyEx(&szMuiDir[cchMuiDir], cchMuiDirAvail,
aull.rgpaulLangs[iLang]->szMuiName,
NULL, NULL, MISTSAFE_STRING_FLAGS);
if (FAILED(hr))
goto done;
// the filename for a language is the same as the parent file with
// a ".mui" added to the end
hr = StringCchCopyEx(paucMui->szFileName, ARRAYSIZE(paucMui->szFileName),
paucParent->szFileName,
NULL, NULL, MISTSAFE_STRING_FLAGS);
if (FAILED(hr))
goto done;
hr = StringCchCopyEx(&paucMui->szFileName[cchParentFile],
ARRAYSIZE(paucMui->szFileName) - cchParentFile,
MUI_EXT,
NULL, NULL, MISTSAFE_STRING_FLAGS);
if (FAILED(hr))
goto done;
hr = ProcessFile(paucParent, paucMui,
szMuiDir,
aull.rgpaulLangs[iLang],
pszCif);
if (FAILED(hr))
goto done;
// Clean up for the next language
szMuiDir[cchMuiDir] = _T('\0');
// don't need to update the file
if (paucMui->fDoUpgrade == FALSE)
continue;
if (szHelpFile[0] != _T('\0'))
{
paucMui->pNextFileInCab = (AU_FILEINCAB *)malloc(sizeof(AU_FILEINCAB));
if (paucMui->pNextFileInCab == NULL)
{
hr = E_OUTOFMEMORY;
goto done;
}
ZeroMemory(paucMui->pNextFileInCab, sizeof(AU_FILEINCAB));
hr = StringCchCopyEx(&szHelpMuiDir[cchHelpMuiDir], cchHelpMuiDirAvail,
aull.rgpaulLangs[iLang]->szMuiName,
NULL, NULL, MISTSAFE_STRING_FLAGS);
if (FAILED(hr))
goto done;
hr = BuildPaths(paucMui->pNextFileInCab,
szHelpFile, szHelpMuiDir,
pszCif,
aull.rgpaulLangs[iLang]);
if (FAILED(hr))
goto done;
}
// we do need to update the file, so add it to our list of files
// to update
paucMui->pNext = paucRoot;
paucRoot = paucMui;
paucMui = NULL;
}
}
// if we need to update the parent file, add it to our list of files to
// update
if (paucParent->fDoUpgrade)
{
paucParent->pNext = paucRoot;
paucRoot = paucParent;
paucParent = NULL;
}
}
// short cut the rest of the function if we have no work to do
hr = S_OK;
if (paucRoot == NULL)
goto done;
// PASS 2: bring down the required cabs
DWORD dwFlags = 0;
if (gpState->fInCorpWU())
{
dwFlags |= WUDF_DONTALLOWPROXY;
}
for (paucCurr = paucRoot; paucCurr != NULL; paucCurr = paucCurr->pNext)
{
LPCTSTR pszDownloadUrl;
pszDownloadUrl = (paucCurr->fMuiFile) ? pszMuiUpdateUrl : pszSelfUpdateUrl;
DEBUGMSG("PASS 2 -- downloading %S", paucCurr->szCabName);
// We have to install so bring down the full cab
hr = DownloadCab(ghServiceFinished,
paucCurr->szCabName,
pszDownloadUrl,
pszFileCacheDir,
dwFlags);
if (FAILED(hr))
{
DEBUGMSG("Failed to download %S (%#lx)", paucCurr->szCabName, hr);
goto done;
}
//Verify that the extracted file is a binary and it's subsystem matches that of the OS
if (FAILED(hr = IsBinaryCompatible(paucCurr->szExtractFilePath)))
{
DEBUGMSG("%S is not a valid binary file (error %#lx)", paucCurr->szExtractFilePath, hr);
goto done;
}
// Check version number against cif
DWORD dwNewMS, dwNewLS;
LPSTR pszTmp;
pszTmp = T2A(paucCurr->szExtractFilePath);
if (pszTmp == NULL)
{
hr = E_OUTOFMEMORY;
goto done;
}
// this function will never return a failure code. Intstead, check if
// both return values are 0
hr = GetVersionFromFileEx(pszTmp, &dwNewMS, &dwNewLS, TRUE /* get version */);
if (FAILED(hr) || (dwNewMS == 0 && dwNewLS == 0))
{
DEBUGMSG("Failed to get version info from %S (%#lx)", paucCurr->szExtractFilePath, hr);
goto done;
}
if (paucCurr->dwUpdateMS != dwNewMS ||
paucCurr->dwUpdateLS != dwNewLS)
{
hr = HRESULT_FROM_WIN32(ERROR_INSTALL_PACKAGE_VERSION);
DEBUGMSG("Version mismatch for %S - %d.%d.%d.%d vs %d.%d.%d.%d",
paucCurr->szExtractFilePath,
HIWORD(paucCurr->dwUpdateMS),
LOWORD(paucCurr->dwUpdateMS),
HIWORD(paucCurr->dwUpdateLS),
LOWORD(paucCurr->dwUpdateLS),
HIWORD(dwNewMS),
LOWORD(dwNewMS),
HIWORD(dwNewLS),
LOWORD(dwNewLS));
goto done;
}
}
hr = StringCchCopyEx(szSrcPath, ARRAYSIZE(szSrcPath), pszCif,
NULL, NULL, MISTSAFE_STRING_FLAGS);
if (FAILED(hr))
goto done;
PathRemoveFileSpec(szSrcPath);
// PASS 3: Copy files to *.new in destination directory.
for (paucCurr = paucRoot; paucCurr != NULL; paucCurr = paucCurr->pNext)
{
if (FAILED(hr = vAU_W2A(paucCurr->szCabPath,
paucCurr->a_szCabPath,
sizeof(paucCurr->a_szCabPath))))
{
fFailedInstall = TRUE;
goto done;
}
// copy all the files to their new locations
for (paufic = paucCurr; paufic != NULL; paufic = paufic->pNextFileInCab)
{
DEBUGMSG("PASS 3 -- copying %S --> %S",
paufic->szExtractFilePath,
paufic->szNewFilePath);
if ( !CopyFile(paufic->szExtractFilePath, paufic->szNewFilePath, FALSE) )
{
fFailedInstall = TRUE;
hr = E_FAIL;
goto done;
}
}
// this comparison is sufficient because we don't care if we replaced a
// MUI lang pack for wuaueng.dll. The reason is that the service runs
// as local system, which always uses the native language (and the
// service doesn't pop up UI anyway)
// we do, however, need to check for a winhttp update
if (StrCmpI(WUAUENG_DLL, paucCurr->szFileName) == 0 ||
StrCmpI(c_szWinHttpDll, paucCurr->szFileName) == 0)
{
*pfInstalledWUAUENG = TRUE;
}
}
// PASS 4: Move the <file>.new into its proper location
for (paucCurr = paucRoot; paucCurr != NULL; paucCurr = paucCurr->pNext)
{
// copy all the files to their new locations
for (paufic = paucCurr; paufic != NULL; paufic = paufic->pNextFileInCab)
{
if ( paufic->fFileExists )
{
DEBUGMSG("PASS 4 -- renaming %S --> %S", paufic->szFilePath, paufic->szBackupFilePath);
if ( !SfcMoveFileEx(paufic->szFilePath, paufic->szBackupFilePath,
paufic->szFilePath, SfcRpcHandle) )
{
fFailedInstall = TRUE;
hr = E_FAIL;
goto done;
}
paufic->fCreatedBackup = TRUE;
}
DEBUGMSG("PASS 4 -- renaming %S --> %S", paufic->szNewFilePath, paufic->szFilePath);
if (!MoveFileEx(paufic->szNewFilePath, paufic->szFilePath, MOVEFILE_REPLACE_EXISTING))
{
fFailedInstall = TRUE;
hr = E_FAIL;
goto done;
}
}
}
// PASS 5: Run any .inf file.
for (paucCurr = paucRoot; paucCurr != NULL; paucCurr = paucCurr->pNext)
{
if (paucCurr->szInfName[0] != _T('\0'))
{
DEBUGMSG("PASS 5A -- executing inf %S", paucCurr->szInfName);
CABINFO cabinfo;
HRESULT hr2;
cabinfo.pszCab = paucCurr->a_szCabPath;
cabinfo.pszInf = paucCurr->a_szInfName;
if (FAILED( hr2 = vAU_W2A(paucCurr->szInfName, paucCurr->a_szInfName, sizeof(paucCurr->a_szInfName)))
|| FAILED(hr2 = vAU_W2A(szSrcPath, cabinfo.szSrcPath, sizeof(cabinfo.szSrcPath))))
{
DEBUGMSG("vAU_W2A failed: %#lx", hr2);
if (SUCCEEDED(hr))
{
hr = hr2;
fFailedInstall = TRUE;
}
// don't delete the backup file. Need to restore it afterwards.
continue;
}
cabinfo.pszSection = "DefaultInstall";
cabinfo.dwFlags = ALINF_QUIET;
if ( FAILED(hr2 = ExecuteCab(NULL, &cabinfo, NULL)) )
{
DEBUGMSG("ExecuteCab failed on %s (%#lx)", paucCurr->a_szInfName, hr2);
if (SUCCEEDED(hr))
{
hr = hr2;
fFailedInstall = TRUE;
}
// don't delete the backup file. Need to restore it afterwards.
continue;
}
}
for (paufic = paucCurr; paufic != NULL; paufic = paufic->pNextFileInCab)
{
// delete the backup file corresponding to the .inf which was successfully installed
if (paufic->fCreatedBackup &&
StrCmpI(WUAUENG_DLL, paucCurr->szFileName) != 0)
{
DEBUGMSG("PASS 5B - deleting bak file %S", paufic->szBackupFilePath);
if ( DeleteFile(paufic->szBackupFilePath) )
{
paufic->fCreatedBackup = FALSE;
}
#ifdef DBG
else
{
DEBUGMSG("Could not delete %S (error %d)", paufic->szBackupFilePath, GetLastError());
}
#endif
}
}
}
done:
// if we failed an install, revert all the prior installs
if ( fFailedInstall )
{
for (paucCurr = paucRoot; paucCurr != NULL; paucCurr = paucCurr->pNext)
{
for(paufic = paucCurr; paufic != NULL; paufic = paufic->pNextFileInCab)
{
if (paufic->fCreatedBackup)
{
DEBUGMSG("Reverting %S --> %S", paufic->szBackupFilePath, paufic->szFilePath);
MoveFileEx(paufic->szBackupFilePath, paufic->szFilePath, MOVEFILE_REPLACE_EXISTING);
}
}
}
}
if (paucParent != NULL)
free(paucParent);
if (paucMui != NULL)
{
while (paucMui->pNextFileInCab != NULL)
{
paufic = paucMui->pNextFileInCab;
paucMui->pNextFileInCab = paucMui->pNextFileInCab->pNextFileInCab;
free(paufic);
}
free(paucMui);
}
// cleanup the linked list of files
while(paucRoot != NULL)
{
paucCurr = paucRoot;
paucRoot = paucCurr->pNext;
while (paucCurr->pNextFileInCab != NULL)
{
paufic = paucCurr->pNextFileInCab;
paucCurr->pNextFileInCab = paucCurr->pNextFileInCab->pNextFileInCab;
free(paufic);
}
free(paucCurr);
}
// cleanup the MUI stuff
CleanupMuiLangList(&aull);
if ( NULL != SfcRpcHandle )
{
SfcClose(SfcRpcHandle);
}
return hr;
}
////////////////////////////////////////////////////////////////////////////
//
// fConvertDotVersionStrToDwords
//
////////////////////////////////////////////////////////////////////////////
BOOL fConvertVersionStrToDwords(LPTSTR pszVer, LPDWORD pdwMS, LPDWORD pdwLS)
{
DWORD grVerFields[4] = {0,0,0,0};
TCHAR *pch = pszVer;
int i;
// _ttol will stop when it hits a non-numeric character, so we're
// safe calling it this way
grVerFields[0] = _ttol(pch);
for (i = 1; i < 4; i++)
{
while (*pch != _T('\0') && _istdigit(*pch))
pch++;
if (*pch == _T('\0'))
break;
pch++;
// _ttol will stop when it hits a non-numeric character, so we're
// safe calling it this way
grVerFields[i] = _ttol(pch);
}
*pdwMS = (grVerFields[0] << 16) + grVerFields[1];
*pdwLS = (grVerFields[2] << 16) + grVerFields[3];
return true;
}
////////////////////////////////////////////////////////////////////////////
//
// MyGetPrivateProfileString
//
// Same as normal call but if buffer is too small or default string is returned
// then function returns FALSE.
////////////////////////////////////////////////////////////////////////////
BOOL MyGetPrivateProfileString( IN LPCTSTR lpAppName,
IN LPCTSTR lpKeyName,
OUT LPTSTR lpReturnedString,
IN DWORD nSize,
IN LPCTSTR lpFileName,
IN LPCTSTR lpDefault)
{
BOOL fRet = TRUE;
if (NULL == lpAppName || NULL == lpKeyName || NULL == lpDefault || NULL == lpReturnedString)
{
return FALSE;
}
DWORD dwRet = GetPrivateProfileString(lpAppName,
lpKeyName,
lpDefault,
lpReturnedString,
nSize,
lpFileName);
if ( ((nSize - 1) == dwRet) || (_T('\0') == *lpReturnedString) )
{
fRet = FALSE;
}
return fRet;
}