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.

2379 lines
66 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. StrCpyNW(pwszAppend, c_wszUserAgentAppend, iLenNew - (int)(pwszAppend - m_pwszUserAgent));
  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. // See if this protocol will give us something for the cache
  760. BOOL fUsesCache=FALSE;
  761. DWORD dwBufSize=0;
  762. CoInternetQueryInfo(lpURL, QUERY_USES_CACHE, 0,
  763. &fUsesCache, sizeof(fUsesCache), &dwBufSize, 0);
  764. if (!fUsesCache || (S_FALSE == ::IsValidURL(NULL, lpURL, 0)))
  765. return FALSE;
  766. return TRUE;
  767. }
  768. HRESULT CUrlDownload::GetRealURL(LPWSTR *ppwszURL)
  769. {
  770. *ppwszURL = NULL;
  771. if (!m_fBrowserValid)
  772. {
  773. if (m_pwszURL)
  774. *ppwszURL = StrDupW(m_pwszURL);
  775. }
  776. else
  777. {
  778. // Get the real URL from the browser in case we were redirected
  779. // We could optimize to do this only once
  780. ITargetContainer *pTarget=NULL;
  781. LPWSTR pwszThisUrl=NULL;
  782. if (m_pDocument)
  783. {
  784. m_pDocument->QueryInterface(IID_ITargetContainer, (void **)&pTarget);
  785. if (pTarget)
  786. {
  787. pTarget->GetFrameUrl(&pwszThisUrl);
  788. pTarget->Release();
  789. }
  790. }
  791. if (pwszThisUrl)
  792. {
  793. if (m_pwszURL) MemFree(m_pwszURL);
  794. m_pwszURL = StrDupW(pwszThisUrl);
  795. *ppwszURL = StrDupW(pwszThisUrl);
  796. CoTaskMemFree(pwszThisUrl);
  797. }
  798. else if (m_pwszURL)
  799. {
  800. *ppwszURL = StrDupW(m_pwszURL);
  801. }
  802. }
  803. return (*ppwszURL) ? S_OK : E_OUTOFMEMORY;
  804. }
  805. HRESULT CUrlDownload::GetDocument(IHTMLDocument2 **ppDoc)
  806. {
  807. HRESULT hr;
  808. if (!m_fBrowserValid)
  809. {
  810. DBG("GetDocument failing, m_fBrowserValid FALSE");
  811. *ppDoc = NULL;
  812. return E_FAIL;
  813. }
  814. *ppDoc = m_pDocument;
  815. if (m_pDocument)
  816. {
  817. m_pDocument->AddRef();
  818. hr = S_OK;
  819. }
  820. else
  821. hr = E_NOINTERFACE;
  822. return hr;
  823. }
  824. HRESULT CUrlDownload::GetStream(IStream **ppStm)
  825. {
  826. if (!m_pStm)
  827. {
  828. DBG("Stream not available, CUrlDownload::GetStream failing");
  829. *ppStm = NULL;
  830. return E_FAIL;
  831. }
  832. *ppStm = m_pStm;
  833. (*ppStm)->AddRef();
  834. return S_OK;
  835. }
  836. HRESULT CUrlDownload::GetLastModified(SYSTEMTIME *pstLastModified)
  837. {
  838. if (NULL == pstLastModified)
  839. return E_INVALIDARG;
  840. if (NULL == m_pstLastModified)
  841. return E_FAIL;
  842. CopyMemory(pstLastModified, m_pstLastModified, sizeof(SYSTEMTIME));
  843. return S_OK;
  844. }
  845. HRESULT CUrlDownload::GetResponseCode(DWORD *pdwResponseCode)
  846. {
  847. if (m_dwResponseCode == 0)
  848. return E_FAIL;
  849. *pdwResponseCode = m_dwResponseCode;
  850. return S_OK;
  851. }
  852. // Start or extend timer
  853. void CUrlDownload::StartTimer()
  854. {
  855. if (m_hwndMe)
  856. {
  857. if (!m_iTimerID)
  858. {
  859. m_iTimerID = 1;
  860. DBG("CUrlDownload Creating new timeout timer");
  861. }
  862. m_iTimerID = SetTimer(m_hwndMe, 1, 1000 * m_nTimeout, NULL);
  863. }
  864. }
  865. void CUrlDownload::StopTimer()
  866. {
  867. if (m_hwndMe && m_iTimerID)
  868. {
  869. DBG("CUrlDownload destroying timeout timer");
  870. KillTimer(m_hwndMe, m_iTimerID);
  871. m_iTimerID = 0;
  872. }
  873. }
  874. //
  875. // IUnknown of CUrlDownload
  876. //
  877. STDMETHODIMP CUrlDownload::QueryInterface(REFIID riid, void ** ppv)
  878. {
  879. *ppv=NULL;
  880. // Validate requested interface
  881. if (IID_IOleClientSite == riid)
  882. *ppv=(IOleClientSite *)this;
  883. else if (IID_IPropertyNotifySink == riid)
  884. *ppv=(IPropertyNotifySink *)this;
  885. else if (IID_IOleCommandTarget == riid)
  886. *ppv=(IOleCommandTarget *)this;
  887. else if (IID_IDispatch == riid)
  888. *ppv=(IDispatch *)this;
  889. else if (IID_IServiceProvider == riid)
  890. *ppv = (IServiceProvider *)this;
  891. else if (IID_IAuthenticate == riid)
  892. *ppv = (IAuthenticate *)this;
  893. else if (IID_IInternetSecurityManager == riid)
  894. *ppv = (IInternetSecurityManager *)this;
  895. else if (IID_IHttpSecurity == riid)
  896. *ppv = (IHttpSecurity *)this;
  897. else if ((IID_IUnknown == riid) ||
  898. (IID_IHlinkFrame == riid))
  899. *ppv = (IHlinkFrame *)this;
  900. else
  901. {
  902. // DBGIID("CUrlDownload::QueryInterface() failing", riid);
  903. }
  904. // Addref through the interface
  905. if (NULL != *ppv)
  906. {
  907. ((LPUNKNOWN)*ppv)->AddRef();
  908. return S_OK;
  909. }
  910. return E_NOINTERFACE;
  911. }
  912. STDMETHODIMP_(ULONG) CUrlDownload::AddRef(void)
  913. {
  914. return ++m_cRef;
  915. }
  916. STDMETHODIMP_(ULONG) CUrlDownload::Release(void)
  917. {
  918. if (0L != --m_cRef)
  919. return 1L;
  920. delete this;
  921. return 0L;
  922. }
  923. STDMETHODIMP CUrlDownload::GetTypeInfoCount(UINT *pctinfo)
  924. {
  925. return E_NOTIMPL;
  926. }
  927. STDMETHODIMP CUrlDownload::GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo **pptinfo)
  928. {
  929. return E_NOTIMPL;
  930. }
  931. STDMETHODIMP CUrlDownload::GetIDsOfNames(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID *rgdispid)
  932. {
  933. return E_NOTIMPL;
  934. }
  935. STDMETHODIMP CUrlDownload::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags,
  936. DISPPARAMS *pdispparams, VARIANT *pvarResult,
  937. EXCEPINFO *pexcepinfo, UINT *puArgErr)
  938. {
  939. if (!pvarResult)
  940. return E_INVALIDARG;
  941. ASSERT(pvarResult->vt == VT_EMPTY);
  942. if (wFlags == DISPATCH_PROPERTYGET)
  943. {
  944. HRESULT hr = DISP_E_MEMBERNOTFOUND;
  945. switch (dispidMember)
  946. {
  947. case DISPID_AMBIENT_DLCONTROL :
  948. TraceMsg(TF_THISMODULE, "Returning DLCONTROL ambient property 0x%08x", m_lBindFlags);
  949. pvarResult->vt = VT_I4;
  950. pvarResult->lVal = m_lBindFlags;
  951. hr = S_OK;
  952. break;
  953. case DISPID_AMBIENT_USERAGENT:
  954. DBG("Returning User Agent ambient property");
  955. pvarResult->bstrVal = SysAllocString(GetUserAgent());
  956. if (pvarResult->bstrVal != NULL)
  957. {
  958. pvarResult->vt = VT_BSTR;
  959. hr = S_OK;
  960. }
  961. break;
  962. }
  963. return hr;
  964. }
  965. return DISP_E_MEMBERNOTFOUND;
  966. }
  967. // IPropertyNotifySink
  968. STDMETHODIMP CUrlDownload::OnChanged(DISPID dispID)
  969. {
  970. // We've received a notification, extend our timer if it's currently running
  971. if (m_iTimerID)
  972. StartTimer();
  973. if ((DISPID_READYSTATE == dispID) ||
  974. (DISPID_UNKNOWN == dispID))
  975. {
  976. // Find out if we're done
  977. if (m_fWaitingForReadyState)
  978. {
  979. VARIANT varState;
  980. DISPPARAMS dp;
  981. VariantInit(&varState);
  982. if (SUCCEEDED(m_pDocument->Invoke(DISPID_READYSTATE,
  983. IID_NULL,
  984. GetUserDefaultLCID(),
  985. DISPATCH_PROPERTYGET,
  986. &dp,
  987. &varState, NULL, NULL)) &&
  988. V_VT(&varState)==VT_I4 &&
  989. V_I4(&varState)== READYSTATE_COMPLETE)
  990. {
  991. m_fWaitingForReadyState = FALSE;
  992. // Successful download. See if a client-pull is waiting.
  993. if (m_pwszClientPullURL)
  994. PostMessage(m_hwndMe, WM_URLDL_CLIENTPULL, 0, 0);
  995. else
  996. OnDownloadComplete(BDU2_ERROR_NONE);
  997. }
  998. }
  999. }
  1000. return S_OK;
  1001. }
  1002. STDMETHODIMP CUrlDownload::OnRequestEdit(DISPID dispID)
  1003. {
  1004. return S_OK;
  1005. }
  1006. // IOleCommandTarget
  1007. STDMETHODIMP CUrlDownload::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds,
  1008. OLECMD prgCmds[], OLECMDTEXT *pCmdText)
  1009. {
  1010. return OLECMDERR_E_UNKNOWNGROUP;
  1011. }
  1012. STDMETHODIMP CUrlDownload::Exec(const GUID *pguidCmdGroup, DWORD nCmdID,
  1013. DWORD nCmdexecopt, VARIANTARG *pvarargIn,
  1014. VARIANTARG *pvarargOut)
  1015. {
  1016. HRESULT hres = OLECMDERR_E_NOTSUPPORTED;
  1017. if (pguidCmdGroup == NULL)
  1018. {
  1019. switch(nCmdID)
  1020. {
  1021. case OLECMDID_SETPROGRESSPOS:
  1022. {
  1023. hres = S_OK;
  1024. VARIANT varBytes;
  1025. if (m_pOleCmdTarget)
  1026. {
  1027. varBytes.vt=VT_EMPTY;
  1028. m_pOleCmdTarget->Exec(&CGID_MSHTML, IDM_GETBYTESDOWNLOADED, 0, NULL, &varBytes);
  1029. if (varBytes.vt == VT_I4)
  1030. {
  1031. DWORD dwBytes = (DWORD) varBytes.lVal;
  1032. TraceMsg(TF_THISMODULE, "%d bytes on page so far (mshtml)", dwBytes);
  1033. ProgressBytes(dwBytes);
  1034. }
  1035. }
  1036. // 14032: If dialmon is around, tell it that something is going on
  1037. IndicateDialmonActivity();
  1038. }
  1039. break;
  1040. //
  1041. // The containee has found an http-equiv meta tag; handle it
  1042. // appropriately (client pull)
  1043. //
  1044. case OLECMDID_HTTPEQUIV_DONE:
  1045. hres = S_OK;
  1046. break;
  1047. case OLECMDID_HTTPEQUIV:
  1048. {
  1049. LPWSTR pwszEquivString = pvarargIn? pvarargIn->bstrVal : NULL;
  1050. BOOL fHasHeader = (pwszEquivString!=NULL);
  1051. if (pvarargIn && pvarargIn->vt != VT_BSTR)
  1052. return OLECMDERR_E_NOTSUPPORTED;
  1053. if (!fHasHeader || StrCmpNIW(c_wszRefresh, pwszEquivString, lstrlenW(c_wszRefresh)) == 0)
  1054. {
  1055. // Hit. Now do the right thing for this header
  1056. // We pass both the header and a pointer to the first char after
  1057. // ':', which is usually the delimiter handlers will look for.
  1058. LPWSTR pwszColon = fHasHeader ? StrChrW(pwszEquivString, ':') : NULL;
  1059. // Enforce the : at the end of the header
  1060. if (fHasHeader && !pwszColon)
  1061. {
  1062. return OLECMDERR_E_NOTSUPPORTED;
  1063. }
  1064. hres = HandleRefresh(pwszEquivString, pwszColon ? pwszColon+1:NULL,
  1065. (nCmdID == OLECMDID_HTTPEQUIV_DONE));
  1066. }
  1067. }
  1068. // if we return OLECMDERR_E_NOTSUPPORTED, we don't handle
  1069. // client pull
  1070. break;
  1071. }
  1072. }
  1073. if ((hres == OLECMDERR_E_NOTSUPPORTED) && m_pParent)
  1074. {
  1075. hres = m_pParent->OnOleCommandTargetExec(pguidCmdGroup, nCmdID, nCmdexecopt,
  1076. pvarargIn, pvarargOut);
  1077. }
  1078. return hres;
  1079. }
  1080. // The basic operation was lifted from shdocvw\basesb.cpp
  1081. HRESULT CUrlDownload::HandleRefresh(LPWSTR pwszEquivString, LPWSTR pwszContent, BOOL fDone)
  1082. {
  1083. unsigned int uiTimeout = 0;
  1084. WCHAR awch[INTERNET_MAX_URL_LENGTH];
  1085. if (fDone)
  1086. {
  1087. return S_OK; // fDone means we don't process this
  1088. }
  1089. // NSCompat: we only honor the first successfully parsed Refresh
  1090. if (m_pwszClientPullURL)
  1091. return S_OK;
  1092. if (!pwszContent ||
  1093. !ParseRefreshContent(pwszContent, &uiTimeout, awch, INTERNET_MAX_URL_LENGTH))
  1094. {
  1095. return OLECMDERR_E_NOTSUPPORTED; // cannot handle refresh w/o timeout
  1096. }
  1097. if (!awch[0])
  1098. {
  1099. DBG("CUrlDownload ignoring client-pull directive with no url");
  1100. return S_OK;
  1101. }
  1102. if (m_iNumClientPull >= MAX_CLIENT_PULL_NUM)
  1103. {
  1104. DBG("Max # client pulls exceeded; ignoring client pull directive");
  1105. return S_OK;
  1106. }
  1107. TraceMsg(TF_THISMODULE, "CUrlDownload client pull (refresh=%d) url=%ws", uiTimeout, awch);
  1108. if (uiTimeout > MAX_CLIENT_PULL_TIMEOUT)
  1109. {
  1110. DBG("Ignoring client-pull directive with large timeout");
  1111. return S_OK;
  1112. }
  1113. m_pwszClientPullURL = StrDupW(awch);
  1114. // If we can't copy the URL, don't set the timer or else we'll
  1115. // keep reloading the same page.
  1116. if (m_pwszClientPullURL == NULL)
  1117. return OLECMDERR_E_NOTSUPPORTED;
  1118. return S_OK;
  1119. }
  1120. HRESULT CUrlDownload::SetDLCTL(long lFlags)
  1121. {
  1122. // TraceMsg(TF_THISMODULE, "CUrlDownload: SetDLCTL %04x", lFlags);
  1123. m_lBindFlags = lFlags | DLCTL_SILENT;
  1124. if (m_fSetResync)
  1125. m_lBindFlags |= DLCTL_RESYNCHRONIZE;
  1126. return S_OK;
  1127. }
  1128. #define INET_E_AGENT_BIND_IN_PROGRESS 0x800C0FFF
  1129. //==============================================================================
  1130. // UrlMon download code
  1131. //==============================================================================
  1132. HRESULT CUrlDownload::BeginDownloadWithUrlMon(
  1133. LPCWSTR pwszURL,
  1134. LPTSTR pszLocalFile,
  1135. IEnumFORMATETC *pEFE)
  1136. {
  1137. IStream* pstm = NULL;
  1138. IMoniker* pmk = NULL;
  1139. IBindCtx* pbc = NULL;
  1140. HRESULT hr;
  1141. hr = CreateURLMoniker(NULL, pwszURL, &pmk);
  1142. if (FAILED(hr))
  1143. {
  1144. DBG_WARN("CreateURLMoniker failed");
  1145. goto LErrExit;
  1146. }
  1147. SAFE_RELEASE_BSC();
  1148. m_pCbsc = new CUrlDownload_BSC(m_iMethod, m_iOptions, pszLocalFile);
  1149. if (m_pCbsc == NULL)
  1150. {
  1151. hr = E_OUTOFMEMORY;
  1152. goto LErrExit;
  1153. }
  1154. hr = CreateBindCtx(0, &pbc);
  1155. if (FAILED(hr))
  1156. goto LErrExit;
  1157. if (pEFE)
  1158. {
  1159. hr = RegisterFormatEnumerator(pbc, pEFE, 0);
  1160. if (FAILED(hr))
  1161. DBG_WARN("RegisterFormatEnumerator failed (continuing download)");
  1162. }
  1163. hr = RegisterBindStatusCallback(pbc,
  1164. (IBindStatusCallback *)m_pCbsc,
  1165. 0,
  1166. 0L);
  1167. if (FAILED(hr))
  1168. goto LErrExit;
  1169. m_pCbsc->SetParent(this);
  1170. m_fbscValid = TRUE;
  1171. m_hrStatus = INET_E_AGENT_BIND_IN_PROGRESS;
  1172. StartTimer(); // Start our timeout
  1173. hr = pmk->BindToStorage(pbc, 0, IID_IStream, (void**)&pstm);
  1174. if (m_hrStatus != INET_E_AGENT_BIND_IN_PROGRESS)
  1175. {
  1176. // Synchronous success or failure. Call OnDownloadComplete.
  1177. // We can't do it in OnStopBinding because Urlmon returns hrStatus=S_OK...
  1178. // even if it fails.
  1179. if (FAILED(hr) || FAILED(m_hrStatus))
  1180. OnDownloadComplete(BDU2_ERROR_GENERAL);
  1181. else
  1182. OnDownloadComplete(BDU2_ERROR_NONE);
  1183. DBG("Synchronous bind; OnDownloadComplete called");
  1184. }
  1185. m_hrStatus = S_OK; // need this so we get OnDownloadComplete (asynch OnStopBinding)
  1186. hr = S_OK; // need this so we don't get extra OnDownloadComplete (BDU2)
  1187. // Bind has started (and maybe completed), release stuff we don't need
  1188. pmk->Release();
  1189. pbc->Release();
  1190. if (pstm)
  1191. pstm->Release();
  1192. return hr;
  1193. LErrExit:
  1194. DBG_WARN("Error in CUrlDownload::BeginDownloadWithUrlMon");
  1195. if (pbc) pbc->Release();
  1196. if (pmk) pmk->Release();
  1197. if (pstm) pstm->Release();
  1198. SAFERELEASE(m_pCbsc);
  1199. return hr;
  1200. } // CUrlDownload::BeginDownloadWithUrlMon
  1201. void CUrlDownload::BSC_OnStartBinding()
  1202. {
  1203. DBG("BSC_OnStartBinding");
  1204. }
  1205. // We only get this call if we're not downloading with the browser.
  1206. void CUrlDownload::BSC_OnStopBinding(HRESULT hrStatus, IStream *pStm)
  1207. {
  1208. TraceMsg(TF_THISMODULE, "BSC_OnStopBinding (hrStatus=0x%08x)", (long)hrStatus);
  1209. ASSERT(m_pCbsc);
  1210. // It is ok to not have stream when we requested it (robots.txt)
  1211. // ASSERT(( pStm && (m_iOptions & BDU2_NEEDSTREAM)) ||
  1212. // (!pStm && !(m_iOptions & BDU2_NEEDSTREAM)));
  1213. ASSERT(!pStm || (m_iOptions & BDU2_NEEDSTREAM));
  1214. ASSERT(!m_pStm);
  1215. // Save stream for caller if they requested it
  1216. // We keep it until the release it (ReleaseStream) or nav to another url
  1217. if (pStm && (m_iOptions & BDU2_NEEDSTREAM))
  1218. {
  1219. if (m_pStm) m_pStm->Release();
  1220. m_pStm = pStm;
  1221. m_pStm->AddRef();
  1222. }
  1223. // Send OnDownloadComplete, stop the timer
  1224. if (m_iMethod == BDU2_HEADONLY && m_pstLastModified)
  1225. hrStatus = S_OK; // We got what we came for (hrStatus will be E_ABORT)
  1226. if (m_hrStatus != INET_E_AGENT_BIND_IN_PROGRESS)
  1227. OnDownloadComplete(SUCCEEDED(hrStatus) ? BDU2_ERROR_NONE : BDU2_ERROR_GENERAL);
  1228. else
  1229. {
  1230. DBG("Not calling OnDownloadComplete; synchronous bind");
  1231. m_hrStatus = hrStatus;
  1232. }
  1233. m_fbscValid = FALSE;
  1234. SAFE_RELEASE_BSC();
  1235. }
  1236. void CUrlDownload::BSC_OnProgress(ULONG ulProgress, ULONG ulProgressMax)
  1237. {
  1238. // extend our timer
  1239. if (m_iTimerID)
  1240. StartTimer();
  1241. }
  1242. void CUrlDownload::BSC_FoundLastModified(SYSTEMTIME *pstLastModified)
  1243. {
  1244. DBG("Received last modified time");
  1245. SAFELOCALFREE(m_pstLastModified);
  1246. m_pstLastModified = (SYSTEMTIME *)MemAlloc(LMEM_FIXED, sizeof(SYSTEMTIME));
  1247. if (m_pstLastModified)
  1248. {
  1249. CopyMemory(m_pstLastModified, pstLastModified, sizeof(SYSTEMTIME));
  1250. }
  1251. }
  1252. void CUrlDownload::BSC_FoundMimeType(CLIPFORMAT cf)
  1253. {
  1254. TraceMsg(TF_THISMODULE, "FoundMimeType %d", (int)cf);
  1255. BOOL fAbort = FALSE, fBrowser=FALSE;
  1256. HRESULT hr=S_OK;
  1257. // Abort if not html if necessary.
  1258. if ((m_iOptions & BDU2_FAIL_IF_NOT_HTML) && (cf != g_cfHTML))
  1259. {
  1260. DBG("Aborting non-HTML download");
  1261. fAbort = TRUE;
  1262. OnDownloadComplete(BDU2_ERROR_NOT_HTML);
  1263. }
  1264. // Abort the UrlMon download if necessary. Fire off
  1265. // a browser download if necessary.
  1266. if (((m_iMethod == BDU2_SMART) || (m_iMethod == BDU2_SNIFF)) && (cf == g_cfHTML))
  1267. {
  1268. // Switch into the browser.
  1269. ASSERT(m_pwszURL);
  1270. if (m_pwszURL &&
  1271. (m_dwResponseCode != 401)) // Don't bother if it's auth failure
  1272. {
  1273. DBG("Switching UrlMon download into browser");
  1274. hr = BeginDownloadWithBrowser(m_pwszURL);
  1275. if (SUCCEEDED(hr))
  1276. fBrowser = TRUE;
  1277. }
  1278. }
  1279. if (fAbort || fBrowser)
  1280. {
  1281. // Disconnect the BSC so that we don't get any more notifications.
  1282. // If we're switching into the browser, don't abort the UrlMon
  1283. // download to help avoid getting multiple GET requests. We do
  1284. // disconnect the BSC but still maintain a ref to it so we abort
  1285. // it if necessary.
  1286. ASSERT(m_pCbsc);
  1287. if (m_pCbsc)
  1288. {
  1289. m_pCbsc->SetParent(NULL); // We don't want OnStopBinding
  1290. if (fAbort)
  1291. {
  1292. m_pCbsc->Abort();
  1293. m_pCbsc->Release();
  1294. m_pCbsc=NULL;
  1295. m_fbscValid = FALSE;
  1296. }
  1297. }
  1298. }
  1299. }
  1300. // Returns content for Accept-Language header
  1301. LPCWSTR CUrlDownload::GetAcceptLanguages()
  1302. {
  1303. if (0 == m_iLangStatus)
  1304. {
  1305. DWORD cchLang = ARRAYSIZE(m_achLang);
  1306. if (SUCCEEDED(::GetAcceptLanguagesW(m_achLang, &cchLang)))
  1307. {
  1308. m_iLangStatus = 1;
  1309. }
  1310. else
  1311. {
  1312. m_iLangStatus = 2;
  1313. }
  1314. }
  1315. if (1 == m_iLangStatus)
  1316. {
  1317. return m_achLang;
  1318. }
  1319. return NULL;
  1320. }
  1321. HRESULT CUrlDownload::ProgressBytes(DWORD dwBytes)
  1322. {
  1323. if (m_dwMaxSize > 0 && dwBytes > m_dwMaxSize)
  1324. {
  1325. TraceMsg(TF_THISMODULE, "CUrlDownload MaxSize exceeded aborting. %d of %d bytes", dwBytes, m_dwMaxSize);
  1326. AbortDownload(BDU2_ERROR_MAXSIZE);
  1327. return E_ABORT;
  1328. }
  1329. return S_OK;
  1330. }
  1331. //---------------------------------------------------------------
  1332. // IServiceProvider
  1333. STDMETHODIMP CUrlDownload::QueryService(REFGUID guidService, REFIID riid, void **ppvObject)
  1334. {
  1335. if ((SID_SHlinkFrame == guidService && IID_IHlinkFrame == riid) ||
  1336. (IID_IAuthenticate == guidService && IID_IAuthenticate == riid) ||
  1337. (SID_SInternetSecurityManager == guidService && IID_IInternetSecurityManager == riid) ||
  1338. (IID_IHttpSecurity == guidService && IID_IHttpSecurity == riid))
  1339. {
  1340. return QueryInterface(riid, ppvObject);
  1341. }
  1342. else
  1343. {
  1344. *ppvObject = NULL;
  1345. return E_NOINTERFACE;
  1346. }
  1347. }
  1348. //---------------------------------------------------------------
  1349. //IHttpSecurity
  1350. STDMETHODIMP CUrlDownload::OnSecurityProblem(DWORD dwProblem)
  1351. {
  1352. return S_FALSE;
  1353. }
  1354. STDMETHODIMP CUrlDownload::GetWindow( REFGUID rguidReason, HWND *phwnd ) {
  1355. if(phwnd && m_hwndMe) {
  1356. *phwnd = m_hwndMe;
  1357. } else
  1358. return E_FAIL;
  1359. return S_OK;
  1360. }
  1361. //---------------------------------------------------------------
  1362. // IAuthenticate
  1363. STDMETHODIMP CUrlDownload::Authenticate(HWND *phwnd, LPWSTR *ppszUsername, LPWSTR *ppszPassword)
  1364. {
  1365. HRESULT hr;
  1366. ASSERT(phwnd && ppszUsername && ppszPassword);
  1367. *phwnd = (HWND)-1;
  1368. *ppszUsername = NULL;
  1369. *ppszPassword = NULL;
  1370. if (m_pParent)
  1371. hr = m_pParent->OnAuthenticate(phwnd, ppszUsername, ppszPassword);
  1372. else
  1373. hr = E_NOTIMPL;
  1374. TraceMsg(TF_THISMODULE, "CUrlDownload::Authenticate returning hr=%08x", hr);
  1375. return hr;
  1376. }
  1377. //---------------------------------------------------------------
  1378. // IHlinkFrame
  1379. STDMETHODIMP CUrlDownload::SetBrowseContext(IHlinkBrowseContext *pihlbc)
  1380. {
  1381. DBG_WARN("CUrlDownload::SetBrowseContext() not implemented");
  1382. return E_NOTIMPL;
  1383. }
  1384. STDMETHODIMP CUrlDownload::GetBrowseContext(IHlinkBrowseContext **ppihlbc)
  1385. {
  1386. DBG_WARN("CUrlDownload::GetBrowseContext() not implemented");
  1387. return E_NOTIMPL;
  1388. }
  1389. STDMETHODIMP CUrlDownload::Navigate(DWORD grfHLNF, LPBC pbc, IBindStatusCallback *pibsc, IHlink *pihlNavigate)
  1390. {
  1391. // We should only get a call through IHlinkFrame->Navigate()
  1392. // when the webcrawler has submitted a form for authentication.
  1393. // Bail out if that's not the case.
  1394. if (!m_fFormSubmitted)
  1395. {
  1396. DBG_WARN("CUrlDownload::Navigate() without a form submission!!!");
  1397. return E_NOTIMPL;
  1398. }
  1399. // Our timer has already been started. If this fails, OnDownloadComplete will get
  1400. // called when we time out.
  1401. // We don't support a wide variety of parameters.
  1402. ASSERT(grfHLNF == 0);
  1403. ASSERT(pbc);
  1404. ASSERT(pibsc);
  1405. ASSERT(pihlNavigate);
  1406. // Get the moniker from IHlink
  1407. HRESULT hr;
  1408. IMoniker *pmk = NULL;
  1409. hr = pihlNavigate->GetMonikerReference(HLINKGETREF_ABSOLUTE, &pmk, NULL);
  1410. if (SUCCEEDED(hr))
  1411. {
  1412. // Load the URL with the post data.
  1413. // WARNING: What if we get redirected to something other than HTML? (beta 2)
  1414. hr = m_pPersistMk->Load(FALSE, pmk, pbc, 0);
  1415. SAFERELEASE(pmk);
  1416. if (SUCCEEDED(hr))
  1417. {
  1418. m_fBrowserValid = TRUE;
  1419. StartTimer(); // Start our timeout
  1420. // Need to wait again.
  1421. m_fWaitingForReadyState = TRUE;
  1422. DBG("CUrlDownload::Navigate (IHLinkFrame) succeeded");
  1423. }
  1424. }
  1425. return hr;
  1426. }
  1427. STDMETHODIMP CUrlDownload::OnNavigate(DWORD grfHLNF, IMoniker *pimkTarget, LPCWSTR pwzLocation, LPCWSTR pwzFriendlyName, DWORD dwreserved)
  1428. {
  1429. DBG_WARN("CUrlDownload::OnNavigate() not implemented");
  1430. return E_NOTIMPL;
  1431. }
  1432. STDMETHODIMP CUrlDownload::UpdateHlink(ULONG uHLID, IMoniker *pimkTarget, LPCWSTR pwzLocation, LPCWSTR pwzFriendlyName)
  1433. {
  1434. DBG_WARN("CUrlDownload::UpdateHlink() not implemented");
  1435. return E_NOTIMPL;
  1436. }
  1437. //---------------------------------------------------------------------
  1438. // IInternetSecurityManager interface
  1439. // Used to override security to allow form submits, for form auth sites
  1440. HRESULT CUrlDownload::SetSecuritySite(IInternetSecurityMgrSite *pSite)
  1441. {
  1442. return E_NOTIMPL;
  1443. }
  1444. HRESULT CUrlDownload::GetSecuritySite(IInternetSecurityMgrSite **ppSite)
  1445. {
  1446. return E_NOTIMPL;
  1447. }
  1448. HRESULT CUrlDownload::MapUrlToZone(LPCWSTR pwszUrl, DWORD *pdwZone, DWORD dwFlags)
  1449. {
  1450. return INET_E_DEFAULT_ACTION;
  1451. }
  1452. HRESULT CUrlDownload::GetSecurityId(LPCWSTR pwszUrl, BYTE *pbSecurityId, DWORD *pcbSecurityId, DWORD_PTR dwReserved)
  1453. {
  1454. return INET_E_DEFAULT_ACTION;
  1455. }
  1456. HRESULT CUrlDownload::ProcessUrlAction(LPCWSTR pwszUrl, DWORD dwAction, BYTE __RPC_FAR *pPolicy, DWORD cbPolicy, BYTE *pContext, DWORD cbContext, DWORD dwFlags, DWORD dwReserved)
  1457. {
  1458. if ((dwAction == URLACTION_HTML_SUBMIT_FORMS_TO) ||
  1459. (dwAction == URLACTION_HTML_SUBMIT_FORMS_FROM))
  1460. {
  1461. return S_OK;
  1462. }
  1463. return INET_E_DEFAULT_ACTION;
  1464. }
  1465. HRESULT CUrlDownload::QueryCustomPolicy(LPCWSTR pwszUrl, REFGUID guidKey, BYTE **ppPolicy, DWORD *pcbPolicy, BYTE *pContext, DWORD cbContext, DWORD dwReserved)
  1466. {
  1467. return INET_E_DEFAULT_ACTION;
  1468. }
  1469. HRESULT CUrlDownload::SetZoneMapping(DWORD dwZone, LPCWSTR lpszPattern, DWORD dwFlags)
  1470. {
  1471. return INET_E_DEFAULT_ACTION;
  1472. }
  1473. HRESULT CUrlDownload::GetZoneMappings(DWORD dwZone, IEnumString **ppenumString, DWORD dwFlags)
  1474. {
  1475. return INET_E_DEFAULT_ACTION;
  1476. }
  1477. //---------------------------------------------------------------
  1478. // CUrlDownload_BSC class
  1479. //---------------------------------------------------------------
  1480. CUrlDownload_BSC::CUrlDownload_BSC(
  1481. BDUMethod iMethod,
  1482. BDUOptions iOptions,
  1483. LPTSTR pszLocalFile)
  1484. {
  1485. // Maintain global count of objects
  1486. DllAddRef();
  1487. m_cRef = 1;
  1488. m_iMethod = iMethod;
  1489. m_iOptions = iOptions;
  1490. if (NULL != pszLocalFile)
  1491. {
  1492. m_pszLocalFileDest = StrDup(pszLocalFile);
  1493. if (m_iMethod != BDU2_URLMON)
  1494. {
  1495. DBG_WARN("CUrlDownload_BSC changing method to URLMON (local file specified)");
  1496. m_iMethod = BDU2_URLMON;
  1497. }
  1498. }
  1499. }
  1500. CUrlDownload_BSC::~CUrlDownload_BSC()
  1501. {
  1502. // Maintain global count of objects
  1503. DllRelease();
  1504. ASSERT(!m_pBinding);
  1505. SAFERELEASE(m_pstm);
  1506. SAFELOCALFREE(m_pszLocalFileDest);
  1507. SAFELOCALFREE(m_pwszLocalFileSrc);
  1508. }
  1509. void CUrlDownload_BSC::SetParent(CUrlDownload *pUrlDownload)
  1510. {
  1511. m_pParent = pUrlDownload;
  1512. }
  1513. HRESULT CUrlDownload_BSC::Abort()
  1514. {
  1515. if (m_pBinding)
  1516. {
  1517. return m_pBinding->Abort();
  1518. }
  1519. return S_FALSE;
  1520. }
  1521. STDMETHODIMP CUrlDownload_BSC::QueryInterface(REFIID riid, void** ppv)
  1522. {
  1523. *ppv = NULL;
  1524. if (riid==IID_IUnknown || riid==IID_IBindStatusCallback)
  1525. {
  1526. *ppv = (IBindStatusCallback *)this;
  1527. AddRef();
  1528. return S_OK;
  1529. }
  1530. if (riid==IID_IHttpNegotiate)
  1531. {
  1532. *ppv = (IHttpNegotiate *)this;
  1533. AddRef();
  1534. return S_OK;
  1535. }
  1536. if (riid==IID_IAuthenticate)
  1537. {
  1538. *ppv = (IAuthenticate *)this;
  1539. AddRef();
  1540. return S_OK;
  1541. }
  1542. return E_NOINTERFACE;
  1543. }
  1544. //---------------------------------------------------------------
  1545. // IAuthenticate
  1546. STDMETHODIMP CUrlDownload_BSC::Authenticate(HWND *phwnd, LPWSTR *ppszUsername, LPWSTR *ppszPassword)
  1547. { //copied from CUrlDownload::Authenticate (to whom we pass off anyway)
  1548. HRESULT hr;
  1549. ASSERT(phwnd && ppszUsername && ppszPassword);
  1550. *phwnd = (HWND)-1;
  1551. *ppszUsername = NULL;
  1552. *ppszPassword = NULL;
  1553. // Only try this once. If Urlmon asks again, fail it and flag an error.
  1554. if (m_fTriedAuthenticate)
  1555. {
  1556. if (m_pParent)
  1557. {
  1558. m_pParent->m_dwResponseCode = 401;
  1559. DBG("CUrlDownload_BSC::Authenticate called twice. Faking 401 response");
  1560. }
  1561. return E_FAIL;
  1562. }
  1563. m_fTriedAuthenticate = TRUE;
  1564. if (m_pParent)
  1565. hr = m_pParent->Authenticate(phwnd, ppszUsername, ppszPassword);
  1566. else
  1567. hr = E_NOTIMPL;
  1568. if (FAILED(hr) && m_pParent)
  1569. {
  1570. m_pParent->m_dwResponseCode = 401;
  1571. DBG("CUrlDownload_BSC::Authenticate called; no username/pass. Faking 401 response");
  1572. }
  1573. TraceMsg(TF_THISMODULE, "CUrlDownload_BSC::Authenticate returning hr=%08x", hr);
  1574. return hr;
  1575. }
  1576. STDMETHODIMP CUrlDownload_BSC::OnStartBinding(
  1577. DWORD dwReserved,
  1578. IBinding* pbinding)
  1579. {
  1580. m_fSentMimeType = FALSE;
  1581. if (m_pBinding != NULL)
  1582. m_pBinding->Release();
  1583. m_pBinding = pbinding;
  1584. if (m_pBinding != NULL)
  1585. {
  1586. m_pBinding->AddRef();
  1587. }
  1588. if (m_pParent)
  1589. m_pParent->BSC_OnStartBinding();
  1590. return S_OK;
  1591. }
  1592. // ---------------------------------------------------------------------------
  1593. // %%Function: CUrlDownload_BSC::GetPriority
  1594. // ---------------------------------------------------------------------------
  1595. STDMETHODIMP
  1596. CUrlDownload_BSC::GetPriority(LONG* pnPriority)
  1597. {
  1598. return E_NOTIMPL;
  1599. }
  1600. // ---------------------------------------------------------------------------
  1601. // %%Function: CUrlDownload_BSC::OnLowResource
  1602. // ---------------------------------------------------------------------------
  1603. STDMETHODIMP
  1604. CUrlDownload_BSC::OnLowResource(DWORD dwReserved)
  1605. {
  1606. return E_NOTIMPL;
  1607. }
  1608. // ---------------------------------------------------------------------------
  1609. // %%Function: CUrlDownload_BSC::OnProgress
  1610. // ---------------------------------------------------------------------------
  1611. STDMETHODIMP
  1612. CUrlDownload_BSC::OnProgress(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
  1613. {
  1614. // TraceMsg(TF_THISMODULE, "cbsc::OnProgress %d of %d : msg %ws", ulProgress, ulProgressMax, szStatusText);
  1615. /*
  1616. if (ulStatusCode==BINDSTATUS_USINGCACHEDCOPY)
  1617. */
  1618. if (ulStatusCode == BINDSTATUS_REDIRECTING)
  1619. {
  1620. DBG("CUrlDownload_BSC::OnProgress getting redirected url");
  1621. TraceMsg(TF_THISMODULE, "New url=%ws", szStatusText);
  1622. if (m_pParent)
  1623. {
  1624. if (m_pParent->m_pwszURL) MemFree(m_pParent->m_pwszURL);
  1625. m_pParent->m_pwszURL = StrDupW(szStatusText);
  1626. }
  1627. }
  1628. if ((ulStatusCode == BINDSTATUS_CACHEFILENAMEAVAILABLE) && m_pszLocalFileDest)
  1629. {
  1630. ASSERT(!m_pwszLocalFileSrc);
  1631. DBG("CUrlDownload_BSC::OnProgress Getting local file name");
  1632. if (!m_pwszLocalFileSrc)
  1633. m_pwszLocalFileSrc = StrDupW(szStatusText);
  1634. }
  1635. if (m_pParent)
  1636. m_pParent->BSC_OnProgress(ulProgress, ulProgressMax);
  1637. // 14032: If dialmon is around, tell it that something is going on
  1638. IndicateDialmonActivity();
  1639. return S_OK;
  1640. }
  1641. STDMETHODIMP CUrlDownload_BSC::OnStopBinding(
  1642. HRESULT hrStatus,
  1643. LPCWSTR pszError)
  1644. {
  1645. #ifdef DEBUG
  1646. if (hrStatus && (hrStatus != E_ABORT))
  1647. TraceMsg(TF_THISMODULE,
  1648. "cbsc: File download Failed hr=%08x.", (int)hrStatus);
  1649. #endif
  1650. if (m_pParent)
  1651. m_pParent->BSC_OnStopBinding(hrStatus, (m_iOptions&BDU2_NEEDSTREAM) ? m_pstm : NULL);
  1652. // We should have neither or both of these
  1653. ASSERT(!m_pwszLocalFileSrc == !m_pszLocalFileDest);
  1654. if (m_pwszLocalFileSrc && m_pszLocalFileDest)
  1655. {
  1656. // Copy or move file from cache file to file/directory requested
  1657. // We have a LPWSTR source name and an LPTSTR destination
  1658. TCHAR szSrc[MAX_PATH];
  1659. TCHAR szDest[MAX_PATH];
  1660. LPTSTR pszSrcFileName, pszDest=NULL;
  1661. MyOleStrToStrN(szSrc, MAX_PATH, m_pwszLocalFileSrc);
  1662. // Combine paths to find destination filename if necessary
  1663. if (PathIsDirectory(m_pszLocalFileDest))
  1664. {
  1665. pszSrcFileName = PathFindFileName(szSrc);
  1666. if (pszSrcFileName)
  1667. {
  1668. PathCombine(szDest, m_pszLocalFileDest, pszSrcFileName);
  1669. pszDest = szDest;
  1670. }
  1671. }
  1672. else
  1673. {
  1674. pszDest = m_pszLocalFileDest;
  1675. }
  1676. if (pszDest)
  1677. {
  1678. TraceMsg(TF_THISMODULE, "Copying file\n%s\n to file \n%s", szSrc, pszDest);
  1679. CopyFile(szSrc, pszDest, FALSE);
  1680. }
  1681. else
  1682. DBG_WARN("Unable to get dest path for local file");
  1683. }
  1684. SAFERELEASE(m_pstm);
  1685. SAFERELEASE(m_pBinding);
  1686. return S_OK;
  1687. }
  1688. STDMETHODIMP CUrlDownload_BSC::GetBindInfo(
  1689. DWORD *pgrfBINDF,
  1690. BINDINFO *pbindInfo)
  1691. {
  1692. if ( !pgrfBINDF || !pbindInfo || !pbindInfo->cbSize )
  1693. return E_INVALIDARG;
  1694. *pgrfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_NO_UI;
  1695. if (m_pszLocalFileDest)
  1696. *pgrfBINDF |= BINDF_NEEDFILE;
  1697. if (m_pParent && m_pParent->m_fSetResync)
  1698. *pgrfBINDF |= BINDF_RESYNCHRONIZE;
  1699. if (m_pParent && (m_pParent->m_lBindFlags & DLCTL_FORCEOFFLINE))
  1700. *pgrfBINDF |= BINDF_OFFLINEOPERATION;
  1701. // clear BINDINFO but keep its size
  1702. DWORD cbSize = pbindInfo->cbSize;
  1703. ZeroMemory( pbindInfo, cbSize );
  1704. pbindInfo->cbSize = cbSize;
  1705. pbindInfo->dwBindVerb = BINDVERB_GET;
  1706. if (m_iMethod == BDU2_HEADONLY)
  1707. {
  1708. LPWSTR pwszVerb = (LPWSTR) CoTaskMemAlloc(sizeof(c_wszHeadVerb));
  1709. if (pwszVerb)
  1710. {
  1711. CopyMemory(pwszVerb, c_wszHeadVerb, sizeof(c_wszHeadVerb));
  1712. pbindInfo->dwBindVerb = BINDVERB_CUSTOM;
  1713. pbindInfo->szCustomVerb = pwszVerb;
  1714. DBG("Using 'HEAD' custom bind verb.");
  1715. }
  1716. else
  1717. {
  1718. DBG_WARN("MemAlloc failure CUrlDownload_BSC::GetBindInfo");
  1719. return E_OUTOFMEMORY;
  1720. }
  1721. }
  1722. return S_OK;
  1723. }
  1724. STDMETHODIMP CUrlDownload_BSC::OnDataAvailable(
  1725. DWORD grfBSCF,
  1726. DWORD dwSize,
  1727. FORMATETC* pfmtetc,
  1728. STGMEDIUM* pstgmed)
  1729. {
  1730. TraceMsg(TF_THISMODULE, "%d bytes on page so far (urlmon)", dwSize);
  1731. if (m_pParent)
  1732. if (FAILED(m_pParent->ProgressBytes(dwSize)))
  1733. return S_OK;
  1734. // Get the Stream passed if we want a local file (to lock the file)
  1735. // We just ignore any data in any case
  1736. if (BSCF_FIRSTDATANOTIFICATION & grfBSCF)
  1737. {
  1738. if (!m_pstm && (pstgmed->tymed==TYMED_ISTREAM) &&
  1739. (m_pszLocalFileDest || (m_iOptions & BDU2_NEEDSTREAM)))
  1740. {
  1741. m_pstm = pstgmed->pstm;
  1742. if (m_pstm)
  1743. m_pstm->AddRef();
  1744. }
  1745. }
  1746. if (!m_fSentMimeType && pfmtetc && m_pParent)
  1747. {
  1748. m_pParent->BSC_FoundMimeType(pfmtetc->cfFormat);
  1749. m_fSentMimeType = TRUE;
  1750. }
  1751. if (BSCF_LASTDATANOTIFICATION & grfBSCF)
  1752. {
  1753. DBG("cbsc: LastDataNotification");
  1754. }
  1755. return S_OK;
  1756. } // CUrlDownload_BSC::OnDataAvailable
  1757. STDMETHODIMP CUrlDownload_BSC::OnObjectAvailable(REFIID riid, IUnknown* punk)
  1758. {
  1759. return E_NOTIMPL;
  1760. }
  1761. STDMETHODIMP CUrlDownload_BSC::BeginningTransaction(
  1762. LPCWSTR szURL, LPCWSTR szHeaders,
  1763. DWORD dwReserved, LPWSTR *pszAdditionalHeaders)
  1764. {
  1765. // Add User-Agent and Accept-Language headers
  1766. DBG("CUrlDownload_BSC::BeginningTransaction returning headers");
  1767. LPCWSTR pwszAcceptLanguage;
  1768. int iUAlen=0, iALlen=0; // in chars, with \r\n, without null-term
  1769. LPWSTR pwsz;
  1770. LPCWSTR pwszUA = m_pParent ? m_pParent->GetUserAgent() : NULL;
  1771. pwszAcceptLanguage = (m_pParent) ? m_pParent->GetAcceptLanguages() : NULL;
  1772. if (pwszUA)
  1773. {
  1774. iUAlen = ARRAYSIZE(c_szUserAgentPrefix) + lstrlenW(pwszUA) + 1;
  1775. }
  1776. if (pwszAcceptLanguage)
  1777. {
  1778. iALlen = ARRAYSIZE(c_szAcceptLanguagePrefix) + lstrlenW(pwszAcceptLanguage)+1;
  1779. }
  1780. if (iUAlen || iALlen)
  1781. {
  1782. int iAlloc = iUAlen + iALlen + 1;
  1783. pwsz = (WCHAR *)CoTaskMemAlloc(iAlloc * sizeof(WCHAR));
  1784. if (pwsz)
  1785. {
  1786. pwsz[0] = L'\0';
  1787. if (iUAlen)
  1788. {
  1789. StrCpyNW(pwsz, c_szUserAgentPrefix, iAlloc);
  1790. StrCatBuffW(pwsz, pwszUA, iAlloc);
  1791. StrCatBuffW(pwsz, L"\r\n", iAlloc);
  1792. }
  1793. if (iALlen)
  1794. {
  1795. StrCatBuffW(pwsz, c_szAcceptLanguagePrefix, iAlloc);
  1796. StrCatBuffW(pwsz, pwszAcceptLanguage, iAlloc);
  1797. StrCatBuffW(pwsz, L"\r\n", iAlloc);
  1798. }
  1799. ASSERT(lstrlenW(pwsz) == (iUAlen + iALlen));
  1800. *pszAdditionalHeaders = pwsz;
  1801. return S_OK;
  1802. }
  1803. }
  1804. return E_OUTOFMEMORY;
  1805. }
  1806. STDMETHODIMP CUrlDownload_BSC::OnResponse(
  1807. DWORD dwResponseCode, LPCWSTR szResponseHeaders,
  1808. LPCWSTR szRequestHeaders, LPWSTR *pszAdditionalRequestHeaders)
  1809. {
  1810. TraceMsg(TF_THISMODULE, "CUrlDownload_BSC::OnResponse - %d", dwResponseCode);
  1811. // If we sent a "HEAD" request, Urlmon will hang expecting data.
  1812. // Abort it here.
  1813. if (m_iMethod == BDU2_HEADONLY)
  1814. {
  1815. // First get the Last-Modified date from Urlmon
  1816. IWinInetHttpInfo *pInfo;
  1817. if (m_pParent
  1818. && SUCCEEDED(m_pBinding->QueryInterface(IID_IWinInetHttpInfo, (void **)&pInfo)
  1819. && pInfo))
  1820. {
  1821. SYSTEMTIME st;
  1822. DWORD dwSize = sizeof(st), dwZero=0;
  1823. if (SUCCEEDED(pInfo->QueryInfo(HTTP_QUERY_FLAG_SYSTEMTIME | HTTP_QUERY_LAST_MODIFIED,
  1824. (LPVOID) &st, &dwSize, &dwZero, 0)))
  1825. {
  1826. m_pParent->BSC_FoundLastModified(&st);
  1827. }
  1828. pInfo->Release();
  1829. }
  1830. Abort(); // FEATURE: return E_ABORT and handle abort internally
  1831. }
  1832. if (m_pParent)
  1833. m_pParent->m_dwResponseCode = dwResponseCode;
  1834. else
  1835. DBG_WARN("CUrlDownload_BSC::OnResponse - Parent already NULL");
  1836. return S_OK;
  1837. }
  1838. //
  1839. // IOleClientSite
  1840. //
  1841. STDMETHODIMP CUrlDownload:: SaveObject(void)
  1842. {
  1843. return E_NOTIMPL;
  1844. }
  1845. STDMETHODIMP CUrlDownload:: GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, IMoniker **ppmk)
  1846. {
  1847. DBG("CUrlDownload::GetMoniker returning failure");
  1848. return E_NOTIMPL;
  1849. }
  1850. STDMETHODIMP CUrlDownload:: GetContainer(IOleContainer **ppContainer)
  1851. {
  1852. return E_NOTIMPL;
  1853. }
  1854. STDMETHODIMP CUrlDownload:: ShowObject(void)
  1855. {
  1856. return E_NOTIMPL;
  1857. }
  1858. STDMETHODIMP CUrlDownload:: OnShowWindow(BOOL fShow)
  1859. {
  1860. return E_NOTIMPL;
  1861. }
  1862. STDMETHODIMP CUrlDownload:: RequestNewObjectLayout(void)
  1863. {
  1864. return E_NOTIMPL;
  1865. }
  1866. // ParseRefreshContent was lifted in its entirety from shdocvw\basesb.cpp
  1867. BOOL ParseRefreshContent(LPWSTR pwzContent,
  1868. UINT * puiDelay, LPWSTR pwzUrlBuf, UINT cchUrlBuf)
  1869. {
  1870. // We are parsing the following string:
  1871. //
  1872. // [ws]* [0-9]+ [ws]* ; [ws]* url [ws]* = [ws]* { ' | " } [any]* { ' | " }
  1873. //
  1874. // Netscape insists that the string begins with a delay. If not, it
  1875. // ignores the entire directive. There can be more than one URL mentioned,
  1876. // and the last one wins. An empty URL is treated the same as not having
  1877. // a URL at all. An empty URL which follows a non-empty URL resets
  1878. // the previous URL.
  1879. enum { PRC_START, PRC_DIG, PRC_DIG_WS, PRC_SEMI, PRC_SEMI_URL,
  1880. PRC_SEMI_URL_EQL, PRC_SEMI_URL_EQL_ANY };
  1881. #define ISSPACE(ch) (((ch) == 32) || ((unsigned)((ch) - 9)) <= 13 - 9)
  1882. UINT uiState = PRC_START;
  1883. UINT uiDelay = 0;
  1884. LPWSTR pwz = pwzContent;
  1885. LPWSTR pwzUrl = NULL;
  1886. UINT cchUrl = 0;
  1887. WCHAR wch, wchDel = 0;
  1888. *pwzUrlBuf = 0;
  1889. do
  1890. {
  1891. wch = *pwz;
  1892. switch (uiState)
  1893. {
  1894. case PRC_START:
  1895. if (wch >= TEXT('0') && wch <= TEXT('9'))
  1896. {
  1897. uiState = PRC_DIG;
  1898. uiDelay = wch - TEXT('0');
  1899. }
  1900. else if (!ISSPACE(wch))
  1901. goto done;
  1902. break;
  1903. case PRC_DIG:
  1904. if (wch >= TEXT('0') && wch <= TEXT('9'))
  1905. uiDelay = uiDelay * 10 + wch - TEXT('0');
  1906. else if (ISSPACE(wch))
  1907. uiState = PRC_DIG_WS;
  1908. else if (wch == TEXT(';'))
  1909. uiState = PRC_SEMI;
  1910. else
  1911. goto done;
  1912. break;
  1913. case PRC_DIG_WS:
  1914. if (wch == TEXT(';'))
  1915. uiState = PRC_SEMI;
  1916. else if (!ISSPACE(wch))
  1917. goto done;
  1918. break;
  1919. case PRC_SEMI:
  1920. if ( (wch == TEXT('u') || wch == TEXT('U'))
  1921. && (pwz[1] == TEXT('r') || pwz[1] == TEXT('R'))
  1922. && (pwz[2] == TEXT('l') || pwz[2] == TEXT('L')))
  1923. {
  1924. uiState = PRC_SEMI_URL;
  1925. pwz += 2;
  1926. }
  1927. else if (!ISSPACE(wch) && wch != TEXT(';'))
  1928. goto done;
  1929. break;
  1930. case PRC_SEMI_URL:
  1931. if (wch == TEXT('='))
  1932. {
  1933. uiState = PRC_SEMI_URL_EQL;
  1934. *pwzUrlBuf = 0;
  1935. }
  1936. else if (wch == TEXT(';'))
  1937. uiState = PRC_SEMI;
  1938. else if (!ISSPACE(wch))
  1939. goto done;
  1940. break;
  1941. case PRC_SEMI_URL_EQL:
  1942. if (wch == TEXT(';'))
  1943. uiState = PRC_SEMI;
  1944. else if (!ISSPACE(wch))
  1945. {
  1946. uiState = PRC_SEMI_URL_EQL_ANY;
  1947. pwzUrl = pwzUrlBuf;
  1948. cchUrl = cchUrlBuf;
  1949. if (wch == TEXT('\'')|| wch == TEXT('\"'))
  1950. wchDel = wch;
  1951. else
  1952. {
  1953. wchDel = 0;
  1954. *pwzUrl++ = wch;
  1955. cchUrl--;
  1956. }
  1957. }
  1958. break;
  1959. case PRC_SEMI_URL_EQL_ANY:
  1960. if ( !wch
  1961. || ( wchDel && wch == wchDel)
  1962. || (!wchDel && wch == L';'))
  1963. {
  1964. *pwzUrl = 0;
  1965. uiState = wch == TEXT(';') ? PRC_SEMI : PRC_DIG_WS;
  1966. }
  1967. else if (cchUrl > 1)
  1968. {
  1969. *pwzUrl++ = wch;
  1970. cchUrl--;
  1971. }
  1972. break;
  1973. }
  1974. ++pwz;
  1975. } while (wch);
  1976. done:
  1977. *puiDelay = uiDelay;
  1978. return(uiState >= PRC_DIG);
  1979. } // ParseRefreshContent