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.

2437 lines
66 KiB

  1. /************************************************************************
  2. Copyright (c) 2000 - 2000 Microsoft Corporation
  3. Module Name :
  4. downloader.cpp
  5. Abstract :
  6. Main Source file for downloader.
  7. Author :
  8. Revision History :
  9. ***********************************************************************/
  10. #include "stdafx.h"
  11. #include "malloc.h"
  12. #if !defined(BITS_V12_ON_NT4)
  13. #include "downloader.tmh"
  14. #endif
  15. void SafeCloseInternetHandle( HINTERNET & h )
  16. {
  17. if (h)
  18. {
  19. InternetCloseHandle( h );
  20. h = NULL;
  21. }
  22. }
  23. #define ACCEPT_ENCODING_STRING _T("Accept-encoding: identity")
  24. BOOL NeedRetry( QMErrInfo * );
  25. bool NeedCredentials( HINTERNET hRequest, DWORD err );
  26. bool IsPossibleProxyFailure( DWORD err );
  27. DWORD GetRequestStatus( HINTERNET hRequest ) throw( ComError );
  28. bool
  29. ApplyCredentials(
  30. HINTERNET hRequest,
  31. const CCredentialsContainer * Credentials,
  32. WCHAR UserName[],
  33. WCHAR Password[]
  34. ) throw( ComError );
  35. bool
  36. ApplySchemeCredentials(
  37. HINTERNET hRequest,
  38. DWORD dwTarget,
  39. DWORD dwScheme,
  40. const CCredentialsContainer * Credentials,
  41. WCHAR UserName[],
  42. WCHAR Password[]
  43. ) throw( ComError );
  44. HRESULT
  45. CheckReplyRange(
  46. HINTERNET hRequest,
  47. UINT64 CorrectStart,
  48. UINT64 CorrectEnd,
  49. UINT64 CorrectTotal
  50. );
  51. HRESULT
  52. CheckReplyLength(
  53. HINTERNET hRequest,
  54. UINT64 CorrectOffset,
  55. UINT64 CorrectTotal
  56. );
  57. #ifndef USE_WININET
  58. VOID CALLBACK
  59. HttpRequestCallback(
  60. IN HINTERNET hInternet,
  61. IN DWORD_PTR dwContext,
  62. IN DWORD dwInternetStatus,
  63. IN LPVOID lpvStatusInformation OPTIONAL,
  64. IN DWORD dwStatusInformationLength
  65. );
  66. DWORD
  67. MapSecureHttpErrorCode(
  68. DWORD flags
  69. );
  70. #endif
  71. CACHED_AUTOPROXY * g_ProxyCache;
  72. BYTE g_FileDataBuffer[ FILE_DATA_BUFFER_LEN ];
  73. HRESULT
  74. CreateHttpDownloader(
  75. Downloader **ppDownloader,
  76. QMErrInfo *pErrInfo
  77. )
  78. {
  79. Downloader * pDownloader = 0;
  80. try
  81. {
  82. *ppDownloader = NULL;
  83. g_ProxyCache = new CACHED_AUTOPROXY;
  84. pDownloader = new CProgressiveDL( pErrInfo );
  85. *ppDownloader = pDownloader;
  86. return S_OK;
  87. }
  88. catch ( ComError err )
  89. {
  90. delete g_ProxyCache; g_ProxyCache = 0;
  91. delete pDownloader;
  92. return err.Error();
  93. }
  94. }
  95. void
  96. DeleteHttpDownloader(
  97. Downloader *pDownloader
  98. )
  99. {
  100. CProgressiveDL * ptr = (CProgressiveDL *) pDownloader;
  101. delete ptr;
  102. delete g_ProxyCache; g_ProxyCache = 0;
  103. }
  104. /////////////////////////////////////////////////////////////////////////////
  105. // CProgressiveDL
  106. CProgressiveDL::CProgressiveDL(
  107. QMErrInfo *pErrInfo
  108. ) :
  109. m_bThrottle( TRUE ),
  110. m_wupdinfo( NULL ),
  111. m_hOpenRequest( NULL ),
  112. m_hFile( INVALID_HANDLE_VALUE )
  113. {
  114. m_pQMInfo = pErrInfo;
  115. }
  116. CProgressiveDL::~CProgressiveDL()
  117. {
  118. ASSERT( m_hFile == INVALID_HANDLE_VALUE );
  119. }
  120. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  121. // Public function Download()
  122. // Accepts a URL and destination to download, callback to report various status
  123. // Input: url, destination, flags, callback for status
  124. // Output: todo handle
  125. // Return: hresult
  126. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  127. HRESULT
  128. CProgressiveDL::Download(
  129. LPCTSTR szURL,
  130. LPCTSTR szDest,
  131. UINT64 Offset,
  132. ITransferCallback * Callbacks,
  133. QMErrInfo *pQMErrInfo,
  134. HANDLE hToken,
  135. BOOL bThrottle,
  136. const PROXY_SETTINGS * ProxySettings,
  137. const CCredentialsContainer * Credentials,
  138. const StringHandle HostId
  139. )
  140. {
  141. HRESULT hr = S_OK;
  142. DWORD dwThreadID;
  143. m_Callbacks = Callbacks;
  144. m_pQMInfo = pQMErrInfo;
  145. m_bThrottle = bThrottle;
  146. m_pQMInfo->result = QM_FILE_FATAL_ERROR;
  147. ASSERT( Callbacks );
  148. ASSERT( pQMErrInfo );
  149. if (!m_pQMInfo)
  150. {
  151. return E_FAIL;
  152. }
  153. if ((!szURL) || (!szDest))
  154. {
  155. SetError( SOURCE_HTTP_UNKNOWN, ERROR_STYLE_HRESULT, E_INVALIDARG, "NULL file name" );
  156. return E_FAIL;
  157. }
  158. ASSERT( wcslen( szURL ) <= INTERNET_MAX_URL_LENGTH );
  159. ASSERT( wcslen( szDest ) <= MAX_PATH );
  160. LogDl( "---------------------------------------------------------------------" );
  161. LogDl( "Downloading file %!ts! offset %d", szDest, DWORD(Offset) );
  162. m_pQMInfo->result = QM_IN_PROGRESS;
  163. do
  164. {
  165. hr = DownloadFile( hToken, ProxySettings, Credentials, szURL, szDest, Offset, HostId );
  166. }
  167. while ( hr == E_RETRY );
  168. if (hr == S_OK)
  169. {
  170. m_pQMInfo->result = QM_FILE_DONE;
  171. LogDl( "Done file %!ts!", szDest );
  172. }
  173. else if ( hr == S_FALSE )
  174. {
  175. m_pQMInfo->result = QM_FILE_ABORTED;
  176. LogDl( "File %!ts! aborted", szDest );
  177. }
  178. else if ( m_pQMInfo->result != QM_SERVER_FILE_CHANGED )
  179. {
  180. ASSERT( IsErrorSet() );
  181. if (NeedRetry(m_pQMInfo))
  182. {
  183. m_pQMInfo->result = QM_FILE_TRANSIENT_ERROR;
  184. }
  185. else
  186. {
  187. m_pQMInfo->result = QM_FILE_FATAL_ERROR;
  188. }
  189. }
  190. ASSERT( m_pQMInfo->result != QM_IN_PROGRESS );
  191. LogDl( "---------------------------------------------------------------------" );
  192. // if abort request came in after file failed, overwrite failed flag.
  193. if ( (QM_FILE_DONE != m_pQMInfo->result) && IsAbortRequested() )
  194. {
  195. m_pQMInfo->result = QM_FILE_ABORTED;
  196. }
  197. ASSERT( m_hFile == INVALID_HANDLE_VALUE );
  198. return hr;
  199. }
  200. HRESULT
  201. CProgressiveDL::DownloadFile(
  202. HANDLE hToken,
  203. const PROXY_SETTINGS * ProxySettings,
  204. const CCredentialsContainer * Credentials,
  205. LPCTSTR Url,
  206. LPCWSTR Path,
  207. UINT64 Offset,
  208. StringHandle HostId
  209. )
  210. {
  211. HRESULT hr = S_OK;
  212. ASSERT( m_wupdinfo == NULL );
  213. ASSERT( m_hOpenRequest == NULL );
  214. m_pQMInfo->Clear();
  215. try
  216. {
  217. if (!ImpersonateLoggedOnUser(hToken))
  218. {
  219. SetError( SOURCE_HTTP_UNKNOWN, ERROR_STYLE_WIN32, GetLastError(), "Impersonate" );
  220. throw ComError( E_FAIL );
  221. }
  222. //
  223. // Open a connection to the server, and use that data for our first estimate of the line speed.
  224. //
  225. m_wupdinfo = ConnectToUrl( Url, ProxySettings, Credentials, (const TCHAR*)HostId, m_pQMInfo );
  226. if (!m_wupdinfo)
  227. {
  228. ASSERT( IsErrorSet() );
  229. throw ComError( E_FAIL );
  230. }
  231. //
  232. // Get file size and time stamp.
  233. //
  234. if (! GetRemoteResourceInformation( m_wupdinfo, m_pQMInfo ))
  235. {
  236. ASSERT( IsErrorSet() );
  237. throw ComError( E_FAIL );
  238. }
  239. if (!OpenLocalDownloadFile(Path, Offset, m_wupdinfo->FileSize, m_wupdinfo->UrlModificationTime))
  240. {
  241. ASSERT( IsErrorSet() );
  242. throw ComError( E_FAIL );
  243. }
  244. // Be sure to check for an end of file before attempting
  245. // to download more bytes.
  246. if (IsFileComplete())
  247. {
  248. LogDl( "File is done already.\n" );
  249. if (!SetFileTimes())
  250. {
  251. ASSERT( IsErrorSet() );
  252. hr = E_FAIL;
  253. }
  254. hr = S_OK;
  255. }
  256. //
  257. // Transfer data from the server.
  258. //
  259. else if ( !m_bThrottle )
  260. {
  261. hr = DownloadForegroundFile();
  262. }
  263. else
  264. {
  265. //
  266. // Use either the server's host name or the proxy's host name to find the right network adapter..
  267. //
  268. hr = m_Network.SetInterfaceIndex( m_wupdinfo->fProxy ? m_wupdinfo->ProxyHost.get() : m_wupdinfo->HostName );
  269. if (FAILED(hr))
  270. {
  271. SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, hr, "GetInterfaceIndex" );
  272. throw ComError( E_FAIL );
  273. }
  274. while (1)
  275. {
  276. if (IsAbortRequested())
  277. {
  278. hr = S_FALSE;
  279. break;
  280. }
  281. hr = GetNextBlock();
  282. if ( S_FALSE == hr )
  283. break;
  284. if (FAILED(hr) )
  285. {
  286. ASSERT( hr == E_RETRY || IsErrorSet() );
  287. break;
  288. }
  289. if (IsFileComplete())
  290. {
  291. if (!SetFileTimes())
  292. {
  293. ASSERT( IsErrorSet() );
  294. hr = E_FAIL;
  295. break;
  296. }
  297. hr = S_OK;
  298. break;
  299. }
  300. }
  301. }
  302. }
  303. catch( ComError exception )
  304. {
  305. ASSERT( IsErrorSet() );
  306. hr = exception.Error();
  307. }
  308. if ( m_bThrottle )
  309. {
  310. m_Network.StopTimer();
  311. }
  312. CloseLocalFile();
  313. RevertToSelf();
  314. delete m_wupdinfo; m_wupdinfo = NULL;
  315. SafeCloseInternetHandle( m_hOpenRequest );
  316. if (FAILED(hr))
  317. {
  318. ASSERT( hr == E_RETRY || IsErrorSet() );
  319. }
  320. return hr;
  321. }
  322. HRESULT
  323. CProgressiveDL::DownloadForegroundFile()
  324. {
  325. HRESULT hr = E_FAIL;
  326. LogDl( "Starting foreground file download" );
  327. while( 1 )
  328. {
  329. //
  330. // Loop of HTTP requests. This is to work around a wininet/winhttp limitation
  331. // where request sizes can be a maximum of 4GB.
  332. //
  333. UINT64 BlockSize64 = m_wupdinfo->FileSize - m_CurrentOffset;
  334. DWORD BlockSize = (DWORD)min( BlockSize64, 2147483648 );
  335. LogDl( "Starting foreground file download request block: file size %d, offset %d, block %d",
  336. DWORD( m_wupdinfo->FileSize ), DWORD(m_CurrentOffset), BlockSize );
  337. //
  338. // Send a block request and read the reply headers.
  339. //
  340. // hr = (m_wupdinfo->bRange) ? StartRangeRequest( BlockSize ) : StartEncodedRangeRequest( BlockSize );
  341. hr = StartRangeRequest( BlockSize );
  342. if (FAILED(hr))
  343. {
  344. ASSERT( IsErrorSet() );
  345. return hr;
  346. }
  347. const DWORD MIN_FOREGROUND_BLOCK = 4096;
  348. const DWORD MAX_FOREGROUND_BLOCK = FILE_DATA_BUFFER_LEN;
  349. const DWORD FOREGROUND_BLOCK_INCREMENT = 1024;
  350. const DWORD FOREGROUND_UPDATE_RATE = 2000;
  351. DWORD ForegroundBlockSize = min( MIN_FOREGROUND_BLOCK, BlockSize );
  352. DWORD dwPrevTick = GetTickCount();
  353. while( 1 )
  354. {
  355. //
  356. // Loop of read blocks inside an individual request.
  357. //
  358. if (IsAbortRequested())
  359. {
  360. return S_FALSE;
  361. }
  362. if ( IsFileComplete() )
  363. {
  364. LogDl( "File is done, exiting.\n" );
  365. if (!SetFileTimes())
  366. {
  367. ASSERT( IsErrorSet() );
  368. return E_FAIL;
  369. }
  370. return S_OK;
  371. }
  372. BYTE *p = g_FileDataBuffer;
  373. DWORD dwTotalBytesRead = 0;
  374. DWORD dwBytesToRead = ForegroundBlockSize;
  375. DWORD dwRead;
  376. while( 1 )
  377. {
  378. if (! InternetReadFile(m_hOpenRequest, p, dwBytesToRead, &dwRead) )
  379. {
  380. SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_WIN32, GetLastError(), "InternetReadFile" );
  381. LogWarning( "InternetReadFile failed: len=%d, offset=%I64d, err=%d",
  382. ForegroundBlockSize, m_CurrentOffset, GetLastError());
  383. return E_FAIL;
  384. }
  385. if ( !dwRead )
  386. break;
  387. dwTotalBytesRead += dwRead;
  388. dwBytesToRead -= dwRead;
  389. p += dwRead;
  390. if ( !dwBytesToRead )
  391. break;
  392. }
  393. if (!WriteBlockToCache( (LPBYTE) g_FileDataBuffer, dwTotalBytesRead ))
  394. {
  395. ASSERT( IsErrorSet() );
  396. return hr;
  397. }
  398. if (dwTotalBytesRead != ForegroundBlockSize &&
  399. m_CurrentOffset != m_wupdinfo->FileSize)
  400. {
  401. LogError("Download block : EOF after %d", dwRead );
  402. SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_WIN32, ERROR_INTERNET_CONNECTION_RESET, "DownloadBlock" );
  403. return E_FAIL;
  404. }
  405. if (m_Callbacks->DownloaderProgress( m_CurrentOffset, m_wupdinfo->FileSize ))
  406. {
  407. // abort was requested
  408. return S_FALSE;
  409. }
  410. DWORD dwNewTick = GetTickCount();
  411. DWORD dwTimeDelta = dwNewTick - dwPrevTick;
  412. if ( dwTimeDelta < FOREGROUND_UPDATE_RATE )
  413. ForegroundBlockSize = min( ForegroundBlockSize + FOREGROUND_BLOCK_INCREMENT, MAX_FOREGROUND_BLOCK );
  414. else if ( dwTimeDelta > FOREGROUND_UPDATE_RATE )
  415. ForegroundBlockSize = max( ForegroundBlockSize - FOREGROUND_BLOCK_INCREMENT, MIN_FOREGROUND_BLOCK );
  416. ForegroundBlockSize = min( ForegroundBlockSize, ( m_wupdinfo->FileSize - m_CurrentOffset ) );
  417. dwPrevTick = dwNewTick;
  418. //
  419. // End loop of read blocks
  420. //
  421. }
  422. //
  423. // End loop of requests
  424. //
  425. }
  426. }
  427. HRESULT
  428. CProgressiveDL::GetNextBlock()
  429. {
  430. HRESULT hr = E_FAIL;
  431. LogDl( "file size %d, offset %d", DWORD(m_wupdinfo->FileSize), DWORD(m_CurrentOffset) );
  432. m_Network.CalculateIntervalAndBlockSize( m_wupdinfo->FileSize - m_CurrentOffset );
  433. DWORD BlockSize = m_Network.m_BlockSize;
  434. if (BlockSize == 0)
  435. {
  436. m_Network.TakeSnapshot( CNetworkInterface::BLOCK_START );
  437. m_Network.TakeSnapshot( CNetworkInterface::BLOCK_END );
  438. }
  439. else
  440. {
  441. m_Network.TakeSnapshot( CNetworkInterface::BLOCK_START );
  442. //
  443. // Request a block from the server.
  444. //
  445. // hr = (m_wupdinfo->bRange) ? StartRangeRequest( BlockSize ) : StartEncodedRangeRequest( BlockSize );
  446. hr = StartRangeRequest( BlockSize );
  447. if (FAILED(hr))
  448. {
  449. ASSERT( IsErrorSet() );
  450. return hr;
  451. }
  452. //
  453. // A single call to InternetReadFile may return only part of the requested data,
  454. // so loop until the entire block has arrived.
  455. //
  456. DWORD dwBytesRead = 0;
  457. while (dwBytesRead < BlockSize )
  458. {
  459. DWORD dwSize = min( (BlockSize - dwBytesRead) , FILE_DATA_BUFFER_LEN );
  460. DWORD dwRead = 0;
  461. if (! InternetReadFile( m_hOpenRequest,
  462. g_FileDataBuffer,
  463. dwSize,
  464. &dwRead ))
  465. {
  466. SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_WIN32, GetLastError(), "InternetReadFile" );
  467. LogWarning( "InternetReadFile failed: len=%d, offset=%I64d, err=%d",
  468. dwSize, m_CurrentOffset, GetLastError());
  469. return E_FAIL;
  470. }
  471. else if (dwRead == 0)
  472. {
  473. break;
  474. }
  475. dwBytesRead += dwRead;
  476. //
  477. // Save the data.
  478. //
  479. if (!WriteBlockToCache( (LPBYTE) g_FileDataBuffer, dwRead ))
  480. {
  481. ASSERT( IsErrorSet() );
  482. return hr;
  483. }
  484. }
  485. if (dwBytesRead != BlockSize)
  486. {
  487. LogError("Download block : EOF after %d", dwBytesRead );
  488. SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_WIN32, ERROR_INTERNET_CONNECTION_RESET, "DownloadBlock" );
  489. return E_FAIL;
  490. }
  491. m_Network.TakeSnapshot( CNetworkInterface::BLOCK_END );
  492. if (m_Callbacks->DownloaderProgress( m_CurrentOffset, m_wupdinfo->FileSize ))
  493. {
  494. // abort was requested
  495. return S_FALSE;
  496. }
  497. }
  498. //
  499. // Allow other apps to use the network for the rest of the time interval,
  500. // then take the end-of-interval snapshot.
  501. //
  502. m_Network.Wait();
  503. hr = m_Network.TakeSnapshot( CNetworkInterface::BLOCK_INTERVAL_END );
  504. if (SUCCEEDED(hr))
  505. {
  506. m_Network.SetInterfaceSpeed();
  507. }
  508. else if (hr == HRESULT_FROM_WIN32( ERROR_INVALID_DATA ))
  509. {
  510. //
  511. // If the snapshot fails with ERROR_INVALID_DATA and the downloads
  512. // keep working, then our NIC has been removed and the networking
  513. // layer has silently transferred our connection to another available
  514. // NIC. We need to identify the NIC that we are now using.
  515. //
  516. LogWarning("NIC is no longer valid. Requesting retry.");
  517. hr = E_RETRY;
  518. }
  519. else
  520. SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, hr, "TakeSnapshot" );
  521. return hr;
  522. }
  523. URL_INFO::URL_INFO(
  524. LPCTSTR Url,
  525. const PROXY_SETTINGS * a_ProxySettings,
  526. const CCredentialsContainer * a_Credentials,
  527. LPCTSTR HostId
  528. ) :
  529. hInternet( 0 ),
  530. hConnect( 0 ),
  531. FileSize( 0 ),
  532. dwFlags( 0 ),
  533. bHttp11( true ),
  534. ProxySettings( 0 ),
  535. fProxy( false ),
  536. Credentials( a_Credentials )
  537. {
  538. try
  539. {
  540. LogInfo("new URL_INFO at %p", this );
  541. //
  542. // Split the URL into server, path, name, and password components.
  543. //
  544. URL_COMPONENTS UrlComponents;
  545. ZeroMemory(&UrlComponents, sizeof(UrlComponents));
  546. UrlComponents.dwStructSize = sizeof(UrlComponents);
  547. UrlComponents.lpszHostName = HostName;
  548. UrlComponents.dwHostNameLength = RTL_NUMBER_OF(HostName);
  549. UrlComponents.lpszUrlPath = UrlPath;
  550. UrlComponents.dwUrlPathLength = RTL_NUMBER_OF(UrlPath);
  551. UrlComponents.lpszUserName = UserName;
  552. UrlComponents.dwUserNameLength = RTL_NUMBER_OF(UserName);
  553. UrlComponents.lpszPassword = Password;
  554. UrlComponents.dwPasswordLength = RTL_NUMBER_OF(Password);
  555. if (! InternetCrackUrl(Url, 0, 0, &UrlComponents))
  556. {
  557. if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  558. {
  559. THROW_HRESULT( HRESULT_FROM_WIN32( ERROR_WINHTTP_INVALID_URL ));
  560. }
  561. ThrowLastError();
  562. }
  563. if (-1 == UrlComponents.dwHostNameLength ||
  564. -1 == UrlComponents.dwUrlPathLength ||
  565. -1 == UrlComponents.dwUserNameLength ||
  566. -1 == UrlComponents.dwPasswordLength)
  567. {
  568. THROW_HRESULT( HRESULT_FROM_WIN32( ERROR_WINHTTP_INVALID_URL ));
  569. }
  570. Port = UrlComponents.nPort;
  571. if (0 == _tcslen(HostName))
  572. {
  573. THROW_HRESULT( E_INVALIDARG );
  574. }
  575. if ( HostId && *HostId )
  576. {
  577. // redirected to another host.
  578. THROW_HRESULT( StringCbCopy( HostName, sizeof(HostName), HostId ));
  579. LogDl( "Stuck to %!ts!...", UrlComponents.lpszHostName );
  580. }
  581. //
  582. // Set the connect flags.
  583. //
  584. dwFlags = INTERNET_FLAG_NO_UI | INTERNET_FLAG_RELOAD;
  585. #if !defined( USE_WININET )
  586. dwFlags |= WINHTTP_FLAG_ESCAPE_DISABLE_QUERY;
  587. #endif
  588. if(UrlComponents.nScheme == INTERNET_SCHEME_HTTPS)
  589. {
  590. dwFlags |= INTERNET_FLAG_SECURE;
  591. }
  592. ProxySettings = new PROXY_SETTINGS_CONTAINER( Url, a_ProxySettings );
  593. }
  594. catch ( ComError err )
  595. {
  596. Cleanup();
  597. throw;
  598. }
  599. }
  600. //
  601. // Splitting the explicit cleanup into a separate function allows the constructor to re-use the code.
  602. // The constructor *cannot* explicitly call the destructor if it is going to throw an exception,
  603. // because the destructor will call the StringHandle member destructors and then the constructor
  604. // will call them again (because it is throwing an exception and cleans up the already-constructed
  605. // members).
  606. //
  607. URL_INFO::~URL_INFO()
  608. {
  609. Cleanup();
  610. }
  611. void URL_INFO::Cleanup()
  612. {
  613. LogInfo("deleting URL_INFO at %p", this );
  614. Disconnect();
  615. MySecureZeroMemory( UserName, sizeof(UserName) );
  616. MySecureZeroMemory( Password, sizeof(Password) );
  617. delete ProxySettings; ProxySettings = NULL;
  618. }
  619. void
  620. URL_INFO::Disconnect()
  621. {
  622. SafeCloseInternetHandle( hConnect );
  623. SafeCloseInternetHandle( hInternet );
  624. }
  625. QMErrInfo
  626. URL_INFO::Connect()
  627. {
  628. try
  629. {
  630. //
  631. // The proxy stuff is ignored because we will set an explicit proxy on each request.
  632. //
  633. hInternet = InternetOpen( C_BITS_USER_AGENT,
  634. INTERNET_OPEN_TYPE_DIRECT,
  635. NULL,
  636. NULL,
  637. 0 );
  638. if (! hInternet )
  639. {
  640. ThrowLastError();
  641. }
  642. #ifdef USE_WININET
  643. {
  644. DWORD dwDisable = 1;
  645. if (!InternetSetOption(hInternet, INTERNET_OPTION_DISABLE_AUTODIAL, &dwDisable, sizeof(DWORD)))
  646. {
  647. ThrowLastError();
  648. }
  649. }
  650. #endif
  651. if (! (hConnect = WinHttpConnect( hInternet,
  652. HostName,
  653. Port,
  654. 0))) //context
  655. {
  656. ThrowLastError();
  657. }
  658. QMErrInfo Success;
  659. return Success;
  660. }
  661. catch ( ComError err )
  662. {
  663. LogError( "error %x connecting to server", err.Error() );
  664. QMErrInfo QmError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_WIN32, err.Error() );
  665. return QmError;
  666. }
  667. }
  668. URL_INFO *
  669. ConnectToUrl(
  670. LPCTSTR Url,
  671. const PROXY_SETTINGS * ProxySettings,
  672. const CCredentialsContainer * Credentials,
  673. const TCHAR * HostId,
  674. QMErrInfo * pErrInfo
  675. )
  676. {
  677. //
  678. // Open a connection to the server.
  679. //
  680. LogDl( "Connecting to %!ts!...", Url);
  681. //
  682. // This should have been checked by the caller.
  683. //
  684. ASSERT( HostId == NULL || wcslen(HostId) < INTERNET_MAX_HOST_NAME_LENGTH );
  685. try
  686. {
  687. URL_INFO * Info = new URL_INFO( Url, ProxySettings, Credentials, HostId );
  688. *pErrInfo = Info->Connect();
  689. if (pErrInfo->IsSet())
  690. {
  691. delete Info;
  692. return NULL;
  693. }
  694. return Info;
  695. }
  696. catch ( ComError err )
  697. {
  698. pErrInfo->Set( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, err.Error(), "untracked API" );
  699. return NULL;
  700. }
  701. }
  702. BOOL
  703. CProgressiveDL::GetRemoteResourceInformation(
  704. URL_INFO * Info,
  705. QMErrInfo *pQMErrInfo
  706. )
  707. /*
  708. We begin with an HTTP 1.1 HEAD request.
  709. If the server replies with version 1.1, we have a persistent connection and the proxy, if present, can cache our requests.
  710. If the server replies with version 1.0, we do not have either characteristic. Our GET requests will add "Connection: keep-alive"
  711. but it may not do any good. The proxy server, if present, may not understand ranges and if we allow caching then it will
  712. cache a range request as if it were the entire file.
  713. If the server replies with any other version or the call fails, then we should bail with BG_E_INSUFFICIENT_SERVER_SUPPORT.
  714. If an error occurs, we report it and bail.
  715. */
  716. {
  717. HRESULT FailureCode = 0;
  718. unsigned FailureLine = 0;
  719. #define CHECK_HRESULT( x ) \
  720. { \
  721. HRESULT _hr_ = x; \
  722. if (FAILED(_hr_)) \
  723. { \
  724. FailureCode = _hr_; \
  725. FailureLine = __LINE__; \
  726. goto exit; \
  727. } \
  728. }
  729. #define CHECK_BOOL( x ) \
  730. { \
  731. if (! x ) \
  732. { \
  733. FailureCode = HRESULT_FROM_WIN32( GetLastError() ); \
  734. FailureLine = __LINE__; \
  735. goto exit; \
  736. } \
  737. }
  738. // Assume HTTP1.1 with no proxy until we determine otherwise.
  739. //
  740. Info->bHttp11 = TRUE;
  741. Info->bRange = TRUE;
  742. Info->fProxy = FALSE;
  743. BOOL b = FALSE;
  744. HRESULT hr;
  745. HINTERNET hRequest = NULL;
  746. DWORD dwErr, dwLength = 0, dwStatus = 0, dwState = 0;
  747. CHECK_HRESULT( OpenHttpRequest( _T("HEAD"), _T("HTTP/1.1"), *Info, &hRequest ) );
  748. CHECK_HRESULT( SendRequest( hRequest, Info ));
  749. // check status
  750. dwLength = sizeof(dwStatus);
  751. CHECK_BOOL( HttpQueryInfo( hRequest,
  752. HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
  753. (LPVOID)&dwStatus,
  754. &dwLength,
  755. NULL));
  756. if (dwStatus != HTTP_STATUS_OK)
  757. {
  758. pQMErrInfo->Set( SOURCE_HTTP_SERVER, ERROR_STYLE_HTTP, dwStatus );
  759. goto exit;
  760. }
  761. //
  762. // We know that the server replied with a success code. Now determine the HTTP version.
  763. //
  764. unsigned MajorVersion;
  765. unsigned MinorVersion;
  766. CHECK_HRESULT( GetResponseVersion( hRequest, &MajorVersion, &MinorVersion ));
  767. if (MajorVersion != 1)
  768. {
  769. LogWarning("server version %d.%d is outside our supported range", MajorVersion, MinorVersion );
  770. pQMErrInfo->Set( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, BG_E_INSUFFICIENT_HTTP_SUPPORT );
  771. goto exit;
  772. }
  773. if (MinorVersion < 1)
  774. {
  775. Info->bHttp11 = FALSE;
  776. Info->dwFlags |= INTERNET_FLAG_DONT_CACHE;
  777. }
  778. //
  779. // Now determine the proxy server.
  780. //
  781. CHECK_BOOL( Info->GetProxyUsage( hRequest, pQMErrInfo ));
  782. // check file size
  783. WCHAR FileSizeText[ INT64_DIGITS+1 ];
  784. dwLength = sizeof( FileSizeText );
  785. if (!HttpQueryInfo( hRequest,
  786. HTTP_QUERY_CONTENT_LENGTH,
  787. FileSizeText,
  788. &dwLength,
  789. NULL))
  790. {
  791. pQMErrInfo->Set( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, BG_E_MISSING_FILE_SIZE, "HttpQueryInfo: content length" );
  792. goto exit;
  793. }
  794. if ( 1 != swscanf( FileSizeText, L"%I64u", &Info->FileSize ) )
  795. {
  796. pQMErrInfo->Set( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, BG_E_MISSING_FILE_SIZE, "swscanf: content length" );
  797. goto exit;
  798. }
  799. LogDl( "File size of %!ts! = %I64u", Info->UrlPath, Info->FileSize);
  800. // check file time
  801. //
  802. SYSTEMTIME sysTime;
  803. dwLength = sizeof(sysTime);
  804. if (!HttpQueryInfo( hRequest,
  805. HTTP_QUERY_LAST_MODIFIED | HTTP_QUERY_FLAG_SYSTEMTIME,
  806. (LPVOID)&sysTime,
  807. &dwLength,
  808. NULL))
  809. {
  810. LogWarning( "GetFileSize : HttpQueryInfo( LAST_MODIFIED ) failed with %d", GetLastError());
  811. memset( &Info->UrlModificationTime, 0, sizeof(Info->UrlModificationTime) );
  812. }
  813. else
  814. {
  815. SystemTimeToFileTime(&sysTime, &Info->UrlModificationTime);
  816. }
  817. b = TRUE;
  818. exit:
  819. if (FailureCode)
  820. {
  821. LogError("failure at line %d; hresult = %x", FailureLine, FailureCode );
  822. pQMErrInfo->Set( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, FailureCode );
  823. }
  824. // release allocated objects
  825. //
  826. SafeCloseInternetHandle( hRequest );
  827. return b;
  828. #undef CHECK_HRESULT
  829. #undef CHECK_BOOL
  830. }
  831. BOOL
  832. URL_INFO::GetProxyUsage(
  833. HINTERNET hRequest,
  834. QMErrInfo *ErrInfo
  835. )
  836. /*
  837. This function determines whether the completed request in <hRequest> used a proxy server,
  838. and if so which one. In BITS 1.0 (Windows XP), it looked in the HTTP 1.1 Via header, but
  839. that header isn't present in HTTP 1.0 replies, and a server is allowed to return a fake host name.
  840. (see RFC 2616 section 14.45 for details.)
  841. The current version parses the current proxy value in this->ProxySettings, which was calculated
  842. by the HTTP layer. The format of a proxy-server entry is as follows:
  843. ([<scheme>=][<scheme>"://"]<server>[":"<port>])
  844. this->ProxyHost should include only the server name.
  845. On exit:
  846. if TRUE, fProxy and ProxyHost are set.
  847. if FALSE, fProxy and ProxyHost are unchanged and ErrInfo is set.
  848. */
  849. {
  850. try
  851. {
  852. LPCWSTR p = ProxySettings->GetCurrentProxy();
  853. if (!p)
  854. {
  855. fProxy = FALSE;
  856. return TRUE;
  857. }
  858. LPCWSTR p2;
  859. //
  860. // Skip past the [<scheme>=] segment.
  861. //
  862. p2 = wcschr( p, '=' );
  863. if (p2)
  864. {
  865. ++p2;
  866. p = p2;
  867. }
  868. //
  869. // Skip past the [<scheme>"://"] segment.
  870. //
  871. p2 = wcschr( p, '/' );
  872. if (p2)
  873. {
  874. ++p2;
  875. if (*p2 == '/')
  876. {
  877. ++p2;
  878. }
  879. p = p2;
  880. }
  881. //
  882. // p now points to the beginning of the server name. Copy it.
  883. //
  884. ProxyHost = CAutoString( CopyString( p ));
  885. //
  886. // Find the [":"<port>] segment.
  887. //
  888. LPWSTR pColon = wcschr( ProxyHost.get(), ':' );
  889. if (pColon)
  890. {
  891. *pColon = '\0';
  892. }
  893. fProxy = TRUE;
  894. return TRUE;
  895. }
  896. catch ( ComError err )
  897. {
  898. ErrInfo->Set( SOURCE_HTTP_UNKNOWN, ERROR_STYLE_HRESULT, err.Error() );
  899. return FALSE;
  900. }
  901. }
  902. //HRESULT
  903. //CProgressiveDL::StartEncodedRangeRequest(
  904. // DWORD Length
  905. // )
  906. //{
  907. // HRESULT hr = S_OK;
  908. // DWORD dwTotalRead = 0, dwRead = 0, dwErr, dwSize = 0, dwStatus;
  909. //
  910. // if (FAILED(hr = CreateBlockUrl(m_wupdinfo->BlockUrl, Length)))
  911. // {
  912. // return hr;
  913. // }
  914. //
  915. // UINT64 Offset = m_CurrentOffset;
  916. //
  917. // //
  918. // // Every block goes to a different URL, so open a new request each time.
  919. // //
  920. // SafeCloseInternetHandle( m_hOpenRequest );
  921. //
  922. // LPCTSTR AcceptTypes[] = {_T("*/*"), NULL};
  923. //
  924. // if (! (m_hOpenRequest = HttpOpenRequest(
  925. // m_wupdinfo->hConnect,
  926. // NULL, // "GET"
  927. // m_wupdinfo->BlockUrl,
  928. // _T("HTTP/1.0"),
  929. // NULL, //referer
  930. // AcceptTypes,
  931. // m_wupdinfo->dwFlags,
  932. // 0))) //context
  933. // {
  934. // SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_WIN32, GetLastError(), "InternetOpenUrl" );
  935. // return E_FAIL;
  936. // }
  937. //
  938. // hr = AddIf_Unmodified_SinceHeader( m_hOpenRequest, m_wupdinfo->FileCreationTime );
  939. // if (FAILED(hr))
  940. // {
  941. // SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, hr, "adding if-unmodifed-since header" );
  942. // return E_FAIL;
  943. // }
  944. //
  945. // if (! HttpAddRequestHeaders(m_hOpenRequest, ACCEPT_ENCODING_STRING, -1L, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE ))
  946. // {
  947. // SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_WIN32, GetLastError(), "add header: accept-encoding" );
  948. // return E_FAIL;
  949. // }
  950. //
  951. // hr = SendRequest( m_hOpenRequest, m_wupdinfo );
  952. // if (FAILED(hr))
  953. // {
  954. // SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, hr, "HttpSendRequest" );
  955. // LogError( "HttpSendRequest failed in progressive download loop - offset=%I64d",
  956. // m_CurrentOffset );
  957. // return E_FAIL;
  958. // }
  959. //
  960. // dwSize = sizeof(dwStatus);
  961. // if (! HttpQueryInfo(m_hOpenRequest,
  962. // HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
  963. // (void *)&dwStatus,
  964. // &dwSize,
  965. // NULL))
  966. // {
  967. // SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_WIN32, GetLastError(), "HttpQueryInfo" );
  968. // return E_FAIL;
  969. // }
  970. //
  971. // if ( HTTP_STATUS_PRECOND_FAILED == dwStatus )
  972. // {
  973. // SetError( SOURCE_HTTP_SERVER, ERROR_STYLE_HTTP, dwStatus );
  974. // m_pQMInfo->result = QM_SERVER_FILE_CHANGED;
  975. // return E_FAIL;
  976. // }
  977. //
  978. // if ((dwStatus != HTTP_STATUS_OK) && (dwStatus != HTTP_STATUS_PARTIAL_CONTENT))
  979. // {
  980. // if (DoesErrorIndicateNoISAPI( dwStatus ) )
  981. // {
  982. // LogError("HTTP 1.0 server returned %d, returning INSUFFICIENT_HTTP_SUPPORT", dwStatus);
  983. // SetError( SOURCE_HTTP_SERVER, ERROR_STYLE_HRESULT, BG_E_INSUFFICIENT_HTTP_SUPPORT );
  984. // return BG_E_INSUFFICIENT_HTTP_SUPPORT;
  985. // }
  986. // else
  987. // {
  988. // SetError( SOURCE_HTTP_SERVER, ERROR_STYLE_HTTP, dwStatus );
  989. // return E_FAIL;
  990. // }
  991. // }
  992. // return S_OK;
  993. //}
  994. //
  995. //HRESULT
  996. //CProgressiveDL::CreateBlockUrl(
  997. // LPTSTR lpszNewUrl,
  998. // DWORD Length
  999. // )
  1000. //{
  1001. // HRESULT hr;
  1002. // UINT64 Offset = m_CurrentOffset;
  1003. //
  1004. // hr = StringCchPrintf( lpszNewUrl,
  1005. // INTERNET_MAX_URL_LENGTH,
  1006. // _T("%s@%I64d-%I64d@"),
  1007. // m_wupdinfo->UrlPath,
  1008. // Offset,
  1009. // Offset + Length - 1
  1010. // );
  1011. // if (FAILED(hr))
  1012. // {
  1013. // SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, hr, "printf" );
  1014. // return E_FAIL;
  1015. // }
  1016. //
  1017. // return S_OK;
  1018. //}
  1019. HRESULT
  1020. CProgressiveDL::StartRangeRequest(
  1021. DWORD Length
  1022. )
  1023. {
  1024. HRESULT hr;
  1025. DWORD dwBegin, dwEnd, dwTotalRead = 0, dwRead = 0, dwErr, dwLength, dwStatus;
  1026. UINT64 Offset = m_CurrentOffset;
  1027. //todo cleanup by goto exit and close handles
  1028. if ( !m_hOpenRequest )
  1029. {
  1030. HINTERNET hRequest;
  1031. hr = OpenHttpRequest( NULL, // default is "GET"
  1032. m_wupdinfo->bHttp11 ? _T("HTTP/1.1") : _T("HTTP/1.0"),
  1033. *m_wupdinfo,
  1034. &hRequest
  1035. );
  1036. if (FAILED(hr))
  1037. {
  1038. SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, hr, "CreateHttpRequest");
  1039. return E_FAIL;
  1040. }
  1041. m_hOpenRequest = hRequest;
  1042. //
  1043. // These headers are constant for a particular file download attempt.
  1044. //
  1045. hr = AddIf_Unmodified_SinceHeader( m_hOpenRequest, m_wupdinfo->FileCreationTime );
  1046. if (FAILED(hr))
  1047. {
  1048. SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, hr );
  1049. LogError( "unable to add If-Unmodified-Since header: %x", hr);
  1050. return E_FAIL;
  1051. }
  1052. if (! HttpAddRequestHeaders(m_hOpenRequest, ACCEPT_ENCODING_STRING, -1L, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE ))
  1053. {
  1054. SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_WIN32, GetLastError(), "add header: accept-encoding" );
  1055. return E_FAIL;
  1056. }
  1057. }
  1058. hr = AddRangeHeader( m_hOpenRequest, Offset, Offset + Length - 1 );
  1059. if (FAILED(hr))
  1060. {
  1061. SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, hr, "AddRangeHeader" );
  1062. return E_FAIL;
  1063. }
  1064. hr = SendRequest( m_hOpenRequest, m_wupdinfo );
  1065. if (FAILED(hr))
  1066. {
  1067. SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, hr, "HttpSendRequest" );
  1068. LogError( "HttpSendRequest failed in progressive download loop - offset=%I64d",
  1069. m_CurrentOffset );
  1070. return E_FAIL;
  1071. }
  1072. //
  1073. // The server sent a reply. See if it was successful.
  1074. //
  1075. dwLength = sizeof(dwStatus);
  1076. if (! HttpQueryInfo(m_hOpenRequest,
  1077. HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
  1078. (LPVOID)&dwStatus,
  1079. &dwLength,
  1080. NULL))
  1081. {
  1082. SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_WIN32, GetLastError(), "HttpQueryInfo" );
  1083. return E_FAIL;
  1084. }
  1085. //
  1086. // If the server file changed, stop downloading and indicate that to the caller.
  1087. //
  1088. if ( HTTP_STATUS_PRECOND_FAILED == dwStatus )
  1089. {
  1090. SetError( SOURCE_HTTP_SERVER, ERROR_STYLE_HTTP, dwStatus );
  1091. m_pQMInfo->result = QM_SERVER_FILE_CHANGED;
  1092. return E_FAIL;
  1093. }
  1094. //
  1095. // If the server sent an error, fail.
  1096. //
  1097. if ( dwStatus != HTTP_STATUS_PARTIAL_CONTENT &&
  1098. dwStatus != HTTP_STATUS_OK)
  1099. {
  1100. SetError( SOURCE_HTTP_SERVER, ERROR_STYLE_HTTP, dwStatus );
  1101. return E_FAIL;
  1102. }
  1103. if (dwStatus == HTTP_STATUS_PARTIAL_CONTENT)
  1104. {
  1105. //
  1106. // Now see whether the server understood the range request.
  1107. // If it understands ranges, then it should have responded with a Content-Range header
  1108. // matching our request.
  1109. //
  1110. hr = CheckReplyRange( m_hOpenRequest,
  1111. m_CurrentOffset,
  1112. m_CurrentOffset + Length - 1,
  1113. m_wupdinfo->FileSize
  1114. );
  1115. if (FAILED(hr))
  1116. {
  1117. SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, hr, "Reply range" );
  1118. return hr;
  1119. }
  1120. //
  1121. // If the server appears not to support ranges, give up.
  1122. //
  1123. if (S_FALSE == hr)
  1124. {
  1125. m_wupdinfo->Disconnect();
  1126. SetError( SOURCE_HTTP_SERVER, ERROR_STYLE_HRESULT, BG_E_INSUFFICIENT_RANGE_SUPPORT );
  1127. return BG_E_INSUFFICIENT_RANGE_SUPPORT;
  1128. }
  1129. }
  1130. else
  1131. {
  1132. //
  1133. // The server replied with status 200. This could mean that the server doesn't understand
  1134. // range requests, or that the request encompassed the entire file.
  1135. // (In this situation, IIS 5.0 and 6.0 return 206, but some Apache versions return 200.)
  1136. // To distinguish them, make sure the starting offset of the request was zero and the
  1137. // file length is equal to the original request length.
  1138. //
  1139. hr = CheckReplyLength( m_hOpenRequest, m_CurrentOffset, Length );
  1140. if (FAILED(hr))
  1141. {
  1142. SetError( SOURCE_HTTP_CLIENT_CONN, ERROR_STYLE_HRESULT, hr, "content length" );
  1143. return hr;
  1144. }
  1145. //
  1146. // If the server did not include a Content-Length header, give up.
  1147. //
  1148. if (S_FALSE == hr)
  1149. {
  1150. m_wupdinfo->Disconnect();
  1151. SetError( SOURCE_HTTP_SERVER, ERROR_STYLE_HRESULT, BG_E_INSUFFICIENT_RANGE_SUPPORT );
  1152. return BG_E_INSUFFICIENT_RANGE_SUPPORT;
  1153. }
  1154. }
  1155. //
  1156. // Here is the code to switch to encoded range format, in case we need it later.
  1157. //
  1158. // if (S_FALSE == hr)
  1159. // {
  1160. // LogDl( "server does not support ranges." );
  1161. //
  1162. // m_wupdinfo->bHttp11 = FALSE;
  1163. // m_wupdinfo->bRange = FALSE;
  1164. //
  1165. // //
  1166. // // We can't just drain the rest of the server response and send again, because the server
  1167. // // response is very likely the entire file. Closing the connection will prevent the server
  1168. // // from writing, at max, any more than the client socket buffer size (16K).
  1169. // //
  1170. // m_wupdinfo->Disconnect();
  1171. //
  1172. // *m_pQMInfo = m_wupdinfo->Connect();
  1173. // if (m_pQMInfo->IsSet())
  1174. // {
  1175. // return E_FAIL;
  1176. // }
  1177. //
  1178. // HRESULT HrReadUrl = StartEncodedRangeRequest( Length );
  1179. //
  1180. // if ( BG_E_INSUFFICIENT_HTTP_SUPPORT == HrReadUrl )
  1181. // {
  1182. // SetError( SOURCE_HTTP_SERVER, ERROR_STYLE_HRESULT, BG_E_INSUFFICIENT_RANGE_SUPPORT );
  1183. // return BG_E_INSUFFICIENT_RANGE_SUPPORT;
  1184. // }
  1185. //
  1186. // return HrReadUrl;
  1187. // }
  1188. //
  1189. // Getting here means the range request succeeded.
  1190. //
  1191. return S_OK;
  1192. }
  1193. bool
  1194. CProgressiveDL::DoesErrorIndicateNoISAPI(
  1195. DWORD dwHttpError
  1196. )
  1197. {
  1198. // This function is used on the HTTP return code on an attept
  1199. // to use the isapi dll to estimate if the isapi is installed.
  1200. // Note, that the ISAPI should only be used after trying
  1201. // native HTTP/1.1 and this table assume 1.1 was tried first.
  1202. // From RFC 2616
  1203. switch( dwHttpError )
  1204. {
  1205. case 100: return false; // Continue
  1206. case 101: return false; // Switching Protocols
  1207. case 200: return false; // OK
  1208. case 201: return false; // Created
  1209. case 202: return false; // Accepted
  1210. case 203: return false; // Non-Authoritative
  1211. case 204: return false; // No Content
  1212. case 205: return false; // Reset Context
  1213. case 206: return false; // Partial Content
  1214. case 300: return false; // Multiple Choices
  1215. case 301: return false; // Moved Permanently
  1216. case 302: return false; // Found
  1217. case 303: return false; // See other
  1218. case 304: return false; // Not Modified
  1219. case 305: return false; // Use Proxy
  1220. case 306: return false; // Unused
  1221. case 307: return false; // Temporary Redirect
  1222. case 400: return true; // Bad Request
  1223. case 401: return false; // Unauthorized
  1224. case 402: return false; // Payment Required
  1225. case 403: return false; // Forbidden
  1226. case 404: return true; // Not Found
  1227. case 405: return false; // Method Not Allowed
  1228. case 406: return false; // Not Acceptable
  1229. case 407: return false; // Proxy Authentication Required
  1230. case 408: return false; // Request Timeout
  1231. case 409: return false; // Conflict
  1232. case 410: return true; // Gone
  1233. case 411: return false; // Length Required
  1234. case 412: return false; // Precondition Failed
  1235. case 413: return false; // Request Entity Too Large
  1236. case 414: return false; // Request URI too long
  1237. case 415: return false; // Unsupported Media Type
  1238. case 416: return false; // Requested Range Not Satisfiable
  1239. case 417: return false; // Expectation Failed
  1240. case 500: return true; // Internal Server Error
  1241. case 501: return true; // Not Implemented
  1242. case 502: return true; // Bad Gateway
  1243. case 503: return false; // Service Unavailable
  1244. case 504: return false; // Gateway Timeout
  1245. case 505: return false; // HTTP Version Not Supported
  1246. default:
  1247. // As indicated in the spec, map unknown codes
  1248. // to first code in the catagory
  1249. if ( dwHttpError >= 100 && dwHttpError < 200 )
  1250. return DoesErrorIndicateNoISAPI( 100 );
  1251. else if ( dwHttpError >= 200 && dwHttpError < 300 )
  1252. return DoesErrorIndicateNoISAPI( 200 );
  1253. else if ( dwHttpError >= 300 && dwHttpError < 400 )
  1254. return DoesErrorIndicateNoISAPI( 300 );
  1255. else if ( dwHttpError >= 400 && dwHttpError < 500 )
  1256. return DoesErrorIndicateNoISAPI( 400 );
  1257. else if ( dwHttpError >= 500 && dwHttpError < 500 )
  1258. return DoesErrorIndicateNoISAPI( 500 );
  1259. else
  1260. // No clue what the error is, assume this has nothing to do with the ISAPI
  1261. return false;
  1262. }
  1263. }
  1264. BOOL
  1265. NeedRetry(
  1266. QMErrInfo * ErrInfo
  1267. )
  1268. {
  1269. BOOL bRetry = FALSE;
  1270. if (ErrInfo->Source == SOURCE_HTTP_SERVER)
  1271. {
  1272. // Almost all of the 400 series HTTP errors( client errors ) are
  1273. // fatal. A few such as request timeout may happen during
  1274. // stress conditions...
  1275. // Note that RFC 2616 says to handle unknown 400 errors as error 400.
  1276. if ( ( ErrInfo->Code >= 400 ) &&
  1277. ( ErrInfo->Code < 500 ) )
  1278. {
  1279. switch( ErrInfo->Code )
  1280. {
  1281. case 408: // request timeout
  1282. case 409: // Conflict - Isn't really clear what this is about...
  1283. return TRUE; // retry these error
  1284. default:
  1285. return FALSE; // don't retry other 400
  1286. }
  1287. }
  1288. }
  1289. if ( ErrInfo->Style == ERROR_STYLE_HRESULT )
  1290. {
  1291. switch( LONG(ErrInfo->Code) )
  1292. {
  1293. // These codes indicate dynamic content or
  1294. // an unsupported server so no retries are necessary.
  1295. case BG_E_MISSING_FILE_SIZE:
  1296. case BG_E_INSUFFICIENT_HTTP_SUPPORT:
  1297. case BG_E_INSUFFICIENT_RANGE_SUPPORT:
  1298. case HRESULT_FROM_WIN32( ERROR_INTERNET_SEC_CERT_ERRORS ):
  1299. case HRESULT_FROM_WIN32( ERROR_INTERNET_INVALID_CA ):
  1300. case HRESULT_FROM_WIN32( ERROR_INTERNET_SEC_CERT_CN_INVALID ):
  1301. case HRESULT_FROM_WIN32( ERROR_INTERNET_SEC_CERT_DATE_INVALID ):
  1302. case HRESULT_FROM_WIN32( ERROR_INTERNET_SEC_CERT_REV_FAILED ):
  1303. case HRESULT_FROM_WIN32( ERROR_INTERNET_SEC_CERT_NO_REV ):
  1304. case HRESULT_FROM_WIN32( ERROR_WINHTTP_SECURE_CERT_REVOKED ):
  1305. case HRESULT_FROM_WIN32( ERROR_WINHTTP_SECURE_INVALID_CERT ):
  1306. return FALSE;
  1307. }
  1308. }
  1309. if (COMPONENT_TRANS == (ErrInfo->Source & COMPONENT_MASK))
  1310. {
  1311. return TRUE;
  1312. }
  1313. switch (ErrInfo->Style)
  1314. {
  1315. case ERROR_STYLE_WIN32:
  1316. {
  1317. switch (ErrInfo->Code)
  1318. {
  1319. case ERROR_NOT_ENOUGH_MEMORY:
  1320. return TRUE;
  1321. }
  1322. }
  1323. }
  1324. return FALSE;
  1325. }
  1326. HRESULT
  1327. CProgressiveDL::GetRemoteFileInformation(
  1328. HANDLE hToken,
  1329. LPCTSTR szURL,
  1330. UINT64 * pFileSize,
  1331. FILETIME *pFileTime,
  1332. QMErrInfo *pErrInfo,
  1333. const PROXY_SETTINGS * pProxySettings,
  1334. const CCredentialsContainer * Credentials,
  1335. StringHandle HostId
  1336. )
  1337. {
  1338. *pFileSize = 0;
  1339. memset( pFileTime, 0, sizeof(FILETIME) );
  1340. pErrInfo->result = QM_IN_PROGRESS;
  1341. HRESULT Hr = S_OK;
  1342. try
  1343. {
  1344. CNestedImpersonation imp( hToken );
  1345. auto_ptr<URL_INFO> UrlInfo = auto_ptr<URL_INFO>( ConnectToUrl( szURL, pProxySettings, Credentials, (const WCHAR*)HostId, pErrInfo ));
  1346. if (!UrlInfo.get())
  1347. {
  1348. ASSERT( pErrInfo->IsSet() );
  1349. throw ComError( E_FAIL );
  1350. }
  1351. //
  1352. // Get file size and time stamp.
  1353. //
  1354. if (! GetRemoteResourceInformation( UrlInfo.get(), pErrInfo ))
  1355. {
  1356. ASSERT( pErrInfo->IsSet() );
  1357. throw ComError( E_FAIL );
  1358. }
  1359. *pFileTime = UrlInfo.get()->UrlModificationTime;
  1360. *pFileSize = UrlInfo.get()->FileSize;
  1361. pErrInfo->result = QM_FILE_DONE;
  1362. return S_OK;
  1363. }
  1364. catch( ComError Error )
  1365. {
  1366. Hr = Error.Error();
  1367. if (!pErrInfo->IsSet())
  1368. {
  1369. pErrInfo->Set( SOURCE_HTTP_UNKNOWN, ERROR_STYLE_HRESULT, Hr );
  1370. }
  1371. if (NeedRetry(pErrInfo))
  1372. {
  1373. pErrInfo->result = QM_FILE_TRANSIENT_ERROR;
  1374. }
  1375. else
  1376. {
  1377. pErrInfo->result = QM_FILE_FATAL_ERROR;
  1378. }
  1379. return E_FAIL;
  1380. }
  1381. return Hr;
  1382. }
  1383. void
  1384. CProgressiveDL::SetError(
  1385. ERROR_SOURCE Source,
  1386. ERROR_STYLE Style,
  1387. UINT64 Code,
  1388. char * comment
  1389. )
  1390. {
  1391. m_pQMInfo->Set( Source, Style, Code, comment );
  1392. }
  1393. void QMErrInfo::Log()
  1394. {
  1395. LogDl( "errinfo: result=%d, error style=%d, code=0x%x, source=%x, description='%S'",
  1396. result, (DWORD) Style, (DWORD) Code, (DWORD) Source, Description ? Description : L"" );
  1397. }
  1398. QMErrInfo::QMErrInfo(
  1399. ERROR_SOURCE Source,
  1400. ERROR_STYLE Style,
  1401. UINT64 Code,
  1402. char * comment
  1403. )
  1404. {
  1405. result = QM_FILE_TRANSIENT_ERROR;
  1406. Description = NULL;
  1407. Set( Source, Style, Code, comment );
  1408. }
  1409. void
  1410. QMErrInfo::Set(
  1411. ERROR_SOURCE Source,
  1412. ERROR_STYLE Style,
  1413. UINT64 Code,
  1414. char * comment
  1415. )
  1416. {
  1417. this->Source = Source;
  1418. this->Style = Style;
  1419. this->Code = Code;
  1420. LogWarning( " errinfo: error %s %s : style %d, source %x, code 0x%x",
  1421. comment ? "in" : "",
  1422. comment ? comment : "",
  1423. (DWORD) Style,
  1424. (DWORD) Source,
  1425. (DWORD) Code
  1426. );
  1427. }
  1428. HRESULT
  1429. OpenHttpRequest(
  1430. LPCTSTR Verb,
  1431. LPCTSTR Protocol,
  1432. URL_INFO & Info,
  1433. HINTERNET * phRequest
  1434. )
  1435. {
  1436. HINTERNET hRequest = 0;
  1437. *phRequest = 0;
  1438. try
  1439. {
  1440. LPCTSTR AcceptTypes[] = {_T("*/*"), NULL};
  1441. if (! (hRequest = HttpOpenRequest( Info.hConnect, Verb,
  1442. Info.UrlPath,
  1443. Protocol,
  1444. NULL, //referer
  1445. AcceptTypes,
  1446. Info.dwFlags,
  1447. 0))) //context
  1448. {
  1449. ThrowLastError();
  1450. }
  1451. #ifndef USE_WININET
  1452. //
  1453. // BUGBUG jroberts, 10-2-2001:
  1454. // WinHttp sometimes mistakenly thinks a site is outside the org, and disallows NTLM.
  1455. //
  1456. DWORD flag = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW;
  1457. if (!WinHttpSetOption( hRequest,
  1458. WINHTTP_OPTION_AUTOLOGON_POLICY,
  1459. &flag,
  1460. sizeof(DWORD)
  1461. ))
  1462. {
  1463. ThrowLastError();
  1464. }
  1465. #endif
  1466. *phRequest = hRequest;
  1467. return S_OK;
  1468. }
  1469. catch ( ComError err )
  1470. {
  1471. SafeCloseInternetHandle( hRequest );
  1472. return err.Error();
  1473. }
  1474. }
  1475. HRESULT
  1476. SendRequest(
  1477. HINTERNET hRequest,
  1478. URL_INFO * Info
  1479. )
  1480. {
  1481. DWORD err = 0;
  1482. PVOID Address = &err;
  1483. try
  1484. {
  1485. if (!WinHttpSetOption( hRequest,
  1486. WINHTTP_OPTION_CONTEXT_VALUE,
  1487. &Address,
  1488. sizeof(PVOID)
  1489. ))
  1490. {
  1491. err = GetLastError();
  1492. LogWarning( "can't set context option: %!winerr!", err );
  1493. throw ComError( HRESULT_FROM_WIN32( err ));
  1494. }
  1495. //
  1496. // Catch errors in the server certificate.
  1497. //
  1498. if (WINHTTP_INVALID_STATUS_CALLBACK == WinHttpSetStatusCallback( hRequest,
  1499. HttpRequestCallback,
  1500. WINHTTP_CALLBACK_FLAG_SECURE_FAILURE,
  1501. NULL
  1502. ))
  1503. {
  1504. err = GetLastError();
  1505. LogError("WinHttpSetStatusCallback failed %d", err );
  1506. throw ComError( HRESULT_FROM_WIN32( err ));
  1507. }
  1508. bool fProxyCredentials = false;
  1509. bool fServerCredentials = false;
  1510. retry:
  1511. err = 0;
  1512. RETURN_HRESULT( SetRequestProxy( hRequest, Info->ProxySettings ));
  1513. RETURN_HRESULT( SetRequestProxy( Info->hInternet, Info->ProxySettings ));
  1514. BOOL b = HttpSendRequest( hRequest, NULL, 0, NULL, 0 );
  1515. // err is modified by the callback routine if something was wrong with the server certificate
  1516. if (!b)
  1517. {
  1518. if (!err)
  1519. {
  1520. err = GetLastError();
  1521. }
  1522. LogError("SendRequest failed %d", err );
  1523. }
  1524. //
  1525. // If the proxy failed, try the next proxy in the list.
  1526. //
  1527. if (IsPossibleProxyFailure( err ))
  1528. {
  1529. if (Info->ProxySettings->UseNextProxy())
  1530. {
  1531. fProxyCredentials = false;
  1532. goto retry;
  1533. }
  1534. throw ComError( HRESULT_FROM_WIN32( err ));
  1535. }
  1536. //
  1537. // If the request wasn't sent or the security callback routine reported an error, fail.
  1538. //
  1539. if (err)
  1540. {
  1541. throw ComError( HRESULT_FROM_WIN32( err ));
  1542. }
  1543. //
  1544. // If the server or proxy server asked for auth information and we haven't already set it,
  1545. // find matching credentials and try again.
  1546. //
  1547. switch (GetRequestStatus( hRequest ))
  1548. {
  1549. case HTTP_STATUS_PROXY_AUTH_REQ:
  1550. {
  1551. LogInfo("server returned HTTP_STATUS_PROXY_AUTH_REQ" );
  1552. if (!fProxyCredentials && ApplyCredentials( hRequest, Info->Credentials, Info->UserName, Info->Password ))
  1553. {
  1554. fProxyCredentials = true;
  1555. goto retry;
  1556. }
  1557. else
  1558. {
  1559. // return S_OK and the caller will find the status code
  1560. }
  1561. break;
  1562. }
  1563. case HTTP_STATUS_DENIED:
  1564. {
  1565. LogInfo("server returned HTTP_STATUS_DENIED");
  1566. if (!fServerCredentials && ApplyCredentials( hRequest, Info->Credentials, Info->UserName, Info->Password ))
  1567. {
  1568. fServerCredentials = true;
  1569. goto retry;
  1570. }
  1571. else
  1572. {
  1573. // return S_OK and the caller will find the status code
  1574. }
  1575. break;
  1576. }
  1577. }
  1578. return S_OK;
  1579. }
  1580. catch ( ComError err )
  1581. {
  1582. return err.Error();
  1583. }
  1584. }
  1585. HRESULT
  1586. GetResponseVersion(
  1587. HINTERNET hRequest,
  1588. unsigned * MajorVersion,
  1589. unsigned * MinorVersion
  1590. )
  1591. {
  1592. HRESULT hr;
  1593. CAutoString Value;
  1594. wchar_t Template[] = L"HTTP/%u.%u";
  1595. const MaxChars = RTL_NUMBER_OF( Template ) + INT_DIGITS + INT_DIGITS;
  1596. hr = GetRequestHeader( hRequest,
  1597. WINHTTP_QUERY_VERSION,
  1598. WINHTTP_HEADER_NAME_BY_INDEX,
  1599. Value,
  1600. MaxChars
  1601. );
  1602. if (FAILED(hr))
  1603. {
  1604. LogError("error %x retrieving the response version", hr);
  1605. return BG_E_INSUFFICIENT_RANGE_SUPPORT;
  1606. }
  1607. if (hr == S_FALSE)
  1608. {
  1609. LogError("no response version!");
  1610. return BG_E_INSUFFICIENT_RANGE_SUPPORT;
  1611. }
  1612. if (2 != swscanf(Value.get(), Template, MajorVersion, MinorVersion ))
  1613. {
  1614. LogError("invalid response version");
  1615. return BG_E_INSUFFICIENT_RANGE_SUPPORT;
  1616. }
  1617. LogInfo("server HTTP version is %d.%d", *MajorVersion, *MinorVersion );
  1618. return S_OK;
  1619. }
  1620. HRESULT
  1621. CheckReplyLength(
  1622. HINTERNET hRequest,
  1623. UINT64 CorrectOffset,
  1624. UINT64 CorrectTotal
  1625. )
  1626. {
  1627. HRESULT hr;
  1628. UINT64 ReplyTotal;
  1629. CAutoString Value;
  1630. if (CorrectOffset != 0)
  1631. {
  1632. LogError( "received a 200 reply when the requested offset is nonzero: %I64d", CorrectOffset );
  1633. return BG_E_INSUFFICIENT_RANGE_SUPPORT;
  1634. }
  1635. wchar_t Template[] = L"%I64d";
  1636. const MaxChars = RTL_NUMBER_OF( Template ) + INT64_DIGITS;
  1637. hr = GetRequestHeader( hRequest,
  1638. WINHTTP_QUERY_CONTENT_LENGTH,
  1639. WINHTTP_HEADER_NAME_BY_INDEX,
  1640. Value,
  1641. MaxChars
  1642. );
  1643. if (FAILED(hr))
  1644. {
  1645. LogError("error %x retrieving the content-length header", hr);
  1646. return hr;
  1647. }
  1648. if (hr == S_FALSE)
  1649. {
  1650. LogWarning("no content-length header");
  1651. return S_FALSE;
  1652. }
  1653. if (1 != swscanf(Value.get(), Template, &ReplyTotal))
  1654. {
  1655. LogError("invalid content-length header");
  1656. return BG_E_INSUFFICIENT_RANGE_SUPPORT;
  1657. }
  1658. if (ReplyTotal != CorrectTotal)
  1659. {
  1660. LogError("incorrect content-length header: %S", Value.get());
  1661. return BG_E_INSUFFICIENT_RANGE_SUPPORT;
  1662. }
  1663. return S_OK;
  1664. }
  1665. HRESULT
  1666. CheckReplyRange(
  1667. HINTERNET hRequest,
  1668. UINT64 CorrectStart,
  1669. UINT64 CorrectEnd,
  1670. UINT64 CorrectTotal
  1671. )
  1672. {
  1673. HRESULT hr;
  1674. UINT64 RangeStart;
  1675. UINT64 RangeEnd;
  1676. UINT64 RangeTotal;
  1677. CAutoString Value;
  1678. wchar_t Template[] = L"bytes %I64d-%I64d/%I64d";
  1679. const MaxChars = RTL_NUMBER_OF( Template ) + INT64_DIGITS + INT64_DIGITS + INT64_DIGITS;
  1680. hr = GetRequestHeader( hRequest,
  1681. WINHTTP_QUERY_CONTENT_RANGE,
  1682. WINHTTP_HEADER_NAME_BY_INDEX,
  1683. Value,
  1684. MaxChars
  1685. );
  1686. if (FAILED(hr))
  1687. {
  1688. LogError("error %x retrieving the content-range header", hr);
  1689. return hr;
  1690. }
  1691. if (hr == S_FALSE)
  1692. {
  1693. LogWarning("no reply range header");
  1694. return S_FALSE;
  1695. }
  1696. if (3 != swscanf(Value.get(), Template, &RangeStart, &RangeEnd, &RangeTotal))
  1697. {
  1698. LogError("invalid reply range header");
  1699. return BG_E_INSUFFICIENT_RANGE_SUPPORT;
  1700. }
  1701. if (RangeStart != CorrectStart ||
  1702. RangeEnd != CorrectEnd ||
  1703. RangeTotal != CorrectTotal)
  1704. {
  1705. LogError("incorrect reply range header: %I64d-%I64d/%I64d", RangeStart, RangeEnd, RangeTotal);
  1706. return BG_E_INSUFFICIENT_RANGE_SUPPORT;
  1707. }
  1708. return S_OK;
  1709. }
  1710. HRESULT
  1711. GetRequestHeader(
  1712. HINTERNET hRequest,
  1713. DWORD HeaderIndex,
  1714. LPCWSTR HeaderName,
  1715. CAutoString & Destination,
  1716. size_t MaxChars
  1717. )
  1718. /*
  1719. Fetch an arbitrary header's value from the request, allocating a string to hold it.
  1720. Input:
  1721. HeaderIndex and HeaderName follow the rules for WinHttpQueryHeaders() parameters
  1722. dwInfoLevel and pwszName respectively.
  1723. Returns:
  1724. S_OK: header found, and Destination holds the value.
  1725. S_FALSE: header not found
  1726. all others: an error occurred along the way
  1727. */
  1728. {
  1729. try
  1730. {
  1731. DWORD s;
  1732. HRESULT hr;
  1733. DWORD ValueLength;
  1734. CAutoString Value;
  1735. WinHttpQueryHeaders( hRequest, HeaderIndex, HeaderName, NULL, &ValueLength, WINHTTP_NO_HEADER_INDEX );
  1736. s = GetLastError();
  1737. if (s == ERROR_WINHTTP_HEADER_NOT_FOUND)
  1738. {
  1739. return S_FALSE;
  1740. }
  1741. if (s != ERROR_INSUFFICIENT_BUFFER)
  1742. {
  1743. return HRESULT_FROM_WIN32( s );
  1744. }
  1745. if (ValueLength > ((MaxChars+1) * sizeof(wchar_t)))
  1746. {
  1747. return E_FAIL;
  1748. }
  1749. Value = CAutoString( new wchar_t[ ValueLength ] );
  1750. if (!WinHttpQueryHeaders( hRequest, HeaderIndex, HeaderName, Value.get(), &ValueLength, WINHTTP_NO_HEADER_INDEX ))
  1751. {
  1752. return HRESULT_FROM_WIN32( GetLastError() );
  1753. }
  1754. Destination = Value;
  1755. return S_OK;
  1756. }
  1757. catch ( ComError err )
  1758. {
  1759. return err.Error();
  1760. }
  1761. }
  1762. HRESULT
  1763. AddRangeHeader(
  1764. HINTERNET hRequest,
  1765. UINT64 Start,
  1766. UINT64 End
  1767. )
  1768. {
  1769. static const TCHAR RangeTemplate[] =_T("Range: bytes=%I64d-%I64d\r\n");
  1770. HRESULT hr;
  1771. TCHAR szHeader[ RTL_NUMBER_OF(RangeTemplate) + INT64_DIGITS + INT64_DIGITS ];
  1772. hr = StringCbPrintf(szHeader, sizeof(szHeader), RangeTemplate, Start, End);
  1773. if (FAILED(hr))
  1774. {
  1775. LogError( "range header is too large for its buffer. start %I64d, end %I64d", Start, End );
  1776. return hr;
  1777. }
  1778. if (! HttpAddRequestHeaders( hRequest, szHeader, -1L, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE))
  1779. {
  1780. return HRESULT_FROM_WIN32( GetLastError() );
  1781. }
  1782. return S_OK;
  1783. }
  1784. HRESULT
  1785. AddIf_Unmodified_SinceHeader(
  1786. HINTERNET hRequest,
  1787. const FILETIME &Time
  1788. )
  1789. {
  1790. const TCHAR szIfModifiedTemplate[] = _T("If-Unmodified-Since: %s\r\n");
  1791. static TCHAR szIfModifiedHeader[ (sizeof(szIfModifiedTemplate) / sizeof(TCHAR)) + INTERNET_RFC1123_BUFSIZE*2 ];
  1792. static TCHAR szIfModifiedTime[ INTERNET_RFC1123_BUFSIZE*2 ];
  1793. HRESULT hr;
  1794. SYSTEMTIME stFileCreationTime;
  1795. if ( !FileTimeToSystemTime( &Time, &stFileCreationTime ) )
  1796. {
  1797. return HRESULT_FROM_WIN32( GetLastError() );
  1798. }
  1799. if ( !InternetTimeFromSystemTime( &stFileCreationTime, INTERNET_RFC1123_FORMAT, szIfModifiedTime,
  1800. sizeof( szIfModifiedTime ) ) )
  1801. {
  1802. return HRESULT_FROM_WIN32( GetLastError() );
  1803. }
  1804. hr = StringCbPrintf( szIfModifiedHeader, sizeof(szIfModifiedHeader), szIfModifiedTemplate, szIfModifiedTime );
  1805. if (FAILED(hr))
  1806. {
  1807. return HRESULT_FROM_WIN32( GetLastError() );
  1808. }
  1809. if (! HttpAddRequestHeaders( hRequest, szIfModifiedHeader, -1L, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE ))
  1810. {
  1811. return HRESULT_FROM_WIN32( GetLastError() );
  1812. }
  1813. return S_OK;
  1814. }
  1815. bool
  1816. ApplyCredentials(
  1817. HINTERNET hRequest,
  1818. const CCredentialsContainer * Credentials,
  1819. WCHAR UserName[],
  1820. WCHAR Password[]
  1821. )
  1822. {
  1823. HRESULT hr;
  1824. DWORD dwSupportedSchemes;
  1825. DWORD dwPreferredScheme;
  1826. DWORD dwTarget;
  1827. if (!WinHttpQueryAuthSchemes( hRequest,
  1828. &dwSupportedSchemes,
  1829. &dwPreferredScheme,
  1830. &dwTarget ))
  1831. {
  1832. if (GetLastError() == ERROR_INVALID_OPERATION)
  1833. {
  1834. // no schemes available at all
  1835. LogWarning("the server listed no auth schemes");
  1836. return false;
  1837. }
  1838. ThrowLastError();
  1839. }
  1840. LogInfo("target %d, preferred scheme %x, supported schemes %x", dwTarget, dwPreferredScheme, dwSupportedSchemes );
  1841. //
  1842. // First look for credentials supporting the preferred scheme.
  1843. //
  1844. if (ApplySchemeCredentials( hRequest, dwTarget, dwPreferredScheme, Credentials, UserName, Password ))
  1845. {
  1846. return true;
  1847. }
  1848. //
  1849. // Look for any other credential scheme supported by both sides.
  1850. //
  1851. signed bit;
  1852. for (bit=31; bit >= 0; --bit)
  1853. {
  1854. DWORD dwScheme = (1 << bit);
  1855. if (0 != (dwSupportedSchemes & dwScheme))
  1856. {
  1857. if (ApplySchemeCredentials( hRequest, dwTarget, dwScheme, Credentials, UserName, Password ))
  1858. {
  1859. return true;
  1860. }
  1861. }
  1862. }
  1863. //
  1864. // No matching security credential.
  1865. //
  1866. return false;
  1867. }
  1868. DWORD GetRequestStatus( HINTERNET hRequest )
  1869. {
  1870. DWORD Status;
  1871. DWORD dwLength = sizeof(Status);
  1872. if (! HttpQueryInfo( hRequest,
  1873. HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
  1874. (LPVOID)&Status,
  1875. &dwLength,
  1876. NULL))
  1877. {
  1878. ThrowLastError();
  1879. }
  1880. return Status;
  1881. }
  1882. bool
  1883. SchemeFromWinHttp(
  1884. DWORD Scheme,
  1885. BG_AUTH_SCHEME * pScheme
  1886. )
  1887. {
  1888. switch (Scheme)
  1889. {
  1890. case WINHTTP_AUTH_SCHEME_BASIC: *pScheme = BG_AUTH_SCHEME_BASIC; return true;
  1891. case WINHTTP_AUTH_SCHEME_DIGEST: *pScheme = BG_AUTH_SCHEME_DIGEST; return true;
  1892. case WINHTTP_AUTH_SCHEME_NTLM: *pScheme = BG_AUTH_SCHEME_NTLM; return true;
  1893. case WINHTTP_AUTH_SCHEME_NEGOTIATE: *pScheme = BG_AUTH_SCHEME_NEGOTIATE; return true;
  1894. case WINHTTP_AUTH_SCHEME_PASSPORT: *pScheme = BG_AUTH_SCHEME_PASSPORT; return true;
  1895. default:
  1896. LogWarning("unknown WinHttp scheme 0x%x", Scheme );
  1897. return false;
  1898. }
  1899. }
  1900. BG_AUTH_TARGET TargetFromWinHttp( DWORD Target )
  1901. {
  1902. if (Target == WINHTTP_AUTH_TARGET_PROXY)
  1903. {
  1904. return BG_AUTH_TARGET_PROXY;
  1905. }
  1906. if (Target == WINHTTP_AUTH_TARGET_SERVER)
  1907. {
  1908. return BG_AUTH_TARGET_SERVER;
  1909. }
  1910. LogWarning("unknown WinHttp target 0x%x", Target );
  1911. ASSERT( 0 );
  1912. return BG_AUTH_TARGET_SERVER;
  1913. }
  1914. bool
  1915. ApplySchemeCredentials(
  1916. HINTERNET hRequest,
  1917. DWORD dwTarget,
  1918. DWORD dwScheme,
  1919. const CCredentialsContainer * Credentials,
  1920. WCHAR UserName[],
  1921. WCHAR Password[]
  1922. )
  1923. {
  1924. BG_AUTH_TARGET BitsTarget;
  1925. BG_AUTH_SCHEME BitsScheme;
  1926. BG_AUTH_CREDENTIALS * cred = 0;
  1927. BitsTarget = TargetFromWinHttp( dwTarget );
  1928. //
  1929. // Translate the scheme into the BITS ID and see if a matching credential is available.
  1930. //
  1931. if (!SchemeFromWinHttp( dwScheme, &BitsScheme ))
  1932. {
  1933. // BITS doesn't understand this scheme.
  1934. return false;
  1935. }
  1936. if (BitsScheme == BG_AUTH_SCHEME_BASIC && UserName && UserName[0])
  1937. {
  1938. // use credentials embedded in URL
  1939. //
  1940. }
  1941. else
  1942. {
  1943. HRESULT hr;
  1944. THROW_HRESULT( hr = Credentials->Find( BitsTarget, BitsScheme, &cred ));
  1945. if (hr != S_OK)
  1946. {
  1947. // no credential available for this scheme.
  1948. return false;
  1949. }
  1950. // use the credential in the dictionary.
  1951. //
  1952. UserName = cred->Credentials.Basic.UserName;
  1953. Password = cred->Credentials.Basic.Password;
  1954. }
  1955. //
  1956. // Apply the credentials we found.
  1957. //
  1958. LogInfo("found credentials for scheme %d", BitsScheme );
  1959. if (!WinHttpSetCredentials( hRequest,
  1960. dwTarget,
  1961. dwScheme,
  1962. UserName,
  1963. Password,
  1964. NULL
  1965. ))
  1966. {
  1967. if (cred)
  1968. {
  1969. ScrubCredentials( *cred );
  1970. }
  1971. ThrowLastError();
  1972. }
  1973. if (cred)
  1974. {
  1975. ScrubCredentials( *cred );
  1976. }
  1977. return true;
  1978. }
  1979. #ifndef USE_WININET
  1980. DWORD
  1981. MapSecureHttpErrorCode(
  1982. DWORD flags
  1983. )
  1984. {
  1985. if (flags & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT)
  1986. {
  1987. return ERROR_INTERNET_SEC_CERT_ERRORS;
  1988. }
  1989. if (flags & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA)
  1990. {
  1991. return ERROR_INTERNET_INVALID_CA;
  1992. }
  1993. if (flags & WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID)
  1994. {
  1995. return ERROR_INTERNET_SEC_CERT_CN_INVALID;
  1996. }
  1997. if (flags & WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID)
  1998. {
  1999. return ERROR_INTERNET_SEC_CERT_DATE_INVALID;
  2000. }
  2001. if (flags & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED)
  2002. {
  2003. return ERROR_ACCESS_DENIED;
  2004. }
  2005. if (flags & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED)
  2006. {
  2007. return ERROR_INTERNET_SEC_CERT_REV_FAILED;
  2008. }
  2009. if (flags & WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR)
  2010. {
  2011. return ERROR_INTERNET_INTERNAL_ERROR;
  2012. }
  2013. ASSERT( flags );
  2014. if (flags)
  2015. {
  2016. return ERROR_ACCESS_DENIED;
  2017. }
  2018. return 0;
  2019. }
  2020. VOID CALLBACK
  2021. HttpRequestCallback(
  2022. IN HINTERNET hInternet,
  2023. IN DWORD_PTR dwContext,
  2024. IN DWORD dwInternetStatus,
  2025. IN LPVOID lpvStatusInformation OPTIONAL,
  2026. IN DWORD dwStatusInformationLength
  2027. )
  2028. {
  2029. switch (dwInternetStatus)
  2030. {
  2031. case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE:
  2032. {
  2033. DWORD * pErr = LPDWORD( dwContext );
  2034. DWORD * pFlags = LPDWORD( lpvStatusInformation );
  2035. ASSERT( pErr != NULL );
  2036. LogWarning("SSL error: flags %x", *pFlags );
  2037. *pErr = MapSecureHttpErrorCode( *pFlags );
  2038. break;
  2039. }
  2040. default:
  2041. LogWarning("bogus HTTP notification %x", dwInternetStatus );
  2042. break;
  2043. }
  2044. }
  2045. #endif