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.

737 lines
20 KiB

  1. /*
  2. * m h t m l . c p p
  3. *
  4. * Purpose:
  5. * MHTML packing utilities
  6. *
  7. * History
  8. * August '96: brettm - created
  9. *
  10. * Copyright (C) Microsoft Corp. 1995, 1996.
  11. */
  12. #include <pch.hxx>
  13. #include "dllmain.h"
  14. #include "resource.h"
  15. #include "strconst.h"
  16. #include "htmlstr.h"
  17. #include "mimeutil.h"
  18. #include "triutil.h"
  19. #include "util.h"
  20. #include "oleutil.h"
  21. #include "demand.h"
  22. #include "mhtml.h"
  23. #include "tags.h"
  24. ASSERTDATA
  25. /*
  26. * m a c r o s
  27. */
  28. /*
  29. * c o n s t a n t s
  30. */
  31. static const TCHAR c_szRegExtension[] = "SOFTWARE\\Microsoft\\MimeEdit\\MHTML Extension";
  32. /*
  33. * t y p e d e f s
  34. */
  35. /*
  36. * g l o b a l s
  37. */
  38. /*
  39. * f u n c t i o n p r o t y p e s
  40. */
  41. /*
  42. * f u n c t i o n s
  43. */
  44. class CPackager
  45. {
  46. public:
  47. CPackager();
  48. virtual ~CPackager();
  49. ULONG AddRef();
  50. ULONG Release();
  51. HRESULT PackageData(IHTMLDocument2 *pDoc, IMimeMessage *pMsgSrc, IMimeMessage *pMsgDest, DWORD dwFlags, IHashTable *pHashRestricted);
  52. private:
  53. ULONG m_cRef;
  54. IMimeMessage *m_pMsgSrc,
  55. *m_pMsgDest;
  56. IHashTable *m_pHash,
  57. *m_pHashRestricted;
  58. HRESULT _PackageCollectionData(IMimeEditTagCollection *pCollect);
  59. HRESULT _PackageUrlData(IMimeEditTag *pTag);
  60. HRESULT _RemapUrls(IMimeEditTagCollection *pCollect, BOOL fSave);
  61. HRESULT _CanonicaliseContentId(LPWSTR pszUrlW, BSTR *pbstr);
  62. HRESULT _ShouldUseContentId(LPSTR pszUrl);
  63. HRESULT _BuildCollectionTable(DWORD dwFlags, IHTMLDocument2 *pDoc, IMimeEditTagCollection ***prgpCollect, ULONG *pcCount);
  64. };
  65. CPackager::CPackager()
  66. {
  67. m_cRef = 1;
  68. m_pMsgSrc = NULL;
  69. m_pMsgDest = NULL;
  70. m_pHash = NULL;
  71. m_pHashRestricted = NULL;
  72. }
  73. CPackager::~CPackager()
  74. {
  75. ReleaseObj(m_pMsgSrc);
  76. ReleaseObj(m_pMsgDest);
  77. ReleaseObj(m_pHash);
  78. ReleaseObj(m_pHashRestricted);
  79. }
  80. ULONG CPackager::AddRef()
  81. {
  82. return ++m_cRef;
  83. }
  84. ULONG CPackager::Release()
  85. {
  86. if (--m_cRef==0)
  87. {
  88. delete this;
  89. return 0;
  90. }
  91. return m_cRef;
  92. }
  93. HRESULT CPackager::PackageData(IHTMLDocument2 *pDoc, IMimeMessage *pMsgSrc, IMimeMessage *pMsgDest, DWORD dwFlags, IHashTable *pHashRestricted)
  94. {
  95. LPSTREAM pstm;
  96. HRESULT hr,
  97. hrWarnings=S_OK;
  98. HBODY hBodyHtml=0;
  99. IMimeEditTagCollection **rgpCollect=NULL;
  100. ULONG uCollect,
  101. cCollect=0,
  102. cItems=0,
  103. cCount;
  104. // BUGBUG: propagate hrWarnings back up
  105. TraceCall("CBody::Save");
  106. if (pDoc==NULL || pMsgDest==NULL)
  107. return E_INVALIDARG;
  108. ReplaceInterface(m_pMsgSrc, pMsgSrc);
  109. ReplaceInterface(m_pMsgDest, pMsgDest);
  110. ReplaceInterface(m_pHashRestricted, pHashRestricted);
  111. hr = _BuildCollectionTable(dwFlags, pDoc, &rgpCollect, &cCollect);
  112. if (FAILED(hr))
  113. hrWarnings = hr;
  114. // count the number of items we need to package and
  115. // prepare a hash-table for duplicate entries
  116. for (uCollect = 0; uCollect < cCollect; uCollect++)
  117. {
  118. Assert (rgpCollect[uCollect]);
  119. if ((rgpCollect[uCollect])->Count(&cCount)==S_OK)
  120. cItems+=cCount;
  121. }
  122. if (cItems)
  123. {
  124. // create the hashtable
  125. hr = MimeOleCreateHashTable(cItems, TRUE, &m_pHash);
  126. if (FAILED(hr))
  127. goto error;
  128. }
  129. // package the data required for each collection
  130. for (uCollect = 0; uCollect < cCollect; uCollect++)
  131. {
  132. // package the data
  133. hr = _PackageCollectionData(rgpCollect[uCollect]);
  134. if (FAILED(hr))
  135. goto error;
  136. if (hr != S_OK) // retain any 'warnings'
  137. hrWarnings = hr;
  138. // map all the URLs to CID:// urls if necessary
  139. hr = _RemapUrls(rgpCollect[uCollect], TRUE);
  140. if (FAILED(hr))
  141. goto error;
  142. }
  143. // get an HTML stream
  144. if(dwFlags & MECD_HTML)
  145. {
  146. hr = GetBodyStream(pDoc, TRUE, &pstm);
  147. if (!FAILED(hr))
  148. {
  149. hr = pMsgDest->SetTextBody(TXT_HTML, IET_INETCSET, NULL, pstm, &hBodyHtml);
  150. pstm->Release();
  151. }
  152. if (FAILED(hr))
  153. goto error;
  154. }
  155. // get a plain-text stream
  156. if(dwFlags & MECD_PLAINTEXT)
  157. {
  158. hr = GetBodyStream(pDoc, FALSE, &pstm);
  159. if (!FAILED(hr))
  160. {
  161. // if we set a html body part, then be sure to pass in hBodyHtml so Opie knows what the alternate is
  162. // alternative to.
  163. hr = pMsgDest->SetTextBody(TXT_PLAIN, IET_UNICODE, hBodyHtml, pstm, NULL);
  164. pstm->Release();
  165. }
  166. if (FAILED(hr))
  167. goto error;
  168. }
  169. for (uCollect = 0; uCollect < cCollect; uCollect++)
  170. {
  171. // remap all of the URL's back to their original location
  172. hr = _RemapUrls(rgpCollect[uCollect], FALSE);
  173. if (FAILED(hr))
  174. goto error;
  175. }
  176. error:
  177. // release the collection objects
  178. if (rgpCollect)
  179. {
  180. for (uCollect = 0; uCollect < cCollect; uCollect++)
  181. ReleaseObj(rgpCollect[uCollect]);
  182. MemFree(rgpCollect);
  183. }
  184. return hr==S_OK ? hrWarnings : hr;
  185. }
  186. HRESULT CPackager::_BuildCollectionTable(DWORD dwFlags, IHTMLDocument2 *pDoc, IMimeEditTagCollection ***prgpCollect, ULONG *pcCount)
  187. {
  188. IMimeEditTagCollection **rgpCollect=NULL;
  189. HKEY hkey;
  190. ULONG cPlugin=0,
  191. cCount = 0,
  192. cAlloc = 0,
  193. i,
  194. cb;
  195. TCHAR szGUID[MAX_PATH];
  196. IID iid;
  197. HRESULT hr=E_FAIL;
  198. LONG lResult;
  199. LPWSTR pszGuidW;
  200. *prgpCollect = NULL;
  201. *pcCount = NULL;
  202. // reserve space for 2 image collections (bgimage and img)
  203. if (dwFlags & MECD_ENCODEIMAGES)
  204. cAlloc+=2;
  205. // reserver space for bgsounds
  206. if (dwFlags & MECD_ENCODESOUNDS)
  207. cAlloc++;
  208. // reserver space for active-movies
  209. if (dwFlags & MECD_ENCODEVIDEO)
  210. cAlloc++;
  211. // reserve space for plugin types
  212. if ((dwFlags & MECD_ENCODEPLUGINS) &&
  213. RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szRegExtension, 0, KEY_READ, &hkey) == ERROR_SUCCESS)
  214. {
  215. if (RegQueryInfoKey(hkey, NULL, NULL, 0, &cPlugin, NULL, NULL, NULL, NULL, NULL, NULL, NULL) == ERROR_SUCCESS &&
  216. cPlugin > 0)
  217. cAlloc += cPlugin;
  218. RegCloseKey(hkey);
  219. }
  220. // allocate the table of collection pointers
  221. if (!MemAlloc((LPVOID *)&rgpCollect, sizeof(IMimeEditTagCollection *) * cAlloc))
  222. {
  223. hr = TraceResult(E_OUTOFMEMORY);
  224. goto error;
  225. }
  226. // zero-init the table
  227. ZeroMemory((LPVOID)rgpCollect, sizeof(IMimeEditTagCollection *) * cAlloc);
  228. if (dwFlags & MECD_ENCODEIMAGES)
  229. {
  230. // image collection
  231. if (FAILED(CreateOEImageCollection(pDoc, &rgpCollect[cCount])))
  232. hr = MIMEEDIT_W_BADURLSNOTATTACHED; // bubble back a warning, but don't fail
  233. else
  234. cCount++;
  235. }
  236. if (dwFlags & MECD_ENCODEIMAGES)
  237. {
  238. // background images
  239. if (FAILED(CreateBGImageCollection(pDoc, &rgpCollect[cCount])))
  240. hr = MIMEEDIT_W_BADURLSNOTATTACHED; // bubble back a warning, but don't fail
  241. else
  242. cCount++;
  243. }
  244. if (dwFlags & MECD_ENCODESOUNDS)
  245. {
  246. // background sounds
  247. if (FAILED(CreateBGSoundCollection(pDoc, &rgpCollect[cCount])))
  248. hr = MIMEEDIT_W_BADURLSNOTATTACHED; // bubble back a warning, but don't fail
  249. else
  250. cCount++;
  251. }
  252. if (dwFlags & MECD_ENCODEVIDEO)
  253. {
  254. // active-movie controls (for MSPHONE)
  255. if (FAILED(CreateActiveMovieCollection(pDoc, &rgpCollect[cCount])))
  256. hr = MIMEEDIT_W_BADURLSNOTATTACHED; // bubble back a warning, but don't fail
  257. else
  258. cCount++;
  259. }
  260. if ((dwFlags & MECD_ENCODEPLUGINS) &&
  261. cPlugin &&
  262. RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szRegExtension, 0, KEY_READ, &hkey) == ERROR_SUCCESS)
  263. {
  264. // Start Enumerating the keys
  265. for (i = 0; i < cPlugin; i++)
  266. {
  267. // Enumerate Friendly Names
  268. cb = sizeof(szGUID);
  269. lResult = RegEnumKeyEx(hkey, i, szGUID, &cb, 0, NULL, NULL, NULL);
  270. // No more items
  271. if (lResult == ERROR_NO_MORE_ITEMS)
  272. break;
  273. // Error, lets move onto the next account
  274. if (lResult != ERROR_SUCCESS)
  275. {
  276. Assert(FALSE);
  277. continue;
  278. }
  279. pszGuidW = PszToUnicode(CP_ACP, szGUID);
  280. if (pszGuidW)
  281. {
  282. // convert the string to a guid
  283. if (IIDFromString(pszGuidW, &iid) == S_OK)
  284. {
  285. // cocreate the plugin
  286. if (CoCreateInstance(iid, NULL, CLSCTX_INPROC_SERVER, IID_IMimeEditTagCollection, (LPVOID *)&rgpCollect[cCount])==S_OK)
  287. {
  288. // try and init the document
  289. if (!FAILED((rgpCollect[cCount])->Init(pDoc)))
  290. {
  291. cCount++;
  292. }
  293. else
  294. {
  295. SafeRelease(rgpCollect[cCount]);
  296. }
  297. }
  298. else
  299. hr = MIMEEDIT_W_BADURLSNOTATTACHED; // bubble back a warning, but don't fail
  300. }
  301. MemFree(pszGuidW);
  302. }
  303. }
  304. RegCloseKey(hkey);
  305. }
  306. *prgpCollect = rgpCollect;
  307. *pcCount = cCount;
  308. rgpCollect = NULL;
  309. hr = S_OK;
  310. error:
  311. if (rgpCollect)
  312. {
  313. for (i = 0; i < cAlloc; i++)
  314. SafeRelease(rgpCollect[i]);
  315. MemFree(rgpCollect);
  316. }
  317. return hr;
  318. }
  319. HRESULT CPackager::_PackageCollectionData(IMimeEditTagCollection *pCollect)
  320. {
  321. ULONG cFetched;
  322. IMimeEditTag *pTag;
  323. BOOL fBadLinks = FALSE;
  324. Assert (pCollect);
  325. pCollect->Reset();
  326. while (pCollect->Next(1, &pTag, &cFetched)==S_OK && cFetched==1)
  327. {
  328. Assert (pTag);
  329. if (pTag->CanPackage() != S_OK ||
  330. _PackageUrlData(pTag) != S_OK)
  331. {
  332. // we failed to package this body part. Be sure to return a warning when we're done
  333. // but let's keep trucking for now...
  334. fBadLinks = TRUE;
  335. }
  336. pTag->Release();
  337. }
  338. return fBadLinks ? MIMEEDIT_W_BADURLSNOTATTACHED : S_OK;;
  339. }
  340. HRESULT CPackager::_PackageUrlData(IMimeEditTag *pTag)
  341. {
  342. HRESULT hr=S_OK;
  343. LPSTREAM pstm;
  344. HBODY hBody=0,
  345. hBodyOld;
  346. LPSTR lpszCID=0,
  347. lpszCIDUrl;
  348. LPSTR pszUrlA=0,
  349. pszBody;
  350. BSTR bstrSrc=NULL,
  351. bstrCID=NULL;
  352. LPWSTR pszMimeTypeW=NULL;
  353. if (pTag == NULL)
  354. return E_INVALIDARG;
  355. pTag->GetSrc(&bstrSrc);
  356. pszUrlA = PszToANSI(CP_ACP, bstrSrc);
  357. if (!pszUrlA)
  358. return TraceResult(E_OUTOFMEMORY);
  359. DWORD cchSize = (lstrlenA(pszUrlA) + 1);
  360. // if the URL is a restricted URL then we simply exit without packing any data
  361. if (m_pHashRestricted &&
  362. m_pHashRestricted->Find(pszUrlA, FALSE, (LPVOID *)&hBody)==S_OK)
  363. {
  364. MemFree(pszUrlA);
  365. return S_OK;
  366. }
  367. // hack: if it's an MHTML: url then we have to fixup to get the cid:
  368. if (StrCmpNIA(pszUrlA, "mhtml:", 6)==0)
  369. {
  370. if (!FAILED(MimeOleParseMhtmlUrl(pszUrlA, NULL, &pszBody)))
  371. {
  372. // pszBody pszUrlA is guarnteed to be smaller
  373. StrCpyNA(pszUrlA, pszBody, cchSize * sizeof(pszUrlA[0]));
  374. SafeMimeOleFree(pszBody);
  375. }
  376. }
  377. if (m_pHash &&
  378. m_pHash->Find(pszUrlA, FALSE, (LPVOID *)&hBody)==S_OK)
  379. {
  380. // we've already seen this url one before in this document, and have it's HBODY already
  381. // so there's no need to do any work
  382. // try and get the content-id incase the caller is interested
  383. // BUGBUG? possible more than CID need to be ported here...
  384. MimeOleGetBodyPropA(m_pMsgDest, hBody, PIDTOSTR(PID_HDR_CNTID), NOFLAGS, &lpszCID);
  385. goto found;
  386. }
  387. // see if szUrl is in the related section of the source message
  388. if (m_pMsgSrc &&
  389. m_pMsgSrc->ResolveURL(NULL, NULL, pszUrlA, 0, &hBody)==S_OK)
  390. {
  391. hBodyOld = hBody;
  392. // this URL is already in the related section, and we haven't seen it already.
  393. // then let's bind to the data and attach it
  394. if (m_pMsgSrc->BindToObject(hBody, IID_IStream, (LPVOID *)&pstm)==S_OK)
  395. {
  396. // if it's a FILE:// url we use CID: else we use Content-Location
  397. hr = m_pMsgDest->AttachURL(NULL, pszUrlA, (_ShouldUseContentId(pszUrlA)==S_OK ? URL_ATTACH_GENERATE_CID : 0 )|URL_ATTACH_SET_CNTTYPE, pstm, &lpszCID, &hBody);
  398. pstm->Release();
  399. }
  400. // be sure to copy the old content-type and filename over
  401. HrCopyHeader(m_pMsgDest, hBody, m_pMsgSrc, hBodyOld, PIDTOSTR(PID_HDR_CNTTYPE));
  402. HrCopyHeader(m_pMsgDest, hBody, m_pMsgSrc, hBodyOld, PIDTOSTR(PID_HDR_CNTLOC));
  403. HrCopyHeader(m_pMsgDest, hBody, m_pMsgSrc, hBodyOld, PIDTOSTR(STR_PAR_FILENAME));
  404. }
  405. else
  406. {
  407. // if not, then let's try and bind to it ourselves. We don't go thro' MimeOle for this, as we want to
  408. // fail if the URL is bad, so we don't add the part to the Tree.
  409. hr = HrBindToUrl(pszUrlA, &pstm);
  410. if (!FAILED(hr))
  411. {
  412. hr = SniffStreamForMimeType(pstm, &pszMimeTypeW);
  413. if (!FAILED(hr))
  414. {
  415. if (pTag->IsValidMimeType(pszMimeTypeW)==S_OK)
  416. {
  417. LPWSTR pszFileNameW;
  418. // if it's a FILE:// url we use CID: else we use Content-Location
  419. hr = m_pMsgDest->AttachURL(NULL, pszUrlA, (_ShouldUseContentId(pszUrlA)==S_OK ? URL_ATTACH_GENERATE_CID : 0 )|URL_ATTACH_SET_CNTTYPE, pstm, &lpszCID, &hBody);
  420. if (!FAILED(hr))
  421. {
  422. // if attaching a new attachment, try and sniff the file-name
  423. pszFileNameW = PathFindFileNameW(bstrSrc);
  424. if (pszFileNameW)
  425. MimeOleSetBodyPropW(m_pMsgDest, hBody, PIDTOSTR(STR_PAR_FILENAME), NOFLAGS, pszFileNameW);
  426. }
  427. }
  428. else
  429. hr = E_FAIL;
  430. CoTaskMemFree(pszMimeTypeW);
  431. }
  432. pstm->Release();
  433. }
  434. }
  435. // add to the hash table
  436. if (m_pHash &&
  437. !FAILED(hr) && hBody)
  438. hr = m_pHash->Insert(pszUrlA, (void*)hBody, NOFLAGS);
  439. found:
  440. // if we found the content-ID we need to return an allocated BSTR with it in.
  441. if (lpszCID)
  442. {
  443. LPWSTR pszCIDW;
  444. pszCIDW = PszToUnicode(CP_ACP, lpszCID);
  445. if (pszCIDW)
  446. {
  447. if (_CanonicaliseContentId(pszCIDW, &bstrCID)==S_OK)
  448. {
  449. pTag->SetDest(bstrCID);
  450. SysFreeString(bstrCID);
  451. }
  452. MemFree(pszCIDW);
  453. }
  454. SafeMimeOleFree(lpszCID);
  455. }
  456. SafeMemFree(pszUrlA);
  457. return hr;
  458. }
  459. HRESULT CPackager::_ShouldUseContentId(LPSTR pszUrl)
  460. {
  461. // we use Content-Location for urls that begin with "http:", "https:" and "ftp:" for all
  462. // others we will use Content-Id
  463. if (StrCmpNIA(pszUrl, "ftp:", 4)==0 ||
  464. StrCmpNIA(pszUrl, "http:", 5)==0 ||
  465. StrCmpNIA(pszUrl, "https:", 6)==0)
  466. return S_FALSE;
  467. return S_OK;
  468. }
  469. HRESULT CPackager::_RemapUrls(IMimeEditTagCollection *pCollect, BOOL fSave)
  470. {
  471. ULONG cFetched;
  472. IMimeEditTag *pTag;
  473. Assert (pCollect);
  474. pCollect->Reset();
  475. while (pCollect->Next(1, &pTag, &cFetched)==S_OK && cFetched==1)
  476. {
  477. Assert (pTag);
  478. if (fSave)
  479. pTag->OnPreSave();
  480. else
  481. pTag->OnPostSave();
  482. pTag->Release();
  483. }
  484. return S_OK;
  485. }
  486. HRESULT CPackager::_CanonicaliseContentId(LPWSTR pszUrlW, BSTR *pbstr)
  487. {
  488. HRESULT hr;
  489. *pbstr = NULL;
  490. if (StrCmpNIW(pszUrlW, L"cid:", 4)!=0)
  491. {
  492. DWORD cchSize = (lstrlenW(pszUrlW) + 4);
  493. *pbstr = SysAllocStringLen(NULL, cchSize);
  494. if (*pbstr)
  495. {
  496. StrCpyNW(*pbstr, L"cid:", cchSize);
  497. StrCatBuffW(*pbstr, pszUrlW, cchSize);
  498. }
  499. }
  500. else
  501. *pbstr = SysAllocString(pszUrlW);
  502. return *pbstr ? S_OK : E_OUTOFMEMORY;
  503. }
  504. HRESULT SaveAsMHTML(IHTMLDocument2 *pDoc, DWORD dwFlags, IMimeMessage *pMsgSrc, IMimeMessage *pMsgDest, IHashTable *pHashRestricted)
  505. {
  506. CPackager *pPacker=0;
  507. HRESULT hr;
  508. TraceCall("CBody::Save");
  509. pPacker = new CPackager();
  510. if (!pPacker)
  511. {
  512. hr = TraceResult(E_OUTOFMEMORY);
  513. goto error;
  514. }
  515. hr = pPacker->PackageData(pDoc, pMsgSrc, pMsgDest, dwFlags, pHashRestricted);
  516. if (FAILED(hr))
  517. goto error;
  518. error:
  519. ReleaseObj(pPacker);
  520. return hr;
  521. }
  522. HRESULT HashExternalReferences(IHTMLDocument2 *pDoc, IMimeMessage *pMsg, IHashTable **ppHash)
  523. {
  524. HRESULT hr;
  525. IMimeEditTagCollection *rgpCollect[4];
  526. IMimeEditTagCollection *pCollect;
  527. ULONG uCollect,
  528. cCollect=0,
  529. cItems=0,
  530. cCount,
  531. cFetched;
  532. IHashTable *pHash;
  533. IMimeEditTag *pTag;
  534. BSTR bstrSrc;
  535. LPSTR pszUrlA;
  536. // keep trucking if we fail, to catch as many as we can
  537. *ppHash = NULL;
  538. // image collection
  539. if (CreateOEImageCollection(pDoc, &rgpCollect[cCollect])==S_OK)
  540. cCollect++;
  541. // background images
  542. if (CreateBGImageCollection(pDoc, &rgpCollect[cCollect])==S_OK)
  543. cCollect++;
  544. // background sounds
  545. if (CreateBGSoundCollection(pDoc, &rgpCollect[cCollect])==S_OK)
  546. cCollect++;
  547. // active-movie controls (for MSPHONE)
  548. if (CreateActiveMovieCollection(pDoc, &rgpCollect[cCollect])==S_OK)
  549. cCollect++;
  550. // count the number of items we need to package and
  551. // prepare a hash-table for duplicate entries
  552. for (uCollect = 0; uCollect < cCollect; uCollect++)
  553. {
  554. Assert (rgpCollect[uCollect]);
  555. if ((rgpCollect[uCollect])->Count(&cCount)==S_OK)
  556. cItems+=cCount;
  557. }
  558. // create the hashtable
  559. hr = MimeOleCreateHashTable(cItems, TRUE, &pHash);
  560. if (FAILED(hr))
  561. goto error;
  562. // looks for external references in each
  563. for (uCollect = 0; uCollect < cCollect; uCollect++)
  564. {
  565. pCollect = rgpCollect[uCollect];
  566. if (pCollect)
  567. {
  568. pCollect->Reset();
  569. while (pCollect->Next(1, &pTag, &cFetched)==S_OK && cFetched==1)
  570. {
  571. Assert (pTag);
  572. if (pTag->GetSrc(&bstrSrc)==S_OK)
  573. {
  574. pszUrlA = PszToANSI(CP_ACP, bstrSrc);
  575. if (pszUrlA)
  576. {
  577. if (HrFindUrlInMsg(pMsg, pszUrlA, FINDURL_SEARCH_RELATED_ONLY, NULL)!=S_OK)
  578. {
  579. // this URL was not in the message and it external
  580. // let's track it as a restricted URL in our hash
  581. pHash->Insert(pszUrlA, NULL, NOFLAGS);
  582. }
  583. MemFree(pszUrlA);
  584. }
  585. SysFreeString(bstrSrc);
  586. }
  587. pTag->Release();
  588. }
  589. pCollect->Release();
  590. }
  591. }
  592. // return our new hash
  593. *ppHash = pHash;
  594. pHash = NULL;
  595. error:
  596. ReleaseObj(pHash);
  597. return hr;
  598. }