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