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.

2420 lines
64 KiB

  1. #include "private.h"
  2. #include <exdisp.h>
  3. #include <exdispid.h>
  4. #include <htiface.h>
  5. #include <mshtmdid.h>
  6. #include <mshtmcid.h>
  7. #include <mshtmhst.h>
  8. #include <optary.h> // needed for IHtmlLoadOptions
  9. #include "downld.h"
  10. #define TF_THISMODULE TF_DOWNLD
  11. // CUrlDownload is a single threaded object. We can assume we are always on a single thread.
  12. long g_lRegisteredWnd = 0;
  13. LRESULT UrlDownloadWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
  14. CLIPFORMAT g_cfHTML=CF_NULL;
  15. // User-Agent strings
  16. const WCHAR c_wszUserAgentAppend[] = L"; MSIECrawler)";
  17. // Refresh header for http-equiv (client-pull)
  18. const WCHAR c_wszRefresh[] = L"Refresh";
  19. const int MAX_CLIENT_PULL_NUM = 4; // max # redirections
  20. const int MAX_CLIENT_PULL_TIMEOUT = 6; // max timeout we'll follow
  21. // Function also present in shdocvw\basesb.cpp and in mshtml
  22. BOOL ParseRefreshContent(LPWSTR pwzContent, UINT * puiDelay, LPWSTR pwzUrlBuf, UINT cchUrlBuf);
  23. const WCHAR c_wszHeadVerb[] = L"HEAD";
  24. const WCHAR c_szUserAgentPrefix[] = L"User-Agent: ";
  25. const WCHAR c_szAcceptLanguagePrefix[] = L"Accept-Language: ";
  26. #define WM_URLDL_CLEAN (WM_USER + 0x1010)
  27. #define WM_URLDL_ONDLCOMPLETE (WM_USER + 0x1012)
  28. #define WM_URLDL_CLIENTPULL (WM_USER+0x1013)
  29. #define SAFE_RELEASE_BSC() \
  30. if (m_pCbsc) { \
  31. m_pCbsc->SetParent(NULL); \
  32. m_pCbsc->Release(); \
  33. m_pCbsc = NULL; \
  34. } else
  35. //---------------------------------------------------------------
  36. // CUrlDownload class
  37. CUrlDownload::CUrlDownload(CUrlDownloadSink *pParent, UINT iID /* =0 */)
  38. {
  39. DWORD cbData;
  40. // Maintain global count of objects
  41. DllAddRef();
  42. m_iID = iID;
  43. m_pParent = pParent;
  44. m_cRef = 1;
  45. ASSERT(m_pDocument==NULL && m_dwConnectionCookie==0 && m_pwszURL == NULL);
  46. // Get the timeout value (stored in seconds)
  47. cbData = sizeof(m_nTimeout);
  48. if (NO_ERROR != SHGetValue(HKEY_CURRENT_USER, c_szRegKey, TEXT("Timeout"), NULL, &m_nTimeout, &cbData))
  49. {
  50. // Default to 120 seconds
  51. m_nTimeout = 120;
  52. }
  53. // find the HTML clipboard format
  54. if (!g_cfHTML)
  55. {
  56. g_cfHTML = (CLIPFORMAT) RegisterClipboardFormat(CFSTR_MIME_HTML);
  57. TraceMsg(TF_THISMODULE, "ClipFormat for HTML = %d", (int)g_cfHTML);
  58. }
  59. // find out if we need to set the "RESYNCHRONIZE" flag
  60. INTERNET_CACHE_CONFIG_INFOA CacheConfigInfo;
  61. DWORD dwBufSize = sizeof(CacheConfigInfo);
  62. CacheConfigInfo.dwStructSize = sizeof(CacheConfigInfo);
  63. if (GetUrlCacheConfigInfoA(&CacheConfigInfo, &dwBufSize, CACHE_CONFIG_SYNC_MODE_FC))
  64. {
  65. if ((WININET_SYNC_MODE_ONCE_PER_SESSION == CacheConfigInfo.dwSyncMode) ||
  66. (WININET_SYNC_MODE_ALWAYS == CacheConfigInfo.dwSyncMode) ||
  67. (WININET_SYNC_MODE_AUTOMATIC == CacheConfigInfo.dwSyncMode))
  68. {
  69. m_fSetResync = FALSE;
  70. }
  71. else
  72. {
  73. m_fSetResync = TRUE;
  74. DBG("Browser session update='never', setting RESYNCHRONIZE");
  75. }
  76. }
  77. else
  78. DBG_WARN("GetUrlCacheConfigInfo failed! Not setting Resync.");
  79. m_lBindFlags = DLCTL_SILENT | DLCTL_NO_SCRIPTS | DLCTL_NO_BEHAVIORS |
  80. DLCTL_NO_JAVA | DLCTL_NO_RUNACTIVEXCTLS | DLCTL_NO_DLACTIVEXCTLS;
  81. if (m_fSetResync)
  82. m_lBindFlags |= DLCTL_RESYNCHRONIZE;
  83. // register our window class if necessary
  84. if (!g_lRegisteredWnd)
  85. {
  86. g_lRegisteredWnd++;
  87. WNDCLASS wc;
  88. wc.style = 0;
  89. wc.lpfnWndProc = UrlDownloadWndProc;
  90. wc.cbClsExtra = 0;
  91. wc.cbWndExtra = 0;
  92. wc.hInstance = g_hInst;
  93. wc.hIcon = NULL;
  94. wc.hCursor = NULL;
  95. wc.hbrBackground = (HBRUSH)NULL;
  96. wc.lpszMenuName = NULL;
  97. wc.lpszClassName = URLDL_WNDCLASS;
  98. RegisterClass(&wc);
  99. }
  100. }
  101. CUrlDownload::~CUrlDownload()
  102. {
  103. // Maintain global count of objects
  104. DllRelease();
  105. CleanUp();
  106. DBG("Destroyed CUrlDownload object");
  107. }
  108. void CUrlDownload::CleanUpBrowser()
  109. {
  110. SAFERELEASE(m_pScript);
  111. if (m_fAdviseOn)
  112. {
  113. UnAdviseMe();
  114. }
  115. SAFERELEASE(m_pCP);
  116. SAFERELEASE(m_pDocument);
  117. SAFERELEASE(m_pPersistMk);
  118. SAFERELEASE(m_pOleCmdTarget);
  119. SAFELOCALFREE(m_pwszClientPullURL);
  120. }
  121. void CUrlDownload::CleanUp()
  122. {
  123. CleanUpBrowser();
  124. SAFE_RELEASE_BSC();
  125. SAFELOCALFREE(m_pwszURL);
  126. SAFELOCALFREE(m_pstLastModified);
  127. SAFERELEASE(m_pStm);
  128. SAFELOCALFREE(m_pwszUserAgent);
  129. if (m_hwndMe)
  130. {
  131. SetWindowLongPtr(m_hwndMe, GWLP_USERDATA, 0);
  132. DestroyWindow(m_hwndMe);
  133. m_hwndMe = NULL;
  134. }
  135. }
  136. LRESULT UrlDownloadWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
  137. {
  138. CUrlDownload *pThis = (CUrlDownload*) GetWindowLongPtr(hWnd, GWLP_USERDATA);
  139. // Validate pThis
  140. #ifdef DEBUG
  141. if (pThis && IsBadWritePtr(pThis, sizeof(*pThis)))
  142. {
  143. TraceMsg(TF_THISMODULE,
  144. "Invalid 'this' in UrlDownloadWndProc (0x%08x) - already destroyed?", pThis);
  145. }
  146. #endif
  147. switch (Msg)
  148. {
  149. case WM_CREATE :
  150. {
  151. LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
  152. if (!pcs || !(pcs->lpCreateParams))
  153. {
  154. DBG_WARN("Invalid param UrlDownloadWndProc Create");
  155. return -1;
  156. }
  157. SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) pcs->lpCreateParams);
  158. return 0;
  159. }
  160. case WM_URLDL_CLIENTPULL :
  161. case WM_URLDL_ONDLCOMPLETE :
  162. case WM_TIMER :
  163. if (pThis)
  164. pThis->HandleMessage(hWnd, Msg, wParam, lParam);
  165. break;
  166. default:
  167. return DefWindowProc(hWnd, Msg, wParam, lParam);
  168. }
  169. return 0;
  170. }
  171. HRESULT CUrlDownload::CreateMyWindow()
  172. {
  173. // Create our callback window
  174. if (NULL == m_hwndMe)
  175. {
  176. // TraceMsg(TF_THISMODULE, "Creating MeWnd, this=0x%08x", (DWORD)this);
  177. m_hwndMe = CreateWindow(URLDL_WNDCLASS, TEXT("YO"), WS_OVERLAPPED,
  178. CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
  179. NULL, NULL, g_hInst, (LPVOID)this);
  180. if (NULL == m_hwndMe)
  181. {
  182. DBG_WARN("CUrlDownload CreateWindow(UrlDl WndClass) failed");
  183. return E_FAIL;
  184. }
  185. }
  186. return S_OK;
  187. }
  188. HRESULT CUrlDownload::BeginDownloadURL2(
  189. LPCWSTR pwszURL, // URL
  190. BDUMethod iMethod, // download method
  191. BDUOptions iOptions, // download options
  192. LPTSTR pszLocalFile, // Local file to download to instead of cache
  193. DWORD dwMaxSize // Max size in bytes; will abort if exceeded
  194. )
  195. {
  196. HRESULT hr = S_OK;
  197. // Param validation
  198. ASSERT(pwszURL);
  199. ASSERT(!(iOptions & BDU2_NEEDSTREAM) || (iMethod == BDU2_URLMON));
  200. ASSERT(!pszLocalFile || (iMethod == BDU2_URLMON));
  201. if (pszLocalFile && iMethod != BDU2_URLMON)
  202. {
  203. hr = E_INVALIDARG;
  204. }
  205. else
  206. {
  207. CreateMyWindow();
  208. // Clean up some old stuff
  209. if (m_pCbsc)
  210. {
  211. if (m_fbscValid)
  212. m_pCbsc->Abort();
  213. SAFE_RELEASE_BSC();
  214. }
  215. SAFERELEASE(m_pScript);
  216. SAFERELEASE(m_pStm);
  217. m_fbscValid = m_fBrowserValid = FALSE;
  218. m_iMethod = iMethod;
  219. m_iOptions = iOptions;
  220. m_dwMaxSize = dwMaxSize;
  221. SAFELOCALFREE(m_pwszClientPullURL);
  222. m_iNumClientPull = 0;
  223. // Save URL
  224. SAFELOCALFREE(m_pwszURL);
  225. m_pwszURL = StrDupW(pwszURL);
  226. SAFELOCALFREE(m_pstLastModified);
  227. m_dwResponseCode = 0;
  228. if ((iOptions & BDU2_FAIL_IF_NOT_HTML) && IsNonHtmlUrl(pwszURL))
  229. {
  230. // Hey, this isn't an HTML url! Don't even try to download it.
  231. OnDownloadComplete(BDU2_ERROR_NOT_HTML);
  232. }
  233. else
  234. {
  235. // Determine how to download this URL
  236. if ((iMethod == BDU2_BROWSER) ||
  237. ((iMethod == BDU2_SMART) && IsHtmlUrl(pwszURL)))
  238. {
  239. hr = BeginDownloadWithBrowser(pwszURL);
  240. }
  241. else
  242. {
  243. hr = BeginDownloadWithUrlMon(pwszURL, pszLocalFile, NULL);
  244. }
  245. }
  246. }
  247. if (FAILED(hr))
  248. {
  249. DBG("BeginDownloadURL2 : error HRESULT - calling OnDownloadComplete w/Error");
  250. OnDownloadComplete(BDU2_ERROR_GENERAL);
  251. }
  252. return hr;
  253. }
  254. //
  255. // Looks up the Url in the url history object and if its not CP_ACP
  256. // inserts an IHTMLLoadOptions object that contains the codepage
  257. // into the bind context
  258. //
  259. HRESULT InsertHistoricalCodepageIntoBindCtx(LPCWSTR pwszURL, IBindCtx * pbc)
  260. {
  261. HRESULT hr = S_OK;
  262. if (pwszURL == NULL || pbc == NULL)
  263. {
  264. hr = E_INVALIDARG;
  265. }
  266. else
  267. {
  268. //
  269. // Get the codepage from the intsite database. This is the codepage
  270. // the user set when last visiting this url.
  271. //
  272. PROPVARIANT propCodepage = {0};
  273. propCodepage.vt = VT_UI4;
  274. TCHAR szURL[INTERNET_MAX_URL_LENGTH];
  275. MyOleStrToStrN(szURL, INTERNET_MAX_URL_LENGTH, pwszURL);
  276. hr = IntSiteHelper(szURL, &c_rgPropRead[PROP_CODEPAGE],
  277. &propCodepage, 1, FALSE);
  278. if (SUCCEEDED(hr) && propCodepage.lVal != CP_ACP)
  279. {
  280. //
  281. // We got a codepage that wasn't the ansi one create an
  282. // HTMLLoadOptions object and set the code page in it.
  283. //
  284. IHtmlLoadOptions *phlo = NULL;
  285. hr = CoCreateInstance(CLSID_HTMLLoadOptions, NULL,
  286. CLSCTX_INPROC_SERVER, IID_IHtmlLoadOptions, (void**)&phlo);
  287. if (SUCCEEDED(hr) && phlo)
  288. {
  289. hr = phlo->SetOption(HTMLLOADOPTION_CODEPAGE, &propCodepage.lVal,
  290. sizeof(propCodepage.lVal));
  291. if (SUCCEEDED(hr))
  292. {
  293. //
  294. // Insert the option into the bindctx
  295. //
  296. pbc->RegisterObjectParam(L"__HTMLLOADOPTIONS", phlo);
  297. TraceMsg(TF_THISMODULE,
  298. "InsertHistoricalCodepageIntoBindCtx codepage=%d",
  299. propCodepage.lVal);
  300. }
  301. phlo->Release();
  302. }
  303. }
  304. }
  305. return hr;
  306. }
  307. LPCWSTR CUrlDownload::GetUserAgent()
  308. {
  309. if (m_pwszUserAgent)
  310. {
  311. return m_pwszUserAgent;
  312. }
  313. // Get default User-Agent string from urlmon
  314. CHAR chUA[1024];
  315. DWORD dwBufLen;
  316. // Assume that UrlMkGetSessionOption always succeeds (82160).
  317. chUA[0] = 0;
  318. UrlMkGetSessionOption(URLMON_OPTION_USERAGENT, chUA, sizeof(chUA), &dwBufLen, 0);
  319. // Append "MSIECrawler"
  320. int iLenUA, iLenNew;
  321. iLenUA = lstrlenA(chUA);
  322. iLenNew = iLenUA + ARRAYSIZE(c_wszUserAgentAppend);
  323. ASSERT(iLenUA == (int)(dwBufLen-1));
  324. if (iLenUA > 0)
  325. {
  326. m_pwszUserAgent = (LPWSTR) LocalAlloc(LMEM_FIXED, sizeof(WCHAR)*iLenNew);
  327. if (m_pwszUserAgent)
  328. {
  329. LPWSTR pwszAppend = m_pwszUserAgent+iLenUA-1;
  330. m_pwszUserAgent[0] = L'\0';
  331. SHAnsiToUnicode(chUA, m_pwszUserAgent, iLenNew);
  332. // find the closing parenthesis and append string there
  333. if (*pwszAppend != L')')
  334. {
  335. DBG("GetUserAgent: Last Char in UA isn't closing paren");
  336. pwszAppend = StrRChrW(m_pwszUserAgent, m_pwszUserAgent+iLenUA, L')');
  337. }
  338. if (pwszAppend)
  339. {
  340. StrCpyW(pwszAppend, c_wszUserAgentAppend);
  341. }
  342. else
  343. {
  344. LocalFree(m_pwszUserAgent);
  345. m_pwszUserAgent = NULL;
  346. }
  347. }
  348. }
  349. return m_pwszUserAgent;
  350. }
  351. HRESULT CUrlDownload::BeginDownloadWithBrowser(LPCWSTR pwszURL)
  352. {
  353. HRESULT hr;
  354. // Get browser and hook up sink
  355. // (no-op if we're already set up)
  356. hr = GetBrowser();
  357. if (SUCCEEDED(hr))
  358. {
  359. // browse to the required URL
  360. LPMONIKER pURLMoniker = NULL;
  361. IBindCtx *pbc = NULL;
  362. // create a URL moniker from the canonicalized path
  363. hr=CreateURLMoniker(NULL, pwszURL, &pURLMoniker);
  364. if (FAILED(hr)) DBG_WARN("CreateURLMoniker failed");
  365. // create an empty bind context so that Urlmon will call Trident's
  366. // QueryService on the proper thread so that Trident can delegate
  367. // it to use properly.
  368. hr=CreateBindCtx(0, &pbc);
  369. if (FAILED(hr)) DBG_WARN("CreateBindCtx failed");
  370. if (SUCCEEDED(hr))
  371. {
  372. //
  373. // Looks up the Url in the url history object and if its not CP_ACP
  374. // inserts an IHTMLLoadOptions object that contains the codepage
  375. // into the bind context. This is done so that TRIDENT is seeded
  376. // with the correct codepage.
  377. //
  378. InsertHistoricalCodepageIntoBindCtx(pwszURL, pbc);
  379. hr = m_pPersistMk->Load(FALSE, pURLMoniker, pbc, 0);
  380. if (SUCCEEDED(hr)) m_fWaitingForReadyState = TRUE;
  381. if (FAILED(hr)) DBG_WARN("PersistMoniker::Load failed");
  382. }
  383. // clean up junk
  384. if (pURLMoniker)
  385. pURLMoniker->Release();
  386. if (pbc)
  387. pbc->Release();
  388. if (SUCCEEDED(hr))
  389. {
  390. m_fBrowserValid = TRUE;
  391. StartTimer(); // Start our timeout
  392. }
  393. else
  394. {
  395. DBG("Error binding with Browser's IPersistMoniker");
  396. CleanUpBrowser();
  397. }
  398. }
  399. TraceMsg(TF_THISMODULE,
  400. "CUrlDownload::BeginDownloadWithBrowser (hr=0x%08x)", (long)hr);
  401. return hr;
  402. }
  403. HRESULT CUrlDownload::OnDownloadComplete(int iError)
  404. {
  405. PostMessage(m_hwndMe, WM_URLDL_ONDLCOMPLETE, (WPARAM)iError, 0);
  406. StopTimer();
  407. return S_OK;
  408. }
  409. BOOL CUrlDownload::HandleMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  410. {
  411. switch (uMsg)
  412. {
  413. case WM_URLDL_CLIENTPULL :
  414. {
  415. HRESULT hr = E_FAIL;
  416. // Ask our parent if we should do this
  417. if (m_pwszClientPullURL)
  418. {
  419. if (m_pParent && (m_iNumClientPull < MAX_CLIENT_PULL_NUM))
  420. hr = m_pParent->OnClientPull(m_iID, m_pwszURL, m_pwszClientPullURL);
  421. TraceMsgA(TF_THISMODULE, "CUrlDownload %s executing client pull to %ws",
  422. SUCCEEDED(hr) ? "is" : "**not**", m_pwszClientPullURL);
  423. }
  424. if (SUCCEEDED(hr))
  425. {
  426. // Download this new url. Don't give "downloadcomplete" for first one
  427. // Save member vars since they get reset in BDU2
  428. int iNumClientPull = m_iNumClientPull;
  429. LPWSTR pszNewURL = m_pwszClientPullURL;
  430. m_pwszClientPullURL = NULL;
  431. hr = BeginDownloadURL2(pszNewURL, m_iMethod, m_iOptions, NULL, m_dwMaxSize);
  432. MemFree(pszNewURL);
  433. if (SUCCEEDED(hr))
  434. {
  435. m_iNumClientPull = iNumClientPull + 1;
  436. }
  437. }
  438. }
  439. break;
  440. case WM_URLDL_ONDLCOMPLETE :
  441. if (m_pParent)
  442. m_pParent->OnDownloadComplete(m_iID, (int)wParam);
  443. return TRUE;
  444. case WM_TIMER :
  445. #ifdef DEBUG
  446. DBG_WARN("CUrlDownload ERROR - TIMING OUT");
  447. if ( m_fBrowserValid )
  448. {
  449. TraceMsg( TF_ALWAYS, "CUrlDownload::HandleMessage() - Browser Timeout." );
  450. }
  451. else
  452. {
  453. TraceMsg( TF_ALWAYS, "CUrlDownload::HandleMessage() - Non-Browser Timeout." );
  454. }
  455. if ( m_fbscValid )
  456. {
  457. TraceMsg( TF_ALWAYS, "CUrlDownload::HandleMessage() - UrlMon Timeout." );
  458. }
  459. else
  460. {
  461. TraceMsg( TF_ALWAYS, "CUrlDownload::HandleMessage() - Non-UrlMon Timeout." );
  462. }
  463. #endif
  464. StopTimer();
  465. AbortDownload(BDU2_ERROR_TIMEOUT);
  466. return TRUE;
  467. }
  468. return TRUE;
  469. }
  470. HRESULT CUrlDownload::AbortDownload(int iErrorCode /* =-1 */)
  471. {
  472. HRESULT hr=S_FALSE;
  473. BOOL fAborted=FALSE;
  474. if (m_fBrowserValid)
  475. {
  476. ASSERT(m_pOleCmdTarget);
  477. if (m_pOleCmdTarget)
  478. {
  479. m_pOleCmdTarget->Exec(NULL, OLECMDID_STOP, 0, NULL, NULL);
  480. }
  481. SAFELOCALFREE(m_pwszClientPullURL);
  482. fAborted=TRUE;
  483. m_fBrowserValid = FALSE;
  484. }
  485. if (m_fbscValid)
  486. {
  487. ASSERT(m_pCbsc);
  488. if (m_pCbsc)
  489. {
  490. hr = m_pCbsc->Abort();
  491. fAborted=TRUE;
  492. SAFE_RELEASE_BSC();
  493. }
  494. m_fbscValid=FALSE;
  495. }
  496. if (fAborted && m_pParent)
  497. {
  498. OnDownloadComplete((iErrorCode==-1) ? BDU2_ERROR_ABORT : iErrorCode);
  499. }
  500. return hr;
  501. }
  502. // Loads browser, creates sink and hooks it up to sinks
  503. HRESULT CUrlDownload::GetBrowser()
  504. {
  505. HRESULT hr = S_OK;
  506. if (m_fAdviseOn)
  507. return hr;
  508. if (NULL == m_pDocument)
  509. {
  510. ASSERT(!m_pPersistMk);
  511. ASSERT(!m_pCP);
  512. hr = CoCreateInstance(CLSID_HTMLDocument, NULL,
  513. CLSCTX_INPROC, IID_IHTMLDocument2, (void **)&m_pDocument);
  514. DBG("Created new CLSID_HTMLDocument");
  515. if (SUCCEEDED(hr))
  516. {
  517. IOleObject *pOleObj;
  518. hr = m_pDocument->QueryInterface(IID_IOleObject, (void **)&pOleObj);
  519. if (SUCCEEDED(hr))
  520. {
  521. pOleObj->SetClientSite((IOleClientSite *)this);
  522. pOleObj->Release();
  523. }
  524. }
  525. if (SUCCEEDED(hr))
  526. {
  527. hr = m_pDocument->QueryInterface(IID_IPersistMoniker, (void**)&m_pPersistMk);
  528. }
  529. if (SUCCEEDED(hr))
  530. {
  531. hr = m_pDocument->QueryInterface(IID_IOleCommandTarget, (void**)&m_pOleCmdTarget);
  532. }
  533. }
  534. // At this point we have m_pDocument and m_pPersistMk
  535. // Get DownloadNotify sink hooked up
  536. IDownloadNotify *pNotify=NULL;
  537. BOOL fNotifySet=FALSE;
  538. if (SUCCEEDED(hr) && SUCCEEDED(m_pParent->GetDownloadNotify(&pNotify)) && pNotify)
  539. {
  540. IOleCommandTarget *pTarget=NULL;
  541. if (SUCCEEDED(m_pDocument->QueryInterface(IID_IOleCommandTarget, (void **)&pTarget)) && pTarget)
  542. {
  543. VARIANTARG varIn;
  544. varIn.vt = VT_UNKNOWN;
  545. varIn.punkVal = (IUnknown *)pNotify;
  546. if (SUCCEEDED(pTarget->Exec(&CGID_DownloadHost, DWNHCMDID_SETDOWNLOADNOTIFY, 0,
  547. &varIn, NULL)))
  548. {
  549. fNotifySet=TRUE;
  550. }
  551. pTarget->Release();
  552. }
  553. if (!fNotifySet)
  554. {
  555. DBG_WARN("IDownloadNotify provided, but couldn't set callback!");
  556. }
  557. pNotify->Release();
  558. }
  559. if (!fNotifySet && (m_iOptions & BDU2_DOWNLOADNOTIFY_REQUIRED))
  560. {
  561. DBG_WARN("Couldn't set notify, parent requires it. CUrlDownload failing MSHTML download.");
  562. hr = E_FAIL;
  563. }
  564. // Get PropertyNotifySink hooked up
  565. // Find our connection point if necessary
  566. if (NULL == m_pCP && SUCCEEDED(hr))
  567. {
  568. IConnectionPointContainer *pCPCont=NULL;
  569. hr = m_pDocument->QueryInterface(IID_IConnectionPointContainer,
  570. (void **)&pCPCont);
  571. if (SUCCEEDED(hr))
  572. {
  573. hr = pCPCont->FindConnectionPoint(IID_IPropertyNotifySink, &m_pCP);
  574. pCPCont->Release();
  575. pCPCont = NULL;
  576. }
  577. }
  578. // And hook it up to us
  579. if (SUCCEEDED(hr))
  580. {
  581. // create sink
  582. IPropertyNotifySink *pSink = (IPropertyNotifySink *)this;
  583. hr = m_pCP->Advise(pSink, &m_dwConnectionCookie);
  584. if (SUCCEEDED(hr))
  585. {
  586. m_fAdviseOn = TRUE;
  587. }
  588. }
  589. if (FAILED(hr)) DBG_WARN("CUrlDownload::GetBrowser returning failure");
  590. return hr;
  591. }
  592. void CUrlDownload::UnAdviseMe()
  593. {
  594. if (m_fAdviseOn)
  595. {
  596. m_pCP->Unadvise(m_dwConnectionCookie);
  597. m_fAdviseOn = FALSE;
  598. }
  599. }
  600. void CUrlDownload::DestroyBrowser()
  601. {
  602. CleanUpBrowser();
  603. }
  604. void CUrlDownload::DoneDownloading()
  605. {
  606. // Don't send any more messages to the parent
  607. LeaveMeAlone();
  608. AbortDownload();
  609. CleanUp();
  610. }
  611. HRESULT CUrlDownload::GetScript(IHTMLWindow2 **ppWin)
  612. {
  613. HRESULT hr = E_FAIL;
  614. IDispatch *pDisp=NULL;
  615. ASSERT(ppWin);
  616. *ppWin=NULL;
  617. if (!m_fBrowserValid)
  618. {
  619. DBG("m_fBrowserValid FALSE, GetScript returning failure");
  620. return E_FAIL;
  621. }
  622. *ppWin = NULL;
  623. if (m_pScript)
  624. {
  625. m_pScript->AddRef();
  626. *ppWin = m_pScript;
  627. return S_OK;
  628. }
  629. if (m_pDocument)
  630. {
  631. hr = m_pDocument->get_Script(&pDisp);
  632. if (!pDisp) hr=E_NOINTERFACE;
  633. #ifdef DEBUG
  634. if (FAILED(hr)) DBG_WARN("CUrlDownload::GetScript: get_Script failed");
  635. #endif
  636. }
  637. if (SUCCEEDED(hr))
  638. {
  639. hr = pDisp->QueryInterface(IID_IHTMLWindow2, (void **)ppWin);
  640. if (*ppWin == NULL) hr = E_NOINTERFACE;
  641. pDisp->Release();
  642. #ifdef DEBUG
  643. if (FAILED(hr)) DBG_WARN("CUrlDownload::GetScript: QI IOmWindow2 failed");
  644. #endif
  645. }
  646. // Save this so future GetScript() calls much faster
  647. ASSERT(!m_pScript);
  648. if (SUCCEEDED(hr))
  649. {
  650. m_pScript = *ppWin;
  651. m_pScript->AddRef();
  652. }
  653. return hr;
  654. }
  655. // static member function
  656. // Strips off anchor from URL (# not after ?)
  657. // S_FALSE : Unchanged
  658. // S_OK : Removed anchor
  659. HRESULT CUrlDownload::StripAnchor(LPWSTR lpURL)
  660. {
  661. if (!lpURL) return E_POINTER;
  662. while (*lpURL)
  663. {
  664. if (*lpURL == L'?')
  665. return S_FALSE;
  666. if (*lpURL == L'#')
  667. {
  668. *lpURL = L'\0';
  669. return S_OK;
  670. }
  671. lpURL ++;
  672. }
  673. return S_FALSE;
  674. }
  675. // Returns pointer to '.' or pointer to null-terminator or query '?'
  676. LPWSTR // ptr to period or to null-term or '?'
  677. URLFindExtensionW(
  678. LPCWSTR pszURL,
  679. int *piLen) // length including period
  680. {
  681. LPCWSTR pszDot;
  682. for (pszDot = NULL; *pszURL && *pszURL!='?'; pszURL++)
  683. {
  684. switch (*pszURL) {
  685. case TEXT('.'):
  686. pszDot = pszURL; // remember the last dot
  687. break;
  688. case TEXT('/'):
  689. pszDot = NULL; // forget last dot, it was in a directory
  690. break;
  691. }
  692. }
  693. if (piLen)
  694. {
  695. if (pszDot)
  696. *piLen = (int) (pszURL-pszDot);
  697. else
  698. *piLen = 0;
  699. }
  700. // if we found the extension, return ptr to the dot, else
  701. // ptr to end of the string (NULL extension) (cast->non const)
  702. return pszDot ? (LPWSTR)pszDot : (LPWSTR)pszURL;
  703. }
  704. // Returns TRUE if this appears to be an HTML URL
  705. BOOL CUrlDownload::IsHtmlUrl(LPCWSTR lpURL)
  706. {
  707. LPWSTR pwch;
  708. int iLen;
  709. pwch = URLFindExtensionW(lpURL, &iLen);
  710. if (*pwch && iLen)
  711. {
  712. pwch ++; iLen --;
  713. // We found an extension. Check it out.
  714. if ((iLen == 4 &&
  715. (!MyAsciiCmpNIW(pwch, L"html", 4))) ||
  716. (iLen == 3 &&
  717. (!MyAsciiCmpNIW(pwch, L"htm", 3) ||
  718. !MyAsciiCmpNIW(pwch, L"htt", 3) ||
  719. !MyAsciiCmpNIW(pwch, L"asp", 3) ||
  720. !MyAsciiCmpNIW(pwch, L"htx", 3)
  721. )))
  722. {
  723. // known HTML extension
  724. return TRUE;
  725. }
  726. }
  727. return FALSE;
  728. }
  729. // Returns TRUE if this appears NOT to be an HTML URL
  730. BOOL CUrlDownload::IsNonHtmlUrl(LPCWSTR lpURL)
  731. {
  732. LPWSTR pwch;
  733. int iLen;
  734. pwch = URLFindExtensionW(lpURL, &iLen);
  735. if (*pwch && iLen)
  736. {
  737. pwch ++; iLen --;
  738. // We found an extension. Check it out.
  739. if ((iLen==3) &&
  740. (!MyAsciiCmpNIW(pwch, L"bmp", 3) ||
  741. !MyAsciiCmpNIW(pwch, L"cab", 3) ||
  742. !MyAsciiCmpNIW(pwch, L"cdf", 3) ||
  743. !MyAsciiCmpNIW(pwch, L"jpg", 3) ||
  744. !MyAsciiCmpNIW(pwch, L"exe", 3) ||
  745. !MyAsciiCmpNIW(pwch, L"zip", 3) ||
  746. !MyAsciiCmpNIW(pwch, L"doc", 3) ||
  747. !MyAsciiCmpNIW(pwch, L"gif", 3)
  748. ))
  749. {
  750. // known non-HTML extension
  751. return TRUE;
  752. }
  753. }
  754. return FALSE;
  755. }
  756. // Returns TRUE if this is a URL we should try to download (http:)
  757. BOOL CUrlDownload::IsValidURL(LPCWSTR lpURL)
  758. {
  759. // Check protocol
  760. // HKEY hk;
  761. #if 0
  762. BOOL fValidProtocol = FALSE;
  763. // Always accept http or https
  764. if (!StrCmpNIW(lpURL, L"http", 4))
  765. fValidProtocol = TRUE;
  766. if (!fValidProtocol &&
  767. (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, TEXT("PROTOCOLS\\Handler"), 0, KEY_QUERY_VALUE, &hk)))
  768. {
  769. // Crack out protocol
  770. DWORD dwData=0, cbData = sizeof(DWORD);
  771. int i;
  772. char ch[16];
  773. // we know we are 7-bit
  774. for (i=0; i<ARRAYSIZE(ch) && lpURL[i] != L':' && lpURL[i]; i++)
  775. ch[i] = (char) (lpURL[i]);
  776. if (i<ARRAYSIZE(ch))
  777. {
  778. ch[i] = '\0';
  779. // We have protocol
  780. if (NO_ERROR == SHGetValue(hk, ch, TEXT("SupportsNoUI"), NULL, &dwData, &cbData))
  781. {
  782. if (dwData != 0)
  783. fValidProtocol = TRUE; // Passed test
  784. }
  785. #ifdef DEBUG
  786. if (!fValidProtocol)
  787. TraceMsgA(TF_THISMODULE, "IsValidUrl failing url protocol=%s url=%ws", ch, lpURL);
  788. #endif
  789. }
  790. RegCloseKey(hk);
  791. }
  792. #endif
  793. // See if this protocol will give us something for the cache
  794. BOOL fUsesCache=FALSE;
  795. DWORD dwBufSize=0;
  796. CoInternetQueryInfo(lpURL, QUERY_USES_CACHE, 0,
  797. &fUsesCache, sizeof(fUsesCache), &dwBufSize, 0);
  798. if (!fUsesCache || (S_FALSE == ::IsValidURL(NULL, lpURL, 0)))
  799. return FALSE;
  800. return TRUE;
  801. }
  802. HRESULT CUrlDownload::GetRealURL(LPWSTR *ppwszURL)
  803. {
  804. *ppwszURL = NULL;
  805. if (!m_fBrowserValid)
  806. {
  807. if (m_pwszURL)
  808. *ppwszURL = StrDupW(m_pwszURL);
  809. }
  810. else
  811. {
  812. // Get the real URL from the browser in case we were redirected
  813. // We could optimize to do this only once
  814. ITargetContainer *pTarget=NULL;
  815. LPWSTR pwszThisUrl=NULL;
  816. if (m_pDocument)
  817. {
  818. m_pDocument->QueryInterface(IID_ITargetContainer, (void **)&pTarget);
  819. if (pTarget)
  820. {
  821. pTarget->GetFrameUrl(&pwszThisUrl);
  822. pTarget->Release();
  823. }
  824. }
  825. if (pwszThisUrl)
  826. {
  827. if (m_pwszURL) MemFree(m_pwszURL);
  828. m_pwszURL = StrDupW(pwszThisUrl);
  829. *ppwszURL = StrDupW(pwszThisUrl);
  830. CoTaskMemFree(pwszThisUrl);
  831. }
  832. else if (m_pwszURL)
  833. {
  834. *ppwszURL = StrDupW(m_pwszURL);
  835. }
  836. }
  837. return (*ppwszURL) ? S_OK : E_OUTOFMEMORY;
  838. }
  839. HRESULT CUrlDownload::GetDocument(IHTMLDocument2 **ppDoc)
  840. {
  841. HRESULT hr;
  842. if (!m_fBrowserValid)
  843. {
  844. DBG("GetDocument failing, m_fBrowserValid FALSE");
  845. *ppDoc = NULL;
  846. return E_FAIL;
  847. }
  848. *ppDoc = m_pDocument;
  849. if (m_pDocument)
  850. {
  851. m_pDocument->AddRef();
  852. hr = S_OK;
  853. }
  854. else
  855. hr = E_NOINTERFACE;
  856. return hr;
  857. }
  858. HRESULT CUrlDownload::GetStream(IStream **ppStm)
  859. {
  860. if (!m_pStm)
  861. {
  862. DBG("Stream not available, CUrlDownload::GetStream failing");
  863. *ppStm = NULL;
  864. return E_FAIL;
  865. }
  866. *ppStm = m_pStm;
  867. (*ppStm)->AddRef();
  868. return S_OK;
  869. }
  870. HRESULT CUrlDownload::GetLastModified(SYSTEMTIME *pstLastModified)
  871. {
  872. if (NULL == pstLastModified)
  873. return E_INVALIDARG;
  874. if (NULL == m_pstLastModified)
  875. return E_FAIL;
  876. CopyMemory(pstLastModified, m_pstLastModified, sizeof(SYSTEMTIME));
  877. return S_OK;
  878. }
  879. HRESULT CUrlDownload::GetResponseCode(DWORD *pdwResponseCode)
  880. {
  881. if (m_dwResponseCode == 0)
  882. return E_FAIL;
  883. *pdwResponseCode = m_dwResponseCode;
  884. return S_OK;
  885. }
  886. // Start or extend timer
  887. void CUrlDownload::StartTimer()
  888. {
  889. if (m_hwndMe)
  890. {
  891. if (!m_iTimerID)
  892. {
  893. m_iTimerID = 1;
  894. DBG("CUrlDownload Creating new timeout timer");
  895. }
  896. m_iTimerID = SetTimer(m_hwndMe, 1, 1000 * m_nTimeout, NULL);
  897. }
  898. }
  899. void CUrlDownload::StopTimer()
  900. {
  901. if (m_hwndMe && m_iTimerID)
  902. {
  903. DBG("CUrlDownload destroying timeout timer");
  904. KillTimer(m_hwndMe, m_iTimerID);
  905. m_iTimerID = 0;
  906. }
  907. }
  908. //
  909. // IUnknown of CUrlDownload
  910. //
  911. STDMETHODIMP CUrlDownload::QueryInterface(REFIID riid, void ** ppv)
  912. {
  913. *ppv=NULL;
  914. // Validate requested interface
  915. if (IID_IOleClientSite == riid)
  916. *ppv=(IOleClientSite *)this;
  917. else if (IID_IPropertyNotifySink == riid)
  918. *ppv=(IPropertyNotifySink *)this;
  919. else if (IID_IOleCommandTarget == riid)
  920. *ppv=(IOleCommandTarget *)this;
  921. else if (IID_IDispatch == riid)
  922. *ppv=(IDispatch *)this;
  923. else if (IID_IServiceProvider == riid)
  924. *ppv = (IServiceProvider *)this;
  925. else if (IID_IAuthenticate == riid)
  926. *ppv = (IAuthenticate *)this;
  927. else if (IID_IInternetSecurityManager == riid)
  928. *ppv = (IInternetSecurityManager *)this;
  929. else if (IID_IHttpSecurity == riid)
  930. *ppv = (IHttpSecurity *)this;
  931. else if ((IID_IUnknown == riid) ||
  932. (IID_IHlinkFrame == riid))
  933. *ppv = (IHlinkFrame *)this;
  934. else
  935. {
  936. // DBGIID("CUrlDownload::QueryInterface() failing", riid);
  937. }
  938. // Addref through the interface
  939. if (NULL != *ppv)
  940. {
  941. ((LPUNKNOWN)*ppv)->AddRef();
  942. return S_OK;
  943. }
  944. return E_NOINTERFACE;
  945. }
  946. STDMETHODIMP_(ULONG) CUrlDownload::AddRef(void)
  947. {
  948. return ++m_cRef;
  949. }
  950. STDMETHODIMP_(ULONG) CUrlDownload::Release(void)
  951. {
  952. if (0L != --m_cRef)
  953. return 1L;
  954. delete this;
  955. return 0L;
  956. }
  957. STDMETHODIMP CUrlDownload::GetTypeInfoCount(UINT *pctinfo)
  958. {
  959. return E_NOTIMPL;
  960. }
  961. STDMETHODIMP CUrlDownload::GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo **pptinfo)
  962. {
  963. return E_NOTIMPL;
  964. }
  965. STDMETHODIMP CUrlDownload::GetIDsOfNames(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID *rgdispid)
  966. {
  967. return E_NOTIMPL;
  968. }
  969. STDMETHODIMP CUrlDownload::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags,
  970. DISPPARAMS *pdispparams, VARIANT *pvarResult,
  971. EXCEPINFO *pexcepinfo, UINT *puArgErr)
  972. {
  973. if (!pvarResult)
  974. return E_INVALIDARG;
  975. ASSERT(pvarResult->vt == VT_EMPTY);
  976. if (wFlags == DISPATCH_PROPERTYGET)
  977. {
  978. HRESULT hr = DISP_E_MEMBERNOTFOUND;
  979. switch (dispidMember)
  980. {
  981. case DISPID_AMBIENT_DLCONTROL :
  982. TraceMsg(TF_THISMODULE, "Returning DLCONTROL ambient property 0x%08x", m_lBindFlags);
  983. pvarResult->vt = VT_I4;
  984. pvarResult->lVal = m_lBindFlags;
  985. hr = S_OK;
  986. break;
  987. case DISPID_AMBIENT_USERAGENT:
  988. DBG("Returning User Agent ambient property");
  989. pvarResult->bstrVal = SysAllocString(GetUserAgent());
  990. if (pvarResult->bstrVal != NULL)
  991. {
  992. pvarResult->vt = VT_BSTR;
  993. hr = S_OK;
  994. }
  995. break;
  996. }
  997. return hr;
  998. }
  999. return DISP_E_MEMBERNOTFOUND;
  1000. }
  1001. // IPropertyNotifySink
  1002. STDMETHODIMP CUrlDownload::OnChanged(DISPID dispID)
  1003. {
  1004. // We've received a notification, extend our timer if it's currently running
  1005. if (m_iTimerID)
  1006. StartTimer();
  1007. if ((DISPID_READYSTATE == dispID) ||
  1008. (DISPID_UNKNOWN == dispID))
  1009. {
  1010. // Find out if we're done
  1011. if (m_fWaitingForReadyState)
  1012. {
  1013. VARIANT varState;
  1014. DISPPARAMS dp;
  1015. VariantInit(&varState);
  1016. if (SUCCEEDED(m_pDocument->Invoke(DISPID_READYSTATE,
  1017. IID_NULL,
  1018. GetUserDefaultLCID(),
  1019. DISPATCH_PROPERTYGET,
  1020. &dp,
  1021. &varState, NULL, NULL)) &&
  1022. V_VT(&varState)==VT_I4 &&
  1023. V_I4(&varState)== READYSTATE_COMPLETE)
  1024. {
  1025. m_fWaitingForReadyState = FALSE;
  1026. // Successful download. See if a client-pull is waiting.
  1027. if (m_pwszClientPullURL)
  1028. PostMessage(m_hwndMe, WM_URLDL_CLIENTPULL, 0, 0);
  1029. else
  1030. OnDownloadComplete(BDU2_ERROR_NONE);
  1031. }
  1032. }
  1033. }
  1034. return S_OK;
  1035. }
  1036. STDMETHODIMP CUrlDownload::OnRequestEdit(DISPID dispID)
  1037. {
  1038. return S_OK;
  1039. }
  1040. // IOleCommandTarget
  1041. STDMETHODIMP CUrlDownload::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds,
  1042. OLECMD prgCmds[], OLECMDTEXT *pCmdText)
  1043. {
  1044. return OLECMDERR_E_UNKNOWNGROUP;
  1045. }
  1046. STDMETHODIMP CUrlDownload::Exec(const GUID *pguidCmdGroup, DWORD nCmdID,
  1047. DWORD nCmdexecopt, VARIANTARG *pvarargIn,
  1048. VARIANTARG *pvarargOut)
  1049. {
  1050. HRESULT hres = OLECMDERR_E_NOTSUPPORTED;
  1051. if (pguidCmdGroup == NULL)
  1052. {
  1053. switch(nCmdID)
  1054. {
  1055. case OLECMDID_SETPROGRESSPOS:
  1056. {
  1057. hres = S_OK;
  1058. VARIANT varBytes;
  1059. if (m_pOleCmdTarget)
  1060. {
  1061. varBytes.vt=VT_EMPTY;
  1062. m_pOleCmdTarget->Exec(&CGID_MSHTML, IDM_GETBYTESDOWNLOADED, 0, NULL, &varBytes);
  1063. if (varBytes.vt == VT_I4)
  1064. {
  1065. DWORD dwBytes = (DWORD) varBytes.lVal;
  1066. TraceMsg(TF_THISMODULE, "%d bytes on page so far (mshtml)", dwBytes);
  1067. ProgressBytes(dwBytes);
  1068. }
  1069. }
  1070. // 14032: If dialmon is around, tell it that something is going on
  1071. IndicateDialmonActivity();
  1072. }
  1073. break;
  1074. //
  1075. // The containee has found an http-equiv meta tag; handle it
  1076. // appropriately (client pull)
  1077. //
  1078. case OLECMDID_HTTPEQUIV_DONE:
  1079. hres = S_OK;
  1080. break;
  1081. case OLECMDID_HTTPEQUIV:
  1082. {
  1083. LPWSTR pwszEquivString = pvarargIn? pvarargIn->bstrVal : NULL;
  1084. BOOL fHasHeader = (pwszEquivString!=NULL);
  1085. if (pvarargIn && pvarargIn->vt != VT_BSTR)
  1086. return OLECMDERR_E_NOTSUPPORTED;
  1087. if (!fHasHeader || StrCmpNIW(c_wszRefresh, pwszEquivString, lstrlenW(c_wszRefresh)) == 0)
  1088. {
  1089. // Hit. Now do the right thing for this header
  1090. // We pass both the header and a pointer to the first char after
  1091. // ':', which is usually the delimiter handlers will look for.
  1092. LPWSTR pwszColon = fHasHeader ? StrChrW(pwszEquivString, ':') : NULL;
  1093. // Enforce the : at the end of the header
  1094. if (fHasHeader && !pwszColon)
  1095. {
  1096. return OLECMDERR_E_NOTSUPPORTED;
  1097. }
  1098. hres = HandleRefresh(pwszEquivString, pwszColon ? pwszColon+1:NULL,
  1099. (nCmdID == OLECMDID_HTTPEQUIV_DONE));
  1100. }
  1101. }
  1102. // if we return OLECMDERR_E_NOTSUPPORTED, we don't handle
  1103. // client pull
  1104. break;
  1105. }
  1106. }
  1107. if ((hres == OLECMDERR_E_NOTSUPPORTED) && m_pParent)
  1108. {
  1109. hres = m_pParent->OnOleCommandTargetExec(pguidCmdGroup, nCmdID, nCmdexecopt,
  1110. pvarargIn, pvarargOut);
  1111. }
  1112. return hres;
  1113. }
  1114. // The basic operation was lifted from shdocvw\basesb.cpp
  1115. HRESULT CUrlDownload::HandleRefresh(LPWSTR pwszEquivString, LPWSTR pwszContent, BOOL fDone)
  1116. {
  1117. unsigned int uiTimeout = 0;
  1118. WCHAR awch[INTERNET_MAX_URL_LENGTH];
  1119. if (fDone)
  1120. {
  1121. return S_OK; // fDone means we don't process this
  1122. }
  1123. // NSCompat: we only honor the first successfully parsed Refresh
  1124. if (m_pwszClientPullURL)
  1125. return S_OK;
  1126. if (!pwszContent ||
  1127. !ParseRefreshContent(pwszContent, &uiTimeout, awch, INTERNET_MAX_URL_LENGTH))
  1128. {
  1129. return OLECMDERR_E_NOTSUPPORTED; // cannot handle refresh w/o timeout
  1130. }
  1131. if (!awch[0])
  1132. {
  1133. DBG("CUrlDownload ignoring client-pull directive with no url");
  1134. return S_OK;
  1135. }
  1136. if (m_iNumClientPull >= MAX_CLIENT_PULL_NUM)
  1137. {
  1138. DBG("Max # client pulls exceeded; ignoring client pull directive");
  1139. return S_OK;
  1140. }
  1141. TraceMsg(TF_THISMODULE, "CUrlDownload client pull (refresh=%d) url=%ws", uiTimeout, awch);
  1142. if (uiTimeout > MAX_CLIENT_PULL_TIMEOUT)
  1143. {
  1144. DBG("Ignoring client-pull directive with large timeout");
  1145. return S_OK;
  1146. }
  1147. m_pwszClientPullURL = StrDupW(awch);
  1148. // If we can't copy the URL, don't set the timer or else we'll
  1149. // keep reloading the same page.
  1150. if (m_pwszClientPullURL == NULL)
  1151. return OLECMDERR_E_NOTSUPPORTED;
  1152. return S_OK;
  1153. }
  1154. HRESULT CUrlDownload::SetDLCTL(long lFlags)
  1155. {
  1156. // TraceMsg(TF_THISMODULE, "CUrlDownload: SetDLCTL %04x", lFlags);
  1157. m_lBindFlags = lFlags | DLCTL_SILENT;
  1158. if (m_fSetResync)
  1159. m_lBindFlags |= DLCTL_RESYNCHRONIZE;
  1160. return S_OK;
  1161. }
  1162. #define INET_E_AGENT_BIND_IN_PROGRESS 0x800C0FFF
  1163. //==============================================================================
  1164. // UrlMon download code
  1165. //==============================================================================
  1166. HRESULT CUrlDownload::BeginDownloadWithUrlMon(
  1167. LPCWSTR pwszURL,
  1168. LPTSTR pszLocalFile,
  1169. IEnumFORMATETC *pEFE)
  1170. {
  1171. IStream* pstm = NULL;
  1172. IMoniker* pmk = NULL;
  1173. IBindCtx* pbc = NULL;
  1174. HRESULT hr;
  1175. hr = CreateURLMoniker(NULL, pwszURL, &pmk);
  1176. if (FAILED(hr))
  1177. {
  1178. DBG_WARN("CreateURLMoniker failed");
  1179. goto LErrExit;
  1180. }
  1181. SAFE_RELEASE_BSC();
  1182. m_pCbsc = new CUrlDownload_BSC(m_iMethod, m_iOptions, pszLocalFile);
  1183. if (m_pCbsc == NULL)
  1184. {
  1185. hr = E_OUTOFMEMORY;
  1186. goto LErrExit;
  1187. }
  1188. hr = CreateBindCtx(0, &pbc);
  1189. if (FAILED(hr))
  1190. goto LErrExit;
  1191. if (pEFE)
  1192. {
  1193. hr = RegisterFormatEnumerator(pbc, pEFE, 0);
  1194. if (FAILED(hr))
  1195. DBG_WARN("RegisterFormatEnumerator failed (continuing download)");
  1196. }
  1197. hr = RegisterBindStatusCallback(pbc,
  1198. (IBindStatusCallback *)m_pCbsc,
  1199. 0,
  1200. 0L);
  1201. if (FAILED(hr))
  1202. goto LErrExit;
  1203. m_pCbsc->SetParent(this);
  1204. m_fbscValid = TRUE;
  1205. m_hrStatus = INET_E_AGENT_BIND_IN_PROGRESS;
  1206. StartTimer(); // Start our timeout
  1207. hr = pmk->BindToStorage(pbc, 0, IID_IStream, (void**)&pstm);
  1208. if (m_hrStatus != INET_E_AGENT_BIND_IN_PROGRESS)
  1209. {
  1210. // Synchronous success or failure. Call OnDownloadComplete.
  1211. // We can't do it in OnStopBinding because Urlmon returns hrStatus=S_OK...
  1212. // even if it fails.
  1213. if (FAILED(hr) || FAILED(m_hrStatus))
  1214. OnDownloadComplete(BDU2_ERROR_GENERAL);
  1215. else
  1216. OnDownloadComplete(BDU2_ERROR_NONE);
  1217. DBG("Synchronous bind; OnDownloadComplete called");
  1218. }
  1219. m_hrStatus = S_OK; // need this so we get OnDownloadComplete (asynch OnStopBinding)
  1220. hr = S_OK; // need this so we don't get extra OnDownloadComplete (BDU2)
  1221. // Bind has started (and maybe completed), release stuff we don't need
  1222. pmk->Release();
  1223. pbc->Release();
  1224. if (pstm)
  1225. pstm->Release();
  1226. return hr;
  1227. LErrExit:
  1228. DBG_WARN("Error in CUrlDownload::BeginDownloadWithUrlMon");
  1229. if (pbc) pbc->Release();
  1230. if (pmk) pmk->Release();
  1231. if (pstm) pstm->Release();
  1232. SAFERELEASE(m_pCbsc);
  1233. return hr;
  1234. } // CUrlDownload::BeginDownloadWithUrlMon
  1235. void CUrlDownload::BSC_OnStartBinding()
  1236. {
  1237. DBG("BSC_OnStartBinding");
  1238. }
  1239. // We only get this call if we're not downloading with the browser.
  1240. void CUrlDownload::BSC_OnStopBinding(HRESULT hrStatus, IStream *pStm)
  1241. {
  1242. TraceMsg(TF_THISMODULE, "BSC_OnStopBinding (hrStatus=0x%08x)", (long)hrStatus);
  1243. ASSERT(m_pCbsc);
  1244. // It is ok to not have stream when we requested it (robots.txt)
  1245. // ASSERT(( pStm && (m_iOptions & BDU2_NEEDSTREAM)) ||
  1246. // (!pStm && !(m_iOptions & BDU2_NEEDSTREAM)));
  1247. ASSERT(!pStm || (m_iOptions & BDU2_NEEDSTREAM));
  1248. ASSERT(!m_pStm);
  1249. // Save stream for caller if they requested it
  1250. // We keep it until the release it (ReleaseStream) or nav to another url
  1251. if (pStm && (m_iOptions & BDU2_NEEDSTREAM))
  1252. {
  1253. if (m_pStm) m_pStm->Release();
  1254. m_pStm = pStm;
  1255. m_pStm->AddRef();
  1256. }
  1257. // Send OnDownloadComplete, stop the timer
  1258. if (m_iMethod == BDU2_HEADONLY && m_pstLastModified)
  1259. hrStatus = S_OK; // We got what we came for (hrStatus will be E_ABORT)
  1260. if (m_hrStatus != INET_E_AGENT_BIND_IN_PROGRESS)
  1261. OnDownloadComplete(SUCCEEDED(hrStatus) ? BDU2_ERROR_NONE : BDU2_ERROR_GENERAL);
  1262. else
  1263. {
  1264. DBG("Not calling OnDownloadComplete; synchronous bind");
  1265. m_hrStatus = hrStatus;
  1266. }
  1267. m_fbscValid = FALSE;
  1268. SAFE_RELEASE_BSC();
  1269. }
  1270. void CUrlDownload::BSC_OnProgress(ULONG ulProgress, ULONG ulProgressMax)
  1271. {
  1272. // extend our timer
  1273. if (m_iTimerID)
  1274. StartTimer();
  1275. }
  1276. void CUrlDownload::BSC_FoundLastModified(SYSTEMTIME *pstLastModified)
  1277. {
  1278. DBG("Received last modified time");
  1279. SAFELOCALFREE(m_pstLastModified);
  1280. m_pstLastModified = (SYSTEMTIME *)MemAlloc(LMEM_FIXED, sizeof(SYSTEMTIME));
  1281. if (m_pstLastModified)
  1282. {
  1283. CopyMemory(m_pstLastModified, pstLastModified, sizeof(SYSTEMTIME));
  1284. }
  1285. }
  1286. void CUrlDownload::BSC_FoundMimeType(CLIPFORMAT cf)
  1287. {
  1288. TraceMsg(TF_THISMODULE, "FoundMimeType %d", (int)cf);
  1289. BOOL fAbort = FALSE, fBrowser=FALSE;
  1290. HRESULT hr=S_OK;
  1291. // Abort if not html if necessary.
  1292. if ((m_iOptions & BDU2_FAIL_IF_NOT_HTML) && (cf != g_cfHTML))
  1293. {
  1294. DBG("Aborting non-HTML download");
  1295. fAbort = TRUE;
  1296. OnDownloadComplete(BDU2_ERROR_NOT_HTML);
  1297. }
  1298. // Abort the UrlMon download if necessary. Fire off
  1299. // a browser download if necessary.
  1300. if (((m_iMethod == BDU2_SMART) || (m_iMethod == BDU2_SNIFF)) && (cf == g_cfHTML))
  1301. {
  1302. // Switch into the browser.
  1303. ASSERT(m_pwszURL);
  1304. if (m_pwszURL &&
  1305. (m_dwResponseCode != 401)) // Don't bother if it's auth failure
  1306. {
  1307. DBG("Switching UrlMon download into browser");
  1308. hr = BeginDownloadWithBrowser(m_pwszURL);
  1309. if (SUCCEEDED(hr))
  1310. fBrowser = TRUE;
  1311. }
  1312. }
  1313. if (fAbort || fBrowser)
  1314. {
  1315. // Disconnect the BSC so that we don't get any more notifications.
  1316. // If we're switching into the browser, don't abort the UrlMon
  1317. // download to help avoid getting multiple GET requests. We do
  1318. // disconnect the BSC but still maintain a ref to it so we abort
  1319. // it if necessary.
  1320. ASSERT(m_pCbsc);
  1321. if (m_pCbsc)
  1322. {
  1323. m_pCbsc->SetParent(NULL); // We don't want OnStopBinding
  1324. if (fAbort)
  1325. {
  1326. m_pCbsc->Abort();
  1327. m_pCbsc->Release();
  1328. m_pCbsc=NULL;
  1329. m_fbscValid = FALSE;
  1330. }
  1331. }
  1332. }
  1333. }
  1334. // Returns content for Accept-Language header
  1335. LPCWSTR CUrlDownload::GetAcceptLanguages()
  1336. {
  1337. if (0 == m_iLangStatus)
  1338. {
  1339. DWORD cchLang = ARRAYSIZE(m_achLang);
  1340. if (SUCCEEDED(::GetAcceptLanguagesW(m_achLang, &cchLang)))
  1341. {
  1342. m_iLangStatus = 1;
  1343. }
  1344. else
  1345. {
  1346. m_iLangStatus = 2;
  1347. }
  1348. }
  1349. if (1 == m_iLangStatus)
  1350. {
  1351. return m_achLang;
  1352. }
  1353. return NULL;
  1354. }
  1355. HRESULT CUrlDownload::ProgressBytes(DWORD dwBytes)
  1356. {
  1357. if (m_dwMaxSize > 0 && dwBytes > m_dwMaxSize)
  1358. {
  1359. TraceMsg(TF_THISMODULE, "CUrlDownload MaxSize exceeded aborting. %d of %d bytes", dwBytes, m_dwMaxSize);
  1360. AbortDownload(BDU2_ERROR_MAXSIZE);
  1361. return E_ABORT;
  1362. }
  1363. return S_OK;
  1364. }
  1365. //---------------------------------------------------------------
  1366. // IServiceProvider
  1367. STDMETHODIMP CUrlDownload::QueryService(REFGUID guidService, REFIID riid, void **ppvObject)
  1368. {
  1369. if ((SID_SHlinkFrame == guidService && IID_IHlinkFrame == riid) ||
  1370. (IID_IAuthenticate == guidService && IID_IAuthenticate == riid) ||
  1371. (SID_SInternetSecurityManager == guidService && IID_IInternetSecurityManager == riid) ||
  1372. (IID_IHttpSecurity == guidService && IID_IHttpSecurity == riid))
  1373. {
  1374. return QueryInterface(riid, ppvObject);
  1375. }
  1376. else
  1377. {
  1378. *ppvObject = NULL;
  1379. return E_NOINTERFACE;
  1380. }
  1381. }
  1382. //---------------------------------------------------------------
  1383. //IHttpSecurity
  1384. STDMETHODIMP CUrlDownload::OnSecurityProblem(DWORD dwProblem)
  1385. {
  1386. return S_FALSE;
  1387. }
  1388. STDMETHODIMP CUrlDownload::GetWindow( REFGUID rguidReason, HWND *phwnd ) {
  1389. if(phwnd && m_hwndMe) {
  1390. *phwnd = m_hwndMe;
  1391. } else
  1392. return E_FAIL;
  1393. return S_OK;
  1394. }
  1395. //---------------------------------------------------------------
  1396. // IAuthenticate
  1397. STDMETHODIMP CUrlDownload::Authenticate(HWND *phwnd, LPWSTR *ppszUsername, LPWSTR *ppszPassword)
  1398. {
  1399. HRESULT hr;
  1400. ASSERT(phwnd && ppszUsername && ppszPassword);
  1401. *phwnd = (HWND)-1;
  1402. *ppszUsername = NULL;
  1403. *ppszPassword = NULL;
  1404. if (m_pParent)
  1405. hr = m_pParent->OnAuthenticate(phwnd, ppszUsername, ppszPassword);
  1406. else
  1407. hr = E_NOTIMPL;
  1408. TraceMsg(TF_THISMODULE, "CUrlDownload::Authenticate returning hr=%08x", hr);
  1409. return hr;
  1410. }
  1411. //---------------------------------------------------------------
  1412. // IHlinkFrame
  1413. STDMETHODIMP CUrlDownload::SetBrowseContext(IHlinkBrowseContext *pihlbc)
  1414. {
  1415. DBG_WARN("CUrlDownload::SetBrowseContext() not implemented");
  1416. return E_NOTIMPL;
  1417. }
  1418. STDMETHODIMP CUrlDownload::GetBrowseContext(IHlinkBrowseContext **ppihlbc)
  1419. {
  1420. DBG_WARN("CUrlDownload::GetBrowseContext() not implemented");
  1421. return E_NOTIMPL;
  1422. }
  1423. STDMETHODIMP CUrlDownload::Navigate(DWORD grfHLNF, LPBC pbc, IBindStatusCallback *pibsc, IHlink *pihlNavigate)
  1424. {
  1425. // We should only get a call through IHlinkFrame->Navigate()
  1426. // when the webcrawler has submitted a form for authentication.
  1427. // Bail out if that's not the case.
  1428. if (!m_fFormSubmitted)
  1429. {
  1430. DBG_WARN("CUrlDownload::Navigate() without a form submission!!!");
  1431. return E_NOTIMPL;
  1432. }
  1433. // Our timer has already been started. If this fails, OnDownloadComplete will get
  1434. // called when we time out.
  1435. // We don't support a wide variety of parameters.
  1436. ASSERT(grfHLNF == 0);
  1437. ASSERT(pbc);
  1438. ASSERT(pibsc);
  1439. ASSERT(pihlNavigate);
  1440. // Get the moniker from IHlink
  1441. HRESULT hr;
  1442. IMoniker *pmk = NULL;
  1443. hr = pihlNavigate->GetMonikerReference(HLINKGETREF_ABSOLUTE, &pmk, NULL);
  1444. if (SUCCEEDED(hr))
  1445. {
  1446. // Load the URL with the post data.
  1447. // WARNING: What if we get redirected to something other than HTML? (beta 2)
  1448. hr = m_pPersistMk->Load(FALSE, pmk, pbc, 0);
  1449. SAFERELEASE(pmk);
  1450. if (SUCCEEDED(hr))
  1451. {
  1452. m_fBrowserValid = TRUE;
  1453. StartTimer(); // Start our timeout
  1454. // Need to wait again.
  1455. m_fWaitingForReadyState = TRUE;
  1456. DBG("CUrlDownload::Navigate (IHLinkFrame) succeeded");
  1457. }
  1458. }
  1459. return hr;
  1460. }
  1461. STDMETHODIMP CUrlDownload::OnNavigate(DWORD grfHLNF, IMoniker *pimkTarget, LPCWSTR pwzLocation, LPCWSTR pwzFriendlyName, DWORD dwreserved)
  1462. {
  1463. DBG_WARN("CUrlDownload::OnNavigate() not implemented");
  1464. return E_NOTIMPL;
  1465. }
  1466. STDMETHODIMP CUrlDownload::UpdateHlink(ULONG uHLID, IMoniker *pimkTarget, LPCWSTR pwzLocation, LPCWSTR pwzFriendlyName)
  1467. {
  1468. DBG_WARN("CUrlDownload::UpdateHlink() not implemented");
  1469. return E_NOTIMPL;
  1470. }
  1471. //---------------------------------------------------------------------
  1472. // IInternetSecurityManager interface
  1473. // Used to override security to allow form submits, for form auth sites
  1474. HRESULT CUrlDownload::SetSecuritySite(IInternetSecurityMgrSite *pSite)
  1475. {
  1476. return E_NOTIMPL;
  1477. }
  1478. HRESULT CUrlDownload::GetSecuritySite(IInternetSecurityMgrSite **ppSite)
  1479. {
  1480. return E_NOTIMPL;
  1481. }
  1482. HRESULT CUrlDownload::MapUrlToZone(LPCWSTR pwszUrl, DWORD *pdwZone, DWORD dwFlags)
  1483. {
  1484. return INET_E_DEFAULT_ACTION;
  1485. }
  1486. HRESULT CUrlDownload::GetSecurityId(LPCWSTR pwszUrl, BYTE *pbSecurityId, DWORD *pcbSecurityId, DWORD_PTR dwReserved)
  1487. {
  1488. return INET_E_DEFAULT_ACTION;
  1489. }
  1490. HRESULT CUrlDownload::ProcessUrlAction(LPCWSTR pwszUrl, DWORD dwAction, BYTE __RPC_FAR *pPolicy, DWORD cbPolicy, BYTE *pContext, DWORD cbContext, DWORD dwFlags, DWORD dwReserved)
  1491. {
  1492. if ((dwAction == URLACTION_HTML_SUBMIT_FORMS_TO) ||
  1493. (dwAction == URLACTION_HTML_SUBMIT_FORMS_FROM))
  1494. {
  1495. return S_OK;
  1496. }
  1497. return INET_E_DEFAULT_ACTION;
  1498. }
  1499. HRESULT CUrlDownload::QueryCustomPolicy(LPCWSTR pwszUrl, REFGUID guidKey, BYTE **ppPolicy, DWORD *pcbPolicy, BYTE *pContext, DWORD cbContext, DWORD dwReserved)
  1500. {
  1501. return INET_E_DEFAULT_ACTION;
  1502. }
  1503. HRESULT CUrlDownload::SetZoneMapping(DWORD dwZone, LPCWSTR lpszPattern, DWORD dwFlags)
  1504. {
  1505. return INET_E_DEFAULT_ACTION;
  1506. }
  1507. HRESULT CUrlDownload::GetZoneMappings(DWORD dwZone, IEnumString **ppenumString, DWORD dwFlags)
  1508. {
  1509. return INET_E_DEFAULT_ACTION;
  1510. }
  1511. //---------------------------------------------------------------
  1512. // CUrlDownload_BSC class
  1513. //---------------------------------------------------------------
  1514. CUrlDownload_BSC::CUrlDownload_BSC(
  1515. BDUMethod iMethod,
  1516. BDUOptions iOptions,
  1517. LPTSTR pszLocalFile)
  1518. {
  1519. // Maintain global count of objects
  1520. DllAddRef();
  1521. m_cRef = 1;
  1522. m_iMethod = iMethod;
  1523. m_iOptions = iOptions;
  1524. if (NULL != pszLocalFile)
  1525. {
  1526. m_pszLocalFileDest = StrDup(pszLocalFile);
  1527. if (m_iMethod != BDU2_URLMON)
  1528. {
  1529. DBG_WARN("CUrlDownload_BSC changing method to URLMON (local file specified)");
  1530. m_iMethod = BDU2_URLMON;
  1531. }
  1532. }
  1533. }
  1534. CUrlDownload_BSC::~CUrlDownload_BSC()
  1535. {
  1536. // Maintain global count of objects
  1537. DllRelease();
  1538. ASSERT(!m_pBinding);
  1539. SAFERELEASE(m_pstm);
  1540. SAFELOCALFREE(m_pszLocalFileDest);
  1541. SAFELOCALFREE(m_pwszLocalFileSrc);
  1542. }
  1543. void CUrlDownload_BSC::SetParent(CUrlDownload *pUrlDownload)
  1544. {
  1545. m_pParent = pUrlDownload;
  1546. }
  1547. HRESULT CUrlDownload_BSC::Abort()
  1548. {
  1549. if (m_pBinding)
  1550. {
  1551. return m_pBinding->Abort();
  1552. }
  1553. return S_FALSE;
  1554. }
  1555. STDMETHODIMP CUrlDownload_BSC::QueryInterface(REFIID riid, void** ppv)
  1556. {
  1557. *ppv = NULL;
  1558. if (riid==IID_IUnknown || riid==IID_IBindStatusCallback)
  1559. {
  1560. *ppv = (IBindStatusCallback *)this;
  1561. AddRef();
  1562. return S_OK;
  1563. }
  1564. if (riid==IID_IHttpNegotiate)
  1565. {
  1566. *ppv = (IHttpNegotiate *)this;
  1567. AddRef();
  1568. return S_OK;
  1569. }
  1570. if (riid==IID_IAuthenticate)
  1571. {
  1572. *ppv = (IAuthenticate *)this;
  1573. AddRef();
  1574. return S_OK;
  1575. }
  1576. return E_NOINTERFACE;
  1577. }
  1578. //---------------------------------------------------------------
  1579. // IAuthenticate
  1580. STDMETHODIMP CUrlDownload_BSC::Authenticate(HWND *phwnd, LPWSTR *ppszUsername, LPWSTR *ppszPassword)
  1581. { //copied from CUrlDownload::Authenticate (to whom we pass off anyway)
  1582. HRESULT hr;
  1583. ASSERT(phwnd && ppszUsername && ppszPassword);
  1584. *phwnd = (HWND)-1;
  1585. *ppszUsername = NULL;
  1586. *ppszPassword = NULL;
  1587. // Only try this once. If Urlmon asks again, fail it and flag an error.
  1588. if (m_fTriedAuthenticate)
  1589. {
  1590. if (m_pParent)
  1591. {
  1592. m_pParent->m_dwResponseCode = 401;
  1593. DBG("CUrlDownload_BSC::Authenticate called twice. Faking 401 response");
  1594. }
  1595. return E_FAIL;
  1596. }
  1597. m_fTriedAuthenticate = TRUE;
  1598. if (m_pParent)
  1599. hr = m_pParent->Authenticate(phwnd, ppszUsername, ppszPassword);
  1600. else
  1601. hr = E_NOTIMPL;
  1602. if (FAILED(hr) && m_pParent)
  1603. {
  1604. m_pParent->m_dwResponseCode = 401;
  1605. DBG("CUrlDownload_BSC::Authenticate called; no username/pass. Faking 401 response");
  1606. }
  1607. TraceMsg(TF_THISMODULE, "CUrlDownload_BSC::Authenticate returning hr=%08x", hr);
  1608. return hr;
  1609. }
  1610. STDMETHODIMP CUrlDownload_BSC::OnStartBinding(
  1611. DWORD dwReserved,
  1612. IBinding* pbinding)
  1613. {
  1614. m_fSentMimeType = FALSE;
  1615. if (m_pBinding != NULL)
  1616. m_pBinding->Release();
  1617. m_pBinding = pbinding;
  1618. if (m_pBinding != NULL)
  1619. {
  1620. m_pBinding->AddRef();
  1621. }
  1622. if (m_pParent)
  1623. m_pParent->BSC_OnStartBinding();
  1624. return S_OK;
  1625. }
  1626. // ---------------------------------------------------------------------------
  1627. // %%Function: CUrlDownload_BSC::GetPriority
  1628. // ---------------------------------------------------------------------------
  1629. STDMETHODIMP
  1630. CUrlDownload_BSC::GetPriority(LONG* pnPriority)
  1631. {
  1632. return E_NOTIMPL;
  1633. }
  1634. // ---------------------------------------------------------------------------
  1635. // %%Function: CUrlDownload_BSC::OnLowResource
  1636. // ---------------------------------------------------------------------------
  1637. STDMETHODIMP
  1638. CUrlDownload_BSC::OnLowResource(DWORD dwReserved)
  1639. {
  1640. return E_NOTIMPL;
  1641. }
  1642. // ---------------------------------------------------------------------------
  1643. // %%Function: CUrlDownload_BSC::OnProgress
  1644. // ---------------------------------------------------------------------------
  1645. STDMETHODIMP
  1646. CUrlDownload_BSC::OnProgress(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
  1647. {
  1648. // TraceMsg(TF_THISMODULE, "cbsc::OnProgress %d of %d : msg %ws", ulProgress, ulProgressMax, szStatusText);
  1649. /*
  1650. if (ulStatusCode==BINDSTATUS_USINGCACHEDCOPY)
  1651. */
  1652. if (ulStatusCode == BINDSTATUS_REDIRECTING)
  1653. {
  1654. DBG("CUrlDownload_BSC::OnProgress getting redirected url");
  1655. TraceMsg(TF_THISMODULE, "New url=%ws", szStatusText);
  1656. if (m_pParent)
  1657. {
  1658. if (m_pParent->m_pwszURL) MemFree(m_pParent->m_pwszURL);
  1659. m_pParent->m_pwszURL = StrDupW(szStatusText);
  1660. }
  1661. }
  1662. if ((ulStatusCode == BINDSTATUS_CACHEFILENAMEAVAILABLE) && m_pszLocalFileDest)
  1663. {
  1664. ASSERT(!m_pwszLocalFileSrc);
  1665. DBG("CUrlDownload_BSC::OnProgress Getting local file name");
  1666. if (!m_pwszLocalFileSrc)
  1667. m_pwszLocalFileSrc = StrDupW(szStatusText);
  1668. }
  1669. if (m_pParent)
  1670. m_pParent->BSC_OnProgress(ulProgress, ulProgressMax);
  1671. // 14032: If dialmon is around, tell it that something is going on
  1672. IndicateDialmonActivity();
  1673. return S_OK;
  1674. }
  1675. STDMETHODIMP CUrlDownload_BSC::OnStopBinding(
  1676. HRESULT hrStatus,
  1677. LPCWSTR pszError)
  1678. {
  1679. #ifdef DEBUG
  1680. if (hrStatus && (hrStatus != E_ABORT))
  1681. TraceMsg(TF_THISMODULE,
  1682. "cbsc: File download Failed hr=%08x.", (int)hrStatus);
  1683. #endif
  1684. if (m_pParent)
  1685. m_pParent->BSC_OnStopBinding(hrStatus, (m_iOptions&BDU2_NEEDSTREAM) ? m_pstm : NULL);
  1686. // We should have neither or both of these
  1687. ASSERT(!m_pwszLocalFileSrc == !m_pszLocalFileDest);
  1688. if (m_pwszLocalFileSrc && m_pszLocalFileDest)
  1689. {
  1690. // Copy or move file from cache file to file/directory requested
  1691. // We have a LPWSTR source name and an LPTSTR destination
  1692. TCHAR szSrc[MAX_PATH];
  1693. TCHAR szDest[MAX_PATH];
  1694. LPTSTR pszSrcFileName, pszDest=NULL;
  1695. MyOleStrToStrN(szSrc, MAX_PATH, m_pwszLocalFileSrc);
  1696. // Combine paths to find destination filename if necessary
  1697. if (PathIsDirectory(m_pszLocalFileDest))
  1698. {
  1699. pszSrcFileName = PathFindFileName(szSrc);
  1700. if (pszSrcFileName)
  1701. {
  1702. PathCombine(szDest, m_pszLocalFileDest, pszSrcFileName);
  1703. pszDest = szDest;
  1704. }
  1705. }
  1706. else
  1707. {
  1708. pszDest = m_pszLocalFileDest;
  1709. }
  1710. if (pszDest)
  1711. {
  1712. TraceMsg(TF_THISMODULE, "Copying file\n%s\n to file \n%s", szSrc, pszDest);
  1713. CopyFile(szSrc, pszDest, FALSE);
  1714. }
  1715. else
  1716. DBG_WARN("Unable to get dest path for local file");
  1717. }
  1718. SAFERELEASE(m_pstm);
  1719. SAFERELEASE(m_pBinding);
  1720. return S_OK;
  1721. }
  1722. STDMETHODIMP CUrlDownload_BSC::GetBindInfo(
  1723. DWORD *pgrfBINDF,
  1724. BINDINFO *pbindInfo)
  1725. {
  1726. if ( !pgrfBINDF || !pbindInfo || !pbindInfo->cbSize )
  1727. return E_INVALIDARG;
  1728. *pgrfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_NO_UI;
  1729. if (m_pszLocalFileDest)
  1730. *pgrfBINDF |= BINDF_NEEDFILE;
  1731. if (m_pParent && m_pParent->m_fSetResync)
  1732. *pgrfBINDF |= BINDF_RESYNCHRONIZE;
  1733. if (m_pParent && (m_pParent->m_lBindFlags & DLCTL_FORCEOFFLINE))
  1734. *pgrfBINDF |= BINDF_OFFLINEOPERATION;
  1735. // clear BINDINFO but keep its size
  1736. DWORD cbSize = pbindInfo->cbSize;
  1737. ZeroMemory( pbindInfo, cbSize );
  1738. pbindInfo->cbSize = cbSize;
  1739. pbindInfo->dwBindVerb = BINDVERB_GET;
  1740. if (m_iMethod == BDU2_HEADONLY)
  1741. {
  1742. LPWSTR pwszVerb = (LPWSTR) CoTaskMemAlloc(sizeof(c_wszHeadVerb));
  1743. if (pwszVerb)
  1744. {
  1745. CopyMemory(pwszVerb, c_wszHeadVerb, sizeof(c_wszHeadVerb));
  1746. pbindInfo->dwBindVerb = BINDVERB_CUSTOM;
  1747. pbindInfo->szCustomVerb = pwszVerb;
  1748. DBG("Using 'HEAD' custom bind verb.");
  1749. }
  1750. else
  1751. {
  1752. DBG_WARN("MemAlloc failure CUrlDownload_BSC::GetBindInfo");
  1753. return E_OUTOFMEMORY;
  1754. }
  1755. }
  1756. return S_OK;
  1757. }
  1758. STDMETHODIMP CUrlDownload_BSC::OnDataAvailable(
  1759. DWORD grfBSCF,
  1760. DWORD dwSize,
  1761. FORMATETC* pfmtetc,
  1762. STGMEDIUM* pstgmed)
  1763. {
  1764. TraceMsg(TF_THISMODULE, "%d bytes on page so far (urlmon)", dwSize);
  1765. if (m_pParent)
  1766. if (FAILED(m_pParent->ProgressBytes(dwSize)))
  1767. return S_OK;
  1768. // Get the Stream passed if we want a local file (to lock the file)
  1769. // We just ignore any data in any case
  1770. if (BSCF_FIRSTDATANOTIFICATION & grfBSCF)
  1771. {
  1772. if (!m_pstm && (pstgmed->tymed==TYMED_ISTREAM) &&
  1773. (m_pszLocalFileDest || (m_iOptions & BDU2_NEEDSTREAM)))
  1774. {
  1775. m_pstm = pstgmed->pstm;
  1776. if (m_pstm)
  1777. m_pstm->AddRef();
  1778. }
  1779. }
  1780. if (!m_fSentMimeType && pfmtetc && m_pParent)
  1781. {
  1782. m_pParent->BSC_FoundMimeType(pfmtetc->cfFormat);
  1783. m_fSentMimeType = TRUE;
  1784. }
  1785. if (BSCF_LASTDATANOTIFICATION & grfBSCF)
  1786. {
  1787. DBG("cbsc: LastDataNotification");
  1788. }
  1789. return S_OK;
  1790. } // CUrlDownload_BSC::OnDataAvailable
  1791. STDMETHODIMP CUrlDownload_BSC::OnObjectAvailable(REFIID riid, IUnknown* punk)
  1792. {
  1793. return E_NOTIMPL;
  1794. }
  1795. STDMETHODIMP CUrlDownload_BSC::BeginningTransaction(
  1796. LPCWSTR szURL, LPCWSTR szHeaders,
  1797. DWORD dwReserved, LPWSTR *pszAdditionalHeaders)
  1798. {
  1799. // Add User-Agent and Accept-Language headers
  1800. DBG("CUrlDownload_BSC::BeginningTransaction returning headers");
  1801. LPCWSTR pwszAcceptLanguage;
  1802. int iUAlen=0, iALlen=0; // in chars, with \r\n, without null-term
  1803. LPWSTR pwsz;
  1804. LPCWSTR pwszUA = m_pParent ? m_pParent->GetUserAgent() : NULL;
  1805. pwszAcceptLanguage = (m_pParent) ? m_pParent->GetAcceptLanguages() : NULL;
  1806. if (pwszUA)
  1807. {
  1808. iUAlen = ARRAYSIZE(c_szUserAgentPrefix) + lstrlenW(pwszUA) + 1;
  1809. }
  1810. if (pwszAcceptLanguage)
  1811. {
  1812. iALlen = ARRAYSIZE(c_szAcceptLanguagePrefix) + lstrlenW(pwszAcceptLanguage)+1;
  1813. }
  1814. if (iUAlen || iALlen)
  1815. {
  1816. pwsz = (WCHAR *)CoTaskMemAlloc((iUAlen + iALlen + 1) * sizeof(WCHAR));
  1817. if (pwsz)
  1818. {
  1819. pwsz[0] = L'\0';
  1820. if (iUAlen)
  1821. {
  1822. StrCpyW(pwsz, c_szUserAgentPrefix);
  1823. StrCatW(pwsz, pwszUA);
  1824. StrCatW(pwsz, L"\r\n");
  1825. }
  1826. if (iALlen)
  1827. {
  1828. StrCatW(pwsz, c_szAcceptLanguagePrefix);
  1829. StrCatW(pwsz, pwszAcceptLanguage);
  1830. StrCatW(pwsz, L"\r\n");
  1831. }
  1832. ASSERT(lstrlenW(pwsz) == (iUAlen + iALlen));
  1833. *pszAdditionalHeaders = pwsz;
  1834. return S_OK;
  1835. }
  1836. }
  1837. return E_OUTOFMEMORY;
  1838. }
  1839. STDMETHODIMP CUrlDownload_BSC::OnResponse(
  1840. DWORD dwResponseCode, LPCWSTR szResponseHeaders,
  1841. LPCWSTR szRequestHeaders, LPWSTR *pszAdditionalRequestHeaders)
  1842. {
  1843. TraceMsg(TF_THISMODULE, "CUrlDownload_BSC::OnResponse - %d", dwResponseCode);
  1844. // If we sent a "HEAD" request, Urlmon will hang expecting data.
  1845. // Abort it here.
  1846. if (m_iMethod == BDU2_HEADONLY)
  1847. {
  1848. // First get the Last-Modified date from Urlmon
  1849. IWinInetHttpInfo *pInfo;
  1850. if (m_pParent
  1851. && SUCCEEDED(m_pBinding->QueryInterface(IID_IWinInetHttpInfo, (void **)&pInfo)
  1852. && pInfo))
  1853. {
  1854. SYSTEMTIME st;
  1855. DWORD dwSize = sizeof(st), dwZero=0;
  1856. if (SUCCEEDED(pInfo->QueryInfo(HTTP_QUERY_FLAG_SYSTEMTIME | HTTP_QUERY_LAST_MODIFIED,
  1857. (LPVOID) &st, &dwSize, &dwZero, 0)))
  1858. {
  1859. m_pParent->BSC_FoundLastModified(&st);
  1860. }
  1861. pInfo->Release();
  1862. }
  1863. Abort(); // FEATURE: return E_ABORT and handle abort internally
  1864. }
  1865. if (m_pParent)
  1866. m_pParent->m_dwResponseCode = dwResponseCode;
  1867. else
  1868. DBG_WARN("CUrlDownload_BSC::OnResponse - Parent already NULL");
  1869. return S_OK;
  1870. }
  1871. //
  1872. // IOleClientSite
  1873. //
  1874. STDMETHODIMP CUrlDownload:: SaveObject(void)
  1875. {
  1876. return E_NOTIMPL;
  1877. }
  1878. STDMETHODIMP CUrlDownload:: GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, IMoniker **ppmk)
  1879. {
  1880. DBG("CUrlDownload::GetMoniker returning failure");
  1881. return E_NOTIMPL;
  1882. }
  1883. STDMETHODIMP CUrlDownload:: GetContainer(IOleContainer **ppContainer)
  1884. {
  1885. return E_NOTIMPL;
  1886. }
  1887. STDMETHODIMP CUrlDownload:: ShowObject(void)
  1888. {
  1889. return E_NOTIMPL;
  1890. }
  1891. STDMETHODIMP CUrlDownload:: OnShowWindow(BOOL fShow)
  1892. {
  1893. return E_NOTIMPL;
  1894. }
  1895. STDMETHODIMP CUrlDownload:: RequestNewObjectLayout(void)
  1896. {
  1897. return E_NOTIMPL;
  1898. }
  1899. // ParseRefreshContent was lifted in its entirety from shdocvw\basesb.cpp
  1900. BOOL ParseRefreshContent(LPWSTR pwzContent,
  1901. UINT * puiDelay, LPWSTR pwzUrlBuf, UINT cchUrlBuf)
  1902. {
  1903. // We are parsing the following string:
  1904. //
  1905. // [ws]* [0-9]+ [ws]* ; [ws]* url [ws]* = [ws]* { ' | " } [any]* { ' | " }
  1906. //
  1907. // Netscape insists that the string begins with a delay. If not, it
  1908. // ignores the entire directive. There can be more than one URL mentioned,
  1909. // and the last one wins. An empty URL is treated the same as not having
  1910. // a URL at all. An empty URL which follows a non-empty URL resets
  1911. // the previous URL.
  1912. enum { PRC_START, PRC_DIG, PRC_DIG_WS, PRC_SEMI, PRC_SEMI_URL,
  1913. PRC_SEMI_URL_EQL, PRC_SEMI_URL_EQL_ANY };
  1914. #define ISSPACE(ch) (((ch) == 32) || ((unsigned)((ch) - 9)) <= 13 - 9)
  1915. UINT uiState = PRC_START;
  1916. UINT uiDelay = 0;
  1917. LPWSTR pwz = pwzContent;
  1918. LPWSTR pwzUrl = NULL;
  1919. UINT cchUrl = 0;
  1920. WCHAR wch, wchDel = 0;
  1921. *pwzUrlBuf = 0;
  1922. do
  1923. {
  1924. wch = *pwz;
  1925. switch (uiState)
  1926. {
  1927. case PRC_START:
  1928. if (wch >= TEXT('0') && wch <= TEXT('9'))
  1929. {
  1930. uiState = PRC_DIG;
  1931. uiDelay = wch - TEXT('0');
  1932. }
  1933. else if (!ISSPACE(wch))
  1934. goto done;
  1935. break;
  1936. case PRC_DIG:
  1937. if (wch >= TEXT('0') && wch <= TEXT('9'))
  1938. uiDelay = uiDelay * 10 + wch - TEXT('0');
  1939. else if (ISSPACE(wch))
  1940. uiState = PRC_DIG_WS;
  1941. else if (wch == TEXT(';'))
  1942. uiState = PRC_SEMI;
  1943. else
  1944. goto done;
  1945. break;
  1946. case PRC_DIG_WS:
  1947. if (wch == TEXT(';'))
  1948. uiState = PRC_SEMI;
  1949. else if (!ISSPACE(wch))
  1950. goto done;
  1951. break;
  1952. case PRC_SEMI:
  1953. if ( (wch == TEXT('u') || wch == TEXT('U'))
  1954. && (pwz[1] == TEXT('r') || pwz[1] == TEXT('R'))
  1955. && (pwz[2] == TEXT('l') || pwz[2] == TEXT('L')))
  1956. {
  1957. uiState = PRC_SEMI_URL;
  1958. pwz += 2;
  1959. }
  1960. else if (!ISSPACE(wch) && wch != TEXT(';'))
  1961. goto done;
  1962. break;
  1963. case PRC_SEMI_URL:
  1964. if (wch == TEXT('='))
  1965. {
  1966. uiState = PRC_SEMI_URL_EQL;
  1967. *pwzUrlBuf = 0;
  1968. }
  1969. else if (wch == TEXT(';'))
  1970. uiState = PRC_SEMI;
  1971. else if (!ISSPACE(wch))
  1972. goto done;
  1973. break;
  1974. case PRC_SEMI_URL_EQL:
  1975. if (wch == TEXT(';'))
  1976. uiState = PRC_SEMI;
  1977. else if (!ISSPACE(wch))
  1978. {
  1979. uiState = PRC_SEMI_URL_EQL_ANY;
  1980. pwzUrl = pwzUrlBuf;
  1981. cchUrl = cchUrlBuf;
  1982. if (wch == TEXT('\'')|| wch == TEXT('\"'))
  1983. wchDel = wch;
  1984. else
  1985. {
  1986. wchDel = 0;
  1987. *pwzUrl++ = wch;
  1988. cchUrl--;
  1989. }
  1990. }
  1991. break;
  1992. case PRC_SEMI_URL_EQL_ANY:
  1993. if ( !wch
  1994. || ( wchDel && wch == wchDel)
  1995. || (!wchDel && wch == L';'))
  1996. {
  1997. *pwzUrl = 0;
  1998. uiState = wch == TEXT(';') ? PRC_SEMI : PRC_DIG_WS;
  1999. }
  2000. else if (cchUrl > 1)
  2001. {
  2002. *pwzUrl++ = wch;
  2003. cchUrl--;
  2004. }
  2005. break;
  2006. }
  2007. ++pwz;
  2008. } while (wch);
  2009. done:
  2010. *puiDelay = uiDelay;
  2011. return(uiState >= PRC_DIG);
  2012. } // ParseRefreshContent