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.

673 lines
23 KiB

  1. #include <windows.h>
  2. #include <wininet.h>
  3. #include <shlwapi.h>
  4. #include <logging.h>
  5. #include "iucommon.h"
  6. #include "download.h"
  7. #include "dlutil.h"
  8. #include "trust.h"
  9. #include "fileutil.h"
  10. #include "malloc.h"
  11. extern "C"
  12. {
  13. // wininet
  14. typedef BOOL (STDAPICALLTYPE *pfn_InternetCrackUrl)(LPCTSTR, DWORD, DWORD, LPURL_COMPONENTS);
  15. typedef HINTERNET (STDAPICALLTYPE *pfn_InternetOpen)(LPCTSTR, DWORD, LPCTSTR, LPCTSTR, DWORD);
  16. typedef HINTERNET (STDAPICALLTYPE *pfn_InternetConnect)(HINTERNET, LPCTSTR, INTERNET_PORT, LPCTSTR, LPCTSTR, DWORD, DWORD, DWORD_PTR);
  17. typedef HINTERNET (STDAPICALLTYPE *pfn_HttpOpenRequest)(HINTERNET, LPCTSTR, LPCTSTR, LPCTSTR, LPCTSTR, LPCTSTR FAR *, DWORD, DWORD_PTR);
  18. typedef BOOL (STDAPICALLTYPE *pfn_HttpSendRequest)(HINTERNET, LPCTSTR, DWORD, LPVOID, DWORD);
  19. typedef BOOL (STDAPICALLTYPE *pfn_HttpQueryInfo)(HINTERNET, DWORD, LPVOID, LPDWORD, LPDWORD);
  20. typedef BOOL (STDAPICALLTYPE *pfn_InternetReadFile)(HINTERNET, LPVOID, DWORD, LPDWORD);
  21. typedef BOOL (STDAPICALLTYPE *pfn_InternetCloseHandle)(HINTERNET);
  22. };
  23. struct SWinInetFunctions
  24. {
  25. // wininet function pointers
  26. pfn_InternetCrackUrl pfnInternetCrackUrl;
  27. pfn_InternetOpen pfnInternetOpen;
  28. pfn_InternetConnect pfnInternetConnect;
  29. pfn_HttpOpenRequest pfnHttpOpenRequest;
  30. pfn_HttpSendRequest pfnHttpSendRequest;
  31. pfn_HttpQueryInfo pfnHttpQueryInfo;
  32. pfn_InternetReadFile pfnInternetReadFile;
  33. pfn_InternetCloseHandle pfnInternetCloseHandle;
  34. HMODULE hmod;
  35. };
  36. #define SafeInternetCloseHandle(sfns, x) if (NULL != x) { (*sfns.pfnInternetCloseHandle)(x); x = NULL; }
  37. // **************************************************************************
  38. BOOL LoadWinInetFunctions(HMODULE hmod, SWinInetFunctions *psfns)
  39. {
  40. LOG_Block("LoadWinInetFunctions()");
  41. BOOL fRet = FALSE;
  42. psfns->hmod = hmod;
  43. #if defined(UNICODE)
  44. psfns->pfnInternetCrackUrl = (pfn_InternetCrackUrl)GetProcAddress(hmod, "InternetCrackUrlW");
  45. psfns->pfnInternetOpen = (pfn_InternetOpen)GetProcAddress(hmod, "InternetOpenW");
  46. psfns->pfnInternetConnect = (pfn_InternetConnect)GetProcAddress(hmod, "InternetConnectW");
  47. psfns->pfnHttpOpenRequest = (pfn_HttpOpenRequest)GetProcAddress(hmod, "HttpOpenRequestW");
  48. psfns->pfnHttpSendRequest = (pfn_HttpSendRequest)GetProcAddress(hmod, "HttpSendRequestW");
  49. psfns->pfnHttpQueryInfo = (pfn_HttpQueryInfo)GetProcAddress(hmod, "HttpQueryInfoW");
  50. psfns->pfnInternetReadFile = (pfn_InternetReadFile)GetProcAddress(hmod, "InternetReadFile");
  51. psfns->pfnInternetCloseHandle = (pfn_InternetCloseHandle)GetProcAddress(hmod, "InternetCloseHandle");
  52. #else
  53. psfns->pfnInternetCrackUrl = (pfn_InternetCrackUrl)GetProcAddress(hmod, "InternetCrackUrlA");
  54. psfns->pfnInternetOpen = (pfn_InternetOpen)GetProcAddress(hmod, "InternetOpenA");
  55. psfns->pfnInternetConnect = (pfn_InternetConnect)GetProcAddress(hmod, "InternetConnectA");
  56. psfns->pfnHttpOpenRequest = (pfn_HttpOpenRequest)GetProcAddress(hmod, "HttpOpenRequestA");
  57. psfns->pfnHttpSendRequest = (pfn_HttpSendRequest)GetProcAddress(hmod, "HttpSendRequestA");
  58. psfns->pfnHttpQueryInfo = (pfn_HttpQueryInfo)GetProcAddress(hmod, "HttpQueryInfoA");
  59. psfns->pfnInternetReadFile = (pfn_InternetReadFile)GetProcAddress(hmod, "InternetReadFile");
  60. psfns->pfnInternetCloseHandle = (pfn_InternetCloseHandle)GetProcAddress(hmod, "InternetCloseHandle");
  61. #endif
  62. if (psfns->pfnInternetCrackUrl == NULL ||
  63. psfns->pfnInternetOpen == NULL ||
  64. psfns->pfnInternetConnect == NULL ||
  65. psfns->pfnHttpOpenRequest == NULL ||
  66. psfns->pfnHttpSendRequest == NULL ||
  67. psfns->pfnHttpQueryInfo == NULL ||
  68. psfns->pfnInternetReadFile == NULL ||
  69. psfns->pfnInternetCloseHandle == NULL)
  70. {
  71. // don't free the library here. It should be freed
  72. SetLastError(ERROR_PROC_NOT_FOUND);
  73. ZeroMemory(psfns, sizeof(SWinInetFunctions));
  74. goto done;
  75. }
  76. LOG_Internet(_T("Successfully loaded WinInet functions"));
  77. fRet = TRUE;
  78. done:
  79. return fRet;
  80. }
  81. // **************************************************************************
  82. static
  83. HRESULT MakeRequest(SWinInetFunctions &sfns,
  84. HINTERNET hConnect,
  85. HINTERNET hRequest,
  86. LPCTSTR szVerb,
  87. LPCTSTR szObject,
  88. HANDLE *rghEvents,
  89. DWORD cEvents,
  90. HINTERNET *phRequest)
  91. {
  92. LOG_Block("MakeRequest()");
  93. HINTERNET hOpenRequest = NULL;
  94. LPCTSTR szAcceptTypes[] = { _T("*/*"), NULL };
  95. HRESULT hr = S_OK;
  96. LOG_Internet(_T("WinInet: Making %s request for %s"), szVerb, szObject);
  97. if (hRequest == NULL)
  98. {
  99. // Open a HEAD request to ask for information about this file
  100. hOpenRequest = (*sfns.pfnHttpOpenRequest)(hConnect, szVerb, szObject, NULL, NULL,
  101. szAcceptTypes, INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_UI, 0);
  102. if (!hOpenRequest)
  103. {
  104. hr = HRESULT_FROM_WIN32(GetLastError());
  105. LOG_ErrorMsg(hr);
  106. goto CleanUp;
  107. }
  108. }
  109. else
  110. {
  111. hOpenRequest = hRequest;
  112. }
  113. if (!HandleEvents(rghEvents, cEvents))
  114. {
  115. hr = E_ABORT;
  116. goto CleanUp;
  117. }
  118. if (! (*sfns.pfnHttpSendRequest)(hOpenRequest, NULL, 0, NULL, 0) )
  119. {
  120. hr = HRESULT_FROM_WIN32(GetLastError());
  121. LOG_ErrorMsg(hr);
  122. goto CleanUp;
  123. }
  124. if (HandleEvents(rghEvents, cEvents) == FALSE)
  125. {
  126. hr = E_ABORT;
  127. goto CleanUp;
  128. }
  129. *phRequest = hOpenRequest;
  130. hOpenRequest = NULL;
  131. CleanUp:
  132. // don't want to free handle if we didn't open it.
  133. if (hRequest != hOpenRequest)
  134. SafeInternetCloseHandle(sfns, hOpenRequest);
  135. return hr;
  136. }
  137. // **************************************************************************
  138. static
  139. HRESULT GetContentTypeHeader(SWinInetFunctions &sfns,
  140. HINTERNET hOpenRequest,
  141. LPTSTR *pszContentType)
  142. {
  143. LOG_Block("GetContentTypeHeader()");
  144. HRESULT hr = S_OK;
  145. LPTSTR szContentType = NULL;
  146. DWORD dwLength, dwErr;
  147. BOOL fRet;
  148. *pszContentType = NULL;
  149. dwLength = 0;
  150. fRet = (*sfns.pfnHttpQueryInfo)(hOpenRequest, HTTP_QUERY_CONTENT_TYPE,
  151. (LPVOID)NULL, &dwLength, NULL);
  152. if (fRet == FALSE && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
  153. {
  154. hr = HRESULT_FROM_WIN32(GetLastError());
  155. LOG_ErrorMsg(hr);
  156. goto done;
  157. }
  158. if (dwLength == 0)
  159. {
  160. hr = HRESULT_FROM_WIN32(ERROR_HTTP_HEADER_NOT_FOUND);
  161. goto done;
  162. }
  163. szContentType = (LPTSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength);
  164. if (szContentType == NULL)
  165. {
  166. hr = E_INVALIDARG;
  167. LOG_ErrorMsg(hr);
  168. goto done;
  169. }
  170. if ((*sfns.pfnHttpQueryInfo)(hOpenRequest, HTTP_QUERY_CONTENT_TYPE,
  171. (LPVOID)szContentType, &dwLength,
  172. NULL) == FALSE)
  173. {
  174. hr = HRESULT_FROM_WIN32(GetLastError());
  175. LOG_ErrorMsg(hr);
  176. goto done;
  177. }
  178. *pszContentType = szContentType;
  179. szContentType = NULL;
  180. done:
  181. SafeHeapFree(szContentType);
  182. return hr;
  183. }
  184. // **************************************************************************
  185. HRESULT StartWinInetDownload(HMODULE hmodWinInet,
  186. LPCTSTR pszServerUrl,
  187. LPCTSTR pszLocalFile,
  188. DWORD *pdwDownloadedBytes,
  189. HANDLE *rghQuitEvents,
  190. UINT cQuitEvents,
  191. PFNDownloadCallback pfnCallback,
  192. LPVOID pvCallbackData,
  193. DWORD dwFlags,
  194. DWORD cbDownloadBuffer)
  195. {
  196. LOG_Block("StartWinInetDownload()");
  197. URL_COMPONENTS UrlComponents;
  198. SWinInetFunctions sfns;
  199. HINTERNET hInternet = NULL;
  200. HINTERNET hConnect = NULL;
  201. HINTERNET hOpenRequest = NULL;
  202. DWORD dwStatus, dwAccessType;
  203. LPTSTR pszServerName = NULL;
  204. LPTSTR pszObject = NULL;
  205. LPTSTR pszContentType = NULL;
  206. TCHAR szUserName[UNLEN + 1];
  207. TCHAR szPasswd[UNLEN + 1];
  208. TCHAR szScheme[32];
  209. // NULL (equivalent to "GET") MUST be the last verb in the list
  210. LPCTSTR rgszVerbs[] = { _T("HEAD"), NULL };
  211. DWORD iVerb;
  212. HRESULT hr = S_OK, hrToReturn = S_OK;
  213. BOOL fRet = TRUE;
  214. SYSTEMTIME st;
  215. FILETIME ft;
  216. HANDLE hFile = INVALID_HANDLE_VALUE;
  217. DWORD cbRemoteFile = 0;
  218. DWORD dwLength;
  219. DWORD dwTickStart = 0, dwTickEnd = 0;
  220. int iRetryCounter = -1; // non-negative during download mode
  221. BOOL fAllowProxy = ((dwFlags & WUDF_DONTALLOWPROXY) == 0);
  222. BOOL fCheckStatusOnly = ((dwFlags & WUDF_CHECKREQSTATUSONLY) != 0);
  223. BOOL fAppendCacheBreaker = ((dwFlags & WUDF_APPENDCACHEBREAKER) != 0);
  224. BOOL fSkipDownloadRetry = ((dwFlags & WUDF_DODOWNLOADRETRY) == 0);
  225. BOOL fDoCabValidation = ((dwFlags & WUDF_SKIPCABVALIDATION) == 0);
  226. ZeroMemory(&sfns, sizeof(sfns));
  227. if ((pszServerUrl == NULL) ||
  228. (pszLocalFile == NULL && fCheckStatusOnly == FALSE))
  229. {
  230. LOG_ErrorMsg(E_INVALIDARG);
  231. return E_INVALIDARG;
  232. }
  233. if (NULL != pdwDownloadedBytes)
  234. *pdwDownloadedBytes = 0;
  235. if (LoadWinInetFunctions(hmodWinInet, &sfns) == FALSE)
  236. {
  237. hr = HRESULT_FROM_WIN32(GetLastError());
  238. LOG_ErrorMsg(hr);
  239. return hr;
  240. }
  241. pszServerName = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, c_cchMaxURLSize * sizeof(TCHAR));
  242. pszObject = (LPTSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, c_cchMaxURLSize * sizeof(TCHAR));
  243. if ((pszServerName == NULL) || (pszObject == NULL))
  244. {
  245. hr = E_OUTOFMEMORY;
  246. LOG_ErrorMsg(hr);
  247. goto CleanUp;
  248. }
  249. pszServerName[0] = L'\0';
  250. pszObject[0] = L'\0';
  251. szUserName[0] = L'\0';
  252. szPasswd[0] = L'\0';
  253. if (HandleEvents(rghQuitEvents, cQuitEvents) == FALSE)
  254. {
  255. hr = E_ABORT;
  256. goto CleanUp;
  257. }
  258. // Break down the URL into its various components for the InternetAPI calls.
  259. // Specifically we need the server name, object to download, username and
  260. // password information.
  261. ZeroMemory(&UrlComponents, sizeof(UrlComponents));
  262. UrlComponents.dwStructSize = sizeof(UrlComponents);
  263. UrlComponents.lpszHostName = pszServerName;
  264. UrlComponents.dwHostNameLength = c_cchMaxURLSize;
  265. UrlComponents.lpszUrlPath = pszObject;
  266. UrlComponents.dwUrlPathLength = c_cchMaxURLSize;
  267. UrlComponents.lpszUserName = szUserName;
  268. UrlComponents.dwUserNameLength = ARRAYSIZE(szUserName);
  269. UrlComponents.lpszPassword = szPasswd;
  270. UrlComponents.dwPasswordLength = ARRAYSIZE(szPasswd);
  271. UrlComponents.lpszScheme = szScheme;
  272. UrlComponents.dwSchemeLength = ARRAYSIZE(szScheme);
  273. LOG_Internet(_T("WinInet: Downloading URL %s to FILE %s"), pszServerUrl, pszLocalFile);
  274. if ((*sfns.pfnInternetCrackUrl)(pszServerUrl, 0, 0, &UrlComponents) == FALSE)
  275. {
  276. hr = HRESULT_FROM_WIN32(GetLastError());
  277. LOG_ErrorMsg(hr);
  278. goto CleanUp;
  279. }
  280. if (pszServerUrl[0] == L'\0' || szScheme[0] == L'\0' || pszServerName[0] == L'\0' ||
  281. _tcsicmp(szScheme, _T("http")) != 0)
  282. {
  283. LOG_ErrorMsg(E_INVALIDARG);
  284. hr = E_INVALIDARG;
  285. goto CleanUp;
  286. }
  287. if (fAppendCacheBreaker)
  288. {
  289. SYSTEMTIME stCB;
  290. TCHAR szCacheBreaker[12];
  291. GetSystemTime(&stCB);
  292. hr = StringCchPrintfEx(szCacheBreaker, ARRAYSIZE(szCacheBreaker),
  293. NULL, NULL, MISTSAFE_STRING_FLAGS,
  294. _T("?%02d%02d%02d%02d%02d"),
  295. stCB.wYear % 100,
  296. stCB.wMonth,
  297. stCB.wDay,
  298. stCB.wHour,
  299. stCB.wMinute);
  300. if (FAILED(hr))
  301. goto CleanUp;
  302. hr = StringCchCatEx(pszObject, c_cchMaxURLSize, szCacheBreaker,
  303. NULL, NULL, MISTSAFE_STRING_FLAGS);
  304. if (FAILED(hr))
  305. goto CleanUp;
  306. }
  307. if (fAllowProxy)
  308. dwAccessType = INTERNET_OPEN_TYPE_PRECONFIG;
  309. else
  310. dwAccessType = INTERNET_OPEN_TYPE_DIRECT;
  311. dwTickStart = GetTickCount();
  312. START_INTERNET:
  313. // start to deal with Internet
  314. iRetryCounter++;
  315. // If the connection has already been established re-use it.
  316. hInternet = (*sfns.pfnInternetOpen)(c_tszUserAgent, dwAccessType, NULL, NULL, 0);
  317. if (hInternet == NULL)
  318. {
  319. hr = HRESULT_FROM_WIN32(GetLastError());
  320. LOG_ErrorMsg(hr);
  321. goto CleanUp;
  322. }
  323. hConnect = (*sfns.pfnInternetConnect)(hInternet, pszServerName, INTERNET_DEFAULT_HTTP_PORT,
  324. szUserName, szPasswd,
  325. INTERNET_SERVICE_HTTP,
  326. INTERNET_FLAG_NO_UI | INTERNET_FLAG_RELOAD | INTERNET_FLAG_KEEP_CONNECTION,
  327. 0);
  328. if (hConnect == NULL)
  329. {
  330. hr = HRESULT_FROM_WIN32(GetLastError());
  331. LOG_ErrorMsg(hr);
  332. goto CleanUp;
  333. }
  334. iVerb = (DWORD)((fCheckStatusOnly) ? ARRAYSIZE(rgszVerbs) - 1 : 0);
  335. for(; iVerb < ARRAYSIZE(rgszVerbs); iVerb++)
  336. {
  337. SafeInternetCloseHandle(sfns, hOpenRequest);
  338. hr = MakeRequest(sfns, hConnect, NULL, rgszVerbs[iVerb], pszObject, rghQuitEvents, cQuitEvents,
  339. &hOpenRequest);
  340. if (FAILED(hr))
  341. goto CleanUp;
  342. dwLength = sizeof(dwStatus);
  343. if ((*sfns.pfnHttpQueryInfo)(hOpenRequest, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
  344. (LPVOID)&dwStatus, &dwLength, NULL) == FALSE)
  345. {
  346. hr = HRESULT_FROM_WIN32(GetLastError());
  347. LOG_ErrorMsg(hr);
  348. goto CleanUp;
  349. }
  350. LOG_Internet(_T("WinInet: Request result: %d"), dwStatus);
  351. if (dwStatus == HTTP_STATUS_OK || dwStatus == HTTP_STATUS_PARTIAL_CONTENT)
  352. {
  353. break;
  354. }
  355. else
  356. {
  357. // since a server result is not a proper win32 error code, we can't
  358. // really do a HRESULT_FROM_WIN32 here. Otherwise, we'd return
  359. // a bogus code. However, we do want to pass an error HRESULT back
  360. // that contains this code.
  361. hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_HTTP, dwStatus);
  362. LOG_Error(_T("WinInet: got failed status code from server %d\n"), dwStatus);
  363. // if it's the last verb in the list, then bail...
  364. if (rgszVerbs[iVerb] == NULL)
  365. goto CleanUp;
  366. }
  367. }
  368. // if we made it here & we're only trying to check status, then we're done
  369. if (fCheckStatusOnly)
  370. {
  371. LOG_Internet(_T("WinInet: Only checking status. Exiting before header check and download."));
  372. hr = S_OK;
  373. goto CleanUp;
  374. }
  375. dwLength = sizeof(st);
  376. if ((*sfns.pfnHttpQueryInfo)(hOpenRequest, HTTP_QUERY_LAST_MODIFIED | HTTP_QUERY_FLAG_SYSTEMTIME,
  377. (LPVOID)&st, &dwLength, NULL) == FALSE)
  378. {
  379. hr = HRESULT_FROM_WIN32(GetLastError());
  380. LOG_ErrorMsg(hr);
  381. goto CleanUp;
  382. }
  383. SystemTimeToFileTime(&st, &ft);
  384. // Now Get the FileSize information from the Server
  385. dwLength = sizeof(cbRemoteFile);
  386. if ((*sfns.pfnHttpQueryInfo)(hOpenRequest, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER,
  387. (LPVOID)&cbRemoteFile, &dwLength, NULL) == FALSE)
  388. {
  389. hr = HRESULT_FROM_WIN32(GetLastError());
  390. LOG_ErrorMsg(hr);
  391. goto CleanUp;
  392. }
  393. if (HandleEvents(rghQuitEvents, cQuitEvents) == FALSE)
  394. {
  395. hr = E_ABORT;
  396. goto CleanUp;
  397. }
  398. // unless we have a flag that explicitly allows it, do not retry downloads
  399. // here. The reasoning is that we could be in the middle of a large
  400. // download and have it fail...
  401. if (fSkipDownloadRetry)
  402. iRetryCounter = c_cMaxRetries;
  403. if (IsServerFileDifferent(ft, cbRemoteFile, pszLocalFile))
  404. {
  405. DWORD cbDownloaded;
  406. BOOL fCheckForHTML = fDoCabValidation;
  407. LOG_Internet(_T("WinInet: Server file was newer. Downloading file"));
  408. // if we didn't open with a GET request above, then we gotta open a new
  409. // request. Otherwise, can reuse the request object...
  410. if (rgszVerbs[iVerb] != NULL)
  411. SafeInternetCloseHandle(sfns, hOpenRequest);
  412. hr = MakeRequest(sfns, hConnect, hOpenRequest, NULL, pszObject,
  413. rghQuitEvents, cQuitEvents, &hOpenRequest);
  414. if (FAILED(hr))
  415. goto CleanUp;
  416. // sometimes, we can get fancy error pages back from the site instead of
  417. // a nice nifty HTML error code, so check & see if we got back a html
  418. // file when we were expecting a cab.
  419. if (fCheckForHTML)
  420. {
  421. hr = GetContentTypeHeader(sfns, hOpenRequest, &pszContentType);
  422. if (SUCCEEDED(hr) && pszContentType != NULL)
  423. {
  424. fCheckForHTML = FALSE;
  425. if (_tcsicmp(pszContentType, _T("text/html")) == 0)
  426. {
  427. LOG_Internet(_T("WinInet: Content-Type header is text/html. Bailing."));
  428. hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
  429. goto CleanUp;
  430. }
  431. else
  432. {
  433. LOG_Internet(_T("WinInet: Content-Type header is %s. Continuing."), pszContentType);
  434. }
  435. }
  436. hr = NOERROR;
  437. }
  438. // open the file we're gonna spew into
  439. hFile = CreateFile(pszLocalFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
  440. FILE_ATTRIBUTE_NORMAL, NULL);
  441. if (hFile == INVALID_HANDLE_VALUE)
  442. {
  443. hr = HRESULT_FROM_WIN32(GetLastError());
  444. LOG_ErrorMsg(hr);
  445. goto CleanUp;
  446. }
  447. LOG_Internet(_T("WinInet: downloading to FILE %s"), pszLocalFile);
  448. // bring down the bits
  449. hr = PerformDownloadToFile(sfns.pfnInternetReadFile, hOpenRequest,
  450. hFile, cbRemoteFile,
  451. cbDownloadBuffer,
  452. rghQuitEvents, cQuitEvents,
  453. pfnCallback, pvCallbackData, &cbDownloaded);
  454. if (FAILED(hr))
  455. {
  456. LOG_Internet(_T("WinInet: Download failed: hr: 0x%08x"), hr);
  457. SafeCloseInvalidHandle(hFile);
  458. DeleteFile(pszLocalFile);
  459. goto CleanUp;
  460. }
  461. LOG_Internet(_T("WinInet: Download succeeded"));
  462. // set the file time to match the server file time since we just
  463. // downloaded it. If we don't do this the file time will be set
  464. // to the current system time.
  465. SetFileTime(hFile, &ft, NULL, NULL);
  466. SafeCloseInvalidHandle(hFile);
  467. if (pdwDownloadedBytes != NULL)
  468. *pdwDownloadedBytes = cbRemoteFile;
  469. // sometimes, we can get fancy error pages back from the site instead of
  470. // a nice nifty HTML error code, so check & see if we got back a html
  471. // file when we were expecting a cab.
  472. if (fCheckForHTML)
  473. {
  474. hr = IsFileHtml(pszLocalFile);
  475. if (SUCCEEDED(hr))
  476. {
  477. if (hr == S_FALSE)
  478. {
  479. LOG_Internet(_T("WinInet: Download is not a html file"));
  480. hr = S_OK;
  481. }
  482. else
  483. {
  484. LOG_Internet(_T("WinInet: Download is a html file. Failing download."));
  485. hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
  486. SafeCloseInvalidHandle(hFile);
  487. DeleteFile(pszLocalFile);
  488. goto CleanUp;
  489. }
  490. }
  491. else
  492. {
  493. LOG_Internet(_T("WinInet: Unable to determine if download is a html file or not. Failing download."));
  494. }
  495. }
  496. else
  497. {
  498. LOG_Internet(_T("WinInet: Skipping cab validation."));
  499. }
  500. }
  501. else
  502. {
  503. hr = S_OK;
  504. LOG_Internet(_T("WinInet: Server file is not newer. Skipping download."));
  505. // The server ain't newer & the file is already on machine, so
  506. // send progress callback indicating file downloadeded ok
  507. if (pfnCallback != NULL)
  508. {
  509. // fpnCallback(pCallbackData, DOWNLOAD_STATUS_FILECOMPLETE, dwFileSize, dwFileSize, NULL, NULL);
  510. pfnCallback(pvCallbackData, DOWNLOAD_STATUS_OK, cbRemoteFile, cbRemoteFile, NULL, NULL);
  511. }
  512. }
  513. CleanUp:
  514. SafeInternetCloseHandle(sfns, hOpenRequest);
  515. SafeInternetCloseHandle(sfns, hConnect);
  516. SafeInternetCloseHandle(sfns, hInternet);
  517. SafeHeapFree(pszContentType);
  518. // if we failed, see if it's ok to continue (quit events) and whether
  519. // we've tried enuf times yet.
  520. if (FAILED(hr) &&
  521. HandleEvents(rghQuitEvents, cQuitEvents) &&
  522. iRetryCounter >= 0 && iRetryCounter < c_cMaxRetries)
  523. {
  524. // in case of failure and have no enough retries yet, we retry
  525. // as long as not timeout yet
  526. DWORD dwElapsedTime;
  527. dwTickEnd = GetTickCount();
  528. if (dwTickEnd > dwTickStart)
  529. dwElapsedTime = dwTickEnd - dwTickStart;
  530. else
  531. dwElapsedTime = (0xFFFFFFFF - dwTickStart) + dwTickEnd;
  532. // We haven't hit our retry limit, so log & error and go again
  533. if (dwElapsedTime < c_dwRetryTimeLimitInmsWiuInet)
  534. {
  535. LogError(hr, "Library download error. Will retry.");
  536. // in the case where we're gonna retry, keep track of the very first
  537. // error we encoutered cuz the ops guys say that this is the most
  538. // useful error to know about.
  539. if (iRetryCounter == 0)
  540. {
  541. LOG_Internet(_T("First download error saved: 0x%08x."), hr);
  542. hrToReturn = hr;
  543. }
  544. else
  545. {
  546. LOG_Internet(_T("Subsequent download error: 0x%08x."), hr);
  547. }
  548. hr = S_OK;
  549. goto START_INTERNET;
  550. }
  551. // We've completely timed out, so bail
  552. else
  553. {
  554. LogError(hr, "Library download error and timed out (%d ms). Will not retry.", dwElapsedTime);
  555. }
  556. }
  557. // make a callback indicating a download error
  558. if (FAILED(hr) && pfnCallback != NULL)
  559. pfnCallback(pvCallbackData, DOWNLOAD_STATUS_ERROR, cbRemoteFile, 0, NULL, NULL);
  560. // if we haven't saved off an error, just use the current error. We can't
  561. // have set hrToReturn previously if we didn't fail and want to attempt
  562. // a retry.
  563. // However, if we've got a success from this pass, be sure to return that
  564. // and not a fail code.
  565. if (FAILED(hr) && SUCCEEDED(hrToReturn))
  566. hrToReturn = hr;
  567. else if (SUCCEEDED(hr) && FAILED(hrToReturn))
  568. hrToReturn = hr;
  569. SafeHeapFree(pszServerName);
  570. SafeHeapFree(pszObject);
  571. return hrToReturn;
  572. }