Leaked source code of windows server 2003
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.

752 lines
18 KiB

  1. #include "inspch.h"
  2. #include "util2.h"
  3. #include "download.h"
  4. #define BUFFERSIZE 4096
  5. char g_szBuffer[BUFFERSIZE];
  6. #define TIMEOUT_PERIOD 120
  7. #define PATCHWIN9xKEY "SOFTWARE\\Microsoft\\Advanced INF Setup"
  8. //=--------------------------------------------------------------------------=
  9. // Function name here
  10. //=--------------------------------------------------------------------------=
  11. // Function description
  12. //
  13. // Parameters:
  14. //
  15. // Returns:
  16. //
  17. // Notes:
  18. //
  19. CDownloader::CDownloader() : CTimeTracker(0)
  20. {
  21. _pCb = NULL;
  22. _pBnd = NULL;
  23. _cRef = 1;
  24. _pStm = NULL;
  25. _pMkr = NULL;
  26. _uFlags = NULL;
  27. _hDL = CreateEvent(NULL, TRUE, FALSE, NULL);
  28. DllAddRef();
  29. }
  30. //=--------------------------------------------------------------------------=
  31. // Function name here
  32. //=--------------------------------------------------------------------------=
  33. // Function description
  34. //
  35. // Parameters:
  36. //
  37. // Returns:
  38. //
  39. // Notes:
  40. //
  41. CDownloader::~CDownloader()
  42. {
  43. if(_hDL)
  44. CloseHandle(_hDL);
  45. DllRelease();
  46. }
  47. //=--------------------------------------------------------------------------=
  48. // Function name here
  49. //=--------------------------------------------------------------------------=
  50. // Function description
  51. //
  52. // Parameters:
  53. //
  54. // Returns:
  55. //
  56. // Notes:
  57. //
  58. STDMETHODIMP CDownloader::QueryInterface(const GUID &riid,void **ppv )
  59. {
  60. *ppv = NULL ;
  61. if( IsEqualGUID(riid,IID_IUnknown) ) {
  62. *ppv = (IUnknown *) (IBindStatusCallback *)this;
  63. } else if (IsEqualGUID(riid,IID_IBindStatusCallback) ) {
  64. *ppv = (IBindStatusCallback *) this;
  65. } else if (IsEqualGUID(riid, IID_IAuthenticate))
  66. *ppv = (IAuthenticate *) this;
  67. if (*ppv)
  68. {
  69. // increment our reference count before we hand out our interface
  70. ((LPUNKNOWN)*ppv)->AddRef();
  71. return(NOERROR);
  72. }
  73. return( E_NOINTERFACE );
  74. }
  75. //=--------------------------------------------------------------------------=
  76. // Function name here
  77. //=--------------------------------------------------------------------------=
  78. // Function description
  79. //
  80. // Parameters:
  81. //
  82. // Returns:
  83. //
  84. // Notes:
  85. //
  86. STDMETHODIMP_(ULONG) CDownloader::AddRef()
  87. {
  88. return(++_cRef);
  89. }
  90. //=--------------------------------------------------------------------------=
  91. // Function name here
  92. //=--------------------------------------------------------------------------=
  93. // Function description
  94. //
  95. // Parameters:
  96. //
  97. // Returns:
  98. //
  99. // Notes:
  100. //
  101. STDMETHODIMP_(ULONG) CDownloader::Release()
  102. {
  103. if(!--_cRef)
  104. {
  105. delete this;
  106. return(0);
  107. }
  108. return( _cRef );
  109. }
  110. //=--------------------------------------------------------------------------=
  111. // Function name here
  112. //=--------------------------------------------------------------------------=
  113. // Function description
  114. //
  115. // Parameters:
  116. //
  117. // Returns:
  118. //
  119. // Notes:
  120. //
  121. STDMETHODIMP CDownloader::GetBindInfo( DWORD *grfBINDF, BINDINFO *pbindInfo)
  122. {
  123. // clear BINDINFO but keep its size
  124. DWORD cbSize = pbindInfo->cbSize;
  125. ZeroMemory( pbindInfo, cbSize );
  126. pbindInfo->cbSize = cbSize;
  127. *grfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA | BINDF_RESYNCHRONIZE | BINDF_PREFERDEFAULTHANDLER;
  128. pbindInfo->dwBindVerb = BINDVERB_GET;
  129. return(NOERROR);
  130. }
  131. //=--------------------------------------------------------------------------=
  132. // Function name here
  133. //=--------------------------------------------------------------------------=
  134. // Function description
  135. //
  136. // Parameters:
  137. //
  138. // Returns:
  139. //
  140. // Notes:
  141. //
  142. STDMETHODIMP CDownloader::OnStartBinding(DWORD /*grfBSCOption*/,IBinding *p)
  143. {
  144. // BUGBUG: should check to see options are what we think they are
  145. EnterCriticalSection(&g_cs);
  146. _pBnd = p;
  147. _pBnd->AddRef();
  148. LeaveCriticalSection(&g_cs);
  149. return(NOERROR);
  150. }
  151. //=--------------------------------------------------------------------------=
  152. // Function name here
  153. //=--------------------------------------------------------------------------=
  154. // Function description
  155. //
  156. // Parameters:
  157. //
  158. // Returns:
  159. //
  160. // Notes:
  161. //
  162. STDMETHODIMP CDownloader::GetPriority(LONG *pnPriority)
  163. {
  164. return(E_NOTIMPL);
  165. }
  166. //=--------------------------------------------------------------------------=
  167. // Function name here
  168. //=--------------------------------------------------------------------------=
  169. // Function description
  170. //
  171. // Parameters:
  172. //
  173. // Returns:
  174. //
  175. // Notes:
  176. //
  177. STDMETHODIMP CDownloader::OnProgress(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR pwzStatusText)
  178. {
  179. IndicateWinsockActivity();
  180. return NOERROR;
  181. }
  182. //=--------------------------------------------------------------------------=
  183. // Function name here
  184. //=--------------------------------------------------------------------------=
  185. // Function description
  186. //
  187. // Parameters:
  188. //
  189. // Returns:
  190. //
  191. // Notes:
  192. //
  193. STDMETHODIMP CDownloader::OnDataAvailable(DWORD grfBSCF, DWORD dwSize, FORMATETC *pFmtetc, STGMEDIUM *pstgmed)
  194. {
  195. // bring in major changes here
  196. HRESULT hr = NOERROR;
  197. DWORD dwRead = 0;
  198. DWORD dwReadThisCall = 0;
  199. DWORD dwWritten = 0;
  200. if(!_pStm)
  201. {
  202. _pStm = pstgmed->pstm;
  203. _pStm->AddRef();
  204. }
  205. // should ignore WAIT_TIMEOUT while getting bytes from urlmon
  206. _fTimeoutValid = FALSE;
  207. do
  208. {
  209. hr = _pStm->Read(g_szBuffer, BUFFERSIZE, &dwRead);
  210. if( SUCCEEDED(hr) || ( (hr == E_PENDING) && (dwRead > 0) ) )
  211. {
  212. if(_hFile)
  213. if(WriteFile(_hFile, g_szBuffer, dwRead, &dwWritten, NULL))
  214. {
  215. _uBytesSoFar += dwRead;
  216. dwReadThisCall += dwRead;
  217. if(_pCb)
  218. _pCb->OnProgress(_uBytesSoFar >> 10, NULL);
  219. }
  220. else
  221. {
  222. hr = E_FAIL;
  223. Abort();
  224. }
  225. }
  226. } while (hr == NOERROR);
  227. // SetInstallBytes
  228. SetBytes(dwReadThisCall, TRUE);
  229. _uTickCount = 0;
  230. _fTimeoutValid = TRUE; // should increment dwTickCount if WAIT_TIMEOUT occurs now
  231. return NOERROR;
  232. }
  233. //=--------------------------------------------------------------------------=
  234. // Function name here
  235. //=--------------------------------------------------------------------------=
  236. // Function description
  237. //
  238. // Parameters:
  239. //
  240. // Returns:
  241. //
  242. // Notes:
  243. //
  244. STDMETHODIMP CDownloader::OnObjectAvailable(REFIID riid, IUnknown *punk)
  245. {
  246. return(E_NOTIMPL);
  247. }
  248. //=--------------------------------------------------------------------------=
  249. // Function name here
  250. //=--------------------------------------------------------------------------=
  251. // Function description
  252. //
  253. // Parameters:
  254. //
  255. // Returns:
  256. //
  257. // Notes:
  258. //
  259. STDMETHODIMP CDownloader::OnLowResource(DWORD reserved)
  260. {
  261. // BUGBUG: really should have this kind of harsh policy on this ...
  262. _pBnd->Abort();
  263. return(S_OK);
  264. }
  265. //=--------------------------------------------------------------------------=
  266. // Function name here
  267. //=--------------------------------------------------------------------------=
  268. // Function description
  269. //
  270. // Parameters:
  271. //
  272. // Returns:
  273. //
  274. // Notes:
  275. //
  276. STDMETHODIMP CDownloader::OnStopBinding(HRESULT hrError, LPCWSTR szError)
  277. {
  278. _fTimeoutValid = FALSE;
  279. StopClock();
  280. if((hrError == E_ABORT) && _fTimeout)
  281. {
  282. // This is the timeout case
  283. _hDLResult = INET_E_CONNECTION_TIMEOUT;
  284. }
  285. else
  286. {
  287. // this is all other cases
  288. _hDLResult = hrError;
  289. }
  290. SetEvent(_hDL);
  291. return(NOERROR);
  292. }
  293. /* IAuthenticate::Authenticate
  294. */
  295. STDMETHODIMP CDownloader::Authenticate(HWND *phwnd,
  296. LPWSTR *pszUserName, LPWSTR *pszPassword)
  297. {
  298. if (!phwnd || !pszUserName || !pszPassword)
  299. return E_POINTER;
  300. *pszUserName = NULL;
  301. *pszPassword = NULL;
  302. // BUGBUG: Need to have our own window! NULL does not work!
  303. // *phwnd = NULL;
  304. *phwnd = GetDesktopWindow() ;
  305. return S_OK;
  306. }
  307. //=--------------------------------------------------------------------------=
  308. // Function name here
  309. //=--------------------------------------------------------------------------=
  310. // Function description
  311. //
  312. // Parameters:
  313. //
  314. // Returns:
  315. //
  316. // Notes:
  317. //
  318. HRESULT GetAMoniker( LPOLESTR url, IMoniker ** ppmkr )
  319. {
  320. // FUTURE: This really should be a call to MkParseDisplayNameEx!!!
  321. HRESULT hr = CreateURLMoniker(0,url,ppmkr);
  322. // hr = ::MkParseDisplayNameEx(0, url, 0, ppmkr);
  323. return( hr );
  324. }
  325. //=--------------------------------------------------------------------------=
  326. // Function name here
  327. //=--------------------------------------------------------------------------=
  328. // Function description
  329. //
  330. // Parameters:
  331. //
  332. // Returns:
  333. //
  334. // Notes:
  335. //
  336. HRESULT CDownloader::SetupDownload(LPCSTR pszUrl, IMyDownloadCallback *pcb, DWORD dwFlags, LPCSTR pszFilenameToUse)
  337. {
  338. LPOLESTR pwszUrl;
  339. LPSTR pszFilename;
  340. if(!pszUrl) return E_INVALIDARG;
  341. lstrcpyn(_szURL, pszUrl, INTERNET_MAX_URL_LENGTH);
  342. pwszUrl = OLESTRFROMANSI(pszUrl);
  343. if(!pwszUrl) return E_OUTOFMEMORY;
  344. IMoniker *ptmpmkr;
  345. HRESULT hr = GetAMoniker( pwszUrl, &ptmpmkr );
  346. IBindCtx * pBindCtx = 0;
  347. if( SUCCEEDED(hr) )
  348. {
  349. if(SUCCEEDED(::CreateBindCtx(0,&pBindCtx)))
  350. hr = ::RegisterBindStatusCallback(pBindCtx, (IBindStatusCallback *) this, 0, 0) ;
  351. }
  352. if( SUCCEEDED(hr) )
  353. {
  354. AddRef();
  355. // setup path for download
  356. if(FAILED( CreateTempDirOnMaxDrive(_szDest, sizeof(_szDest))))
  357. goto GetOut;
  358. if(pszFilenameToUse)
  359. {
  360. SafeAddPath(_szDest, pszFilenameToUse, sizeof(_szDest));
  361. }
  362. else
  363. {
  364. pszFilename = ParseURLA(pszUrl);
  365. SafeAddPath(_szDest, pszFilename, sizeof(_szDest));
  366. }
  367. _pMkr = ptmpmkr;
  368. _pCb = pcb;
  369. _uFlags = dwFlags;
  370. _pBndContext = pBindCtx;
  371. _fTimeout = FALSE;
  372. _fTimeoutValid = TRUE;
  373. _uBytesSoFar = 0;
  374. _uTickCount = 0;
  375. _pStm = 0;
  376. }
  377. GetOut:
  378. if(pwszUrl)
  379. CoTaskMemFree(pwszUrl);
  380. return hr;
  381. }
  382. HRESULT CDownloader::DoDownload(LPSTR pszPath, DWORD dwBufSize)
  383. {
  384. HRESULT hr = NOERROR;
  385. BOOL fQuit = FALSE;
  386. DWORD dwRet;
  387. if(!_pMkr)
  388. return E_UNEXPECTED;
  389. pszPath[0] = 0;
  390. StartClock();
  391. // Create the file
  392. _hFile = CreateFile(_szDest, GENERIC_READ | GENERIC_WRITE, 0, NULL,
  393. CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  394. if(_hFile == INVALID_HANDLE_VALUE)
  395. hr = E_FAIL;
  396. if( SUCCEEDED(hr) )
  397. hr = _pMkr->BindToStorage( _pBndContext, 0, IID_IStream, (void**)&_pStm );
  398. // we need this here because it synchronus *FAIL* case,
  399. // we Set the event in onstopbinding, but we skip the loop below so it
  400. // never gets reset.
  401. // If BindToStorage fails without even sending onstopbinding, we are resetting
  402. // an unsignalled event, which is OK.
  403. if(FAILED(hr))
  404. ResetEvent(_hDL);
  405. _pBndContext->Release();
  406. _pBndContext = 0;
  407. // here we wait for Bind to complete
  408. //Wait for download event or abort
  409. while(SUCCEEDED(hr) && !fQuit)
  410. {
  411. dwRet = MsgWaitForMultipleObjects(1, &_hDL, FALSE, 1000, QS_ALLINPUT);
  412. if(dwRet == WAIT_OBJECT_0)
  413. {
  414. // Download is finished
  415. hr = _hDLResult;
  416. ResetEvent(_hDL);
  417. break;
  418. }
  419. else if(dwRet == WAIT_TIMEOUT) // our wait has expired
  420. {
  421. if(_fTimeoutValid)
  422. _uTickCount++;
  423. // if our tick count is past threshold, abort the download
  424. // BUGBUG: What about synch. case? We can't time out
  425. if(_uTickCount >= TIMEOUT_PERIOD)
  426. {
  427. _fTimeout = TRUE;
  428. Abort();
  429. }
  430. }
  431. else
  432. {
  433. MSG msg;
  434. // read all of the messages in this next loop
  435. // removing each message as we read it
  436. while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  437. {
  438. // if it's a quit message we're out of here
  439. if (msg.message == WM_QUIT)
  440. fQuit = TRUE;
  441. else
  442. {
  443. // otherwise dispatch it
  444. DispatchMessage(&msg);
  445. } // end of PeekMessage while loop
  446. }
  447. }
  448. }
  449. // clean up all our stuff
  450. if(_hFile != INVALID_HANDLE_VALUE)
  451. CloseHandle(_hFile);
  452. _hFile = INVALID_HANDLE_VALUE;
  453. // if we are not using cache and download succeeded, delete from cache
  454. if(SUCCEEDED(hr) && !(_uFlags & DOWNLOADFLAGS_USEWRITECACHE))
  455. DeleteUrlCacheEntry(_szURL);
  456. if(FAILED(hr))
  457. {
  458. GetParentDir(_szDest);
  459. DelNode(_szDest,0);
  460. }
  461. EnterCriticalSection(&g_cs);
  462. if(_pBnd)
  463. {
  464. _pBnd->Release();
  465. _pBnd = 0;
  466. }
  467. LeaveCriticalSection(&g_cs);
  468. _pCb = 0;
  469. if(_pStm)
  470. {
  471. _pStm->Release();
  472. _pStm = 0;
  473. }
  474. if(SUCCEEDED(hr))
  475. lstrcpyn(pszPath, _szDest, dwBufSize);
  476. _szDest[0] = 0;
  477. _szURL[0] = 0;
  478. Release();
  479. return hr;
  480. }
  481. HRESULT CDownloader::Suspend()
  482. {
  483. // in theory, we could call _pBnd->Suspend here
  484. return NOERROR;
  485. }
  486. HRESULT CDownloader::Resume()
  487. {
  488. // in theory, we could call _pBnd->Resume here
  489. return NOERROR;
  490. }
  491. HRESULT CDownloader::Abort()
  492. {
  493. EnterCriticalSection(&g_cs);
  494. if(_pBnd)
  495. {
  496. _pBnd->Abort();
  497. }
  498. LeaveCriticalSection(&g_cs);
  499. return NOERROR;
  500. }
  501. //=--------------------------------------------------------------------------=
  502. // Function name here
  503. //=--------------------------------------------------------------------------=
  504. // Function description
  505. //
  506. // Parameters:
  507. //
  508. // Returns:
  509. //
  510. // Notes:
  511. //
  512. /*
  513. HRESULT GetAMoniker( LPOLESTR url, IMoniker ** ppmkr )
  514. {
  515. // FUTURE: This really should be a call to MkParseDisplayNameEx!!!
  516. HRESULT hr = CreateURLMoniker(0,url,ppmkr);
  517. // hr = ::MkParseDisplayNameEx(0, url, 0, ppmkr);
  518. return( hr );
  519. }
  520. */
  521. CPatchDownloader::CPatchDownloader(BOOL fEnable=FALSE) : CTimeTracker(0), _fEnable(fEnable)
  522. {
  523. }
  524. CPatchDownloader::~CPatchDownloader()
  525. {
  526. ;
  527. }
  528. HRESULT CPatchDownloader::SetupDownload(DWORD dwFullTotalSize, UINT uPatchCount, IMyDownloadCallback *pcb, LPCSTR pszDLDir)
  529. {
  530. _dwFullTotalSize = dwFullTotalSize;
  531. _pCb = pcb;
  532. _uNumDownloads = uPatchCount;
  533. if (pszDLDir)
  534. lstrcpyn(_szPath, pszDLDir, sizeof(_szPath));
  535. else
  536. lstrcpy(_szPath, "");
  537. return S_OK;
  538. }
  539. HRESULT CPatchDownloader::DoDownload(LPCTSTR szFile)
  540. {
  541. HINF hInf = NULL;
  542. HRESULT hr = S_OK;
  543. // We shouldn't be called if patching isn't available.
  544. if (!IsEnabled())
  545. return E_FAIL;
  546. // TODO: Advpext currently behaves as a synchronous call, so
  547. // right now we can't do timeouts and progress bar ticks.
  548. StartClock();
  549. if(!IsNT())
  550. {
  551. DWORD fWin9x = 1;
  552. HKEY hKey;
  553. if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, PATCHWIN9xKEY,
  554. 0, KEY_READ | KEY_WRITE, &hKey) == ERROR_SUCCESS)
  555. {
  556. RegSetValueEx(hKey, "Usewin9xDirectory", 0, REG_DWORD,
  557. (LPBYTE)&fWin9x, sizeof(DWORD));
  558. RegCloseKey(hKey);
  559. }
  560. }
  561. if (SUCCEEDED(hr = OpenINFEngine(szFile, NULL, 0, &hInf, NULL)))
  562. {
  563. hr = g_pfnProcessFileSection(hInf, NULL, TRUE, "DefaultInstall", _szPath, CPatchDownloader::Callback, (LPVOID) this);
  564. CloseINFEngine(hInf);
  565. }
  566. _pCb = NULL;
  567. return hr;
  568. }
  569. BOOL CPatchDownloader::Callback(PATCH_DOWNLOAD_REASON Reason, PVOID lpvInfo, PVOID pvContext)
  570. {
  571. if (!pvContext)
  572. {
  573. return FALSE;
  574. }
  575. CPatchDownloader *pPatchInst = (CPatchDownloader *) pvContext;
  576. switch (Reason)
  577. {
  578. case PATCH_DOWNLOAD_ENDDOWNLOADINGDATA:
  579. break;
  580. case PATCH_DOWNLOAD_CONNECTING:
  581. case PATCH_DOWNLOAD_FINDINGSITE:
  582. case PATCH_DOWNLOAD_DOWNLOADINGDATA:
  583. // Not interesting here...
  584. break;
  585. case PATCH_DOWNLOAD_PROGRESS:
  586. {
  587. PDOWNLOAD_INFO ProgressInfo = (PDOWNLOAD_INFO)lpvInfo;
  588. DWORD dwBytesDownloaded = ProgressInfo->dwBytesToDownload - ProgressInfo->dwBytesRemaining;
  589. // Convert to KB
  590. dwBytesDownloaded = dwBytesDownloaded >> 10;
  591. // Adjust because the progress needs to be reflected as if
  592. // it was a full download.
  593. dwBytesDownloaded *= pPatchInst->GetFullDownloadSize();
  594. if (ProgressInfo->dwBytesToDownload != 0)
  595. dwBytesDownloaded /= ProgressInfo->dwBytesToDownload >> 10;
  596. // BUGBUG: We have to handle more than 1 patching INF.
  597. // This hack divides up the progress across
  598. // multiple downloads.
  599. if (pPatchInst->GetDownloadCount() > 0)
  600. dwBytesDownloaded /= pPatchInst->GetDownloadCount();
  601. pPatchInst->GetCallback()->OnProgress(dwBytesDownloaded, NULL);
  602. break;
  603. }
  604. case PATCH_DOWNLOAD_FILE_COMPLETED: // AdditionalInfo is Source file downloaded
  605. {
  606. TCHAR szDstFile[MAX_PATH+1];
  607. lstrcpyn(szDstFile, pPatchInst->GetPath(), MAX_PATH);
  608. SafeAddPath(szDstFile, ParseURLA((LPCTSTR) lpvInfo), sizeof(szDstFile));
  609. // advpext cleans up for us when it's finished downloading all the files.
  610. CopyFile((LPCTSTR)lpvInfo, szDstFile, FALSE);
  611. }
  612. break;
  613. case PATCH_DOWNLOAD_FILE_FAILED:
  614. // advpext automatically retries failures 3 times
  615. return PATCH_DOWNLOAD_FLAG_RETRY;
  616. default:
  617. break;
  618. }
  619. return TRUE;
  620. }