|
|
//+-------------------------------------------------------------------------
//
// 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; }
|