|
|
#include <windows.h>
#include <urlmon.h>
#include <wininet.h>
#include "resource.h"
#include "advpext.h"
#include "download.h"
#include "patchdownload.h"
#include "util.h"
extern "C" { #include "patchapi.h"
#include "redblack.h"
#include "crc32.h"
}
extern HINF g_hInf; extern HINSTANCE g_hInstance; extern HWND g_hProgressDlg; extern BOOL g_fAbort;
HANDLE g_hDownloadProcess = NULL;
HRESULT WINAPI DownloadAndPatchFiles(DWORD dwFileCount, DOWNLOAD_FILEINFO* pFileInfo, LPCSTR lpszUrl, LPCSTR lpszPath, PATCH_DOWNLOAD_CALLBACK pfnCallback, LPVOID lpvContext) { HRESULT hr = S_OK; CSiteMgr csite;
hr = LoadSetupAPIFuncs(); if(FAILED(hr)) { return hr; }
SetProgressText(IDS_INIT); //Download the sites.dat file
hr = csite.Initialize(lpszUrl); if(FAILED(hr)) { return hr; }
CPatchDownloader cpdwn(pFileInfo, dwFileCount, pfnCallback); return cpdwn.InternalDownloadAndPatchFiles(lpszPath, &csite, lpvContext); }
HRESULT CPatchDownloader::InternalDownloadAndPatchFiles(LPCTSTR lpszPath, CSiteMgr* pSite, LPVOID lpvContext) {
HRESULT hr = S_OK; PATCH_THREAD_INFO PatchThreadInfo; HANDLE hThreadPatcher = NULL; ULONG DownloadClientId = 0; int nCount = 0; LPTSTR lpszUrl; BOOL fUseWin9xDirectory = FALSE;
if(!GetTempPath(sizeof(m_lpszDownLoadDir), m_lpszDownLoadDir)) { //Unable to get temp folder, Create a folder in the path sent to us
wsprintf(m_lpszDownLoadDir, "%s\\%s", lpszPath, "AdvExt"); }
//Since the binary is different for NT and 9x, because of binding, we cannot put the files
//under same directory on server. So for 9x put it under a subdirectory and modify the
//url accordingly.
HKEY hKey; OSVERSIONINFO osvi; osvi.dwOSVersionInfoSize = sizeof(osvi); GetVersionEx(&osvi);
if(VER_PLATFORM_WIN32_NT != osvi.dwPlatformId && ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Advanced INF Setup", 0, KEY_ALL_ACCESS, &hKey)) { DWORD dwSize = sizeof(DWORD); if(ERROR_SUCCESS == RegQueryValueEx(hKey, "Usewin9xDirectory", 0, 0, (LPBYTE)&fUseWin9xDirectory, &dwSize)) { RegDeleteValue(hKey, "Usewin9xDirectory"); }
RegCloseKey(hKey); }
if (DownloadClientId == 0) {
// Need to generate a unique DownloadClientId for this machine, but
// it needs to be consistent (persistent) if same machine downloads
// twice, even across process destroy/restart. First we check the
// registry to see if we have previously generated a unique Id for
// this machine, and use that if we find it. Otherwise, we generate
// a unique DownloadClientId and store it in the registry so future
// instances will use the same value.
//
LONG RegStatus; HKEY hKey; DWORD dwHow; DWORD dwValueSize; DWORD dwValueType; DWORD dwValue;
RegStatus = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Advanced INF Setup\\AdvExt", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwHow );
if ( RegStatus == ERROR_SUCCESS ) {
dwValueSize = sizeof(dwValue); RegStatus = RegQueryValueEx(hKey, "DownloadClientId", NULL, &dwValueType, (LPBYTE)&dwValue, &dwValueSize);
dwValue &= 0xFFFFFFF0;
if ((RegStatus == ERROR_SUCCESS) && (dwValueType == REG_DWORD) && (dwValue != 0)) { DownloadClientId = dwValue; } else {
DownloadClientId = GenerateUniqueClientId(); dwValue = DownloadClientId;
RegSetValueEx(hKey, "DownloadClientId", 0, REG_DWORD, (LPBYTE)&dwValue, sizeof(dwValue)); }
RegCloseKey( hKey ); } else { // Failed to open/create registry key, so fall back to just
// creating a unique ID for this process instance. At least
// it will show the same client id if the user hits "retry".
//
DownloadClientId = GenerateUniqueClientId(); } }
m_hSubAllocator = CreateSubAllocator(0x10000, 0x10000); if(!m_hSubAllocator) { hr = HRESULT_FROM_WIN32(GetLastError()); WriteToLog("Memory allocation failed. Can't do much. Exiting with hr=%1!lx!\n", hr); goto done; }
//Set the parameters that needs to be passed on to the thread.
if(!m_lpfnCallback) { m_lpfnCallback = PatchCallback; m_lpvContext = (LPVOID)lpszPath; } else { m_lpvContext = lpvContext; }
PatchThreadInfo.hFileDownloadEvent = _hDL; PatchThreadInfo.FileListInfo.FileList = m_lpFileInfo; PatchThreadInfo.FileListInfo.FileCount = m_dwFileCount; PatchThreadInfo.FileListInfo.Callback = m_lpfnCallback; PatchThreadInfo.FileListInfo.CallbackContext = m_lpvContext;
PatchThreadInfo.lpdwnProgressInfo = &m_DownloadInfo; m_DownloadInfo.dwFilesRemaining = m_dwFileCount; m_DownloadInfo.dwFilesToDownload = m_dwFileCount; m_DownloadInfo.dwBytesToDownload = 0; m_DownloadInfo.dwBytesRemaining = 0;
//Create an event to signal patchthread is ready to process download request
g_hDownloadProcess = CreateEvent(NULL, TRUE, FALSE, NULL);
if(!g_hDownloadProcess) { hr = HRESULT_FROM_WIN32(GetLastError()); WriteToLog("Create event failed with error code:%1!lx!\n", hr); goto done; }
//Do till we have download files or we retry 3 times
while(nCount++ < 3 && m_DownloadInfo.dwFilesRemaining && !g_fAbort) { WriteToLog("\n%1!d! try: Number of Files:%2!d!\n", nCount, m_DownloadInfo.dwFilesRemaining); _hDLResult = 0;
ResetEvent(g_hDownloadProcess); hThreadPatcher = CreateThread(NULL, 0, PatchThread, &PatchThreadInfo, 0, &m_dwPatchThreadId); if(!hThreadPatcher) { hr = HRESULT_FROM_WIN32(GetLastError()); goto done; }
//Generate the request buffer that would be sent on POST
hr = CreateRequestBuffer(DownloadClientId); if(FAILED(hr)) { WriteToLog("\nCreateRequestBuffer failed with error code:%1!lx!\n", hr); goto done; } //Get the url from where bytes needs to be downloaded.
if(!pSite->GetNextSite(&lpszUrl, &m_lpszSiteName)) { WriteToLog("GetNextSite returned false. No site info??"); hr = E_UNEXPECTED; goto done; }
TCHAR szURL[INTERNET_MAX_URL_LENGTH];
if(fUseWin9xDirectory) { lstrcpy(szURL, lpszUrl); if(*(lpszUrl + lstrlen(lpszUrl) - 1) == '/') { lstrcat(szURL, "win9x"); } else { lstrcat(szURL, "/win9x"); } lpszUrl = szURL; }
//Notify callback we are about to begin download
ProtectedPatchDownloadCallback(m_lpfnCallback, PATCH_DOWNLOAD_BEGIN, (LPVOID)lpszUrl, m_lpvContext); hr = DoDownload(lpszUrl, NULL); WriteToLog("DownloadFile returned:%1!lx!\n\n", hr); SetProgressText(IDS_CLEANUP);
//Ask the patch thread to quit once it finishes download.
SetEvent(_hDL);
//Wait till patch thread finishes its work
while(1) { DWORD dw = MsgWaitForMultipleObjects(1, &hThreadPatcher, FALSE, 1000, QS_ALLINPUT); if(dw == WAIT_OBJECT_0) { break; } MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { DispatchMessage(&msg); } }
CloseHandle(hThreadPatcher); hThreadPatcher = NULL; //Setup the downloadinfo structure, incase we need to re-download some files
m_DownloadInfo.dwFilesToDownload = m_DownloadInfo.dwFilesRemaining; m_DownloadInfo.dwBytesToDownload = 0; m_DownloadInfo.dwBytesRemaining = 0; if(m_DownloadInfo.dwFilesToDownload) { SetProgressText(IDS_RETRY); } m_dwServerFileCount=0; ResetEvent(_hDL); }
done:
if(g_hDownloadProcess) { CloseHandle(g_hDownloadProcess); }
if(!hr && m_DownloadInfo.dwFilesToDownload) { hr = E_FAIL; WriteToLog("\nSome files could not be downloaded\n"); }
WriteToLog("DownloadAndPatchFiles returning:%1!lx!\n", hr); return hr; }
HRESULT CPatchDownloader :: CreateRequestBuffer(DWORD dwDownloadClientID) { LPTSTR lpRequestPointer, lpFileNamePortionOfRequest; HRESULT hr = S_OK; DWORD i; DWORD dwHeapSize = 64*1024;
if(!m_lpFileInfo) { return E_INVALIDARG; }
m_lpszRequestBuffer = (LPTSTR)ResizeBuffer(NULL, dwHeapSize, FALSE); if(!m_lpszRequestBuffer) { hr = HRESULT_FROM_WIN32(GetLastError()); goto done; }
lpRequestPointer = m_lpszRequestBuffer;
lpRequestPointer += wsprintf(lpRequestPointer, "SessionId:%u\n", dwDownloadClientID); lpRequestPointer += wsprintf(lpRequestPointer, "FileList:%d\n", m_DownloadInfo.dwFilesToDownload);
WriteToLog("Download ClientID:%1!lx! Number of Files:%2!d!\n", dwDownloadClientID, m_DownloadInfo.dwFilesToDownload);
lpFileNamePortionOfRequest = lpRequestPointer;
for(i=0; i < m_dwFileCount; i++) { if ((DWORD)( lpRequestPointer - m_lpszRequestBuffer ) > (DWORD)( dwHeapSize - (DWORD)MAX_PATH )) { dwHeapSize = dwHeapSize * 2; m_lpszRequestBuffer = (LPTSTR)ResizeBuffer(m_lpszRequestBuffer, dwHeapSize, FALSE); if(!m_lpszRequestBuffer) { hr = HRESULT_FROM_WIN32(GetLastError()); goto done; } }
if(m_lpFileInfo[i].dwFlags != PATCHFLAG_DOWNLOAD_NEEDED) { //Probably already downloaded
continue; }
if ((m_lpFileInfo[i].lpszExistingFilePatchSignature == NULL ) || (*m_lpFileInfo[i].lpszExistingFilePatchSignature == 0 )) { // No file to patch from, request whole file.
lpRequestPointer += wsprintf(lpRequestPointer, "%s\n", m_lpFileInfo[i].lpszFileNameToDownload); } else { lpRequestPointer += wsprintf(lpRequestPointer, "%s,%s\n", m_lpFileInfo[i].lpszFileNameToDownload, m_lpFileInfo[i].lpszExistingFilePatchSignature); }
}
// Now terminate list with "empty" entry.
*lpRequestPointer++ = '\n'; *lpRequestPointer++ = 0;
m_dwRequestDataLength = lpRequestPointer - m_lpszRequestBuffer;
// Now lowercase all the filenames in the request (this offloads the case consistency work from
// the server -- the server expects the request to be all lowercase).
MyLowercase(lpFileNamePortionOfRequest); WriteToLog("RequestBuffer: Size=%1!d!\n\n", m_dwRequestDataLength); WriteToLog("%1", m_lpszRequestBuffer);
done: if(FAILED(hr)) { ResizeBuffer(m_lpszRequestBuffer, 0, 0); }
WriteToLog("\nCreateRequestBuffer returning %1!lx!\n", hr); return hr; }
CPatchDownloader::CPatchDownloader(DOWNLOAD_FILEINFO* pdwn, DWORD dwFileCount, PATCH_DOWNLOAD_CALLBACK lpfn) { m_lpFileInfo = pdwn; m_dwFileCount = dwFileCount; m_lpfnCallback = lpfn; m_hCurrentFileHandle = NULL; m_dwCurrentFileIndex = 0; m_lpFileList = NULL; m_dwServerFileCount = 0; }
CPatchDownloader::~CPatchDownloader() { DestroySubAllocator(m_hSubAllocator); }
STDMETHODIMP CPatchDownloader::GetBindInfo( DWORD *grfBINDF, BINDINFO *pbindInfo) { *grfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA | BINDF_RESYNCHRONIZE | BINDF_NOWRITECACHE; pbindInfo->cbSize = sizeof(BINDINFO); memset(&pbindInfo->stgmedData, 0, sizeof(STGMEDIUM)); pbindInfo->stgmedData.tymed = TYMED_HGLOBAL; pbindInfo->stgmedData.hGlobal = m_lpszRequestBuffer; pbindInfo->grfBindInfoF = BINDINFOF_URLENCODESTGMEDDATA; pbindInfo->dwBindVerb = BINDVERB_POST; pbindInfo->szCustomVerb = NULL; pbindInfo->cbstgmedData = m_dwRequestDataLength; pbindInfo->szExtraInfo = NULL; return(NOERROR); }
STDMETHODIMP CPatchDownloader::OnProgress(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR pwzStatusText) { int PatchStatusCode = -1; UINT uID; switch(ulStatusCode) { case BINDSTATUS_FINDINGRESOURCE: PatchStatusCode = PATCH_DOWNLOAD_FINDINGSITE; uID = IDS_BINDS_FINDING; break;
case BINDSTATUS_CONNECTING: PatchStatusCode = PATCH_DOWNLOAD_CONNECTING; uID = IDS_BINDS_CONN; break;
case BINDSTATUS_BEGINDOWNLOADDATA: PatchStatusCode = PATCH_DOWNLOAD_DOWNLOADINGDATA; uID = IDS_BINDS_DOWNLOADING; break;
case BINDSTATUS_ENDDOWNLOADDATA: PatchStatusCode = PATCH_DOWNLOAD_ENDDOWNLOADINGDATA; uID = IDS_BINDS_ENDDOWNLOAD; break; }
if(PatchStatusCode != -1 && pwzStatusText) { TCHAR szBuffer[MAX_PATH], szTemplate[MAX_PATH]; LoadString(g_hInstance, uID, szTemplate, sizeof(szTemplate)); wsprintf(szBuffer, szTemplate, m_lpszSiteName); ProtectedPatchDownloadCallback(m_lpfnCallback, (PATCH_DOWNLOAD_REASON)PatchStatusCode, szBuffer, m_lpvContext); } return NOERROR; }
STDMETHODIMP CPatchDownloader::OnDataAvailable(DWORD grfBSCF, DWORD dwSize, FORMATETC *pFmtetc, STGMEDIUM *pstgmed) { HRESULT hr = NOERROR; TCHAR szBuffer[4096]; DWORD dwRead = 0, dwWritten=0; do { hr = pstgmed->pstm->Read(szBuffer, 4096, &dwRead); if((SUCCEEDED(hr) || (hr == E_PENDING)) && dwRead > 0) { if(!ProcessDownloadChunk(szBuffer, dwRead)) { WriteToLog("ProcessDownloadChunk returning FALSE. Aborting downloading\n"); hr = E_ABORT; } } } while (hr == NOERROR && !g_fAbort);
if(g_fAbort) Abort(); return hr; }
BOOL CPatchDownloader :: ProcessDownloadChunk(LPTSTR lpBuffer, DWORD dwLength) { CHAR TargetFile[ MAX_PATH ]; ULONG Actual; ULONG WriteSize; BOOL Success;
if ( m_dwServerFileCount == 0 ) {
//
// Haven't processed headers yet.
//
// We expect header to look like this:
//
// "<head><title>"
// "Download Stream of Files"
// "</title></head>\n"
// "<body>\n"
// "FileList:%d\n"
// "filename,%d\n"
// "filename,%d\n"
// ...etc...
// "filename,%d\n"
// "</body>\n"
//
// BUGBUG: if headers don't all fit in first chunk, we're screwed.
//
PCHAR EndOfHeader; PCHAR FileCountText; PCHAR FileNameText; PCHAR FileSizeText; ULONG FileSize; ULONG FileBytes; ULONG i; PCHAR p;
EndOfHeader = ScanForSequence(lpBuffer, dwLength, "</body>\n", sizeof( "</body>\n" ) - 1 // not including terminator
);
if( EndOfHeader == NULL ) { SetLastError( ERROR_INVALID_DATA ); return FALSE; }
EndOfHeader += sizeof( "</body>\n" ) - 1 ;
p = ScanForSequence(lpBuffer, EndOfHeader - lpBuffer, "FileList:", sizeof( "FileList:" ) - 1);
if ( p == NULL ) { SetLastError( ERROR_INVALID_DATA ); return FALSE; }
p += sizeof( "FileList:" ) - 1;
FileCountText = p;
p = ScanForChar( p, '\n', EndOfHeader - p );
*p++ = 0;
m_dwServerFileCount = StrToInt( FileCountText ); WriteToLog("Total Files to be downloaded:%1!d!\n", m_dwServerFileCount);
if(m_dwServerFileCount == 0 ) { SetLastError( ERROR_INVALID_DATA ); return FALSE; }
m_lpFileList = (LPFILE) SubAllocate(m_hSubAllocator, m_dwServerFileCount * sizeof(FILE)); if(!m_lpFileList) { return FALSE; }
m_dwCurrentFileIndex = 0; FileBytes = 0;
for ( i = 0; i < m_dwServerFileCount; i++ ) {
FileNameText = p; p = ScanForChar( p, ',', EndOfHeader - p );
if (( p == NULL ) || ( p == FileNameText )) { SetLastError( ERROR_INVALID_DATA ); return FALSE; }
*p++ = 0;
FileSizeText = p;
p = ScanForChar( p, '\n', EndOfHeader - p );
if ( p == NULL ) { SetLastError( ERROR_INVALID_DATA ); return FALSE; }
*p++ = 0;
FileSize = TextToUnsignedNum(FileSizeText);
if ( FileSize == 0 ) { SetLastError( ERROR_INVALID_DATA ); return FALSE; }
FileBytes += FileSize;
m_lpFileList[i].dwFileSize = FileSize; m_lpFileList[i].lpszFileName = MySubAllocStrDup(m_hSubAllocator, FileNameText);
if (m_lpFileList[i].lpszFileName == NULL) { return FALSE; }
WriteToLog("File Name:%1 \t File Size:%2!d!\n", m_lpFileList[i].lpszFileName, m_lpFileList[i].dwFileSize); }
// If we get to here, all the files in the header have been processed,
// so we can set the state variables and continue with parsing raw
// file data.
m_DownloadInfo.dwBytesToDownload = FileBytes; m_DownloadInfo.dwBytesRemaining = FileBytes; dwLength -= ( EndOfHeader - lpBuffer ); lpBuffer = EndOfHeader;
WriteToLog("\nTotal %1!d! bytes(%2!d! Files) to be downloaded\n", FileBytes, m_dwServerFileCount);
}
// Process raw file info.
m_DownloadInfo.dwBytesRemaining -= dwLength; if(!ProtectedPatchDownloadCallback(m_lpfnCallback, PATCH_DOWNLOAD_PROGRESS, (LPVOID)&m_DownloadInfo, m_lpvContext)) { g_fAbort = TRUE; return FALSE; }
while(dwLength > 0) {
if (m_hCurrentFileHandle == NULL || m_hCurrentFileHandle == INVALID_HANDLE_VALUE) {
if (m_dwCurrentFileIndex >= m_dwServerFileCount) { SetLastError( ERROR_INVALID_DATA ); return FALSE; // more data than we expected
}
// Now open this file.
CombinePaths(m_lpszDownLoadDir, m_lpFileList[m_dwCurrentFileIndex].lpszFileName, TargetFile );
m_dwCurrentFileSize = m_lpFileList[m_dwCurrentFileIndex].dwFileSize; m_dwCurrFileSizeRemaining = m_dwCurrentFileSize; m_hCurrentFileHandle = CreateFile(TargetFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
if (m_hCurrentFileHandle == INVALID_HANDLE_VALUE ) { return FALSE; } }
WriteSize = min( dwLength, m_dwCurrFileSizeRemaining);
Success = WriteFile(m_hCurrentFileHandle, lpBuffer, WriteSize, &Actual, NULL);
if(!Success) { return FALSE; }
if(Actual != WriteSize) { SetLastError( ERROR_INVALID_PARAMETER ); WriteToLog("Error:Actual size not equal to write size for %1. Aborting\n", m_lpFileList[m_dwCurrentFileIndex].lpszFileName); return FALSE; }
m_dwCurrFileSizeRemaining -= WriteSize;
if(m_dwCurrFileSizeRemaining == 0 ) {
CloseHandle(m_hCurrentFileHandle); m_hCurrentFileHandle = NULL;
// Pass this file off to the patch thread.
LPTSTR lpszFileName = (LPTSTR)ResizeBuffer(NULL, MAX_PATH, FALSE); CombinePaths(m_lpszDownLoadDir, m_lpFileList[m_dwCurrentFileIndex].lpszFileName, TargetFile ); lstrcpy(lpszFileName, TargetFile); WaitForSingleObject(g_hDownloadProcess, 10000); PostThreadMessage(m_dwPatchThreadId, WM_FILEAVAILABLE, 0, (LPARAM)lpszFileName); m_dwCurrentFileIndex += 1; }
lpBuffer += WriteSize; dwLength -= WriteSize; }
return TRUE; }
DWORD WINAPI PatchThread(IN LPVOID ThreadParam) { PPATCH_THREAD_INFO PatchThreadInfo = (PPATCH_THREAD_INFO) ThreadParam; PFILE_LIST_INFO FileListInfo = &PatchThreadInfo->FileListInfo; PDOWNLOAD_INFO ProgressInfo = PatchThreadInfo->lpdwnProgressInfo; NAME_TREE FileNameTree; PNAME_NODE FileNameNode; PDOWNLOAD_FILEINFO FileInfo; DWORD Status; BOOL bIsPatch; HANDLE hSubAllocator; ULONG i; BOOL fSuccess, fQuit = FALSE; MSG msg;
//
// First thing we need to do is construct a btree of the filenames
// we expect to get in the queue so we can quickly find the corresponding
// FileList entry when given a filename by the downloader. It will take
// the downloader a little while to get connected, so this CPU intensive
// task shouldn't slow anything down.
//
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST); PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); hSubAllocator = CreateSubAllocator( 0x10000, 0x10000 );
if ( hSubAllocator == NULL ) { return ERROR_NOT_ENOUGH_MEMORY; }
NameRbInitTree( &FileNameTree, hSubAllocator ); TCHAR SourceFileName[MAX_PATH];
for ( i = 0; i < FileListInfo->FileCount; i++ ) {
if(FileListInfo->FileList[ i ].dwFlags != PATCHFLAG_DOWNLOAD_NEEDED) { //Probably already downloaded and this is the second attempt
continue; }
lstrcpy( SourceFileName, FileListInfo->FileList[ i ].lpszFileNameToDownload); MyLowercase( SourceFileName );
FileNameNode = NameRbInsert(&FileNameTree, SourceFileName);
if ( FileNameNode == NULL ) { DestroySubAllocator( hSubAllocator ); return ERROR_NOT_ENOUGH_MEMORY; }
if (FileNameNode->Context != NULL ) {
//
// BUGBUG: Same filename in list twice. Should never be the
// case since we check for duplicates before putting
// them in the queue.
//
}
FileNameNode->Context = &FileListInfo->FileList[ i ];
// Now add another node in the tree based on the compressed filename.
ConvertToCompressedFileName( SourceFileName );
FileNameNode = NameRbInsert(&FileNameTree, SourceFileName);
if ( FileNameNode == NULL ) { DestroySubAllocator( hSubAllocator ); return ERROR_NOT_ENOUGH_MEMORY; }
if ( FileNameNode->Context != NULL ) {
// BUGBUG: Same filename in list twice. This can happen if two
// different files collide on a compressed name (like
// foo.db1 and foo.db2 colliding on foo.db_).
//
// We don't have a good solution for this right now.
//
}
//Set the contect to the file info. When we get back the file from server, we can get the full
//info about this
FileNameNode->Context = &FileListInfo->FileList[ i ];
//Make sure we are not asked to quit
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) && msg.message == WM_QUIT) { goto done; } }
//
// Now wait for file downloads to be delivered to us.
//
SetEvent(g_hDownloadProcess); while (!g_fAbort && !fQuit) { LPTSTR lpszDownloadFileName, lpszSourceFileName; TCHAR szRealFileName[MAX_PATH];
//
// We're going to wait here with a timeout so that if the download
// is stuck in InternetReadFile waiting for data, we can keep a
// heartbeat going to the progress dialog and also check for cancel.
//
Status = MsgWaitForMultipleObjects(1, &PatchThreadInfo->hFileDownloadEvent, FALSE, 1000, QS_ALLINPUT); if (Status == WAIT_TIMEOUT ) { //Keep updating the callback
fSuccess = ProtectedPatchDownloadCallback(FileListInfo->Callback, PATCH_DOWNLOAD_PROGRESS, ProgressInfo, FileListInfo->CallbackContext); if (!fSuccess) { g_fAbort = TRUE; break; } continue; }
if(Status == WAIT_OBJECT_0) { fQuit = TRUE; } while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if(msg.message == WM_FILEAVAILABLE) { lpszDownloadFileName = (LPTSTR)msg.lParam; } else { continue; }
// Ok, now we have a filename lpszDownloadFileName that was just downloaded to the
// temp directory.The filename may be in one of the following
// forms:
// foo.dll
// foo.dl_
// foo.dll._p
//
// We have both "foo.dll" and "foo.dl_" in our name tree, but we
// don't have "foo.dll._p", so we look for that form of the name
// first and convert it to "foo.dll" before looking in name tree.
fSuccess = TRUE; lpszSourceFileName = PathFindFileName(lpszDownloadFileName); ASSERT(lpszSourceFileName);
lstrcpyn(szRealFileName, lpszDownloadFileName, lpszSourceFileName - lpszDownloadFileName + 1);
MyLowercase(lpszSourceFileName); LPTSTR lpExt = PathFindExtension(lpszSourceFileName); bIsPatch = FALSE;
if(lpExt && !lstrcmp(lpExt, "._p")) { bIsPatch = TRUE; *lpExt = 0; // truncate trailing "._p" to leave base file name
}
FileNameNode = NameRbFind( &FileNameTree, lpszSourceFileName);
if ( bIsPatch ) { *lpExt = '.'; // restore complete patch source file name
}
if (FileNameNode != NULL) {
FileInfo = (PDOWNLOAD_FILEINFO)FileNameNode->Context; lstrcat(szRealFileName, FileInfo->lpszFileNameToDownload); if ( bIsPatch ) { fSuccess = ApplyPatchToFile( lpszDownloadFileName, // patch file
FileInfo->lpszExistingFileToPatchFrom, // old file
szRealFileName, // new file
0 ); } else {
FixTimeStampOnCompressedFile(lpszSourceFileName); if(lstrcmpi(lpszDownloadFileName, szRealFileName)) { fSuccess = MySetupDecompressOrCopyFile( lpszDownloadFileName, // compressed or whole file
szRealFileName // new file
); } }
if (fSuccess) { //Notify callback. If it thinks that the hash is incorrect, don't mark the file
//as downloaded, so that we may retry download of this file.
fSuccess = VerifyHash(szRealFileName);
if(fSuccess) { fSuccess = ProtectedPatchDownloadCallback(FileListInfo->Callback, PATCH_DOWNLOAD_FILE_COMPLETED, szRealFileName, FileListInfo->CallbackContext); if(fSuccess == FALSE) { //If callback returned false, we need to abort
WriteToLog("\tDownload complete callback returned false. Aborting\n"); ProtectedPatchDownloadCallback(FileListInfo->Callback, PATCH_DOWNLOAD_ABORT, NULL, FileListInfo->CallbackContext); break; } else { FileInfo->dwFlags = 0; //Notify callback that 1 file was successfully downloaded
WriteToLog("\tSuccesssfully downloaded %1\n", FileInfo->lpszFileNameToDownload); ProgressInfo->dwFilesRemaining -= 1; fSuccess = ProtectedPatchDownloadCallback(FileListInfo->Callback, PATCH_DOWNLOAD_PROGRESS, ProgressInfo, FileListInfo->CallbackContext); if(!fSuccess) { g_fAbort = TRUE; return FALSE; }
} } else { //Mark it so that we resend the request. Remove patch signature so we get full files instead
// of patches.
WriteToLog("\tHash Incorrect. Need to Re-download %1\n", FileInfo->lpszFileNameToDownload); FileInfo->dwFlags = PATCHFLAG_DOWNLOAD_NEEDED; if(FileInfo->lpszExistingFilePatchSignature) { LocalFree(FileInfo->lpszExistingFilePatchSignature); FileInfo->lpszExistingFilePatchSignature = NULL; } }
} else { // Patch or decompress failed. notify callback it failed.
WriteToLog("\tPatch or decompression failed for %1\n", FileInfo->lpszFileNameToDownload); fSuccess = ProtectedPatchDownloadCallback(FileListInfo->Callback, PATCH_DOWNLOAD_FILE_FAILED, FileInfo, FileListInfo->CallbackContext); //If callback says to continue or retry download, we do so. If it needs to abort, it return 0
if (!fSuccess) { ProtectedPatchDownloadCallback(FileListInfo->Callback, PATCH_DOWNLOAD_ABORT, NULL, FileListInfo->CallbackContext); break; }
if(fSuccess == PATCH_DOWNLOAD_FLAG_RETRY) { FileInfo->dwFlags = PATCHFLAG_DOWNLOAD_NEEDED; if(FileInfo->lpszExistingFilePatchSignature) { LocalFree(FileInfo->lpszExistingFilePatchSignature); FileInfo->lpszExistingFilePatchSignature = NULL; } } else if(fSuccess == PATCH_DOWNLOAD_FLAG_CONTINUE) { FileInfo->dwFlags = 0; } } //Delete the temp file. We might be having 2 temp files if this was a patch.
if(lstrcmpi(lpszDownloadFileName, szRealFileName)) { DeleteFile(lpszDownloadFileName); } DeleteFile(szRealFileName); ResizeBuffer(lpszDownloadFileName, 0, 0); } } }
done: DestroySubAllocator( hSubAllocator ); // free entire btree
return 0; }
BOOL VerifyHash(LPTSTR lpszFile) {
TCHAR szHashFromInf[40]; TCHAR szHashFromFile[40];
// Verify MD5 of new file against the MD5 from inf. If we cannot verify
// the MD5 for any reason, then leave the file alone (success).
// Only if computed MD5 does not match do we reject the file.
LPTSTR lpFileName = PathFindFileName(lpszFile);
if(GetHashidFromINF(lpFileName, szHashFromInf, sizeof(szHashFromInf)) && GetFilePatchSignatureA(lpszFile, PATCH_OPTION_SIGNATURE_MD5, NULL, 0, 0, 0, 0, sizeof(szHashFromFile), szHashFromFile)) { if (lstrcmpi(szHashFromFile, szHashFromInf)) { WriteToLog("Hash Incorrect. File hash: %1 Inf hash: %2. Need to Re-download %3\n", szHashFromFile, szHashFromInf, lpFileName); return FALSE; } } else { WriteToLog("Warning:Could not get hashid for %1 in inf file\n", lpFileName); }
return TRUE; }
BOOL ProtectedPatchDownloadCallback(PATCH_DOWNLOAD_CALLBACK Callback, IN PATCH_DOWNLOAD_REASON CallbackReason, IN PVOID CallbackData, IN PVOID CallBackContext) { BOOL Success = TRUE; if (Callback != NULL ) { __try { Success = Callback(CallbackReason, CallbackData, CallBackContext); } __except( EXCEPTION_EXECUTE_HANDLER ) { SetLastError( GetExceptionCode()); Success = FALSE; } }
return Success; }
BOOL WINAPI PatchCallback(PATCH_DOWNLOAD_REASON Reason, PVOID lpvInfo, PVOID lpvCallBackContext) {
switch (Reason) { case PATCH_DOWNLOAD_ENDDOWNLOADINGDATA: case PATCH_DOWNLOAD_CONNECTING: case PATCH_DOWNLOAD_FINDINGSITE: case PATCH_DOWNLOAD_DOWNLOADINGDATA: { LPTSTR lpsz = (LPTSTR)lpvInfo; lpsz[90] = NULL; SetProgressText(lpsz); } break;
case PATCH_DOWNLOAD_PROGRESS: { char szBuffer[100], szText[MAX_PATH]; PDOWNLOAD_INFO ProgressInfo = (PDOWNLOAD_INFO)lpvInfo; LoadString(g_hInstance, IDS_BYTEINFO, szBuffer, sizeof(szBuffer)); DWORD dwBytesDownloaded = ProgressInfo->dwBytesToDownload - ProgressInfo->dwBytesRemaining; wsprintf(szText, szBuffer, dwBytesDownloaded, ProgressInfo->dwBytesToDownload); if(g_hProgressDlg && ProgressInfo->dwBytesToDownload) { SetProgressText(szText); } break; }
case PATCH_DOWNLOAD_FILE_COMPLETED: // AdditionalInfo is Source file downloaded
{ TCHAR szDstFile[MAX_PATH]; LPTSTR lpFileName = PathFindFileName((LPCTSTR)lpvInfo); CombinePaths((LPTSTR)lpvCallBackContext, lpFileName, szDstFile); CopyFile((LPCTSTR)lpvInfo, szDstFile, FALSE); }
break; case PATCH_DOWNLOAD_FILE_FAILED: //ask it to retry
return PATCH_DOWNLOAD_FLAG_RETRY; default: break; }
return TRUE; }
|