#include #include #include #include #include "iucommon.h" #include "download.h" #include "dllite.h" #include "dlutil.h" #include "malloc.h" #include "trust.h" #include "fileutil.h" #include "dlcache.h" #include "wusafefn.h" /////////////////////////////////////////////////////////////////////////////// // typedefs // winhttp extern "C" { typedef BOOL (WINAPI *pfn_WinHttpCrackUrl)(LPCWSTR, DWORD, DWORD, LPURL_COMPONENTS); typedef HINTERNET (WINAPI *pfn_WinHttpOpen)(LPCWSTR, DWORD, LPCWSTR, LPCWSTR, DWORD); typedef HINTERNET (WINAPI *pfn_WinHttpConnect)(HINTERNET, LPCWSTR, INTERNET_PORT, DWORD); typedef HINTERNET (WINAPI *pfn_WinHttpOpenRequest)(HINTERNET, LPCWSTR, LPCWSTR, LPCWSTR, LPCWSTR, LPCWSTR FAR *, DWORD); typedef BOOL (WINAPI *pfn_WinHttpSendRequest)(HINTERNET, LPCWSTR, DWORD, LPVOID, DWORD, DWORD, DWORD_PTR); typedef BOOL (WINAPI *pfn_WinHttpReceiveResponse)(HINTERNET, LPVOID); typedef BOOL (WINAPI *pfn_WinHttpQueryHeaders)(HINTERNET, DWORD, LPCWSTR, LPVOID, LPDWORD, LPDWORD); typedef BOOL (WINAPI *pfn_WinHttpReadData)(HINTERNET, LPVOID, DWORD, LPDWORD); typedef BOOL (WINAPI *pfn_WinHttpCloseHandle)(HINTERNET); typedef BOOL (WINAPI *pfn_WinHttpGetIEProxyConfigForCurrentUser)(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG *); typedef BOOL (WINAPI *pfn_WinHttpGetProxyForUrl)(HINTERNET, LPCWSTR, WINHTTP_AUTOPROXY_OPTIONS *, WINHTTP_PROXY_INFO *); typedef BOOL (WINAPI *pfn_WinHttpSetOption)(HINTERNET, DWORD, LPVOID, DWORD); } struct SWinHTTPFunctions { pfn_WinHttpGetIEProxyConfigForCurrentUser pfnWinHttpGetIEProxyConfigForCurrentUser; pfn_WinHttpGetProxyForUrl pfnWinHttpGetProxyForUrl; pfn_WinHttpCrackUrl pfnWinHttpCrackUrl; pfn_WinHttpOpen pfnWinHttpOpen; pfn_WinHttpConnect pfnWinHttpConnect; pfn_WinHttpOpenRequest pfnWinHttpOpenRequest; pfn_WinHttpSendRequest pfnWinHttpSendRequest; pfn_WinHttpReceiveResponse pfnWinHttpReceiveResponse; pfn_WinHttpQueryHeaders pfnWinHttpQueryHeaders; pfn_WinHttpReadData pfnWinHttpReadData; pfn_WinHttpCloseHandle pfnWinHttpCloseHandle; pfn_WinHttpSetOption pfnWinHttpSetOption; HMODULE hmod; }; typedef struct tagSAUProxyInfo { WINHTTP_PROXY_INFO ProxyInfo; LPWSTR wszProxyOrig; LPWSTR *rgwszProxies; DWORD cProxies; DWORD iProxy; } SAUProxyInfo; typedef enum tagETransportUsed { etuNone = 0, etuWinHttp, etuWinInet, } ETransportUsed; #define SafeWinHTTPCloseHandle(sfns, x) if (NULL != x) { (*sfns.pfnWinHttpCloseHandle)(x); x = NULL; } #define StringOrConstW(wsz, wszConst) (((wsz) != NULL) ? (wsz) : (wszConst)) /////////////////////////////////////////////////////////////////////////////// // globals #if defined(UNICODE) CWUDLProxyCache g_wudlProxyCache; CAutoCritSec g_csCache; #endif HMODULE g_hmodWinHttp = NULL; HMODULE g_hmodWinInet = NULL; /////////////////////////////////////////////////////////////////////////////// // utility functions // ************************************************************************** static LPTSTR MakeFullLocalFilePath(LPCTSTR szUrl, LPCTSTR szFileName, LPCTSTR szPath) { LOG_Block("MakeFullLocalFilePath()"); HRESULT hr = NOERROR; LPTSTR pszRet, pszFileNameToUse = NULL, pszQuery = NULL; LPTSTR pszFullPath = NULL; DWORD cchFile; TCHAR chTemp = _T('\0'); // if we got a local filename passed to us, use it. if (szFileName != NULL) { pszFileNameToUse = (LPTSTR)szFileName; } // otherwise, parse the filename out of the URL & use it instead else { // first get a pointer to the querystring, if any pszQuery = _tcschr(szUrl, _T('?')); // next, find the last slash before the start of the querystring pszFileNameToUse = StrRChr(szUrl, pszQuery, _T('/')); // if we don't have a filename at this point, we can't continue // cuz there's nowhere to download the file to. if (pszFileNameToUse == NULL) { SetLastError(ERROR_INVALID_PARAMETER); goto done; } pszFileNameToUse++; // temporarily NULL out the first character of the querystring, if // we have a querystring. This makes the end of the filename the // end of the string. if (pszQuery != NULL) { chTemp = *pszQuery; *pszQuery = _T('\0'); } } // add 2 for a possible backslash & the null terminator cchFile = 2 + _tcslen(szPath) + _tcslen(pszFileNameToUse); pszFullPath = (LPTSTR)HeapAlloc(GetProcessHeap(), 0, cchFile * sizeof(TCHAR)); if (pszFullPath == NULL) { SetLastError(ERROR_OUTOFMEMORY); if (pszQuery != NULL) *pszQuery = chTemp; goto done; } // construct the path hr = SafePathCombine(pszFullPath, cchFile, szPath, pszFileNameToUse, 0); // if we nuked the first character of the querystring, restore it. if (pszQuery != NULL) *pszQuery = chTemp; if (FAILED(hr)) { SetLastError(HRESULT_CODE(hr)); SafeHeapFree(pszFullPath); goto done; } done: return pszFullPath; } // ************************************************************************** static ETransportUsed LoadTransportDll(SWinHTTPFunctions *psfns, HMODULE *phmod, DWORD dwFlags) { LOG_Block("LoadTransportDll()"); ETransportUsed etu = etuNone; HMODULE hmod = NULL; HRESULT hr = NOERROR; BOOL fAllowWininet; BOOL fAllowWinhttp; BOOL fPersistTrans = ((dwFlags & WUDF_PERSISTTRANSPORTDLL) != 0); if (psfns == NULL || phmod == NULL || (dwFlags & WUDF_TRANSPORTMASK) == WUDF_TRANSPORTMASK) { SetLastError(ERROR_INVALID_PARAMETER); goto done; } dwFlags = GetAllowedDownloadTransport(dwFlags); fAllowWininet = ((dwFlags & WUDF_ALLOWWINHTTPONLY) == 0); fAllowWinhttp = ((dwFlags & WUDF_ALLOWWININETONLY) == 0); ZeroMemory(psfns, sizeof(SWinHTTPFunctions)); *phmod = NULL; // first try to load the winhttp dll if (fAllowWinhttp) { if (g_hmodWinHttp == NULL) { hmod = LoadLibraryFromSystemDir(c_szWinHttpDll); if (hmod != NULL && fPersistTrans && InterlockedCompareExchangePointer((LPVOID *)&g_hmodWinHttp, hmod, NULL) != NULL) { FreeLibrary(hmod); hmod = g_hmodWinHttp; } } else { hmod = g_hmodWinHttp; } } if (hmod != NULL) { psfns->hmod = hmod; psfns->pfnWinHttpGetProxyForUrl = (pfn_WinHttpGetProxyForUrl)GetProcAddress(hmod, "WinHttpGetProxyForUrl"); psfns->pfnWinHttpCrackUrl = (pfn_WinHttpCrackUrl)GetProcAddress(hmod, "WinHttpCrackUrl"); psfns->pfnWinHttpOpen = (pfn_WinHttpOpen)GetProcAddress(hmod, "WinHttpOpen"); psfns->pfnWinHttpConnect = (pfn_WinHttpConnect)GetProcAddress(hmod, "WinHttpConnect"); psfns->pfnWinHttpOpenRequest = (pfn_WinHttpOpenRequest)GetProcAddress(hmod, "WinHttpOpenRequest"); psfns->pfnWinHttpSendRequest = (pfn_WinHttpSendRequest)GetProcAddress(hmod, "WinHttpSendRequest"); psfns->pfnWinHttpReceiveResponse = (pfn_WinHttpReceiveResponse)GetProcAddress(hmod, "WinHttpReceiveResponse"); psfns->pfnWinHttpQueryHeaders = (pfn_WinHttpQueryHeaders)GetProcAddress(hmod, "WinHttpQueryHeaders"); psfns->pfnWinHttpReadData = (pfn_WinHttpReadData)GetProcAddress(hmod, "WinHttpReadData"); psfns->pfnWinHttpCloseHandle = (pfn_WinHttpCloseHandle)GetProcAddress(hmod, "WinHttpCloseHandle"); psfns->pfnWinHttpSetOption = (pfn_WinHttpSetOption)GetProcAddress(hmod, "WinHttpSetOption"); psfns->pfnWinHttpGetIEProxyConfigForCurrentUser = (pfn_WinHttpGetIEProxyConfigForCurrentUser)GetProcAddress(hmod, "WinHttpGetIEProxyConfigForCurrentUser"); if (psfns->pfnWinHttpCrackUrl == NULL || psfns->pfnWinHttpOpen == NULL || psfns->pfnWinHttpConnect == NULL || psfns->pfnWinHttpOpenRequest == NULL || psfns->pfnWinHttpSendRequest == NULL || psfns->pfnWinHttpReceiveResponse == NULL || psfns->pfnWinHttpQueryHeaders == NULL || psfns->pfnWinHttpReadData == NULL || psfns->pfnWinHttpCloseHandle == NULL || psfns->pfnWinHttpGetProxyForUrl == NULL || psfns->pfnWinHttpGetIEProxyConfigForCurrentUser == NULL || psfns->pfnWinHttpSetOption == NULL) { // do this logging here cuz we'll try wininet afterward & we want // to make sure to log this error as well LOG_ErrorMsg(ERROR_PROC_NOT_FOUND); SetLastError(ERROR_PROC_NOT_FOUND); ZeroMemory(psfns, sizeof(SWinHTTPFunctions)); FreeLibrary(hmod); hmod = NULL; } else { LOG_Internet(_T("Successfully loaded WinHttp.dll")); etu = etuWinHttp; *phmod = hmod; } } // if hmod is NULL at this point, then try to fall back to wininet. If // that fails, we can only bail... if (fAllowWininet && hmod == NULL) { if (g_hmodWinInet == NULL) { hmod = LoadLibraryFromSystemDir(c_szWinInetDll); if (hmod == NULL) goto done; if (fPersistTrans && InterlockedCompareExchangePointer((LPVOID *)&g_hmodWinInet, hmod, NULL) != NULL) { FreeLibrary(hmod); hmod = g_hmodWinInet; } } LOG_Internet(_T("Successfully loaded WinInet.dll (no functions yet)")); etu = etuWinInet; *phmod = hmod; } done: return etu; } // ************************************************************************** static BOOL UnloadTransportDll(SWinHTTPFunctions *psfns, HMODULE hmod) { LOG_Block("UnloadTransportDll()"); if (hmod != NULL && hmod != g_hmodWinHttp && hmod != g_hmodWinInet) FreeLibrary(hmod); if (psfns != NULL) ZeroMemory(psfns, sizeof(SWinHTTPFunctions)); return TRUE; } // we only care about winhttp on unicode platforms! #if defined(UNICODE) // ************************************************************************** static BOOL ProxyListToArray(LPWSTR wszProxy, LPWSTR **prgwszProxies, DWORD *pcProxies) { LPWSTR pwszProxy = wszProxy; LPWSTR *rgwszProxies = NULL; DWORD cProxies = 0, iProxy; BOOL fRet = FALSE; if (prgwszProxies == NULL || pcProxies == NULL) { SetLastError(ERROR_INVALID_PARAMETER); goto done; } *prgwszProxies = NULL; *pcProxies = 0; if (wszProxy == NULL || *wszProxy == L'\0') goto done; // walk the string & count how many proxies we have for(;;) { for(; *pwszProxy != L';' && *pwszProxy != L'\0'; pwszProxy++); cProxies++; if (*pwszProxy == L'\0') break; else pwszProxy++; } // alloc an array to hold 'em rgwszProxies = (LPWSTR *)GlobalAlloc(GPTR, sizeof(LPWSTR) * cProxies); if (rgwszProxies == NULL) { SetLastError(ERROR_OUTOFMEMORY); goto done; } // fill the array pwszProxy = wszProxy; for(iProxy = 0; iProxy < cProxies; iProxy++) { rgwszProxies[iProxy] = pwszProxy; for(; *pwszProxy != L';' && *pwszProxy != L'\0'; pwszProxy++); if (*pwszProxy == L'\0') { break; } else { *pwszProxy = L'\0'; pwszProxy++; } } *prgwszProxies = rgwszProxies; *pcProxies = cProxies; rgwszProxies = NULL; fRet = TRUE; done: if (rgwszProxies != NULL) GlobalFree(rgwszProxies); return fRet; } // ************************************************************************** static DWORD GetInitialProxyIndex(DWORD cProxies) { SYSTEMTIME st; DWORD iProxy; GetSystemTime(&st); // this would be incredibly weird, but it's easy to deal with so do so if (st.wMilliseconds >= 1000) st.wMilliseconds = st.wMilliseconds % 1000; // so we don't have to use the crt random number generator, just fake it return (st.wMilliseconds * cProxies) / 1000; } // ************************************************************************** static BOOL GetWinHTTPProxyInfo(SWinHTTPFunctions &sfns, BOOL fCacheResults, HINTERNET hInternet, LPCWSTR wszURL, LPCWSTR wszSrv, SAUProxyInfo *pAUProxyInfo) { LOG_Block("GetWinHTTPProxyInfo()"); WINHTTP_CURRENT_USER_IE_PROXY_CONFIG IEProxyCfg; WINHTTP_AUTOPROXY_OPTIONS AutoProxyOpt; DWORD dwErr = ERROR_SUCCESS; BOOL fUseAutoProxy = FALSE; BOOL fGotProxy = FALSE; BOOL fRet = FALSE; ZeroMemory(&IEProxyCfg, sizeof(IEProxyCfg)); ZeroMemory(&AutoProxyOpt, sizeof(AutoProxyOpt)); // only need to acquire the CS if we're caching results if (fCacheResults) g_csCache.Lock(); if (pAUProxyInfo == NULL || hInternet == NULL) { SetLastError(ERROR_INVALID_PARAMETER); goto done; } ZeroMemory(pAUProxyInfo, sizeof(SAUProxyInfo)); // if we're not caching results, skip directly to the proxy fetch if (fCacheResults && g_wudlProxyCache.Find(wszSrv, &pAUProxyInfo->ProxyInfo.lpszProxy, &pAUProxyInfo->ProxyInfo.lpszProxyBypass, &pAUProxyInfo->ProxyInfo.dwAccessType)) { LOG_Internet(_T("WinHttp: Cached proxy settings Proxy: %ls | Bypass: %ls | AccessType: %d"), StringOrConstW(pAUProxyInfo->ProxyInfo.lpszProxy, L"(none)"), StringOrConstW(pAUProxyInfo->ProxyInfo.lpszProxyBypass, L"(none)"), pAUProxyInfo->ProxyInfo.dwAccessType); pAUProxyInfo->wszProxyOrig = pAUProxyInfo->ProxyInfo.lpszProxy; // we'll deal with this function failing later on when we cycle thru // the proxies. We'll basically only use the first and never cycle if (ProxyListToArray(pAUProxyInfo->wszProxyOrig, &pAUProxyInfo->rgwszProxies, &pAUProxyInfo->cProxies)) { DWORD iProxy; iProxy = GetInitialProxyIndex(pAUProxyInfo->cProxies); pAUProxyInfo->ProxyInfo.lpszProxy = pAUProxyInfo->rgwszProxies[iProxy]; pAUProxyInfo->iProxy = iProxy; } goto done; } // first try to get the current user's IE configuration fRet = (*sfns.pfnWinHttpGetIEProxyConfigForCurrentUser)(&IEProxyCfg); if (fRet) { LOG_Internet(_T("WinHttp: Read IE user proxy settings")); if (IEProxyCfg.fAutoDetect) { AutoProxyOpt.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT; AutoProxyOpt.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A; fUseAutoProxy = TRUE; } if (IEProxyCfg.lpszAutoConfigUrl != NULL) { AutoProxyOpt.dwFlags |= WINHTTP_AUTOPROXY_CONFIG_URL; AutoProxyOpt.lpszAutoConfigUrl = IEProxyCfg.lpszAutoConfigUrl; fUseAutoProxy = TRUE; } AutoProxyOpt.fAutoLogonIfChallenged = TRUE; } // couldn't get current user's config options, so just try autoproxy else { AutoProxyOpt.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT; AutoProxyOpt.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A; AutoProxyOpt.fAutoLogonIfChallenged = TRUE; fUseAutoProxy = TRUE; } if (fUseAutoProxy) { LOG_Internet(_T("WinHttp: Doing autoproxy detection")); fGotProxy = (*sfns.pfnWinHttpGetProxyForUrl)(hInternet, wszURL, &AutoProxyOpt, &pAUProxyInfo->ProxyInfo); } // if we didn't try to autoconfigure the proxy or we did & it failed, then // check and see if we had one defined by the user if ((fUseAutoProxy == FALSE || fGotProxy == FALSE) && IEProxyCfg.lpszProxy != NULL) { // the empty string and L':' are not valid server names, so skip them // if they are what is set for the proxy if (!(IEProxyCfg.lpszProxy[0] == L'\0' || (IEProxyCfg.lpszProxy[0] == L':' && IEProxyCfg.lpszProxy[1] == L'\0'))) { pAUProxyInfo->ProxyInfo.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY; pAUProxyInfo->ProxyInfo.lpszProxy = IEProxyCfg.lpszProxy; IEProxyCfg.lpszProxy = NULL; } // the empty string and L':' are not valid server names, so skip them // if they are what is set for the proxy bypass if (IEProxyCfg.lpszProxyBypass != NULL && !(IEProxyCfg.lpszProxyBypass[0] == L'\0' || (IEProxyCfg.lpszProxyBypass[0] == L':' && IEProxyCfg.lpszProxyBypass[1] == L'\0'))) { pAUProxyInfo->ProxyInfo.lpszProxyBypass = IEProxyCfg.lpszProxyBypass; IEProxyCfg.lpszProxyBypass = NULL; } } LOG_Internet(_T("WinHttp: Proxy settings Proxy: %ls | Bypass: %ls | AccessType: %d"), StringOrConstW(pAUProxyInfo->ProxyInfo.lpszProxy, L"(none)"), StringOrConstW(pAUProxyInfo->ProxyInfo.lpszProxyBypass, L"(none)"), pAUProxyInfo->ProxyInfo.dwAccessType); // don't really care if this fails. It'll just mean a perf hit the next // time we go fetch the proxy info if (fCacheResults && g_wudlProxyCache.Set(wszSrv, pAUProxyInfo->ProxyInfo.lpszProxy, pAUProxyInfo->ProxyInfo.lpszProxyBypass, pAUProxyInfo->ProxyInfo.dwAccessType) == FALSE) { LOG_Internet(_T("WinHttp: Attempt to cache proxy info failed: %d"), GetLastError()); } pAUProxyInfo->wszProxyOrig = pAUProxyInfo->ProxyInfo.lpszProxy; // we'll deal with this function failing later on when we cycle thru the // proxies. We'll basically only use the first and never cycle // Note that this function call has to be AFTER the cache call since we // modify the proxy list by embedding null terminators in it in place of // the separating semicolons. if (ProxyListToArray(pAUProxyInfo->wszProxyOrig, &pAUProxyInfo->rgwszProxies, &pAUProxyInfo->cProxies)) { DWORD iProxy; iProxy = GetInitialProxyIndex(pAUProxyInfo->cProxies); pAUProxyInfo->ProxyInfo.lpszProxy = pAUProxyInfo->rgwszProxies[iProxy]; pAUProxyInfo->iProxy = iProxy; } fRet = TRUE; done: // only need to release the CS if we're caching results if (fCacheResults) g_csCache.Unlock(); dwErr = GetLastError(); if (IEProxyCfg.lpszAutoConfigUrl != NULL) GlobalFree(IEProxyCfg.lpszAutoConfigUrl); if (IEProxyCfg.lpszProxy != NULL) GlobalFree(IEProxyCfg.lpszProxy); if (IEProxyCfg.lpszProxyBypass != NULL) GlobalFree(IEProxyCfg.lpszProxyBypass); SetLastError(dwErr); return fRet; } // ************************************************************************** static HRESULT MakeRequest(SWinHTTPFunctions &sfns, HINTERNET hConnect, HINTERNET hRequest, LPCWSTR wszSrv, LPCWSTR wszVerb, LPCWSTR wszObject, SAUProxyInfo *pAUProxyInfo, HANDLE *rghEvents, DWORD cEvents, HINTERNET *phRequest) { LOG_Block("MakeRequest()"); HINTERNET hOpenRequest = hRequest; LPCWSTR wszAcceptTypes[] = {L"*/*", NULL}; HRESULT hr = S_OK; DWORD iProxy = 0, dwErr; BOOL fProxy, fContinue = TRUE; fProxy = (pAUProxyInfo != NULL && pAUProxyInfo->ProxyInfo.lpszProxy != NULL); LOG_Internet(_T("WinHttp: Making %ls request for %ls"), wszVerb, wszObject); // if we were passed in a request handle, then use it. Otherwise, gotta // open one if (hOpenRequest == NULL) { hOpenRequest = (*sfns.pfnWinHttpOpenRequest)(hConnect, wszVerb, wszObject, NULL, NULL, wszAcceptTypes, 0); if (hOpenRequest == NULL) { hr = HRESULT_FROM_WIN32(GetLastError()); LOG_ErrorMsg(hr); goto done; } } if (HandleEvents(rghEvents, cEvents) == FALSE) { hr = E_ABORT; goto done; } // if we have a list of proxies & the first one is bad, winhttp won't try // any others. So we have to do it ourselves. That is the purpose of this // loop. if (fProxy && pAUProxyInfo->cProxies > 1 && pAUProxyInfo->rgwszProxies != NULL) iProxy = (pAUProxyInfo->iProxy + 1) % pAUProxyInfo->cProxies; for(;;) { if (fProxy) { LOG_Internet(_T("WinHttp: Using proxy: Proxy: %ls | Bypass %ls | AccessType: %d"), StringOrConstW(pAUProxyInfo->ProxyInfo.lpszProxy, L"(none)"), StringOrConstW(pAUProxyInfo->ProxyInfo.lpszProxyBypass, L"(none)"), pAUProxyInfo->ProxyInfo.dwAccessType); if ((*sfns.pfnWinHttpSetOption)(hOpenRequest, WINHTTP_OPTION_PROXY, &pAUProxyInfo->ProxyInfo, sizeof(WINHTTP_PROXY_INFO)) == FALSE) { hr = HRESULT_FROM_WIN32(GetLastError()); LOG_ErrorMsg(hr); goto done; } } if ((*sfns.pfnWinHttpSendRequest)(hOpenRequest, NULL, 0, NULL, 0, 0, 0) == FALSE) { // dwErr = GetLastError(); // LOG_Internet(_T("WinHttp: WinHttpSendRequest failed: %d. Request object at: 0x%x"), // dwErr, hOpenRequest); // SetLastError(dwErr); goto loopDone; } if ((*sfns.pfnWinHttpReceiveResponse)(hOpenRequest, 0) == FALSE) { // dwErr = GetLastError(); // LOG_Internet(_T("WinHttp: WinHttpReceiveResponse failed: %d. Request object at: 0x%x"), // dwErr, hOpenRequest); // SetLastError(dwErr); goto loopDone; } hr = S_OK; SetLastError(ERROR_SUCCESS); loopDone: fContinue = FALSE; dwErr = GetLastError(); if (dwErr != ERROR_SUCCESS) hr = HRESULT_FROM_WIN32(dwErr); else hr = S_OK; // if we succeeded, then we're done here... if (SUCCEEDED(hr)) { if (fProxy) { if (g_csCache.Lock() == FALSE) { hr = E_FAIL; goto done; } g_wudlProxyCache.SetLastGoodProxy(wszSrv, pAUProxyInfo->iProxy); // Unlock returns FALSE as well, but we should never get here cuz // we should not have been able to take the lock above. g_csCache.Unlock(); } break; } LOG_ErrorMsg(hr); // we only care about retrying if we have a proxy server & get a // 'cannot connect' error. if (fProxy && (dwErr == ERROR_WINHTTP_CANNOT_CONNECT || dwErr == ERROR_WINHTTP_CONNECTION_ERROR || dwErr == ERROR_WINHTTP_NAME_NOT_RESOLVED || dwErr == ERROR_WINHTTP_TIMEOUT)) { LOG_Internet(_T("WinHttp: Connection failure: %d"), dwErr); if (pAUProxyInfo->cProxies > 1 && pAUProxyInfo->rgwszProxies != NULL && iProxy != pAUProxyInfo->iProxy) { pAUProxyInfo->ProxyInfo.lpszProxy = pAUProxyInfo->rgwszProxies[iProxy]; iProxy = (iProxy + 1) % pAUProxyInfo->cProxies; fContinue = TRUE; } else { LOG_Internet(_T("WinHttp: No proxies left. Failing download.")); } } if (fContinue == FALSE) goto done; } if (FAILED(hr)) goto done; if (HandleEvents(rghEvents, cEvents) == FALSE) { hr = E_ABORT; goto done; } *phRequest = hOpenRequest; hOpenRequest = NULL; done: // don't want to free the handle if we didn't open it. if (hRequest != hOpenRequest) SafeWinHTTPCloseHandle(sfns, hOpenRequest); return hr; } // ************************************************************************** static HRESULT CheckFileHeader(SWinHTTPFunctions &sfns, HINTERNET hOpenRequest, HANDLE *rghEvents, DWORD cEvents, LPCWSTR wszFile, DWORD *pcbFile, FILETIME *pft) { LOG_Block("CheckFileHeader()"); SYSTEMTIME st; FILETIME ft; HRESULT hr = S_OK; DWORD dwLength, dwStatus, dwFileSize, dwErr; dwLength = sizeof(st); if ((*sfns.pfnWinHttpQueryHeaders)(hOpenRequest, WINHTTP_QUERY_LAST_MODIFIED | WINHTTP_QUERY_FLAG_SYSTEMTIME, NULL, (LPVOID)&st, &dwLength, NULL) == FALSE) { hr = HRESULT_FROM_WIN32(GetLastError()); LOG_ErrorMsg(hr); goto done; } SystemTimeToFileTime(&st, &ft); // Now Get the FileSize information from the Server dwLength = sizeof(dwFileSize); if ((*sfns.pfnWinHttpQueryHeaders)(hOpenRequest, WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER, NULL, (LPVOID)&dwFileSize, &dwLength, NULL) == FALSE) { hr = HRESULT_FROM_WIN32(GetLastError()); LOG_ErrorMsg(hr); goto done; } if (HandleEvents(rghEvents, cEvents) == FALSE) { hr = E_ABORT; goto done; } if (pcbFile != NULL) *pcbFile = dwFileSize; if (pft != NULL) CopyMemory(pft, &ft, sizeof(FILETIME)); hr = IsServerFileDifferentW(ft, dwFileSize, wszFile) ? S_OK : S_FALSE; done: return hr; } // ************************************************************************** static HRESULT GetContentTypeHeader(SWinHTTPFunctions &sfns, HINTERNET hOpenRequest, LPWSTR *pwszContentType) { LOG_Block("GetContentTypeHeader()"); HRESULT hr = S_OK; LPWSTR wszContentType = NULL; DWORD dwLength, dwErr; BOOL fRet; *pwszContentType = NULL; dwLength = 0; fRet = (*sfns.pfnWinHttpQueryHeaders)(hOpenRequest, WINHTTP_QUERY_CONTENT_TYPE, NULL, (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_WINHTTP_HEADER_NOT_FOUND); goto done; } wszContentType = (LPWSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength); if (wszContentType == NULL) { hr = E_INVALIDARG; LOG_ErrorMsg(hr); goto done; } if ((*sfns.pfnWinHttpQueryHeaders)(hOpenRequest, WINHTTP_QUERY_CONTENT_TYPE, NULL, (LPVOID)wszContentType, &dwLength, NULL) == FALSE) { hr = HRESULT_FROM_WIN32(GetLastError()); LOG_ErrorMsg(hr); goto done; } *pwszContentType = wszContentType; wszContentType = NULL; done: SafeHeapFree(wszContentType); return hr; } // ************************************************************************** static HRESULT StartWinHttpDownload(SWinHTTPFunctions &sfns, LPCWSTR wszUrl, LPCWSTR wszLocalFile, DWORD *pcbDownloaded, HANDLE *rghQuitEvents, UINT cQuitEvents, PFNDownloadCallback pfnCallback, LPVOID pvCallbackData, DWORD dwFlags, DWORD cbDownloadBuffer) { LOG_Block("StartWinHttpDownload()"); URL_COMPONENTS UrlComponents; SAUProxyInfo AUProxyInfo; HINTERNET hInternet = NULL; HINTERNET hConnect = NULL; HINTERNET hOpenRequest = NULL; DWORD dwStatus, dwAccessType; LPWSTR wszServerName = NULL; LPWSTR wszObject = NULL; LPWSTR wszContentType = NULL; WCHAR wszUserName[UNLEN + 1]; WCHAR wszPasswd[UNLEN + 1]; WCHAR wszScheme[32]; // NULL (equivalent to "GET") MUST be the last verb in the list LPCWSTR rgwszVerbs[] = { L"HEAD", NULL }; DWORD iVerb; HRESULT hr = S_OK, hrToReturn = S_OK; BOOL fRet = TRUE; FILETIME ft; HANDLE hFile = INVALID_HANDLE_VALUE; DWORD cbRemoteFile; 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); BOOL fCacheProxyInfo = ((dwFlags & WUDF_SKIPAUTOPROXYCACHE) == 0); ZeroMemory(&AUProxyInfo, sizeof(AUProxyInfo)); if ((wszUrl == NULL) || (wszLocalFile == NULL && fCheckStatusOnly == FALSE)) { LOG_ErrorMsg(E_INVALIDARG); return E_INVALIDARG; } if (pcbDownloaded != NULL) *pcbDownloaded = 0; wszServerName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, c_cchMaxURLSize * sizeof(WCHAR)); wszObject = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, c_cchMaxURLSize * sizeof(WCHAR)); if (wszServerName == NULL || wszObject == NULL) { LOG_ErrorMsg(E_OUTOFMEMORY); hr = E_OUTOFMEMORY; goto CleanUp; } wszServerName[0] = L'\0'; wszObject[0] = L'\0'; wszUserName[0] = L'\0'; wszPasswd[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 = wszServerName; UrlComponents.dwHostNameLength = c_cchMaxURLSize; UrlComponents.lpszUrlPath = wszObject; UrlComponents.dwUrlPathLength = c_cchMaxURLSize; UrlComponents.lpszUserName = wszUserName; UrlComponents.dwUserNameLength = ARRAYSIZE(wszUserName); UrlComponents.lpszPassword = wszPasswd; UrlComponents.dwPasswordLength = ARRAYSIZE(wszPasswd); UrlComponents.lpszScheme = wszScheme; UrlComponents.dwSchemeLength = ARRAYSIZE(wszScheme); LOG_Internet(_T("WinHttp: Downloading URL %ls to FILE %ls"), wszUrl, wszLocalFile); if ((*sfns.pfnWinHttpCrackUrl)(wszUrl, 0, 0, &UrlComponents) == FALSE) { hr = HRESULT_FROM_WIN32(GetLastError()); LOG_ErrorMsg(hr); goto CleanUp; } if (wszUrl[0] == L'\0' || wszScheme[0] == L'\0' || wszServerName[0] == L'\0' || _wcsicmp(wszScheme, L"http") != 0) { LOG_ErrorMsg(E_INVALIDARG); hr = E_INVALIDARG; goto CleanUp; } if (fAppendCacheBreaker) { SYSTEMTIME stCB; WCHAR wszCacheBreaker[12]; GetSystemTime(&stCB); hr = StringCchPrintfExW(wszCacheBreaker, ARRAYSIZE(wszCacheBreaker), NULL, NULL, MISTSAFE_STRING_FLAGS, L"?%02d%02d%02d%02d%02d", stCB.wYear % 100, stCB.wMonth, stCB.wDay, stCB.wHour, stCB.wMinute); if (FAILED(hr)) goto CleanUp; hr = StringCchCatExW(wszObject, c_cchMaxURLSize, wszCacheBreaker, NULL, NULL, MISTSAFE_STRING_FLAGS); if (FAILED(hr)) goto CleanUp; } if (fAllowProxy) dwAccessType = WINHTTP_ACCESS_TYPE_DEFAULT_PROXY; else dwAccessType = WINHTTP_ACCESS_TYPE_NO_PROXY; dwTickStart = GetTickCount(); START_INTERNET: // start to deal with Internet iRetryCounter++; // If the connection has already been established re-use it. hInternet = (*sfns.pfnWinHttpOpen)(c_wszUserAgent, dwAccessType, NULL, NULL, 0); if (hInternet == NULL) { hr = HRESULT_FROM_WIN32(GetLastError()); LOG_ErrorMsg(hr); goto CleanUp; } if (fAllowProxy != NULL) { GetWinHTTPProxyInfo(sfns, fCacheProxyInfo, hInternet, wszUrl, wszServerName, &AUProxyInfo); } hConnect = (*sfns.pfnWinHttpConnect)(hInternet, wszServerName, INTERNET_DEFAULT_HTTP_PORT, 0); if (hConnect == NULL) { hr = HRESULT_FROM_WIN32(GetLastError()); LOG_ErrorMsg(hr); goto CleanUp; } // if we're only doing a status check, then may as well just make a GET // request iVerb = (DWORD)((fCheckStatusOnly) ? ARRAYSIZE(rgwszVerbs) - 1 : 0); for(; iVerb < ARRAYSIZE(rgwszVerbs); iVerb++) { SafeWinHTTPCloseHandle(sfns, hOpenRequest); hr = MakeRequest(sfns, hConnect, NULL, wszServerName, rgwszVerbs[iVerb], wszObject, ((fAllowProxy) ? &AUProxyInfo : NULL), rghQuitEvents, cQuitEvents, &hOpenRequest); if (FAILED(hr)) goto CleanUp; dwLength = sizeof(dwStatus); if ((*sfns.pfnWinHttpQueryHeaders)(hOpenRequest, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, NULL, (LPVOID)&dwStatus, &dwLength, NULL) == FALSE) { hr = HRESULT_FROM_WIN32(GetLastError()); LOG_ErrorMsg(hr); goto CleanUp; } LOG_Internet(_T("WinHttp: 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("WinHttp: got failed status code from server %d\n"), dwStatus); // if it's the last verb in the list, then bail... if (rgwszVerbs[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("WinHttp: Only checking status. Exiting before header check and download.")); hr = S_OK; goto CleanUp; } // CheckFileHeader will return S_OK if we need to download the file, S_FALSE // if we don't, and some other HRESULT if a failure occurred hr = CheckFileHeader(sfns, hOpenRequest, rghQuitEvents, cQuitEvents, wszLocalFile, &cbRemoteFile, &ft); if (FAILED(hr)) 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 (hr == S_OK) { DWORD cbDownloaded; BOOL fCheckForHTML = fDoCabValidation; LOG_Internet(_T("WinHttp: 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 (rgwszVerbs[iVerb] != NULL) SafeWinHTTPCloseHandle(sfns, hOpenRequest); // now we know we need to download this file hr = MakeRequest(sfns, hConnect, hOpenRequest, wszServerName, NULL, wszObject, ((fAllowProxy) ? &AUProxyInfo : NULL), 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, &wszContentType); if (SUCCEEDED(hr) && wszContentType != NULL) { fCheckForHTML = FALSE; if (_wcsicmp(wszContentType, L"text/html") == 0) { LOG_Internet(_T("WinHttp: Content-Type header is text/html. Bailing.")); hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); goto CleanUp; } else { LOG_Internet(_T("WinHttp: Content-Type header is %ls. Continuing."), wszContentType); } } hr = NOERROR; } // open the file we're gonna spew into hFile = CreateFileW(wszLocalFile, 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("WinHttp: downloading to FILE %ls"), wszLocalFile); // bring down the bits hr = PerformDownloadToFile(sfns.pfnWinHttpReadData, hOpenRequest, hFile, cbRemoteFile, cbDownloadBuffer, rghQuitEvents, cQuitEvents, pfnCallback, pvCallbackData, &cbDownloaded); if (FAILED(hr)) { LOG_Internet(_T("WinHttp: Download failed: hr: 0x%08x"), hr); SafeCloseInvalidHandle(hFile); DeleteFileW(wszLocalFile); goto CleanUp; } LOG_Internet(_T("WinHttp: 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 (pcbDownloaded != NULL) *pcbDownloaded = 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(wszLocalFile); if (SUCCEEDED(hr)) { if (hr == S_FALSE) { LOG_Internet(_T("WinHttp: Download is not a html file")); hr = S_OK; } else { LOG_Internet(_T("WinHttp: Download is a html file. Failing download.")); hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT); DeleteFileW(wszLocalFile); goto CleanUp; } } else { LOG_Internet(_T("WinHttp: Unable to determine if download is a html file or not. Failing download.")); } } else if (fDoCabValidation == FALSE) { LOG_Internet(_T("WinHttp: Skipping cab validation.")); } } else { hr = S_OK; LOG_Internet(_T("WinHttp: 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: SafeWinHTTPCloseHandle(sfns, hOpenRequest); SafeWinHTTPCloseHandle(sfns, hConnect); SafeWinHTTPCloseHandle(sfns, hInternet); SafeHeapFree(wszContentType); // free up the proxy strings- they were allocated by WinHttp if (AUProxyInfo.ProxyInfo.lpszProxyBypass != NULL) GlobalFree(AUProxyInfo.ProxyInfo.lpszProxyBypass); if (AUProxyInfo.wszProxyOrig != NULL) GlobalFree(AUProxyInfo.wszProxyOrig); if (AUProxyInfo.rgwszProxies != NULL) GlobalFree(AUProxyInfo.rgwszProxies); ZeroMemory(&AUProxyInfo, sizeof(AUProxyInfo)); // 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) { 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_dwRetryTimeLimitInmsWinHttp) { 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(wszServerName); SafeHeapFree(wszObject); return hrToReturn; } #endif // defined(UNICODE) /////////////////////////////////////////////////////////////////////////////// // exported functions #if defined(UNICODE) // ************************************************************************** HRESULT GetAUProxySettings(LPCWSTR wszUrl, SAUProxySettings *paups) { LOG_Block("GetAUProxySettings()"); URL_COMPONENTS UrlComponents; LPWSTR wszServerName = NULL; LPWSTR wszObject = NULL; WCHAR wszUserName[UNLEN + 1]; WCHAR wszPasswd[UNLEN + 1]; WCHAR wszScheme[32]; SWinHTTPFunctions sfns; ETransportUsed etu; HMODULE hmod = NULL; HRESULT hr = S_OK; BOOL fRet, fLocked = FALSE; if (wszUrl == NULL || paups == NULL) { hr = E_INVALIDARG; goto done; } ZeroMemory(paups, sizeof(SAUProxySettings)); etu = LoadTransportDll(&sfns, &hmod, WUDF_ALLOWWINHTTPONLY); if (etu == etuNone) { hr = HRESULT_FROM_WIN32(GetLastError()); LOG_ErrorMsg(hr); goto done; } else if (etu != etuWinHttp) { hr = E_FAIL; LOG_Internet(_T("GetAUProxySettings called when in WinInet mode.")); goto done; } wszServerName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, c_cchMaxURLSize * sizeof(WCHAR)); wszObject = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, c_cchMaxURLSize * sizeof(WCHAR)); if (wszServerName == NULL || wszObject == NULL) { LOG_ErrorMsg(E_OUTOFMEMORY); hr = E_OUTOFMEMORY; goto done; } ZeroMemory(&UrlComponents, sizeof(UrlComponents)); UrlComponents.dwStructSize = sizeof(UrlComponents); UrlComponents.lpszHostName = wszServerName; UrlComponents.dwHostNameLength = c_cchMaxURLSize; UrlComponents.lpszUrlPath = wszObject; UrlComponents.dwUrlPathLength = c_cchMaxURLSize; UrlComponents.lpszUserName = wszUserName; UrlComponents.dwUserNameLength = ARRAYSIZE(wszUserName); UrlComponents.lpszPassword = wszPasswd; UrlComponents.dwPasswordLength = ARRAYSIZE(wszPasswd); UrlComponents.lpszScheme = wszScheme; UrlComponents.dwSchemeLength = ARRAYSIZE(wszScheme); if ((*sfns.pfnWinHttpCrackUrl)(wszUrl, 0, 0, &UrlComponents) == FALSE) { hr = HRESULT_FROM_WIN32(GetLastError()); LOG_ErrorMsg(hr); goto done; } if (wszUrl[0] == L'\0' || wszScheme[0] == L'\0' || wszServerName[0] == L'\0' || (_wcsicmp(wszScheme, L"http") != 0 && _wcsicmp(wszScheme, L"https") != 0)) { LOG_ErrorMsg(E_INVALIDARG); hr = E_INVALIDARG; goto done; } if (g_csCache.Lock() == FALSE) { hr = E_FAIL; goto done; } fLocked = TRUE; // get the proxy list if (g_wudlProxyCache.GetLastGoodProxy(wszServerName, paups) == FALSE) { // proxy was not in list if (GetLastError() == ERROR_FILE_NOT_FOUND) { SAUProxyInfo aupi; HINTERNET hInternet = NULL; LOG_Internet(_T("GetLastGoodProxy did not find a proxy object. Doing autodetect.")); hInternet = (*sfns.pfnWinHttpOpen)(c_wszUserAgent, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, NULL, NULL, 0); if (hInternet == NULL) { hr = HRESULT_FROM_WIN32(GetLastError()); LOG_ErrorMsg(hr); goto done; } fRet = GetWinHTTPProxyInfo(sfns, TRUE, hInternet, wszUrl, wszServerName, &aupi); (*sfns.pfnWinHttpCloseHandle)(hInternet); if (fRet == FALSE) { hr = HRESULT_FROM_WIN32(GetLastError()); LOG_ErrorMsg(hr); goto done; } paups->wszProxyOrig = aupi.wszProxyOrig; paups->wszBypass = aupi.ProxyInfo.lpszProxyBypass; paups->dwAccessType = aupi.ProxyInfo.dwAccessType; paups->cProxies = aupi.cProxies; paups->rgwszProxies = aupi.rgwszProxies; paups->iProxy = (DWORD)-1; SetLastError(ERROR_SUCCESS); } else { LOG_Internet(_T("GetLastGoodProxy failed...")); hr = HRESULT_FROM_WIN32(GetLastError()); LOG_ErrorMsg(hr); goto done; } } else { if (paups->wszProxyOrig != NULL) { // break it up into an array if (ProxyListToArray(paups->wszProxyOrig, &paups->rgwszProxies, &paups->cProxies) == FALSE) { hr = HRESULT_FROM_WIN32(GetLastError()); LOG_ErrorMsg(hr); goto done; } } else { paups->iProxy = (DWORD)-1; } } done: // Unlock returns FALSE as well, but we should never get here cuz we should // not have been able to take the lock above. if (fLocked) g_csCache.Unlock(); if (wszServerName != NULL) HeapFree(GetProcessHeap(), 0, wszServerName); if (wszObject != NULL) HeapFree(GetProcessHeap(), 0, wszObject); if (hmod != NULL) UnloadTransportDll(&sfns, hmod); return hr; } // ************************************************************************** HRESULT FreeAUProxySettings(SAUProxySettings *paups) { LOG_Block("FreeAUProxySettings()"); if (paups == NULL) goto done; if (paups->rgwszProxies != NULL) GlobalFree(paups->rgwszProxies); if (paups->wszBypass != NULL) GlobalFree(paups->wszBypass); if (paups->wszProxyOrig != NULL) GlobalFree(paups->wszProxyOrig); done: return S_OK; } // ************************************************************************** HRESULT CleanupDownloadLib(void) { LOG_Block("CleanupDownloadLib()"); HRESULT hr = S_OK; if (g_hmodWinHttp != NULL) { FreeLibrary(g_hmodWinHttp); g_hmodWinHttp = NULL; } if (g_hmodWinInet != NULL) { FreeLibrary(g_hmodWinInet); g_hmodWinInet = NULL; } if (g_csCache.Lock() == FALSE) return E_FAIL; __try { g_wudlProxyCache.Empty(); } __except(EXCEPTION_EXECUTE_HANDLER) { hr = E_FAIL; } // this returns FALSE as well, but we should never get here cuz we should // not have been able to take the lock above. g_csCache.Unlock(); return hr; } // ************************************************************************** HRESULT DownloadFile( LPCWSTR wszServerUrl, // full http url LPCWSTR wszLocalPath, // local directory to download file to LPCWSTR wszLocalFileName, // optional local file name to rename the downloaded file to if pszLocalPath does not contain file name PDWORD pdwDownloadedBytes, // bytes downloaded for this file HANDLE *hQuitEvents, // optional events causing this function to abort UINT nQuitEventCount, // number of quit events, must be 0 if array is NULL PFNDownloadCallback fpnCallback, // optional call back function VOID* pCallbackData, // parameter for call back function to use DWORD dwFlags ) { LOG_Block("DownloadFile()"); SWinHTTPFunctions sfns; ETransportUsed etu; HMODULE hmod = NULL; HRESULT hr = S_OK; LPWSTR wszLocalFile = NULL; DWORD dwFlagsToUse; // for full download, disable cache breaker. dwFlagsToUse = dwFlags & ~WUDF_APPENDCACHEBREAKER; ZeroMemory(&sfns, sizeof(sfns)); if (wszServerUrl == NULL || wszLocalPath == NULL) { LOG_ErrorMsg(ERROR_INVALID_PARAMETER); hr = E_INVALIDARG; goto done; } etu = LoadTransportDll(&sfns, &hmod, dwFlagsToUse); if (etu == etuNone) { hr = HRESULT_FROM_WIN32(GetLastError()); LOG_ErrorMsg(hr); goto done; } else if (etu != etuWinHttp && etu != etuWinInet) { hr = E_FAIL; LogError(hr, "Unexpected answer from LoadTransportDll(): %d", etu); goto done; } // Since StartDownload just takes a full path to the file to download, build // it here... // Note that we don't need to do this if we're just in status // checking mode) if ((dwFlags & WUDF_CHECKREQSTATUSONLY) == 0) { wszLocalFile = MakeFullLocalFilePath(wszServerUrl, wszLocalFileName, wszLocalPath); if (wszLocalFile == NULL) { hr = HRESULT_FROM_WIN32(GetLastError()); LOG_ErrorMsg(hr); goto done; } } if (etu == etuWinHttp) { hr = StartWinHttpDownload(sfns, wszServerUrl, wszLocalFile, pdwDownloadedBytes, hQuitEvents, nQuitEventCount, fpnCallback, pCallbackData, dwFlagsToUse, c_cbDownloadBuffer); } else { hr = StartWinInetDownload(hmod, wszServerUrl, wszLocalFile, pdwDownloadedBytes, hQuitEvents, nQuitEventCount, fpnCallback, pCallbackData, dwFlagsToUse, c_cbDownloadBuffer); } done: if (hmod != NULL) UnloadTransportDll(&sfns, hmod); SafeHeapFree(wszLocalFile); return hr; } // ************************************************************************** HRESULT DownloadFileLite(LPCWSTR wszDownloadUrl, LPCWSTR wszLocalFile, HANDLE hQuitEvent, DWORD dwFlags) { LOG_Block("DownloadFileLite()"); SWinHTTPFunctions sfns; ETransportUsed etu; HMODULE hmod = NULL; HRESULT hr = S_OK; DWORD dwFlagsToUse; // for lite download, force cache breaker & download retry dwFlagsToUse = dwFlags | WUDF_APPENDCACHEBREAKER | WUDF_DODOWNLOADRETRY; ZeroMemory(&sfns, sizeof(sfns)); etu = LoadTransportDll(&sfns, &hmod, dwFlagsToUse); switch (etu) { case etuNone: LOG_ErrorMsg(GetLastError()); hr = HRESULT_FROM_WIN32(GetLastError()); goto done; case etuWinHttp: hr = StartWinHttpDownload(sfns, wszDownloadUrl, wszLocalFile, NULL, ((hQuitEvent != NULL) ? &hQuitEvent : NULL), ((hQuitEvent != NULL) ? 1 : 0), NULL, NULL, dwFlagsToUse, c_cbDownloadBuffer); break; case etuWinInet: hr = StartWinInetDownload(hmod, wszDownloadUrl, wszLocalFile, NULL, ((hQuitEvent != NULL) ? &hQuitEvent : NULL), ((hQuitEvent != NULL) ? 1 : 0), NULL, NULL, dwFlagsToUse, c_cbDownloadBuffer); break; default: LogError(hr, "Unexpected answer from LoadTransportDll(): %d", etu); hr = HRESULT_FROM_WIN32(GetLastError()); goto done; } done: if (hmod != NULL) UnloadTransportDll(&sfns, hmod); return hr; } #else // !defined(UNICODE) // ************************************************************************** HRESULT GetAUProxySettings(LPCWSTR wszUrl, SAUProxySettings *paups) { return E_NOTIMPL; } // ************************************************************************** HRESULT FreeAUProxySettings(SAUProxySettings *paups) { return E_NOTIMPL; } // ************************************************************************** HRESULT CleanupDownloadLib(void) { if (g_hmodWinInet != NULL) { FreeLibrary(g_hmodWinInet); g_hmodWinInet = NULL; } return NOERROR; } // ************************************************************************** HRESULT DownloadFile( LPCSTR pszServerUrl, // full http url LPCSTR pszLocalPath, // local directory to download file to LPCSTR pszLocalFileName, // optional local file name to rename the downloaded file to if pszLocalPath does not contain file name PDWORD pdwDownloadedBytes, // bytes downloaded for this file HANDLE *hQuitEvents, // optional events causing this function to abort UINT nQuitEventCount, // number of quit events, must be 0 if array is NULL PFNDownloadCallback fpnCallback, // optional call back function VOID* pCallbackData, // parameter for call back function to use DWORD dwFlags ) { LOG_Block("DownloadFile()"); SWinHTTPFunctions sfns; ETransportUsed etu; HMODULE hmod = NULL; HRESULT hr = S_OK; LPSTR pszLocalFile = NULL; DWORD dwFlagsToUse; // for ansi, force wininet & disable any request to force winhttp // for full download, disable cache breaker. dwFlagsToUse = dwFlags | WUDF_ALLOWWININETONLY; dwFlagsToUse &= ~(WUDF_ALLOWWINHTTPONLY | WUDF_APPENDCACHEBREAKER); ZeroMemory(&sfns, sizeof(sfns)); etu = LoadTransportDll(&sfns, &hmod, dwFlagsToUse); if (etu == etuNone) { LOG_ErrorMsg(GetLastError()); hr = HRESULT_FROM_WIN32(GetLastError()); goto done; } else if (etu != etuWinInet) { hr = E_FAIL; LogError(hr, "Unexpected answer from LoadTransportDll(): %d", etu); goto done; } // Since StartDownload just takes a full path to the file to download, build // it here... // Note that we don't need to do this if we're just in status // checking mode) if ((dwFlags & WUDF_CHECKREQSTATUSONLY) == 0) { pszLocalFile = MakeFullLocalFilePath(pszServerUrl, pszLocalFileName, pszLocalPath); if (pszLocalFile == NULL) { LOG_ErrorMsg(GetLastError()); hr = HRESULT_FROM_WIN32(GetLastError()); goto done; } } hr = StartWinInetDownload(hmod, pszServerUrl, pszLocalFile, pdwDownloadedBytes, hQuitEvents, nQuitEventCount, fpnCallback, pCallbackData, dwFlagsToUse, c_cbDownloadBuffer); done: if (hmod != NULL) UnloadTransportDll(&sfns, hmod); SafeHeapFree(pszLocalFile); return hr; } // ************************************************************************** HRESULT DownloadFileLite(LPCSTR pszDownloadUrl, LPCSTR pszLocalFile, HANDLE hQuitEvent, DWORD dwFlags) { LOG_Block("DownloadFileLite()"); SWinHTTPFunctions sfns; ETransportUsed etu; HMODULE hmod = NULL; HRESULT hr = S_OK; DWORD dwFlagsToUse; // for ansi, force wininet & disable any request to force winhttp // for lite download, force cache breaker & download retry dwFlagsToUse = dwFlags | WUDF_APPENDCACHEBREAKER | WUDF_ALLOWWININETONLY | WUDF_DODOWNLOADRETRY; dwFlagsToUse &= ~WUDF_ALLOWWINHTTPONLY; ZeroMemory(&sfns, sizeof(sfns)); etu = LoadTransportDll(&sfns, &hmod, dwFlagsToUse); switch (etu) { case etuNone: LOG_ErrorMsg(GetLastError()); hr = HRESULT_FROM_WIN32(GetLastError()); goto done; case etuWinInet: hr = StartWinInetDownload(hmod, pszDownloadUrl, pszLocalFile, NULL, ((hQuitEvent != NULL) ? &hQuitEvent : NULL), ((hQuitEvent != NULL) ? 1 : 0), NULL, NULL, dwFlagsToUse, c_cbDownloadBuffer); break; default: case etuWinHttp: LogError(hr, "Unexpected answer from LoadTransportDll(): %d", etu); hr = HRESULT_FROM_WIN32(GetLastError()); goto done; } done: if (hmod != NULL) UnloadTransportDll(&sfns, hmod); return hr; } #endif // defined(UNICODE)