Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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