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.

6028 lines
192 KiB

  1. #include "local.h"
  2. #include "resource.h"
  3. #include "cachesrch.h"
  4. #include "sfview.h"
  5. #include <shlwapi.h>
  6. #include <limits.h>
  7. #include "chcommon.h"
  8. #include "hsfolder.h"
  9. #include <mluisupp.h>
  10. #define DM_HSFOLDER 0
  11. #define DM_CACHESEARCH 0x40000000
  12. const TCHAR c_szRegKeyTopNSites[] = TEXT("HistoryTopNSitesView");
  13. #define REGKEYTOPNSITESLEN (ARRAYSIZE(c_szRegKeyTopNSites) - 1)
  14. const TCHAR c_szHistPrefix[] = TEXT("Visited: ");
  15. #define HISTPREFIXLEN (ARRAYSIZE(c_szHistPrefix)-1)
  16. const TCHAR c_szHostPrefix[] = TEXT(":Host: ");
  17. #define HOSTPREFIXLEN (ARRAYSIZE(c_szHostPrefix)-1)
  18. const CHAR c_szIntervalPrefix[] = "MSHist";
  19. #define INTERVALPREFIXLEN (ARRAYSIZE(c_szIntervalPrefix)-1)
  20. const TCHAR c_szTextHeader[] = TEXT("Content-type: text/");
  21. #define TEXTHEADERLEN (ARRAYSIZE(c_szTextHeader) - 1)
  22. const TCHAR c_szHTML[] = TEXT("html");
  23. #define HTMLLEN (ARRAYSIZE(c_szHTML) - 1)
  24. #define TYPICAL_INTERVALS (4+7)
  25. #define ALL_CHANGES (SHCNE_DELETE|SHCNE_MKDIR|SHCNE_RMDIR|SHCNE_CREATE|SHCNE_UPDATEDIR)
  26. #define FORMAT_PARAMS (FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ARGUMENT_ARRAY|FORMAT_MESSAGE_MAX_WIDTH_MASK)
  27. DWORD _DaysInInterval(HSFINTERVAL *pInterval);
  28. void _KeyForInterval(HSFINTERVAL *pInterval, LPTSTR pszInterval, int cchInterval);
  29. void _FileTimeDeltaDays(FILETIME *pftBase, FILETIME *pftNew, int Days);
  30. // BEGIN OF JCORDELL CODE
  31. #define QUANTA_IN_A_SECOND 10000000
  32. #define SECONDS_IN_A_DAY 60 * 60 * 24
  33. #define QUANTA_IN_A_DAY ((__int64) QUANTA_IN_A_SECOND * SECONDS_IN_A_DAY)
  34. #define INT64_VALUE(pFT) ((((__int64)(pFT)->dwHighDateTime) << 32) + (__int64) (pFT)->dwLowDateTime)
  35. #define DAYS_DIFF(s,e) ((int) (( INT64_VALUE(s) - INT64_VALUE(e) ) / QUANTA_IN_A_DAY))
  36. BOOL GetDisplayNameForTimeInterval( const FILETIME *pStartTime, const FILETIME *pEndTime,
  37. TCHAR *szBuffer, int cbBufferLength );
  38. BOOL GetTooltipForTimeInterval( const FILETIME *pStartTime, const FILETIME *pEndTime,
  39. TCHAR *szBuffer, int cbBufferLength );
  40. // END OF JCORDELL CODE
  41. //////////////////////////////////////////////////////////////////////////////
  42. //
  43. // CHistFolderView Functions and Definitions
  44. //
  45. //////////////////////////////////////////////////////////////////////////////
  46. ////////////////////////
  47. //
  48. // Column definition for the Cache Folder DefView
  49. //
  50. enum {
  51. ICOLC_URL_SHORTNAME = 0,
  52. ICOLC_URL_NAME,
  53. ICOLC_URL_TYPE,
  54. ICOLC_URL_SIZE,
  55. ICOLC_URL_EXPIRES,
  56. ICOLC_URL_MODIFIED,
  57. ICOLC_URL_ACCESSED,
  58. ICOLC_URL_LASTSYNCED,
  59. ICOLC_URL_MAX // Make sure this is the last enum item
  60. };
  61. typedef struct _COLSPEC
  62. {
  63. short int iCol;
  64. short int ids; // Id of string for title
  65. short int cchCol; // Number of characters wide to make column
  66. short int iFmt; // The format of the column;
  67. } COLSPEC;
  68. const COLSPEC s_HistIntervalFolder_cols[] = {
  69. {ICOLH_URL_NAME, IDS_TIMEPERIOD_COL, 30, LVCFMT_LEFT},
  70. };
  71. const COLSPEC s_HistHostFolder_cols[] = {
  72. {ICOLH_URL_NAME, IDS_HOSTNAME_COL, 30, LVCFMT_LEFT},
  73. };
  74. const COLSPEC s_HistFolder_cols[] = {
  75. {ICOLH_URL_NAME, IDS_NAME_COL, 30, LVCFMT_LEFT},
  76. {ICOLH_URL_TITLE, IDS_TITLE_COL, 30, LVCFMT_LEFT},
  77. {ICOLH_URL_LASTVISITED, IDS_LASTVISITED_COL, 18, LVCFMT_LEFT},
  78. };
  79. //////////////////////////////////////////////////////////////////////
  80. HRESULT CreateSpecialViewPidl(USHORT usViewType, LPITEMIDLIST* ppidlOut, UINT cbExtra = 0, LPBYTE *ppbExtra = NULL);
  81. HRESULT ConvertStandardHistPidlToSpecialViewPidl(LPCITEMIDLIST pidlStandardHist,
  82. USHORT usViewType,
  83. LPITEMIDLIST *ppidlOut);
  84. #define IS_DIGIT_CHAR(x) (((x) >= '0') && ((x) <= '9'))
  85. #define MIN_MM(x, y) (((x) < (y)) ? (x) : (y))
  86. // _GetHostImportantPart:
  87. // IN: pszHost -- a domain: for example, "www.wisc.edu"
  88. // IN/ puLen -- the length of pszHost
  89. // OUT: puLen -- the length of the new string
  90. // RETURNS: The "important part" of a hostname (e.g. wisc)
  91. //
  92. // Another example: "www.foo.co.uk" ==> "foo"
  93. LPTSTR _GetHostImportantPart(LPTSTR pszHost, UINT *puLen)
  94. {
  95. LPTSTR pszCurEndHostStr = pszHost + (*puLen - 1);
  96. LPTSTR pszDomainBegin = pszHost;
  97. LPTSTR pszSuffix, pszSuffix2;
  98. UINT uSuffixLen;
  99. BOOL fIsIP = FALSE;
  100. LPTSTR pszTemp;
  101. ASSERT(((UINT)lstrlen(pszHost)) == *puLen);
  102. if (*puLen == 0)
  103. return pszHost;
  104. // Filter out IP Addresses
  105. // Heurisitc: Everything after the last "dot"
  106. // has to be a number.
  107. for (pszTemp = (pszHost + *puLen - 1);
  108. pszTemp >= pszHost; --pszTemp)
  109. {
  110. if (*pszTemp == '.')
  111. break;
  112. if (IS_DIGIT_CHAR(*pszTemp))
  113. fIsIP = TRUE;
  114. else
  115. break;
  116. }
  117. if (!fIsIP) {
  118. // Now that we have the url we can strip
  119. if ( ((StrCmpNI(TEXT("www."), pszHost, 4)) == 0) ||
  120. ((StrCmpNI(TEXT("ftp."), pszHost, 4)) == 0) )
  121. pszDomainBegin += 4;
  122. // try to strip out the suffix by finding the last "dot"
  123. if ((pszSuffix = StrRChr(pszHost, pszCurEndHostStr, '.')) &&
  124. (pszSuffix > pszDomainBegin) &&
  125. ((uSuffixLen = (UINT)(pszCurEndHostStr - pszSuffix)) <= 3)) {
  126. // if it is before a two character country code then try
  127. // to rip off some more.
  128. if ( (uSuffixLen <= 2) &&
  129. (pszSuffix2 = StrRChr(pszDomainBegin, pszSuffix - 1, '.')) &&
  130. (pszSuffix2 > pszDomainBegin) &&
  131. ((pszSuffix - pszSuffix2) <= 4) )
  132. pszSuffix = pszSuffix2;
  133. }
  134. else
  135. pszSuffix = pszCurEndHostStr + 1;
  136. *puLen = (UINT)(pszSuffix-pszDomainBegin);
  137. }
  138. return pszDomainBegin;
  139. }
  140. // a utility function for CHistFolder::GetDisplayNameOf
  141. void _GetURLDispName(LPBASEPIDL pcei, LPTSTR pszName, UINT cchName)
  142. {
  143. TCHAR szStr[MAX_PATH];
  144. UINT uHostLen, uImportantPartLen;
  145. static TCHAR szBracketFmt[8] = TEXT(""); // " (%s)" with room for 0253 complex script marker char
  146. ualstrcpyn(szStr, _GetURLTitle(pcei), ARRAYSIZE(szStr));
  147. uImportantPartLen = uHostLen = lstrlen(szStr);
  148. StrCpyN(pszName, _GetHostImportantPart(szStr, &uImportantPartLen), MIN_MM(uImportantPartLen + 1, cchName));
  149. // don't add extra bit on the end if we haven't modified the string
  150. if (uImportantPartLen != uHostLen)
  151. {
  152. if (!szBracketFmt[0])
  153. {
  154. MLLoadString(IDS_HISTHOST_FMT, szBracketFmt, ARRAYSIZE(szBracketFmt));
  155. }
  156. wnsprintf(pszName + uImportantPartLen, cchName - uImportantPartLen, szBracketFmt, szStr);
  157. }
  158. }
  159. HRESULT HistFolderView_MergeMenu(UINT idMenu, LPQCMINFO pqcm)
  160. {
  161. HMENU hmenu = LoadMenu(MLGetHinst(), MAKEINTRESOURCE(idMenu));
  162. if (hmenu)
  163. {
  164. MergeMenuHierarchy(pqcm->hmenu, hmenu, pqcm->idCmdFirst, pqcm->idCmdLast);
  165. DestroyMenu(hmenu);
  166. }
  167. return S_OK;
  168. }
  169. HRESULT HistFolderView_DidDragDrop(IDataObject *pdo, DWORD dwEffect)
  170. {
  171. if (dwEffect & DROPEFFECT_MOVE)
  172. {
  173. CHistItem *pHCItem;
  174. BOOL fBulkDelete;
  175. if (SUCCEEDED(pdo->QueryInterface(IID_IHist, (void **)&pHCItem)))
  176. {
  177. fBulkDelete = pHCItem->_cItems > LOTS_OF_FILES;
  178. for (UINT i = 0; i < pHCItem->_cItems; i++)
  179. {
  180. if (DeleteUrlCacheEntry(HPidlToSourceUrl((LPBASEPIDL)pHCItem->_ppidl[i])))
  181. {
  182. if (!fBulkDelete)
  183. {
  184. _GenerateEvent(SHCNE_DELETE, pHCItem->_pHCFolder->_pidl, pHCItem->_ppidl[i], NULL);
  185. }
  186. }
  187. }
  188. if (fBulkDelete)
  189. {
  190. _GenerateEvent(SHCNE_UPDATEDIR, pHCItem->_pHCFolder->_pidl, NULL, NULL);
  191. }
  192. SHChangeNotifyHandleEvents();
  193. pHCItem->Release();
  194. return S_OK;
  195. }
  196. }
  197. return E_FAIL;
  198. }
  199. // There are copies of exactly this function in SHELL32
  200. // Add the File Type page
  201. HRESULT HistFolderView_OnAddPropertyPages(DWORD pv, SFVM_PROPPAGE_DATA * ppagedata)
  202. {
  203. IShellPropSheetExt * pspse;
  204. HRESULT hr = CoCreateInstance(CLSID_FileTypes, NULL, CLSCTX_INPROC_SERVER,
  205. IID_PPV_ARG(IShellPropSheetExt, &pspse));
  206. if (SUCCEEDED(hr))
  207. {
  208. hr = pspse->AddPages(ppagedata->pfn, ppagedata->lParam);
  209. pspse->Release();
  210. }
  211. return hr;
  212. }
  213. HRESULT HistFolderView_OnGetSortDefaults(FOLDER_TYPE FolderType, int * piDirection, int * plParamSort)
  214. {
  215. *plParamSort = (int)ICOLH_URL_LASTVISITED;
  216. *piDirection = 1;
  217. return S_OK;
  218. }
  219. HRESULT CALLBACK CHistFolder::_sViewCallback(IShellView *psv, IShellFolder *psf,
  220. HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  221. {
  222. CHistFolder *pfolder = NULL;
  223. HRESULT hr = S_OK;
  224. switch (uMsg)
  225. {
  226. case DVM_GETHELPTEXT:
  227. {
  228. TCHAR szText[MAX_PATH];
  229. UINT id = LOWORD(wParam);
  230. UINT cchBuf = HIWORD(wParam);
  231. LPTSTR pszBuf = (LPTSTR)lParam;
  232. MLLoadString(id + IDS_MH_FIRST, szText, ARRAYSIZE(szText));
  233. // we know for a fact that this parameter is really a TCHAR
  234. if ( IsOS( OS_NT ))
  235. {
  236. SHTCharToUnicode( szText, (LPWSTR) pszBuf, cchBuf );
  237. }
  238. else
  239. {
  240. SHTCharToAnsi( szText, (LPSTR) pszBuf, cchBuf );
  241. }
  242. break;
  243. }
  244. case SFVM_GETNOTIFY:
  245. hr = psf->QueryInterface(CLSID_HistFolder, (void **)&pfolder);
  246. if (SUCCEEDED(hr))
  247. {
  248. *(LPCITEMIDLIST*)wParam = pfolder->_pidl; // evil alias
  249. pfolder->Release();
  250. }
  251. else
  252. wParam = 0;
  253. *(LONG*)lParam = ALL_CHANGES;
  254. break;
  255. case DVM_DIDDRAGDROP:
  256. hr = HistFolderView_DidDragDrop((IDataObject *)lParam, (DWORD)wParam);
  257. break;
  258. case DVM_INITMENUPOPUP:
  259. hr = S_OK;
  260. break;
  261. case DVM_INVOKECOMMAND:
  262. _ArrangeFolder(hwnd, (UINT)wParam);
  263. break;
  264. case DVM_COLUMNCLICK:
  265. ShellFolderView_ReArrange(hwnd, (UINT)wParam);
  266. hr = S_OK;
  267. break;
  268. case DVM_MERGEMENU:
  269. hr = HistFolderView_MergeMenu(MENU_HISTORY, (LPQCMINFO)lParam);
  270. break;
  271. case DVM_DEFVIEWMODE:
  272. *(FOLDERVIEWMODE *)lParam = FVM_DETAILS;
  273. break;
  274. case SFVM_ADDPROPERTYPAGES:
  275. hr = HistFolderView_OnAddPropertyPages((DWORD)wParam, (SFVM_PROPPAGE_DATA *)lParam);
  276. break;
  277. case SFVM_GETSORTDEFAULTS:
  278. hr = psf->QueryInterface(CLSID_HistFolder, (void **)&pfolder);
  279. if (SUCCEEDED(hr))
  280. {
  281. hr = HistFolderView_OnGetSortDefaults(pfolder->_foldertype, (int *)wParam, (int *)lParam);
  282. pfolder->Release();
  283. }
  284. else
  285. {
  286. wParam = 0;
  287. lParam = 0;
  288. }
  289. break;
  290. case SFVM_UPDATESTATUSBAR:
  291. ResizeStatusBar(hwnd, FALSE);
  292. // We did not set any text; let defview do it
  293. hr = E_NOTIMPL;
  294. break;
  295. case SFVM_SIZE:
  296. ResizeStatusBar(hwnd, FALSE);
  297. break;
  298. case SFVM_GETPANE:
  299. if (wParam == PANE_ZONE)
  300. *(DWORD*)lParam = 1;
  301. else
  302. *(DWORD*)lParam = PANE_NONE;
  303. break;
  304. case SFVM_WINDOWCREATED:
  305. ResizeStatusBar(hwnd, TRUE);
  306. break;
  307. case SFVM_GETZONE:
  308. *(DWORD*)lParam = URLZONE_LOCAL_MACHINE; // Internet by default
  309. break;
  310. default:
  311. hr = E_FAIL;
  312. }
  313. return hr;
  314. }
  315. HRESULT HistFolderView_CreateInstance(CHistFolder *pHCFolder, void **ppv)
  316. {
  317. CSFV csfv;
  318. csfv.cbSize = sizeof(csfv);
  319. csfv.pshf = (IShellFolder *)pHCFolder;
  320. csfv.psvOuter = NULL;
  321. csfv.pidl = pHCFolder->_pidl;
  322. csfv.lEvents = SHCNE_DELETE; // SHCNE_DISKEVENTS | SHCNE_ASSOCCHANGED | SHCNE_GLOBALEVENTS;
  323. csfv.pfnCallback = CHistFolder::_sViewCallback;
  324. csfv.fvm = (FOLDERVIEWMODE)0; // Have defview restore the folder view mode
  325. return SHCreateShellFolderViewEx(&csfv, (IShellView**)ppv);
  326. }
  327. //////////////////////////////////////////////////////////////////////////////
  328. //
  329. // CHistFolderEnum Object
  330. //
  331. //////////////////////////////////////////////////////////////////////////////
  332. CHistFolderEnum::CHistFolderEnum(DWORD grfFlags, CHistFolder *pHCFolder)
  333. {
  334. TraceMsg(DM_HSFOLDER, "hcfe - CHistFolderEnum() called");
  335. _cRef = 1;
  336. DllAddRef();
  337. _grfFlags = grfFlags,
  338. _pHCFolder = pHCFolder;
  339. pHCFolder->AddRef();
  340. ASSERT(_hEnum == NULL &&
  341. _cbCurrentInterval == 0 &&
  342. _cbIntervals == 0 &&
  343. _pshHashTable == NULL &&
  344. _polFrequentPages == NULL &&
  345. _pIntervalCache == NULL);
  346. }
  347. CHistFolderEnum::~CHistFolderEnum()
  348. {
  349. ASSERT(_cRef == 0); // we should always have a zero ref count here
  350. TraceMsg(DM_HSFOLDER, "hcfe - ~CHistFolderEnum() called.");
  351. _pHCFolder->Release();
  352. if (_pceiWorking)
  353. {
  354. LocalFree(_pceiWorking);
  355. _pceiWorking = NULL;
  356. }
  357. if (_pIntervalCache)
  358. {
  359. LocalFree(_pIntervalCache);
  360. _pIntervalCache = NULL;
  361. }
  362. if (_hEnum)
  363. {
  364. FindCloseUrlCache(_hEnum);
  365. _hEnum = NULL;
  366. }
  367. if (_pshHashTable)
  368. delete _pshHashTable;
  369. if (_polFrequentPages)
  370. delete _polFrequentPages;
  371. if (_pstatenum)
  372. _pstatenum->Release();
  373. DllRelease();
  374. }
  375. HRESULT CHistFolderEnum_CreateInstance(DWORD grfFlags, CHistFolder *pHCFolder, IEnumIDList **ppeidl)
  376. {
  377. TraceMsg(DM_HSFOLDER, "hcfe - CreateInstance() called.");
  378. *ppeidl = NULL; // null the out param
  379. CHistFolderEnum *pHCFE = new CHistFolderEnum(grfFlags, pHCFolder);
  380. if (!pHCFE)
  381. return E_OUTOFMEMORY;
  382. *ppeidl = pHCFE;
  383. return S_OK;
  384. }
  385. HRESULT CHistFolderEnum::QueryInterface(REFIID riid, void **ppv)
  386. {
  387. static const QITAB qit[] = {
  388. QITABENT(CHistFolderEnum, IEnumIDList),
  389. { 0 },
  390. };
  391. return QISearch(this, qit, riid, ppv);
  392. }
  393. ULONG CHistFolderEnum::AddRef(void)
  394. {
  395. return InterlockedIncrement(&_cRef);
  396. }
  397. ULONG CHistFolderEnum::Release(void)
  398. {
  399. if (InterlockedDecrement(&_cRef))
  400. return _cRef;
  401. delete this;
  402. return 0;
  403. }
  404. HRESULT CHistFolderEnum::_NextHistInterval(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched)
  405. {
  406. HRESULT hr = S_OK;
  407. LPBASEPIDL pcei = NULL;
  408. TCHAR szCurrentInterval[INTERVAL_SIZE+1];
  409. // chrisfra 3/27/97 on NT cache files are per user, not so on win95. how do
  410. // we manage containers on win95 if different users are specified different history
  411. // intervals
  412. if (0 == _cbCurrentInterval)
  413. {
  414. hr = _pHCFolder->_ValidateIntervalCache();
  415. if (SUCCEEDED(hr))
  416. {
  417. hr = S_OK;
  418. ENTERCRITICAL;
  419. if (_pIntervalCache)
  420. {
  421. LocalFree(_pIntervalCache);
  422. _pIntervalCache = NULL;
  423. }
  424. if (_pHCFolder->_pIntervalCache)
  425. {
  426. _pIntervalCache = (HSFINTERVAL *)LocalAlloc(LPTR,
  427. _pHCFolder->_cbIntervals*sizeof(HSFINTERVAL));
  428. if (_pIntervalCache == NULL)
  429. {
  430. hr = E_OUTOFMEMORY;
  431. }
  432. else
  433. {
  434. _cbIntervals = _pHCFolder->_cbIntervals;
  435. CopyMemory(_pIntervalCache,
  436. _pHCFolder->_pIntervalCache,
  437. _cbIntervals*sizeof(HSFINTERVAL));
  438. }
  439. }
  440. LEAVECRITICAL;
  441. }
  442. }
  443. if (_pIntervalCache && _cbCurrentInterval < _cbIntervals)
  444. {
  445. _KeyForInterval(&_pIntervalCache[_cbCurrentInterval], szCurrentInterval,
  446. ARRAYSIZE(szCurrentInterval));
  447. pcei = _CreateIdCacheFolderPidl(TRUE,
  448. _pIntervalCache[_cbCurrentInterval].usSign,
  449. szCurrentInterval);
  450. _cbCurrentInterval++;
  451. }
  452. if (pcei)
  453. {
  454. rgelt[0] = (LPITEMIDLIST)pcei;
  455. if (pceltFetched) *pceltFetched = 1;
  456. }
  457. else
  458. {
  459. if (pceltFetched) *pceltFetched = 0;
  460. rgelt[0] = NULL;
  461. hr = S_FALSE;
  462. }
  463. return hr;
  464. }
  465. // This function dispatches the different "views" on History that are possible
  466. HRESULT CHistFolderEnum::_NextViewPart(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched)
  467. {
  468. switch(_pHCFolder->_uViewType) {
  469. case VIEWPIDL_SEARCH:
  470. return _NextViewPart_OrderSearch(celt, rgelt, pceltFetched);
  471. case VIEWPIDL_ORDER_TODAY:
  472. return _NextViewPart_OrderToday(celt, rgelt, pceltFetched);
  473. case VIEWPIDL_ORDER_SITE:
  474. return _NextViewPart_OrderSite(celt, rgelt, pceltFetched);
  475. case VIEWPIDL_ORDER_FREQ:
  476. return _NextViewPart_OrderFreq(celt, rgelt, pceltFetched);
  477. default:
  478. return E_NOTIMPL;
  479. }
  480. }
  481. LPITEMIDLIST _Combine_ViewPidl(USHORT usViewType, LPITEMIDLIST pidl);
  482. // This function wraps wininet's Find(First/Next)UrlCacheEntry API
  483. // returns DWERROR code or zero if successful
  484. DWORD _FindURLCacheEntry(IN LPCTSTR pszCachePrefix,
  485. IN OUT LPINTERNET_CACHE_ENTRY_INFO pcei,
  486. IN OUT HANDLE &hEnum,
  487. IN OUT LPDWORD pdwBuffSize)
  488. {
  489. if (!hEnum)
  490. {
  491. if (! (hEnum = FindFirstUrlCacheEntry(pszCachePrefix, pcei, pdwBuffSize)) )
  492. return GetLastError();
  493. }
  494. else if (!FindNextUrlCacheEntry(hEnum, pcei, pdwBuffSize))
  495. return GetLastError();
  496. return S_OK;
  497. }
  498. // Thie function provides an iterator over all entries in all (MSHIST-type) buckets
  499. // in the cache
  500. DWORD _FindURLFlatCacheEntry(IN HSFINTERVAL *pIntervalCache,
  501. IN LPTSTR pszUserName, // filter out cache entries owned by user
  502. IN BOOL fHostEntry, // retrieve host entries only (FALSE), or no host entries (TRUE)
  503. IN OUT int &cbCurrentInterval, // should begin at the maximum number of intervals
  504. IN OUT LPINTERNET_CACHE_ENTRY_INFO pcei,
  505. IN OUT HANDLE &hEnum,
  506. IN OUT LPDWORD pdwBuffSize
  507. )
  508. {
  509. DWORD dwStoreBuffSize = *pdwBuffSize;
  510. DWORD dwResult = ERROR_NO_MORE_ITEMS;
  511. while (cbCurrentInterval >= 0)
  512. {
  513. if ((dwResult = _FindURLCacheEntry(pIntervalCache[cbCurrentInterval].szPrefix,
  514. pcei, hEnum, pdwBuffSize)) != S_OK)
  515. {
  516. if (dwResult == ERROR_NO_MORE_ITEMS)
  517. {
  518. // This bucket is done, now go get the next one
  519. FindCloseUrlCache(hEnum);
  520. hEnum = NULL;
  521. --cbCurrentInterval;
  522. }
  523. else
  524. break;
  525. }
  526. else
  527. {
  528. // Do requested filtering...
  529. BOOL fIsHost = (StrStr(pcei->lpszSourceUrlName, c_szHostPrefix) == NULL);
  530. if ( ((!pszUserName) || // if requested, filter username
  531. _FilterUserName(pcei, pIntervalCache[cbCurrentInterval].szPrefix, pszUserName)) &&
  532. ((!fHostEntry && !fIsHost) || // filter for host entries
  533. (fHostEntry && fIsHost)) )
  534. {
  535. break;
  536. }
  537. }
  538. // reset for next iteration
  539. *pdwBuffSize = dwStoreBuffSize;
  540. }
  541. return dwResult;
  542. }
  543. // This guy will search the flat cache (MSHist buckets) for a particular URL
  544. // * This function assumes that the Interval cache is good and loaded
  545. // RETURNS: Windows Error code
  546. DWORD CHistFolder::_SearchFlatCacheForUrl(LPCTSTR pszUrl, LPINTERNET_CACHE_ENTRY_INFO pcei, LPDWORD pdwBuffSize)
  547. {
  548. TCHAR szUserName[INTERNET_MAX_USER_NAME_LENGTH + 1]; // username of person logged on
  549. DWORD dwUserNameLen = ARRAYSIZE(szUserName);
  550. if (FAILED(_GetUserName(szUserName, dwUserNameLen)))
  551. szUserName[0] = TEXT('\0');
  552. UINT uSuffixLen = lstrlen(pszUrl) + lstrlen(szUserName) + 1; // extra 1 for '@'
  553. LPTSTR pszPrefixedUrl = ((LPTSTR)LocalAlloc(LPTR, (PREFIX_SIZE + uSuffixLen + 1) * sizeof(TCHAR)));
  554. DWORD dwError = ERROR_FILE_NOT_FOUND;
  555. if (pszPrefixedUrl != NULL)
  556. {
  557. // pszPrefixedUrl will have the format of "PREFIX username@
  558. wnsprintf(pszPrefixedUrl + PREFIX_SIZE, uSuffixLen + 1, TEXT("%s@%s"), szUserName, pszUrl);
  559. for (int i =_cbIntervals - 1; i >= 0; --i)
  560. {
  561. // memcpy doesn't null terminate
  562. memcpy(pszPrefixedUrl, _pIntervalCache[i].szPrefix, PREFIX_SIZE * sizeof(TCHAR));
  563. if (GetUrlCacheEntryInfo(pszPrefixedUrl, pcei, pdwBuffSize))
  564. {
  565. dwError = ERROR_SUCCESS;
  566. break;
  567. }
  568. else if ( ((dwError = GetLastError()) != ERROR_FILE_NOT_FOUND) )
  569. {
  570. break;
  571. }
  572. }
  573. LocalFree(pszPrefixedUrl);
  574. pszPrefixedUrl = NULL;
  575. }
  576. else
  577. {
  578. dwError = ERROR_OUTOFMEMORY;
  579. }
  580. return dwError;
  581. }
  582. //////////////////////////////////////////////////////////////////////
  583. // Most Frequently Visited Sites;
  584. // this structure is used by the enumeration of the cache
  585. // to get the most frequently seen sites
  586. class OrderList_CacheElement : public OrderedList::Element
  587. {
  588. public:
  589. LPTSTR pszUrl;
  590. DWORD dwHitRate;
  591. __int64 llPriority;
  592. int nDaysSinceLastHit;
  593. LPSTATURL lpSTATURL;
  594. static FILETIME ftToday;
  595. static BOOL fInited;
  596. OrderList_CacheElement(LPTSTR pszStr, DWORD dwHR, LPSTATURL lpSU)
  597. {
  598. s_initToday();
  599. ASSERT(pszStr);
  600. pszUrl = (pszStr ? StrDup(pszStr) : StrDup(TEXT("")));
  601. dwHitRate = dwHR;
  602. lpSTATURL = lpSU;
  603. nDaysSinceLastHit = DAYS_DIFF(&ftToday, &(lpSTATURL->ftLastVisited));
  604. // prevent division by zero
  605. if (nDaysSinceLastHit < 0)
  606. nDaysSinceLastHit = 0;
  607. // scale division up by a little less than half of the __int64
  608. llPriority = ((((__int64)dwHitRate) * LONG_MAX) /
  609. ((__int64)(nDaysSinceLastHit + 1)));
  610. //dPriority = ((double)dwHitRate / (double)(nDaysSinceLastHit + 1));
  611. }
  612. virtual int compareWith(OrderedList::Element *pelt)
  613. {
  614. OrderList_CacheElement *polce;
  615. if (pelt)
  616. {
  617. polce = reinterpret_cast<OrderList_CacheElement *>(pelt);
  618. // we're cheating here a bit by returning 1 instead of testing
  619. // for equality, but that's ok...
  620. // return ( (dwHitRate < polce->dwHitRate) ? -1 : 1 );
  621. return ( (llPriority < polce->llPriority) ? -1 : 1 );
  622. }
  623. return 0;
  624. }
  625. virtual ~OrderList_CacheElement()
  626. {
  627. if (pszUrl)
  628. {
  629. LocalFree(pszUrl);
  630. pszUrl = NULL;
  631. }
  632. if (lpSTATURL)
  633. {
  634. if (lpSTATURL->pwcsUrl)
  635. OleFree(lpSTATURL->pwcsUrl);
  636. if (lpSTATURL->pwcsTitle)
  637. OleFree(lpSTATURL->pwcsTitle);
  638. delete lpSTATURL;
  639. }
  640. }
  641. /*
  642. friend ostream& operator<<(ostream& os, OrderList_CacheElement& olce) {
  643. os << " (" << olce.dwHitRate << "; " << olce.nDaysSinceLastHit
  644. << " days; pri=" << olce.llPriority << ") " << olce.pszUrl;
  645. return os;
  646. }
  647. */
  648. static void s_initToday()
  649. {
  650. if (!fInited)
  651. {
  652. SYSTEMTIME sysTime;
  653. GetLocalTime(&sysTime);
  654. SystemTimeToFileTime(&sysTime, &ftToday);
  655. fInited = TRUE;
  656. }
  657. }
  658. };
  659. FILETIME OrderList_CacheElement::ftToday;
  660. BOOL OrderList_CacheElement::fInited = FALSE;
  661. // caller must delete OrderedList
  662. OrderedList* CHistFolderEnum::_GetMostFrequentPages()
  663. {
  664. TCHAR szUserName[INTERNET_MAX_USER_NAME_LENGTH + 1]; // username of person logged on
  665. DWORD dwUserNameLen = INTERNET_MAX_USER_NAME_LENGTH + 1;
  666. if (FAILED(_pHCFolder->_GetUserName(szUserName, dwUserNameLen)))
  667. szUserName[0] = TEXT('\0');
  668. UINT uUserNameLen = lstrlen(szUserName);
  669. // reinit the current time
  670. OrderList_CacheElement::fInited = FALSE;
  671. IUrlHistoryPriv *pUrlHistStg = _pHCFolder->_GetHistStg();
  672. OrderedList *pol = NULL;
  673. if (pUrlHistStg)
  674. {
  675. IEnumSTATURL *penum = NULL;
  676. if (SUCCEEDED(pUrlHistStg->EnumUrls(&penum)) && penum)
  677. {
  678. DWORD dwSites = -1;
  679. DWORD dwType = REG_DWORD;
  680. DWORD dwSize = sizeof(DWORD);
  681. EVAL(SHRegGetUSValue(REGSTR_PATH_MAIN, c_szRegKeyTopNSites, &dwType,
  682. (void *)&dwSites, &dwSize, FALSE,
  683. (void *)&dwSites, dwSize) == ERROR_SUCCESS);
  684. if ( (dwType != REG_DWORD) ||
  685. (dwSize != sizeof(DWORD)) ||
  686. ((int)dwSites < 0) )
  687. {
  688. dwSites = NUM_TOP_SITES;
  689. SHRegSetUSValue(REGSTR_PATH_MAIN, c_szRegKeyTopNSites, REG_DWORD,
  690. (void *)&dwSites, dwSize, SHREGSET_HKCU);
  691. dwSites = NUM_TOP_SITES;
  692. }
  693. pol = new OrderedList(dwSites);
  694. if (pol)
  695. {
  696. STATURL *psuThis = new STATURL;
  697. if (psuThis)
  698. {
  699. penum->SetFilter(NULL, STATURL_QUERYFLAG_TOPLEVEL);
  700. while (pol) {
  701. psuThis->cbSize = sizeof(STATURL);
  702. psuThis->pwcsUrl = NULL;
  703. psuThis->pwcsTitle = NULL;
  704. ULONG cFetched;
  705. if (SUCCEEDED(penum->Next(1, psuThis, &cFetched)) && cFetched)
  706. {
  707. // test: the url (taken from the VISITED history bucket) is a "top-level"
  708. // url that would be in the MSHIST (displayed to user) history bucket
  709. // things ommitted will be certain error urls and frame children pages etc...
  710. if ( (psuThis->dwFlags & STATURLFLAG_ISTOPLEVEL) &&
  711. (psuThis->pwcsUrl) &&
  712. (!IsErrorUrl(psuThis->pwcsUrl)) )
  713. {
  714. UINT uUrlLen = lstrlenW(psuThis->pwcsUrl);
  715. UINT uPrefixLen = HISTPREFIXLEN + uUserNameLen + 1; // '@' and '\0'
  716. LPTSTR pszPrefixedUrl =
  717. ((LPTSTR)LocalAlloc(LPTR, (uUrlLen + uPrefixLen + 1) * sizeof(TCHAR)));
  718. if (pszPrefixedUrl)
  719. {
  720. wnsprintf(pszPrefixedUrl, uPrefixLen + 1 , TEXT("%s%s@"), c_szHistPrefix, szUserName);
  721. StrCpyN(pszPrefixedUrl + uPrefixLen, psuThis->pwcsUrl, uUrlLen + 1);
  722. PROPVARIANT vProp = {0};
  723. if (SUCCEEDED(pUrlHistStg->GetProperty(pszPrefixedUrl + uPrefixLen,
  724. PID_INTSITE_VISITCOUNT, &vProp)) &&
  725. (vProp.vt == VT_UI4))
  726. {
  727. pol->insert(new OrderList_CacheElement(pszPrefixedUrl,
  728. vProp.lVal,
  729. psuThis));
  730. // OrderList now owns this -- he'll free it
  731. psuThis = new STATURL;
  732. if (psuThis)
  733. {
  734. psuThis->cbSize = sizeof(STATURL);
  735. psuThis->pwcsUrl = NULL;
  736. psuThis->pwcsTitle = NULL;
  737. }
  738. else if (pol) {
  739. delete pol;
  740. pol = NULL;
  741. }
  742. }
  743. LocalFree(pszPrefixedUrl);
  744. pszPrefixedUrl = NULL;
  745. }
  746. else if (pol)
  747. { // couldn't allocate
  748. delete pol;
  749. pol = NULL;
  750. }
  751. }
  752. if (psuThis && psuThis->pwcsUrl)
  753. OleFree(psuThis->pwcsUrl);
  754. if (psuThis && psuThis->pwcsTitle)
  755. OleFree(psuThis->pwcsTitle);
  756. }
  757. else // nothing more from the enumeration...
  758. break;
  759. } //while
  760. if (psuThis)
  761. delete psuThis;
  762. }
  763. else if (pol) { //allocation failed
  764. delete pol;
  765. pol = NULL;
  766. }
  767. }
  768. penum->Release();
  769. }
  770. /* DWORD dwBuffSize = MAX_URLCACHE_ENTRY;
  771. DWORD dwError; */
  772. // This commented-out code does the same thing WITHOUT going through
  773. // the IUrlHistoryPriv interface, but, instead going directly
  774. // to wininet
  775. /*
  776. while ((dwError = _FindURLCacheEntry(c_szHistPrefix, _pceiWorking,
  777. _hEnum, &dwBuffSize)) == S_OK) {
  778. // if its a top-level history guy && is cache entry to valid username
  779. if ( (((HISTDATA *)_pceiWorking->lpHeaderInfo)->dwFlags & PIDISF_HISTORY) && //top-level
  780. (_FilterUserName(_pceiWorking, c_szHistPrefix, szUserName)) ) // username is good
  781. {
  782. // perf: we can avoid needlessly creating new cache elements if we're less lazy
  783. pol->insert(new OrderList_CacheElement(_pceiWorking->lpszSourceUrlName,
  784. _pceiWorking->dwHitRate,
  785. _pceiWorking->LastModifiedTime));
  786. }
  787. dwBuffSize = MAX_URLCACHE_ENTRY;
  788. }
  789. ASSERT(dwError == ERROR_NO_MORE_ITEMS);
  790. */
  791. pUrlHistStg->Release();
  792. } // no storage
  793. return pol;
  794. }
  795. HRESULT CHistFolderEnum::_NextViewPart_OrderFreq(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched)
  796. {
  797. HRESULT hr = E_INVALIDARG;
  798. if ( (!_polFrequentPages) && (!(_polFrequentPages = _GetMostFrequentPages())) )
  799. return E_FAIL;
  800. if (rgelt && pceltFetched) {
  801. // loop to fetch as many elements as requested.
  802. for (*pceltFetched = 0; *pceltFetched < celt;) {
  803. // contruct a pidl out of the first element in the orderedlist cache
  804. OrderList_CacheElement *polce = reinterpret_cast<OrderList_CacheElement *>
  805. (_polFrequentPages->removeFirst());
  806. if (polce) {
  807. if (!(rgelt[*pceltFetched] =
  808. reinterpret_cast<LPITEMIDLIST>
  809. (_CreateHCacheFolderPidl(TRUE,
  810. polce->pszUrl, polce->lpSTATURL->ftLastVisited,
  811. polce->lpSTATURL,
  812. polce->llPriority,
  813. polce->dwHitRate))))
  814. {
  815. delete polce;
  816. hr = E_OUTOFMEMORY;
  817. break;
  818. }
  819. ++(*pceltFetched);
  820. delete polce;
  821. hr = S_OK;
  822. }
  823. else {
  824. hr = S_FALSE; // no more...
  825. break;
  826. }
  827. }
  828. }
  829. return hr;
  830. }
  831. // The Next method for view -- Order by Site
  832. HRESULT CHistFolderEnum::_NextViewPart_OrderSite(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched)
  833. {
  834. DWORD dwError = S_OK;
  835. TCHAR szUserName[INTERNET_MAX_USER_NAME_LENGTH + 1]; // username of person logged on
  836. DWORD dwUserNameLen = INTERNET_MAX_USER_NAME_LENGTH + 1; // len of this buffer
  837. LPCTSTR pszStrippedUrl, pszHost, pszCachePrefix = NULL;
  838. LPITEMIDLIST pcei = NULL;
  839. LPCTSTR pszHostToMatch = NULL;
  840. UINT nHostToMatchLen = 0;
  841. if (FAILED(_pHCFolder->_GetUserName(szUserName, dwUserNameLen)))
  842. szUserName[0] = TEXT('\0');
  843. if ((!_pceiWorking) &&
  844. (!(_pceiWorking = (LPINTERNET_CACHE_ENTRY_INFO)LocalAlloc(LPTR, MAX_URLCACHE_ENTRY))))
  845. return E_OUTOFMEMORY;
  846. DWORD dwBuffSize = MAX_URLCACHE_ENTRY;
  847. // load all the intervals and do some cache maintenance:
  848. if (FAILED(_pHCFolder->_ValidateIntervalCache()))
  849. return E_OUTOFMEMORY;
  850. /* To get all sites, we will search all the history buckets
  851. for "Host"-type entries. These entries will be put into
  852. a hash table as we enumerate so that redundant results are
  853. not returned. */
  854. if (!_pshHashTable)
  855. {
  856. // start a new case-insensitive hash table
  857. _pshHashTable = new StrHash(TRUE);
  858. if (_pshHashTable == NULL)
  859. {
  860. return E_OUTOFMEMORY;
  861. }
  862. }
  863. // if we are looking for individual pages within a host,
  864. // then we must find which host to match...
  865. if (_pHCFolder->_uViewDepth == 1) {
  866. LPCITEMIDLIST pidlHost = ILFindLastID(_pHCFolder->_pidl);
  867. ASSERT(_IsValid_IDPIDL(pidlHost) &&
  868. EQUIV_IDSIGN(((LPBASEPIDL)pidlHost)->usSign, IDDPIDL_SIGN));
  869. ua_GetURLTitle( &pszHostToMatch, (LPBASEPIDL)pidlHost );
  870. nHostToMatchLen = (pszHostToMatch ? lstrlen(pszHostToMatch) : 0);
  871. }
  872. // iterate backwards through containers so most recent
  873. // information gets put into the final pidl
  874. if (!_hEnum)
  875. _cbCurrentInterval = (_pHCFolder->_cbIntervals - 1);
  876. while((dwError = _FindURLFlatCacheEntry(_pHCFolder->_pIntervalCache, szUserName,
  877. (_pHCFolder->_uViewDepth == 1),
  878. _cbCurrentInterval,
  879. _pceiWorking, _hEnum, &dwBuffSize)) == S_OK)
  880. {
  881. // reset for next iteration
  882. dwBuffSize = MAX_CACHE_ENTRY_INFO_SIZE;
  883. // this guy takes out the "t-marcmi@" part of the URL
  884. pszStrippedUrl = _StripHistoryUrlToUrl(_pceiWorking->lpszSourceUrlName);
  885. if (_pHCFolder->_uViewDepth == 0) {
  886. if ((DWORD)lstrlen(pszStrippedUrl) > HOSTPREFIXLEN) {
  887. pszHost = &pszStrippedUrl[HOSTPREFIXLEN];
  888. // insertUnique returns non-NULL if this key already exists
  889. if (_pshHashTable->insertUnique(pszHost, TRUE, reinterpret_cast<void *>(1)))
  890. continue; // already given out
  891. pcei = (LPITEMIDLIST)_CreateIdCacheFolderPidl(TRUE, IDDPIDL_SIGN, pszHost);
  892. }
  893. break;
  894. }
  895. else if (_pHCFolder->_uViewDepth == 1) {
  896. TCHAR szHost[INTERNET_MAX_HOST_NAME_LENGTH+1];
  897. // is this entry a doc from the host we're looking for?
  898. _GetURLHost(_pceiWorking, szHost, INTERNET_MAX_HOST_NAME_LENGTH, _GetLocalHost());
  899. if ( (!StrCmpI(szHost, pszHostToMatch)) &&
  900. (!_pshHashTable->insertUnique(pszStrippedUrl,
  901. TRUE, reinterpret_cast<void *>(1))) )
  902. {
  903. STATURL suThis;
  904. HRESULT hrLocal = E_FAIL;
  905. IUrlHistoryPriv *pUrlHistStg = _pHCFolder->_GetHistStg();
  906. if (pUrlHistStg) {
  907. hrLocal = pUrlHistStg->QueryUrl(pszStrippedUrl, STATURL_QUERYFLAG_NOURL, &suThis);
  908. pUrlHistStg->Release();
  909. }
  910. pcei = (LPITEMIDLIST)
  911. _CreateHCacheFolderPidl(TRUE, _pceiWorking->lpszSourceUrlName,
  912. _pceiWorking->LastModifiedTime,
  913. (SUCCEEDED(hrLocal) ? &suThis : NULL), 0,
  914. _pHCFolder->_GetHitCount(_StripHistoryUrlToUrl(_pceiWorking->lpszSourceUrlName)));
  915. if (SUCCEEDED(hrLocal) && suThis.pwcsTitle)
  916. OleFree(suThis.pwcsTitle);
  917. break;
  918. }
  919. }
  920. }
  921. if (pcei && rgelt) {
  922. rgelt[0] = (LPITEMIDLIST)pcei;
  923. if (pceltFetched)
  924. *pceltFetched = 1;
  925. }
  926. else {
  927. dwError = ERROR_NOT_ENOUGH_MEMORY;
  928. }
  929. if (dwError != S_OK) {
  930. if (pceltFetched)
  931. *pceltFetched = 0;
  932. if (_hEnum)
  933. FindCloseUrlCache(_hEnum);
  934. return S_FALSE;
  935. }
  936. return S_OK;
  937. }
  938. // "Next" method for View by "Order seen today"
  939. HRESULT CHistFolderEnum::_NextViewPart_OrderToday(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched)
  940. {
  941. DWORD dwError = S_OK;
  942. TCHAR szUserName[INTERNET_MAX_USER_NAME_LENGTH + 1]; // username of person logged on
  943. DWORD dwUserNameLen = INTERNET_MAX_USER_NAME_LENGTH + 1; // len of this buffer
  944. LPCTSTR pszStrippedUrl, pszHost;
  945. LPBASEPIDL pcei = NULL;
  946. if (FAILED(_pHCFolder->_GetUserName(szUserName, dwUserNameLen)))
  947. szUserName[0] = TEXT('\0');
  948. if ((!_pceiWorking) &&
  949. (!(_pceiWorking = (LPINTERNET_CACHE_ENTRY_INFO)LocalAlloc(LPTR, MAX_URLCACHE_ENTRY))))
  950. return E_OUTOFMEMORY;
  951. if (!_hEnum) {
  952. // load all the intervals and do some cache maintenance:
  953. if (FAILED(_pHCFolder->_ValidateIntervalCache()))
  954. return E_OUTOFMEMORY;
  955. // get only entries for TODAY (important part)
  956. SYSTEMTIME sysTime;
  957. FILETIME fileTime;
  958. GetLocalTime(&sysTime);
  959. SystemTimeToFileTime(&sysTime, &fileTime);
  960. if (FAILED(_pHCFolder->_GetInterval(&fileTime, FALSE, &_pIntervalCur)))
  961. return E_FAIL; // couldn't get interval for Today
  962. }
  963. DWORD dwBuffSize = MAX_CACHE_ENTRY_INFO_SIZE;
  964. while ( (dwError = _FindURLCacheEntry(_pIntervalCur->szPrefix, _pceiWorking, _hEnum,
  965. &dwBuffSize)) == S_OK )
  966. {
  967. dwBuffSize = MAX_CACHE_ENTRY_INFO_SIZE;
  968. // Make sure that his cache entry belongs to szUserName
  969. if (_FilterUserName(_pceiWorking, _pIntervalCur->szPrefix, szUserName)) {
  970. // this guy takes out the "t-marcmi@" part of the URL
  971. pszStrippedUrl = _StripHistoryUrlToUrl(_pceiWorking->lpszSourceUrlName);
  972. if ((DWORD)lstrlen(pszStrippedUrl) > HOSTPREFIXLEN) {
  973. pszHost = &pszStrippedUrl[HOSTPREFIXLEN];
  974. if (StrCmpNI(c_szHostPrefix, pszStrippedUrl, HOSTPREFIXLEN) == 0)
  975. continue; // this is a HOST placeholder, not a real doc
  976. }
  977. IUrlHistoryPriv *pUrlHistStg = _pHCFolder->_GetHistStg();
  978. STATURL suThis;
  979. HRESULT hrLocal = E_FAIL;
  980. if (pUrlHistStg) {
  981. hrLocal = pUrlHistStg->QueryUrl(pszStrippedUrl, STATURL_QUERYFLAG_NOURL, &suThis);
  982. pUrlHistStg->Release();
  983. }
  984. pcei = (LPBASEPIDL) _CreateHCacheFolderPidl(TRUE, _pceiWorking->lpszSourceUrlName,
  985. _pceiWorking->LastModifiedTime,
  986. (SUCCEEDED(hrLocal) ? &suThis : NULL), 0,
  987. _pHCFolder->_GetHitCount(_StripHistoryUrlToUrl(_pceiWorking->lpszSourceUrlName)));
  988. if (SUCCEEDED(hrLocal) && suThis.pwcsTitle)
  989. OleFree(suThis.pwcsTitle);
  990. break;
  991. }
  992. }
  993. if (pcei && rgelt) {
  994. rgelt[0] = (LPITEMIDLIST)pcei;
  995. if (pceltFetched)
  996. *pceltFetched = 1;
  997. }
  998. if (dwError == ERROR_NO_MORE_ITEMS) {
  999. if (pceltFetched)
  1000. *pceltFetched = 0;
  1001. if (_hEnum)
  1002. FindCloseUrlCache(_hEnum);
  1003. return S_FALSE;
  1004. }
  1005. else if (dwError == S_OK)
  1006. return S_OK;
  1007. else
  1008. return E_FAIL;
  1009. }
  1010. /***********************************************************************
  1011. Search Mamagement Stuff:
  1012. In order to maintian state between binds to the IShellFolder from
  1013. the desktop, we base our state information for the searches off a
  1014. global database (linked list) that is keyed by a timestamp generated
  1015. when the search begins.
  1016. This FILETIME is in the pidl for the search.
  1017. ********************************************************************/
  1018. class _CurrentSearches {
  1019. public:
  1020. LONG _cRef;
  1021. FILETIME _ftSearchKey;
  1022. LPWSTR _pwszSearchTarget;
  1023. IShellFolderSearchableCallback *_psfscOnAsyncSearch;
  1024. CacheSearchEngine::StreamSearcher _streamsearcher;
  1025. // Currently doing async search
  1026. BOOL _fSearchingAsync;
  1027. // On next pass, kill this search
  1028. BOOL _fKillSwitch;
  1029. // WARNING: DO NOT access these elements without a critical section!
  1030. _CurrentSearches *_pcsNext;
  1031. _CurrentSearches *_pcsPrev;
  1032. static _CurrentSearches* s_pcsCurrentCacheSearchThreads;
  1033. _CurrentSearches(FILETIME &ftSearchKey, LPCWSTR pwszSrch,
  1034. IShellFolderSearchableCallback *psfsc,
  1035. _CurrentSearches *pcsNext = s_pcsCurrentCacheSearchThreads) :
  1036. _streamsearcher(pwszSrch),
  1037. _fSearchingAsync(FALSE), _fKillSwitch(FALSE), _cRef(1)
  1038. {
  1039. _ftSearchKey = ftSearchKey;
  1040. _pcsNext = pcsNext;
  1041. _pcsPrev = NULL;
  1042. if (psfsc)
  1043. psfsc->AddRef();
  1044. _psfscOnAsyncSearch = psfsc;
  1045. SHStrDupW(pwszSrch, &_pwszSearchTarget);
  1046. }
  1047. ULONG AddRef() {
  1048. return InterlockedIncrement(&_cRef);
  1049. }
  1050. ULONG Release() {
  1051. if (InterlockedDecrement(&_cRef))
  1052. return _cRef;
  1053. delete this;
  1054. return 0;
  1055. }
  1056. // this will increment the refcount to be decremented by s_RemoveSearch
  1057. static void s_NewSearch(_CurrentSearches *pcsNew,
  1058. _CurrentSearches *&pcsHead = s_pcsCurrentCacheSearchThreads)
  1059. {
  1060. ENTERCRITICAL;
  1061. // make sure we're inserting at the front of the list
  1062. ASSERT(pcsNew->_pcsNext == pcsHead);
  1063. ASSERT(pcsNew->_pcsPrev == NULL);
  1064. pcsNew->AddRef();
  1065. if (pcsHead)
  1066. pcsHead->_pcsPrev = pcsNew;
  1067. pcsHead = pcsNew;
  1068. LEAVECRITICAL;
  1069. }
  1070. static void s_RemoveSearch(_CurrentSearches *pcsRemove,
  1071. _CurrentSearches *&pcsHead = s_pcsCurrentCacheSearchThreads);
  1072. // This searches for the search.
  1073. // To find this search searcher, use the search searcher searcher :)
  1074. static _CurrentSearches *s_FindSearch(const FILETIME &ftSearchKey,
  1075. _CurrentSearches *pcsHead = s_pcsCurrentCacheSearchThreads);
  1076. protected:
  1077. ~_CurrentSearches() {
  1078. if (_psfscOnAsyncSearch)
  1079. _psfscOnAsyncSearch->Release();
  1080. CoTaskMemFree(_pwszSearchTarget);
  1081. }
  1082. };
  1083. // A linked list of current cache searchers:
  1084. // For multiple entries to occur in this list, the user would have to be
  1085. // searching the cache on two or more separate queries simultaneously
  1086. _CurrentSearches *_CurrentSearches::s_pcsCurrentCacheSearchThreads = NULL;
  1087. void _CurrentSearches::s_RemoveSearch(_CurrentSearches *pcsRemove, _CurrentSearches *&pcsHead)
  1088. {
  1089. ENTERCRITICAL;
  1090. if (pcsRemove->_pcsPrev)
  1091. pcsRemove->_pcsPrev->_pcsNext = pcsRemove->_pcsNext;
  1092. else
  1093. pcsHead = pcsRemove->_pcsNext;
  1094. if (pcsRemove->_pcsNext)
  1095. pcsRemove->_pcsNext->_pcsPrev = pcsRemove->_pcsPrev;
  1096. pcsRemove->Release();
  1097. LEAVECRITICAL;
  1098. }
  1099. // Caller: Remember to Release() the returned data!!
  1100. _CurrentSearches *_CurrentSearches::s_FindSearch(const FILETIME &ftSearchKey,
  1101. _CurrentSearches *pcsHead)
  1102. {
  1103. ENTERCRITICAL;
  1104. _CurrentSearches *pcsTemp = pcsHead;
  1105. _CurrentSearches *pcsRet = NULL;
  1106. while (pcsTemp) {
  1107. if (((pcsTemp->_ftSearchKey).dwLowDateTime == ftSearchKey.dwLowDateTime) &&
  1108. ((pcsTemp->_ftSearchKey).dwHighDateTime == ftSearchKey.dwHighDateTime))
  1109. {
  1110. pcsRet = pcsTemp;
  1111. break;
  1112. }
  1113. pcsTemp = pcsTemp->_pcsNext;
  1114. }
  1115. if (pcsRet)
  1116. pcsRet->AddRef();
  1117. LEAVECRITICAL;
  1118. return pcsRet;
  1119. }
  1120. /**********************************************************************/
  1121. HRESULT CHistFolderEnum::_NextViewPart_OrderSearch(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched) {
  1122. HRESULT hr = E_FAIL;
  1123. ULONG uFetched = 0;
  1124. TCHAR szUserName[INTERNET_MAX_USER_NAME_LENGTH + 1];
  1125. DWORD dwUserNameLen = INTERNET_MAX_USER_NAME_LENGTH + 1;
  1126. if (FAILED(_pHCFolder->_GetUserName(szUserName, dwUserNameLen)))
  1127. szUserName[0] = TEXT('\0');
  1128. UINT uUserNameLen = lstrlen(szUserName);
  1129. if (_pstatenum == NULL) {
  1130. // This hashtable will eventually be passed off to the background
  1131. // cache search thread so that it doesn't return duplicates.
  1132. ASSERT(NULL == _pshHashTable) // don't leak a _pshHashTable
  1133. _pshHashTable = new StrHash(TRUE);
  1134. if (_pshHashTable) {
  1135. IUrlHistoryPriv *pUrlHistStg = _pHCFolder->_GetHistStg();
  1136. if (pUrlHistStg) {
  1137. if (SUCCEEDED((hr = pUrlHistStg->EnumUrls(&_pstatenum))))
  1138. _pstatenum->SetFilter(NULL, STATURL_QUERYFLAG_TOPLEVEL);
  1139. pUrlHistStg->Release();
  1140. }
  1141. }
  1142. }
  1143. else
  1144. hr = S_OK;
  1145. if (SUCCEEDED(hr)) {
  1146. ASSERT(_pstatenum && _pshHashTable);
  1147. for (uFetched; uFetched < celt;) {
  1148. STATURL staturl = { 0 };
  1149. staturl.cbSize = sizeof(staturl);
  1150. ULONG celtFetched = 0;
  1151. if (SUCCEEDED((hr = _pstatenum->Next(1, &staturl, &celtFetched)))) {
  1152. if (celtFetched) {
  1153. ASSERT(celtFetched == 1);
  1154. if (staturl.pwcsUrl && (staturl.dwFlags & STATURLFLAG_ISTOPLEVEL)) {
  1155. BOOL fMatch = FALSE;
  1156. // all this streamsearcher stuff is just like a 'smart' StrStr
  1157. CacheSearchEngine::StringStream ssUrl(staturl.pwcsUrl);
  1158. if ((!(fMatch =
  1159. (_pHCFolder->_pcsCurrentSearch->_streamsearcher).SearchCharStream(ssUrl))) &&
  1160. staturl.pwcsTitle)
  1161. {
  1162. CacheSearchEngine::StringStream ssTitle(staturl.pwcsTitle);
  1163. fMatch = (_pHCFolder->_pcsCurrentSearch->_streamsearcher).SearchCharStream(ssTitle);
  1164. }
  1165. if (fMatch){ // MATCH!
  1166. // Now, we have to convert the url to a prefixed (ansi, if necessary) url
  1167. UINT uUrlLen = lstrlenW(staturl.pwcsUrl);
  1168. UINT uPrefixLen = HISTPREFIXLEN + uUserNameLen + 1; // '@' and '\0'
  1169. LPTSTR pszPrefixedUrl =
  1170. ((LPTSTR)LocalAlloc(LPTR, (uUrlLen + uPrefixLen + 1) * sizeof(TCHAR)));
  1171. if (pszPrefixedUrl){
  1172. wnsprintf(pszPrefixedUrl, uPrefixLen + uUrlLen + 1,
  1173. TEXT("%s%s@%ls"), c_szHistPrefix, szUserName,
  1174. staturl.pwcsUrl);
  1175. LPHEIPIDL pheiTemp =
  1176. _CreateHCacheFolderPidl(TRUE,
  1177. pszPrefixedUrl, staturl.ftLastVisited,
  1178. &staturl, 0,
  1179. _pHCFolder->_GetHitCount(pszPrefixedUrl + uPrefixLen));
  1180. if (pheiTemp) {
  1181. _pshHashTable->insertUnique(pszPrefixedUrl + uPrefixLen, TRUE,
  1182. reinterpret_cast<void *>(1));
  1183. rgelt[uFetched++] = (LPITEMIDLIST)pheiTemp;
  1184. hr = S_OK;
  1185. }
  1186. LocalFree(pszPrefixedUrl);
  1187. pszPrefixedUrl = NULL;
  1188. }
  1189. }
  1190. }
  1191. if (staturl.pwcsUrl)
  1192. OleFree(staturl.pwcsUrl);
  1193. if (staturl.pwcsTitle)
  1194. OleFree(staturl.pwcsTitle);
  1195. }
  1196. else {
  1197. hr = S_FALSE;
  1198. // Addref this for the ThreadProc who then frees it...
  1199. AddRef();
  1200. SHQueueUserWorkItem((LPTHREAD_START_ROUTINE)s_CacheSearchThreadProc,
  1201. (void *)this,
  1202. 0,
  1203. (DWORD_PTR)NULL,
  1204. (DWORD_PTR *)NULL,
  1205. "shdocvw.dll",
  1206. 0
  1207. );
  1208. break;
  1209. }
  1210. } // succeeded getnext url
  1211. } //for
  1212. if (pceltFetched)
  1213. *pceltFetched = uFetched;
  1214. } // succeeded initalising
  1215. return hr;
  1216. }
  1217. // helper function for s_CacheSearchThreadProc
  1218. BOOL_PTR CHistFolderEnum::s_DoCacheSearch(LPINTERNET_CACHE_ENTRY_INFO pcei,
  1219. LPTSTR pszUserName, UINT uUserNameLen,
  1220. CHistFolderEnum *penum,
  1221. _CurrentSearches *pcsThisThread, IUrlHistoryPriv *pUrlHistStg)
  1222. {
  1223. BOOL_PTR fFound = FALSE;
  1224. LPTSTR pszTextHeader;
  1225. // The header contains "Content-type: text/*"
  1226. if (pcei->lpHeaderInfo && (pszTextHeader = StrStrI(pcei->lpHeaderInfo, c_szTextHeader)))
  1227. {
  1228. // in some cases, urls in the cache differ from urls in the history
  1229. // by only the trailing slash -- we strip it out and test both
  1230. UINT uUrlLen = lstrlen(pcei->lpszSourceUrlName);
  1231. if (uUrlLen && (pcei->lpszSourceUrlName[uUrlLen - 1] == TEXT('/')))
  1232. {
  1233. pcei->lpszSourceUrlName[uUrlLen - 1] = TEXT('\0');
  1234. fFound = (BOOL_PTR)(penum->_pshHashTable->retrieve(pcei->lpszSourceUrlName));
  1235. pcei->lpszSourceUrlName[uUrlLen - 1] = TEXT('/');
  1236. }
  1237. DWORD dwSize = MAX_URLCACHE_ENTRY;
  1238. // see if its already been found and added...
  1239. if ((!fFound) && !(penum->_pshHashTable->retrieve(pcei->lpszSourceUrlName)))
  1240. {
  1241. BOOL fIsHTML = !StrCmpNI(pszTextHeader + TEXTHEADERLEN, c_szHTML, HTMLLEN);
  1242. // Now, try to find the url in history...
  1243. STATURL staturl;
  1244. HRESULT hrLocal;
  1245. hrLocal = pUrlHistStg->QueryUrl(pcei->lpszSourceUrlName, STATFLAG_NONAME, &staturl);
  1246. if (hrLocal == S_OK)
  1247. {
  1248. HANDLE hCacheStream;
  1249. hCacheStream = RetrieveUrlCacheEntryStream(pcei->lpszSourceUrlName, pcei, &dwSize, FALSE, 0);
  1250. if (hCacheStream)
  1251. {
  1252. if (CacheSearchEngine::SearchCacheStream(pcsThisThread->_streamsearcher,
  1253. hCacheStream, fIsHTML)) {
  1254. EVAL(UnlockUrlCacheEntryStream(hCacheStream, 0));
  1255. // Prefix the url so that we can create a pidl out of it -- for now, we will
  1256. // prefix it with "Visited: ", but "Bogus: " may be more appropriate.
  1257. UINT uUrlLen = lstrlen(pcei->lpszSourceUrlName);
  1258. UINT uPrefixLen = HISTPREFIXLEN + uUserNameLen + 1; // '@' and '\0'
  1259. UINT uBuffSize = uUrlLen + uPrefixLen + 1;
  1260. LPTSTR pszPrefixedUrl =
  1261. ((LPTSTR)LocalAlloc(LPTR, uBuffSize * sizeof(TCHAR)));
  1262. if (pszPrefixedUrl)
  1263. {
  1264. wnsprintf(pszPrefixedUrl, uBuffSize, TEXT("%s%s@%s"), c_szHistPrefix, pszUserName,
  1265. pcei->lpszSourceUrlName);
  1266. // Create a pidl for this url
  1267. LPITEMIDLIST pidlFound = (LPITEMIDLIST)
  1268. penum->_pHCFolder->_CreateHCacheFolderPidlFromUrl(FALSE, pszPrefixedUrl);
  1269. if (pidlFound)
  1270. {
  1271. LPITEMIDLIST pidlNotify = ILCombine(penum->_pHCFolder->_pidl, pidlFound);
  1272. if (pidlNotify)
  1273. {
  1274. // add the item to the results list...
  1275. /* without the flush, the shell will coalesce these and turn
  1276. them info SHChangeNotify(SHCNE_UPDATEDIR,..), which will cause nsc
  1277. to do an EnumObjects(), which will start the search up again and again...
  1278. */
  1279. SHChangeNotify(SHCNE_CREATE, SHCNF_IDLIST | SHCNF_FLUSH, pidlNotify, NULL);
  1280. ILFree(pidlNotify);
  1281. fFound = TRUE;
  1282. }
  1283. LocalFree(pidlFound);
  1284. pidlFound = NULL;
  1285. }
  1286. LocalFree(pszPrefixedUrl);
  1287. pszPrefixedUrl = NULL;
  1288. }
  1289. }
  1290. else
  1291. EVAL(UnlockUrlCacheEntryStream(hCacheStream, 0));
  1292. }
  1293. }
  1294. else
  1295. TraceMsg(DM_CACHESEARCH, "In Cache -- Not In History: %s", pcei->lpszSourceUrlName);
  1296. }
  1297. }
  1298. return fFound;
  1299. }
  1300. DWORD WINAPI CHistFolderEnum::s_CacheSearchThreadProc(CHistFolderEnum *penum)
  1301. {
  1302. TCHAR szUserName[INTERNET_MAX_USER_NAME_LENGTH + 1];
  1303. DWORD dwUserNameLen = INTERNET_MAX_USER_NAME_LENGTH + 1;
  1304. if (FAILED(penum->_pHCFolder->_GetUserName(szUserName, dwUserNameLen)))
  1305. szUserName[0] = TEXT('\0');
  1306. UINT uUserNameLen = lstrlen(szUserName);
  1307. BOOL fNoConflictingSearch = TRUE;
  1308. _CurrentSearches *pcsThisThread = NULL;
  1309. IUrlHistoryPriv *pUrlHistStg = penum->_pHCFolder->_GetHistStg();
  1310. if (pUrlHistStg)
  1311. {
  1312. pcsThisThread = _CurrentSearches::s_FindSearch(penum->_pHCFolder->_pcsCurrentSearch->_ftSearchKey);
  1313. if (pcsThisThread)
  1314. {
  1315. // if no one else is doing the same search
  1316. if (FALSE == InterlockedExchange((LONG *)&(pcsThisThread->_fSearchingAsync), TRUE))
  1317. {
  1318. if (pcsThisThread->_psfscOnAsyncSearch)
  1319. pcsThisThread->_psfscOnAsyncSearch->RunBegin(0);
  1320. BYTE ab[MAX_URLCACHE_ENTRY];
  1321. LPINTERNET_CACHE_ENTRY_INFO pcei = (LPINTERNET_CACHE_ENTRY_INFO)(&ab);
  1322. DWORD dwSize = MAX_URLCACHE_ENTRY;
  1323. HANDLE hCacheEnum = FindFirstUrlCacheEntry(NULL, pcei, &dwSize);
  1324. if (hCacheEnum)
  1325. {
  1326. while(!(pcsThisThread->_fKillSwitch))
  1327. {
  1328. s_DoCacheSearch(pcei, szUserName, uUserNameLen, penum, pcsThisThread, pUrlHistStg);
  1329. dwSize = MAX_URLCACHE_ENTRY;
  1330. if (!FindNextUrlCacheEntry(hCacheEnum, pcei, &dwSize))
  1331. {
  1332. ASSERT(GetLastError() == ERROR_NO_MORE_ITEMS);
  1333. break;
  1334. }
  1335. }
  1336. FindCloseUrlCache(hCacheEnum);
  1337. }
  1338. if (pcsThisThread->_psfscOnAsyncSearch)
  1339. pcsThisThread->_psfscOnAsyncSearch->RunEnd(0);
  1340. pcsThisThread->_fSearchingAsync = FALSE; // It's been removed - no chance of
  1341. // a race condition
  1342. }
  1343. pcsThisThread->Release();
  1344. }
  1345. ATOMICRELEASE(pUrlHistStg);
  1346. }
  1347. ATOMICRELEASE(penum);
  1348. return 0;
  1349. }
  1350. //
  1351. // this gets the local host name as known by the shell
  1352. // by default assume "My Computer" or whatever
  1353. //
  1354. void _GetLocalHost(LPTSTR psz, DWORD cch)
  1355. {
  1356. *psz = 0;
  1357. IShellFolder* psf;
  1358. if (SUCCEEDED(SHGetDesktopFolder(&psf)))
  1359. {
  1360. WCHAR sz[GUIDSTR_MAX + 3];
  1361. sz[0] = sz[1] = TEXT(':');
  1362. SHStringFromGUIDW(CLSID_MyComputer, sz+2, SIZECHARS(sz)-2);
  1363. LPITEMIDLIST pidl;
  1364. if (SUCCEEDED(psf->ParseDisplayName(NULL, NULL, sz, NULL, &pidl, NULL)))
  1365. {
  1366. STRRET sr;
  1367. if (SUCCEEDED(psf->GetDisplayNameOf(pidl, SHGDN_NORMAL, &sr)))
  1368. StrRetToBuf(&sr, pidl, psz, cch);
  1369. ILFree(pidl);
  1370. }
  1371. psf->Release();
  1372. }
  1373. if (!*psz)
  1374. MLLoadString(IDS_NOTNETHOST, psz, cch);
  1375. }
  1376. LPCTSTR CHistFolderEnum::_GetLocalHost(void)
  1377. {
  1378. if (!*_szLocalHost)
  1379. ::_GetLocalHost(_szLocalHost, SIZECHARS(_szLocalHost));
  1380. return _szLocalHost;
  1381. }
  1382. //////////////////////////////////
  1383. //
  1384. // IEnumIDList Methods
  1385. //
  1386. HRESULT CHistFolderEnum::Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched)
  1387. {
  1388. HRESULT hr = S_FALSE;
  1389. DWORD dwBuffSize;
  1390. DWORD dwError;
  1391. LPTSTR pszSearchPattern = NULL;
  1392. TCHAR szUserName[INTERNET_MAX_USER_NAME_LENGTH + 1]; // username of person logged on
  1393. DWORD dwUserNameLen = INTERNET_MAX_USER_NAME_LENGTH + 1; // len of this buffer
  1394. TCHAR szHistSearchPattern[PREFIX_SIZE + 1]; // search pattern for history items
  1395. TCHAR szHost[INTERNET_MAX_HOST_NAME_LENGTH+1];
  1396. TraceMsg(DM_HSFOLDER, "hcfe - Next() called.");
  1397. if (_pHCFolder->_uViewType)
  1398. return _NextViewPart(celt, rgelt, pceltFetched);
  1399. if ((IsLeaf(_pHCFolder->_foldertype) && 0 == (SHCONTF_NONFOLDERS & _grfFlags)) ||
  1400. (!IsLeaf(_pHCFolder->_foldertype) && 0 == (SHCONTF_FOLDERS & _grfFlags)))
  1401. {
  1402. dwError = 0xFFFFFFFF;
  1403. goto exitPoint;
  1404. }
  1405. if (FOLDER_TYPE_Hist == _pHCFolder->_foldertype)
  1406. {
  1407. return _NextHistInterval(celt, rgelt, pceltFetched);
  1408. }
  1409. if (_pceiWorking == NULL)
  1410. {
  1411. _pceiWorking = (LPINTERNET_CACHE_ENTRY_INFO)LocalAlloc(LPTR, MAX_URLCACHE_ENTRY);
  1412. if (_pceiWorking == NULL)
  1413. {
  1414. dwError = ERROR_NOT_ENOUGH_MEMORY;
  1415. goto exitPoint;
  1416. }
  1417. }
  1418. // Set up things to enumerate history items, if appropriate, otherwise,
  1419. // we'll just pass in NULL and enumerate all items as before.
  1420. if (!_hEnum)
  1421. {
  1422. if (FAILED(_pHCFolder->_ValidateIntervalCache()))
  1423. {
  1424. dwError = ERROR_NOT_ENOUGH_MEMORY;
  1425. goto exitPoint;
  1426. }
  1427. }
  1428. if (FAILED(_pHCFolder->_GetUserName(szUserName, dwUserNameLen)))
  1429. szUserName[0] = TEXT('\0');
  1430. StrCpyN(szHistSearchPattern, _pHCFolder->_pszCachePrefix, ARRAYSIZE(szHistSearchPattern));
  1431. // We can't pass in the whole search pattern that we want,
  1432. // because FindFirstUrlCacheEntry is busted. It will only look at the
  1433. // prefix if there is a cache container for that prefix. So, we can
  1434. // pass in "Visited: " and enumerate all the history items in the cache,
  1435. // but then we need to pull out only the ones with the correct username.
  1436. // StrCpy(szHistSearchPattern, szUserName);
  1437. pszSearchPattern = szHistSearchPattern;
  1438. TryAgain:
  1439. dwBuffSize = MAX_URLCACHE_ENTRY;
  1440. dwError = S_OK;
  1441. if (!_hEnum) // _hEnum maintains our state as we iterate over all the cache entries
  1442. {
  1443. _hEnum = FindFirstUrlCacheEntry(pszSearchPattern, _pceiWorking, &dwBuffSize);
  1444. if (!_hEnum)
  1445. dwError = GetLastError();
  1446. }
  1447. else if (!FindNextUrlCacheEntry(_hEnum, _pceiWorking, &dwBuffSize))
  1448. {
  1449. dwError = GetLastError();
  1450. }
  1451. if (S_OK == dwError)
  1452. {
  1453. LPBASEPIDL pcei = NULL;
  1454. TCHAR szTempStrippedUrl[MAX_URL_STRING];
  1455. LPCTSTR pszStrippedUrl;
  1456. BOOL fIsHost;
  1457. LPCTSTR pszHost;
  1458. //mm: Make sure that this cache entry belongs to szUserName (relevant to Win95)
  1459. if (!_FilterUserName(_pceiWorking, _pHCFolder->_pszCachePrefix, szUserName))
  1460. goto TryAgain;
  1461. StrCpyN(szTempStrippedUrl, _pceiWorking->lpszSourceUrlName, ARRAYSIZE(szTempStrippedUrl));
  1462. pszStrippedUrl = _StripHistoryUrlToUrl(szTempStrippedUrl);
  1463. if ((DWORD)lstrlen(pszStrippedUrl) > HOSTPREFIXLEN)
  1464. {
  1465. pszHost = &pszStrippedUrl[HOSTPREFIXLEN];
  1466. fIsHost = !StrCmpNI(c_szHostPrefix, pszStrippedUrl, HOSTPREFIXLEN);
  1467. }
  1468. else
  1469. {
  1470. fIsHost = FALSE;
  1471. }
  1472. //mm: this is most likely domains:
  1473. if (FOLDER_TYPE_HistInterval == _pHCFolder->_foldertype) // return unique domains
  1474. {
  1475. if (!fIsHost)
  1476. goto TryAgain;
  1477. pcei = _CreateIdCacheFolderPidl(TRUE, IDDPIDL_SIGN, pszHost);
  1478. }
  1479. else if (NULL != _pHCFolder->_pszDomain) //mm: this must be docs
  1480. {
  1481. TCHAR szSourceUrl[MAX_URL_STRING];
  1482. STATURL suThis;
  1483. HRESULT hrLocal = E_FAIL;
  1484. IUrlHistoryPriv *pUrlHistStg = NULL;
  1485. if (fIsHost)
  1486. goto TryAgain;
  1487. // Filter domain in history view!
  1488. _GetURLHost(_pceiWorking, szHost, INTERNET_MAX_HOST_NAME_LENGTH, _GetLocalHost());
  1489. if (StrCmpI(szHost, _pHCFolder->_pszDomain)) //mm: is this in our domain?!
  1490. goto TryAgain;
  1491. pUrlHistStg = _pHCFolder->_GetHistStg();
  1492. if (pUrlHistStg)
  1493. {
  1494. CHAR szTempUrl[MAX_URL_STRING];
  1495. SHTCharToAnsi(pszStrippedUrl, szTempUrl, ARRAYSIZE(szTempUrl));
  1496. hrLocal = pUrlHistStg->QueryUrlA(szTempUrl, STATURL_QUERYFLAG_NOURL, &suThis);
  1497. pUrlHistStg->Release();
  1498. }
  1499. StrCpyN(szSourceUrl, _pceiWorking->lpszSourceUrlName, ARRAYSIZE(szSourceUrl));
  1500. pcei = (LPBASEPIDL) _CreateHCacheFolderPidl(TRUE,
  1501. szSourceUrl,
  1502. _pceiWorking->LastModifiedTime,
  1503. (SUCCEEDED(hrLocal) ? &suThis : NULL), 0,
  1504. _pHCFolder->_GetHitCount(_StripHistoryUrlToUrl(szSourceUrl)));
  1505. if (SUCCEEDED(hrLocal) && suThis.pwcsTitle)
  1506. OleFree(suThis.pwcsTitle);
  1507. }
  1508. if (pcei)
  1509. {
  1510. rgelt[0] = (LPITEMIDLIST)pcei;
  1511. if (pceltFetched)
  1512. *pceltFetched = 1;
  1513. }
  1514. else
  1515. {
  1516. dwError = ERROR_NOT_ENOUGH_MEMORY;
  1517. }
  1518. }
  1519. exitPoint:
  1520. if (dwError != S_OK)
  1521. {
  1522. if (_hEnum)
  1523. {
  1524. FindCloseUrlCache(_hEnum);
  1525. _hEnum = NULL;
  1526. }
  1527. if (pceltFetched)
  1528. *pceltFetched = 0;
  1529. rgelt[0] = NULL;
  1530. hr = S_FALSE;
  1531. }
  1532. else
  1533. {
  1534. hr = S_OK;
  1535. }
  1536. return hr;
  1537. }
  1538. HRESULT CHistFolderEnum::Skip(ULONG celt)
  1539. {
  1540. TraceMsg(DM_HSFOLDER, "hcfe - Skip() called.");
  1541. return E_NOTIMPL;
  1542. }
  1543. HRESULT CHistFolderEnum::Reset()
  1544. {
  1545. TraceMsg(DM_HSFOLDER, "hcfe - Reset() called.");
  1546. return E_NOTIMPL;
  1547. }
  1548. HRESULT CHistFolderEnum::Clone(IEnumIDList **ppenum)
  1549. {
  1550. TraceMsg(DM_HSFOLDER, "hcfe - Clone() called.");
  1551. return E_NOTIMPL;
  1552. }
  1553. //////////////////////////////////////////////////////////////////////////////
  1554. //
  1555. // CHistFolder Object
  1556. //
  1557. //////////////////////////////////////////////////////////////////////////////
  1558. CHistFolder::CHistFolder(FOLDER_TYPE FolderType)
  1559. {
  1560. TraceMsg(DM_HSFOLDER, "hcf - CHistFolder() called.");
  1561. _cRef = 1;
  1562. _foldertype = FolderType;
  1563. ASSERT( _uViewType == 0 &&
  1564. _uViewDepth == 0 &&
  1565. _pszCachePrefix == NULL &&
  1566. _pszDomain == NULL &&
  1567. _cbIntervals == 0 &&
  1568. _pIntervalCache == NULL &&
  1569. _fValidatingCache == FALSE &&
  1570. _dwIntervalCached == 0 &&
  1571. _ftDayCached.dwHighDateTime == 0 &&
  1572. _ftDayCached.dwLowDateTime == 0 &&
  1573. _pidl == NULL );
  1574. DllAddRef();
  1575. }
  1576. CHistFolder::~CHistFolder()
  1577. {
  1578. ASSERT(_cRef == 0); // should always have zero
  1579. TraceMsg(DM_HSFOLDER, "hcf - ~CHistFolder() called.");
  1580. if (_pIntervalCache)
  1581. {
  1582. LocalFree(_pIntervalCache);
  1583. _pIntervalCache = NULL;
  1584. }
  1585. if (_pszCachePrefix)
  1586. {
  1587. LocalFree(_pszCachePrefix);
  1588. _pszCachePrefix = NULL;
  1589. }
  1590. if (_pszDomain)
  1591. {
  1592. LocalFree(_pszDomain);
  1593. _pszDomain = NULL;
  1594. }
  1595. if (_pidl)
  1596. ILFree(_pidl);
  1597. if (_pUrlHistStg)
  1598. {
  1599. _pUrlHistStg->Release();
  1600. _pUrlHistStg = NULL;
  1601. }
  1602. if (_pcsCurrentSearch)
  1603. _pcsCurrentSearch->Release();
  1604. DllRelease();
  1605. }
  1606. LPITEMIDLIST _Combine_ViewPidl(USHORT usViewType, LPITEMIDLIST pidl)
  1607. {
  1608. LPITEMIDLIST pidlResult = NULL;
  1609. LPVIEWPIDL pviewpidl = (LPVIEWPIDL)SHAlloc(sizeof(VIEWPIDL) + sizeof(USHORT));
  1610. if (pviewpidl)
  1611. {
  1612. memset(pviewpidl, 0, sizeof(VIEWPIDL) + sizeof(USHORT));
  1613. pviewpidl->cb = sizeof(VIEWPIDL);
  1614. pviewpidl->usSign = VIEWPIDL_SIGN;
  1615. pviewpidl->usViewType = usViewType;
  1616. ASSERT(pviewpidl->usExtra == 0);//pcei->usSign;
  1617. if (pidl)
  1618. {
  1619. pidlResult = ILCombine((LPITEMIDLIST)pviewpidl, pidl);
  1620. SHFree(pviewpidl);
  1621. }
  1622. else
  1623. pidlResult = (LPITEMIDLIST)pviewpidl;
  1624. }
  1625. return pidlResult;
  1626. }
  1627. STDMETHODIMP CHistFolder::_GetDetail(LPCITEMIDLIST pidl, UINT iColumn, LPTSTR pszStr, UINT cchStr)
  1628. {
  1629. *pszStr = 0;
  1630. switch (iColumn)
  1631. {
  1632. case ICOLH_URL_NAME:
  1633. if (_IsLeaf())
  1634. StrCpyN(pszStr, _StripHistoryUrlToUrl(HPidlToSourceUrl((LPBASEPIDL)pidl)), cchStr);
  1635. else
  1636. _GetURLDispName((LPBASEPIDL)pidl, pszStr, cchStr);
  1637. break;
  1638. case ICOLH_URL_TITLE:
  1639. _GetHistURLDispName((LPHEIPIDL)pidl, pszStr, cchStr);
  1640. break;
  1641. case ICOLH_URL_LASTVISITED:
  1642. FileTimeToDateTimeStringInternal(&((LPHEIPIDL)pidl)->ftModified, pszStr, cchStr, TRUE);
  1643. break;
  1644. }
  1645. return S_OK;
  1646. }
  1647. HRESULT CHistFolder::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pdi)
  1648. {
  1649. HRESULT hr;
  1650. const COLSPEC *pcol;
  1651. UINT nCols;
  1652. if (_foldertype == FOLDER_TYPE_Hist)
  1653. {
  1654. pcol = s_HistIntervalFolder_cols;
  1655. nCols = ARRAYSIZE(s_HistIntervalFolder_cols);
  1656. }
  1657. else if (_foldertype == FOLDER_TYPE_HistInterval)
  1658. {
  1659. pcol = s_HistHostFolder_cols;
  1660. nCols = ARRAYSIZE(s_HistHostFolder_cols);
  1661. }
  1662. else
  1663. {
  1664. pcol = s_HistFolder_cols;
  1665. nCols = ARRAYSIZE(s_HistFolder_cols);
  1666. }
  1667. if (pidl == NULL)
  1668. {
  1669. if (iColumn < nCols)
  1670. {
  1671. TCHAR szTemp[128];
  1672. pdi->fmt = pcol[iColumn].iFmt;
  1673. pdi->cxChar = pcol[iColumn].cchCol;
  1674. MLLoadString(pcol[iColumn].ids, szTemp, ARRAYSIZE(szTemp));
  1675. hr = StringToStrRet(szTemp, &pdi->str);
  1676. }
  1677. else
  1678. hr = E_FAIL; // enum done
  1679. }
  1680. else
  1681. {
  1682. // Make sure the pidl is dword aligned.
  1683. if(iColumn >= nCols)
  1684. hr = E_FAIL;
  1685. else
  1686. {
  1687. BOOL fRealigned;
  1688. hr = AlignPidl(&pidl, &fRealigned);
  1689. if (SUCCEEDED(hr) )
  1690. {
  1691. TCHAR szTemp[MAX_URL_STRING];
  1692. hr = _GetDetail(pidl, iColumn, szTemp, ARRAYSIZE(szTemp));
  1693. if (SUCCEEDED(hr))
  1694. hr = StringToStrRet(szTemp, &pdi->str);
  1695. }
  1696. if (fRealigned)
  1697. FreeRealignedPidl(pidl);
  1698. }
  1699. }
  1700. return hr;
  1701. }
  1702. STDAPI HistFolder_CreateInstance(IUnknown* punkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
  1703. {
  1704. *ppunk = NULL; // null the out param
  1705. if (punkOuter)
  1706. return CLASS_E_NOAGGREGATION;
  1707. CHistFolder *phist = new CHistFolder(FOLDER_TYPE_Hist);
  1708. if (!phist)
  1709. return E_OUTOFMEMORY;
  1710. *ppunk = SAFECAST(phist, IShellFolder2*);
  1711. return S_OK;
  1712. }
  1713. HRESULT CHistFolder::QueryInterface(REFIID iid, void **ppv)
  1714. {
  1715. static const QITAB qitHist[] = {
  1716. QITABENT(CHistFolder, IShellFolder2),
  1717. QITABENTMULTI(CHistFolder, IShellFolder, IShellFolder2),
  1718. QITABENT(CHistFolder, IShellIcon),
  1719. QITABENT(CHistFolder, IPersistFolder2),
  1720. QITABENTMULTI(CHistFolder, IPersistFolder, IPersistFolder2),
  1721. QITABENTMULTI(CHistFolder, IPersist, IPersistFolder2),
  1722. QITABENT(CHistFolder, IHistSFPrivate),
  1723. QITABENT(CHistFolder, IShellFolderViewType),
  1724. QITABENT(CHistFolder, IShellFolderSearchable),
  1725. { 0 },
  1726. };
  1727. if (iid == IID_IPersistFolder)
  1728. {
  1729. if (FOLDER_TYPE_Hist != _foldertype)
  1730. {
  1731. *ppv = NULL;
  1732. return E_NOINTERFACE;
  1733. }
  1734. }
  1735. else if (iid == CLSID_HistFolder)
  1736. {
  1737. *ppv = (void *)(CHistFolder *)this;
  1738. AddRef();
  1739. return S_OK;
  1740. }
  1741. return QISearch(this, qitHist, iid, ppv);
  1742. }
  1743. ULONG CHistFolder::AddRef()
  1744. {
  1745. return InterlockedIncrement(&_cRef);
  1746. }
  1747. ULONG CHistFolder::Release()
  1748. {
  1749. if (InterlockedDecrement(&_cRef))
  1750. return _cRef;
  1751. delete this;
  1752. return 0;
  1753. }
  1754. HRESULT CHistFolder::_ExtractInfoFromPidl()
  1755. {
  1756. LPITEMIDLIST pidlThis;
  1757. LPITEMIDLIST pidlLast = NULL;
  1758. LPITEMIDLIST pidlSecondLast = NULL;
  1759. ASSERT(!_uViewType);
  1760. pidlThis = _pidl;
  1761. while (pidlThis->mkid.cb)
  1762. {
  1763. pidlSecondLast = pidlLast;
  1764. pidlLast = pidlThis;
  1765. pidlThis = _ILNext(pidlThis);
  1766. }
  1767. switch (_foldertype)
  1768. {
  1769. case FOLDER_TYPE_Hist:
  1770. _pidlRest = pidlThis;
  1771. break;
  1772. case FOLDER_TYPE_HistInterval:
  1773. _pidlRest = pidlLast;
  1774. break;
  1775. case FOLDER_TYPE_HistDomain:
  1776. _pidlRest = pidlSecondLast;
  1777. break;
  1778. default:
  1779. _pidlRest = NULL;
  1780. }
  1781. HRESULT hr = NULL == _pidlRest ? E_FAIL : S_OK;
  1782. pidlThis = _pidlRest;
  1783. while (SUCCEEDED(hr) && pidlThis->mkid.cb)
  1784. {
  1785. if (_IsValid_IDPIDL(pidlThis))
  1786. {
  1787. LPBASEPIDL pcei = (LPBASEPIDL)pidlThis;
  1788. TCHAR szUrlTitle[MAX_URL_STRING];
  1789. PCTSTR pszUrlTitle = _GetURLTitleAlign((LPBASEPIDL)pidlThis, szUrlTitle, ARRAYSIZE(szUrlTitle));
  1790. if (EQUIV_IDSIGN(pcei->usSign, IDIPIDL_SIGN)) // This is our interval, it implies prefix
  1791. {
  1792. LPCTSTR pszCachePrefix;
  1793. if (_foldertype == FOLDER_TYPE_Hist)
  1794. _foldertype = FOLDER_TYPE_HistInterval;
  1795. hr = _LoadIntervalCache();
  1796. if (SUCCEEDED(hr))
  1797. {
  1798. hr = _GetPrefixForInterval(pszUrlTitle, &pszCachePrefix);
  1799. if (SUCCEEDED(hr))
  1800. {
  1801. hr = SetCachePrefix(pszCachePrefix);
  1802. }
  1803. }
  1804. }
  1805. else // This is our domain
  1806. {
  1807. if (_foldertype == FOLDER_TYPE_HistInterval)
  1808. _foldertype = FOLDER_TYPE_HistDomain;
  1809. SetDomain(pszUrlTitle);
  1810. }
  1811. }
  1812. pidlThis = _ILNext(pidlThis);
  1813. }
  1814. if (SUCCEEDED(hr))
  1815. {
  1816. switch (_foldertype)
  1817. {
  1818. case FOLDER_TYPE_HistDomain:
  1819. if (_pszDomain == NULL)
  1820. hr = E_FAIL;
  1821. //FALL THROUGH INTENDED
  1822. case FOLDER_TYPE_HistInterval:
  1823. if (_pszCachePrefix == NULL)
  1824. hr = E_FAIL;
  1825. break;
  1826. }
  1827. }
  1828. return hr;
  1829. }
  1830. void _SetValueSign(HSFINTERVAL *pInterval, FILETIME ftNow)
  1831. {
  1832. if (_DaysInInterval(pInterval) == 1 && !CompareFileTime(&(pInterval->ftStart), &ftNow))
  1833. {
  1834. pInterval->usSign = IDTPIDL_SIGN;
  1835. }
  1836. else
  1837. {
  1838. pInterval->usSign = IDIPIDL_SIGN;
  1839. }
  1840. }
  1841. void _SetVersion(HSFINTERVAL *pInterval, LPCSTR szInterval)
  1842. {
  1843. USHORT usVers = 0;
  1844. int i;
  1845. DWORD dwIntervalLen = lstrlenA(szInterval);
  1846. // Unknown versions are 0
  1847. if (dwIntervalLen == INTERVAL_SIZE)
  1848. {
  1849. for (i = INTERVAL_PREFIX_LEN; i < INTERVAL_PREFIX_LEN+INTERVAL_VERS_LEN; i++)
  1850. {
  1851. if ('0' > szInterval[i] || '9' < szInterval[i])
  1852. {
  1853. usVers = UNK_INTERVAL_VERS;
  1854. break;
  1855. }
  1856. usVers = usVers * 10 + (szInterval[i] - '0');
  1857. }
  1858. }
  1859. pInterval->usVers = usVers;
  1860. }
  1861. #ifdef UNICODE
  1862. #define _ValueToInterval _ValueToIntervalW
  1863. #else // UNICODE
  1864. #define _ValueToInterval _ValueToIntervalA
  1865. #endif // UNICODE
  1866. HRESULT _ValueToIntervalA(LPCSTR szInterval, FILETIME *pftStart, FILETIME *pftEnd)
  1867. {
  1868. int i;
  1869. int iBase;
  1870. HRESULT hr = E_FAIL;
  1871. SYSTEMTIME sysTime;
  1872. unsigned int digits[RANGE_LEN];
  1873. iBase = lstrlenA(szInterval)-RANGE_LEN;
  1874. for (i = 0; i < RANGE_LEN; i++)
  1875. {
  1876. digits[i] = szInterval[i+iBase] - '0';
  1877. if (digits[i] > 9) goto exitPoint;
  1878. }
  1879. ZeroMemory(&sysTime, sizeof(sysTime));
  1880. sysTime.wYear = digits[0]*1000 + digits[1]*100 + digits[2] * 10 + digits[3];
  1881. sysTime.wMonth = digits[4] * 10 + digits[5];
  1882. sysTime.wDay = digits[6] * 10 + digits[7];
  1883. if (!SystemTimeToFileTime(&sysTime, pftStart)) goto exitPoint;
  1884. ZeroMemory(&sysTime, sizeof(sysTime));
  1885. sysTime.wYear = digits[8]*1000 + digits[9]*100 + digits[10] * 10 + digits[11];
  1886. sysTime.wMonth = digits[12] * 10 + digits[13];
  1887. sysTime.wDay = digits[14] * 10 + digits[15];
  1888. if (!SystemTimeToFileTime(&sysTime, pftEnd)) goto exitPoint;
  1889. // Intervals are open on the end, so end should be strictly > start
  1890. if (CompareFileTime(pftStart, pftEnd) >= 0) goto exitPoint;
  1891. hr = S_OK;
  1892. exitPoint:
  1893. return hr;
  1894. }
  1895. HRESULT _ValueToIntervalW(LPCUWSTR wzInterval, FILETIME *pftStart, FILETIME *pftEnd)
  1896. {
  1897. CHAR szInterval[MAX_PATH];
  1898. LPCWSTR wzAlignedInterval;
  1899. WSTR_ALIGNED_STACK_COPY( &wzAlignedInterval,
  1900. wzInterval );
  1901. ASSERT(lstrlenW(wzAlignedInterval) < ARRAYSIZE(szInterval));
  1902. UnicodeToAnsi(wzAlignedInterval, szInterval, ARRAYSIZE(szInterval));
  1903. return _ValueToIntervalA((LPCSTR) szInterval, pftStart, pftEnd);
  1904. }
  1905. HRESULT CHistFolder::_LoadIntervalCache()
  1906. {
  1907. HRESULT hr;
  1908. DWORD dwLastModified;
  1909. DWORD dwValueIndex;
  1910. DWORD dwPrefixIndex;
  1911. HSFINTERVAL *pIntervalCache = NULL;
  1912. struct {
  1913. INTERNET_CACHE_CONTAINER_INFOA cInfo;
  1914. char szBuffer[MAX_PATH+MAX_PATH];
  1915. } ContainerInfo;
  1916. DWORD dwContainerInfoSize;
  1917. CHAR chSave;
  1918. HANDLE hContainerEnum;
  1919. BOOL fContinue = TRUE;
  1920. FILETIME ftNow;
  1921. SYSTEMTIME st;
  1922. DWORD dwOptions;
  1923. GetLocalTime (&st);
  1924. SystemTimeToFileTime(&st, &ftNow);
  1925. _FileTimeDeltaDays(&ftNow, &ftNow, 0);
  1926. dwLastModified = _dwIntervalCached;
  1927. dwContainerInfoSize = sizeof(ContainerInfo);
  1928. if (_pIntervalCache == NULL || CompareFileTime(&ftNow, &_ftDayCached))
  1929. {
  1930. dwOptions = 0;
  1931. }
  1932. else
  1933. {
  1934. dwOptions = CACHE_FIND_CONTAINER_RETURN_NOCHANGE;
  1935. }
  1936. hContainerEnum = FindFirstUrlCacheContainerA(&dwLastModified,
  1937. &ContainerInfo.cInfo,
  1938. &dwContainerInfoSize,
  1939. dwOptions);
  1940. if (hContainerEnum == NULL)
  1941. {
  1942. DWORD err = GetLastError();
  1943. if (err == ERROR_NO_MORE_ITEMS)
  1944. {
  1945. fContinue = FALSE;
  1946. }
  1947. else if (err == ERROR_INTERNET_NO_NEW_CONTAINERS)
  1948. {
  1949. hr = S_OK;
  1950. goto exitPoint;
  1951. }
  1952. else
  1953. {
  1954. hr = HRESULT_FROM_WIN32(err);
  1955. goto exitPoint;
  1956. }
  1957. }
  1958. // Guarantee we return S_OK we have _pIntervalCache even if we haven't
  1959. // yet created the interval registry keys.
  1960. dwPrefixIndex = 0;
  1961. dwValueIndex = TYPICAL_INTERVALS;
  1962. pIntervalCache = (HSFINTERVAL *) LocalAlloc(LPTR, dwValueIndex*sizeof(HSFINTERVAL));
  1963. if (!pIntervalCache)
  1964. {
  1965. hr = E_OUTOFMEMORY;
  1966. goto exitPoint;
  1967. }
  1968. // All of our intervals map to cache containers starting with
  1969. // c_szIntervalPrefix followed by YYYYMMDDYYYYMMDD
  1970. while (fContinue)
  1971. {
  1972. chSave = ContainerInfo.cInfo.lpszName[INTERVAL_PREFIX_LEN];
  1973. ContainerInfo.cInfo.lpszName[INTERVAL_PREFIX_LEN] = '\0';
  1974. if (!StrCmpIA(ContainerInfo.cInfo.lpszName, c_szIntervalPrefix))
  1975. {
  1976. ContainerInfo.cInfo.lpszName[INTERVAL_PREFIX_LEN] = chSave;
  1977. DWORD dwCNameLen;
  1978. if (dwPrefixIndex >= dwValueIndex)
  1979. {
  1980. HSFINTERVAL *pIntervalCacheNew;
  1981. pIntervalCacheNew = (HSFINTERVAL *) LocalReAlloc(pIntervalCache,
  1982. (dwValueIndex*2)*sizeof(HSFINTERVAL),
  1983. LMEM_ZEROINIT|LMEM_MOVEABLE);
  1984. if (pIntervalCacheNew == NULL)
  1985. {
  1986. hr = E_OUTOFMEMORY;
  1987. goto exitPoint;
  1988. }
  1989. pIntervalCache = pIntervalCacheNew;
  1990. dwValueIndex *= 2;
  1991. }
  1992. dwCNameLen = lstrlenA(ContainerInfo.cInfo.lpszName);
  1993. if (dwCNameLen <= INTERVAL_SIZE && dwCNameLen >= INTERVAL_MIN_SIZE &&
  1994. lstrlenA(ContainerInfo.cInfo.lpszCachePrefix) == PREFIX_SIZE)
  1995. {
  1996. _SetVersion(&pIntervalCache[dwPrefixIndex], ContainerInfo.cInfo.lpszName);
  1997. if (pIntervalCache[dwPrefixIndex].usVers != UNK_INTERVAL_VERS)
  1998. {
  1999. AnsiToTChar(ContainerInfo.cInfo.lpszCachePrefix, pIntervalCache[dwPrefixIndex].szPrefix, ARRAYSIZE(pIntervalCache[dwPrefixIndex].szPrefix));
  2000. hr = _ValueToIntervalA( ContainerInfo.cInfo.lpszName,
  2001. &pIntervalCache[dwPrefixIndex].ftStart,
  2002. &pIntervalCache[dwPrefixIndex].ftEnd);
  2003. if (FAILED(hr))
  2004. goto exitPoint;
  2005. _SetValueSign(&pIntervalCache[dwPrefixIndex], ftNow);
  2006. dwPrefixIndex++;
  2007. }
  2008. else
  2009. {
  2010. pIntervalCache[dwPrefixIndex].usVers = 0;
  2011. }
  2012. }
  2013. //
  2014. // HACK! IE5 bld 807 created containers with prefix length PREFIX_SIZE - 1.
  2015. // Delete these entries so history shows up for anyone upgrading over this
  2016. // build. Delete this code! (edwardp 8/8/98)
  2017. //
  2018. else if (dwCNameLen <= INTERVAL_SIZE && dwCNameLen >= INTERVAL_MIN_SIZE &&
  2019. lstrlenA(ContainerInfo.cInfo.lpszCachePrefix) == PREFIX_SIZE - 1)
  2020. {
  2021. DeleteUrlCacheContainerA(ContainerInfo.cInfo.lpszName, 0);
  2022. }
  2023. }
  2024. dwContainerInfoSize = sizeof(ContainerInfo);
  2025. fContinue = FindNextUrlCacheContainerA(hContainerEnum,
  2026. &ContainerInfo.cInfo,
  2027. &dwContainerInfoSize);
  2028. }
  2029. hr = S_OK;
  2030. _dwIntervalCached = dwLastModified;
  2031. _ftDayCached = ftNow;
  2032. {
  2033. ENTERCRITICAL;
  2034. if (_pIntervalCache)
  2035. {
  2036. LocalFree(_pIntervalCache);
  2037. _pIntervalCache = NULL;
  2038. }
  2039. _pIntervalCache = pIntervalCache;
  2040. LEAVECRITICAL;
  2041. }
  2042. _cbIntervals = dwPrefixIndex;
  2043. // because it will be freed by our destructor
  2044. pIntervalCache = NULL;
  2045. exitPoint:
  2046. if (hContainerEnum) FindCloseUrlCache(hContainerEnum);
  2047. if (pIntervalCache)
  2048. {
  2049. LocalFree(pIntervalCache);
  2050. pIntervalCache = NULL;
  2051. }
  2052. return hr;
  2053. }
  2054. // Returns true if *pftItem falls in the days *pftStart..*pftEnd inclusive
  2055. BOOL _InInterval(FILETIME *pftStart, FILETIME *pftEnd, FILETIME *pftItem)
  2056. {
  2057. return (CompareFileTime(pftStart,pftItem) <= 0 && CompareFileTime(pftItem,pftEnd) < 0);
  2058. }
  2059. // Truncates filetime increments beyond the day and then deltas by Days and converts back
  2060. // to FILETIME increments
  2061. void _FileTimeDeltaDays(FILETIME *pftBase, FILETIME *pftNew, int Days)
  2062. {
  2063. _int64 i64Base;
  2064. i64Base = (((_int64)pftBase->dwHighDateTime) << 32) | pftBase->dwLowDateTime;
  2065. i64Base /= FILE_SEC_TICKS;
  2066. i64Base /= DAY_SECS;
  2067. i64Base += Days;
  2068. i64Base *= FILE_SEC_TICKS;
  2069. i64Base *= DAY_SECS;
  2070. pftNew->dwHighDateTime = (DWORD) ((i64Base >> 32) & 0xFFFFFFFF);
  2071. pftNew->dwLowDateTime = (DWORD) (i64Base & 0xFFFFFFFF);
  2072. }
  2073. DWORD _DaysInInterval(HSFINTERVAL *pInterval)
  2074. {
  2075. _int64 i64Start;
  2076. _int64 i64End;
  2077. i64Start = (((_int64)pInterval->ftStart.dwHighDateTime) << 32) | pInterval->ftStart.dwLowDateTime;
  2078. i64Start /= FILE_SEC_TICKS;
  2079. i64Start /= DAY_SECS;
  2080. i64End = (((_int64)pInterval->ftEnd.dwHighDateTime) << 32) | pInterval->ftEnd.dwLowDateTime;
  2081. i64End /= FILE_SEC_TICKS;
  2082. i64End /= DAY_SECS;
  2083. // NOTE: the lower bound is closed, upper is open (ie first tick of next day)
  2084. return (DWORD) (i64End - i64Start);
  2085. }
  2086. // Returns S_OK if found, S_FALSE if not, error on error
  2087. // finds weekly interval in preference to daily if both exist
  2088. HRESULT CHistFolder::_GetInterval(FILETIME *pftItem, BOOL fWeekOnly, HSFINTERVAL **ppInterval)
  2089. {
  2090. HRESULT hr = E_FAIL;
  2091. HSFINTERVAL *pReturn = NULL;
  2092. int i;
  2093. HSFINTERVAL *pDailyInterval = NULL;
  2094. if (NULL == _pIntervalCache) goto exitPoint;
  2095. for (i = 0; i < _cbIntervals; i ++)
  2096. {
  2097. if (_pIntervalCache[i].usVers == OUR_VERS)
  2098. {
  2099. if (_InInterval(&_pIntervalCache[i].ftStart,
  2100. &_pIntervalCache[i].ftEnd,
  2101. pftItem))
  2102. {
  2103. if (7 != _DaysInInterval(&_pIntervalCache[i]))
  2104. {
  2105. if (!fWeekOnly)
  2106. {
  2107. pDailyInterval = &_pIntervalCache[i];
  2108. }
  2109. continue;
  2110. }
  2111. else
  2112. {
  2113. pReturn = &_pIntervalCache[i];
  2114. hr = S_OK;
  2115. goto exitPoint;
  2116. }
  2117. }
  2118. }
  2119. }
  2120. pReturn = pDailyInterval;
  2121. hr = pReturn ? S_OK : S_FALSE;
  2122. exitPoint:
  2123. if (ppInterval) *ppInterval = pReturn;
  2124. return hr;
  2125. }
  2126. HRESULT CHistFolder::_GetPrefixForInterval(LPCTSTR pszInterval, LPCTSTR *ppszCachePrefix)
  2127. {
  2128. HRESULT hr = E_FAIL;
  2129. int i;
  2130. LPCTSTR pszReturn = NULL;
  2131. FILETIME ftStart;
  2132. FILETIME ftEnd;
  2133. if (NULL == _pIntervalCache) goto exitPoint;
  2134. hr = _ValueToInterval(pszInterval, &ftStart, &ftEnd);
  2135. if (FAILED(hr))
  2136. goto exitPoint;
  2137. for (i = 0; i < _cbIntervals; i ++)
  2138. {
  2139. if(_pIntervalCache[i].usVers == OUR_VERS)
  2140. {
  2141. if (CompareFileTime(&_pIntervalCache[i].ftStart,&ftStart) == 0 &&
  2142. CompareFileTime(&_pIntervalCache[i].ftEnd,&ftEnd) == 0)
  2143. {
  2144. pszReturn = _pIntervalCache[i].szPrefix;
  2145. hr = S_OK;
  2146. break;
  2147. }
  2148. }
  2149. }
  2150. hr = pszReturn ? S_OK : S_FALSE;
  2151. exitPoint:
  2152. if (ppszCachePrefix) *ppszCachePrefix = pszReturn;
  2153. return hr;
  2154. }
  2155. void _KeyForInterval(HSFINTERVAL *pInterval, LPTSTR pszInterval, int cchInterval)
  2156. {
  2157. SYSTEMTIME stStart;
  2158. SYSTEMTIME stEnd;
  2159. CHAR szVers[3];
  2160. #ifndef UNIX
  2161. CHAR szTempBuff[MAX_PATH];
  2162. #else
  2163. CHAR szTempBuff[INTERVAL_SIZE+1];
  2164. #endif
  2165. ASSERT(pInterval->usVers!=UNK_INTERVAL_VERS && pInterval->usVers < 100);
  2166. if (pInterval->usVers)
  2167. {
  2168. wnsprintfA(szVers, ARRAYSIZE(szVers), "%02lu", (ULONG) (pInterval->usVers));
  2169. }
  2170. else
  2171. {
  2172. szVers[0] = '\0';
  2173. }
  2174. FileTimeToSystemTime(&pInterval->ftStart, &stStart);
  2175. FileTimeToSystemTime(&pInterval->ftEnd, &stEnd);
  2176. wnsprintfA(szTempBuff, ARRAYSIZE(szTempBuff),
  2177. "%s%s%04lu%02lu%02lu%04lu%02lu%02lu",
  2178. c_szIntervalPrefix,
  2179. szVers,
  2180. (ULONG) stStart.wYear,
  2181. (ULONG) stStart.wMonth,
  2182. (ULONG) stStart.wDay,
  2183. (ULONG) stEnd.wYear,
  2184. (ULONG) stEnd.wMonth,
  2185. (ULONG) stEnd.wDay);
  2186. AnsiToTChar(szTempBuff, pszInterval, cchInterval);
  2187. }
  2188. LPITEMIDLIST CHistFolder::_HostPidl(LPCTSTR pszHostUrl, HSFINTERVAL *pInterval)
  2189. {
  2190. ASSERT(!_uViewType)
  2191. LPITEMIDLIST pidlReturn;
  2192. LPITEMIDLIST pidl;
  2193. struct _HOSTIDL
  2194. {
  2195. USHORT cb;
  2196. USHORT usSign;
  2197. TCHAR szHost[INTERNET_MAX_HOST_NAME_LENGTH+1];
  2198. } HostIDL;
  2199. struct _INTERVALIDL
  2200. {
  2201. USHORT cb;
  2202. USHORT usSign;
  2203. TCHAR szInterval[INTERVAL_SIZE+1];
  2204. struct _HOSTIDL hostIDL;
  2205. USHORT cbTrail;
  2206. } IntervalIDL;
  2207. LPBYTE pb;
  2208. USHORT cbSave;
  2209. ASSERT(_pidlRest);
  2210. pidl = _pidlRest;
  2211. cbSave = pidl->mkid.cb;
  2212. pidl->mkid.cb = 0;
  2213. ZeroMemory(&IntervalIDL, sizeof(IntervalIDL));
  2214. IntervalIDL.usSign = pInterval->usSign;
  2215. _KeyForInterval(pInterval, IntervalIDL.szInterval, ARRAYSIZE(IntervalIDL.szInterval));
  2216. IntervalIDL.cb = (USHORT)(2*sizeof(USHORT)+ (lstrlen(IntervalIDL.szInterval) + 1) * sizeof(TCHAR));
  2217. pb = ((LPBYTE) (&IntervalIDL)) + IntervalIDL.cb;
  2218. StrCpyN((LPTSTR)(pb+2*sizeof(USHORT)), pszHostUrl,
  2219. (sizeof(IntervalIDL) - (IntervalIDL.cb + (3 * sizeof(USHORT)))) / sizeof(TCHAR));
  2220. HostIDL.usSign = (USHORT)IDDPIDL_SIGN;
  2221. HostIDL.cb = (USHORT)(2*sizeof(USHORT)+(lstrlen((LPTSTR)(pb+2*sizeof(USHORT))) + 1) * sizeof(TCHAR));
  2222. memcpy(pb, &HostIDL, 2*sizeof(USHORT));
  2223. *(USHORT *)(&pb[HostIDL.cb]) = 0; // terminate the HostIDL ItemID
  2224. pidlReturn = ILCombine(_pidl, (LPITEMIDLIST) (&IntervalIDL));
  2225. pidl->mkid.cb = cbSave;
  2226. return pidlReturn;
  2227. }
  2228. // Notify that an event has occured that affects a specific element in
  2229. // history for special viewtypes
  2230. HRESULT CHistFolder::_ViewType_NotifyEvent(IN LPITEMIDLIST pidlRoot,
  2231. IN LPITEMIDLIST pidlHost,
  2232. IN LPITEMIDLIST pidlPage,
  2233. IN LONG wEventId)
  2234. {
  2235. HRESULT hr = S_OK;
  2236. ASSERT(pidlRoot && pidlHost && pidlPage);
  2237. // VIEPWIDL_ORDER_TODAY
  2238. LPITEMIDLIST pidlToday = _Combine_ViewPidl(VIEWPIDL_ORDER_TODAY, pidlPage);
  2239. if (pidlToday)
  2240. {
  2241. LPITEMIDLIST pidlNotify = ILCombine(pidlRoot, pidlToday);
  2242. if (pidlNotify)
  2243. {
  2244. SHChangeNotify(wEventId, SHCNF_IDLIST, pidlNotify, NULL);
  2245. ILFree(pidlNotify);
  2246. }
  2247. ILFree(pidlToday);
  2248. }
  2249. // VIEWPIDL_ORDER_SITE
  2250. LPITEMIDLIST pidlSite = _Combine_ViewPidl(VIEWPIDL_ORDER_SITE, pidlHost);
  2251. if (pidlSite)
  2252. {
  2253. LPITEMIDLIST pidlSitePage = ILCombine(pidlSite, pidlPage);
  2254. if (pidlSitePage)
  2255. {
  2256. LPITEMIDLIST pidlNotify = ILCombine(pidlRoot, pidlSitePage);
  2257. if (pidlNotify)
  2258. {
  2259. SHChangeNotify(wEventId, SHCNF_IDLIST, pidlNotify, NULL);
  2260. ILFree(pidlNotify);
  2261. }
  2262. ILFree(pidlSitePage);
  2263. }
  2264. ILFree(pidlSite);
  2265. }
  2266. return hr;
  2267. }
  2268. LPCTSTR CHistFolder::_GetLocalHost(void)
  2269. {
  2270. if (!*_szLocalHost)
  2271. ::_GetLocalHost(_szLocalHost, SIZECHARS(_szLocalHost));
  2272. return _szLocalHost;
  2273. }
  2274. // NOTE: modifies pszUrl.
  2275. HRESULT CHistFolder::_NotifyWrite(LPTSTR pszUrl, int cchUrl, FILETIME *pftModified, LPITEMIDLIST * ppidlSelect)
  2276. {
  2277. HRESULT hr = S_OK;
  2278. DWORD dwBuffSize = MAX_URLCACHE_ENTRY;
  2279. USHORT cbSave;
  2280. LPITEMIDLIST pidl;
  2281. LPITEMIDLIST pidlNotify;
  2282. LPITEMIDLIST pidlTemp;
  2283. LPITEMIDLIST pidlHost;
  2284. LPHEIPIDL phei = NULL;
  2285. HSFINTERVAL *pInterval;
  2286. FILETIME ftExpires = {0,0};
  2287. BOOL fNewHost;
  2288. LPCTSTR pszStrippedUrl = _StripHistoryUrlToUrl(pszUrl);
  2289. LPCTSTR pszHostUrl = pszStrippedUrl + HOSTPREFIXLEN;
  2290. DWORD cchFree = cchUrl - (DWORD)(pszStrippedUrl-pszUrl);
  2291. CHAR szAnsiUrl[MAX_URL_STRING];
  2292. ASSERT(_pidlRest);
  2293. pidl = _pidlRest;
  2294. cbSave = pidl->mkid.cb;
  2295. pidl->mkid.cb = 0;
  2296. /// Should also be able to get hitcount
  2297. STATURL suThis;
  2298. HRESULT hrLocal = E_FAIL;
  2299. IUrlHistoryPriv *pUrlHistStg = _GetHistStg();
  2300. if (pUrlHistStg)
  2301. {
  2302. hrLocal = pUrlHistStg->QueryUrl(_StripHistoryUrlToUrl(pszUrl),
  2303. STATURL_QUERYFLAG_NOURL, &suThis);
  2304. pUrlHistStg->Release();
  2305. }
  2306. phei = _CreateHCacheFolderPidl(FALSE, pszUrl, *pftModified,
  2307. (SUCCEEDED(hrLocal) ? &suThis : NULL), 0,
  2308. _GetHitCount(_StripHistoryUrlToUrl(pszUrl)));
  2309. if (SUCCEEDED(hrLocal) && suThis.pwcsTitle)
  2310. OleFree(suThis.pwcsTitle);
  2311. if (phei == NULL)
  2312. {
  2313. hr = E_OUTOFMEMORY;
  2314. goto exitPoint;
  2315. }
  2316. if (cchFree <= HOSTPREFIXLEN)
  2317. {
  2318. hr = E_OUTOFMEMORY;
  2319. goto exitPoint;
  2320. }
  2321. StrCpyN((LPTSTR)pszStrippedUrl, c_szHostPrefix, cchFree); // whack on the PIDL!
  2322. cchFree -= HOSTPREFIXLEN;
  2323. _GetURLHostFromUrl(HPidlToSourceUrl((LPBASEPIDL)phei),
  2324. (LPTSTR)pszHostUrl, cchFree, _GetLocalHost());
  2325. // chrisfra 4/9/97 we could take a small performance hit here and always
  2326. // update host entry. this would allow us to efficiently sort domains by most
  2327. // recent access.
  2328. fNewHost = FALSE;
  2329. dwBuffSize = MAX_URLCACHE_ENTRY;
  2330. SHTCharToAnsi(pszUrl, szAnsiUrl, ARRAYSIZE(szAnsiUrl));
  2331. if (!GetUrlCacheEntryInfoA(szAnsiUrl, NULL, 0))
  2332. {
  2333. fNewHost = TRUE;
  2334. if (!CommitUrlCacheEntryA(szAnsiUrl, NULL, ftExpires, *pftModified,
  2335. URLHISTORY_CACHE_ENTRY|STICKY_CACHE_ENTRY,
  2336. NULL, 0, NULL, 0))
  2337. {
  2338. hr = HRESULT_FROM_WIN32(GetLastError());
  2339. }
  2340. if (FAILED(hr))
  2341. goto exitPoint;
  2342. }
  2343. hr = _GetInterval(pftModified, FALSE, &pInterval);
  2344. if (FAILED(hr))
  2345. goto exitPoint;
  2346. pidlTemp = _HostPidl(pszHostUrl, pInterval);
  2347. if (pidlTemp == NULL)
  2348. {
  2349. hr = E_OUTOFMEMORY;
  2350. goto exitPoint;
  2351. }
  2352. // Get just the host part of the pidl
  2353. pidlHost = ILFindLastID(pidlTemp);
  2354. ASSERT(pidlHost);
  2355. if (fNewHost)
  2356. {
  2357. SHChangeNotify(SHCNE_MKDIR, SHCNF_IDLIST, pidlTemp, NULL);
  2358. // We also need to notify special history views if they are listening:
  2359. // For now, just "View by Site" is relevant...
  2360. LPITEMIDLIST pidlViewSuffix = _Combine_ViewPidl(VIEWPIDL_ORDER_SITE, pidlHost);
  2361. if (pidlViewSuffix)
  2362. {
  2363. LPITEMIDLIST pidlNotify = ILCombine(_pidl, pidlViewSuffix);
  2364. if (pidlNotify)
  2365. {
  2366. SHChangeNotify(SHCNE_MKDIR, SHCNF_IDLIST, pidlNotify, NULL);
  2367. ILFree(pidlNotify);
  2368. }
  2369. ILFree(pidlViewSuffix);
  2370. }
  2371. }
  2372. pidlNotify = ILCombine(pidlTemp, (LPITEMIDLIST) phei);
  2373. if (pidlNotify == NULL)
  2374. {
  2375. ILFree(pidlTemp);
  2376. hr = E_OUTOFMEMORY;
  2377. goto exitPoint;
  2378. }
  2379. // Create (if its not there already) and Rename (if its there)
  2380. // Sending both notifys will be faster than trying to figure out
  2381. // which one is appropriate
  2382. SHChangeNotify(SHCNE_CREATE, SHCNF_IDLIST, pidlNotify, NULL);
  2383. // Also notify events for specail viewpidls!
  2384. _ViewType_NotifyEvent(_pidl, pidlHost, (LPITEMIDLIST)phei, SHCNE_CREATE);
  2385. if (ppidlSelect)
  2386. {
  2387. *ppidlSelect = pidlNotify;
  2388. }
  2389. else
  2390. {
  2391. ILFree(pidlNotify);
  2392. }
  2393. ILFree(pidlTemp);
  2394. exitPoint:
  2395. if (phei)
  2396. {
  2397. LocalFree(phei);
  2398. phei = NULL;
  2399. }
  2400. pidl->mkid.cb = cbSave;
  2401. return hr;
  2402. }
  2403. HRESULT CHistFolder::_NotifyInterval(HSFINTERVAL *pInterval, LONG lEventID)
  2404. {
  2405. // special history views are not relevant here...
  2406. if (_uViewType)
  2407. return S_FALSE;
  2408. USHORT cbSave = 0;
  2409. LPITEMIDLIST pidl;
  2410. LPITEMIDLIST pidlNotify = NULL;
  2411. LPITEMIDLIST pidlNotify2 = NULL;
  2412. LPITEMIDLIST pidlNotify3 = NULL;
  2413. HRESULT hr = S_OK;
  2414. struct _INTERVALIDL
  2415. {
  2416. USHORT cb;
  2417. USHORT usSign;
  2418. TCHAR szInterval[INTERVAL_SIZE+1];
  2419. USHORT cbTrail;
  2420. } IntervalIDL,IntervalIDL2;
  2421. ASSERT(_pidlRest);
  2422. pidl = _pidlRest;
  2423. cbSave = pidl->mkid.cb;
  2424. pidl->mkid.cb = 0;
  2425. ZeroMemory(&IntervalIDL, sizeof(IntervalIDL));
  2426. IntervalIDL.usSign = pInterval->usSign;
  2427. _KeyForInterval(pInterval, IntervalIDL.szInterval, ARRAYSIZE(IntervalIDL.szInterval));
  2428. IntervalIDL.cb = (USHORT)(2*sizeof(USHORT) + (lstrlen(IntervalIDL.szInterval) + 1)*sizeof(TCHAR));
  2429. if (lEventID&SHCNE_RENAMEFOLDER || // was TODAY, now is a weekday
  2430. (lEventID&SHCNE_RMDIR && 1 == _DaysInInterval(pInterval)) ) // one day, maybe TODAY
  2431. {
  2432. memcpy(&IntervalIDL2, &IntervalIDL, sizeof(IntervalIDL));
  2433. IntervalIDL2.usSign = (USHORT)IDTPIDL_SIGN;
  2434. pidlNotify2 = ILCombine(_pidl, (LPITEMIDLIST) (&IntervalIDL));
  2435. pidlNotify = ILCombine(_pidl, (LPITEMIDLIST) (&IntervalIDL2));
  2436. if (pidlNotify2 == NULL)
  2437. {
  2438. hr = E_OUTOFMEMORY;
  2439. goto exitPoint;
  2440. }
  2441. if (lEventID&SHCNE_RMDIR)
  2442. {
  2443. pidlNotify3 = pidlNotify2;
  2444. pidlNotify2 = NULL;
  2445. }
  2446. }
  2447. else
  2448. {
  2449. pidlNotify = ILCombine(_pidl, (LPITEMIDLIST) (&IntervalIDL));
  2450. }
  2451. if (pidlNotify == NULL)
  2452. {
  2453. hr = E_OUTOFMEMORY;
  2454. goto exitPoint;
  2455. }
  2456. SHChangeNotify(lEventID, SHCNF_IDLIST, pidlNotify, pidlNotify2);
  2457. if (pidlNotify3) SHChangeNotify(lEventID, SHCNF_IDLIST, pidlNotify3, NULL);
  2458. exitPoint:
  2459. ILFree(pidlNotify);
  2460. ILFree(pidlNotify2);
  2461. ILFree(pidlNotify3);
  2462. if (cbSave) pidl->mkid.cb = cbSave;
  2463. return hr;
  2464. }
  2465. HRESULT CHistFolder::_CreateInterval(FILETIME *pftStart, DWORD dwDays)
  2466. {
  2467. HSFINTERVAL interval;
  2468. TCHAR szInterval[INTERVAL_SIZE+1];
  2469. UINT err;
  2470. FILETIME ftNow;
  2471. SYSTEMTIME stNow;
  2472. CHAR szIntervalAnsi[INTERVAL_SIZE+1], szCachePrefixAnsi[INTERVAL_SIZE+1];
  2473. #define CREATE_OPTIONS (INTERNET_CACHE_CONTAINER_AUTODELETE | \
  2474. INTERNET_CACHE_CONTAINER_NOSUBDIRS | \
  2475. INTERNET_CACHE_CONTAINER_NODESKTOPINIT)
  2476. // _FileTimeDeltaDays guarantees times just at the 0th tick of the day
  2477. _FileTimeDeltaDays(pftStart, &interval.ftStart, 0);
  2478. _FileTimeDeltaDays(pftStart, &interval.ftEnd, dwDays);
  2479. interval.usVers = OUR_VERS;
  2480. GetLocalTime(&stNow);
  2481. SystemTimeToFileTime(&stNow, &ftNow);
  2482. _FileTimeDeltaDays(&ftNow, &ftNow, 0);
  2483. _SetValueSign(&interval, ftNow);
  2484. _KeyForInterval(&interval, szInterval, ARRAYSIZE(szInterval));
  2485. interval.szPrefix[0] = ':';
  2486. StrCpyN(&interval.szPrefix[1], &szInterval[INTERVAL_PREFIX_LEN+INTERVAL_VERS_LEN],
  2487. ARRAYSIZE(interval.szPrefix) - 1);
  2488. StrCatBuff(interval.szPrefix, TEXT(": "), ARRAYSIZE(interval.szPrefix));
  2489. SHTCharToAnsi(szInterval, szIntervalAnsi, ARRAYSIZE(szIntervalAnsi));
  2490. SHTCharToAnsi(interval.szPrefix, szCachePrefixAnsi, ARRAYSIZE(szCachePrefixAnsi));
  2491. if (CreateUrlCacheContainerA(szIntervalAnsi, // Name
  2492. szCachePrefixAnsi, // CachePrefix
  2493. NULL, // Path
  2494. 0, // Cache Limit
  2495. 0, // Container Type
  2496. CREATE_OPTIONS, // Create Options
  2497. NULL, // Create Buffer
  2498. 0)) // Create Buffer size
  2499. {
  2500. _NotifyInterval(&interval, SHCNE_MKDIR);
  2501. err = ERROR_SUCCESS;
  2502. }
  2503. else
  2504. {
  2505. err = GetLastError();
  2506. }
  2507. return ERROR_SUCCESS == err ? S_OK : HRESULT_FROM_WIN32(err);
  2508. }
  2509. HRESULT CHistFolder::_PrefixUrl(LPCTSTR pszStrippedUrl,
  2510. FILETIME *pftLastModifiedTime,
  2511. LPTSTR pszPrefixedUrl,
  2512. DWORD cchPrefixedUrl)
  2513. {
  2514. HRESULT hr;
  2515. HSFINTERVAL *pInterval;
  2516. hr = _GetInterval(pftLastModifiedTime, FALSE, &pInterval);
  2517. if (S_OK == hr)
  2518. {
  2519. if ((DWORD)((lstrlen(pszStrippedUrl) + lstrlen(pInterval->szPrefix) + 1) * sizeof(TCHAR)) > cchPrefixedUrl)
  2520. {
  2521. hr = E_OUTOFMEMORY;
  2522. }
  2523. else
  2524. {
  2525. StrCpyN(pszPrefixedUrl, pInterval->szPrefix, cchPrefixedUrl);
  2526. StrCatBuff(pszPrefixedUrl, pszStrippedUrl, cchPrefixedUrl);
  2527. }
  2528. }
  2529. return hr;
  2530. }
  2531. HRESULT CHistFolder::_WriteHistory(LPCTSTR pszPrefixedUrl, FILETIME ftExpires, FILETIME ftModified,
  2532. BOOL fSendNotify, LPITEMIDLIST * ppidlSelect)
  2533. {
  2534. TCHAR szNewPrefixedUrl[INTERNET_MAX_URL_LENGTH+1];
  2535. HRESULT hr = E_INVALIDARG;
  2536. LPCTSTR pszUrlMinusContainer;
  2537. pszUrlMinusContainer = _StripContainerUrlUrl(pszPrefixedUrl);
  2538. if (pszUrlMinusContainer)
  2539. {
  2540. hr = _PrefixUrl(pszUrlMinusContainer,
  2541. &ftModified,
  2542. szNewPrefixedUrl,
  2543. ARRAYSIZE(szNewPrefixedUrl));
  2544. if (S_OK == hr)
  2545. {
  2546. CHAR szAnsiUrl[MAX_URL_STRING+1];
  2547. SHTCharToAnsi(szNewPrefixedUrl, szAnsiUrl, ARRAYSIZE(szAnsiUrl));
  2548. if (!CommitUrlCacheEntryA(
  2549. szAnsiUrl,
  2550. NULL,
  2551. ftExpires,
  2552. ftModified,
  2553. URLHISTORY_CACHE_ENTRY|STICKY_CACHE_ENTRY,
  2554. NULL,
  2555. 0,
  2556. NULL,
  2557. 0))
  2558. {
  2559. hr = HRESULT_FROM_WIN32(GetLastError());
  2560. }
  2561. else
  2562. {
  2563. if (fSendNotify)
  2564. _NotifyWrite(szNewPrefixedUrl, ARRAYSIZE(szNewPrefixedUrl),
  2565. &ftModified, ppidlSelect);
  2566. }
  2567. }
  2568. }
  2569. return hr;
  2570. }
  2571. // This function will update any shell that might be listening to us
  2572. // to redraw the directory.
  2573. // It will do this by generating a SHCNE_UPDATE for all possible pidl roots
  2574. // that the shell could have. Hopefully, this should be sufficient...
  2575. // Specifically, this is meant to be called by ClearHistory.
  2576. HRESULT CHistFolder::_ViewType_NotifyUpdateAll()
  2577. {
  2578. LPITEMIDLIST pidlHistory;
  2579. if (SUCCEEDED(SHGetHistoryPIDL(&pidlHistory)))
  2580. {
  2581. for (USHORT us = 1; us <= VIEWPIDL_ORDER_MAX; ++us)
  2582. {
  2583. LPITEMIDLIST pidlView;
  2584. if (SUCCEEDED(CreateSpecialViewPidl(us, &pidlView)))
  2585. {
  2586. LPITEMIDLIST pidlTemp = ILCombine(pidlHistory, pidlView);
  2587. if (pidlTemp)
  2588. {
  2589. SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST, pidlTemp, NULL);
  2590. ILFree(pidlTemp);
  2591. }
  2592. ILFree(pidlView);
  2593. }
  2594. }
  2595. ILFree(pidlHistory);
  2596. SHChangeNotifyHandleEvents();
  2597. }
  2598. return S_OK;
  2599. }
  2600. // On a per user basis.
  2601. // chrisfra 6/11/97. _DeleteItems of a Time Interval deletes the entire interval.
  2602. // ClearHistory should probably work the same. Pros of _DeleteEntries is on non-profile,
  2603. // multi-user machine, other user's history is preserved. Cons is that on profile based
  2604. // machine, empty intervals are created.
  2605. HRESULT CHistFolder::ClearHistory()
  2606. {
  2607. HRESULT hr = S_OK;
  2608. int i;
  2609. hr = _ValidateIntervalCache();
  2610. if (SUCCEEDED(hr))
  2611. {
  2612. for (i = 0; i < _cbIntervals; i++)
  2613. {
  2614. #if 0
  2615. if (_DeleteEntries(_pIntervalCache[i].szPrefix, NULL, NULL))
  2616. hr = S_FALSE;
  2617. _NotifyInterval(&_pIntervalCache[i], SHCNE_UPDATEDIR);
  2618. #else
  2619. _DeleteInterval(&_pIntervalCache[i]);
  2620. #endif
  2621. }
  2622. }
  2623. #ifndef UNIX
  2624. _ViewType_NotifyUpdateAll();
  2625. #endif
  2626. return hr;
  2627. }
  2628. // ftModified is in "User Perceived", ie local time
  2629. // stuffed into FILETIME as if it were UNC. ftExpires is in normal UNC time.
  2630. HRESULT CHistFolder::WriteHistory(LPCTSTR pszPrefixedUrl,
  2631. FILETIME ftExpires, FILETIME ftModified,
  2632. LPITEMIDLIST * ppidlSelect)
  2633. {
  2634. HRESULT hr;
  2635. hr = _ValidateIntervalCache();
  2636. if (SUCCEEDED(hr))
  2637. {
  2638. hr = _WriteHistory(pszPrefixedUrl, ftExpires, ftModified, TRUE, ppidlSelect);
  2639. }
  2640. return hr;
  2641. }
  2642. // Makes best efforts attempt to copy old style history items into new containers
  2643. HRESULT CHistFolder::_CopyEntries(LPCTSTR pszHistPrefix)
  2644. {
  2645. HANDLE hEnum = NULL;
  2646. HRESULT hr;
  2647. BOOL fNotCopied = FALSE;
  2648. LPINTERNET_CACHE_ENTRY_INFO pceiWorking;
  2649. DWORD dwBuffSize;
  2650. LPTSTR pszSearchPattern = NULL;
  2651. TCHAR szHistSearchPattern[65]; // search pattern for history items
  2652. StrCpyN(szHistSearchPattern, pszHistPrefix, ARRAYSIZE(szHistSearchPattern));
  2653. // We can't pass in the whole search pattern that we want,
  2654. // because FindFirstUrlCacheEntry is busted. It will only look at the
  2655. // prefix if there is a cache container for that prefix. So, we can
  2656. // pass in "Visited: " and enumerate all the history items in the cache,
  2657. // but then we need to pull out only the ones with the correct username.
  2658. // StrCpy(szHistSearchPattern, szUserName);
  2659. pszSearchPattern = szHistSearchPattern;
  2660. pceiWorking = (LPINTERNET_CACHE_ENTRY_INFO)LocalAlloc(LPTR, MAX_URLCACHE_ENTRY);
  2661. if (NULL == pceiWorking)
  2662. {
  2663. hr = E_OUTOFMEMORY;
  2664. goto exitPoint;
  2665. }
  2666. hr = _ValidateIntervalCache();
  2667. if (FAILED(hr))
  2668. goto exitPoint;
  2669. while (SUCCEEDED(hr))
  2670. {
  2671. dwBuffSize = MAX_URLCACHE_ENTRY;
  2672. if (!hEnum)
  2673. {
  2674. hEnum = FindFirstUrlCacheEntry(pszSearchPattern, pceiWorking, &dwBuffSize);
  2675. if (!hEnum)
  2676. {
  2677. goto exitPoint;
  2678. }
  2679. }
  2680. else if (!FindNextUrlCacheEntry(hEnum, pceiWorking, &dwBuffSize))
  2681. {
  2682. // chrisfra 4/3/97 should we distinquish eod vs hard errors?
  2683. // old code for cachevu doesn't (see above in enum code)
  2684. hr = S_OK;
  2685. goto exitPoint;
  2686. }
  2687. if (SUCCEEDED(hr) &&
  2688. ((pceiWorking->CacheEntryType & URLHISTORY_CACHE_ENTRY) == URLHISTORY_CACHE_ENTRY) &&
  2689. _FilterPrefix(pceiWorking, (LPTSTR) pszHistPrefix))
  2690. {
  2691. hr = _WriteHistory(pceiWorking->lpszSourceUrlName,
  2692. pceiWorking->ExpireTime,
  2693. pceiWorking->LastModifiedTime,
  2694. FALSE,
  2695. NULL);
  2696. if (S_FALSE == hr) fNotCopied = TRUE;
  2697. }
  2698. }
  2699. exitPoint:
  2700. if (pceiWorking)
  2701. {
  2702. LocalFree(pceiWorking);
  2703. pceiWorking = NULL;
  2704. }
  2705. if (hEnum)
  2706. {
  2707. FindCloseUrlCache(hEnum);
  2708. }
  2709. return SUCCEEDED(hr) ? (fNotCopied ? S_FALSE : S_OK) : hr;
  2710. }
  2711. HRESULT CHistFolder::_GetUserName(LPTSTR pszUserName, DWORD cchUserName)
  2712. {
  2713. HRESULT hr = _EnsureHistStg();
  2714. if (SUCCEEDED(hr))
  2715. {
  2716. hr = _pUrlHistStg->GetUserName(pszUserName, cchUserName);
  2717. }
  2718. return hr;
  2719. }
  2720. // Makes best efforts attempt to delete old history items in container on a per
  2721. // user basis. if we get rid of per user - can just empty whole container
  2722. HRESULT CHistFolder::_DeleteEntries(LPCTSTR pszHistPrefix, PFNDELETECALLBACK pfnDeleteFilter, void * pDelData)
  2723. {
  2724. HANDLE hEnum = NULL;
  2725. HRESULT hr = S_OK;
  2726. BOOL fNotDeleted = FALSE;
  2727. LPINTERNET_CACHE_ENTRY_INFO pceiWorking;
  2728. DWORD dwBuffSize;
  2729. LPTSTR pszSearchPattern = NULL;
  2730. TCHAR szUserName[INTERNET_MAX_USER_NAME_LENGTH + 1]; // username of person logged on
  2731. DWORD dwUserNameLen = INTERNET_MAX_USER_NAME_LENGTH + 1; // len of this buffer
  2732. TCHAR szHistSearchPattern[PREFIX_SIZE+1]; // search pattern for history items
  2733. LPITEMIDLIST pidlNotify;
  2734. StrCpyN(szHistSearchPattern, pszHistPrefix, ARRAYSIZE(szHistSearchPattern));
  2735. if (FAILED(_GetUserName(szUserName, dwUserNameLen)))
  2736. szUserName[0] = TEXT('\0');
  2737. // We can't pass in the whole search pattern that we want,
  2738. // because FindFirstUrlCacheEntry is busted. It will only look at the
  2739. // prefix if there is a cache container for that prefix. So, we can
  2740. // pass in "Visited: " and enumerate all the history items in the cache,
  2741. // but then we need to pull out only the ones with the correct username.
  2742. // StrCpy(szHistSearchPattern, szUserName);
  2743. pszSearchPattern = szHistSearchPattern;
  2744. pceiWorking = (LPINTERNET_CACHE_ENTRY_INFO)LocalAlloc(LPTR, MAX_URLCACHE_ENTRY);
  2745. if (NULL == pceiWorking)
  2746. {
  2747. hr = E_OUTOFMEMORY;
  2748. goto exitPoint;
  2749. }
  2750. while (SUCCEEDED(hr))
  2751. {
  2752. dwBuffSize = MAX_URLCACHE_ENTRY;
  2753. if (!hEnum)
  2754. {
  2755. hEnum = FindFirstUrlCacheEntry(pszSearchPattern, pceiWorking, &dwBuffSize);
  2756. if (!hEnum)
  2757. {
  2758. goto exitPoint;
  2759. }
  2760. }
  2761. else if (!FindNextUrlCacheEntry(hEnum, pceiWorking, &dwBuffSize))
  2762. {
  2763. // chrisfra 4/3/97 should we distinquish eod vs hard errors?
  2764. // old code for cachevu doesn't (see above in enum code)
  2765. hr = S_OK;
  2766. goto exitPoint;
  2767. }
  2768. pidlNotify = NULL;
  2769. if (SUCCEEDED(hr) &&
  2770. ((pceiWorking->CacheEntryType & URLHISTORY_CACHE_ENTRY) == URLHISTORY_CACHE_ENTRY) &&
  2771. _FilterUserName(pceiWorking, pszHistPrefix, szUserName) &&
  2772. (NULL == pfnDeleteFilter || pfnDeleteFilter(pceiWorking, pDelData, &pidlNotify)))
  2773. {
  2774. //if (!DeleteUrlCacheEntryA(pceiWorking->lpszSourceUrlName))
  2775. if (FAILED(_DeleteUrlFromBucket(pceiWorking->lpszSourceUrlName)))
  2776. {
  2777. fNotDeleted = TRUE;
  2778. }
  2779. else if (pidlNotify)
  2780. {
  2781. SHChangeNotify(SHCNE_DELETE, SHCNF_IDLIST, pidlNotify, NULL);
  2782. }
  2783. }
  2784. ILFree(pidlNotify);
  2785. }
  2786. exitPoint:
  2787. if (pceiWorking)
  2788. {
  2789. LocalFree(pceiWorking);
  2790. pceiWorking = NULL;
  2791. }
  2792. if (hEnum)
  2793. {
  2794. FindCloseUrlCache(hEnum);
  2795. }
  2796. return SUCCEEDED(hr) ? (fNotDeleted ? S_FALSE : S_OK) : hr;
  2797. }
  2798. HRESULT CHistFolder::_DeleteInterval(HSFINTERVAL *pInterval)
  2799. {
  2800. UINT err = S_OK;
  2801. TCHAR szInterval[INTERVAL_SIZE+1];
  2802. CHAR szAnsiInterval[INTERVAL_SIZE+1];
  2803. _KeyForInterval(pInterval, szInterval, ARRAYSIZE(szInterval));
  2804. SHTCharToAnsi(szInterval, szAnsiInterval, ARRAYSIZE(szAnsiInterval));
  2805. if (!DeleteUrlCacheContainerA(szAnsiInterval, 0))
  2806. {
  2807. err = GetLastError();
  2808. }
  2809. else
  2810. {
  2811. _NotifyInterval(pInterval, SHCNE_RMDIR);
  2812. }
  2813. return S_OK == err ? S_OK : HRESULT_FROM_WIN32(err);
  2814. }
  2815. // Returns S_OK if no intervals we're deleted, S_FALSE if at least
  2816. // one interval was deleted.
  2817. HRESULT CHistFolder::_CleanUpHistory(FILETIME ftLimit, FILETIME ftTommorrow)
  2818. {
  2819. HRESULT hr;
  2820. BOOL fChangedRegistry = FALSE;
  2821. int i;
  2822. // _CleanUpHistory does two things:
  2823. //
  2824. // If we have any stale weeks destroy them and flag the change
  2825. //
  2826. // If we have any days that should be in cache but not in dailies
  2827. // copy them to the relevant week then destroy those days
  2828. // and flag the change
  2829. hr = _LoadIntervalCache();
  2830. if (FAILED(hr))
  2831. goto exitPoint;
  2832. for (i = 0; i < _cbIntervals; i++)
  2833. {
  2834. // Delete old intervals or ones which start at a day in the future
  2835. // (due to fooling with the clock)
  2836. if (CompareFileTime(&_pIntervalCache[i].ftEnd, &ftLimit) < 0 ||
  2837. CompareFileTime(&_pIntervalCache[i].ftStart, &ftTommorrow) >= 0)
  2838. {
  2839. fChangedRegistry = TRUE;
  2840. hr = _DeleteInterval(&_pIntervalCache[i]);
  2841. if (FAILED(hr))
  2842. goto exitPoint;
  2843. }
  2844. else if (1 == _DaysInInterval(&_pIntervalCache[i]))
  2845. {
  2846. HSFINTERVAL *pWeek;
  2847. // NOTE: at this point we have guaranteed, we've built weeks
  2848. // for all days outside of current week
  2849. if (S_OK == _GetInterval(&_pIntervalCache[i].ftStart, TRUE, &pWeek))
  2850. {
  2851. fChangedRegistry = TRUE;
  2852. hr = _CopyEntries(_pIntervalCache[i].szPrefix);
  2853. if (FAILED(hr))
  2854. goto exitPoint;
  2855. _NotifyInterval(pWeek, SHCNE_UPDATEDIR);
  2856. hr = _DeleteInterval(&_pIntervalCache[i]);
  2857. if (FAILED(hr))
  2858. goto exitPoint;
  2859. }
  2860. }
  2861. }
  2862. exitPoint:
  2863. if (S_OK == hr && fChangedRegistry) hr = S_FALSE;
  2864. return hr;
  2865. }
  2866. typedef struct _HSFDELETEDATA
  2867. {
  2868. UINT cidl;
  2869. LPCITEMIDLIST *ppidl;
  2870. LPCITEMIDLIST pidlParent;
  2871. } HSFDELETEDATA,*LPHSFDELETEDATA;
  2872. // delete if matches any host on list
  2873. BOOL fDeleteInHostList(LPINTERNET_CACHE_ENTRY_INFO pceiWorking, void * pDelData, LPITEMIDLIST *ppidlNotify)
  2874. {
  2875. LPHSFDELETEDATA phsfd = (LPHSFDELETEDATA)pDelData;
  2876. TCHAR szHost[INTERNET_MAX_HOST_NAME_LENGTH+1];
  2877. TCHAR szLocalHost[INTERNET_MAX_HOST_NAME_LENGTH+1];
  2878. UINT i;
  2879. _GetLocalHost(szLocalHost, SIZECHARS(szLocalHost));
  2880. _GetURLHost(pceiWorking, szHost, INTERNET_MAX_HOST_NAME_LENGTH, szLocalHost);
  2881. for (i = 0; i < phsfd->cidl; i++)
  2882. {
  2883. if (!ualstrcmpi(szHost, _GetURLTitle((LPBASEPIDL)(phsfd->ppidl[i]))))
  2884. {
  2885. return TRUE;
  2886. }
  2887. }
  2888. return FALSE;
  2889. }
  2890. // Will attempt to hunt down all occurrances of this url in any of the
  2891. // various history buckets...
  2892. // This is a utility function for _ViewType_DeleteItems -- it may
  2893. // be used in other contexts providing these preconditions
  2894. // are kept in mind:
  2895. //
  2896. // *The URL passed in should be prefixed ONLY with the username portion
  2897. // such that this function can prepend prefixes to these urls
  2898. // *WARNING: This function ASSUMES that _ValidateIntervalCache
  2899. // has been called recently!!!! DANGER DANGER!
  2900. //
  2901. // RETURNS: S_OK if at least one entry was found and deleted
  2902. //
  2903. HRESULT CHistFolder::_DeleteUrlHistoryGlobal(LPCTSTR pszUrl) {
  2904. HRESULT hr = E_FAIL;
  2905. if (pszUrl) {
  2906. IUrlHistoryPriv *pUrlHistStg = _GetHistStg();
  2907. if (pUrlHistStg) {
  2908. LPCTSTR pszStrippedUrl = _StripHistoryUrlToUrl(pszUrl);
  2909. if (pszStrippedUrl)
  2910. {
  2911. UINT cchwTempUrl = lstrlen(pszStrippedUrl) + 1;
  2912. LPWSTR pwszTempUrl = ((LPWSTR)LocalAlloc(LPTR, cchwTempUrl * sizeof(WCHAR)));
  2913. if (pwszTempUrl)
  2914. {
  2915. SHTCharToUnicode(pszStrippedUrl, pwszTempUrl, cchwTempUrl);
  2916. hr = pUrlHistStg->DeleteUrl(pwszTempUrl, URLFLAG_DONT_DELETE_SUBSCRIBED);
  2917. for (int i = 0; i < _cbIntervals; ++i) {
  2918. // should this length be constant? (bucket sizes shouldn't vary)
  2919. UINT cchTempUrl = (PREFIX_SIZE +
  2920. lstrlen(pszUrl) + 1);
  2921. LPTSTR pszTempUrl = ((LPTSTR)LocalAlloc(LPTR, cchTempUrl * sizeof(TCHAR)));
  2922. if (pszTempUrl) {
  2923. // StrCpy null terminates
  2924. StrCpyN(pszTempUrl, _pIntervalCache[i].szPrefix, cchTempUrl);
  2925. StrCpyN(pszTempUrl + PREFIX_SIZE, pszUrl, cchTempUrl - PREFIX_SIZE);
  2926. if (DeleteUrlCacheEntry(pszTempUrl))
  2927. hr = S_OK;
  2928. LocalFree(pszTempUrl);
  2929. pszTempUrl = NULL;
  2930. }
  2931. else {
  2932. hr = E_OUTOFMEMORY;
  2933. break;
  2934. }
  2935. }
  2936. LocalFree(pwszTempUrl);
  2937. pwszTempUrl = NULL;
  2938. }
  2939. else {
  2940. hr = E_OUTOFMEMORY;
  2941. }
  2942. }
  2943. pUrlHistStg->Release();
  2944. }
  2945. }
  2946. else
  2947. hr = E_INVALIDARG;
  2948. return hr;
  2949. }
  2950. // WARNING: assumes ppidl
  2951. HRESULT CHistFolder::_ViewBySite_DeleteItems(LPCITEMIDLIST *ppidl, UINT cidl)
  2952. {
  2953. HRESULT hr = E_INVALIDARG;
  2954. TCHAR szUserName[INTERNET_MAX_USER_NAME_LENGTH + 1];
  2955. if (FAILED(_GetUserName(szUserName, ARRAYSIZE(szUserName))))
  2956. szUserName[0] = TEXT('\0');
  2957. IUrlHistoryPriv *pUrlHistStg = _GetHistStg();
  2958. if (pUrlHistStg)
  2959. {
  2960. IEnumSTATURL *penum;
  2961. if (SUCCEEDED(pUrlHistStg->EnumUrls(&penum)) &&
  2962. penum) {
  2963. for (UINT i = 0; i < cidl; ++i)
  2964. {
  2965. LPCUTSTR pszHostName = _GetURLTitle((LPBASEPIDL)ppidl[i]);
  2966. UINT uUserNameLen = lstrlen(szUserName);
  2967. UINT uBuffLen = (USHORT)((HOSTPREFIXLEN + uUserNameLen +
  2968. ualstrlen(pszHostName) + 2)); // insert '@' and '\0'
  2969. LPTSTR pszUrl =
  2970. ((LPTSTR)LocalAlloc(LPTR, (uBuffLen) * sizeof(TCHAR)));
  2971. if (pszUrl) {
  2972. // get rid of ":Host: " prefixed entires in the cache
  2973. // Generates "username@:Host: hostname" -- wnsprintf null terminates
  2974. wnsprintf(pszUrl, uBuffLen, TEXT("%s@%s%s"), szUserName,
  2975. c_szHostPrefix, pszHostName);
  2976. hr = _DeleteUrlHistoryGlobal(pszUrl);
  2977. // enumerate over all urls in history
  2978. ULONG cFetched;
  2979. // don't retrieve TITLE information (too much overhead)
  2980. penum->SetFilter(NULL, STATURL_QUERYFLAG_NOTITLE);
  2981. STATURL statUrl;
  2982. statUrl.cbSize = sizeof(STATURL);
  2983. while(SUCCEEDED(penum->Next(1, &statUrl, &cFetched)) && cFetched) {
  2984. if (statUrl.pwcsUrl) {
  2985. // these next few lines painfully constructs a string
  2986. // that is of the form "username@url"
  2987. LPTSTR pszStatUrlUrl;
  2988. UINT uStatUrlUrlLen = lstrlenW(statUrl.pwcsUrl);
  2989. pszStatUrlUrl = statUrl.pwcsUrl;
  2990. TCHAR szHost[INTERNET_MAX_HOST_NAME_LENGTH + 1];
  2991. _GetURLHostFromUrl_NoStrip(pszStatUrlUrl, szHost, INTERNET_MAX_HOST_NAME_LENGTH + 1, _GetLocalHost());
  2992. if (!ualstrcmpi(szHost, pszHostName)) {
  2993. LPTSTR pszDelUrl; // url to be deleted
  2994. UINT uUrlLen = uUserNameLen + 1 + uStatUrlUrlLen; // +1 for '@'
  2995. pszDelUrl = ((LPTSTR)LocalAlloc(LPTR, (uUrlLen + 1) * sizeof(TCHAR)));
  2996. if (pszDelUrl) {
  2997. wnsprintf(pszDelUrl, uUrlLen + 1, TEXT("%s@%s"), szUserName, pszStatUrlUrl);
  2998. // finally, delete all all occurrances of that URL in all history buckets
  2999. hr = _DeleteUrlHistoryGlobal(pszDelUrl);
  3000. //
  3001. // Is is really safe to delete *during* an enumeration like this, or should
  3002. // we cache all of the URLS and delete at the end? I'd rather do it this
  3003. // way if possible -- anyhoo, no docs say its bad to do -- 'course there are no docs ;)
  3004. // Also, there is an example of code later that deletes during an enumeration
  3005. // and seems to work...
  3006. LocalFree(pszDelUrl);
  3007. pszDelUrl = NULL;
  3008. }
  3009. else
  3010. hr = E_OUTOFMEMORY;
  3011. }
  3012. OleFree(statUrl.pwcsUrl);
  3013. }
  3014. }
  3015. penum->Reset();
  3016. LocalFree(pszUrl);
  3017. pszUrl = NULL;
  3018. }
  3019. else
  3020. hr = E_OUTOFMEMORY;
  3021. LPITEMIDLIST pidlTemp = ILCombine(_pidl, ppidl[i]);
  3022. if (pidlTemp) {
  3023. SHChangeNotify(SHCNE_RMDIR, SHCNF_IDLIST, pidlTemp, NULL);
  3024. ILFree(pidlTemp);
  3025. }
  3026. else
  3027. hr = E_OUTOFMEMORY;
  3028. if (hr == E_OUTOFMEMORY)
  3029. break;
  3030. } // for
  3031. penum->Release();
  3032. } // if penum
  3033. else
  3034. hr = E_FAIL;
  3035. pUrlHistStg->Release();
  3036. } // if purlHistStg
  3037. else
  3038. hr = E_FAIL;
  3039. return hr;
  3040. }
  3041. // This guy will delete an URL from one history (MSHIST-type) bucket
  3042. // and then try to find it in other (MSHIST-type) buckets.
  3043. // If it can't be found, then the URL will be removed from the main
  3044. // history (Visited-type) bucket.
  3045. // NOTE: Only the url will be deleted and not any of its "frame-children"
  3046. // This is probably not the a great thing...
  3047. // ASSUMES that _ValidateIntervalCache has been called recently
  3048. HRESULT CHistFolder::_DeleteUrlFromBucket(LPCTSTR pszPrefixedUrl) {
  3049. HRESULT hr = E_FAIL;
  3050. if (DeleteUrlCacheEntry(pszPrefixedUrl)) {
  3051. // check if we need to delete this url from the main Visited container, too
  3052. // we make sure that url exists in at least one other bucket
  3053. LPCTSTR pszUrl = _StripHistoryUrlToUrl(pszPrefixedUrl);
  3054. if (pszUrl)
  3055. {
  3056. DWORD dwError = _SearchFlatCacheForUrl(pszUrl, NULL, NULL);
  3057. if (dwError == ERROR_FILE_NOT_FOUND)
  3058. {
  3059. IUrlHistoryPriv *pUrlHistStg = _GetHistStg();
  3060. if (pUrlHistStg)
  3061. {
  3062. pUrlHistStg->DeleteUrl(pszUrl, 0);
  3063. pUrlHistStg->Release();
  3064. hr = S_OK;
  3065. }
  3066. }
  3067. else
  3068. hr = S_OK;
  3069. }
  3070. }
  3071. return hr;
  3072. }
  3073. // Tries to delete as many as possible, and returns E_FAIL if the last one could not
  3074. // be deleted.
  3075. // <RATIONALIZATION>not usually called with more than one pidl</RATIONALIZATION>
  3076. // ASSUMES that _ValidateIntervalCache has been called recently
  3077. HRESULT CHistFolder::_ViewType_DeleteItems(LPCITEMIDLIST *ppidl, UINT cidl)
  3078. {
  3079. ASSERT(_uViewType);
  3080. HRESULT hr = E_INVALIDARG;
  3081. if (ppidl) {
  3082. switch(_uViewType) {
  3083. case VIEWPIDL_ORDER_SITE:
  3084. if (_uViewDepth == 0) {
  3085. hr = _ViewBySite_DeleteItems(ppidl, cidl);
  3086. break;
  3087. }
  3088. ASSERT(_uViewDepth == 1);
  3089. // FALLTHROUGH INTENTIONAL!!
  3090. case VIEWPIDL_SEARCH:
  3091. case VIEWPIDL_ORDER_FREQ: {
  3092. for (UINT i = 0; i < cidl; ++i) {
  3093. LPCTSTR pszPrefixedUrl = HPidlToSourceUrl(ppidl[i]);
  3094. if (pszPrefixedUrl) {
  3095. if (SUCCEEDED((hr =
  3096. _DeleteUrlHistoryGlobal(_StripContainerUrlUrl(pszPrefixedUrl)))))
  3097. {
  3098. LPITEMIDLIST pidlTemp = ILCombine(_pidl, ppidl[i]);
  3099. if (pidlTemp) {
  3100. SHChangeNotify(SHCNE_DELETE, SHCNF_IDLIST, pidlTemp, NULL);
  3101. ILFree(pidlTemp);
  3102. }
  3103. else
  3104. hr = E_OUTOFMEMORY;
  3105. }
  3106. }
  3107. else
  3108. hr = E_FAIL;
  3109. }
  3110. break;
  3111. }
  3112. case VIEWPIDL_ORDER_TODAY: {
  3113. // find the entry in the cache and delete it:
  3114. for (UINT i = 0; i < cidl; ++i)
  3115. {
  3116. if (_IsValid_HEIPIDL(ppidl[i]))
  3117. {
  3118. hr = _DeleteUrlFromBucket(HPidlToSourceUrl(ppidl[i]));
  3119. if (SUCCEEDED(hr))
  3120. {
  3121. LPITEMIDLIST pidlTemp = ILCombine(_pidl, ppidl[i]);
  3122. if (pidlTemp)
  3123. {
  3124. SHChangeNotify(SHCNE_DELETE, SHCNF_IDLIST, pidlTemp, NULL);
  3125. ILFree(pidlTemp);
  3126. }
  3127. else
  3128. hr = E_OUTOFMEMORY;
  3129. }
  3130. }
  3131. else
  3132. hr = E_FAIL;
  3133. }
  3134. break;
  3135. }
  3136. default:
  3137. hr = E_NOTIMPL;
  3138. ASSERT(0);
  3139. break;
  3140. }
  3141. }
  3142. return hr;
  3143. }
  3144. HRESULT CHistFolder::_DeleteItems(LPCITEMIDLIST *ppidl, UINT cidl)
  3145. {
  3146. UINT i;
  3147. HSFDELETEDATA hsfDeleteData = {cidl, ppidl, _pidl};
  3148. HSFINTERVAL *pDelInterval;
  3149. FILETIME ftStart;
  3150. FILETIME ftEnd;
  3151. LPCUTSTR pszIntervalName;
  3152. HRESULT hr = _ValidateIntervalCache();
  3153. if (FAILED(hr))
  3154. goto exitPoint;
  3155. if (_uViewType)
  3156. {
  3157. hr = _ViewType_DeleteItems(ppidl, cidl);
  3158. goto exitPoint; // when in rome...
  3159. }
  3160. switch(_foldertype)
  3161. {
  3162. case FOLDER_TYPE_Hist:
  3163. for (i = 0; i < cidl; i++)
  3164. {
  3165. pszIntervalName = _GetURLTitle((LPBASEPIDL)ppidl[i]);
  3166. hr = _ValueToInterval(pszIntervalName, &ftStart, &ftEnd);
  3167. if (FAILED(hr))
  3168. goto exitPoint;
  3169. if (S_OK == _GetInterval(&ftStart, FALSE, &pDelInterval))
  3170. {
  3171. hr = _DeleteInterval(pDelInterval);
  3172. if (FAILED(hr))
  3173. goto exitPoint;
  3174. }
  3175. }
  3176. break;
  3177. case FOLDER_TYPE_HistInterval:
  3178. // last id of of _pidl is name of interval, which implies start and end
  3179. pszIntervalName = _GetURLTitle((LPBASEPIDL)ILFindLastID(_pidl));
  3180. hr = _ValueToInterval(pszIntervalName, &ftStart, &ftEnd);
  3181. if (FAILED(hr))
  3182. goto exitPoint;
  3183. if (S_OK == _GetInterval(&ftStart, FALSE, &pDelInterval))
  3184. {
  3185. // It's important to delete the host: <HOSTNAME> url's first so that
  3186. // an interleaved _NotityWrite() will not leave us inserting a pidl
  3187. // but the the host: directory. it is a conscious performance tradeoff
  3188. // we're making here to not MUTEX this operation (rare) with _NotifyWrite
  3189. for (i = 0; i < cidl; i++)
  3190. {
  3191. LPCTSTR pszHost;
  3192. LPITEMIDLIST pidlTemp;
  3193. TCHAR szNewPrefixedUrl[INTERNET_MAX_URL_LENGTH+1];
  3194. TCHAR szUrlMinusContainer[INTERNET_MAX_URL_LENGTH+1];
  3195. ua_GetURLTitle( &pszHost, (LPBASEPIDL)ppidl[i] );
  3196. DWORD cbHost = lstrlen(pszHost);
  3197. // Compose the prefixed URL for the host cache entry, then
  3198. // use it to delete host entry
  3199. hr = _GetUserName(szUrlMinusContainer, ARRAYSIZE(szUrlMinusContainer));
  3200. if (FAILED(hr))
  3201. goto exitPoint;
  3202. DWORD cbUserName = lstrlen(szUrlMinusContainer);
  3203. if ((cbHost + cbUserName + 1)*sizeof(TCHAR) + HOSTPREFIXLEN > INTERNET_MAX_URL_LENGTH)
  3204. {
  3205. hr = E_FAIL;
  3206. goto exitPoint;
  3207. }
  3208. StrCatBuff(szUrlMinusContainer, TEXT("@"), ARRAYSIZE(szUrlMinusContainer));
  3209. StrCatBuff(szUrlMinusContainer, c_szHostPrefix, ARRAYSIZE(szUrlMinusContainer));
  3210. StrCatBuff(szUrlMinusContainer, pszHost, ARRAYSIZE(szUrlMinusContainer));
  3211. hr = _PrefixUrl(szUrlMinusContainer,
  3212. &ftStart,
  3213. szNewPrefixedUrl,
  3214. ARRAYSIZE(szNewPrefixedUrl));
  3215. if (FAILED(hr))
  3216. goto exitPoint;
  3217. if (!DeleteUrlCacheEntry(szNewPrefixedUrl))
  3218. {
  3219. hr = E_FAIL;
  3220. goto exitPoint;
  3221. }
  3222. pidlTemp = _HostPidl(pszHost, pDelInterval);
  3223. if (pidlTemp == NULL)
  3224. {
  3225. hr = E_OUTOFMEMORY;
  3226. goto exitPoint;
  3227. }
  3228. SHChangeNotify(SHCNE_RMDIR, SHCNF_IDLIST, pidlTemp, NULL);
  3229. ILFree(pidlTemp);
  3230. }
  3231. hr = _DeleteEntries(_pszCachePrefix , fDeleteInHostList, &hsfDeleteData);
  3232. }
  3233. break;
  3234. case FOLDER_TYPE_HistDomain:
  3235. for (i = 0; i < cidl; ++i)
  3236. {
  3237. if (_IsValid_HEIPIDL(ppidl[i]))
  3238. {
  3239. hr = _DeleteUrlFromBucket(HPidlToSourceUrl(ppidl[i]));
  3240. if (SUCCEEDED(hr))
  3241. {
  3242. LPITEMIDLIST pidlTemp = ILCombine(_pidl, ppidl[i]);
  3243. if (pidlTemp)
  3244. {
  3245. SHChangeNotify(SHCNE_DELETE, SHCNF_IDLIST, pidlTemp, NULL);
  3246. ILFree(pidlTemp);
  3247. }
  3248. }
  3249. }
  3250. else
  3251. hr = E_FAIL;
  3252. }
  3253. break;
  3254. }
  3255. exitPoint:
  3256. if (SUCCEEDED(hr))
  3257. SHChangeNotifyHandleEvents();
  3258. return hr;
  3259. }
  3260. IUrlHistoryPriv *CHistFolder::_GetHistStg()
  3261. {
  3262. _EnsureHistStg();
  3263. if (_pUrlHistStg)
  3264. {
  3265. _pUrlHistStg->AddRef();
  3266. }
  3267. return _pUrlHistStg;
  3268. }
  3269. HRESULT CHistFolder::_EnsureHistStg()
  3270. {
  3271. HRESULT hr = S_OK;
  3272. if (_pUrlHistStg == NULL)
  3273. {
  3274. hr = CoCreateInstance(CLSID_CUrlHistory, NULL, CLSCTX_INPROC_SERVER, IID_IUrlHistoryPriv, (void **)&_pUrlHistStg);
  3275. }
  3276. return hr;
  3277. }
  3278. HRESULT CHistFolder::_ValidateIntervalCache()
  3279. {
  3280. HRESULT hr = S_OK;
  3281. SYSTEMTIME stNow;
  3282. SYSTEMTIME stThen;
  3283. FILETIME ftNow;
  3284. FILETIME ftTommorrow;
  3285. FILETIME ftMonday;
  3286. FILETIME ftDayOfWeek;
  3287. FILETIME ftLimit;
  3288. BOOL fChangedRegistry = FALSE;
  3289. DWORD dwWaitResult = WAIT_TIMEOUT;
  3290. HSFINTERVAL *pWeirdWeek;
  3291. HSFINTERVAL *pPrevDay;
  3292. long compareResult;
  3293. BOOL fCleanupVisitedDB = FALSE;
  3294. int i;
  3295. int daysToKeep;
  3296. // Check for reentrancy
  3297. if (_fValidatingCache) return S_OK;
  3298. _fValidatingCache = TRUE;
  3299. // IE6 RAID 2031
  3300. // Is this mutex necessary?
  3301. // In IE4 days, this mutex was named _!MSFTHISTORY!_, the same as that in wininet.
  3302. // As a consequence, sometimes you got into one-minute timeouts that caused the entire
  3303. // browser to hang. (Since one thread could be cleaning up the history while another thread is
  3304. // trying to access the cache for non-history purposes.)
  3305. // I've changed the name of the mutex to prevent shdocvw from locking wininet, but we need
  3306. // to understand exactly what purpose this mutex serves, and if none, remove it.
  3307. if (g_hMutexHistory == NULL)
  3308. {
  3309. ENTERCRITICAL;
  3310. if (g_hMutexHistory == NULL)
  3311. {
  3312. //
  3313. // Use the "A" version for W95 compatability.
  3314. //
  3315. g_hMutexHistory = OpenMutexA(SYNCHRONIZE, FALSE, "_!SHMSFTHISTORY!_");
  3316. if (g_hMutexHistory == NULL && (GetLastError() == ERROR_FILE_NOT_FOUND
  3317. || GetLastError() == ERROR_INVALID_NAME))
  3318. {
  3319. g_hMutexHistory = CreateMutexA(CreateAllAccessSecurityAttributes(NULL, NULL, NULL), FALSE, "_!SHMSFTHISTORY!_");
  3320. }
  3321. }
  3322. LEAVECRITICAL;
  3323. }
  3324. // Note that if multiple processes are trying to clean up the history, we're still going to
  3325. // hang the other processes for a minute. Oops.
  3326. if (g_hMutexHistory)
  3327. dwWaitResult = WaitForSingleObject(g_hMutexHistory, FAILSAFE_TIMEOUT);
  3328. if ((dwWaitResult!=WAIT_OBJECT_0) && (dwWaitResult!=WAIT_ABANDONED))
  3329. {
  3330. ASSERT(FALSE);
  3331. goto exitPoint;
  3332. }
  3333. hr = _LoadIntervalCache();
  3334. if (FAILED(hr))
  3335. goto exitPoint;
  3336. // All history is maintained using "User Perceived Time", which is the
  3337. // local time when navigate was made.
  3338. GetLocalTime(&stNow);
  3339. SystemTimeToFileTime(&stNow, &ftNow);
  3340. _FileTimeDeltaDays(&ftNow, &ftNow, 0);
  3341. _FileTimeDeltaDays(&ftNow, &ftTommorrow, 1);
  3342. hr = _EnsureHistStg();
  3343. if (FAILED(hr))
  3344. goto exitPoint;
  3345. // Compute ftLimit as first instant of first day to keep in history
  3346. // _FileTimeDeltaDays truncates to first FILETIME incr of day before computing
  3347. // earlier/later, day.
  3348. daysToKeep = (int)_pUrlHistStg->GetDaysToKeep();
  3349. if (daysToKeep < 0) daysToKeep = 0;
  3350. _FileTimeDeltaDays(&ftNow, &ftLimit, 1-daysToKeep);
  3351. FileTimeToSystemTime(&ftNow, &stThen);
  3352. // We take monday as day 0 of week, and adjust it for file time
  3353. // tics per day (100ns per tick
  3354. _FileTimeDeltaDays(&ftNow, &ftMonday, stThen.wDayOfWeek ? 1-stThen.wDayOfWeek: -6);
  3355. // Delete old version intervals so prefix matching in wininet isn't hosed
  3356. for (i = 0; i < _cbIntervals; i++)
  3357. {
  3358. if (_pIntervalCache[i].usVers < OUR_VERS)
  3359. {
  3360. fChangedRegistry = TRUE;
  3361. hr = _DeleteInterval(&_pIntervalCache[i]);
  3362. if (FAILED(hr))
  3363. goto exitPoint;
  3364. }
  3365. }
  3366. // If someone set their clock forward and then back, we could have
  3367. // a week that shouldn't be there. delete it. they will lose that week
  3368. // of history, c'est la guerre! quel domage!
  3369. if (S_OK == _GetInterval(&ftMonday, TRUE, &pWeirdWeek))
  3370. {
  3371. hr = _DeleteInterval(pWeirdWeek);
  3372. fCleanupVisitedDB = TRUE;
  3373. if (FAILED(hr))
  3374. goto exitPoint;
  3375. fChangedRegistry = TRUE;
  3376. }
  3377. // Create weeks as needed to house days that are within "days to keep" limit
  3378. // but are not in the same week at today
  3379. for (i = 0; i < _cbIntervals; i++)
  3380. {
  3381. FILETIME ftThisDay = _pIntervalCache[i].ftStart;
  3382. if (_pIntervalCache[i].usVers >= OUR_VERS &&
  3383. 1 == _DaysInInterval(&_pIntervalCache[i]) &&
  3384. CompareFileTime(&ftThisDay, &ftLimit) >= 0 &&
  3385. CompareFileTime(&ftThisDay, &ftMonday) < 0)
  3386. {
  3387. if (S_OK != _GetInterval(&ftThisDay, TRUE, NULL))
  3388. {
  3389. int j;
  3390. BOOL fProcessed = FALSE;
  3391. FILETIME ftThisMonday;
  3392. FILETIME ftNextMonday;
  3393. FileTimeToSystemTime(&ftThisDay, &stThen);
  3394. // We take monday as day 0 of week, and adjust it for file time
  3395. // tics per day (100ns per tick
  3396. _FileTimeDeltaDays(&ftThisDay, &ftThisMonday, stThen.wDayOfWeek ? 1-stThen.wDayOfWeek: -6);
  3397. _FileTimeDeltaDays(&ftThisMonday, &ftNextMonday, 7);
  3398. // Make sure we haven't already done this week
  3399. for (j = 0; j < i; j++)
  3400. {
  3401. if (_pIntervalCache[j].usVers >= OUR_VERS &&
  3402. CompareFileTime(&_pIntervalCache[j].ftStart, &ftLimit) >= 0 &&
  3403. _InInterval(&ftThisMonday,
  3404. &ftNextMonday,
  3405. &_pIntervalCache[j].ftStart))
  3406. {
  3407. fProcessed = TRUE;
  3408. break;
  3409. }
  3410. }
  3411. if (!fProcessed)
  3412. {
  3413. hr = _CreateInterval(&ftThisMonday, 7);
  3414. if (FAILED(hr))
  3415. goto exitPoint;
  3416. fChangedRegistry = TRUE;
  3417. }
  3418. }
  3419. }
  3420. }
  3421. // Guarantee today is created and old TODAY is renamed to Day of Week
  3422. ftDayOfWeek = ftMonday;
  3423. pPrevDay = NULL;
  3424. while ((compareResult = CompareFileTime(&ftDayOfWeek, &ftNow)) <= 0)
  3425. {
  3426. HSFINTERVAL *pFound;
  3427. if (S_OK != _GetInterval(&ftDayOfWeek, FALSE, &pFound))
  3428. {
  3429. if (0 == compareResult)
  3430. {
  3431. if (pPrevDay) // old today's name changes
  3432. {
  3433. _NotifyInterval(pPrevDay, SHCNE_RENAMEFOLDER);
  3434. }
  3435. hr = _CreateInterval(&ftDayOfWeek, 1);
  3436. if (FAILED(hr))
  3437. goto exitPoint;
  3438. fChangedRegistry = TRUE;
  3439. }
  3440. }
  3441. else
  3442. {
  3443. pPrevDay = pFound;
  3444. }
  3445. _FileTimeDeltaDays(&ftDayOfWeek, &ftDayOfWeek, 1);
  3446. }
  3447. // On the first time through, we do not migrate history, wininet
  3448. // changed cache file format so users going to 4.0B2 from 3.0 or B1
  3449. // will lose their history anyway
  3450. // _CleanUpHistory does two things:
  3451. //
  3452. // If we have any stale weeks destroy them and flag the change
  3453. //
  3454. // If we have any days that should be in cache but not in dailies
  3455. // copy them to the relevant week then destroy those days
  3456. // and flag the change
  3457. hr = _CleanUpHistory(ftLimit, ftTommorrow);
  3458. if (S_FALSE == hr)
  3459. {
  3460. hr = S_OK;
  3461. fChangedRegistry = TRUE;
  3462. fCleanupVisitedDB = TRUE;
  3463. }
  3464. if (fChangedRegistry)
  3465. hr = _LoadIntervalCache();
  3466. exitPoint:
  3467. if ((dwWaitResult == WAIT_OBJECT_0)
  3468. || (dwWaitResult == WAIT_ABANDONED))
  3469. ReleaseMutex(g_hMutexHistory);
  3470. if (fCleanupVisitedDB)
  3471. {
  3472. if (SUCCEEDED(_EnsureHistStg()))
  3473. {
  3474. HRESULT hrLocal = _pUrlHistStg->CleanupHistory();
  3475. ASSERT(SUCCEEDED(hrLocal));
  3476. }
  3477. }
  3478. _fValidatingCache = FALSE;
  3479. return hr;
  3480. }
  3481. HRESULT CHistFolder::_CopyTSTRField(LPTSTR *ppszField, LPCTSTR pszValue)
  3482. {
  3483. if (*ppszField)
  3484. {
  3485. LocalFree(*ppszField);
  3486. *ppszField = NULL;
  3487. }
  3488. if (pszValue)
  3489. {
  3490. int cchField = lstrlen(pszValue) + 1;
  3491. *ppszField = (LPTSTR)LocalAlloc(LPTR, cchField * sizeof(TCHAR));
  3492. if (*ppszField)
  3493. {
  3494. StrCpyN(*ppszField, pszValue, cchField);
  3495. }
  3496. else
  3497. {
  3498. return E_OUTOFMEMORY;
  3499. }
  3500. }
  3501. return S_OK;
  3502. }
  3503. //
  3504. // IHistSFPrivate methods...
  3505. //
  3506. HRESULT CHistFolder::SetCachePrefix(LPCTSTR pszCachePrefix)
  3507. {
  3508. return _CopyTSTRField(&_pszCachePrefix, pszCachePrefix);
  3509. }
  3510. HRESULT CHistFolder::SetDomain(LPCTSTR pszDomain)
  3511. {
  3512. return _CopyTSTRField(&_pszDomain, pszDomain);
  3513. }
  3514. //
  3515. // IShellFolder
  3516. //
  3517. HRESULT CHistFolder::ParseDisplayName(HWND hwnd, LPBC pbc,
  3518. LPOLESTR pszDisplayName, ULONG *pchEaten,
  3519. LPITEMIDLIST *ppidl, ULONG *pdwAttributes)
  3520. {
  3521. *ppidl = NULL;
  3522. return E_FAIL;
  3523. }
  3524. HRESULT CHistFolder::EnumObjects(HWND hwnd, DWORD grfFlags,
  3525. IEnumIDList **ppenumIDList)
  3526. {
  3527. return CHistFolderEnum_CreateInstance(grfFlags, this, ppenumIDList);
  3528. }
  3529. HRESULT CHistFolder::_ViewPidl_BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
  3530. {
  3531. HRESULT hr = E_FAIL;
  3532. switch(((LPVIEWPIDL)pidl)->usViewType)
  3533. {
  3534. case VIEWPIDL_SEARCH:
  3535. case VIEWPIDL_ORDER_TODAY:
  3536. case VIEWPIDL_ORDER_SITE:
  3537. case VIEWPIDL_ORDER_FREQ:
  3538. CHistFolder *phsf = new CHistFolder(FOLDER_TYPE_HistDomain);
  3539. if (phsf)
  3540. {
  3541. // initialize?
  3542. phsf->_uViewType = ((LPVIEWPIDL)pidl)->usViewType;
  3543. LPITEMIDLIST pidlLeft = ILCloneFirst(pidl);
  3544. if (pidlLeft)
  3545. {
  3546. hr = S_OK;
  3547. if (((LPVIEWPIDL)pidl)->usViewType == VIEWPIDL_SEARCH)
  3548. {
  3549. // find this search in the global database
  3550. phsf->_pcsCurrentSearch =
  3551. _CurrentSearches::s_FindSearch(((LPSEARCHVIEWPIDL)pidl)->ftSearchKey);
  3552. // search not found -- do not proceed
  3553. if (!phsf->_pcsCurrentSearch)
  3554. hr = E_FAIL;
  3555. }
  3556. if (SUCCEEDED(hr))
  3557. {
  3558. if (phsf->_pidl)
  3559. ILFree(phsf->_pidl);
  3560. phsf->_pidl = ILCombine(_pidl, pidlLeft);
  3561. LPCITEMIDLIST pidlNext = _ILNext(pidl);
  3562. if (pidlNext->mkid.cb)
  3563. {
  3564. CHistFolder *phsf2;
  3565. hr = phsf->BindToObject(pidlNext, pbc, riid, (void **)&phsf2);
  3566. if (SUCCEEDED(hr))
  3567. {
  3568. phsf->Release();
  3569. phsf = phsf2;
  3570. }
  3571. else
  3572. {
  3573. phsf->Release();
  3574. phsf = NULL;
  3575. break;
  3576. }
  3577. }
  3578. hr = phsf->QueryInterface(riid, ppv);
  3579. }
  3580. ILFree(pidlLeft);
  3581. }
  3582. ASSERT(phsf);
  3583. phsf->Release();
  3584. }
  3585. else
  3586. hr = E_OUTOFMEMORY;
  3587. break;
  3588. }
  3589. return hr;
  3590. }
  3591. HRESULT CHistFolder::_ViewType_BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
  3592. {
  3593. HRESULT hr = E_FAIL;
  3594. switch (_uViewType)
  3595. {
  3596. case VIEWPIDL_ORDER_SITE:
  3597. if (_uViewDepth++ < 1)
  3598. {
  3599. LPITEMIDLIST pidlNext = _ILNext(pidl);
  3600. if (!(ILIsEmpty(pidlNext)))
  3601. {
  3602. hr = BindToObject(pidlNext, pbc, riid, ppv);
  3603. }
  3604. else
  3605. {
  3606. *ppv = (void *)this;
  3607. LPITEMIDLIST pidlOld = _pidl;
  3608. if (pidlOld)
  3609. {
  3610. _pidl = ILCombine(_pidl, pidl);
  3611. ILFree(pidlOld);
  3612. }
  3613. else
  3614. {
  3615. _pidl = ILClone(pidl);
  3616. }
  3617. AddRef();
  3618. hr = S_OK;
  3619. }
  3620. }
  3621. break;
  3622. case VIEWPIDL_ORDER_FREQ:
  3623. case VIEWPIDL_ORDER_TODAY:
  3624. case VIEWPIDL_SEARCH:
  3625. hr = E_NOTIMPL;
  3626. break;
  3627. }
  3628. return hr;
  3629. }
  3630. HRESULT CHistFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
  3631. {
  3632. *ppv = NULL;
  3633. BOOL fRealignedPidl;
  3634. HRESULT hr = AlignPidl(&pidl, &fRealignedPidl);
  3635. if (SUCCEEDED(hr))
  3636. {
  3637. if (IS_VALID_VIEWPIDL(pidl))
  3638. {
  3639. hr = _ViewPidl_BindToObject(pidl, pbc, riid, ppv);
  3640. }
  3641. else if (_uViewType)
  3642. {
  3643. hr = _ViewType_BindToObject(pidl, pbc, riid, ppv);
  3644. }
  3645. else
  3646. {
  3647. FOLDER_TYPE ftNew = _foldertype;
  3648. LPCITEMIDLIST pidlNext = pidl;
  3649. while (pidlNext->mkid.cb && SUCCEEDED(hr))
  3650. {
  3651. LPHIDPIDL phid = (LPHIDPIDL)pidlNext;
  3652. switch (ftNew)
  3653. {
  3654. case FOLDER_TYPE_Hist:
  3655. if (phid->usSign != IDIPIDL_SIGN && phid->usSign != IDTPIDL_SIGN)
  3656. hr = E_FAIL;
  3657. else
  3658. ftNew = FOLDER_TYPE_HistInterval;
  3659. break;
  3660. case FOLDER_TYPE_HistDomain:
  3661. if (phid->usSign != HEIPIDL_SIGN)
  3662. hr = E_FAIL;
  3663. break;
  3664. case FOLDER_TYPE_HistInterval:
  3665. if (phid->usSign != IDDPIDL_SIGN)
  3666. hr = E_FAIL;
  3667. else
  3668. ftNew = FOLDER_TYPE_HistDomain;
  3669. break;
  3670. default:
  3671. hr = E_FAIL;
  3672. }
  3673. if (SUCCEEDED(hr))
  3674. pidlNext = _ILNext(pidlNext);
  3675. }
  3676. if (SUCCEEDED(hr))
  3677. {
  3678. CHistFolder *phsf = new CHistFolder(ftNew);
  3679. if (phsf)
  3680. {
  3681. // If we're binding to a Domain from an Interval, pidl will not contain the
  3682. // interval, so we've got to do a SetCachePrefix.
  3683. hr = phsf->SetCachePrefix(_pszCachePrefix);
  3684. if (SUCCEEDED(hr))
  3685. {
  3686. LPITEMIDLIST pidlNew;
  3687. hr = SHILCombine(_pidl, pidl, &pidlNew);
  3688. if (SUCCEEDED(hr))
  3689. {
  3690. hr = phsf->Initialize(pidlNew);
  3691. if (SUCCEEDED(hr))
  3692. {
  3693. hr = phsf->QueryInterface(riid, ppv);
  3694. }
  3695. ILFree(pidlNew);
  3696. }
  3697. }
  3698. phsf->Release();
  3699. }
  3700. else
  3701. {
  3702. hr = E_OUTOFMEMORY;
  3703. }
  3704. }
  3705. }
  3706. if (fRealignedPidl)
  3707. FreeRealignedPidl(pidl);
  3708. }
  3709. return hr;
  3710. }
  3711. HRESULT CHistFolder::BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
  3712. {
  3713. *ppv = NULL;
  3714. return E_NOTIMPL;
  3715. }
  3716. // A Successor to the IsLeaf
  3717. BOOL CHistFolder::_IsLeaf()
  3718. {
  3719. BOOL fRet = FALSE;
  3720. switch(_uViewType) {
  3721. case 0:
  3722. fRet = IsLeaf(_foldertype);
  3723. break;
  3724. case VIEWPIDL_ORDER_FREQ:
  3725. case VIEWPIDL_ORDER_TODAY:
  3726. case VIEWPIDL_SEARCH:
  3727. fRet = TRUE;
  3728. break;
  3729. case VIEWPIDL_ORDER_SITE:
  3730. fRet = (_uViewDepth == 1);
  3731. break;
  3732. }
  3733. return fRet;
  3734. }
  3735. // coroutine for CompaireIDs -- makes recursive call
  3736. int CHistFolder::_View_ContinueCompare(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  3737. {
  3738. int iRet = 0;
  3739. if ( (pidl1 = _ILNext(pidl1)) && (pidl2 = _ILNext(pidl2)) )
  3740. {
  3741. BOOL fEmpty1 = ILIsEmpty(pidl1);
  3742. BOOL fEmpty2 = ILIsEmpty(pidl2);
  3743. if (fEmpty1 || fEmpty2)
  3744. {
  3745. if (fEmpty1 && fEmpty2)
  3746. iRet = 0;
  3747. else
  3748. iRet = (fEmpty1 ? -1 : 1);
  3749. }
  3750. else
  3751. {
  3752. IShellFolder *psf;
  3753. if (SUCCEEDED(BindToObject(pidl1, NULL, IID_PPV_ARG(IShellFolder, &psf))))
  3754. {
  3755. iRet = psf->CompareIDs(0, pidl1, pidl2);
  3756. psf->Release();
  3757. }
  3758. }
  3759. }
  3760. return iRet;
  3761. }
  3762. int _CompareTitles(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  3763. {
  3764. int iRet = 0;
  3765. LPCTSTR pszTitle1;
  3766. LPCTSTR pszTitle2;
  3767. LPCTSTR pszUrl1 = _StripHistoryUrlToUrl(HPidlToSourceUrl(pidl1));
  3768. LPCTSTR pszUrl2 = _StripHistoryUrlToUrl(HPidlToSourceUrl(pidl2));
  3769. ua_GetURLTitle( &pszTitle1, (LPBASEPIDL)pidl1 );
  3770. ua_GetURLTitle( &pszTitle2, (LPBASEPIDL)pidl2 );
  3771. // CompareIDs has to check for equality, also -- two URLs are only equal when
  3772. // they have the same URL (not title)
  3773. int iUrlCmp;
  3774. if (!(iUrlCmp = StrCmpI(pszUrl1, pszUrl2)))
  3775. iRet = 0;
  3776. else
  3777. {
  3778. iRet = StrCmpI( (pszTitle1 ? pszTitle1 : pszUrl1),
  3779. (pszTitle2 ? pszTitle2 : pszUrl2) );
  3780. // this says: if two docs have the same Title, but different URL
  3781. // we then sort by url -- the last thing we want to do
  3782. // is return that they're equal!! Ay Caramba!
  3783. if (iRet == 0)
  3784. iRet = iUrlCmp;
  3785. }
  3786. return iRet;
  3787. }
  3788. // unalligned verison
  3789. #if defined(UNIX) || !defined(_X86_)
  3790. UINT ULCompareFileTime(UNALIGNED const FILETIME *pft1, UNALIGNED const FILETIME *pft2)
  3791. {
  3792. FILETIME tmpFT1, tmpFT2;
  3793. CopyMemory(&tmpFT1, pft1, sizeof(tmpFT1));
  3794. CopyMemory(&tmpFT2, pft2, sizeof(tmpFT1));
  3795. return CompareFileTime( &tmpFT1, &tmpFT2 );
  3796. }
  3797. #else
  3798. #define ULCompareFileTime(pft1, pft2) CompareFileTime(pft1, pft2)
  3799. #endif
  3800. HRESULT CHistFolder::_ViewType_CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  3801. {
  3802. ASSERT(_uViewType);
  3803. int iRet = -1;
  3804. if (pidl1 && pidl2)
  3805. {
  3806. switch (_uViewType) {
  3807. case VIEWPIDL_ORDER_FREQ:
  3808. ASSERT(_IsValid_HEIPIDL(pidl1) && _IsValid_HEIPIDL(pidl2));
  3809. // need to strip because freq pidls are "Visited: " and
  3810. // all others come from our special bucket
  3811. if (!_CompareHCURLs(pidl1, pidl2))
  3812. iRet = 0;
  3813. else
  3814. iRet = ((((LPHEIPIDL)pidl2)->llPriority < ((LPHEIPIDL)pidl1)->llPriority) ? -1 : +1);
  3815. break;
  3816. case VIEWPIDL_SEARCH:
  3817. iRet = _CompareTitles(pidl1, pidl2);
  3818. break;
  3819. case VIEWPIDL_ORDER_TODAY: // view by order visited today
  3820. {
  3821. int iNameDiff;
  3822. ASSERT(_IsValid_HEIPIDL(pidl1) && _IsValid_HEIPIDL(pidl2));
  3823. // must do this comparison because CompareIDs is not only called for Sorting
  3824. // but to see if some pidls are equal
  3825. if ((iNameDiff = _CompareHCURLs(pidl1, pidl2)) == 0)
  3826. iRet = 0;
  3827. else
  3828. {
  3829. iRet = ULCompareFileTime(&(((LPHEIPIDL)pidl2)->ftModified), &(((LPHEIPIDL)pidl1)->ftModified));
  3830. // if the file times are equal, they're still not the same url -- so
  3831. // they have to be ordered on url
  3832. if (iRet == 0)
  3833. iRet = iNameDiff;
  3834. }
  3835. break;
  3836. }
  3837. case VIEWPIDL_ORDER_SITE:
  3838. if (_uViewDepth == 0)
  3839. {
  3840. TCHAR szName1[MAX_PATH], szName2[MAX_PATH];
  3841. _GetURLDispName((LPBASEPIDL)pidl1, szName1, ARRAYSIZE(szName1));
  3842. _GetURLDispName((LPBASEPIDL)pidl2, szName2, ARRAYSIZE(szName2));
  3843. iRet = StrCmpI(szName1, szName2);
  3844. }
  3845. else if (_uViewDepth == 1) {
  3846. iRet = _CompareTitles(pidl1, pidl2);
  3847. }
  3848. break;
  3849. }
  3850. if (iRet == 0)
  3851. iRet = _View_ContinueCompare(pidl1, pidl2);
  3852. }
  3853. else {
  3854. iRet = -1;
  3855. }
  3856. return ResultFromShort((SHORT)iRet);
  3857. }
  3858. HRESULT CHistFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  3859. {
  3860. BOOL fRealigned1;
  3861. HRESULT hr = AlignPidl(&pidl1, &fRealigned1);
  3862. if (SUCCEEDED(hr))
  3863. {
  3864. BOOL fRealigned2;
  3865. hr = AlignPidl(&pidl2, &fRealigned2);
  3866. if (SUCCEEDED(hr))
  3867. {
  3868. hr = _CompareAlignedIDs(lParam, pidl1, pidl2);
  3869. if (fRealigned2)
  3870. FreeRealignedPidl(pidl2);
  3871. }
  3872. if (fRealigned1)
  3873. FreeRealignedPidl(pidl1);
  3874. }
  3875. return hr;
  3876. }
  3877. HRESULT CHistFolder::_CompareAlignedIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  3878. {
  3879. int iRet = 0;
  3880. USHORT usSign;
  3881. FOLDER_TYPE FolderType = _foldertype;
  3882. LPHEIPIDL phei1 = NULL;
  3883. LPHEIPIDL phei2 = NULL;
  3884. if (NULL == pidl1 || NULL == pidl2)
  3885. return E_INVALIDARG;
  3886. if (_uViewType)
  3887. {
  3888. return _ViewType_CompareIDs(lParam, pidl1, pidl2);
  3889. }
  3890. if (IS_VALID_VIEWPIDL(pidl1) && IS_VALID_VIEWPIDL(pidl2))
  3891. {
  3892. if ((((LPVIEWPIDL)pidl1)->usViewType == ((LPVIEWPIDL)pidl2)->usViewType) &&
  3893. (((LPVIEWPIDL)pidl1)->usExtra == ((LPVIEWPIDL)pidl2)->usExtra))
  3894. {
  3895. iRet = _View_ContinueCompare(pidl1, pidl2);
  3896. }
  3897. else
  3898. {
  3899. iRet = ((((LPVIEWPIDL)pidl1)->usViewType < ((LPVIEWPIDL)pidl2)->usViewType) ? -1 : 1);
  3900. }
  3901. goto exitPoint;
  3902. }
  3903. if (!IsLeaf(_foldertype))
  3904. {
  3905. // We try to avoid unneccessary BindToObjs to compare partial paths
  3906. usSign = FOLDER_TYPE_Hist == FolderType ? IDIPIDL_SIGN : IDDPIDL_SIGN;
  3907. while (TRUE)
  3908. {
  3909. LPBASEPIDL pceip1 = (LPBASEPIDL) pidl1;
  3910. LPBASEPIDL pceip2 = (LPBASEPIDL) pidl2;
  3911. if (pidl1->mkid.cb == 0 || pidl2->mkid.cb == 0)
  3912. {
  3913. iRet = pidl1->mkid.cb == pidl2->mkid.cb ? 0 : 1;
  3914. goto exitPoint;
  3915. }
  3916. if (!_IsValid_IDPIDL(pidl1) || !_IsValid_IDPIDL(pidl2))
  3917. return E_FAIL;
  3918. if (!EQUIV_IDSIGN(pceip1->usSign,usSign) || !EQUIV_IDSIGN(pceip2->usSign,usSign))
  3919. return E_FAIL;
  3920. if (_foldertype == FOLDER_TYPE_HistInterval)
  3921. {
  3922. TCHAR szName1[MAX_PATH], szName2[MAX_PATH];
  3923. _GetURLDispName((LPBASEPIDL)pidl1, szName1, ARRAYSIZE(szName1));
  3924. _GetURLDispName((LPBASEPIDL)pidl2, szName2, ARRAYSIZE(szName2));
  3925. iRet = StrCmpI(szName1, szName2);
  3926. goto exitPoint;
  3927. }
  3928. else
  3929. {
  3930. iRet = ualstrcmpi(_GetURLTitle((LPBASEPIDL)pidl1), _GetURLTitle((LPBASEPIDL)pidl2));
  3931. if (iRet != 0)
  3932. goto exitPoint;
  3933. }
  3934. if (pceip1->usSign != pceip2->usSign)
  3935. {
  3936. iRet = -1;
  3937. goto exitPoint;
  3938. }
  3939. pidl1 = _ILNext(pidl1);
  3940. pidl2 = _ILNext(pidl2);
  3941. if (IDIPIDL_SIGN == usSign)
  3942. {
  3943. usSign = IDDPIDL_SIGN;
  3944. }
  3945. }
  3946. }
  3947. // At this point, both pidls have resolved to leaf (history or cache)
  3948. phei1 = _IsValid_HEIPIDL(pidl1);
  3949. phei2 = _IsValid_HEIPIDL(pidl2);
  3950. if (!phei1 || !phei2)
  3951. return E_FAIL;
  3952. switch (lParam & SHCIDS_COLUMNMASK)
  3953. {
  3954. case ICOLH_URL_TITLE:
  3955. {
  3956. TCHAR szStr1[MAX_PATH], szStr2[MAX_PATH];
  3957. _GetHistURLDispName(phei1, szStr1, ARRAYSIZE(szStr1));
  3958. _GetHistURLDispName(phei2, szStr2, ARRAYSIZE(szStr2));
  3959. iRet = StrCmpI(szStr1, szStr2);
  3960. }
  3961. break;
  3962. case ICOLH_URL_NAME:
  3963. iRet = _CompareHFolderPidl(pidl1, pidl2);
  3964. break;
  3965. case ICOLH_URL_LASTVISITED:
  3966. iRet = ULCompareFileTime(&((LPHEIPIDL)pidl2)->ftModified, &((LPHEIPIDL)pidl1)->ftModified);
  3967. break;
  3968. default:
  3969. // The high bit on means to compare absolutely, ie: even if only filetimes
  3970. // are different, we rule file pidls to be different
  3971. if (lParam & SHCIDS_ALLFIELDS)
  3972. {
  3973. iRet = CompareIDs(ICOLH_URL_NAME, pidl1, pidl2);
  3974. if (iRet == 0)
  3975. {
  3976. iRet = CompareIDs(ICOLH_URL_TITLE, pidl1, pidl2);
  3977. if (iRet == 0)
  3978. {
  3979. iRet = CompareIDs(ICOLH_URL_LASTVISITED, pidl1, pidl2);
  3980. }
  3981. }
  3982. }
  3983. else
  3984. {
  3985. iRet = -1;
  3986. }
  3987. break;
  3988. }
  3989. exitPoint:
  3990. return ResultFromShort((SHORT)iRet);
  3991. }
  3992. HRESULT CHistFolder::CreateViewObject(HWND hwnd, REFIID riid, void **ppv)
  3993. {
  3994. HRESULT hr = E_NOINTERFACE;
  3995. *ppv = NULL;
  3996. if (riid == IID_IShellView)
  3997. {
  3998. ASSERT(!_uViewType);
  3999. hr = HistFolderView_CreateInstance(this, ppv);
  4000. }
  4001. else if (riid == IID_IContextMenu)
  4002. {
  4003. // this creates the "Arrange Icons" cascased menu in the background of folder view
  4004. if (IsLeaf(_foldertype))
  4005. {
  4006. CFolderArrangeMenu *p = new CFolderArrangeMenu(MENU_HISTORY);
  4007. if (p)
  4008. {
  4009. hr = p->QueryInterface(riid, ppv);
  4010. p->Release();
  4011. }
  4012. else
  4013. hr = E_OUTOFMEMORY;
  4014. }
  4015. }
  4016. else if (riid == IID_IShellDetails)
  4017. {
  4018. CDetailsOfFolder *p = new CDetailsOfFolder(hwnd, this);
  4019. if (p)
  4020. {
  4021. hr = p->QueryInterface(riid, ppv);
  4022. p->Release();
  4023. }
  4024. else
  4025. hr = E_OUTOFMEMORY;
  4026. }
  4027. return hr;
  4028. }
  4029. HRESULT CHistFolder::_ViewType_GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *prgfInOut)
  4030. {
  4031. ASSERT(_uViewType);
  4032. if (!prgfInOut || !apidl)
  4033. return E_INVALIDARG;
  4034. HRESULT hr = S_OK;
  4035. int cGoodPidls = 0;
  4036. if (*prgfInOut & SFGAO_VALIDATE)
  4037. {
  4038. for (UINT u = 0; SUCCEEDED(hr) && (u < cidl); ++u)
  4039. {
  4040. switch(_uViewType)
  4041. {
  4042. case VIEWPIDL_ORDER_TODAY:
  4043. _EnsureHistStg();
  4044. if (_IsValid_HEIPIDL(apidl[u]) &&
  4045. SUCCEEDED(_pUrlHistStg->QueryUrl(_StripHistoryUrlToUrl(HPidlToSourceUrl(apidl[u])),
  4046. STATURL_QUERYFLAG_NOURL, NULL)))
  4047. {
  4048. ++cGoodPidls;
  4049. }
  4050. else
  4051. hr = E_FAIL;
  4052. break;
  4053. case VIEWPIDL_SEARCH:
  4054. case VIEWPIDL_ORDER_FREQ:
  4055. // this is a temporary fix to get the behaviour of the namespace
  4056. // control correct -- the long-term fix involves cacheing a
  4057. // generated list of these items and validating that list
  4058. break;
  4059. case VIEWPIDL_ORDER_SITE:
  4060. {
  4061. ASSERT(_uViewDepth == 1);
  4062. _ValidateIntervalCache();
  4063. LPCWSTR psz = _StripHistoryUrlToUrl(HPidlToSourceUrl(apidl[u]));
  4064. if (psz && _SearchFlatCacheForUrl(psz, NULL, NULL) == ERROR_SUCCESS)
  4065. {
  4066. ++cGoodPidls;
  4067. }
  4068. else
  4069. hr = E_FAIL;
  4070. }
  4071. break;
  4072. default:
  4073. hr = E_FAIL;
  4074. }
  4075. }
  4076. }
  4077. if (SUCCEEDED(hr))
  4078. {
  4079. if (_IsLeaf())
  4080. *prgfInOut = SFGAO_CANCOPY | SFGAO_HASPROPSHEET;
  4081. else
  4082. *prgfInOut = SFGAO_FOLDER;
  4083. }
  4084. return hr;
  4085. }
  4086. // Right now, we will allow TIF Drag in Browser Only, even though
  4087. // it will not be Zone Checked at the Drop.
  4088. //#define BROWSERONLY_NOTIFDRAG
  4089. HRESULT CHistFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST * apidl, ULONG * prgfInOut)
  4090. {
  4091. ULONG rgfInOut;
  4092. FOLDER_TYPE FolderType = _foldertype;
  4093. // Make sure each pidl in the array is dword aligned.
  4094. BOOL fRealigned;
  4095. HRESULT hr = AlignPidlArray(apidl, cidl, &apidl, &fRealigned);
  4096. if (SUCCEEDED(hr))
  4097. {
  4098. // For view types, we'll map FolderType to do the right thing...
  4099. if (_uViewType)
  4100. {
  4101. hr = _ViewType_GetAttributesOf(cidl, apidl, prgfInOut);
  4102. }
  4103. else
  4104. {
  4105. switch (FolderType)
  4106. {
  4107. case FOLDER_TYPE_Hist:
  4108. rgfInOut = SFGAO_FOLDER | SFGAO_HASSUBFOLDER;
  4109. break;
  4110. case FOLDER_TYPE_HistInterval:
  4111. rgfInOut = SFGAO_FOLDER;
  4112. break;
  4113. case FOLDER_TYPE_HistDomain:
  4114. {
  4115. UINT cGoodPidls;
  4116. if (SFGAO_VALIDATE & *prgfInOut)
  4117. {
  4118. cGoodPidls = 0;
  4119. if (SUCCEEDED(_EnsureHistStg()))
  4120. {
  4121. for (UINT i = 0; i < cidl; i++)
  4122. {
  4123. // NOTE: QueryUrlA checks for NULL URL and returns E_INVALIDARG
  4124. if (!_IsValid_HEIPIDL(apidl[i]) ||
  4125. FAILED(_pUrlHistStg->QueryUrl(_StripHistoryUrlToUrl(HPidlToSourceUrl(apidl[i])),
  4126. STATURL_QUERYFLAG_NOURL, NULL)))
  4127. {
  4128. break;
  4129. }
  4130. cGoodPidls++;
  4131. }
  4132. }
  4133. }
  4134. else
  4135. cGoodPidls = cidl;
  4136. if (cidl == cGoodPidls)
  4137. {
  4138. rgfInOut = SFGAO_CANCOPY | SFGAO_HASPROPSHEET;
  4139. break;
  4140. }
  4141. // FALL THROUGH INTENDED!
  4142. }
  4143. default:
  4144. rgfInOut = 0;
  4145. hr = E_FAIL;
  4146. break;
  4147. }
  4148. // all items can be deleted
  4149. if (SUCCEEDED(hr))
  4150. rgfInOut |= SFGAO_CANDELETE;
  4151. *prgfInOut = rgfInOut;
  4152. }
  4153. if (fRealigned)
  4154. FreeRealignedPidlArray(apidl, cidl);
  4155. }
  4156. return hr;
  4157. }
  4158. HRESULT CHistFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST * apidl,
  4159. REFIID riid, UINT * prgfInOut, void **ppv)
  4160. {
  4161. *ppv = NULL; // null the out param
  4162. // Make sure all pidls in the array are dword aligned.
  4163. BOOL fRealigned;
  4164. HRESULT hr = AlignPidlArray(apidl, cidl, &apidl, &fRealigned);
  4165. if (SUCCEEDED(hr))
  4166. {
  4167. if ((riid == IID_IShellLinkA ||
  4168. riid == IID_IShellLinkW ||
  4169. riid == IID_IExtractIconA ||
  4170. riid == IID_IExtractIconW) &&
  4171. _IsLeaf())
  4172. {
  4173. LPCTSTR pszURL = HPidlToSourceUrl(apidl[0]);
  4174. pszURL = _StripHistoryUrlToUrl(pszURL);
  4175. hr = _GetShortcut(pszURL, riid, ppv);
  4176. }
  4177. else if ((riid == IID_IContextMenu) ||
  4178. (riid == IID_IDataObject) ||
  4179. (riid == IID_IExtractIconA) ||
  4180. (riid == IID_IExtractIconW) ||
  4181. (riid == IID_IQueryInfo))
  4182. {
  4183. hr = CHistItem_CreateInstance(this, hwnd, cidl, apidl, riid, ppv);
  4184. }
  4185. else
  4186. {
  4187. hr = E_FAIL;
  4188. }
  4189. if (fRealigned)
  4190. FreeRealignedPidlArray(apidl, cidl);
  4191. }
  4192. return hr;
  4193. }
  4194. HRESULT CHistFolder::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
  4195. {
  4196. if (pSort)
  4197. {
  4198. if (_uViewType == 0 && _foldertype == FOLDER_TYPE_HistDomain)
  4199. *pSort = ICOLH_URL_TITLE;
  4200. else
  4201. *pSort = 0;
  4202. }
  4203. if (pDisplay)
  4204. {
  4205. if (_uViewType == 0 && _foldertype == FOLDER_TYPE_HistDomain)
  4206. *pDisplay = ICOLH_URL_TITLE;
  4207. else
  4208. *pDisplay = 0;
  4209. }
  4210. return S_OK;
  4211. }
  4212. LPCTSTR _GetUrlForPidl(LPCITEMIDLIST pidl)
  4213. {
  4214. LPCTSTR pszUrl = _StripHistoryUrlToUrl(HPidlToSourceUrl(pidl));
  4215. return pszUrl ? pszUrl : TEXT("");
  4216. }
  4217. HRESULT CHistFolder::_GetInfoTip(LPCITEMIDLIST pidl, DWORD dwFlags, WCHAR **ppwszTip)
  4218. {
  4219. HRESULT hr;
  4220. TCHAR szTip[MAX_URL_STRING + 100], szPart2[MAX_URL_STRING];
  4221. szTip[0] = szPart2[0] = 0;
  4222. FOLDER_TYPE FolderType = _foldertype;
  4223. // For special views, map FolderType to do the right thing
  4224. if (_uViewType)
  4225. {
  4226. switch(_uViewType) {
  4227. case VIEWPIDL_SEARCH:
  4228. case VIEWPIDL_ORDER_FREQ:
  4229. case VIEWPIDL_ORDER_TODAY:
  4230. FolderType = FOLDER_TYPE_HistDomain;
  4231. break;
  4232. case VIEWPIDL_ORDER_SITE:
  4233. if (_uViewDepth == 0)
  4234. FolderType = FOLDER_TYPE_HistInterval;
  4235. else
  4236. FolderType = FOLDER_TYPE_HistDomain;
  4237. break;
  4238. }
  4239. }
  4240. switch (FolderType)
  4241. {
  4242. case FOLDER_TYPE_HistDomain:
  4243. {
  4244. _GetHistURLDispName((LPHEIPIDL)pidl, szTip, ARRAYSIZE(szTip));
  4245. DWORD cchPart2 = ARRAYSIZE(szPart2);
  4246. PrepareURLForDisplayUTF8(_GetUrlForPidl(pidl), szPart2, &cchPart2, TRUE);
  4247. }
  4248. break;
  4249. case FOLDER_TYPE_Hist:
  4250. {
  4251. FILETIME ftStart, ftEnd;
  4252. LPCTSTR pszIntervalName;
  4253. ua_GetURLTitle(&pszIntervalName, (LPBASEPIDL)pidl);
  4254. if (SUCCEEDED(_ValueToInterval(pszIntervalName, &ftStart, &ftEnd)))
  4255. {
  4256. GetTooltipForTimeInterval(&ftStart, &ftEnd, szTip, ARRAYSIZE(szTip));
  4257. }
  4258. break;
  4259. }
  4260. case FOLDER_TYPE_HistInterval:
  4261. {
  4262. TCHAR szFmt[64];
  4263. MLLoadString(IDS_SITETOOLTIP, szFmt, ARRAYSIZE(szFmt));
  4264. wnsprintf(szTip, ARRAYSIZE(szTip), szFmt, _GetURLTitle((LPBASEPIDL)pidl));
  4265. break;
  4266. }
  4267. }
  4268. if (szTip[0])
  4269. {
  4270. // Only combine the 2 parts if the second part exists, and if
  4271. // the 2 parts are not equal.
  4272. if (szPart2[0] && StrCmpI(szTip, szPart2) != 0)
  4273. {
  4274. StrCatBuff(szTip, TEXT("\r\n"), ARRAYSIZE(szTip));
  4275. StrCatBuff(szTip, szPart2, ARRAYSIZE(szTip));
  4276. }
  4277. hr = SHStrDup(szTip, ppwszTip);
  4278. }
  4279. else
  4280. {
  4281. hr = E_FAIL;
  4282. *ppwszTip = NULL;
  4283. }
  4284. return hr;
  4285. }
  4286. //
  4287. // _GetFriendlyUrlDispName -- compute the "friendly name" of an URL
  4288. //
  4289. // in: A UTF8 encoded URL. For example, ftp://ftp.nsca.uiuc.edu/foo.bar
  4290. //
  4291. // out: A "friendly name" for the URL with the path stripped if necessary
  4292. // (ie ftp://ftp.ncsa.uiuc.edu ==> ftp.ncsa.uiuc.edu
  4293. // and ftp://www.foo.bar/foo.bar ==> foo -or- foo.bar depeneding on
  4294. // whether file xtnsn hiding is on or off
  4295. //
  4296. // NOTE: pszUrl and pszOut may be the same buffer -- this is allowed
  4297. //
  4298. HRESULT _GetFriendlyUrlDispName(LPCTSTR pszUrl, LPTSTR pszOut, DWORD cchBuf)
  4299. {
  4300. HRESULT hr = E_FAIL;
  4301. PrepareURLForDisplayUTF8(pszUrl, pszOut, &cchBuf, TRUE);
  4302. TCHAR szUrlPath[MAX_PATH];
  4303. TCHAR szUrlHost[MAX_PATH];
  4304. // Set up InternetCrackUrl parameter block
  4305. SHURL_COMPONENTSW urlcomponents = { 0 };
  4306. urlcomponents.dwStructSize = sizeof(URL_COMPONENTS);
  4307. urlcomponents.lpszUrlPath = szUrlPath;
  4308. urlcomponents.dwUrlPathLength = ARRAYSIZE(szUrlPath);
  4309. urlcomponents.lpszHostName = szUrlHost;
  4310. urlcomponents.dwHostNameLength = ARRAYSIZE(szUrlHost);
  4311. if (UrlCrackW(pszOut, cchBuf, ICU_DECODE, &urlcomponents))
  4312. {
  4313. SHELLSTATE ss;
  4314. SHGetSetSettings(&ss, SSF_SHOWEXTENSIONS, FALSE);
  4315. // eliminate trailing slash
  4316. if ((urlcomponents.dwUrlPathLength > 0) &&
  4317. (urlcomponents.lpszUrlPath[urlcomponents.dwUrlPathLength - 1] == TEXT('/')))
  4318. {
  4319. urlcomponents.lpszUrlPath[urlcomponents.dwUrlPathLength - 1] = TEXT('\0');
  4320. --urlcomponents.dwUrlPathLength;
  4321. }
  4322. if (urlcomponents.dwUrlPathLength > 0)
  4323. {
  4324. // LPCTSTR _FindURLFileName(LPCTSTR) --> const_cast is OK
  4325. LPTSTR pszFileName = const_cast<LPTSTR>(_FindURLFileName(urlcomponents.lpszUrlPath));
  4326. if (!ss.fShowExtensions)
  4327. {
  4328. PathRemoveExtension(pszFileName);
  4329. }
  4330. StrCpyN(pszOut, pszFileName, cchBuf);
  4331. }
  4332. else
  4333. {
  4334. StrCpyN(pszOut, urlcomponents.lpszHostName, cchBuf);
  4335. }
  4336. hr = S_OK;
  4337. }
  4338. return hr;
  4339. }
  4340. void CHistFolder::_GetHistURLDispName(LPHEIPIDL phei, LPTSTR pszStr, UINT cchStr)
  4341. {
  4342. *pszStr = 0;
  4343. if ((phei->usFlags & HISTPIDL_VALIDINFO) && phei->usTitle)
  4344. {
  4345. StrCpyN(pszStr, (LPTSTR)((BYTE*)phei + phei->usTitle), cchStr);
  4346. }
  4347. else if (SUCCEEDED(_EnsureHistStg()))
  4348. {
  4349. LPCTSTR pszUrl = _StripHistoryUrlToUrl(HPidlToSourceUrl((LPCITEMIDLIST)phei));
  4350. if (pszUrl)
  4351. {
  4352. STATURL suThis;
  4353. if (SUCCEEDED(_pUrlHistStg->QueryUrl(pszUrl, STATURL_QUERYFLAG_NOURL, &suThis)) && suThis.pwcsTitle)
  4354. {
  4355. // sometimes the URL is stored in the title
  4356. // avoid using those titles.
  4357. if (_TitleIsGood(suThis.pwcsTitle))
  4358. SHUnicodeToTChar(suThis.pwcsTitle, pszStr, cchStr);
  4359. OleFree(suThis.pwcsTitle);
  4360. }
  4361. // if we havent got anything yet
  4362. if (!*pszStr)
  4363. {
  4364. if (FAILED(_GetFriendlyUrlDispName(pszUrl, pszStr, cchStr)))
  4365. {
  4366. // last resort: display the whole URL
  4367. StrCpyN(pszStr, pszUrl, cchStr);
  4368. }
  4369. }
  4370. }
  4371. }
  4372. }
  4373. HRESULT CHistFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, STRRET *lpName)
  4374. {
  4375. TCHAR szTemp[MAX_URL_STRING];
  4376. szTemp[0] = 0;
  4377. // Make sure the pidl is dword aligned.
  4378. BOOL fRealigned;
  4379. if (SUCCEEDED(AlignPidl(&pidl, &fRealigned)))
  4380. {
  4381. if (IS_VALID_VIEWPIDL(pidl))
  4382. {
  4383. UINT idRsrc;
  4384. switch(((LPVIEWPIDL)pidl)->usViewType) {
  4385. case VIEWPIDL_ORDER_SITE: idRsrc = IDS_HISTVIEW_SITE; break;
  4386. case VIEWPIDL_ORDER_TODAY: idRsrc = IDS_HISTVIEW_TODAY; break;
  4387. case VIEWPIDL_ORDER_FREQ:
  4388. default:
  4389. idRsrc = IDS_HISTVIEW_FREQUENCY; break;
  4390. }
  4391. MLLoadString(idRsrc, szTemp, ARRAYSIZE(szTemp));
  4392. }
  4393. else
  4394. {
  4395. if (_uViewType == VIEWPIDL_ORDER_SITE &&
  4396. _uViewDepth == 0)
  4397. {
  4398. _GetURLDispName((LPBASEPIDL)pidl, szTemp, ARRAYSIZE(szTemp));
  4399. }
  4400. else if (_IsLeaf())
  4401. {
  4402. LPCTSTR pszTitle;
  4403. BOOL fDoUnescape;
  4404. ua_GetURLTitle(&pszTitle, (LPBASEPIDL)pidl);
  4405. // _GetURLTitle could return the real title or just an URL.
  4406. // We use _URLTitleIsURL to make sure we don't unescape any titles.
  4407. if (pszTitle && *pszTitle)
  4408. {
  4409. StrCpyN(szTemp, pszTitle, ARRAYSIZE(szTemp));
  4410. fDoUnescape = _URLTitleIsURL((LPBASEPIDL)pidl);
  4411. }
  4412. else
  4413. {
  4414. LPCTSTR pszUrl = _StripHistoryUrlToUrl(HPidlToSourceUrl(pidl));
  4415. if (pszUrl)
  4416. StrCpyN(szTemp, pszUrl, ARRAYSIZE(szTemp));
  4417. fDoUnescape = TRUE;
  4418. }
  4419. if (fDoUnescape)
  4420. {
  4421. // at this point, szTemp contains part of an URL
  4422. // we will crack (smoke) the URL
  4423. LPCTSTR pszUrl = HPidlToSourceUrl(pidl);
  4424. // Is this pidl a history entry?
  4425. if (((LPBASEPIDL)pidl)->usSign == (USHORT)HEIPIDL_SIGN)
  4426. {
  4427. pszUrl = _StripHistoryUrlToUrl(pszUrl);
  4428. }
  4429. if (pszUrl)
  4430. {
  4431. if (FAILED(_GetFriendlyUrlDispName(pszUrl, szTemp, ARRAYSIZE(szTemp))))
  4432. {
  4433. StrCpyN(szTemp, pszUrl, ARRAYSIZE(szTemp));
  4434. }
  4435. }
  4436. }
  4437. }
  4438. else
  4439. {
  4440. // for the history, we'll use the title if we have one, otherwise we'll use
  4441. // the url filename.
  4442. switch (_foldertype)
  4443. {
  4444. case FOLDER_TYPE_HistDomain:
  4445. _GetHistURLDispName((LPHEIPIDL)pidl, szTemp, ARRAYSIZE(szTemp));
  4446. break;
  4447. case FOLDER_TYPE_Hist:
  4448. {
  4449. FILETIME ftStart, ftEnd;
  4450. _ValueToInterval(_GetURLTitle((LPBASEPIDL)pidl), &ftStart, &ftEnd);
  4451. GetDisplayNameForTimeInterval(&ftStart, &ftEnd, szTemp, ARRAYSIZE(szTemp));
  4452. }
  4453. break;
  4454. case FOLDER_TYPE_HistInterval:
  4455. _GetURLDispName((LPBASEPIDL)pidl, szTemp, ARRAYSIZE(szTemp));
  4456. break;
  4457. }
  4458. }
  4459. }
  4460. if (fRealigned)
  4461. FreeRealignedPidl(pidl);
  4462. }
  4463. return StringToStrRet(szTemp, lpName);
  4464. }
  4465. HRESULT CHistFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl,
  4466. LPCOLESTR pszName, DWORD uFlags, LPITEMIDLIST *ppidlOut)
  4467. {
  4468. if (ppidlOut)
  4469. *ppidlOut = NULL; // null the out param
  4470. return E_FAIL;
  4471. }
  4472. //////////////////////////////////
  4473. //
  4474. // IShellIcon Methods...
  4475. //
  4476. HRESULT CHistFolder::GetIconOf(LPCITEMIDLIST pidl, UINT flags, LPINT lpIconIndex)
  4477. {
  4478. return S_FALSE;
  4479. }
  4480. // IPersist
  4481. HRESULT CHistFolder::GetClassID(CLSID *pclsid)
  4482. {
  4483. *pclsid = CLSID_HistFolder;
  4484. return S_OK;
  4485. }
  4486. HRESULT CHistFolder::Initialize(LPCITEMIDLIST pidlInit)
  4487. {
  4488. HRESULT hr = S_OK;
  4489. ILFree(_pidl);
  4490. if ((FOLDER_TYPE_Hist == _foldertype) && !IsCSIDLFolder(CSIDL_HISTORY, pidlInit))
  4491. hr = E_FAIL;
  4492. else
  4493. {
  4494. hr = SHILClone(pidlInit, &_pidl);
  4495. if (SUCCEEDED(hr))
  4496. hr = _ExtractInfoFromPidl();
  4497. }
  4498. return hr;
  4499. }
  4500. //////////////////////////////////
  4501. //
  4502. // IPersistFolder2 Methods...
  4503. //
  4504. HRESULT CHistFolder::GetCurFolder(LPITEMIDLIST *ppidl)
  4505. {
  4506. if (_pidl)
  4507. return SHILClone(_pidl, ppidl);
  4508. *ppidl = NULL;
  4509. return S_FALSE; // success but empty
  4510. }
  4511. //////////////////////////////////////////////////
  4512. // IShellFolderViewType Methods
  4513. //
  4514. // but first, the enumerator class...
  4515. class CHistViewTypeEnum : public IEnumIDList
  4516. {
  4517. friend class CHistFolder;
  4518. public:
  4519. // IUnknown Methods
  4520. STDMETHODIMP QueryInterface(REFIID,void **);
  4521. STDMETHODIMP_(ULONG) AddRef(void);
  4522. STDMETHODIMP_(ULONG) Release(void);
  4523. // IEnumIDList Methods
  4524. STDMETHODIMP Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched);
  4525. STDMETHODIMP Skip(ULONG celt) { _uCurViewType += celt; return S_OK; }
  4526. STDMETHODIMP Reset() { _uCurViewType = 1; return S_OK; }
  4527. STDMETHODIMP Clone(IEnumIDList **ppenum);
  4528. private:
  4529. ~CHistViewTypeEnum() {}
  4530. CHistViewTypeEnum() : _cRef(1), _uCurViewType(1) {}
  4531. LONG _cRef;
  4532. UINT _uCurViewType;
  4533. };
  4534. STDMETHODIMP CHistViewTypeEnum::QueryInterface(REFIID riid, void **ppv)
  4535. {
  4536. static const QITAB qit[] = {
  4537. QITABENT(CHistViewTypeEnum, IEnumIDList), // IID_IEnumIDList
  4538. { 0 },
  4539. };
  4540. return QISearch(this, qit, riid, ppv);
  4541. }
  4542. ULONG CHistViewTypeEnum::AddRef(void)
  4543. {
  4544. return InterlockedIncrement(&_cRef);
  4545. }
  4546. ULONG CHistViewTypeEnum::Release(void)
  4547. {
  4548. if (InterlockedDecrement(&_cRef))
  4549. return _cRef;
  4550. delete this;
  4551. return 0;
  4552. }
  4553. HRESULT CHistViewTypeEnum::Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched)
  4554. {
  4555. HRESULT hr = S_FALSE;
  4556. if (rgelt && (pceltFetched || 1 == celt))
  4557. {
  4558. ULONG i = 0;
  4559. while (i < celt)
  4560. {
  4561. if (_uCurViewType <= VIEWPIDL_ORDER_MAX)
  4562. {
  4563. hr = CreateSpecialViewPidl(_uCurViewType, &(rgelt[i]));
  4564. if (SUCCEEDED(hr))
  4565. {
  4566. ++i;
  4567. ++_uCurViewType;
  4568. }
  4569. else
  4570. {
  4571. while (i)
  4572. ILFree(rgelt[--i]);
  4573. break;
  4574. }
  4575. }
  4576. else
  4577. {
  4578. break;
  4579. }
  4580. }
  4581. if (pceltFetched)
  4582. *pceltFetched = i;
  4583. }
  4584. else
  4585. {
  4586. hr = E_INVALIDARG;
  4587. }
  4588. return hr;
  4589. }
  4590. STDMETHODIMP CHistViewTypeEnum::Clone(IEnumIDList **ppenum)
  4591. {
  4592. CHistViewTypeEnum* phvte = new CHistViewTypeEnum();
  4593. if (phvte)
  4594. {
  4595. phvte->_uCurViewType = _uCurViewType;
  4596. *ppenum = phvte;
  4597. return S_OK;
  4598. }
  4599. else
  4600. return E_OUTOFMEMORY;
  4601. }
  4602. // Continuing with the CHistFolder::IShellFolderViewType
  4603. STDMETHODIMP CHistFolder::EnumViews(ULONG grfFlags, IEnumIDList **ppenum)
  4604. {
  4605. *ppenum = new CHistViewTypeEnum();
  4606. return *ppenum ? S_OK : E_OUTOFMEMORY;
  4607. }
  4608. STDMETHODIMP CHistFolder::GetDefaultViewName(DWORD uFlags, LPWSTR *ppwszName)
  4609. {
  4610. TCHAR szName[MAX_PATH];
  4611. MLLoadString(IDS_HISTVIEW_DEFAULT, szName, ARRAYSIZE(szName));
  4612. return SHStrDup(szName, ppwszName);
  4613. }
  4614. // Remember that these *MUST* be in order so that
  4615. // the array can be accessed by VIEWPIDL type
  4616. const DWORD CHistFolder::_rdwFlagsTable[] = {
  4617. SFVTFLAG_NOTIFY_CREATE, // Date
  4618. SFVTFLAG_NOTIFY_CREATE, // site
  4619. 0, // freq
  4620. SFVTFLAG_NOTIFY_CREATE | SFVTFLAG_NOTIFY_RESORT // today
  4621. };
  4622. STDMETHODIMP CHistFolder::GetViewTypeProperties(LPCITEMIDLIST pidl, DWORD *pdwFlags)
  4623. {
  4624. HRESULT hr = S_OK;
  4625. UINT uFlagTableIndex = 0;
  4626. if ((pidl != NULL) && !ILIsEmpty(pidl)) // default view
  4627. {
  4628. // Make sure the pidl is dword aligned.
  4629. BOOL fRealigned;
  4630. hr = AlignPidl(&pidl, &fRealigned);
  4631. if (SUCCEEDED(hr))
  4632. {
  4633. if (IS_VALID_VIEWPIDL(pidl))
  4634. {
  4635. uFlagTableIndex = ((LPVIEWPIDL)pidl)->usViewType;
  4636. ASSERT(uFlagTableIndex <= VIEWPIDL_ORDER_MAX);
  4637. }
  4638. else
  4639. {
  4640. hr = E_INVALIDARG;
  4641. }
  4642. if (fRealigned)
  4643. FreeRealignedPidl(pidl);
  4644. }
  4645. }
  4646. *pdwFlags = _rdwFlagsTable[uFlagTableIndex];
  4647. return hr;
  4648. }
  4649. HRESULT CHistFolder::TranslateViewPidl(LPCITEMIDLIST pidl, LPCITEMIDLIST pidlView,
  4650. LPITEMIDLIST *ppidlOut)
  4651. {
  4652. HRESULT hr;
  4653. if (pidl && IS_VALID_VIEWPIDL(pidlView))
  4654. {
  4655. if (!IS_VALID_VIEWPIDL(pidl))
  4656. {
  4657. hr = ConvertStandardHistPidlToSpecialViewPidl(pidl,
  4658. ((LPVIEWPIDL)pidlView)->usViewType,
  4659. ppidlOut);
  4660. }
  4661. else
  4662. {
  4663. hr = E_NOTIMPL;
  4664. }
  4665. }
  4666. else
  4667. {
  4668. hr = E_INVALIDARG;
  4669. }
  4670. return hr;
  4671. }
  4672. //////////////////////////////////////////////////
  4673. //
  4674. // IShellFolderSearchable Methods
  4675. //
  4676. // For more information about how this search stuff works,
  4677. // please see the comments for _CurrentSearches above
  4678. STDMETHODIMP CHistFolder::FindString(LPCWSTR pwszTarget, LPDWORD pdwFlags,
  4679. IUnknown *punkOnAsyncSearch,
  4680. LPITEMIDLIST *ppidlOut)
  4681. {
  4682. HRESULT hr = E_INVALIDARG;
  4683. if (ppidlOut)
  4684. {
  4685. *ppidlOut = NULL;
  4686. if (pwszTarget)
  4687. {
  4688. LPITEMIDLIST pidlView;
  4689. SYSTEMTIME systime;
  4690. FILETIME ftNow;
  4691. GetLocalTime(&systime);
  4692. SystemTimeToFileTime(&systime, &ftNow);
  4693. hr = CreateSpecialViewPidl(VIEWPIDL_SEARCH, &pidlView, sizeof(SEARCHVIEWPIDL) - sizeof(VIEWPIDL));
  4694. if (SUCCEEDED(hr))
  4695. {
  4696. ((LPSEARCHVIEWPIDL)pidlView)->ftSearchKey = ftNow;
  4697. IShellFolderSearchableCallback *psfscOnAsyncSearch = NULL;
  4698. if (punkOnAsyncSearch)
  4699. punkOnAsyncSearch->QueryInterface(IID_PPV_ARG(IShellFolderSearchableCallback, &psfscOnAsyncSearch));
  4700. // Insert this search into the global database
  4701. // This constructor will AddRef psfscOnAsyncSearch
  4702. _CurrentSearches *pcsNew = new _CurrentSearches(ftNow, pwszTarget, psfscOnAsyncSearch);
  4703. if (pcsNew)
  4704. {
  4705. if (psfscOnAsyncSearch)
  4706. psfscOnAsyncSearch->Release(); // _CurrentSearches now holds the ref
  4707. // This will AddRef pcsNew 'cause its going in the list
  4708. _CurrentSearches::s_NewSearch(pcsNew);
  4709. pcsNew->Release();
  4710. *ppidlOut = pidlView;
  4711. hr = S_OK;
  4712. }
  4713. else
  4714. {
  4715. ILFree(pidlView);
  4716. hr = E_OUTOFMEMORY;
  4717. }
  4718. }
  4719. }
  4720. }
  4721. return hr;
  4722. }
  4723. STDMETHODIMP CHistFolder::CancelAsyncSearch(LPCITEMIDLIST pidlSearch, LPDWORD pdwFlags)
  4724. {
  4725. HRESULT hr = E_INVALIDARG;
  4726. if (IS_VALID_VIEWPIDL(pidlSearch) &&
  4727. (((LPVIEWPIDL)pidlSearch)->usViewType == VIEWPIDL_SEARCH))
  4728. {
  4729. hr = S_FALSE;
  4730. _CurrentSearches *pcs = _CurrentSearches::s_FindSearch(((LPSEARCHVIEWPIDL)pidlSearch)->ftSearchKey);
  4731. if (pcs) {
  4732. pcs->_fKillSwitch = TRUE;
  4733. hr = S_OK;
  4734. pcs->Release();
  4735. }
  4736. }
  4737. return hr;
  4738. }
  4739. STDMETHODIMP CHistFolder::InvalidateSearch(LPCITEMIDLIST pidlSearch, LPDWORD pdwFlags)
  4740. {
  4741. HRESULT hr = E_INVALIDARG;
  4742. if (IS_VALID_VIEWPIDL(pidlSearch) &&
  4743. (((LPVIEWPIDL)pidlSearch)->usViewType == VIEWPIDL_SEARCH))
  4744. {
  4745. hr = S_FALSE;
  4746. _CurrentSearches *pcs = _CurrentSearches::s_FindSearch(((LPSEARCHVIEWPIDL)pidlSearch)->ftSearchKey);
  4747. if (pcs) {
  4748. _CurrentSearches::s_RemoveSearch(pcs);
  4749. pcs->Release();
  4750. }
  4751. }
  4752. return hr;
  4753. }
  4754. //////////////////////////////////////////////////
  4755. DWORD CHistFolder::_GetHitCount(LPCTSTR pszUrl)
  4756. {
  4757. DWORD dwHitCount = 0;
  4758. IUrlHistoryPriv *pUrlHistStg = _GetHistStg();
  4759. if (pUrlHistStg)
  4760. {
  4761. PROPVARIANT vProp = {0};
  4762. if (SUCCEEDED(pUrlHistStg->GetProperty(pszUrl, PID_INTSITE_VISITCOUNT, &vProp)) &&
  4763. (vProp.vt == VT_UI4))
  4764. {
  4765. dwHitCount = vProp.lVal;
  4766. }
  4767. pUrlHistStg->Release();
  4768. }
  4769. return dwHitCount;
  4770. }
  4771. // pidl should be freed by caller
  4772. // URL must have some sort of cache container prefix
  4773. LPHEIPIDL CHistFolder::_CreateHCacheFolderPidlFromUrl(BOOL fOleMalloc, LPCTSTR pszPrefixedUrl)
  4774. {
  4775. LPHEIPIDL pheiRet;
  4776. HRESULT hrLocal = E_FAIL;
  4777. STATURL suThis;
  4778. LPCTSTR pszStrippedUrl = _StripHistoryUrlToUrl(pszPrefixedUrl);
  4779. IUrlHistoryPriv *pUrlHistStg = _GetHistStg();
  4780. if (pUrlHistStg)
  4781. {
  4782. hrLocal = pUrlHistStg->QueryUrl(pszStrippedUrl, STATURL_QUERYFLAG_NOURL, &suThis);
  4783. pUrlHistStg->Release();
  4784. }
  4785. FILETIME ftLastVisit = { 0 };
  4786. DWORD dwNumHits = 0;
  4787. if (FAILED(hrLocal)) { // maybe the cache knows...
  4788. BYTE ab[MAX_URLCACHE_ENTRY];
  4789. LPINTERNET_CACHE_ENTRY_INFO pcei = (LPINTERNET_CACHE_ENTRY_INFO)(&ab);
  4790. DWORD dwSize = MAX_URLCACHE_ENTRY;
  4791. if (GetUrlCacheEntryInfo(_StripHistoryUrlToUrl(pszPrefixedUrl), pcei, &dwSize)) {
  4792. ftLastVisit = pcei->LastAccessTime;
  4793. dwNumHits = pcei->dwHitRate;
  4794. }
  4795. }
  4796. pheiRet = _CreateHCacheFolderPidl(fOleMalloc, pszPrefixedUrl,
  4797. SUCCEEDED(hrLocal) ? suThis.ftLastVisited : ftLastVisit,
  4798. SUCCEEDED(hrLocal) ? &suThis : NULL, 0,
  4799. SUCCEEDED(hrLocal) ? _GetHitCount(pszStrippedUrl) : dwNumHits);
  4800. if (SUCCEEDED(hrLocal) && suThis.pwcsTitle)
  4801. OleFree(suThis.pwcsTitle);
  4802. return pheiRet;
  4803. }
  4804. UINT _CountPidlParts(LPCITEMIDLIST pidl) {
  4805. LPCITEMIDLIST pidlTemp = pidl;
  4806. UINT uParts = 0;
  4807. if (pidl)
  4808. {
  4809. for (uParts = 0; pidlTemp->mkid.cb; pidlTemp = _ILNext(pidlTemp))
  4810. ++uParts;
  4811. }
  4812. return uParts;
  4813. }
  4814. // you must dealloc (LocalFree) the ppidl returned
  4815. LPITEMIDLIST* _SplitPidl(LPCITEMIDLIST pidl, UINT& uSizeInOut) {
  4816. LPCITEMIDLIST pidlTemp = pidl;
  4817. LPITEMIDLIST* ppidlList =
  4818. reinterpret_cast<LPITEMIDLIST *>(LocalAlloc(LPTR,
  4819. sizeof(LPITEMIDLIST) * uSizeInOut));
  4820. if (pidlTemp && ppidlList) {
  4821. UINT uCount;
  4822. for (uCount = 0; ( (uCount < uSizeInOut) && (pidlTemp->mkid.cb) );
  4823. ++uCount, pidlTemp = _ILNext(pidlTemp))
  4824. ppidlList[uCount] = const_cast<LPITEMIDLIST>(pidlTemp);
  4825. }
  4826. return ppidlList;
  4827. }
  4828. LPITEMIDLIST* _SplitPidlEasy(LPCITEMIDLIST pidl, UINT& uSizeOut) {
  4829. uSizeOut = _CountPidlParts(pidl);
  4830. return _SplitPidl(pidl, uSizeOut);
  4831. }
  4832. // caller LocalFree's *ppidlOut
  4833. // returned pidl should be combined with the history folder location
  4834. HRESULT _ConvertStdPidlToViewPidl_OrderSite(LPCITEMIDLIST pidlSecondLast,
  4835. LPCITEMIDLIST pidlLast,
  4836. LPITEMIDLIST *ppidlOut) {
  4837. HRESULT hr = E_FAIL;
  4838. // painfully construct the final pidl by concatenating the little
  4839. // peices [special_viewpidl, iddpidl, heipidl]
  4840. if ( _IsValid_IDPIDL(pidlSecondLast) &&
  4841. EQUIV_IDSIGN(IDDPIDL_SIGN,
  4842. (reinterpret_cast<LPBASEPIDL>
  4843. (const_cast<LPITEMIDLIST>(pidlSecondLast)))->usSign) &&
  4844. (_IsValid_HEIPIDL(pidlLast)) )
  4845. {
  4846. LPITEMIDLIST pidlViewTemp = NULL;
  4847. hr = CreateSpecialViewPidl(VIEWPIDL_ORDER_SITE, &pidlViewTemp);
  4848. if (SUCCEEDED(hr) && pidlViewTemp)
  4849. {
  4850. hr = SHILCombine(pidlViewTemp, pidlSecondLast, ppidlOut);
  4851. ILFree(pidlViewTemp);
  4852. }
  4853. }
  4854. else
  4855. hr = E_INVALIDARG;
  4856. return hr;
  4857. }
  4858. // caller LocalFree's *ppidlOut
  4859. // returned pidl should be combined with the history folder location
  4860. HRESULT _ConvertStdPidlToViewPidl_OrderToday(LPITEMIDLIST pidlLast,
  4861. LPITEMIDLIST *ppidlOut,
  4862. USHORT usViewType = VIEWPIDL_ORDER_TODAY)
  4863. {
  4864. HRESULT hr = E_FAIL;
  4865. // painfully construct the final pidl by concatenating the little
  4866. // peices [special_viewpidl, heipidl]
  4867. if (_IsValid_HEIPIDL(pidlLast))
  4868. {
  4869. LPHEIPIDL phei = reinterpret_cast<LPHEIPIDL>(pidlLast);
  4870. LPITEMIDLIST pidlViewTemp = NULL;
  4871. hr = CreateSpecialViewPidl(usViewType, &pidlViewTemp);
  4872. if (SUCCEEDED(hr) && pidlViewTemp)
  4873. {
  4874. hr = SHILCombine(pidlViewTemp, reinterpret_cast<LPITEMIDLIST>(phei), ppidlOut);
  4875. ILFree(pidlViewTemp);
  4876. }
  4877. }
  4878. else
  4879. hr = E_INVALIDARG;
  4880. return hr;
  4881. }
  4882. // remember to ILFree pidl
  4883. HRESULT ConvertStandardHistPidlToSpecialViewPidl(LPCITEMIDLIST pidlStandardHist,
  4884. USHORT usViewType,
  4885. LPITEMIDLIST *ppidlOut) {
  4886. if (!pidlStandardHist || !ppidlOut)
  4887. {
  4888. return E_FAIL;
  4889. }
  4890. HRESULT hr = E_FAIL;
  4891. UINT uPidlCount;
  4892. LPITEMIDLIST *ppidlSplit = _SplitPidlEasy(pidlStandardHist, uPidlCount);
  4893. /* Standard Hist Pidl should be in this form:
  4894. * [IDIPIDL, IDDPIDL, HEIPIDL]
  4895. * ex: [Today, foo.com, http://foo.com/bar.html]
  4896. */
  4897. if (ppidlSplit)
  4898. {
  4899. if (uPidlCount >= 3)
  4900. {
  4901. LPITEMIDLIST pidlTemp = NULL;
  4902. switch(usViewType)
  4903. {
  4904. case VIEWPIDL_ORDER_FREQ:
  4905. case VIEWPIDL_ORDER_TODAY:
  4906. hr = _ConvertStdPidlToViewPidl_OrderToday(ppidlSplit[uPidlCount - 1],
  4907. &pidlTemp, usViewType);
  4908. break;
  4909. case VIEWPIDL_ORDER_SITE:
  4910. hr = _ConvertStdPidlToViewPidl_OrderSite(ppidlSplit[uPidlCount - 2],
  4911. ppidlSplit[uPidlCount - 1],
  4912. &pidlTemp);
  4913. break;
  4914. default:
  4915. hr = E_INVALIDARG;
  4916. }
  4917. if (SUCCEEDED(hr) && pidlTemp)
  4918. {
  4919. *ppidlOut = pidlTemp;
  4920. hr = (*ppidlOut ? S_OK : E_OUTOFMEMORY);
  4921. }
  4922. }
  4923. else {
  4924. hr = E_INVALIDARG;
  4925. }
  4926. LocalFree(ppidlSplit);
  4927. ppidlSplit = NULL;
  4928. }
  4929. else
  4930. hr = E_OUTOFMEMORY;
  4931. return hr;
  4932. }
  4933. // START OF JCORDELL CODE
  4934. #ifdef DEBUG
  4935. BOOL ValidBeginningOfDay( const SYSTEMTIME *pTime )
  4936. {
  4937. return pTime->wHour == 0 && pTime->wMinute == 0 && pTime->wSecond == 0 && pTime->wMilliseconds == 0;
  4938. }
  4939. BOOL ValidBeginningOfDay( const FILETIME *pTime )
  4940. {
  4941. SYSTEMTIME sysTime;
  4942. FileTimeToSystemTime( pTime, &sysTime );
  4943. return ValidBeginningOfDay( &sysTime);
  4944. }
  4945. #endif
  4946. void _CommonTimeFormatProcessing(const FILETIME *pStartTime, const FILETIME *pEndTime,
  4947. int *pdays_delta,
  4948. int *pdays_delta_from_today,
  4949. TCHAR *szStartDateBuffer,
  4950. DWORD dwStartDateBuffer,
  4951. SYSTEMTIME *pSysStartTime,
  4952. SYSTEMTIME *pSysEndTime,
  4953. LCID lcidUI)
  4954. {
  4955. SYSTEMTIME sysStartTime, sysEndTime, sysLocalTime;
  4956. FILETIME fileLocalTime;
  4957. // ASSERTS
  4958. ASSERT(ValidBeginningOfDay( pStartTime ));
  4959. ASSERT(ValidBeginningOfDay( pEndTime ));
  4960. // Get times in SYSTEMTIME format
  4961. FileTimeToSystemTime( pStartTime, &sysStartTime );
  4962. FileTimeToSystemTime( pEndTime, &sysEndTime );
  4963. // Get string date of start time
  4964. GetDateFormat(lcidUI, DATE_SHORTDATE, &sysStartTime, NULL, szStartDateBuffer, dwStartDateBuffer);
  4965. // Get FILETIME of the first instant of today
  4966. GetLocalTime( &sysLocalTime );
  4967. sysLocalTime.wHour = sysLocalTime.wMinute = sysLocalTime.wSecond = sysLocalTime.wMilliseconds = 0;
  4968. SystemTimeToFileTime( &sysLocalTime, &fileLocalTime );
  4969. *pdays_delta = DAYS_DIFF(pEndTime, pStartTime);
  4970. *pdays_delta_from_today = DAYS_DIFF(&fileLocalTime, pStartTime);
  4971. *pSysEndTime = sysEndTime;
  4972. *pSysStartTime = sysStartTime;
  4973. }
  4974. // this wrapper allows the FormatMessage wrapper to make use of FormatMessageLite, which
  4975. // does not require a code page for correct operation on Win9x. The original FormatMessage calls
  4976. // used the FORMAT_MESSAGE_MAX_WIDTH_MASK (which is not relevant to our strings), and used an array
  4977. // of arguments. Now we make the call compatible with FormatMessageLite.
  4978. DWORD FormatMessageLiteWrapperW(LPCWSTR lpSource, LPWSTR lpBuffer, DWORD nSize, ...)
  4979. {
  4980. va_list arguments;
  4981. va_start(arguments, nSize);
  4982. DWORD dwRet = FormatMessage(FORMAT_MESSAGE_FROM_STRING, lpSource, 0, 0, lpBuffer, nSize, &arguments);
  4983. va_end(arguments);
  4984. return dwRet;
  4985. }
  4986. BOOL GetTooltipForTimeInterval( const FILETIME *pStartTime, const FILETIME *pEndTime,
  4987. TCHAR *szBuffer, int cbBufferLength )
  4988. {
  4989. SYSTEMTIME sysStartTime, sysEndTime;
  4990. int days_delta; // number of days between start and end time
  4991. int days_delta_from_today; // number of days between today and start time
  4992. TCHAR szStartDateBuffer[64];
  4993. TCHAR szDayBuffer[64];
  4994. TCHAR szEndDateBuffer[64];
  4995. TCHAR *args[2];
  4996. TCHAR szFmt[64];
  4997. int idFormat;
  4998. LANGID lidUI;
  4999. LCID lcidUI;
  5000. lidUI = MLGetUILanguage();
  5001. lcidUI = MAKELCID(lidUI, SORT_DEFAULT);
  5002. _CommonTimeFormatProcessing(pStartTime,
  5003. pEndTime,
  5004. &days_delta,
  5005. &days_delta_from_today,
  5006. szStartDateBuffer,
  5007. ARRAYSIZE(szStartDateBuffer),
  5008. &sysStartTime,
  5009. &sysEndTime,
  5010. lcidUI);
  5011. if ( days_delta == 1 ) {
  5012. args[0] = &szDayBuffer[0];
  5013. idFormat = IDS_DAYTOOLTIP;
  5014. // day sized bucket
  5015. if ( days_delta_from_today == 0 ) {
  5016. // today
  5017. szDayBuffer[0] = 0;
  5018. idFormat = IDS_TODAYTOOLTIP;
  5019. }
  5020. else if ( days_delta_from_today > 0 && days_delta_from_today < 7 )
  5021. {
  5022. // within the last week, put day of week
  5023. GetDateFormat(lcidUI, 0, &sysStartTime, TEXT("dddd"), szDayBuffer, ARRAYSIZE(szDayBuffer));
  5024. }
  5025. else {
  5026. // just a plain day bucket
  5027. StrCpyN( szDayBuffer, szStartDateBuffer, ARRAYSIZE(szDayBuffer) );
  5028. }
  5029. }
  5030. else if ( days_delta == 7 && sysStartTime.wDayOfWeek == 1 ) {
  5031. // week-size bucket starting on a Monday
  5032. args[0] = &szStartDateBuffer[0];
  5033. // make is point to the first string for safety sake. This will be ignored by wsprintf
  5034. args[1] = args[0];
  5035. idFormat = IDS_WEEKTOOLTIP;
  5036. }
  5037. else {
  5038. // non-standard bucket (not exactly a week and not exactly a day)
  5039. args[0] = &szStartDateBuffer[0];
  5040. args[1] = &szEndDateBuffer[0];
  5041. idFormat = IDS_MISCTOOLTIP;
  5042. GetDateFormat(lcidUI, DATE_SHORTDATE, &sysEndTime, NULL, szEndDateBuffer, ARRAYSIZE(szEndDateBuffer) );
  5043. }
  5044. MLLoadString(idFormat, szFmt, ARRAYSIZE(szFmt));
  5045. // NOTE, if the second parameter is not needed by the szFMt, then it will be ignored by wnsprintf
  5046. if (idFormat == IDS_DAYTOOLTIP)
  5047. wnsprintf(szBuffer, cbBufferLength, szFmt, args[0]);
  5048. else
  5049. FormatMessageLiteWrapperW(szFmt, szBuffer, cbBufferLength, args[0], args[1]);
  5050. return TRUE;
  5051. }
  5052. BOOL GetDisplayNameForTimeInterval( const FILETIME *pStartTime, const FILETIME *pEndTime,
  5053. LPTSTR szBuffer, int cbBufferLength)
  5054. {
  5055. SYSTEMTIME sysStartTime, sysEndTime;
  5056. int days_delta; // number of days between start and end time
  5057. int days_delta_from_today; // number of days between today and start time
  5058. TCHAR szStartDateBuffer[64];
  5059. LANGID lidUI;
  5060. LCID lcidUI;
  5061. lidUI = MLGetUILanguage();
  5062. lcidUI = MAKELCID(lidUI, SORT_DEFAULT);
  5063. _CommonTimeFormatProcessing(pStartTime,
  5064. pEndTime,
  5065. &days_delta,
  5066. &days_delta_from_today,
  5067. szStartDateBuffer,
  5068. ARRAYSIZE(szStartDateBuffer),
  5069. &sysStartTime,
  5070. &sysEndTime,
  5071. lcidUI);
  5072. if ( days_delta == 1 ) {
  5073. // day sized bucket
  5074. if ( days_delta_from_today == 0 ) {
  5075. // today
  5076. MLLoadString(IDS_TODAY, szBuffer, cbBufferLength/sizeof(TCHAR));
  5077. }
  5078. else if ( days_delta_from_today > 0 && days_delta_from_today < 7 )
  5079. {
  5080. // within the last week, put day of week
  5081. int nResult = GetDateFormat(lcidUI, 0, &sysStartTime, TEXT("dddd"), szBuffer, cbBufferLength);
  5082. ASSERT(nResult);
  5083. }
  5084. else {
  5085. // just a plain day bucket
  5086. StrCpyN( szBuffer, szStartDateBuffer, cbBufferLength );
  5087. }
  5088. }
  5089. else if ( days_delta == 7 && sysStartTime.wDayOfWeek == 1 ) {
  5090. // week-size bucket starting on a Monday
  5091. TCHAR szFmt[64];
  5092. int nWeeksAgo = days_delta_from_today / 7;
  5093. if (nWeeksAgo == 1) {
  5094. // print "Last Week"
  5095. MLLoadString(IDS_LASTWEEK, szBuffer, cbBufferLength/sizeof(TCHAR));
  5096. }
  5097. else {
  5098. // print "n Weeks Ago"
  5099. MLLoadString(IDS_WEEKSAGO, szFmt, ARRAYSIZE(szFmt));
  5100. wnsprintf(szBuffer, cbBufferLength, szFmt, nWeeksAgo);
  5101. }
  5102. }
  5103. else {
  5104. // non-standard bucket (not exactly a week and not exactly a day)
  5105. TCHAR szFmt[64];
  5106. TCHAR szEndDateBuffer[64];
  5107. TCHAR *args[2];
  5108. args[0] = &szStartDateBuffer[0];
  5109. args[1] = &szEndDateBuffer[0];
  5110. GetDateFormat(lcidUI, DATE_SHORTDATE, &sysEndTime, NULL, szEndDateBuffer, ARRAYSIZE(szEndDateBuffer) );
  5111. MLLoadString(IDS_FROMTO, szFmt, ARRAYSIZE(szFmt));
  5112. FormatMessageLiteWrapperW(szFmt, szBuffer, cbBufferLength, args[0], args[1]);
  5113. }
  5114. return TRUE;
  5115. }
  5116. // END OF JCORDELL CODE
  5117. // if !fOleMalloc, use LocalAlloc for speed // ok to pass in NULL for lpStatURL
  5118. LPHEIPIDL _CreateHCacheFolderPidl(BOOL fOleMalloc, LPCTSTR pszUrl, FILETIME ftModified, LPSTATURL lpStatURL,
  5119. __int64 llPriority/* = 0*/, DWORD dwNumHits/* = 0*/) // used in freqnecy view
  5120. {
  5121. USHORT usUrlSize = (USHORT)((lstrlen(pszUrl) + 1) * sizeof(TCHAR));
  5122. DWORD dwSize = sizeof(HEIPIDL) + usUrlSize;
  5123. USHORT usTitleSize = 0;
  5124. BOOL fUseTitle = (lpStatURL && lpStatURL->pwcsTitle && _TitleIsGood(lpStatURL->pwcsTitle));
  5125. if (fUseTitle)
  5126. usTitleSize = (USHORT)((lstrlen(lpStatURL->pwcsTitle) + 1) * sizeof(WCHAR));
  5127. dwSize += usTitleSize;
  5128. #if defined(UNIX)
  5129. dwSize = ALIGN4(dwSize);
  5130. #endif
  5131. LPHEIPIDL pheip = (LPHEIPIDL)_CreateBaseFolderPidl(fOleMalloc, dwSize, HEIPIDL_SIGN);
  5132. if (pheip)
  5133. {
  5134. pheip->usUrl = sizeof(HEIPIDL);
  5135. pheip->usFlags = lpStatURL ? HISTPIDL_VALIDINFO : 0;
  5136. pheip->usTitle = fUseTitle ? pheip->usUrl+usUrlSize :0;
  5137. pheip->ftModified = ftModified;
  5138. pheip->llPriority = llPriority;
  5139. pheip->dwNumHits = dwNumHits;
  5140. if (lpStatURL)
  5141. {
  5142. pheip->ftLastVisited = lpStatURL->ftLastVisited;
  5143. #ifndef UNIX
  5144. if (fUseTitle)
  5145. StrCpyN((LPTSTR)(((BYTE*)pheip)+pheip->usTitle), lpStatURL->pwcsTitle, usTitleSize / sizeof(TCHAR));
  5146. #else
  5147. // IEUNIX : BUG BUG _CreateBaseFolderPidl() uses lstrlenA
  5148. // while creating the pidl.
  5149. if (fUseTitle)
  5150. StrCpyN((LPTSTR)(((BYTE*)pheip)+pheip->usTitle), lpStatURL->pwcsTitle, usTitleSize / sizeof(TCHAR));
  5151. #endif
  5152. }
  5153. else {
  5154. //mm98: not so sure about the semantics on this one -- but this call
  5155. // with lpstaturl NULL (called from _NotifyWrite<--_WriteHistory<--WriteHistory<--CUrlHistory::_WriteToHistory
  5156. // makes for an uninitialised "Last Visited Member" which wreaks havoc
  5157. // when we want to order URLs by last visited
  5158. pheip->ftLastVisited = ftModified;
  5159. }
  5160. StrCpyN((LPTSTR)(((BYTE*)pheip)+pheip->usUrl), pszUrl, usUrlSize / sizeof(TCHAR));
  5161. }
  5162. return pheip;
  5163. }
  5164. // if !fOleMalloc, use LocalAlloc for speed
  5165. LPBASEPIDL _CreateIdCacheFolderPidl(BOOL fOleMalloc, USHORT usSign, LPCTSTR szId)
  5166. {
  5167. DWORD cch = lstrlen(szId) + 1;
  5168. DWORD dwSize = cch * sizeof(TCHAR);
  5169. dwSize += sizeof(BASEPIDL);
  5170. LPBASEPIDL pceip = _CreateBaseFolderPidl(fOleMalloc, dwSize, usSign);
  5171. if (pceip)
  5172. {
  5173. // dst <- src
  5174. // since pcei is ID type sign, _GetURLTitle points into correct place in pcei
  5175. StrCpyN((LPTSTR)_GetURLTitle(pceip), szId, cch);
  5176. }
  5177. return pceip;
  5178. }
  5179. // if !fOleAlloc, use LocalAlloc for speed
  5180. LPBASEPIDL _CreateBaseFolderPidl(BOOL fOleAlloc, DWORD dwSize, USHORT usSign)
  5181. {
  5182. LPBASEPIDL pcei;
  5183. DWORD dwTotalSize;
  5184. // Note: the buffer size returned by wininet includes INTERNET_CACHE_ENTRY_INFO
  5185. dwTotalSize = sizeof(BASEPIDL) + dwSize;
  5186. #if defined(UNIX)
  5187. dwTotalSize = ALIGN4(dwTotalSize);
  5188. #endif
  5189. if (fOleAlloc)
  5190. {
  5191. pcei = (LPBASEPIDL)OleAlloc(dwTotalSize);
  5192. if (pcei != NULL)
  5193. {
  5194. memset(pcei, 0, dwTotalSize);
  5195. }
  5196. }
  5197. else
  5198. {
  5199. pcei = (LPBASEPIDL) LocalAlloc(GPTR, dwTotalSize);
  5200. // LocalAlloc zero inits
  5201. }
  5202. if (pcei)
  5203. {
  5204. pcei->cb = (USHORT)(dwTotalSize - sizeof(USHORT));
  5205. pcei->usSign = usSign;
  5206. }
  5207. return pcei;
  5208. }
  5209. // returns a pidl (viewpidl)
  5210. // You must free the pidl with ILFree
  5211. // cbExtra - count of how much to allocate at the end of the pidl
  5212. // ppbExtra - pointer to buffer at end of pidl that is cbExtra big
  5213. HRESULT CreateSpecialViewPidl(USHORT usViewType, LPITEMIDLIST* ppidlOut, UINT cbExtra /* = 0*/, LPBYTE *ppbExtra /* = NULL*/)
  5214. {
  5215. HRESULT hr;
  5216. if (ppidlOut) {
  5217. *ppidlOut = NULL;
  5218. ASSERT((usViewType > 0) &&
  5219. ((usViewType <= VIEWPIDL_ORDER_MAX) ||
  5220. (usViewType == VIEWPIDL_SEARCH)));
  5221. // Tack another ITEMIDLIST on the end to be the empty "null terminating" pidl
  5222. USHORT cbSize = sizeof(VIEWPIDL) + cbExtra + sizeof(ITEMIDLIST);
  5223. // use the shell's allocator because folks will want to free it with ILFree
  5224. VIEWPIDL *viewpidl = ((VIEWPIDL *)SHAlloc(cbSize));
  5225. if (viewpidl) {
  5226. // this should also make the "next" pidl empty
  5227. memset(viewpidl, 0, cbSize);
  5228. viewpidl->cb = (USHORT)(sizeof(VIEWPIDL) + cbExtra);
  5229. viewpidl->usSign = VIEWPIDL_SIGN;
  5230. viewpidl->usViewType = usViewType;
  5231. viewpidl->usExtra = 0; // currently unused
  5232. if (ppbExtra)
  5233. *ppbExtra = ((LPBYTE)viewpidl) + sizeof(VIEWPIDL);
  5234. *ppidlOut = (LPITEMIDLIST)viewpidl;
  5235. hr = S_OK;
  5236. }
  5237. else
  5238. hr = E_OUTOFMEMORY;
  5239. }
  5240. else
  5241. hr = E_INVALIDARG;
  5242. return hr;
  5243. }