Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

673 lines
24 KiB

#include <windows.h>
#include <wininet.h>
#include <shlwapi.h>
#include <logging.h>
#include "iucommon.h"
#include "download.h"
#include "dlutil.h"
#include "trust.h"
#include "fileutil.h"
#include "malloc.h"
extern "C"
{
// wininet
typedef BOOL (STDAPICALLTYPE *pfn_InternetCrackUrl)(LPCTSTR, DWORD, DWORD, LPURL_COMPONENTS);
typedef HINTERNET (STDAPICALLTYPE *pfn_InternetOpen)(LPCTSTR, DWORD, LPCTSTR, LPCTSTR, DWORD);
typedef HINTERNET (STDAPICALLTYPE *pfn_InternetConnect)(HINTERNET, LPCTSTR, INTERNET_PORT, LPCTSTR, LPCTSTR, DWORD, DWORD, DWORD_PTR);
typedef HINTERNET (STDAPICALLTYPE *pfn_HttpOpenRequest)(HINTERNET, LPCTSTR, LPCTSTR, LPCTSTR, LPCTSTR, LPCTSTR FAR *, DWORD, DWORD_PTR);
typedef BOOL (STDAPICALLTYPE *pfn_HttpSendRequest)(HINTERNET, LPCTSTR, DWORD, LPVOID, DWORD);
typedef BOOL (STDAPICALLTYPE *pfn_HttpQueryInfo)(HINTERNET, DWORD, LPVOID, LPDWORD, LPDWORD);
typedef BOOL (STDAPICALLTYPE *pfn_InternetReadFile)(HINTERNET, LPVOID, DWORD, LPDWORD);
typedef BOOL (STDAPICALLTYPE *pfn_InternetCloseHandle)(HINTERNET);
};
struct SWinInetFunctions
{
// wininet function pointers
pfn_InternetCrackUrl pfnInternetCrackUrl;
pfn_InternetOpen pfnInternetOpen;
pfn_InternetConnect pfnInternetConnect;
pfn_HttpOpenRequest pfnHttpOpenRequest;
pfn_HttpSendRequest pfnHttpSendRequest;
pfn_HttpQueryInfo pfnHttpQueryInfo;
pfn_InternetReadFile pfnInternetReadFile;
pfn_InternetCloseHandle pfnInternetCloseHandle;
HMODULE hmod;
};
#define SafeInternetCloseHandle(sfns, x) if (NULL != x) { (*sfns.pfnInternetCloseHandle)(x); x = NULL; }
// **************************************************************************
BOOL LoadWinInetFunctions(HMODULE hmod, SWinInetFunctions *psfns)
{
LOG_Block("LoadWinInetFunctions()");
BOOL fRet = FALSE;
psfns->hmod = hmod;
#if defined(UNICODE)
psfns->pfnInternetCrackUrl = (pfn_InternetCrackUrl)GetProcAddress(hmod, "InternetCrackUrlW");
psfns->pfnInternetOpen = (pfn_InternetOpen)GetProcAddress(hmod, "InternetOpenW");
psfns->pfnInternetConnect = (pfn_InternetConnect)GetProcAddress(hmod, "InternetConnectW");
psfns->pfnHttpOpenRequest = (pfn_HttpOpenRequest)GetProcAddress(hmod, "HttpOpenRequestW");
psfns->pfnHttpSendRequest = (pfn_HttpSendRequest)GetProcAddress(hmod, "HttpSendRequestW");
psfns->pfnHttpQueryInfo = (pfn_HttpQueryInfo)GetProcAddress(hmod, "HttpQueryInfoW");
psfns->pfnInternetReadFile = (pfn_InternetReadFile)GetProcAddress(hmod, "InternetReadFile");
psfns->pfnInternetCloseHandle = (pfn_InternetCloseHandle)GetProcAddress(hmod, "InternetCloseHandle");
#else
psfns->pfnInternetCrackUrl = (pfn_InternetCrackUrl)GetProcAddress(hmod, "InternetCrackUrlA");
psfns->pfnInternetOpen = (pfn_InternetOpen)GetProcAddress(hmod, "InternetOpenA");
psfns->pfnInternetConnect = (pfn_InternetConnect)GetProcAddress(hmod, "InternetConnectA");
psfns->pfnHttpOpenRequest = (pfn_HttpOpenRequest)GetProcAddress(hmod, "HttpOpenRequestA");
psfns->pfnHttpSendRequest = (pfn_HttpSendRequest)GetProcAddress(hmod, "HttpSendRequestA");
psfns->pfnHttpQueryInfo = (pfn_HttpQueryInfo)GetProcAddress(hmod, "HttpQueryInfoA");
psfns->pfnInternetReadFile = (pfn_InternetReadFile)GetProcAddress(hmod, "InternetReadFile");
psfns->pfnInternetCloseHandle = (pfn_InternetCloseHandle)GetProcAddress(hmod, "InternetCloseHandle");
#endif
if (psfns->pfnInternetCrackUrl == NULL ||
psfns->pfnInternetOpen == NULL ||
psfns->pfnInternetConnect == NULL ||
psfns->pfnHttpOpenRequest == NULL ||
psfns->pfnHttpSendRequest == NULL ||
psfns->pfnHttpQueryInfo == NULL ||
psfns->pfnInternetReadFile == NULL ||
psfns->pfnInternetCloseHandle == NULL)
{
// don't free the library here. It should be freed
SetLastError(ERROR_PROC_NOT_FOUND);
ZeroMemory(psfns, sizeof(SWinInetFunctions));
goto done;
}
LOG_Internet(_T("Successfully loaded WinInet functions"));
fRet = TRUE;
done:
return fRet;
}
// **************************************************************************
static
HRESULT MakeRequest(SWinInetFunctions &sfns,
HINTERNET hConnect,
HINTERNET hRequest,
LPCTSTR szVerb,
LPCTSTR szObject,
HANDLE *rghEvents,
DWORD cEvents,
HINTERNET *phRequest)
{
LOG_Block("MakeRequest()");
HINTERNET hOpenRequest = NULL;
LPCTSTR szAcceptTypes[] = { _T("*/*"), NULL };
HRESULT hr = S_OK;
LOG_Internet(_T("WinInet: Making %s request for %s"), szVerb, szObject);
if (hRequest == NULL)
{
// Open a HEAD request to ask for information about this file
hOpenRequest = (*sfns.pfnHttpOpenRequest)(hConnect, szVerb, szObject, NULL, NULL,
szAcceptTypes, INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_UI, 0);
if (!hOpenRequest)
{
hr = HRESULT_FROM_WIN32(GetLastError());
LOG_ErrorMsg(hr);
goto CleanUp;
}
}
else
{
hOpenRequest = hRequest;
}
if (!HandleEvents(rghEvents, cEvents))
{
hr = E_ABORT;
goto CleanUp;
}
if (! (*sfns.pfnHttpSendRequest)(hOpenRequest, NULL, 0, NULL, 0) )
{
hr = HRESULT_FROM_WIN32(GetLastError());
LOG_ErrorMsg(hr);
goto CleanUp;
}
if (HandleEvents(rghEvents, cEvents) == FALSE)
{
hr = E_ABORT;
goto CleanUp;
}
*phRequest = hOpenRequest;
hOpenRequest = NULL;
CleanUp:
// don't want to free handle if we didn't open it.
if (hRequest != hOpenRequest)
SafeInternetCloseHandle(sfns, hOpenRequest);
return hr;
}
// **************************************************************************
static
HRESULT GetContentTypeHeader(SWinInetFunctions &sfns,
HINTERNET hOpenRequest,
LPTSTR *pszContentType)
{
LOG_Block("GetContentTypeHeader()");
HRESULT hr = S_OK;
LPTSTR szContentType = NULL;
DWORD dwLength, dwErr;
BOOL fRet;
*pszContentType = NULL;
dwLength = 0;
fRet = (*sfns.pfnHttpQueryInfo)(hOpenRequest, HTTP_QUERY_CONTENT_TYPE,
(LPVOID)NULL, &dwLength, NULL);
if (fRet == FALSE && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
hr = HRESULT_FROM_WIN32(GetLastError());
LOG_ErrorMsg(hr);
goto done;
}
if (dwLength == 0)
{
hr = HRESULT_FROM_WIN32(ERROR_HTTP_HEADER_NOT_FOUND);
goto done;
}
szContentType = (LPTSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength);
if (szContentType == NULL)
{
hr = E_INVALIDARG;
LOG_ErrorMsg(hr);
goto done;
}
if ((*sfns.pfnHttpQueryInfo)(hOpenRequest, HTTP_QUERY_CONTENT_TYPE,
(LPVOID)szContentType, &dwLength,
NULL) == FALSE)
{
hr = HRESULT_FROM_WIN32(GetLastError());
LOG_ErrorMsg(hr);
goto done;
}
*pszContentType = szContentType;
szContentType = NULL;
done:
SafeHeapFree(szContentType);
return hr;
}
// **************************************************************************
HRESULT StartWinInetDownload(HMODULE hmodWinInet,
LPCTSTR pszServerUrl,
LPCTSTR pszLocalFile,
DWORD *pdwDownloadedBytes,
HANDLE *rghQuitEvents,
UINT cQuitEvents,
PFNDownloadCallback pfnCallback,
LPVOID pvCallbackData,
DWORD dwFlags,
DWORD cbDownloadBuffer)
{
LOG_Block("StartWinInetDownload()");
URL_COMPONENTS UrlComponents;
SWinInetFunctions sfns;
HINTERNET hInternet = NULL;
HINTERNET hConnect = NULL;
HINTERNET hOpenRequest = NULL;
DWORD dwStatus, dwAccessType;
LPTSTR pszServerName = NULL;
LPTSTR pszObject = NULL;
LPTSTR pszContentType = NULL;
TCHAR szUserName[UNLEN + 1];
TCHAR szPasswd[UNLEN + 1];
TCHAR szScheme[32];
// NULL (equivalent to "GET") MUST be the last verb in the list
LPCTSTR rgszVerbs[] = { _T("HEAD"), NULL };
DWORD iVerb;
HRESULT hr = S_OK, hrToReturn = S_OK;
BOOL fRet = TRUE;
SYSTEMTIME st;
FILETIME ft;
HANDLE hFile = INVALID_HANDLE_VALUE;
DWORD cbRemoteFile = 0;
DWORD dwLength;
DWORD dwTickStart = 0, dwTickEnd = 0;
int iRetryCounter = -1; // non-negative during download mode
BOOL fAllowProxy = ((dwFlags & WUDF_DONTALLOWPROXY) == 0);
BOOL fCheckStatusOnly = ((dwFlags & WUDF_CHECKREQSTATUSONLY) != 0);
BOOL fAppendCacheBreaker = ((dwFlags & WUDF_APPENDCACHEBREAKER) != 0);
BOOL fSkipDownloadRetry = ((dwFlags & WUDF_DODOWNLOADRETRY) == 0);
BOOL fDoCabValidation = ((dwFlags & WUDF_SKIPCABVALIDATION) == 0);
ZeroMemory(&sfns, sizeof(sfns));
if ((pszServerUrl == NULL) ||
(pszLocalFile == NULL && fCheckStatusOnly == FALSE))
{
LOG_ErrorMsg(E_INVALIDARG);
return E_INVALIDARG;
}
if (NULL != pdwDownloadedBytes)
*pdwDownloadedBytes = 0;
if (LoadWinInetFunctions(hmodWinInet, &sfns) == FALSE)
{
hr = HRESULT_FROM_WIN32(GetLastError());
LOG_ErrorMsg(hr);
return hr;
}
pszServerName = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, c_cchMaxURLSize * sizeof(TCHAR));
pszObject = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, c_cchMaxURLSize * sizeof(TCHAR));
if ((pszServerName == NULL) || (pszObject == NULL))
{
hr = E_OUTOFMEMORY;
LOG_ErrorMsg(hr);
goto CleanUp;
}
pszServerName[0] = L'\0';
pszObject[0] = L'\0';
szUserName[0] = L'\0';
szPasswd[0] = L'\0';
if (HandleEvents(rghQuitEvents, cQuitEvents) == FALSE)
{
hr = E_ABORT;
goto CleanUp;
}
// Break down the URL into its various components for the InternetAPI calls.
// Specifically we need the server name, object to download, username and
// password information.
ZeroMemory(&UrlComponents, sizeof(UrlComponents));
UrlComponents.dwStructSize = sizeof(UrlComponents);
UrlComponents.lpszHostName = pszServerName;
UrlComponents.dwHostNameLength = c_cchMaxURLSize;
UrlComponents.lpszUrlPath = pszObject;
UrlComponents.dwUrlPathLength = c_cchMaxURLSize;
UrlComponents.lpszUserName = szUserName;
UrlComponents.dwUserNameLength = ARRAYSIZE(szUserName);
UrlComponents.lpszPassword = szPasswd;
UrlComponents.dwPasswordLength = ARRAYSIZE(szPasswd);
UrlComponents.lpszScheme = szScheme;
UrlComponents.dwSchemeLength = ARRAYSIZE(szScheme);
LOG_Internet(_T("WinInet: Downloading URL %s to FILE %s"), pszServerUrl, pszLocalFile);
if ((*sfns.pfnInternetCrackUrl)(pszServerUrl, 0, 0, &UrlComponents) == FALSE)
{
hr = HRESULT_FROM_WIN32(GetLastError());
LOG_ErrorMsg(hr);
goto CleanUp;
}
if (pszServerUrl[0] == L'\0' || szScheme[0] == L'\0' || pszServerName[0] == L'\0' ||
_tcsicmp(szScheme, _T("http")) != 0)
{
LOG_ErrorMsg(E_INVALIDARG);
hr = E_INVALIDARG;
goto CleanUp;
}
if (fAppendCacheBreaker)
{
SYSTEMTIME stCB;
TCHAR szCacheBreaker[12];
GetSystemTime(&stCB);
hr = StringCchPrintfEx(szCacheBreaker, ARRAYSIZE(szCacheBreaker),
NULL, NULL, MISTSAFE_STRING_FLAGS,
_T("?%02d%02d%02d%02d%02d"),
stCB.wYear % 100,
stCB.wMonth,
stCB.wDay,
stCB.wHour,
stCB.wMinute);
if (FAILED(hr))
goto CleanUp;
hr = StringCchCatEx(pszObject, c_cchMaxURLSize, szCacheBreaker,
NULL, NULL, MISTSAFE_STRING_FLAGS);
if (FAILED(hr))
goto CleanUp;
}
if (fAllowProxy)
dwAccessType = INTERNET_OPEN_TYPE_PRECONFIG;
else
dwAccessType = INTERNET_OPEN_TYPE_DIRECT;
dwTickStart = GetTickCount();
START_INTERNET:
// start to deal with Internet
iRetryCounter++;
// If the connection has already been established re-use it.
hInternet = (*sfns.pfnInternetOpen)(c_tszUserAgent, dwAccessType, NULL, NULL, 0);
if (hInternet == NULL)
{
hr = HRESULT_FROM_WIN32(GetLastError());
LOG_ErrorMsg(hr);
goto CleanUp;
}
hConnect = (*sfns.pfnInternetConnect)(hInternet, pszServerName, INTERNET_DEFAULT_HTTP_PORT,
szUserName, szPasswd,
INTERNET_SERVICE_HTTP,
INTERNET_FLAG_NO_UI | INTERNET_FLAG_RELOAD | INTERNET_FLAG_KEEP_CONNECTION,
0);
if (hConnect == NULL)
{
hr = HRESULT_FROM_WIN32(GetLastError());
LOG_ErrorMsg(hr);
goto CleanUp;
}
iVerb = (DWORD)((fCheckStatusOnly) ? ARRAYSIZE(rgszVerbs) - 1 : 0);
for(; iVerb < ARRAYSIZE(rgszVerbs); iVerb++)
{
SafeInternetCloseHandle(sfns, hOpenRequest);
hr = MakeRequest(sfns, hConnect, NULL, rgszVerbs[iVerb], pszObject, rghQuitEvents, cQuitEvents,
&hOpenRequest);
if (FAILED(hr))
goto CleanUp;
dwLength = sizeof(dwStatus);
if ((*sfns.pfnHttpQueryInfo)(hOpenRequest, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
(LPVOID)&dwStatus, &dwLength, NULL) == FALSE)
{
hr = HRESULT_FROM_WIN32(GetLastError());
LOG_ErrorMsg(hr);
goto CleanUp;
}
LOG_Internet(_T("WinInet: Request result: %d"), dwStatus);
if (dwStatus == HTTP_STATUS_OK || dwStatus == HTTP_STATUS_PARTIAL_CONTENT)
{
break;
}
else
{
// since a server result is not a proper win32 error code, we can't
// really do a HRESULT_FROM_WIN32 here. Otherwise, we'd return
// a bogus code. However, we do want to pass an error HRESULT back
// that contains this code.
hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_HTTP, dwStatus);
LOG_Error(_T("WinInet: got failed status code from server %d\n"), dwStatus);
// if it's the last verb in the list, then bail...
if (rgszVerbs[iVerb] == NULL)
goto CleanUp;
}
}
// if we made it here & we're only trying to check status, then we're done
if (fCheckStatusOnly)
{
LOG_Internet(_T("WinInet: Only checking status. Exiting before header check and download."));
hr = S_OK;
goto CleanUp;
}
dwLength = sizeof(st);
if ((*sfns.pfnHttpQueryInfo)(hOpenRequest, HTTP_QUERY_LAST_MODIFIED | HTTP_QUERY_FLAG_SYSTEMTIME,
(LPVOID)&st, &dwLength, NULL) == FALSE)
{
hr = HRESULT_FROM_WIN32(GetLastError());
LOG_ErrorMsg(hr);
goto CleanUp;
}
SystemTimeToFileTime(&st, &ft);
// Now Get the FileSize information from the Server
dwLength = sizeof(cbRemoteFile);
if ((*sfns.pfnHttpQueryInfo)(hOpenRequest, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER,
(LPVOID)&cbRemoteFile, &dwLength, NULL) == FALSE)
{
hr = HRESULT_FROM_WIN32(GetLastError());
LOG_ErrorMsg(hr);
goto CleanUp;
}
if (HandleEvents(rghQuitEvents, cQuitEvents) == FALSE)
{
hr = E_ABORT;
goto CleanUp;
}
// unless we have a flag that explicitly allows it, do not retry downloads
// here. The reasoning is that we could be in the middle of a large
// download and have it fail...
if (fSkipDownloadRetry)
iRetryCounter = c_cMaxRetries;
if (IsServerFileDifferent(ft, cbRemoteFile, pszLocalFile))
{
DWORD cbDownloaded;
BOOL fCheckForHTML = fDoCabValidation;
LOG_Internet(_T("WinInet: Server file was newer. Downloading file"));
// if we didn't open with a GET request above, then we gotta open a new
// request. Otherwise, can reuse the request object...
if (rgszVerbs[iVerb] != NULL)
SafeInternetCloseHandle(sfns, hOpenRequest);
hr = MakeRequest(sfns, hConnect, hOpenRequest, NULL, pszObject,
rghQuitEvents, cQuitEvents, &hOpenRequest);
if (FAILED(hr))
goto CleanUp;
// sometimes, we can get fancy error pages back from the site instead of
// a nice nifty HTML error code, so check & see if we got back a html
// file when we were expecting a cab.
if (fCheckForHTML)
{
hr = GetContentTypeHeader(sfns, hOpenRequest, &pszContentType);
if (SUCCEEDED(hr) && pszContentType != NULL)
{
fCheckForHTML = FALSE;
if (_tcsicmp(pszContentType, _T("text/html")) == 0)
{
LOG_Internet(_T("WinInet: Content-Type header is text/html. Bailing."));
hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
goto CleanUp;
}
else
{
LOG_Internet(_T("WinInet: Content-Type header is %s. Continuing."), pszContentType);
}
}
hr = NOERROR;
}
// open the file we're gonna spew into
hFile = CreateFile(pszLocalFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
hr = HRESULT_FROM_WIN32(GetLastError());
LOG_ErrorMsg(hr);
goto CleanUp;
}
LOG_Internet(_T("WinInet: downloading to FILE %s"), pszLocalFile);
// bring down the bits
hr = PerformDownloadToFile(sfns.pfnInternetReadFile, hOpenRequest,
hFile, cbRemoteFile,
cbDownloadBuffer,
rghQuitEvents, cQuitEvents,
pfnCallback, pvCallbackData, &cbDownloaded);
if (FAILED(hr))
{
LOG_Internet(_T("WinInet: Download failed: hr: 0x%08x"), hr);
SafeCloseInvalidHandle(hFile);
DeleteFile(pszLocalFile);
goto CleanUp;
}
LOG_Internet(_T("WinInet: Download succeeded"));
// set the file time to match the server file time since we just
// downloaded it. If we don't do this the file time will be set
// to the current system time.
SetFileTime(hFile, &ft, NULL, NULL);
SafeCloseInvalidHandle(hFile);
if (pdwDownloadedBytes != NULL)
*pdwDownloadedBytes = cbRemoteFile;
// sometimes, we can get fancy error pages back from the site instead of
// a nice nifty HTML error code, so check & see if we got back a html
// file when we were expecting a cab.
if (fCheckForHTML)
{
hr = IsFileHtml(pszLocalFile);
if (SUCCEEDED(hr))
{
if (hr == S_FALSE)
{
LOG_Internet(_T("WinInet: Download is not a html file"));
hr = S_OK;
}
else
{
LOG_Internet(_T("WinInet: Download is a html file. Failing download."));
hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
SafeCloseInvalidHandle(hFile);
DeleteFile(pszLocalFile);
goto CleanUp;
}
}
else
{
LOG_Internet(_T("WinInet: Unable to determine if download is a html file or not. Failing download."));
}
}
else
{
LOG_Internet(_T("WinInet: Skipping cab validation."));
}
}
else
{
hr = S_OK;
LOG_Internet(_T("WinInet: Server file is not newer. Skipping download."));
// The server ain't newer & the file is already on machine, so
// send progress callback indicating file downloadeded ok
if (pfnCallback != NULL)
{
// fpnCallback(pCallbackData, DOWNLOAD_STATUS_FILECOMPLETE, dwFileSize, dwFileSize, NULL, NULL);
pfnCallback(pvCallbackData, DOWNLOAD_STATUS_OK, cbRemoteFile, cbRemoteFile, NULL, NULL);
}
}
CleanUp:
SafeInternetCloseHandle(sfns, hOpenRequest);
SafeInternetCloseHandle(sfns, hConnect);
SafeInternetCloseHandle(sfns, hInternet);
SafeHeapFree(pszContentType);
// if we failed, see if it's ok to continue (quit events) and whether
// we've tried enuf times yet.
if (FAILED(hr) &&
HandleEvents(rghQuitEvents, cQuitEvents) &&
iRetryCounter >= 0 && iRetryCounter < c_cMaxRetries)
{
// in case of failure and have no enough retries yet, we retry
// as long as not timeout yet
DWORD dwElapsedTime;
dwTickEnd = GetTickCount();
if (dwTickEnd > dwTickStart)
dwElapsedTime = dwTickEnd - dwTickStart;
else
dwElapsedTime = (0xFFFFFFFF - dwTickStart) + dwTickEnd;
// We haven't hit our retry limit, so log & error and go again
if (dwElapsedTime < c_dwRetryTimeLimitInmsWiuInet)
{
LogError(hr, "Library download error. Will retry.");
// in the case where we're gonna retry, keep track of the very first
// error we encoutered cuz the ops guys say that this is the most
// useful error to know about.
if (iRetryCounter == 0)
{
LOG_Internet(_T("First download error saved: 0x%08x."), hr);
hrToReturn = hr;
}
else
{
LOG_Internet(_T("Subsequent download error: 0x%08x."), hr);
}
hr = S_OK;
goto START_INTERNET;
}
// We've completely timed out, so bail
else
{
LogError(hr, "Library download error and timed out (%d ms). Will not retry.", dwElapsedTime);
}
}
// make a callback indicating a download error
if (FAILED(hr) && pfnCallback != NULL)
pfnCallback(pvCallbackData, DOWNLOAD_STATUS_ERROR, cbRemoteFile, 0, NULL, NULL);
// if we haven't saved off an error, just use the current error. We can't
// have set hrToReturn previously if we didn't fail and want to attempt
// a retry.
// However, if we've got a success from this pass, be sure to return that
// and not a fail code.
if (FAILED(hr) && SUCCEEDED(hrToReturn))
hrToReturn = hr;
else if (SUCCEEDED(hr) && FAILED(hrToReturn))
hrToReturn = hr;
SafeHeapFree(pszServerName);
SafeHeapFree(pszObject);
return hrToReturn;
}