mirror of https://github.com/tongzx/nt5src
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
23 KiB
673 lines
23 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;
|
|
}
|
|
|
|
|
|
|