Source code of Windows XP (NT5)
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.

1856 lines
61 KiB

  1. #include <windows.h>
  2. #include <winhttp.h>
  3. #include <shlwapi.h>
  4. #include <logging.h>
  5. #include "iucommon.h"
  6. #include "download.h"
  7. #include "dllite.h"
  8. #include "dlutil.h"
  9. #include "malloc.h"
  10. #include "trust.h"
  11. #include "fileutil.h"
  12. #include "dlcache.h"
  13. #include "wusafefn.h"
  14. ///////////////////////////////////////////////////////////////////////////////
  15. // typedefs
  16. // winhttp
  17. extern "C"
  18. {
  19. typedef BOOL (WINAPI *pfn_WinHttpCrackUrl)(LPCWSTR, DWORD, DWORD, LPURL_COMPONENTS);
  20. typedef HINTERNET (WINAPI *pfn_WinHttpOpen)(LPCWSTR, DWORD, LPCWSTR, LPCWSTR, DWORD);
  21. typedef HINTERNET (WINAPI *pfn_WinHttpConnect)(HINTERNET, LPCWSTR, INTERNET_PORT, DWORD);
  22. typedef HINTERNET (WINAPI *pfn_WinHttpOpenRequest)(HINTERNET, LPCWSTR, LPCWSTR, LPCWSTR, LPCWSTR, LPCWSTR FAR *, DWORD);
  23. typedef BOOL (WINAPI *pfn_WinHttpSendRequest)(HINTERNET, LPCWSTR, DWORD, LPVOID, DWORD, DWORD, DWORD_PTR);
  24. typedef BOOL (WINAPI *pfn_WinHttpReceiveResponse)(HINTERNET, LPVOID);
  25. typedef BOOL (WINAPI *pfn_WinHttpQueryHeaders)(HINTERNET, DWORD, LPCWSTR, LPVOID, LPDWORD, LPDWORD);
  26. typedef BOOL (WINAPI *pfn_WinHttpReadData)(HINTERNET, LPVOID, DWORD, LPDWORD);
  27. typedef BOOL (WINAPI *pfn_WinHttpCloseHandle)(HINTERNET);
  28. typedef BOOL (WINAPI *pfn_WinHttpGetIEProxyConfigForCurrentUser)(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG *);
  29. typedef BOOL (WINAPI *pfn_WinHttpGetProxyForUrl)(HINTERNET, LPCWSTR, WINHTTP_AUTOPROXY_OPTIONS *, WINHTTP_PROXY_INFO *);
  30. typedef BOOL (WINAPI *pfn_WinHttpSetOption)(HINTERNET, DWORD, LPVOID, DWORD);
  31. }
  32. struct SWinHTTPFunctions
  33. {
  34. pfn_WinHttpGetIEProxyConfigForCurrentUser pfnWinHttpGetIEProxyConfigForCurrentUser;
  35. pfn_WinHttpGetProxyForUrl pfnWinHttpGetProxyForUrl;
  36. pfn_WinHttpCrackUrl pfnWinHttpCrackUrl;
  37. pfn_WinHttpOpen pfnWinHttpOpen;
  38. pfn_WinHttpConnect pfnWinHttpConnect;
  39. pfn_WinHttpOpenRequest pfnWinHttpOpenRequest;
  40. pfn_WinHttpSendRequest pfnWinHttpSendRequest;
  41. pfn_WinHttpReceiveResponse pfnWinHttpReceiveResponse;
  42. pfn_WinHttpQueryHeaders pfnWinHttpQueryHeaders;
  43. pfn_WinHttpReadData pfnWinHttpReadData;
  44. pfn_WinHttpCloseHandle pfnWinHttpCloseHandle;
  45. pfn_WinHttpSetOption pfnWinHttpSetOption;
  46. HMODULE hmod;
  47. };
  48. typedef struct tagSAUProxyInfo
  49. {
  50. WINHTTP_PROXY_INFO ProxyInfo;
  51. LPWSTR wszProxyOrig;
  52. LPWSTR *rgwszProxies;
  53. DWORD cProxies;
  54. DWORD iProxy;
  55. } SAUProxyInfo;
  56. typedef enum tagETransportUsed
  57. {
  58. etuNone = 0,
  59. etuWinHttp,
  60. etuWinInet,
  61. } ETransportUsed;
  62. #define SafeWinHTTPCloseHandle(sfns, x) if (NULL != x) { (*sfns.pfnWinHttpCloseHandle)(x); x = NULL; }
  63. #define StringOrConstW(wsz, wszConst) (((wsz) != NULL) ? (wsz) : (wszConst))
  64. ///////////////////////////////////////////////////////////////////////////////
  65. // globals
  66. #if defined(UNICODE)
  67. CWUDLProxyCache g_wudlProxyCache;
  68. CAutoCritSec g_csCache;
  69. #endif
  70. HMODULE g_hmodWinHttp = NULL;
  71. HMODULE g_hmodWinInet = NULL;
  72. ///////////////////////////////////////////////////////////////////////////////
  73. // utility functions
  74. // **************************************************************************
  75. static
  76. LPTSTR MakeFullLocalFilePath(LPCTSTR szUrl,
  77. LPCTSTR szFileName,
  78. LPCTSTR szPath)
  79. {
  80. LOG_Block("MakeFullLocalFilePath()");
  81. HRESULT hr = NOERROR;
  82. LPTSTR pszRet, pszFileNameToUse = NULL, pszQuery = NULL;
  83. LPTSTR pszFullPath = NULL;
  84. DWORD cchFile;
  85. TCHAR chTemp = _T('\0');
  86. // if we got a local filename passed to us, use it.
  87. if (szFileName != NULL)
  88. {
  89. pszFileNameToUse = (LPTSTR)szFileName;
  90. }
  91. // otherwise, parse the filename out of the URL & use it instead
  92. else
  93. {
  94. // first get a pointer to the querystring, if any
  95. pszQuery = _tcschr(szUrl, _T('?'));
  96. // next, find the last slash before the start of the querystring
  97. pszFileNameToUse = StrRChr(szUrl, pszQuery, _T('/'));
  98. // if we don't have a filename at this point, we can't continue
  99. // cuz there's nowhere to download the file to.
  100. if (pszFileNameToUse == NULL)
  101. {
  102. SetLastError(ERROR_INVALID_PARAMETER);
  103. goto done;
  104. }
  105. pszFileNameToUse++;
  106. // temporarily NULL out the first character of the querystring, if
  107. // we have a querystring. This makes the end of the filename the
  108. // end of the string.
  109. if (pszQuery != NULL)
  110. {
  111. chTemp = *pszQuery;
  112. *pszQuery = _T('\0');
  113. }
  114. }
  115. // add 2 for a possible backslash & the null terminator
  116. cchFile = 2 + _tcslen(szPath) + _tcslen(pszFileNameToUse);
  117. pszFullPath = (LPTSTR)HeapAlloc(GetProcessHeap(), 0, cchFile * sizeof(TCHAR));
  118. if (pszFullPath == NULL)
  119. {
  120. SetLastError(ERROR_OUTOFMEMORY);
  121. if (pszQuery != NULL)
  122. *pszQuery = chTemp;
  123. goto done;
  124. }
  125. // construct the path
  126. hr = SafePathCombine(pszFullPath, cchFile, szPath, pszFileNameToUse, 0);
  127. // if we nuked the first character of the querystring, restore it.
  128. if (pszQuery != NULL)
  129. *pszQuery = chTemp;
  130. if (FAILED(hr))
  131. {
  132. SetLastError(HRESULT_CODE(hr));
  133. SafeHeapFree(pszFullPath);
  134. goto done;
  135. }
  136. done:
  137. return pszFullPath;
  138. }
  139. // **************************************************************************
  140. static
  141. ETransportUsed LoadTransportDll(SWinHTTPFunctions *psfns, HMODULE *phmod,
  142. DWORD dwFlags)
  143. {
  144. LOG_Block("LoadTransportDll()");
  145. ETransportUsed etu = etuNone;
  146. HMODULE hmod = NULL;
  147. HRESULT hr = NOERROR;
  148. BOOL fAllowWininet;
  149. BOOL fAllowWinhttp;
  150. BOOL fPersistTrans = ((dwFlags & WUDF_PERSISTTRANSPORTDLL) != 0);
  151. if (psfns == NULL || phmod == NULL ||
  152. (dwFlags & WUDF_TRANSPORTMASK) == WUDF_TRANSPORTMASK)
  153. {
  154. SetLastError(ERROR_INVALID_PARAMETER);
  155. goto done;
  156. }
  157. dwFlags = GetAllowedDownloadTransport(dwFlags);
  158. fAllowWininet = ((dwFlags & WUDF_ALLOWWINHTTPONLY) == 0);
  159. fAllowWinhttp = ((dwFlags & WUDF_ALLOWWININETONLY) == 0);
  160. ZeroMemory(psfns, sizeof(SWinHTTPFunctions));
  161. *phmod = NULL;
  162. // first try to load the winhttp dll
  163. if (fAllowWinhttp)
  164. {
  165. if (g_hmodWinHttp == NULL)
  166. {
  167. hmod = LoadLibraryFromSystemDir(c_szWinHttpDll);
  168. if (hmod != NULL && fPersistTrans &&
  169. InterlockedCompareExchangePointer((LPVOID *)&g_hmodWinHttp,
  170. hmod, NULL) != NULL)
  171. {
  172. FreeLibrary(hmod);
  173. hmod = g_hmodWinHttp;
  174. }
  175. }
  176. else
  177. {
  178. hmod = g_hmodWinHttp;
  179. }
  180. }
  181. if (hmod != NULL)
  182. {
  183. psfns->hmod = hmod;
  184. psfns->pfnWinHttpGetProxyForUrl = (pfn_WinHttpGetProxyForUrl)GetProcAddress(hmod, "WinHttpGetProxyForUrl");
  185. psfns->pfnWinHttpCrackUrl = (pfn_WinHttpCrackUrl)GetProcAddress(hmod, "WinHttpCrackUrl");
  186. psfns->pfnWinHttpOpen = (pfn_WinHttpOpen)GetProcAddress(hmod, "WinHttpOpen");
  187. psfns->pfnWinHttpConnect = (pfn_WinHttpConnect)GetProcAddress(hmod, "WinHttpConnect");
  188. psfns->pfnWinHttpOpenRequest = (pfn_WinHttpOpenRequest)GetProcAddress(hmod, "WinHttpOpenRequest");
  189. psfns->pfnWinHttpSendRequest = (pfn_WinHttpSendRequest)GetProcAddress(hmod, "WinHttpSendRequest");
  190. psfns->pfnWinHttpReceiveResponse = (pfn_WinHttpReceiveResponse)GetProcAddress(hmod, "WinHttpReceiveResponse");
  191. psfns->pfnWinHttpQueryHeaders = (pfn_WinHttpQueryHeaders)GetProcAddress(hmod, "WinHttpQueryHeaders");
  192. psfns->pfnWinHttpReadData = (pfn_WinHttpReadData)GetProcAddress(hmod, "WinHttpReadData");
  193. psfns->pfnWinHttpCloseHandle = (pfn_WinHttpCloseHandle)GetProcAddress(hmod, "WinHttpCloseHandle");
  194. psfns->pfnWinHttpSetOption = (pfn_WinHttpSetOption)GetProcAddress(hmod, "WinHttpSetOption");
  195. psfns->pfnWinHttpGetIEProxyConfigForCurrentUser = (pfn_WinHttpGetIEProxyConfigForCurrentUser)GetProcAddress(hmod, "WinHttpGetIEProxyConfigForCurrentUser");
  196. if (psfns->pfnWinHttpCrackUrl == NULL ||
  197. psfns->pfnWinHttpOpen == NULL ||
  198. psfns->pfnWinHttpConnect == NULL ||
  199. psfns->pfnWinHttpOpenRequest == NULL ||
  200. psfns->pfnWinHttpSendRequest == NULL ||
  201. psfns->pfnWinHttpReceiveResponse == NULL ||
  202. psfns->pfnWinHttpQueryHeaders == NULL ||
  203. psfns->pfnWinHttpReadData == NULL ||
  204. psfns->pfnWinHttpCloseHandle == NULL ||
  205. psfns->pfnWinHttpGetProxyForUrl == NULL ||
  206. psfns->pfnWinHttpGetIEProxyConfigForCurrentUser == NULL ||
  207. psfns->pfnWinHttpSetOption == NULL)
  208. {
  209. // do this logging here cuz we'll try wininet afterward & we want
  210. // to make sure to log this error as well
  211. LOG_ErrorMsg(ERROR_PROC_NOT_FOUND);
  212. SetLastError(ERROR_PROC_NOT_FOUND);
  213. ZeroMemory(psfns, sizeof(SWinHTTPFunctions));
  214. FreeLibrary(hmod);
  215. hmod = NULL;
  216. }
  217. else
  218. {
  219. LOG_Internet(_T("Successfully loaded WinHttp.dll"));
  220. etu = etuWinHttp;
  221. *phmod = hmod;
  222. }
  223. }
  224. // if hmod is NULL at this point, then try to fall back to wininet. If
  225. // that fails, we can only bail...
  226. if (fAllowWininet && hmod == NULL)
  227. {
  228. if (g_hmodWinInet == NULL)
  229. {
  230. hmod = LoadLibraryFromSystemDir(c_szWinInetDll);
  231. if (hmod == NULL)
  232. goto done;
  233. if (fPersistTrans &&
  234. InterlockedCompareExchangePointer((LPVOID *)&g_hmodWinInet,
  235. hmod, NULL) != NULL)
  236. {
  237. FreeLibrary(hmod);
  238. hmod = g_hmodWinInet;
  239. }
  240. }
  241. LOG_Internet(_T("Successfully loaded WinInet.dll (no functions yet)"));
  242. etu = etuWinInet;
  243. *phmod = hmod;
  244. }
  245. done:
  246. return etu;
  247. }
  248. // **************************************************************************
  249. static
  250. BOOL UnloadTransportDll(SWinHTTPFunctions *psfns, HMODULE hmod)
  251. {
  252. LOG_Block("UnloadTransportDll()");
  253. if (hmod != NULL && hmod != g_hmodWinHttp && hmod != g_hmodWinInet)
  254. FreeLibrary(hmod);
  255. if (psfns != NULL)
  256. ZeroMemory(psfns, sizeof(SWinHTTPFunctions));
  257. return TRUE;
  258. }
  259. // we only care about winhttp on unicode platforms!
  260. #if defined(UNICODE)
  261. // **************************************************************************
  262. static
  263. BOOL ProxyListToArray(LPWSTR wszProxy, LPWSTR **prgwszProxies, DWORD *pcProxies)
  264. {
  265. LPWSTR pwszProxy = wszProxy;
  266. LPWSTR *rgwszProxies = NULL;
  267. DWORD cProxies = 0, iProxy;
  268. BOOL fRet = FALSE;
  269. if (prgwszProxies == NULL || pcProxies == NULL)
  270. {
  271. SetLastError(ERROR_INVALID_PARAMETER);
  272. goto done;
  273. }
  274. *prgwszProxies = NULL;
  275. *pcProxies = 0;
  276. if (wszProxy == NULL || *wszProxy == L'\0')
  277. goto done;
  278. // walk the string & count how many proxies we have
  279. for(;;)
  280. {
  281. for(;
  282. *pwszProxy != L';' && *pwszProxy != L'\0';
  283. pwszProxy++);
  284. cProxies++;
  285. if (*pwszProxy == L'\0')
  286. break;
  287. else
  288. pwszProxy++;
  289. }
  290. // alloc an array to hold 'em
  291. rgwszProxies = (LPWSTR *)GlobalAlloc(GPTR, sizeof(LPWSTR) * cProxies);
  292. if (rgwszProxies == NULL)
  293. {
  294. SetLastError(ERROR_OUTOFMEMORY);
  295. goto done;
  296. }
  297. // fill the array
  298. pwszProxy = wszProxy;
  299. for(iProxy = 0; iProxy < cProxies; iProxy++)
  300. {
  301. rgwszProxies[iProxy] = pwszProxy;
  302. for(;
  303. *pwszProxy != L';' && *pwszProxy != L'\0';
  304. pwszProxy++);
  305. if (*pwszProxy == L'\0')
  306. {
  307. break;
  308. }
  309. else
  310. {
  311. *pwszProxy = L'\0';
  312. pwszProxy++;
  313. }
  314. }
  315. *prgwszProxies = rgwszProxies;
  316. *pcProxies = cProxies;
  317. rgwszProxies = NULL;
  318. fRet = TRUE;
  319. done:
  320. if (rgwszProxies != NULL)
  321. GlobalFree(rgwszProxies);
  322. return fRet;
  323. }
  324. // **************************************************************************
  325. static
  326. DWORD GetInitialProxyIndex(DWORD cProxies)
  327. {
  328. SYSTEMTIME st;
  329. DWORD iProxy;
  330. GetSystemTime(&st);
  331. // this would be incredibly weird, but it's easy to deal with so do so
  332. if (st.wMilliseconds >= 1000)
  333. st.wMilliseconds = st.wMilliseconds % 1000;
  334. // so we don't have to use the crt random number generator, just fake it
  335. return (st.wMilliseconds * cProxies) / 1000;
  336. }
  337. // **************************************************************************
  338. static
  339. BOOL GetWinHTTPProxyInfo(SWinHTTPFunctions &sfns, BOOL fCacheResults,
  340. HINTERNET hInternet, LPCWSTR wszURL, LPCWSTR wszSrv,
  341. SAUProxyInfo *pAUProxyInfo)
  342. {
  343. LOG_Block("GetWinHTTPProxyInfo()");
  344. WINHTTP_CURRENT_USER_IE_PROXY_CONFIG IEProxyCfg;
  345. WINHTTP_AUTOPROXY_OPTIONS AutoProxyOpt;
  346. DWORD dwErr = ERROR_SUCCESS;
  347. BOOL fUseAutoProxy = FALSE;
  348. BOOL fGotProxy = FALSE;
  349. BOOL fRet = FALSE;
  350. ZeroMemory(&IEProxyCfg, sizeof(IEProxyCfg));
  351. ZeroMemory(&AutoProxyOpt, sizeof(AutoProxyOpt));
  352. // only need to acquire the CS if we're caching results
  353. if (fCacheResults)
  354. g_csCache.Lock();
  355. if (pAUProxyInfo == NULL || hInternet == NULL)
  356. {
  357. SetLastError(ERROR_INVALID_PARAMETER);
  358. goto done;
  359. }
  360. ZeroMemory(pAUProxyInfo, sizeof(SAUProxyInfo));
  361. // if we're not caching results, skip directly to the proxy fetch
  362. if (fCacheResults &&
  363. g_wudlProxyCache.Find(wszSrv, &pAUProxyInfo->ProxyInfo.lpszProxy,
  364. &pAUProxyInfo->ProxyInfo.lpszProxyBypass,
  365. &pAUProxyInfo->ProxyInfo.dwAccessType))
  366. {
  367. LOG_Internet(_T("WinHttp: Cached proxy settings Proxy: %ls | Bypass: %ls | AccessType: %d"),
  368. StringOrConstW(pAUProxyInfo->ProxyInfo.lpszProxy, L"(none)"),
  369. StringOrConstW(pAUProxyInfo->ProxyInfo.lpszProxyBypass, L"(none)"),
  370. pAUProxyInfo->ProxyInfo.dwAccessType);
  371. pAUProxyInfo->wszProxyOrig = pAUProxyInfo->ProxyInfo.lpszProxy;
  372. // we'll deal with this function failing later on when we cycle thru
  373. // the proxies. We'll basically only use the first and never cycle
  374. if (ProxyListToArray(pAUProxyInfo->wszProxyOrig,
  375. &pAUProxyInfo->rgwszProxies,
  376. &pAUProxyInfo->cProxies))
  377. {
  378. DWORD iProxy;
  379. iProxy = GetInitialProxyIndex(pAUProxyInfo->cProxies);
  380. pAUProxyInfo->ProxyInfo.lpszProxy = pAUProxyInfo->rgwszProxies[iProxy];
  381. pAUProxyInfo->iProxy = iProxy;
  382. }
  383. goto done;
  384. }
  385. // first try to get the current user's IE configuration
  386. fRet = (*sfns.pfnWinHttpGetIEProxyConfigForCurrentUser)(&IEProxyCfg);
  387. if (fRet)
  388. {
  389. LOG_Internet(_T("WinHttp: Read IE user proxy settings"));
  390. if (IEProxyCfg.fAutoDetect)
  391. {
  392. AutoProxyOpt.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
  393. AutoProxyOpt.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP |
  394. WINHTTP_AUTO_DETECT_TYPE_DNS_A;
  395. fUseAutoProxy = TRUE;
  396. }
  397. if (IEProxyCfg.lpszAutoConfigUrl != NULL)
  398. {
  399. AutoProxyOpt.dwFlags |= WINHTTP_AUTOPROXY_CONFIG_URL;
  400. AutoProxyOpt.lpszAutoConfigUrl = IEProxyCfg.lpszAutoConfigUrl;
  401. fUseAutoProxy = TRUE;
  402. }
  403. AutoProxyOpt.fAutoLogonIfChallenged = TRUE;
  404. }
  405. // couldn't get current user's config options, so just try autoproxy
  406. else
  407. {
  408. AutoProxyOpt.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
  409. AutoProxyOpt.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP |
  410. WINHTTP_AUTO_DETECT_TYPE_DNS_A;
  411. AutoProxyOpt.fAutoLogonIfChallenged = TRUE;
  412. fUseAutoProxy = TRUE;
  413. }
  414. if (fUseAutoProxy)
  415. {
  416. LOG_Internet(_T("WinHttp: Doing autoproxy detection"));
  417. fGotProxy = (*sfns.pfnWinHttpGetProxyForUrl)(hInternet, wszURL,
  418. &AutoProxyOpt,
  419. &pAUProxyInfo->ProxyInfo);
  420. }
  421. // if we didn't try to autoconfigure the proxy or we did & it failed, then
  422. // check and see if we had one defined by the user
  423. if ((fUseAutoProxy == FALSE || fGotProxy == FALSE) &&
  424. IEProxyCfg.lpszProxy != NULL)
  425. {
  426. // the empty string and L':' are not valid server names, so skip them
  427. // if they are what is set for the proxy
  428. if (!(IEProxyCfg.lpszProxy[0] == L'\0' ||
  429. (IEProxyCfg.lpszProxy[0] == L':' &&
  430. IEProxyCfg.lpszProxy[1] == L'\0')))
  431. {
  432. pAUProxyInfo->ProxyInfo.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
  433. pAUProxyInfo->ProxyInfo.lpszProxy = IEProxyCfg.lpszProxy;
  434. IEProxyCfg.lpszProxy = NULL;
  435. }
  436. // the empty string and L':' are not valid server names, so skip them
  437. // if they are what is set for the proxy bypass
  438. if (IEProxyCfg.lpszProxyBypass != NULL &&
  439. !(IEProxyCfg.lpszProxyBypass[0] == L'\0' ||
  440. (IEProxyCfg.lpszProxyBypass[0] == L':' &&
  441. IEProxyCfg.lpszProxyBypass[1] == L'\0')))
  442. {
  443. pAUProxyInfo->ProxyInfo.lpszProxyBypass = IEProxyCfg.lpszProxyBypass;
  444. IEProxyCfg.lpszProxyBypass = NULL;
  445. }
  446. }
  447. LOG_Internet(_T("WinHttp: Proxy settings Proxy: %ls | Bypass: %ls | AccessType: %d"),
  448. StringOrConstW(pAUProxyInfo->ProxyInfo.lpszProxy, L"(none)"),
  449. StringOrConstW(pAUProxyInfo->ProxyInfo.lpszProxyBypass, L"(none)"),
  450. pAUProxyInfo->ProxyInfo.dwAccessType);
  451. // don't really care if this fails. It'll just mean a perf hit the next
  452. // time we go fetch the proxy info
  453. if (fCacheResults &&
  454. g_wudlProxyCache.Set(wszSrv, pAUProxyInfo->ProxyInfo.lpszProxy,
  455. pAUProxyInfo->ProxyInfo.lpszProxyBypass,
  456. pAUProxyInfo->ProxyInfo.dwAccessType) == FALSE)
  457. {
  458. LOG_Internet(_T("WinHttp: Attempt to cache proxy info failed: %d"),
  459. GetLastError());
  460. }
  461. pAUProxyInfo->wszProxyOrig = pAUProxyInfo->ProxyInfo.lpszProxy;
  462. // we'll deal with this function failing later on when we cycle thru the
  463. // proxies. We'll basically only use the first and never cycle
  464. // Note that this function call has to be AFTER the cache call since we
  465. // modify the proxy list by embedding null terminators in it in place of
  466. // the separating semicolons.
  467. if (ProxyListToArray(pAUProxyInfo->wszProxyOrig, &pAUProxyInfo->rgwszProxies,
  468. &pAUProxyInfo->cProxies))
  469. {
  470. DWORD iProxy;
  471. iProxy = GetInitialProxyIndex(pAUProxyInfo->cProxies);
  472. pAUProxyInfo->ProxyInfo.lpszProxy = pAUProxyInfo->rgwszProxies[iProxy];
  473. pAUProxyInfo->iProxy = iProxy;
  474. }
  475. fRet = TRUE;
  476. done:
  477. // only need to release the CS if we're caching results
  478. if (fCacheResults)
  479. g_csCache.Unlock();
  480. dwErr = GetLastError();
  481. if (IEProxyCfg.lpszAutoConfigUrl != NULL)
  482. GlobalFree(IEProxyCfg.lpszAutoConfigUrl);
  483. if (IEProxyCfg.lpszProxy != NULL)
  484. GlobalFree(IEProxyCfg.lpszProxy);
  485. if (IEProxyCfg.lpszProxyBypass != NULL)
  486. GlobalFree(IEProxyCfg.lpszProxyBypass);
  487. SetLastError(dwErr);
  488. return fRet;
  489. }
  490. // **************************************************************************
  491. static
  492. HRESULT MakeRequest(SWinHTTPFunctions &sfns,
  493. HINTERNET hConnect,
  494. HINTERNET hRequest,
  495. LPCWSTR wszSrv,
  496. LPCWSTR wszVerb,
  497. LPCWSTR wszObject,
  498. SAUProxyInfo *pAUProxyInfo,
  499. HANDLE *rghEvents,
  500. DWORD cEvents,
  501. HINTERNET *phRequest)
  502. {
  503. LOG_Block("MakeRequest()");
  504. HINTERNET hOpenRequest = hRequest;
  505. LPCWSTR wszAcceptTypes[] = {L"*/*", NULL};
  506. HRESULT hr = S_OK;
  507. DWORD iProxy = 0, dwErr;
  508. BOOL fProxy, fContinue = TRUE;
  509. fProxy = (pAUProxyInfo != NULL && pAUProxyInfo->ProxyInfo.lpszProxy != NULL);
  510. LOG_Internet(_T("WinHttp: Making %ls request for %ls"), wszVerb, wszObject);
  511. // if we were passed in a request handle, then use it. Otherwise, gotta
  512. // open one
  513. if (hOpenRequest == NULL)
  514. {
  515. hOpenRequest = (*sfns.pfnWinHttpOpenRequest)(hConnect, wszVerb, wszObject,
  516. NULL, NULL, wszAcceptTypes, 0);
  517. if (hOpenRequest == NULL)
  518. {
  519. hr = HRESULT_FROM_WIN32(GetLastError());
  520. LOG_ErrorMsg(hr);
  521. goto done;
  522. }
  523. }
  524. if (HandleEvents(rghEvents, cEvents) == FALSE)
  525. {
  526. hr = E_ABORT;
  527. goto done;
  528. }
  529. // if we have a list of proxies & the first one is bad, winhttp won't try
  530. // any others. So we have to do it ourselves. That is the purpose of this
  531. // loop.
  532. if (fProxy &&
  533. pAUProxyInfo->cProxies > 1 && pAUProxyInfo->rgwszProxies != NULL)
  534. iProxy = (pAUProxyInfo->iProxy + 1) % pAUProxyInfo->cProxies;
  535. for(;;)
  536. {
  537. if (fProxy)
  538. {
  539. LOG_Internet(_T("WinHttp: Using proxy: Proxy: %ls | Bypass %ls | AccessType: %d"),
  540. StringOrConstW(pAUProxyInfo->ProxyInfo.lpszProxy, L"(none)"),
  541. StringOrConstW(pAUProxyInfo->ProxyInfo.lpszProxyBypass, L"(none)"),
  542. pAUProxyInfo->ProxyInfo.dwAccessType);
  543. if ((*sfns.pfnWinHttpSetOption)(hOpenRequest, WINHTTP_OPTION_PROXY,
  544. &pAUProxyInfo->ProxyInfo,
  545. sizeof(WINHTTP_PROXY_INFO)) == FALSE)
  546. {
  547. hr = HRESULT_FROM_WIN32(GetLastError());
  548. LOG_ErrorMsg(hr);
  549. goto done;
  550. }
  551. }
  552. hr = S_OK;
  553. SetLastError(ERROR_SUCCESS);
  554. if ((*sfns.pfnWinHttpSendRequest)(hOpenRequest, NULL, 0, NULL, 0, 0, 0) == FALSE)
  555. {
  556. // dwErr = GetLastError();
  557. // LOG_Internet(_T("WinHttp: WinHttpSendRequest failed: %d. Request object at: 0x%x"),
  558. // dwErr, hOpenRequest);
  559. // SetLastError(dwErr);
  560. goto loopDone;
  561. }
  562. if ((*sfns.pfnWinHttpReceiveResponse)(hOpenRequest, 0) == FALSE)
  563. {
  564. // dwErr = GetLastError();
  565. // LOG_Internet(_T("WinHttp: WinHttpReceiveResponse failed: %d. Request object at: 0x%x"),
  566. // dwErr, hOpenRequest);
  567. // SetLastError(dwErr);
  568. goto loopDone;
  569. }
  570. loopDone:
  571. fContinue = FALSE;
  572. dwErr = GetLastError();
  573. if (dwErr != ERROR_SUCCESS)
  574. hr = HRESULT_FROM_WIN32(dwErr);
  575. else
  576. hr = S_OK;
  577. // if we succeeded, then we're done here...
  578. if (SUCCEEDED(hr))
  579. {
  580. if (fProxy)
  581. {
  582. if (g_csCache.Lock() == FALSE)
  583. {
  584. hr = E_FAIL;
  585. goto done;
  586. }
  587. g_wudlProxyCache.SetLastGoodProxy(wszSrv, pAUProxyInfo->iProxy);
  588. // Unlock returns FALSE as well, but we should never get here cuz
  589. // we should not have been able to take the lock above.
  590. g_csCache.Unlock();
  591. }
  592. break;
  593. }
  594. LOG_ErrorMsg(hr);
  595. // we only care about retrying if we have a proxy server & get a
  596. // 'cannot connect' error.
  597. if (fProxy &&
  598. (dwErr == ERROR_WINHTTP_CANNOT_CONNECT ||
  599. dwErr == ERROR_WINHTTP_CONNECTION_ERROR ||
  600. dwErr == ERROR_WINHTTP_NAME_NOT_RESOLVED ||
  601. dwErr == ERROR_WINHTTP_TIMEOUT))
  602. {
  603. LOG_Internet(_T("WinHttp: Connection failure: %d"), dwErr);
  604. if (pAUProxyInfo->cProxies > 1 && pAUProxyInfo->rgwszProxies != NULL &&
  605. iProxy != pAUProxyInfo->iProxy)
  606. {
  607. pAUProxyInfo->ProxyInfo.lpszProxy = pAUProxyInfo->rgwszProxies[iProxy];
  608. iProxy = (iProxy + 1) % pAUProxyInfo->cProxies;
  609. fContinue = TRUE;
  610. }
  611. else
  612. {
  613. LOG_Internet(_T("WinHttp: No proxies left. Failing download."));
  614. }
  615. }
  616. if (fContinue == FALSE)
  617. goto done;
  618. }
  619. if (FAILED(hr))
  620. goto done;
  621. if (HandleEvents(rghEvents, cEvents) == FALSE)
  622. {
  623. hr = E_ABORT;
  624. goto done;
  625. }
  626. *phRequest = hOpenRequest;
  627. hOpenRequest = NULL;
  628. done:
  629. // don't want to free the handle if we didn't open it.
  630. if (hRequest != hOpenRequest)
  631. SafeWinHTTPCloseHandle(sfns, hOpenRequest);
  632. return hr;
  633. }
  634. // **************************************************************************
  635. static
  636. HRESULT CheckFileHeader(SWinHTTPFunctions &sfns,
  637. HINTERNET hOpenRequest,
  638. HANDLE *rghEvents,
  639. DWORD cEvents,
  640. LPCWSTR wszFile,
  641. DWORD *pcbFile,
  642. FILETIME *pft)
  643. {
  644. LOG_Block("CheckFileHeader()");
  645. SYSTEMTIME st;
  646. FILETIME ft;
  647. HRESULT hr = S_OK;
  648. DWORD dwLength, dwStatus, dwFileSize, dwErr;
  649. dwLength = sizeof(st);
  650. if ((*sfns.pfnWinHttpQueryHeaders)(hOpenRequest,
  651. WINHTTP_QUERY_LAST_MODIFIED | WINHTTP_QUERY_FLAG_SYSTEMTIME,
  652. NULL, (LPVOID)&st, &dwLength, NULL) == FALSE)
  653. {
  654. hr = HRESULT_FROM_WIN32(GetLastError());
  655. LOG_ErrorMsg(hr);
  656. goto done;
  657. }
  658. SystemTimeToFileTime(&st, &ft);
  659. // Now Get the FileSize information from the Server
  660. dwLength = sizeof(dwFileSize);
  661. if ((*sfns.pfnWinHttpQueryHeaders)(hOpenRequest,
  662. WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER,
  663. NULL, (LPVOID)&dwFileSize, &dwLength, NULL) == FALSE)
  664. {
  665. hr = HRESULT_FROM_WIN32(GetLastError());
  666. LOG_ErrorMsg(hr);
  667. goto done;
  668. }
  669. if (HandleEvents(rghEvents, cEvents) == FALSE)
  670. {
  671. hr = E_ABORT;
  672. goto done;
  673. }
  674. if (pcbFile != NULL)
  675. *pcbFile = dwFileSize;
  676. if (pft != NULL)
  677. CopyMemory(pft, &ft, sizeof(FILETIME));
  678. hr = IsServerFileDifferentW(ft, dwFileSize, wszFile) ? S_OK : S_FALSE;
  679. done:
  680. return hr;
  681. }
  682. // **************************************************************************
  683. static
  684. HRESULT GetContentTypeHeader(SWinHTTPFunctions &sfns,
  685. HINTERNET hOpenRequest,
  686. LPWSTR *pwszContentType)
  687. {
  688. LOG_Block("GetContentTypeHeader()");
  689. HRESULT hr = S_OK;
  690. LPWSTR wszContentType = NULL;
  691. DWORD dwLength, dwErr;
  692. BOOL fRet;
  693. *pwszContentType = NULL;
  694. dwLength = 0;
  695. fRet = (*sfns.pfnWinHttpQueryHeaders)(hOpenRequest,
  696. WINHTTP_QUERY_CONTENT_TYPE,
  697. NULL, (LPVOID)NULL, &dwLength,
  698. NULL);
  699. if (fRet == FALSE && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
  700. {
  701. hr = HRESULT_FROM_WIN32(GetLastError());
  702. LOG_ErrorMsg(hr);
  703. goto done;
  704. }
  705. if (dwLength == 0)
  706. {
  707. hr = HRESULT_FROM_WIN32(ERROR_WINHTTP_HEADER_NOT_FOUND);
  708. goto done;
  709. }
  710. wszContentType = (LPWSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
  711. dwLength);
  712. if (wszContentType == NULL)
  713. {
  714. hr = E_INVALIDARG;
  715. LOG_ErrorMsg(hr);
  716. goto done;
  717. }
  718. if ((*sfns.pfnWinHttpQueryHeaders)(hOpenRequest,
  719. WINHTTP_QUERY_CONTENT_TYPE,
  720. NULL, (LPVOID)wszContentType, &dwLength,
  721. NULL) == FALSE)
  722. {
  723. hr = HRESULT_FROM_WIN32(GetLastError());
  724. LOG_ErrorMsg(hr);
  725. goto done;
  726. }
  727. *pwszContentType = wszContentType;
  728. wszContentType = NULL;
  729. done:
  730. SafeHeapFree(wszContentType);
  731. return hr;
  732. }
  733. // **************************************************************************
  734. static
  735. HRESULT StartWinHttpDownload(SWinHTTPFunctions &sfns,
  736. LPCWSTR wszUrl,
  737. LPCWSTR wszLocalFile,
  738. DWORD *pcbDownloaded,
  739. HANDLE *rghQuitEvents,
  740. UINT cQuitEvents,
  741. PFNDownloadCallback pfnCallback,
  742. LPVOID pvCallbackData,
  743. DWORD dwFlags,
  744. DWORD cbDownloadBuffer)
  745. {
  746. LOG_Block("StartWinHttpDownload()");
  747. URL_COMPONENTS UrlComponents;
  748. SAUProxyInfo AUProxyInfo;
  749. HINTERNET hInternet = NULL;
  750. HINTERNET hConnect = NULL;
  751. HINTERNET hOpenRequest = NULL;
  752. DWORD dwStatus, dwAccessType;
  753. LPWSTR wszServerName = NULL;
  754. LPWSTR wszObject = NULL;
  755. LPWSTR wszContentType = NULL;
  756. WCHAR wszUserName[UNLEN + 1];
  757. WCHAR wszPasswd[UNLEN + 1];
  758. WCHAR wszScheme[32];
  759. // NULL (equivalent to "GET") MUST be the last verb in the list
  760. LPCWSTR rgwszVerbs[] = { L"HEAD", NULL };
  761. DWORD iVerb;
  762. HRESULT hr = S_OK, hrToReturn = S_OK;
  763. BOOL fRet = TRUE;
  764. FILETIME ft;
  765. HANDLE hFile = INVALID_HANDLE_VALUE;
  766. DWORD cbRemoteFile;
  767. DWORD dwLength;
  768. DWORD dwTickStart = 0, dwTickEnd = 0;
  769. int iRetryCounter = -1; // non-negative during download mode
  770. BOOL fAllowProxy = ((dwFlags & WUDF_DONTALLOWPROXY) == 0);
  771. BOOL fCheckStatusOnly = ((dwFlags & WUDF_CHECKREQSTATUSONLY) != 0);
  772. BOOL fAppendCacheBreaker = ((dwFlags & WUDF_APPENDCACHEBREAKER) != 0);
  773. BOOL fSkipDownloadRetry = ((dwFlags & WUDF_DODOWNLOADRETRY) == 0);
  774. BOOL fDoCabValidation = ((dwFlags & WUDF_SKIPCABVALIDATION) == 0);
  775. BOOL fCacheProxyInfo = ((dwFlags & WUDF_SKIPAUTOPROXYCACHE) == 0);
  776. ZeroMemory(&AUProxyInfo, sizeof(AUProxyInfo));
  777. if ((wszUrl == NULL) ||
  778. (wszLocalFile == NULL && fCheckStatusOnly == FALSE))
  779. {
  780. LOG_ErrorMsg(E_INVALIDARG);
  781. return E_INVALIDARG;
  782. }
  783. if (pcbDownloaded != NULL)
  784. *pcbDownloaded = 0;
  785. wszServerName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, c_cchMaxURLSize * sizeof(WCHAR));
  786. wszObject = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, c_cchMaxURLSize * sizeof(WCHAR));
  787. if (wszServerName == NULL || wszObject == NULL)
  788. {
  789. LOG_ErrorMsg(E_OUTOFMEMORY);
  790. hr = E_OUTOFMEMORY;
  791. goto CleanUp;
  792. }
  793. wszServerName[0] = L'\0';
  794. wszObject[0] = L'\0';
  795. wszUserName[0] = L'\0';
  796. wszPasswd[0] = L'\0';
  797. if (HandleEvents(rghQuitEvents, cQuitEvents) == FALSE)
  798. {
  799. hr = E_ABORT;
  800. goto CleanUp;
  801. }
  802. // Break down the URL into its various components for the InternetAPI calls.
  803. // Specifically we need the server name, object to download, username and
  804. // password information.
  805. ZeroMemory(&UrlComponents, sizeof(UrlComponents));
  806. UrlComponents.dwStructSize = sizeof(UrlComponents);
  807. UrlComponents.lpszHostName = wszServerName;
  808. UrlComponents.dwHostNameLength = c_cchMaxURLSize;
  809. UrlComponents.lpszUrlPath = wszObject;
  810. UrlComponents.dwUrlPathLength = c_cchMaxURLSize;
  811. UrlComponents.lpszUserName = wszUserName;
  812. UrlComponents.dwUserNameLength = ARRAYSIZE(wszUserName);
  813. UrlComponents.lpszPassword = wszPasswd;
  814. UrlComponents.dwPasswordLength = ARRAYSIZE(wszPasswd);
  815. UrlComponents.lpszScheme = wszScheme;
  816. UrlComponents.dwSchemeLength = ARRAYSIZE(wszScheme);
  817. LOG_Internet(_T("WinHttp: Downloading URL %ls to FILE %ls"), wszUrl, wszLocalFile);
  818. if ((*sfns.pfnWinHttpCrackUrl)(wszUrl, 0, 0, &UrlComponents) == FALSE)
  819. {
  820. hr = HRESULT_FROM_WIN32(GetLastError());
  821. LOG_ErrorMsg(hr);
  822. goto CleanUp;
  823. }
  824. if (wszUrl[0] == L'\0' || wszScheme[0] == L'\0' || wszServerName[0] == L'\0' ||
  825. _wcsicmp(wszScheme, L"http") != 0)
  826. {
  827. LOG_ErrorMsg(E_INVALIDARG);
  828. hr = E_INVALIDARG;
  829. goto CleanUp;
  830. }
  831. if (fAppendCacheBreaker)
  832. {
  833. SYSTEMTIME stCB;
  834. WCHAR wszCacheBreaker[12];
  835. GetSystemTime(&stCB);
  836. hr = StringCchPrintfExW(wszCacheBreaker, ARRAYSIZE(wszCacheBreaker),
  837. NULL, NULL, MISTSAFE_STRING_FLAGS,
  838. L"?%02d%02d%02d%02d%02d",
  839. stCB.wYear % 100,
  840. stCB.wMonth,
  841. stCB.wDay,
  842. stCB.wHour,
  843. stCB.wMinute);
  844. if (FAILED(hr))
  845. goto CleanUp;
  846. hr = StringCchCatExW(wszObject, c_cchMaxURLSize, wszCacheBreaker,
  847. NULL, NULL, MISTSAFE_STRING_FLAGS);
  848. if (FAILED(hr))
  849. goto CleanUp;
  850. }
  851. if (fAllowProxy)
  852. dwAccessType = WINHTTP_ACCESS_TYPE_DEFAULT_PROXY;
  853. else
  854. dwAccessType = WINHTTP_ACCESS_TYPE_NO_PROXY;
  855. dwTickStart = GetTickCount();
  856. START_INTERNET:
  857. // start to deal with Internet
  858. iRetryCounter++;
  859. // If the connection has already been established re-use it.
  860. hInternet = (*sfns.pfnWinHttpOpen)(c_wszUserAgent, dwAccessType, NULL, NULL, 0);
  861. if (hInternet == NULL)
  862. {
  863. hr = HRESULT_FROM_WIN32(GetLastError());
  864. LOG_ErrorMsg(hr);
  865. goto CleanUp;
  866. }
  867. if (fAllowProxy != NULL)
  868. {
  869. GetWinHTTPProxyInfo(sfns, fCacheProxyInfo, hInternet, wszUrl,
  870. wszServerName, &AUProxyInfo);
  871. }
  872. hConnect = (*sfns.pfnWinHttpConnect)(hInternet, wszServerName, INTERNET_DEFAULT_HTTP_PORT, 0);
  873. if (hConnect == NULL)
  874. {
  875. hr = HRESULT_FROM_WIN32(GetLastError());
  876. LOG_ErrorMsg(hr);
  877. goto CleanUp;
  878. }
  879. // if we're only doing a status check, then may as well just make a GET
  880. // request
  881. iVerb = (DWORD)((fCheckStatusOnly) ? ARRAYSIZE(rgwszVerbs) - 1 : 0);
  882. for(; iVerb < ARRAYSIZE(rgwszVerbs); iVerb++)
  883. {
  884. SafeWinHTTPCloseHandle(sfns, hOpenRequest);
  885. hr = MakeRequest(sfns, hConnect, NULL, wszServerName, rgwszVerbs[iVerb],
  886. wszObject, ((fAllowProxy) ? &AUProxyInfo : NULL),
  887. rghQuitEvents, cQuitEvents, &hOpenRequest);
  888. if (FAILED(hr))
  889. goto CleanUp;
  890. dwLength = sizeof(dwStatus);
  891. if ((*sfns.pfnWinHttpQueryHeaders)(hOpenRequest,
  892. WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
  893. NULL, (LPVOID)&dwStatus, &dwLength, NULL) == FALSE)
  894. {
  895. hr = HRESULT_FROM_WIN32(GetLastError());
  896. LOG_ErrorMsg(hr);
  897. goto CleanUp;
  898. }
  899. LOG_Internet(_T("WinHttp: Request result: %d"), dwStatus);
  900. if (dwStatus == HTTP_STATUS_OK || dwStatus == HTTP_STATUS_PARTIAL_CONTENT)
  901. {
  902. break;
  903. }
  904. else
  905. {
  906. // since a server result is not a proper win32 error code, we can't
  907. // really do a HRESULT_FROM_WIN32 here. Otherwise, we'd return
  908. // a bogus code. However, we do want to pass an error HRESULT back
  909. // that contains this code.
  910. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_HTTP, dwStatus);
  911. LOG_Error(_T("WinHttp: got failed status code from server %d\n"), dwStatus);
  912. // if it's the last verb in the list, then bail...
  913. if (rgwszVerbs[iVerb] == NULL)
  914. goto CleanUp;
  915. }
  916. }
  917. // if we made it here & we're only trying to check status, then we're done
  918. if (fCheckStatusOnly)
  919. {
  920. LOG_Internet(_T("WinHttp: Only checking status. Exiting before header check and download."));
  921. hr = S_OK;
  922. goto CleanUp;
  923. }
  924. // CheckFileHeader will return S_OK if we need to download the file, S_FALSE
  925. // if we don't, and some other HRESULT if a failure occurred
  926. hr = CheckFileHeader(sfns, hOpenRequest, rghQuitEvents, cQuitEvents,
  927. wszLocalFile, &cbRemoteFile, &ft);
  928. if (FAILED(hr))
  929. goto CleanUp;
  930. // unless we have a flag that explicitly allows it, do not retry downloads
  931. // here. The reasoning is that we could be in the middle of a large
  932. // download and have it fail...
  933. if (fSkipDownloadRetry)
  934. iRetryCounter = c_cMaxRetries;
  935. if (hr == S_OK)
  936. {
  937. DWORD cbDownloaded;
  938. BOOL fCheckForHTML = fDoCabValidation;
  939. LOG_Internet(_T("WinHttp: Server file was newer. Downloading file"));
  940. // if we didn't open with a GET request above, then we gotta open a new
  941. // request. Otherwise, can reuse the request object...
  942. if (rgwszVerbs[iVerb] != NULL)
  943. SafeWinHTTPCloseHandle(sfns, hOpenRequest);
  944. // now we know we need to download this file
  945. hr = MakeRequest(sfns, hConnect, hOpenRequest, wszServerName, NULL,
  946. wszObject, ((fAllowProxy) ? &AUProxyInfo : NULL),
  947. rghQuitEvents, cQuitEvents, &hOpenRequest);
  948. if (FAILED(hr))
  949. goto CleanUp;
  950. // sometimes, we can get fancy error pages back from the site instead of
  951. // a nice nifty HTML error code, so check & see if we got back a html
  952. // file when we were expecting a cab.
  953. if (fCheckForHTML)
  954. {
  955. hr = GetContentTypeHeader(sfns, hOpenRequest, &wszContentType);
  956. if (SUCCEEDED(hr) && wszContentType != NULL)
  957. {
  958. fCheckForHTML = FALSE;
  959. if (_wcsicmp(wszContentType, L"text/html") == 0)
  960. {
  961. LOG_Internet(_T("WinHttp: Content-Type header is text/html. Bailing."));
  962. hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
  963. goto CleanUp;
  964. }
  965. else
  966. {
  967. LOG_Internet(_T("WinHttp: Content-Type header is %ls. Continuing."), wszContentType);
  968. }
  969. }
  970. hr = NOERROR;
  971. }
  972. // open the file we're gonna spew into
  973. hFile = CreateFileW(wszLocalFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
  974. FILE_ATTRIBUTE_NORMAL, NULL);
  975. if (hFile == INVALID_HANDLE_VALUE)
  976. {
  977. hr = HRESULT_FROM_WIN32(GetLastError());
  978. LOG_ErrorMsg(hr);
  979. goto CleanUp;
  980. }
  981. LOG_Internet(_T("WinHttp: downloading to FILE %ls"), wszLocalFile);
  982. // bring down the bits
  983. hr = PerformDownloadToFile(sfns.pfnWinHttpReadData, hOpenRequest,
  984. hFile, cbRemoteFile,
  985. cbDownloadBuffer,
  986. rghQuitEvents, cQuitEvents,
  987. pfnCallback, pvCallbackData, &cbDownloaded);
  988. if (FAILED(hr))
  989. {
  990. LOG_Internet(_T("WinHttp: Download failed: hr: 0x%08x"), hr);
  991. SafeCloseInvalidHandle(hFile);
  992. DeleteFileW(wszLocalFile);
  993. goto CleanUp;
  994. }
  995. LOG_Internet(_T("WinHttp: Download succeeded"));
  996. // set the file time to match the server file time since we just
  997. // downloaded it. If we don't do this the file time will be set
  998. // to the current system time.
  999. SetFileTime(hFile, &ft, NULL, NULL);
  1000. SafeCloseInvalidHandle(hFile);
  1001. if (pcbDownloaded != NULL)
  1002. *pcbDownloaded = cbRemoteFile;
  1003. // sometimes, we can get fancy error pages back from the site instead of
  1004. // a nice nifty HTML error code, so check & see if we got back a html
  1005. // file when we were expecting a cab.
  1006. if (fCheckForHTML)
  1007. {
  1008. hr = IsFileHtml(wszLocalFile);
  1009. if (SUCCEEDED(hr))
  1010. {
  1011. if (hr == S_FALSE)
  1012. {
  1013. LOG_Internet(_T("WinHttp: Download is not a html file"));
  1014. hr = S_OK;
  1015. }
  1016. else
  1017. {
  1018. LOG_Internet(_T("WinHttp: Download is a html file. Failing download."));
  1019. hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
  1020. DeleteFileW(wszLocalFile);
  1021. goto CleanUp;
  1022. }
  1023. }
  1024. else
  1025. {
  1026. LOG_Internet(_T("WinHttp: Unable to determine if download is a html file or not. Failing download."));
  1027. }
  1028. }
  1029. else if (fDoCabValidation == FALSE)
  1030. {
  1031. LOG_Internet(_T("WinHttp: Skipping cab validation."));
  1032. }
  1033. }
  1034. else
  1035. {
  1036. hr = S_OK;
  1037. LOG_Internet(_T("WinHttp: Server file is not newer. Skipping download."));
  1038. // The server ain't newer & the file is already on machine, so
  1039. // send progress callback indicating file downloadeded ok
  1040. if (pfnCallback != NULL)
  1041. {
  1042. // fpnCallback(pCallbackData, DOWNLOAD_STATUS_FILECOMPLETE, dwFileSize, dwFileSize, NULL, NULL);
  1043. pfnCallback(pvCallbackData, DOWNLOAD_STATUS_OK, cbRemoteFile, cbRemoteFile, NULL, NULL);
  1044. }
  1045. }
  1046. CleanUp:
  1047. SafeWinHTTPCloseHandle(sfns, hOpenRequest);
  1048. SafeWinHTTPCloseHandle(sfns, hConnect);
  1049. SafeWinHTTPCloseHandle(sfns, hInternet);
  1050. SafeHeapFree(wszContentType);
  1051. // free up the proxy strings- they were allocated by WinHttp
  1052. if (AUProxyInfo.ProxyInfo.lpszProxyBypass != NULL)
  1053. GlobalFree(AUProxyInfo.ProxyInfo.lpszProxyBypass);
  1054. if (AUProxyInfo.wszProxyOrig != NULL)
  1055. GlobalFree(AUProxyInfo.wszProxyOrig);
  1056. if (AUProxyInfo.rgwszProxies != NULL)
  1057. GlobalFree(AUProxyInfo.rgwszProxies);
  1058. ZeroMemory(&AUProxyInfo, sizeof(AUProxyInfo));
  1059. // if we failed, see if it's ok to continue (quit events) and whether
  1060. // we've tried enuf times yet.
  1061. if (FAILED(hr) &&
  1062. HandleEvents(rghQuitEvents, cQuitEvents) &&
  1063. iRetryCounter >= 0 && iRetryCounter < c_cMaxRetries)
  1064. {
  1065. DWORD dwElapsedTime;
  1066. dwTickEnd = GetTickCount();
  1067. if (dwTickEnd > dwTickStart)
  1068. dwElapsedTime = dwTickEnd - dwTickStart;
  1069. else
  1070. dwElapsedTime = (0xFFFFFFFF - dwTickStart) + dwTickEnd;
  1071. // We haven't hit our retry limit, so log & error and go again
  1072. if (dwElapsedTime < c_dwRetryTimeLimitInmsWinHttp)
  1073. {
  1074. LogError(hr, "Library download error. Will retry.");
  1075. // in the case where we're gonna retry, keep track of the very first
  1076. // error we encoutered cuz the ops guys say that this is the most
  1077. // useful error to know about.
  1078. if (iRetryCounter == 0)
  1079. {
  1080. LOG_Internet(_T("First download error saved: 0x%08x."), hr);
  1081. hrToReturn = hr;
  1082. }
  1083. else
  1084. {
  1085. LOG_Internet(_T("Subsequent download error: 0x%08x."), hr);
  1086. }
  1087. hr = S_OK;
  1088. goto START_INTERNET;
  1089. }
  1090. // We've completely timed out, so bail
  1091. else
  1092. {
  1093. LogError(hr, "Library download error and timed out (%d ms). Will not retry.", dwElapsedTime);
  1094. }
  1095. }
  1096. // make a callback indicating a download error
  1097. if (FAILED(hr) && pfnCallback != NULL)
  1098. pfnCallback(pvCallbackData, DOWNLOAD_STATUS_ERROR, cbRemoteFile, 0, NULL, NULL);
  1099. // if we haven't saved off an error, just use the current error. We can't
  1100. // have set hrToReturn previously if we didn't fail and want to attempt
  1101. // a retry.
  1102. // However, if we've got a success from this pass, be sure to return that
  1103. // and not a fail code.
  1104. if (FAILED(hr) && SUCCEEDED(hrToReturn))
  1105. hrToReturn = hr;
  1106. else if (SUCCEEDED(hr) && FAILED(hrToReturn))
  1107. hrToReturn = hr;
  1108. SafeHeapFree(wszServerName);
  1109. SafeHeapFree(wszObject);
  1110. return hrToReturn;
  1111. }
  1112. #endif // defined(UNICODE)
  1113. ///////////////////////////////////////////////////////////////////////////////
  1114. // exported functions
  1115. #if defined(UNICODE)
  1116. // **************************************************************************
  1117. HRESULT GetAUProxySettings(LPCWSTR wszUrl, SAUProxySettings *paups)
  1118. {
  1119. LOG_Block("GetAUProxySettings()");
  1120. URL_COMPONENTS UrlComponents;
  1121. LPWSTR wszServerName = NULL;
  1122. LPWSTR wszObject = NULL;
  1123. WCHAR wszUserName[UNLEN + 1];
  1124. WCHAR wszPasswd[UNLEN + 1];
  1125. WCHAR wszScheme[32];
  1126. SWinHTTPFunctions sfns;
  1127. ETransportUsed etu;
  1128. HMODULE hmod = NULL;
  1129. HRESULT hr = S_OK;
  1130. BOOL fRet, fLocked = FALSE;
  1131. if (wszUrl == NULL || paups == NULL)
  1132. {
  1133. hr = E_INVALIDARG;
  1134. goto done;
  1135. }
  1136. ZeroMemory(paups, sizeof(SAUProxySettings));
  1137. etu = LoadTransportDll(&sfns, &hmod, WUDF_ALLOWWINHTTPONLY);
  1138. if (etu == etuNone)
  1139. {
  1140. hr = HRESULT_FROM_WIN32(GetLastError());
  1141. LOG_ErrorMsg(hr);
  1142. goto done;
  1143. }
  1144. else if (etu != etuWinHttp)
  1145. {
  1146. hr = E_FAIL;
  1147. LOG_Internet(_T("GetAUProxySettings called when in WinInet mode."));
  1148. goto done;
  1149. }
  1150. wszServerName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, c_cchMaxURLSize * sizeof(WCHAR));
  1151. wszObject = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, c_cchMaxURLSize * sizeof(WCHAR));
  1152. if (wszServerName == NULL || wszObject == NULL)
  1153. {
  1154. LOG_ErrorMsg(E_OUTOFMEMORY);
  1155. hr = E_OUTOFMEMORY;
  1156. goto done;
  1157. }
  1158. ZeroMemory(&UrlComponents, sizeof(UrlComponents));
  1159. UrlComponents.dwStructSize = sizeof(UrlComponents);
  1160. UrlComponents.lpszHostName = wszServerName;
  1161. UrlComponents.dwHostNameLength = c_cchMaxURLSize;
  1162. UrlComponents.lpszUrlPath = wszObject;
  1163. UrlComponents.dwUrlPathLength = c_cchMaxURLSize;
  1164. UrlComponents.lpszUserName = wszUserName;
  1165. UrlComponents.dwUserNameLength = ARRAYSIZE(wszUserName);
  1166. UrlComponents.lpszPassword = wszPasswd;
  1167. UrlComponents.dwPasswordLength = ARRAYSIZE(wszPasswd);
  1168. UrlComponents.lpszScheme = wszScheme;
  1169. UrlComponents.dwSchemeLength = ARRAYSIZE(wszScheme);
  1170. if ((*sfns.pfnWinHttpCrackUrl)(wszUrl, 0, 0, &UrlComponents) == FALSE)
  1171. {
  1172. hr = HRESULT_FROM_WIN32(GetLastError());
  1173. LOG_ErrorMsg(hr);
  1174. goto done;
  1175. }
  1176. if (wszUrl[0] == L'\0' || wszScheme[0] == L'\0' || wszServerName[0] == L'\0' ||
  1177. (_wcsicmp(wszScheme, L"http") != 0 && _wcsicmp(wszScheme, L"https") != 0))
  1178. {
  1179. LOG_ErrorMsg(E_INVALIDARG);
  1180. hr = E_INVALIDARG;
  1181. goto done;
  1182. }
  1183. if (g_csCache.Lock() == FALSE)
  1184. {
  1185. hr = E_FAIL;
  1186. goto done;
  1187. }
  1188. fLocked = TRUE;
  1189. // get the proxy list
  1190. if (g_wudlProxyCache.GetLastGoodProxy(wszServerName, paups) == FALSE)
  1191. {
  1192. // proxy was not in list
  1193. if (GetLastError() == ERROR_FILE_NOT_FOUND)
  1194. {
  1195. SAUProxyInfo aupi;
  1196. HINTERNET hInternet = NULL;
  1197. LOG_Internet(_T("GetLastGoodProxy did not find a proxy object. Doing autodetect."));
  1198. hInternet = (*sfns.pfnWinHttpOpen)(c_wszUserAgent,
  1199. WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
  1200. NULL, NULL, 0);
  1201. if (hInternet == NULL)
  1202. {
  1203. hr = HRESULT_FROM_WIN32(GetLastError());
  1204. LOG_ErrorMsg(hr);
  1205. goto done;
  1206. }
  1207. fRet = GetWinHTTPProxyInfo(sfns, TRUE, hInternet, wszUrl,
  1208. wszServerName, &aupi);
  1209. (*sfns.pfnWinHttpCloseHandle)(hInternet);
  1210. if (fRet == FALSE)
  1211. {
  1212. hr = HRESULT_FROM_WIN32(GetLastError());
  1213. LOG_ErrorMsg(hr);
  1214. goto done;
  1215. }
  1216. paups->wszProxyOrig = aupi.wszProxyOrig;
  1217. paups->wszBypass = aupi.ProxyInfo.lpszProxyBypass;
  1218. paups->dwAccessType = aupi.ProxyInfo.dwAccessType;
  1219. paups->cProxies = aupi.cProxies;
  1220. paups->rgwszProxies = aupi.rgwszProxies;
  1221. paups->iProxy = (DWORD)-1;
  1222. SetLastError(ERROR_SUCCESS);
  1223. }
  1224. else
  1225. {
  1226. LOG_Internet(_T("GetLastGoodProxy failed..."));
  1227. hr = HRESULT_FROM_WIN32(GetLastError());
  1228. LOG_ErrorMsg(hr);
  1229. goto done;
  1230. }
  1231. }
  1232. else
  1233. {
  1234. if (paups->wszProxyOrig != NULL)
  1235. {
  1236. // break it up into an array
  1237. if (ProxyListToArray(paups->wszProxyOrig, &paups->rgwszProxies,
  1238. &paups->cProxies) == FALSE)
  1239. {
  1240. hr = HRESULT_FROM_WIN32(GetLastError());
  1241. LOG_ErrorMsg(hr);
  1242. goto done;
  1243. }
  1244. }
  1245. else
  1246. {
  1247. paups->iProxy = (DWORD)-1;
  1248. }
  1249. }
  1250. done:
  1251. // Unlock returns FALSE as well, but we should never get here cuz we should
  1252. // not have been able to take the lock above.
  1253. if (fLocked)
  1254. g_csCache.Unlock();
  1255. if (wszServerName != NULL)
  1256. HeapFree(GetProcessHeap(), 0, wszServerName);
  1257. if (wszObject != NULL)
  1258. HeapFree(GetProcessHeap(), 0, wszObject);
  1259. if (hmod != NULL)
  1260. UnloadTransportDll(&sfns, hmod);
  1261. return hr;
  1262. }
  1263. // **************************************************************************
  1264. HRESULT FreeAUProxySettings(SAUProxySettings *paups)
  1265. {
  1266. LOG_Block("FreeAUProxySettings()");
  1267. if (paups == NULL)
  1268. goto done;
  1269. if (paups->rgwszProxies != NULL)
  1270. GlobalFree(paups->rgwszProxies);
  1271. if (paups->wszBypass != NULL)
  1272. GlobalFree(paups->wszBypass);
  1273. if (paups->wszProxyOrig != NULL)
  1274. GlobalFree(paups->wszProxyOrig);
  1275. done:
  1276. return S_OK;
  1277. }
  1278. // **************************************************************************
  1279. HRESULT CleanupDownloadLib(void)
  1280. {
  1281. LOG_Block("CleanupDownloadLib()");
  1282. HRESULT hr = S_OK;
  1283. if (g_hmodWinHttp != NULL)
  1284. {
  1285. FreeLibrary(g_hmodWinHttp);
  1286. g_hmodWinHttp = NULL;
  1287. }
  1288. if (g_hmodWinInet != NULL)
  1289. {
  1290. FreeLibrary(g_hmodWinInet);
  1291. g_hmodWinInet = NULL;
  1292. }
  1293. if (g_csCache.Lock() == FALSE)
  1294. return E_FAIL;
  1295. __try { g_wudlProxyCache.Empty(); }
  1296. __except(EXCEPTION_EXECUTE_HANDLER) { hr = E_FAIL; }
  1297. // this returns FALSE as well, but we should never get here cuz we should
  1298. // not have been able to take the lock above.
  1299. g_csCache.Unlock();
  1300. return hr;
  1301. }
  1302. // **************************************************************************
  1303. HRESULT DownloadFile(
  1304. LPCWSTR wszServerUrl, // full http url
  1305. LPCWSTR wszLocalPath, // local directory to download file to
  1306. LPCWSTR wszLocalFileName, // optional local file name to rename the downloaded file to if pszLocalPath does not contain file name
  1307. PDWORD pdwDownloadedBytes, // bytes downloaded for this file
  1308. HANDLE *hQuitEvents, // optional events causing this function to abort
  1309. UINT nQuitEventCount, // number of quit events, must be 0 if array is NULL
  1310. PFNDownloadCallback fpnCallback, // optional call back function
  1311. VOID* pCallbackData, // parameter for call back function to use
  1312. DWORD dwFlags
  1313. )
  1314. {
  1315. LOG_Block("DownloadFile()");
  1316. SWinHTTPFunctions sfns;
  1317. ETransportUsed etu;
  1318. HMODULE hmod = NULL;
  1319. HRESULT hr = S_OK;
  1320. LPWSTR wszLocalFile = NULL;
  1321. DWORD dwFlagsToUse;
  1322. // for full download, disable cache breaker.
  1323. dwFlagsToUse = dwFlags & ~WUDF_APPENDCACHEBREAKER;
  1324. ZeroMemory(&sfns, sizeof(sfns));
  1325. if (wszServerUrl == NULL || wszLocalPath == NULL)
  1326. {
  1327. LOG_ErrorMsg(ERROR_INVALID_PARAMETER);
  1328. hr = E_INVALIDARG;
  1329. goto done;
  1330. }
  1331. etu = LoadTransportDll(&sfns, &hmod, dwFlagsToUse);
  1332. if (etu == etuNone)
  1333. {
  1334. hr = HRESULT_FROM_WIN32(GetLastError());
  1335. LOG_ErrorMsg(hr);
  1336. goto done;
  1337. }
  1338. else if (etu != etuWinHttp && etu != etuWinInet)
  1339. {
  1340. hr = E_FAIL;
  1341. LogError(hr, "Unexpected answer from LoadTransportDll(): %d", etu);
  1342. goto done;
  1343. }
  1344. // Since StartDownload just takes a full path to the file to download, build
  1345. // it here...
  1346. // Note that we don't need to do this if we're just in status
  1347. // checking mode)
  1348. if ((dwFlags & WUDF_CHECKREQSTATUSONLY) == 0)
  1349. {
  1350. wszLocalFile = MakeFullLocalFilePath(wszServerUrl, wszLocalFileName,
  1351. wszLocalPath);
  1352. if (wszLocalFile == NULL)
  1353. {
  1354. hr = HRESULT_FROM_WIN32(GetLastError());
  1355. LOG_ErrorMsg(hr);
  1356. goto done;
  1357. }
  1358. }
  1359. if (etu == etuWinHttp)
  1360. {
  1361. hr = StartWinHttpDownload(sfns, wszServerUrl, wszLocalFile,
  1362. pdwDownloadedBytes, hQuitEvents, nQuitEventCount,
  1363. fpnCallback, pCallbackData, dwFlagsToUse,
  1364. c_cbDownloadBuffer);
  1365. }
  1366. else
  1367. {
  1368. hr = StartWinInetDownload(hmod, wszServerUrl, wszLocalFile,
  1369. pdwDownloadedBytes, hQuitEvents, nQuitEventCount,
  1370. fpnCallback, pCallbackData, dwFlagsToUse,
  1371. c_cbDownloadBuffer);
  1372. }
  1373. done:
  1374. if (hmod != NULL)
  1375. UnloadTransportDll(&sfns, hmod);
  1376. SafeHeapFree(wszLocalFile);
  1377. return hr;
  1378. }
  1379. // **************************************************************************
  1380. HRESULT DownloadFileLite(LPCWSTR wszDownloadUrl,
  1381. LPCWSTR wszLocalFile,
  1382. HANDLE hQuitEvent,
  1383. DWORD dwFlags)
  1384. {
  1385. LOG_Block("DownloadFileLite()");
  1386. SWinHTTPFunctions sfns;
  1387. ETransportUsed etu;
  1388. HMODULE hmod = NULL;
  1389. HRESULT hr = S_OK;
  1390. DWORD dwFlagsToUse;
  1391. // for lite download, force cache breaker & download retry
  1392. dwFlagsToUse = dwFlags | WUDF_APPENDCACHEBREAKER | WUDF_DODOWNLOADRETRY;
  1393. ZeroMemory(&sfns, sizeof(sfns));
  1394. etu = LoadTransportDll(&sfns, &hmod, dwFlagsToUse);
  1395. switch (etu)
  1396. {
  1397. case etuNone:
  1398. LOG_ErrorMsg(GetLastError());
  1399. hr = HRESULT_FROM_WIN32(GetLastError());
  1400. goto done;
  1401. case etuWinHttp:
  1402. hr = StartWinHttpDownload(sfns, wszDownloadUrl, wszLocalFile,
  1403. NULL,
  1404. ((hQuitEvent != NULL) ? &hQuitEvent : NULL),
  1405. ((hQuitEvent != NULL) ? 1 : 0),
  1406. NULL, NULL, dwFlagsToUse,
  1407. c_cbDownloadBuffer);
  1408. break;
  1409. case etuWinInet:
  1410. hr = StartWinInetDownload(hmod, wszDownloadUrl, wszLocalFile,
  1411. NULL,
  1412. ((hQuitEvent != NULL) ? &hQuitEvent : NULL),
  1413. ((hQuitEvent != NULL) ? 1 : 0),
  1414. NULL, NULL, dwFlagsToUse,
  1415. c_cbDownloadBuffer);
  1416. break;
  1417. default:
  1418. LogError(hr, "Unexpected answer from LoadTransportDll(): %d", etu);
  1419. hr = HRESULT_FROM_WIN32(GetLastError());
  1420. goto done;
  1421. }
  1422. done:
  1423. if (hmod != NULL)
  1424. UnloadTransportDll(&sfns, hmod);
  1425. return hr;
  1426. }
  1427. #else // !defined(UNICODE)
  1428. // **************************************************************************
  1429. HRESULT GetAUProxySettings(LPCWSTR wszUrl, SAUProxySettings *paups)
  1430. {
  1431. return E_NOTIMPL;
  1432. }
  1433. // **************************************************************************
  1434. HRESULT FreeAUProxySettings(SAUProxySettings *paups)
  1435. {
  1436. return E_NOTIMPL;
  1437. }
  1438. // **************************************************************************
  1439. HRESULT CleanupDownloadLib(void)
  1440. {
  1441. if (g_hmodWinInet != NULL)
  1442. {
  1443. FreeLibrary(g_hmodWinInet);
  1444. g_hmodWinInet = NULL;
  1445. }
  1446. return NOERROR;
  1447. }
  1448. // **************************************************************************
  1449. HRESULT DownloadFile(
  1450. LPCSTR pszServerUrl, // full http url
  1451. LPCSTR pszLocalPath, // local directory to download file to
  1452. LPCSTR pszLocalFileName, // optional local file name to rename the downloaded file to if pszLocalPath does not contain file name
  1453. PDWORD pdwDownloadedBytes, // bytes downloaded for this file
  1454. HANDLE *hQuitEvents, // optional events causing this function to abort
  1455. UINT nQuitEventCount, // number of quit events, must be 0 if array is NULL
  1456. PFNDownloadCallback fpnCallback, // optional call back function
  1457. VOID* pCallbackData, // parameter for call back function to use
  1458. DWORD dwFlags
  1459. )
  1460. {
  1461. LOG_Block("DownloadFile()");
  1462. SWinHTTPFunctions sfns;
  1463. ETransportUsed etu;
  1464. HMODULE hmod = NULL;
  1465. HRESULT hr = S_OK;
  1466. LPSTR pszLocalFile = NULL;
  1467. DWORD dwFlagsToUse;
  1468. // for ansi, force wininet & disable any request to force winhttp
  1469. // for full download, disable cache breaker.
  1470. dwFlagsToUse = dwFlags | WUDF_ALLOWWININETONLY;
  1471. dwFlagsToUse &= ~(WUDF_ALLOWWINHTTPONLY | WUDF_APPENDCACHEBREAKER);
  1472. ZeroMemory(&sfns, sizeof(sfns));
  1473. etu = LoadTransportDll(&sfns, &hmod, dwFlagsToUse);
  1474. if (etu == etuNone)
  1475. {
  1476. LOG_ErrorMsg(GetLastError());
  1477. hr = HRESULT_FROM_WIN32(GetLastError());
  1478. goto done;
  1479. }
  1480. else if (etu != etuWinInet)
  1481. {
  1482. hr = E_FAIL;
  1483. LogError(hr, "Unexpected answer from LoadTransportDll(): %d", etu);
  1484. goto done;
  1485. }
  1486. // Since StartDownload just takes a full path to the file to download, build
  1487. // it here...
  1488. // Note that we don't need to do this if we're just in status
  1489. // checking mode)
  1490. if ((dwFlags & WUDF_CHECKREQSTATUSONLY) == 0)
  1491. {
  1492. pszLocalFile = MakeFullLocalFilePath(pszServerUrl, pszLocalFileName,
  1493. pszLocalPath);
  1494. if (pszLocalFile == NULL)
  1495. {
  1496. LOG_ErrorMsg(GetLastError());
  1497. hr = HRESULT_FROM_WIN32(GetLastError());
  1498. goto done;
  1499. }
  1500. }
  1501. hr = StartWinInetDownload(hmod, pszServerUrl, pszLocalFile,
  1502. pdwDownloadedBytes, hQuitEvents, nQuitEventCount,
  1503. fpnCallback, pCallbackData, dwFlagsToUse,
  1504. c_cbDownloadBuffer);
  1505. done:
  1506. if (hmod != NULL)
  1507. UnloadTransportDll(&sfns, hmod);
  1508. SafeHeapFree(pszLocalFile);
  1509. return hr;
  1510. }
  1511. // **************************************************************************
  1512. HRESULT DownloadFileLite(LPCSTR pszDownloadUrl,
  1513. LPCSTR pszLocalFile,
  1514. HANDLE hQuitEvent,
  1515. DWORD dwFlags)
  1516. {
  1517. LOG_Block("DownloadFileLite()");
  1518. SWinHTTPFunctions sfns;
  1519. ETransportUsed etu;
  1520. HMODULE hmod = NULL;
  1521. HRESULT hr = S_OK;
  1522. DWORD dwFlagsToUse;
  1523. // for ansi, force wininet & disable any request to force winhttp
  1524. // for lite download, force cache breaker & download retry
  1525. dwFlagsToUse = dwFlags | WUDF_APPENDCACHEBREAKER | WUDF_ALLOWWININETONLY |
  1526. WUDF_DODOWNLOADRETRY;
  1527. dwFlagsToUse &= ~WUDF_ALLOWWINHTTPONLY;
  1528. ZeroMemory(&sfns, sizeof(sfns));
  1529. etu = LoadTransportDll(&sfns, &hmod, dwFlagsToUse);
  1530. switch (etu)
  1531. {
  1532. case etuNone:
  1533. LOG_ErrorMsg(GetLastError());
  1534. hr = HRESULT_FROM_WIN32(GetLastError());
  1535. goto done;
  1536. case etuWinInet:
  1537. hr = StartWinInetDownload(hmod, pszDownloadUrl, pszLocalFile,
  1538. NULL,
  1539. ((hQuitEvent != NULL) ? &hQuitEvent : NULL),
  1540. ((hQuitEvent != NULL) ? 1 : 0),
  1541. NULL, NULL, dwFlagsToUse,
  1542. c_cbDownloadBuffer);
  1543. break;
  1544. default:
  1545. case etuWinHttp:
  1546. LogError(hr, "Unexpected answer from LoadTransportDll(): %d", etu);
  1547. hr = HRESULT_FROM_WIN32(GetLastError());
  1548. goto done;
  1549. }
  1550. done:
  1551. if (hmod != NULL)
  1552. UnloadTransportDll(&sfns, hmod);
  1553. return hr;
  1554. }
  1555. #endif // defined(UNICODE)