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.

2962 lines
98 KiB

  1. #include "shellprv.h"
  2. #include "ids.h"
  3. #include "findhlp.h"
  4. #include "fstreex.h"
  5. #include "findfilter.h"
  6. #include "prop.h"
  7. #include "filtgrep.h"
  8. #include "shstr.h"
  9. #include "mtpt.h"
  10. #include "idltree.h"
  11. #include "enumidlist.h"
  12. // Can't put this in varutil.cpp since a delay-load thunk for VariantTimeToDosDateTime
  13. // pulls in the floating point init code, which pulls in _wWinMainCRTStartup which
  14. // requires a _wWinMain which shlwapi does not have
  15. //
  16. STDAPI VariantToDosDateTime(VARIANT varIn, WORD *pwDate, WORD *pwTime)
  17. {
  18. VARIANT varResult = {0};
  19. HRESULT hr = VariantChangeType(&varResult, &varIn, 0, VT_DATE);
  20. if (SUCCEEDED(hr))
  21. {
  22. VariantTimeToDosDateTime(varResult.date, pwDate, pwTime);
  23. }
  24. return hr;
  25. }
  26. STDAPI InitVariantFromDosDateTime(VARIANT *pvar, WORD wDate, WORD wTime)
  27. {
  28. pvar->vt = VT_DATE;
  29. return DosDateTimeToVariantTime(wDate, wTime, &pvar->date) ? S_OK : S_FALSE;
  30. }
  31. // {DBEC1000-6AB8-11d1-B758-00A0C90564FE}
  32. const IID IID_IFindFilter = {0xdbec1000, 0x6ab8, 0x11d1, {0xb7, 0x58, 0x0, 0xa0, 0xc9, 0x5, 0x64, 0xfe}};
  33. // constants to define types of date we are searching on
  34. #define DFF_DATE_ALL (IDD_MDATE_ALL-IDD_MDATE_ALL)
  35. #define DFF_DATE_DAYS (IDD_MDATE_DAYS-IDD_MDATE_ALL)
  36. #define DFF_DATE_MONTHS (IDD_MDATE_MONTHS-IDD_MDATE_ALL)
  37. #define DFF_DATE_BETWEEN (IDD_MDATE_BETWEEN-IDD_MDATE_ALL)
  38. #define DFF_DATE_RANGEMASK 0x00ff
  39. // Define new criteria to be saved in file...
  40. #define DFSC_SEARCHFOR 0x5000
  41. #define DFSLI_VER 0
  42. #define DFSLI_TYPE_PIDL 0 // Pidl is streamed after this
  43. #define DFSLI_TYPE_STRING 1 // cb follows this for length then string...
  44. // Document folders and children - Warning we assume the order of items after Document Folders
  45. #define DFSLI_TYPE_DOCUMENTFOLDERS 0x10
  46. #define DFSLI_TYPE_DESKTOP 0x11
  47. #define DFSLI_TYPE_PERSONAL 0x12
  48. // My computer and children...
  49. #define DFSLI_TYPE_MYCOMPUTER 0x20
  50. #define DFSLI_TYPE_LOCALDRIVES 0x21
  51. #define DFPAGE_INIT 0x0001 /* This page has been initialized */
  52. #define DFPAGE_CHANGE 0x0002 /* The user has modified the page */
  53. #define SFGAO_FS_SEARCH (SFGAO_FILESYSANCESTOR | SFGAO_FOLDER)
  54. // Use same enum and string table between updatefield and getting the constraints
  55. // back out...
  56. typedef enum
  57. {
  58. CDFFUFE_IndexedSearch = 0,
  59. CDFFUFE_LookIn,
  60. CDFFUFE_IncludeSubFolders,
  61. CDFFUFE_Named,
  62. CDFFUFE_ContainingText,
  63. CDFFUFE_FileType,
  64. CDFFUFE_WhichDate,
  65. CDFFUFE_DateLE,
  66. CDFFUFE_DateGE,
  67. CDFFUFE_DateNDays,
  68. CDFFUFE_DateNMonths,
  69. CDFFUFE_SizeLE,
  70. CDFFUFE_SizeGE,
  71. CDFFUFE_TextCaseSen,
  72. CDFFUFE_TextReg,
  73. CDFFUFE_SearchSlowFiles,
  74. CDFFUFE_QueryDialect,
  75. CDFFUFE_WarningFlags,
  76. CDFFUFE_StartItem,
  77. CDFFUFE_SearchSystemDirs,
  78. CDFFUFE_SearchHidden,
  79. } CDFFUFE;
  80. static const struct
  81. {
  82. LPCWSTR pwszField;
  83. int cdffufe;
  84. }
  85. s_cdffuf[] = // Warning: index of fields is used below in case...
  86. {
  87. {L"IndexedSearch", CDFFUFE_IndexedSearch},
  88. {L"LookIn", CDFFUFE_LookIn}, // VARIANT: pidl, string or IEnumIDList object
  89. {L"IncludeSubFolders", CDFFUFE_IncludeSubFolders},
  90. {L"Named", CDFFUFE_Named},
  91. {L"ContainingText", CDFFUFE_ContainingText},
  92. {L"FileType", CDFFUFE_FileType},
  93. {L"WhichDate", CDFFUFE_WhichDate},
  94. {L"DateLE", CDFFUFE_DateLE},
  95. {L"DateGE", CDFFUFE_DateGE},
  96. {L"DateNDays", CDFFUFE_DateNDays},
  97. {L"DateNMonths", CDFFUFE_DateNMonths},
  98. {L"SizeLE", CDFFUFE_SizeLE},
  99. {L"SizeGE", CDFFUFE_SizeGE},
  100. {L"CaseSensitive", CDFFUFE_TextCaseSen},
  101. {L"RegularExpressions", CDFFUFE_TextReg},
  102. {L"SearchSlowFiles", CDFFUFE_SearchSlowFiles},
  103. {L"QueryDialect", CDFFUFE_QueryDialect},
  104. {L"WarningFlags", CDFFUFE_WarningFlags}, /*DFW_xxx bits*/
  105. {L"StartItem", CDFFUFE_LookIn}, // VARIANT: pidl, string or IEnumIDList object
  106. {L"SearchSystemDirs", CDFFUFE_SearchSystemDirs},
  107. {L"SearchHidden", CDFFUFE_SearchHidden},
  108. };
  109. // internal support functions
  110. STDAPI_(BOOL) SetupWildCardingOnFileSpec(LPTSTR pszSpecIn, LPTSTR *ppszSpecOut);
  111. // data filter object
  112. class CFindFilter : public IFindFilter
  113. {
  114. public:
  115. CFindFilter();
  116. // IUnknown
  117. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  118. STDMETHODIMP_(ULONG) AddRef();
  119. STDMETHODIMP_(ULONG) Release();
  120. // IFindFilter
  121. STDMETHODIMP GetStatusMessageIndex(UINT uContext, UINT *puMsgIndex);
  122. STDMETHODIMP GetFolderMergeMenuIndex(UINT *puBGMainMergeMenu, UINT *puBGPopupMergeMenu);
  123. STDMETHODIMP FFilterChanged();
  124. STDMETHODIMP GenerateTitle(LPTSTR *ppszTile, BOOL fFileName);
  125. STDMETHODIMP PrepareToEnumObjects(HWND hwnd, DWORD * pdwFlags);
  126. STDMETHODIMP ClearSearchCriteria();
  127. STDMETHODIMP EnumObjects(IShellFolder *psf, LPCITEMIDLIST pidlStart, DWORD grfFlags, int iColSort,
  128. LPTSTR pszProgressText, IRowsetWatchNotify *prwn, IFindEnum **ppdfenum);
  129. STDMETHODIMP GetColumnsFolder(IShellFolder2 **ppsf);
  130. STDMETHODIMP_(BOOL) MatchFilter(IShellFolder *psf, LPCITEMIDLIST pidl);
  131. STDMETHODIMP SaveCriteria(IStream * pstm, WORD fCharType);
  132. STDMETHODIMP RestoreCriteria(IStream * pstm, int cCriteria, WORD fCharType);
  133. STDMETHODIMP DeclareFSNotifyInterest(HWND hwnd, UINT uMsg);
  134. STDMETHODIMP GetColSaveStream(WPARAM wParam, LPSTREAM *ppstm);
  135. STDMETHODIMP GenerateQueryRestrictions(LPWSTR *ppwszQuery, DWORD *pdwGQRFlags);
  136. STDMETHODIMP ReleaseQuery();
  137. STDMETHODIMP UpdateField(LPCWSTR pszField, VARIANT vValue);
  138. STDMETHODIMP ResetFieldsToDefaults();
  139. STDMETHODIMP GetItemContextMenu(HWND hwndOwner, IFindFolder* pdfFolder, IContextMenu** ppcm);
  140. STDMETHODIMP GetDefaultSearchGUID(IShellFolder2 *psf2, LPGUID lpGuid);
  141. STDMETHODIMP EnumSearches(IShellFolder2 *psf2, LPENUMEXTRASEARCH *ppenum);
  142. STDMETHODIMP GetSearchFolderClassId(LPGUID lpGuid);
  143. STDMETHODIMP GetNextConstraint(VARIANT_BOOL fReset, BSTR *pName, VARIANT *pValue, VARIANT_BOOL *pfFound);
  144. STDMETHODIMP GetQueryLanguageDialect(ULONG * pulDialect);
  145. STDMETHODIMP GetWarningFlags(DWORD *pdwWarningFlags);
  146. STDMETHODIMP InitSelf(void);
  147. STDMETHODIMP_(BOOL) TopLevelOnly() const { return _fTopLevelOnly; }
  148. private:
  149. ~CFindFilter();
  150. HRESULT _GetDetailsFolder();
  151. void _GenerateQuery(LPWSTR pwszQuery, DWORD *pcchQuery);
  152. void _UpdateTypeField(const VARIANT *pvar);
  153. static int _SaveCriteriaItem(IStream * pstm, WORD wNum, LPCTSTR psz, WORD fCharType);
  154. DWORD _QueryDosDate(BOOL *pbFirst, DWORD *pcchBuf, LPWSTR *ppszCurrent, WORD wDate, BOOL bBefore);
  155. HRESULT _GetPropertyUI(IPropertyUI **pppui);
  156. DWORD _CIQuerySize(BOOL *pbFirst, DWORD *pcchBuf, LPWSTR *ppszCurrent, ULONGLONG ullSize, int iSizeType);
  157. DWORD _CIQueryFilePatterns(BOOL *pbFirst, DWORD *pcchBuf, LPWSTR *ppszCurrent, LPCWSTR pszFilePatterns);
  158. DWORD _CIQueryTextPatterns(BOOL *pbFirst, DWORD *pcchBuf, LPWSTR *ppszCurrent, LPWSTR pszText, BOOL bTextReg);
  159. DWORD _CIQueryShellSettings(BOOL *pbFirst, DWORD *pcchBuf, LPWSTR *ppszCurrent);
  160. DWORD _CIQueryIndex(BOOL *pbFirst, DWORD *pcchBuf, LPWSTR *ppszCurrent, LPWSTR pszText);
  161. DWORD _AddToQuery(LPWSTR *ppszBuf, DWORD *pcchBuf, LPWSTR pszAdd);
  162. WORD _GetTodaysDosDateMinusNMonths(int nMonths);
  163. WORD _GetTodaysDosDateMinusNDays(int nDays);
  164. HRESULT _ScopeEnumerator(IEnumIDList **ppenum);
  165. void _ResetRoots();
  166. LONG _cRef;
  167. IFindEnum *_penumAsync; // Added support for Query results to be async...
  168. // Data associated with the file name.
  169. LPTSTR _pszFileSpec; // $$ the one we do compares with
  170. LPTSTR _pszSpecs; // same as pszFileSpec but with '\0's for ';'s
  171. LPTSTR * _apszFileSpecs; // pointers into pszSpecs for each token
  172. int _cFileSpecs; // count of specs
  173. TCHAR _szPath[MAX_URL_STRING]; // Location of where to start search from
  174. TCHAR _szUserInputFileSpec[MAX_PATH]; // File pattern.
  175. TCHAR _szText[128]; // Limit text to max editable size
  176. BOOL _fTopLevelOnly; // Search on top level only?
  177. BOOL _fSearchHidden; // $$ Should we show all files?
  178. BOOL _fFilterChanged; // Something in the filter changed.
  179. BOOL _fWeRestoredSomeCriteria; // We need to initilize the pages...
  180. BOOL _fANDSearch; // Perform search using AND vs OR?
  181. // Fields associated with the file type
  182. BOOL _fTypeChanged; // Type changed;
  183. int _iType; // Index of the type.
  184. TCHAR _szTypeName[80]; // The display name for type
  185. SHSTR _strTypeFilePatterns;// $$ The file patterns associated with type
  186. LPTSTR _pszIndexedSearch; // what to search for... (Maybe larger than MAX_PATH because it's a list of paths.
  187. ULONG _ulQueryDialect; // ISQLANG_V1 or ISQLANG_V2
  188. DWORD _dwWarningFlags; // Warning bits (DFW_xxx).
  189. CFilterGrep _filtgrep;
  190. int _iSizeType; // $$ What type of size 0 - none, 1 > 2 <
  191. ULONGLONG _ullSize; // $$ Size comparison
  192. WORD _wDateType; // $$ 0 - none, 1 days before, 2 months before...
  193. WORD _wDateValue; // (Num of months or days)
  194. WORD _dateModifiedBefore; // $$
  195. WORD _dateModifiedAfter; // $$
  196. BOOL _fFoldersOnly; // $$ Are we searching for folders?
  197. BOOL _fTextCaseSen; // $$ Case sensitive searching...
  198. BOOL _fTextReg; // $$ regular expressions.
  199. BOOL _fSearchSlowFiles; // && probably missleading as file over a 300baud modem is also slow
  200. BOOL _fSearchSystemDirs; // Search system directories?
  201. int _iNextConstraint; // which constraint to look at next...
  202. HWND _hwnd; // for enum UI
  203. SHCOLUMNID _scidDate; // which date property to operate on
  204. SHCOLUMNID _scidSize; // which numeric property to operate on
  205. IEnumIDList *_penumRoots; // idlist enumerator for search roots
  206. IPropertyUI *_ppui;
  207. };
  208. // Target folder queue.
  209. class CFolderQueue
  210. {
  211. public:
  212. CFolderQueue() : _hdpa(NULL) {}
  213. ~CFolderQueue();
  214. HRESULT Add(IShellFolder *psf, LPCITEMIDLIST pidl);
  215. IShellFolder *Remove();
  216. private:
  217. HRESULT _AddFolder(IShellFolder *psf);
  218. HDPA _hdpa;
  219. };
  220. class CNamespaceEnum : public IFindEnum
  221. {
  222. public:
  223. CNamespaceEnum(IFindFilter *pfilter, IShellFolder *psf, IFindEnum *pdfEnumAsync,
  224. IEnumIDList *penumScopes, HWND hwnd, DWORD grfFlags, LPTSTR pszProgressText);
  225. // IUnknown
  226. STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
  227. STDMETHODIMP_(ULONG) AddRef();
  228. STDMETHODIMP_(ULONG) Release();
  229. // IFindEnum
  230. STDMETHODIMP Next(LPITEMIDLIST *ppidl, int *pcObjectSearched, int *pcFoldersSearched, BOOL *pfContinue, int *pState);
  231. STDMETHODIMP Skip(int celt) { return E_NOTIMPL; }
  232. STDMETHODIMP Reset() { return E_NOTIMPL; }
  233. STDMETHODIMP StopSearch();
  234. STDMETHODIMP_(BOOL) FQueryIsAsync();
  235. STDMETHODIMP GetAsyncCount(DBCOUNTITEM *pdwTotalAsync, int *pnPercentComplete, BOOL *pfQueryDone);
  236. STDMETHODIMP GetItemIDList(UINT iItem, LPITEMIDLIST *ppidl);
  237. STDMETHODIMP GetItemID(UINT iItem, DWORD *puWorkID);
  238. STDMETHODIMP SortOnColumn(UINT iCol, BOOL fAscending);
  239. private:
  240. ~CNamespaceEnum();
  241. BOOL _ShouldPushItem(LPCITEMIDLIST pidl);
  242. BOOL _IsSystemFolderByCLSID(LPCITEMIDLIST pidlFull);
  243. IShellFolder *_NextRootScope();
  244. LONG _cRef;
  245. IFindFilter *_pfilter; // parent filter object
  246. IFindFolder *_pff; // docfind folder interface over results
  247. HWND _hwnd; // for enum UI
  248. DWORD _grfFlags; // docfind enumeration flags (DFOO_xxx).
  249. // Recursion state...
  250. IShellFolder* _psf; // current shell folder
  251. LPITEMIDLIST _pidlFolder; // current shell folder, as pidl
  252. LPITEMIDLIST _pidlCurrentRootScope; // the last scope pulled out of _penumScopes
  253. IEnumIDList *_penum; // current enumerator.
  254. int _iFolder; // index of current folder in docfind results' folder list.
  255. // filter info...
  256. LPTSTR _pszProgressText; // path buffer pointer; caller-owned (evil!)
  257. // enumeration state
  258. IEnumIDList *_penumScopes; // Queue of target folders passed as arguments.
  259. CFolderQueue _queSubFolders; // Queue of subfolders to search in next recursive pass.
  260. // tree to store the exclude items (i.e. already seached)
  261. CIDLTree _treeExcludeFolders;
  262. // We may have an Async Enum that does some of the scopes...
  263. IFindEnum *_penumAsync;
  264. };
  265. // Constants used to keep track of how/why an item was added to the
  266. // exclude tree.
  267. enum
  268. {
  269. EXCLUDE_SEARCHED = 1,
  270. EXCLUDE_SYSTEMDIR = 2,
  271. };
  272. // Create the default filter for our find code... They should be completly
  273. // self contained...
  274. STDAPI CreateNameSpaceFindFilter(IFindFilter **ppff)
  275. {
  276. CFindFilter *pff;
  277. HRESULT hr = E_OUTOFMEMORY;
  278. pff = new CFindFilter();
  279. if (pff)
  280. {
  281. hr = pff->InitSelf();
  282. if (SUCCEEDED(hr))
  283. {
  284. *ppff = pff;
  285. }
  286. else
  287. {
  288. pff->Release();
  289. }
  290. }
  291. return hr;
  292. }
  293. HRESULT CFolderQueue::Add(IShellFolder *psf, LPCITEMIDLIST pidl)
  294. {
  295. IShellFolder *psfNew;
  296. HRESULT hr = SHBindToObject(psf, IID_X_PPV_ARG(IShellFolder, pidl, &psfNew));
  297. if (SUCCEEDED(hr))
  298. {
  299. hr = _AddFolder(psfNew);
  300. psfNew->Release();
  301. }
  302. return hr;
  303. }
  304. HRESULT CFolderQueue::_AddFolder(IShellFolder *psf)
  305. {
  306. HRESULT hr = E_OUTOFMEMORY;
  307. if (NULL == _hdpa)
  308. {
  309. _hdpa = DPA_Create(4);
  310. }
  311. if (_hdpa)
  312. {
  313. if (DPA_AppendPtr(_hdpa, psf) >= 0)
  314. {
  315. psf->AddRef();
  316. hr = S_OK;
  317. }
  318. }
  319. return hr;
  320. }
  321. // remove the folder from the queue
  322. // give the caller the ownership of this folder
  323. IShellFolder *CFolderQueue::Remove()
  324. {
  325. IShellFolder *psf = NULL;
  326. if (_hdpa && DPA_GetPtrCount(_hdpa))
  327. psf = (IShellFolder *)DPA_DeletePtr(_hdpa, 0);
  328. return psf;
  329. }
  330. CFolderQueue::~CFolderQueue()
  331. {
  332. if (_hdpa)
  333. {
  334. while (TRUE)
  335. {
  336. IShellFolder *psf = Remove();
  337. if (psf)
  338. {
  339. psf->Release();
  340. }
  341. else
  342. {
  343. break;
  344. }
  345. }
  346. DPA_Destroy(_hdpa);
  347. }
  348. }
  349. CFindFilter::CFindFilter() : _cRef(1), _wDateType(DFF_DATE_ALL), _ulQueryDialect(ISQLANG_V2)
  350. {
  351. }
  352. CFindFilter::~CFindFilter()
  353. {
  354. Str_SetPtr(&_pszFileSpec, NULL);
  355. Str_SetPtr(&_pszSpecs, NULL);
  356. LocalFree(_apszFileSpecs); // elements point to pszSpecs so no free for them
  357. Str_SetPtr(&_pszIndexedSearch, NULL);
  358. if (_ppui)
  359. _ppui->Release();
  360. if (_penumRoots)
  361. _penumRoots->Release();
  362. }
  363. STDMETHODIMP CFindFilter::InitSelf(void)
  364. {
  365. return _filtgrep.InitSelf();
  366. }
  367. STDMETHODIMP CFindFilter::QueryInterface(REFIID riid, void **ppv)
  368. {
  369. static const QITAB qit[] = {
  370. QITABENT(CFindFilter, IFindFilter),
  371. { 0 },
  372. };
  373. return QISearch(this, qit, riid, ppv);
  374. }
  375. STDMETHODIMP_(ULONG) CFindFilter::AddRef()
  376. {
  377. return InterlockedIncrement(&_cRef);
  378. }
  379. STDMETHODIMP_(ULONG) CFindFilter::Release()
  380. {
  381. ASSERT( 0 != _cRef );
  382. ULONG cRef = InterlockedDecrement(&_cRef);
  383. if ( 0 == cRef )
  384. {
  385. delete this;
  386. }
  387. return cRef;
  388. }
  389. // Retrieves the string resource index number that is proper for the
  390. // current type of search.
  391. STDMETHODIMP CFindFilter::GetStatusMessageIndex(UINT uContext, UINT *puMsgIndex)
  392. {
  393. // Currently context is not used
  394. *puMsgIndex = IDS_FILESFOUND;
  395. return S_OK;
  396. }
  397. // Retrieves which menu to load to merge for the folder
  398. STDMETHODIMP CFindFilter::GetFolderMergeMenuIndex(UINT *puBGMainMergeMenu, UINT *puBGPopupMergeMenu)
  399. {
  400. *puBGMainMergeMenu = POPUP_DOCFIND_MERGE;
  401. *puBGPopupMergeMenu = 0;
  402. return S_OK;
  403. }
  404. STDMETHODIMP CFindFilter::GetItemContextMenu(HWND hwndOwner, IFindFolder* pdfFolder, IContextMenu **ppcm)
  405. {
  406. return CFindItem_Create(hwndOwner, pdfFolder, ppcm);
  407. }
  408. STDMETHODIMP CFindFilter::GetDefaultSearchGUID(IShellFolder2 *psf2, GUID *pGuid)
  409. {
  410. return DefaultSearchGUID(pGuid);
  411. }
  412. STDMETHODIMP CFindFilter::EnumSearches(IShellFolder2 *psf2, IEnumExtraSearch **ppenum)
  413. {
  414. *ppenum = NULL;
  415. return E_NOTIMPL;
  416. }
  417. STDMETHODIMP CFindFilter::GetSearchFolderClassId(GUID *pGuid)
  418. {
  419. *pGuid = CLSID_DocFindFolder;
  420. return S_OK;
  421. }
  422. // (returns S_OK if nothing changed.)
  423. STDMETHODIMP CFindFilter::FFilterChanged()
  424. {
  425. BOOL fFilterChanged = _fFilterChanged;
  426. this->_fFilterChanged = FALSE;
  427. return fFilterChanged ? S_FALSE : S_OK;
  428. }
  429. // Generates the title given the current search criteria.
  430. STDMETHODIMP CFindFilter::GenerateTitle(LPTSTR *ppszTitle, BOOL fFileName)
  431. {
  432. BOOL fFilePattern;
  433. int iRes;
  434. TCHAR szFindName[80]; // German should not exceed this find: ->???
  435. LPTSTR pszFileSpec = _szUserInputFileSpec;
  436. LPTSTR pszText = _szText;
  437. //
  438. // Lets generate a title for the search. The title will depend on
  439. // the file patern(s), the type field and the containing text field
  440. // Complicate this a bit with the search for field...
  441. //
  442. fFilePattern = (pszFileSpec[0] != 0) &&
  443. (lstrcmp(pszFileSpec, c_szStarDotStar) != 0);
  444. if (!fFilePattern && (_penumAsync == NULL) && _pszIndexedSearch)
  445. {
  446. pszFileSpec = _pszIndexedSearch;
  447. fFilePattern = (pszFileSpec[0] != 0) &&
  448. (lstrcmp(pszFileSpec, c_szStarDotStar) != 0);
  449. }
  450. if ((pszText[0] == 0) && (_penumAsync != NULL) && _pszIndexedSearch)
  451. pszText = _pszIndexedSearch;
  452. // First see if there is a type field
  453. if (_iType > 0)
  454. {
  455. // We have a type field no check for content...
  456. if (pszText[0] != 0)
  457. {
  458. // There is text!
  459. // Should now use type but...
  460. // else see if the name field is not NULL and not *.*
  461. if (fFilePattern)
  462. iRes = IDS_FIND_TITLE_TYPE_NAME_TEXT;
  463. else
  464. iRes = IDS_FIND_TITLE_TYPE_TEXT;
  465. }
  466. else
  467. {
  468. // No type or text, see if file pattern
  469. // Containing not found, first search for type then named
  470. if (fFilePattern)
  471. iRes = IDS_FIND_TITLE_TYPE_NAME;
  472. else
  473. iRes = IDS_FIND_TITLE_TYPE;
  474. }
  475. }
  476. else
  477. {
  478. // No Type field ...
  479. // first see if there is text to be searched for!
  480. if (pszText[0] != 0)
  481. {
  482. // There is text!
  483. // Should now use type but...
  484. // else see if the name field is not NULL and not *.*
  485. if (fFilePattern)
  486. iRes = IDS_FIND_TITLE_NAME_TEXT;
  487. else
  488. iRes = IDS_FIND_TITLE_TEXT;
  489. }
  490. else
  491. {
  492. // No type or text, see if file pattern
  493. // Containing not found, first search for type then named
  494. if (fFilePattern)
  495. iRes = IDS_FIND_TITLE_NAME;
  496. else
  497. iRes = IDS_FIND_TITLE_ALL;
  498. }
  499. }
  500. // We put : in for first spot for title bar. For name creation
  501. // we remove it which will put the number at the end...
  502. if (!fFileName)
  503. LoadString(HINST_THISDLL, IDS_FIND_TITLE_FIND,
  504. szFindName, ARRAYSIZE(szFindName));
  505. *ppszTitle = ShellConstructMessageString(HINST_THISDLL,
  506. MAKEINTRESOURCE(iRes),
  507. fFileName? szNULL : szFindName,
  508. _szTypeName, pszFileSpec, pszText);
  509. return *ppszTitle ? S_OK : E_OUTOFMEMORY;
  510. }
  511. STDMETHODIMP CFindFilter::ClearSearchCriteria()
  512. {
  513. // Also clear out a few other fields...
  514. _szUserInputFileSpec[0] = 0;
  515. _iType = 0;
  516. _szText[0] = 0;
  517. return S_OK;
  518. }
  519. STDMETHODIMP CFindFilter::PrepareToEnumObjects(HWND hwnd, DWORD *pdwFlags)
  520. {
  521. *pdwFlags = 0; // start out empty
  522. _hwnd = hwnd; // used for the first enum so that can do UI (auth/insert media)
  523. // Update the flags and buffer strings
  524. if (!_fTopLevelOnly)
  525. *pdwFlags |= DFOO_INCLUDESUBDIRS;
  526. if (_fTextCaseSen)
  527. *pdwFlags |= DFOO_CASESEN;
  528. if (_fSearchSystemDirs)
  529. *pdwFlags |= DFOO_SEARCHSYSTEMDIRS;
  530. // Also get the shell state variables to see if we should show extensions and the like
  531. if (_fSearchHidden)
  532. *pdwFlags |= DFOO_SHOWALLOBJECTS;
  533. // Now lets generate the file patern we will ask the system to look for
  534. // Here is where we try to put some smarts into the file patterns stuff
  535. // It will go something like:
  536. // look between each; or , and see if there are any wild cards. If not
  537. // do something like *patern*.
  538. // Also if there is no search pattern or if it is * or *.*, set the
  539. // filter to NULL as to speed it up.
  540. //
  541. _fANDSearch = SetupWildCardingOnFileSpec(_szUserInputFileSpec, &_pszFileSpec);
  542. _cFileSpecs = 0;
  543. if (_pszFileSpec && _pszFileSpec[0])
  544. {
  545. Str_SetPtr(&_pszSpecs, _pszFileSpec);
  546. if (_pszSpecs)
  547. {
  548. int cTokens = 0;
  549. LPTSTR pszToken = _pszSpecs;
  550. // Count number of file spces
  551. while (pszToken)
  552. {
  553. // let's walk pszFileSpec to see how many specs we have...
  554. pszToken = StrChr(pszToken, TEXT(';'));
  555. // If delimiter, then advance past for next iteration
  556. if (pszToken)
  557. pszToken++;
  558. cTokens++;
  559. }
  560. if (cTokens)
  561. {
  562. // cleanup the previous search
  563. if (_apszFileSpecs)
  564. LocalFree(_apszFileSpecs);
  565. _apszFileSpecs = (LPTSTR *)LocalAlloc(LPTR, cTokens * sizeof(LPTSTR *));
  566. if (_apszFileSpecs)
  567. {
  568. _cFileSpecs = cTokens;
  569. pszToken = _pszSpecs;
  570. for (int i = 0; i < cTokens; i++)
  571. {
  572. _apszFileSpecs[i] = pszToken;
  573. pszToken = StrChr(pszToken, TEXT(';'));
  574. if (pszToken)
  575. *pszToken++ = 0;
  576. }
  577. }
  578. }
  579. }
  580. }
  581. _filtgrep.Reset();
  582. HRESULT hr = S_OK;
  583. if (_szText[0])
  584. {
  585. DWORD dwGrepFlags = FGIF_BLANKETGREP | FGIF_GREPFILENAME;
  586. if (*pdwFlags & DFOO_CASESEN)
  587. dwGrepFlags |= FGIF_CASESENSITIVE;
  588. hr = _filtgrep.Initialize(GetACP(), _szText, NULL, dwGrepFlags);
  589. }
  590. return hr;
  591. }
  592. STDMETHODIMP CFindFilter::GetColumnsFolder(IShellFolder2 **ppsf)
  593. {
  594. *ppsf = NULL;
  595. LPITEMIDLIST pidl;
  596. HRESULT hr = SHGetFolderLocation(NULL, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, NULL, 0, &pidl);
  597. if (SUCCEEDED(hr))
  598. {
  599. hr = SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder2, pidl, ppsf));
  600. ILFree(pidl);
  601. }
  602. return hr;
  603. }
  604. void FreePathArray(LPTSTR rgpszPaths[], UINT cPaths)
  605. {
  606. for (UINT i = 0; i < cPaths; i++)
  607. {
  608. LocalFree((HLOCAL)rgpszPaths[i]);
  609. rgpszPaths[i] = NULL;
  610. }
  611. }
  612. HRESULT NamesFromEnumIDList(IEnumIDList *penum, LPTSTR rgpszPaths[], UINT sizePaths, UINT *pcPaths)
  613. {
  614. *pcPaths = 0;
  615. ZeroMemory(rgpszPaths, sizeof(rgpszPaths[0]) * sizePaths);
  616. penum->Reset();
  617. LPITEMIDLIST pidl;
  618. while (S_OK == penum->Next(1, &pidl, NULL))
  619. {
  620. TCHAR szPath[MAX_PATH];
  621. if (SUCCEEDED(SHGetNameAndFlags(pidl, SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath), NULL)))
  622. {
  623. if ((*pcPaths) < sizePaths)
  624. {
  625. rgpszPaths[*pcPaths] = StrDup(szPath);
  626. if (rgpszPaths[*pcPaths])
  627. (*pcPaths)++;
  628. }
  629. }
  630. ILFree(pidl);
  631. }
  632. return S_OK;
  633. }
  634. void ClearIDArray(LPITEMIDLIST rgItems[], UINT cItems)
  635. {
  636. for (UINT i = 0; i < cItems; i++)
  637. {
  638. ILFree(rgItems[i]);
  639. rgItems[i] = NULL;
  640. }
  641. }
  642. #define MAX_ROOTS 32
  643. HRESULT FilterEnumeratorByNames(const LPCTSTR rgpszNames[], UINT cNames, IEnumIDList **ppenum)
  644. {
  645. LPITEMIDLIST rgItems[MAX_ROOTS] = {0};
  646. int cItems = 0;
  647. (*ppenum)->Reset();
  648. // capture all of the other pidls in the current enumerator
  649. LPITEMIDLIST pidl;
  650. while (S_OK == (*ppenum)->Next(1, &pidl, NULL))
  651. {
  652. TCHAR szPath[MAX_PATH];
  653. if (SUCCEEDED(SHGetNameAndFlags(pidl, SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath), NULL)))
  654. {
  655. for (UINT i = 0; i < cNames; i++)
  656. {
  657. if (rgpszNames[i] &&
  658. (cItems < ARRAYSIZE(rgItems)) &&
  659. (0 == StrCmpIC(szPath, rgpszNames[i])))
  660. {
  661. rgItems[cItems++] = pidl;
  662. pidl = NULL; // don't free below
  663. break;
  664. }
  665. }
  666. }
  667. ILFree(pidl); // may be NULL
  668. }
  669. IEnumIDList *penum;
  670. if (SUCCEEDED(CreateIEnumIDListOnIDLists(rgItems, cItems, &penum)))
  671. {
  672. (*ppenum)->Release();
  673. *ppenum = penum;
  674. }
  675. ClearIDArray(rgItems, cItems);
  676. return S_OK;
  677. }
  678. HRESULT CFindFilter::_ScopeEnumerator(IEnumIDList **ppenum)
  679. {
  680. *ppenum = NULL;
  681. HRESULT hr = E_FAIL;
  682. if (_penumRoots)
  683. {
  684. hr = _penumRoots->Clone(ppenum);
  685. if (SUCCEEDED(hr))
  686. (*ppenum)->Reset(); // clone above will clone the index as well
  687. }
  688. return hr;
  689. }
  690. //==========================================================================
  691. //add helper funtion to check if the Path is restricted (WinseBUG 20189)
  692. //==========================================================================
  693. BOOL PathIsRestricted(TCHAR * szPath, RESTRICTIONS iFlag)
  694. {
  695. UINT driveNum, dwRest;
  696. if((driveNum = PathGetDriveNumber(szPath)) != -1){
  697. dwRest = SHRestricted(iFlag);
  698. if (dwRest & (1 << driveNum))
  699. {
  700. return TRUE;
  701. }
  702. }
  703. return FALSE;
  704. }
  705. void FilterNoViewDrives(LPTSTR rgpszNames[], UINT *pcNames)
  706. {
  707. UINT cNames = *pcNames;
  708. UINT cNamesResult = 0;
  709. for (UINT iName = 0; iName < cNames; iName++)
  710. {
  711. if (PathIsRestricted(rgpszNames[iName], REST_NOVIEWONDRIVE))
  712. {
  713. LocalFree((HLOCAL)rgpszNames[iName]);
  714. rgpszNames[iName] = NULL;
  715. }
  716. else
  717. {
  718. rgpszNames[cNamesResult++] = rgpszNames[iName];
  719. }
  720. }
  721. *pcNames = cNamesResult;
  722. }
  723. // produce the find enumerator
  724. STDMETHODIMP CFindFilter::EnumObjects(IShellFolder *psf, LPCITEMIDLIST pidlStart,
  725. DWORD grfFlags, int iColSort, LPTSTR pszProgressText,
  726. IRowsetWatchNotify *prwn, IFindEnum **ppdfenum)
  727. {
  728. *ppdfenum = NULL;
  729. HRESULT hr;
  730. IEnumIDList *penum;
  731. if (pidlStart)
  732. {
  733. hr = CreateIEnumIDListOnIDLists(&pidlStart, 1, &penum);
  734. }
  735. else
  736. {
  737. hr = _ScopeEnumerator(&penum);
  738. }
  739. if (SUCCEEDED(hr))
  740. {
  741. UINT cPaths;
  742. LPTSTR rgpszPaths[MAX_ROOTS];
  743. hr = NamesFromEnumIDList(penum, rgpszPaths, ARRAYSIZE(rgpszPaths), &cPaths);
  744. if (SUCCEEDED(hr))
  745. {
  746. FilterNoViewDrives(rgpszPaths, &cPaths);
  747. *ppdfenum = NULL;
  748. if (cPaths > 0)
  749. {
  750. hr = CreateOleDBEnum(this, psf, rgpszPaths, &cPaths, grfFlags, iColSort, pszProgressText, prwn, ppdfenum);
  751. if (S_OK == hr && *ppdfenum != NULL)
  752. {
  753. _penumAsync = *ppdfenum;
  754. _penumAsync->AddRef();
  755. }
  756. else
  757. {
  758. _penumAsync = NULL;
  759. }
  760. // are their more paths to process?
  761. if (cPaths)
  762. {
  763. // did user specified CI query that we can't grep for?
  764. DWORD dwFlags;
  765. if (FAILED(GenerateQueryRestrictions(NULL, &dwFlags)) ||
  766. !(dwFlags & GQR_REQUIRES_CI))
  767. {
  768. FilterEnumeratorByNames(rgpszPaths, ARRAYSIZE(rgpszPaths), &penum);
  769. IFindEnum *pdfenum = new CNamespaceEnum(
  770. SAFECAST(this, IFindFilter *), psf, *ppdfenum,
  771. penum, _hwnd, grfFlags, pszProgressText);
  772. if (pdfenum)
  773. {
  774. // The rest of the fields should be zero/NULL
  775. *ppdfenum = pdfenum;
  776. hr = S_OK;
  777. }
  778. else
  779. {
  780. hr = E_OUTOFMEMORY;
  781. }
  782. }
  783. }
  784. }
  785. FreePathArray(rgpszPaths, cPaths);
  786. }
  787. penum->Release();
  788. }
  789. return hr;
  790. }
  791. // IFindFilter::MatchFilter
  792. STDMETHODIMP_(BOOL) CFindFilter::MatchFilter(IShellFolder *psf, LPCITEMIDLIST pidl)
  793. {
  794. BOOL bMatch = TRUE;
  795. TCHAR szName[MAX_PATH], szDisplayName[MAX_PATH];
  796. DWORD dwAttrib = SHGetAttributes(psf, pidl, SFGAO_HIDDEN | SFGAO_FOLDER | SFGAO_ISSLOW);
  797. if (SUCCEEDED(DisplayNameOf(psf, pidl, SHGDN_INFOLDER | SHGDN_FORPARSING, szName, ARRAYSIZE(szName))) &&
  798. SUCCEEDED(DisplayNameOf(psf, pidl, SHGDN_INFOLDER | SHGDN_NORMAL, szDisplayName, ARRAYSIZE(szDisplayName))))
  799. {
  800. IShellFolder2 *psf2;
  801. psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2)); // optional, may be NULL
  802. // First things we dont show hidden files
  803. // If show all is set then we should include hidden files also...
  804. if (!_fSearchHidden && (SFGAO_HIDDEN & dwAttrib))
  805. bMatch = FALSE; // does not match
  806. if (bMatch && _fFoldersOnly && !(SFGAO_FOLDER & dwAttrib))
  807. bMatch = FALSE; // does not match
  808. if (bMatch && _iSizeType)
  809. {
  810. ULONGLONG ullSize;
  811. if (psf2 && SUCCEEDED(GetLongProperty(psf2, pidl, &_scidSize, &ullSize)))
  812. {
  813. if (1 == _iSizeType) // >
  814. {
  815. if (!(ullSize > _ullSize))
  816. bMatch = FALSE; // does not match
  817. }
  818. else if (2 == _iSizeType) // <
  819. {
  820. if (!(ullSize < _ullSize))
  821. bMatch = FALSE; // does not match
  822. }
  823. }
  824. else
  825. {
  826. bMatch = FALSE;
  827. }
  828. }
  829. if (bMatch && (_scidDate.fmtid != CLSID_NULL))
  830. {
  831. FILETIME ft;
  832. if (psf2 && SUCCEEDED(GetDateProperty(psf2, pidl, &_scidDate, &ft)))
  833. {
  834. FILETIME ftLocal;
  835. FileTimeToLocalFileTime(&ft, &ftLocal);
  836. WORD wFileDate = 0, wFileTime = 0;
  837. FileTimeToDosDateTime(&ftLocal, &wFileDate, &wFileTime);
  838. if (_dateModifiedBefore && !(wFileDate <= _dateModifiedBefore))
  839. bMatch = FALSE; // does not match
  840. if (_dateModifiedAfter && !(wFileDate >= _dateModifiedAfter))
  841. bMatch = FALSE; // does not match
  842. }
  843. else
  844. {
  845. bMatch = FALSE;
  846. }
  847. }
  848. // Match file specificaitions.
  849. if (bMatch && _pszFileSpec && _pszFileSpec[0])
  850. {
  851. // if we have split up version of the specs we'll use it because PathMatchSpec
  852. // can take up to 5-6 hours for more than 10 wildcard specs
  853. if (_cFileSpecs)
  854. {
  855. // Only search the actual file system file name if the user specified
  856. // an extention
  857. BOOL bHasExtension = (0 != *PathFindExtension(_pszFileSpec));
  858. if (bHasExtension)
  859. {
  860. for (int i = 0; i < _cFileSpecs; i++)
  861. {
  862. bMatch = PathMatchSpec(szName, _apszFileSpecs[i]);
  863. if (_fANDSearch)
  864. {
  865. // AND we quit on the first one that doesn't match
  866. if (!bMatch)
  867. break;
  868. }
  869. else
  870. {
  871. // OR we quit on the first one that does match
  872. if (bMatch)
  873. break;
  874. }
  875. }
  876. }
  877. // Compare the displayable name to the filter.
  878. // This is needed for searching the recylcle bin becuase the actual file names
  879. // are similar to "DC0.LNK" instead of "New Text Document.txt"
  880. if (!bMatch || !bHasExtension)
  881. {
  882. for (int i = 0; i < _cFileSpecs; i++)
  883. {
  884. bMatch = PathMatchSpec(szDisplayName, _apszFileSpecs[i]);
  885. if (_fANDSearch)
  886. {
  887. // AND we quit on the first one that doesn't match
  888. if (!bMatch)
  889. break;
  890. }
  891. else
  892. {
  893. // OR we quit on the first one that does match
  894. if (bMatch)
  895. break;
  896. }
  897. }
  898. }
  899. }
  900. else if (!PathMatchSpec(szName, _pszFileSpec)
  901. && !PathMatchSpec(szDisplayName, _pszFileSpec))
  902. {
  903. bMatch = FALSE; // does not match
  904. }
  905. }
  906. if (bMatch && _strTypeFilePatterns[0])
  907. {
  908. // if looking for folders only and file pattern is all folders then no need to check
  909. // if folder name matches the pattern -- we know it is the folder, otherwise we
  910. // would have bailed out earlier in the function
  911. if (!(_fFoldersOnly && lstrcmp(_strTypeFilePatterns, TEXT(".")) == 0))
  912. {
  913. if (!PathMatchSpec(szName, _strTypeFilePatterns))
  914. bMatch = FALSE; // does not match
  915. }
  916. }
  917. // See if we need to do a grep of the file
  918. if (bMatch && (SFGAO_ISSLOW & dwAttrib) && !_fSearchSlowFiles)
  919. bMatch = FALSE; // does not match
  920. if (bMatch &&
  921. (S_OK == _filtgrep.GetMatchTokens(NULL, 0) ||
  922. S_OK == _filtgrep.GetExcludeTokens(NULL, 0)))
  923. {
  924. bMatch = (S_OK == _filtgrep.Grep(psf, pidl, szName));
  925. }
  926. if (psf2)
  927. psf2->Release();
  928. }
  929. else
  930. bMatch = FALSE;
  931. return bMatch; // return TRUE -> yes, a match!
  932. }
  933. // date ordinal mapper helpers to deal with old way to refer to dates
  934. BOOL MapValueToDateSCID(UINT i, SHCOLUMNID *pscid)
  935. {
  936. ZeroMemory(pscid, sizeof(*pscid));
  937. switch (i)
  938. {
  939. case 1:
  940. *pscid = SCID_WRITETIME;
  941. break;
  942. case 2:
  943. *pscid = SCID_CREATETIME;
  944. break;
  945. case 3:
  946. *pscid = SCID_ACCESSTIME;
  947. break;
  948. default:
  949. return FALSE;
  950. }
  951. return TRUE;
  952. }
  953. // returns 0 as invalid ordinal
  954. int MapDateSCIDToValue(const SHCOLUMNID *pscid)
  955. {
  956. int i = 0; // 0 invalid scid
  957. if (IsEqualSCID(*pscid, SCID_WRITETIME))
  958. {
  959. i = 1;
  960. }
  961. else if (IsEqualSCID(*pscid, SCID_CREATETIME))
  962. {
  963. i = 2;
  964. }
  965. else if (IsEqualSCID(*pscid, SCID_ACCESSTIME))
  966. {
  967. i = 3;
  968. }
  969. return i;
  970. }
  971. // IFindFilter::SaveCriteria
  972. // util.cpp
  973. STDAPI_(int) Int64ToString(LONGLONG n, LPTSTR szOutStr, UINT nSize, BOOL bFormat,
  974. NUMBERFMT *pFmt, DWORD dwNumFmtFlags);
  975. #define MAX_ULONGLONG_LEN 20+1 // "18446744073709551616"
  976. STDMETHODIMP CFindFilter::SaveCriteria(IStream * pstm, WORD fCharType)
  977. {
  978. TCHAR szTemp[40]; // some random size
  979. // The caller should have already validated the stuff and updated
  980. // everything for the current filter information.
  981. // we need to walk through and check each of the items to see if we
  982. // have a criteria to save away. this includes:
  983. // (Name, Path, Type, Contents, size, modification dates)
  984. int cCriteria = _SaveCriteriaItem(pstm, IDD_FILESPEC, _szUserInputFileSpec, fCharType);
  985. cCriteria += _SaveCriteriaItem(pstm, IDD_PATH, _szPath, fCharType);
  986. cCriteria += _SaveCriteriaItem(pstm, DFSC_SEARCHFOR, _pszIndexedSearch, fCharType);
  987. cCriteria += _SaveCriteriaItem(pstm, IDD_TYPECOMBO, _strTypeFilePatterns, fCharType);
  988. cCriteria += _SaveCriteriaItem(pstm, IDD_CONTAINS, _szText, fCharType);
  989. // Also save away the state of the top level only
  990. wsprintf(szTemp, TEXT("%d"), _fTopLevelOnly);
  991. cCriteria += _SaveCriteriaItem(pstm, IDD_TOPLEVELONLY, szTemp, fCharType);
  992. // The Size field is little more fun!
  993. if (_iSizeType != 0)
  994. {
  995. WCHAR szNum[MAX_ULONGLONG_LEN];
  996. Int64ToString(_ullSize, szNum, ARRAYSIZE(szNum), FALSE, NULL, 0);
  997. wsprintf(szTemp, TEXT("%d %ws"), _iSizeType, szNum);
  998. cCriteria += _SaveCriteriaItem(pstm, IDD_SIZECOMP, szTemp, fCharType);
  999. }
  1000. // Likewise for the dates, should be fun as we need to save it depending on
  1001. // how the date was specified
  1002. switch (_wDateType & DFF_DATE_RANGEMASK)
  1003. {
  1004. case DFF_DATE_ALL:
  1005. // nothing to store
  1006. break;
  1007. case DFF_DATE_DAYS:
  1008. wsprintf(szTemp, TEXT("%d"), _wDateValue);
  1009. cCriteria += _SaveCriteriaItem(pstm, IDD_MDATE_NUMDAYS, szTemp, fCharType);
  1010. break;
  1011. case DFF_DATE_MONTHS:
  1012. wsprintf(szTemp, TEXT("%d"), _wDateValue);
  1013. cCriteria += _SaveCriteriaItem(pstm, IDD_MDATE_NUMMONTHS, szTemp, fCharType);
  1014. break;
  1015. case DFF_DATE_BETWEEN:
  1016. if (_dateModifiedAfter)
  1017. {
  1018. wsprintf(szTemp, TEXT("%d"), _dateModifiedAfter);
  1019. cCriteria += _SaveCriteriaItem(pstm, IDD_MDATE_FROM, szTemp, fCharType);
  1020. }
  1021. if (_dateModifiedBefore)
  1022. {
  1023. wsprintf(szTemp, TEXT("%d"), _dateModifiedBefore);
  1024. cCriteria += _SaveCriteriaItem(pstm, IDD_MDATE_TO, szTemp, fCharType);
  1025. }
  1026. break;
  1027. }
  1028. if ((_wDateType & DFF_DATE_RANGEMASK) != DFF_DATE_ALL)
  1029. {
  1030. int i = MapDateSCIDToValue(&_scidDate);
  1031. if (i)
  1032. {
  1033. // strangly we write a 0 based version of this ordinal out
  1034. wsprintf(szTemp, TEXT("%d"), i - 1);
  1035. cCriteria += _SaveCriteriaItem(pstm, IDD_MDATE_TYPE, szTemp, fCharType);
  1036. }
  1037. }
  1038. if (_fTextCaseSen)
  1039. {
  1040. wsprintf(szTemp, TEXT("%d"), _fTextCaseSen);
  1041. cCriteria += _SaveCriteriaItem(pstm, IDD_TEXTCASESEN, szTemp, fCharType);
  1042. }
  1043. if (_fTextReg)
  1044. {
  1045. wsprintf(szTemp, TEXT("%d"), _fTextReg);
  1046. cCriteria += _SaveCriteriaItem(pstm, IDD_TEXTREG, szTemp, fCharType);
  1047. }
  1048. if (_fSearchSlowFiles)
  1049. {
  1050. wsprintf(szTemp, TEXT("%d"), _fSearchSlowFiles);
  1051. cCriteria += _SaveCriteriaItem(pstm, IDD_SEARCHSLOWFILES, szTemp, fCharType);
  1052. }
  1053. // Save value for searching system directories.
  1054. if (_fSearchSystemDirs)
  1055. {
  1056. wsprintf(szTemp, TEXT("%d"), _fSearchSystemDirs);
  1057. cCriteria += _SaveCriteriaItem(pstm, IDD_SEARCHSYSTEMDIRS, szTemp, fCharType);
  1058. }
  1059. if (_fSearchHidden)
  1060. {
  1061. wsprintf(szTemp, TEXT("%d"), _fSearchHidden);
  1062. cCriteria += _SaveCriteriaItem(pstm, IDD_SEARCHHIDDEN, szTemp, fCharType);
  1063. }
  1064. return MAKE_SCODE(0, 0, cCriteria);
  1065. }
  1066. // Helper function for save criteria that will output the string and
  1067. // and id to the specified file. it will also test for NULL and the like
  1068. int CFindFilter::_SaveCriteriaItem(IStream *pstm, WORD wNum, LPCTSTR psz, WORD fCharType)
  1069. {
  1070. if ((psz == NULL) || (*psz == 0))
  1071. return 0;
  1072. else
  1073. {
  1074. const void *pszText = (const void *)psz; // Ptr to output text. Defaults to source.
  1075. // These are required to support ANSI-unicode conversions.
  1076. LPSTR pszAnsi = NULL; // For unicode-to-ansi conversion.
  1077. LPWSTR pszWide = NULL; // For ansi-to-unicode conversion.
  1078. DFCRITERIA dfc;
  1079. dfc.wNum = wNum;
  1080. // Note: Problem if string is longer than 64K
  1081. dfc.cbText = (WORD) ((lstrlen(psz) + 1) * sizeof(TCHAR));
  1082. // Source string is Unicode but caller wants to save as ANSI.
  1083. //
  1084. if (DFC_FMT_ANSI == fCharType)
  1085. {
  1086. // Convert to ansi and write ansi.
  1087. dfc.cbText = (WORD) WideCharToMultiByte(CP_ACP, 0L, psz, -1, pszAnsi, 0, NULL, NULL);
  1088. pszAnsi = (LPSTR)LocalAlloc(LMEM_FIXED, dfc.cbText);
  1089. if (pszAnsi)
  1090. {
  1091. WideCharToMultiByte(CP_ACP, 0L, psz, -1, pszAnsi, dfc.cbText / sizeof(pszAnsi[0]), NULL, NULL);
  1092. pszText = (void *)pszAnsi;
  1093. }
  1094. }
  1095. pstm->Write(&dfc, sizeof(dfc), NULL); // Output index
  1096. pstm->Write(pszText, dfc.cbText, NULL); // output string + NULL
  1097. // Free up conversion buffers if any were created.
  1098. if (pszAnsi)
  1099. LocalFree(pszAnsi);
  1100. if (pszWide)
  1101. LocalFree(pszWide);
  1102. }
  1103. return 1;
  1104. }
  1105. // IFindFilter::RestoreCriteria
  1106. STDMETHODIMP CFindFilter::RestoreCriteria(IStream *pstm, int cCriteria, WORD fCharType)
  1107. {
  1108. SHSTR strTemp;
  1109. SHSTRA strTempA;
  1110. if (cCriteria > 0)
  1111. _fWeRestoredSomeCriteria = TRUE;
  1112. while (cCriteria--)
  1113. {
  1114. DFCRITERIA dfc;
  1115. DWORD cb;
  1116. if (FAILED(pstm->Read(&dfc, sizeof(dfc), &cb)) || cb != sizeof(dfc))
  1117. break;
  1118. if (DFC_FMT_UNICODE == fCharType)
  1119. {
  1120. // Destination is Unicode and we're reading Unicode data from stream.
  1121. // No conversion required.
  1122. if (FAILED(strTemp.SetSize(dfc.cbText / sizeof(TCHAR))) ||
  1123. FAILED(pstm->Read(strTemp.GetInplaceStr(), dfc.cbText, &cb))
  1124. || (cb != dfc.cbText))
  1125. break;
  1126. }
  1127. else
  1128. {
  1129. // Destination is Unicode but we're reading ANSI data from stream.
  1130. // Read ansi. Convert to unicode.
  1131. if (FAILED(strTempA.SetSize(dfc.cbText / sizeof(CHAR))) ||
  1132. FAILED(pstm->Read(strTempA.GetInplaceStr(), dfc.cbText, &cb))
  1133. || (cb != dfc.cbText))
  1134. break;
  1135. strTemp.SetStr(strTempA);
  1136. }
  1137. switch (dfc.wNum)
  1138. {
  1139. case IDD_FILESPEC:
  1140. lstrcpyn(_szUserInputFileSpec, strTemp, ARRAYSIZE(_szUserInputFileSpec));
  1141. break;
  1142. case DFSC_SEARCHFOR:
  1143. Str_SetPtr(&_pszIndexedSearch, strTemp);
  1144. break;
  1145. case IDD_PATH:
  1146. _ResetRoots();
  1147. lstrcpyn(_szPath, strTemp, ARRAYSIZE(_szPath));
  1148. CreateIEnumIDListPaths(_szPath, &_penumRoots);
  1149. break;
  1150. case IDD_TOPLEVELONLY:
  1151. _fTopLevelOnly = StrToInt(strTemp);
  1152. break;
  1153. case IDD_TYPECOMBO:
  1154. _strTypeFilePatterns.SetStr(strTemp);
  1155. break;
  1156. case IDD_CONTAINS:
  1157. lstrcpyn(_szText, strTemp, ARRAYSIZE(_szText));
  1158. break;
  1159. case IDD_SIZECOMP:
  1160. // we need to extract off the two parts, the type and
  1161. // the value
  1162. _iSizeType = strTemp[0] - TEXT('0');
  1163. StrToInt64Ex(&(strTemp.GetStr())[2], STIF_DEFAULT, (LONGLONG*)&_ullSize);
  1164. break;
  1165. case IDD_MDATE_NUMDAYS:
  1166. _wDateType = DFF_DATE_DAYS;
  1167. _wDateValue = (WORD) StrToInt(strTemp);
  1168. break;
  1169. case IDD_MDATE_NUMMONTHS:
  1170. _wDateType = DFF_DATE_MONTHS;
  1171. _wDateValue = (WORD) StrToInt(strTemp);
  1172. break;
  1173. case IDD_MDATE_FROM:
  1174. _wDateType = DFF_DATE_BETWEEN;
  1175. _dateModifiedAfter = (WORD) StrToInt(strTemp);
  1176. break;
  1177. case IDD_MDATE_TO:
  1178. _wDateType = DFF_DATE_BETWEEN;
  1179. _dateModifiedBefore = (WORD) StrToInt(strTemp);
  1180. break;
  1181. case IDD_MDATE_TYPE:
  1182. // persisted value is zero based, adjust that by adding 1
  1183. MapValueToDateSCID(StrToInt(strTemp) + 1, &_scidDate);
  1184. break;
  1185. case IDD_TEXTCASESEN:
  1186. _fTextCaseSen = StrToInt(strTemp);
  1187. break;
  1188. case IDD_TEXTREG:
  1189. _fTextReg = StrToInt(strTemp);
  1190. break;
  1191. case IDD_SEARCHSLOWFILES:
  1192. _fSearchSlowFiles = StrToInt(strTemp);
  1193. break;
  1194. case IDD_SEARCHSYSTEMDIRS:
  1195. _fSearchSystemDirs = StrToInt(strTemp);
  1196. break;
  1197. }
  1198. }
  1199. return S_OK;
  1200. }
  1201. // IFindFilter::GetColSaveStream
  1202. STDMETHODIMP CFindFilter::GetColSaveStream(WPARAM wParam, IStream **ppstm)
  1203. {
  1204. *ppstm = OpenRegStream(HKEY_CURRENT_USER, REGSTR_PATH_EXPLORER, TEXT("DocFindColsX"), (DWORD) wParam);
  1205. return *ppstm ? S_OK : E_FAIL;
  1206. }
  1207. void CFindFilter::_GenerateQuery(LPWSTR pwszQuery, DWORD *pcchQuery)
  1208. {
  1209. DWORD cchNeeded = 0, cchLeft = *pcchQuery;
  1210. LPWSTR pszCurrent = pwszQuery;
  1211. BOOL bFirst = TRUE; // first property
  1212. if (_pszFileSpec && _pszFileSpec[0])
  1213. {
  1214. cchNeeded += _CIQueryFilePatterns(&bFirst, &cchLeft, &pszCurrent, _pszFileSpec);
  1215. }
  1216. // fFoldersOnly = TRUE implies szTypeFilePatterns = "."
  1217. // we cannot pass "." to CI because they won't understand it as give me the folder types
  1218. // we could check for @attrib ^a FILE_ATTRIBUTE_DIRECTORY (0x10) but ci doesn't index the
  1219. // folder names by default so we normally won't get any results...
  1220. if (!_fFoldersOnly && _strTypeFilePatterns[0])
  1221. {
  1222. cchNeeded += _CIQueryFilePatterns(&bFirst, &cchLeft, &pszCurrent, _strTypeFilePatterns);
  1223. }
  1224. // Date:
  1225. if (_dateModifiedBefore)
  1226. {
  1227. cchNeeded += _QueryDosDate(&bFirst, &cchLeft, &pszCurrent, _dateModifiedBefore, TRUE);
  1228. }
  1229. if (_dateModifiedAfter)
  1230. {
  1231. cchNeeded += _QueryDosDate(&bFirst, &cchLeft, &pszCurrent, _dateModifiedAfter, FALSE);
  1232. }
  1233. if (_iSizeType)
  1234. {
  1235. cchNeeded += _CIQuerySize(&bFirst, &cchLeft, &pszCurrent, _ullSize, _iSizeType);
  1236. }
  1237. // Indexed Search: raw query
  1238. if (_pszIndexedSearch && _pszIndexedSearch[0])
  1239. {
  1240. // HACK Alert if first Char is ! then we assume Raw and pass it through directly to CI...
  1241. // Likewise if it starts with @ or # pass through, but remember the @...
  1242. cchNeeded += _CIQueryIndex(&bFirst, &cchLeft, &pszCurrent, _pszIndexedSearch);
  1243. }
  1244. // Containing Text:
  1245. if (_szText[0])
  1246. {
  1247. // Try not to quote the strings unless we need to. This allows more flexability to do the
  1248. // searching for example: "cat near dog" is different than: cat near dog
  1249. cchNeeded += _CIQueryTextPatterns(&bFirst, &cchLeft, &pszCurrent, _szText, _fTextReg);
  1250. }
  1251. cchNeeded += _CIQueryShellSettings(&bFirst, &cchLeft, &pszCurrent);
  1252. IEnumIDList *penum;
  1253. if (SUCCEEDED(_ScopeEnumerator(&penum)))
  1254. {
  1255. TCHAR szPath[MAX_PATH];
  1256. LPITEMIDLIST pidl;
  1257. while (S_OK == penum->Next(1, &pidl, NULL))
  1258. {
  1259. if (SHGetPathFromIDList(pidl, szPath) && PathStripToRoot(szPath))
  1260. {
  1261. // don't search recycle bin folder. we add both nt4's recycled
  1262. // and nt5's recycler for every drive we search.
  1263. static const LPCTSTR s_rgszRecycleBins[] =
  1264. {
  1265. TEXT("Recycled\\*"),
  1266. TEXT("Recycler\\*"),
  1267. };
  1268. for (int iBin = 0; iBin < ARRAYSIZE(s_rgszRecycleBins); iBin++)
  1269. {
  1270. TCHAR szExclude[MAX_PATH];
  1271. if (PathCombine(szExclude, szPath, s_rgszRecycleBins[iBin]))
  1272. {
  1273. DWORD cchSize = lstrlen(szExclude) + ARRAYSIZE(TEXT(" & !#PATH "));
  1274. // don't bail out early if we are asked for size of query
  1275. if (pwszQuery && cchSize > cchLeft)
  1276. break;
  1277. cchNeeded += _AddToQuery(&pszCurrent, &cchLeft, TEXT(" & !#PATH "));
  1278. cchNeeded += _AddToQuery(&pszCurrent, &cchLeft, szExclude);
  1279. }
  1280. }
  1281. }
  1282. ILFree(pidl);
  1283. }
  1284. penum->Release();
  1285. }
  1286. // we must exclude the special folders from the results or ci will find items that
  1287. // we cannot get pidls for.
  1288. HKEY hkey;
  1289. if (RegOpenKeyEx(HKEY_CURRENT_USER, CI_SPECIAL_FOLDERS, 0, KEY_READ, &hkey) == ERROR_SUCCESS)
  1290. {
  1291. DWORD cValues = 0; // init to zero in case query info bellow fails
  1292. RegQueryInfoKey(hkey, NULL, NULL, NULL, NULL, NULL, NULL, &cValues, NULL, NULL, NULL, NULL);
  1293. for (DWORD i = 0; i < cValues; i++)
  1294. {
  1295. TCHAR szExcludePath[MAX_PATH];
  1296. DWORD cb = sizeof(szExcludePath);
  1297. TCHAR szName[10];
  1298. wsprintf(szName, TEXT("%d"), i);
  1299. if (RegQueryValueEx(hkey, szName, NULL, NULL, (BYTE *)szExcludePath, &cb) == ERROR_SUCCESS)
  1300. {
  1301. // this is in the query (or a drive letter of the query)
  1302. DWORD cchSize = lstrlen(szExcludePath) + ARRAYSIZE(TEXT(" & !#PATH "));
  1303. // don't bail out early if we are asked for size of query
  1304. if (pwszQuery && cchSize > cchLeft)
  1305. break;
  1306. cchNeeded += _AddToQuery(&pszCurrent, &cchLeft, TEXT(" & !#PATH "));
  1307. cchNeeded += _AddToQuery(&pszCurrent, &cchLeft, szExcludePath);
  1308. }
  1309. }
  1310. RegCloseKey(hkey);
  1311. }
  1312. // we need at least some constraints so give a query of "all files"
  1313. if (pwszQuery && pszCurrent == pwszQuery)
  1314. _CIQueryFilePatterns(&bFirst, &cchLeft, &pszCurrent, L"*.*");
  1315. if (pszCurrent)
  1316. {
  1317. // Make sure we terminate the string at the end...
  1318. *pszCurrent = 0;
  1319. }
  1320. if (!pwszQuery)
  1321. {
  1322. *pcchQuery = cchNeeded;
  1323. }
  1324. else
  1325. {
  1326. ASSERT(*pcchQuery > cchNeeded);
  1327. }
  1328. }
  1329. // Create a query command string out of the search criteria
  1330. STDMETHODIMP CFindFilter::GenerateQueryRestrictions(LPWSTR *ppwszQuery, DWORD *pdwQueryRestrictions)
  1331. {
  1332. // we should be able to make use of ci no matter what (exceptions at the end of the function)
  1333. DWORD dwQueryRestrictions = GQR_MAKES_USE_OF_CI;
  1334. HRESULT hr = S_OK;
  1335. #ifdef DEBUG
  1336. if (GetKeyState(VK_SHIFT) < 0)
  1337. {
  1338. dwQueryRestrictions |= GQR_REQUIRES_CI;
  1339. }
  1340. #endif
  1341. if (ppwszQuery)
  1342. {
  1343. DWORD cchNeeded = 0;
  1344. _GenerateQuery(NULL, &cchNeeded);
  1345. cchNeeded++; // for \0
  1346. *ppwszQuery = (LPWSTR)LocalAlloc(LPTR, cchNeeded * sizeof(**ppwszQuery));
  1347. if (*ppwszQuery)
  1348. {
  1349. _GenerateQuery(*ppwszQuery, &cchNeeded);
  1350. }
  1351. else
  1352. {
  1353. hr = E_OUTOFMEMORY;
  1354. }
  1355. }
  1356. if (SUCCEEDED(hr))
  1357. {
  1358. if (_pszIndexedSearch && _pszIndexedSearch[0])
  1359. dwQueryRestrictions |= GQR_REQUIRES_CI;
  1360. // ci is not case sensitive, so if user wanted case sensitive search we cannot use ci
  1361. // also ci doesn't index folder names by default so to be safe we just default to our
  1362. // disk traversal algorithm...
  1363. if (_fTextCaseSen || _fFoldersOnly)
  1364. {
  1365. if ((dwQueryRestrictions & GQR_REQUIRES_CI) && _fTextCaseSen)
  1366. hr = MAKE_HRESULT(3, FACILITY_SEARCHCOMMAND, SCEE_CASESENINDEX);
  1367. else if (dwQueryRestrictions & GQR_MAKES_USE_OF_CI)
  1368. dwQueryRestrictions &= ~GQR_MAKES_USE_OF_CI;
  1369. }
  1370. }
  1371. *pdwQueryRestrictions = dwQueryRestrictions; // return calculated Flags...
  1372. return hr;
  1373. }
  1374. STDMETHODIMP CFindFilter::ReleaseQuery()
  1375. {
  1376. ATOMICRELEASE(_penumAsync);
  1377. return S_OK;
  1378. }
  1379. STDMETHODIMP CFindFilter::GetQueryLanguageDialect(ULONG* pulDialect)
  1380. {
  1381. *pulDialect = _ulQueryDialect;
  1382. return S_OK;
  1383. }
  1384. STDMETHODIMP CFindFilter::GetWarningFlags(DWORD* pdwWarningFlags)
  1385. {
  1386. *pdwWarningFlags = _dwWarningFlags;
  1387. return S_OK;
  1388. }
  1389. // Registering our interest in FS change notifications.
  1390. //
  1391. // In:
  1392. // hwnd = window handle of the find dialog
  1393. // uMsg = message to be sent to window when informing of notify
  1394. STDMETHODIMP CFindFilter::DeclareFSNotifyInterest(HWND hwnd, UINT uMsg)
  1395. {
  1396. HDPA hdpa = DPA_Create(10); // Used to manage list of pidls to add
  1397. if (hdpa)
  1398. {
  1399. IEnumIDList *penum;
  1400. if (SUCCEEDED(_ScopeEnumerator(&penum)))
  1401. {
  1402. LPITEMIDLIST pidl;
  1403. while (S_OK == penum->Next(1, &pidl, NULL))
  1404. {
  1405. if (-1 == DPA_AppendPtr(hdpa, pidl))
  1406. {
  1407. // Failed to add it, so free it.
  1408. ILFree(pidl);
  1409. }
  1410. }
  1411. penum->Release();
  1412. }
  1413. // Eliminate any pidls in the hdpa that are children of other pidls.
  1414. // this is needed to prevent receiving the multiple updates for one change.
  1415. // For example, if searching My Documents and C:\, then we will get 2 updates
  1416. // for a change in My Documents if My Documents is on the C:\.
  1417. int cItems = DPA_GetPtrCount(hdpa);
  1418. for (int iOuterLoop = 0; iOuterLoop < cItems - 1; iOuterLoop++)
  1419. {
  1420. LPITEMIDLIST pidlOuter = (LPITEMIDLIST) DPA_GetPtr(hdpa, iOuterLoop);
  1421. for (int iInnerLoop = iOuterLoop + 1;
  1422. pidlOuter && iInnerLoop < cItems;
  1423. iInnerLoop++)
  1424. {
  1425. LPITEMIDLIST pidlInner = (LPITEMIDLIST) DPA_GetPtr(hdpa, iInnerLoop);
  1426. if (pidlInner)
  1427. {
  1428. if (ILIsParent(pidlInner, pidlOuter, FALSE))
  1429. {
  1430. // Since pidlInner is pidlOuter's parent, free pidlOuter and
  1431. // don't register for events on it.
  1432. ILFree(pidlOuter);
  1433. pidlOuter = NULL;
  1434. DPA_SetPtr(hdpa, iOuterLoop, NULL);
  1435. }
  1436. else if (ILIsParent(pidlOuter, pidlInner, FALSE))
  1437. {
  1438. // Since pidlOuter is pidlInner's parent, free pidlInner and
  1439. // don't register for events on it.
  1440. ILFree(pidlInner);
  1441. pidlInner = NULL;
  1442. DPA_SetPtr(hdpa, iInnerLoop, NULL);
  1443. }
  1444. }
  1445. }
  1446. }
  1447. // Declare that we are interested in events on remaining pidls
  1448. for (int iRegIndex = 0; iRegIndex < cItems; iRegIndex++)
  1449. {
  1450. SHChangeNotifyEntry fsne = {0};
  1451. fsne.fRecursive = TRUE;
  1452. fsne.pidl = (LPITEMIDLIST)DPA_GetPtr(hdpa, iRegIndex);
  1453. if (fsne.pidl)
  1454. {
  1455. SHChangeNotifyRegister(hwnd,
  1456. SHCNRF_NewDelivery | SHCNRF_ShellLevel | SHCNRF_InterruptLevel,
  1457. SHCNE_DISKEVENTS, uMsg, 1, &fsne);
  1458. ILFree((LPITEMIDLIST)fsne.pidl);
  1459. }
  1460. }
  1461. DPA_Destroy(hdpa);
  1462. }
  1463. return S_OK;
  1464. }
  1465. void CFindFilter::_UpdateTypeField(const VARIANT *pvar)
  1466. {
  1467. LPCWSTR pszValue = VariantToStrCast(pvar); // input needs to be a BSTR
  1468. if (pszValue)
  1469. {
  1470. if (StrStr(pszValue, TEXT(".Folder;.")))
  1471. {
  1472. // Special searching for folders...
  1473. _fFoldersOnly = TRUE;
  1474. _strTypeFilePatterns.SetStr(TEXT("."));
  1475. }
  1476. else
  1477. {
  1478. // Assume if the first one is wildcarded than all are,...
  1479. if (*pszValue == TEXT('*'))
  1480. _strTypeFilePatterns.SetStr(pszValue);
  1481. else
  1482. {
  1483. TCHAR szNextPattern[MAX_PATH]; // overkill in size
  1484. BOOL fFirst = TRUE;
  1485. LPCTSTR pszNextPattern = pszValue;
  1486. while ((pszNextPattern = NextPath(pszNextPattern, szNextPattern, ARRAYSIZE(szNextPattern))) != NULL)
  1487. {
  1488. if (!fFirst)
  1489. _strTypeFilePatterns.Append(TEXT(";"));
  1490. fFirst = FALSE;
  1491. if (szNextPattern[0] != TEXT('*'))
  1492. _strTypeFilePatterns.Append(TEXT("*"));
  1493. _strTypeFilePatterns.Append(szNextPattern);
  1494. }
  1495. }
  1496. }
  1497. }
  1498. }
  1499. int _MapConstraint(LPCWSTR pszField)
  1500. {
  1501. for (int i = 0; i < ARRAYSIZE(s_cdffuf); i++)
  1502. {
  1503. if (StrCmpIW(pszField, s_cdffuf[i].pwszField) == 0)
  1504. {
  1505. return i;
  1506. }
  1507. }
  1508. return -1;
  1509. }
  1510. HRESULT CFindFilter::_GetPropertyUI(IPropertyUI **pppui)
  1511. {
  1512. if (!_ppui)
  1513. SHCoCreateInstance(NULL, &CLSID_PropertiesUI, NULL, IID_PPV_ARG(IPropertyUI, &_ppui));
  1514. return _ppui ? _ppui->QueryInterface(IID_PPV_ARG(IPropertyUI, pppui)) : E_NOTIMPL;
  1515. }
  1516. HRESULT CFindFilter::UpdateField(LPCWSTR pszField, VARIANT vValue)
  1517. {
  1518. _fFilterChanged = TRUE; // force rebuilding name of files...
  1519. USHORT uDosTime;
  1520. switch (_MapConstraint(pszField))
  1521. {
  1522. case CDFFUFE_IndexedSearch:
  1523. Str_SetPtr(&_pszIndexedSearch, NULL); // zero this out
  1524. _pszIndexedSearch = VariantToStr(&vValue, NULL, 0);
  1525. break;
  1526. case CDFFUFE_LookIn:
  1527. _ResetRoots();
  1528. if (FAILED(QueryInterfaceVariant(vValue, IID_PPV_ARG(IEnumIDList, &_penumRoots))))
  1529. {
  1530. if (vValue.vt == VT_BSTR)
  1531. {
  1532. VariantToStr(&vValue, _szPath, ARRAYSIZE(_szPath));
  1533. CreateIEnumIDListPaths(_szPath, &_penumRoots);
  1534. }
  1535. else
  1536. {
  1537. LPITEMIDLIST pidl = VariantToIDList(&vValue);
  1538. if (pidl)
  1539. {
  1540. CreateIEnumIDListOnIDLists(&pidl, 1, &_penumRoots);
  1541. ILFree(pidl);
  1542. }
  1543. }
  1544. }
  1545. break;
  1546. case CDFFUFE_IncludeSubFolders:
  1547. _fTopLevelOnly = !VariantToBOOL(vValue); // invert sense
  1548. break;
  1549. case CDFFUFE_Named:
  1550. VariantToStr(&vValue, _szUserInputFileSpec, ARRAYSIZE(_szUserInputFileSpec));
  1551. break;
  1552. case CDFFUFE_ContainingText:
  1553. ZeroMemory(_szText, sizeof(_szText)); // special zero init whole buffer
  1554. VariantToStr(&vValue, _szText, ARRAYSIZE(_szText));
  1555. break;
  1556. case CDFFUFE_FileType:
  1557. _UpdateTypeField(&vValue);
  1558. break;
  1559. case CDFFUFE_WhichDate:
  1560. if (vValue.vt == VT_BSTR)
  1561. {
  1562. IPropertyUI *ppui;
  1563. if (SUCCEEDED(_GetPropertyUI(&ppui)))
  1564. {
  1565. ULONG cch = 0; // in/out
  1566. ppui->ParsePropertyName(vValue.bstrVal, &_scidDate.fmtid, &_scidDate.pid, &cch);
  1567. ppui->Release();
  1568. }
  1569. }
  1570. else
  1571. {
  1572. MapValueToDateSCID(VariantToInt(vValue), &_scidDate);
  1573. }
  1574. break;
  1575. case CDFFUFE_DateLE:
  1576. _wDateType |= DFF_DATE_BETWEEN;
  1577. VariantToDosDateTime(vValue, &_dateModifiedBefore, &uDosTime);
  1578. if (_dateModifiedAfter && _dateModifiedBefore)
  1579. {
  1580. if (_dateModifiedAfter > _dateModifiedBefore)
  1581. {
  1582. WORD wTemp = _dateModifiedAfter;
  1583. _dateModifiedAfter = _dateModifiedBefore;
  1584. _dateModifiedBefore = wTemp;
  1585. }
  1586. }
  1587. break;
  1588. case CDFFUFE_DateGE:
  1589. _wDateType |= DFF_DATE_BETWEEN;
  1590. VariantToDosDateTime(vValue, &_dateModifiedAfter, &uDosTime);
  1591. if (_dateModifiedAfter && _dateModifiedBefore)
  1592. {
  1593. if (_dateModifiedAfter > _dateModifiedBefore)
  1594. {
  1595. WORD wTemp = _dateModifiedAfter;
  1596. _dateModifiedAfter = _dateModifiedBefore;
  1597. _dateModifiedBefore = wTemp;
  1598. }
  1599. }
  1600. break;
  1601. case CDFFUFE_DateNDays:
  1602. _wDateType |= DFF_DATE_DAYS;
  1603. _wDateValue = (WORD)VariantToInt(vValue);
  1604. _dateModifiedAfter = _GetTodaysDosDateMinusNDays(_wDateValue);
  1605. break;
  1606. case CDFFUFE_DateNMonths:
  1607. _wDateType |= DFF_DATE_MONTHS;
  1608. _wDateValue = (WORD)VariantToInt(vValue);
  1609. _dateModifiedAfter = _GetTodaysDosDateMinusNMonths(_wDateValue);
  1610. break;
  1611. case CDFFUFE_SizeLE:
  1612. _iSizeType = 2;
  1613. _ullSize = VariantToULONGLONG(vValue);
  1614. break;
  1615. case CDFFUFE_SizeGE:
  1616. _iSizeType = 1;
  1617. _ullSize = VariantToULONGLONG(vValue);
  1618. break;
  1619. case CDFFUFE_TextCaseSen:
  1620. _fTextCaseSen = VariantToBOOL(vValue);
  1621. break;
  1622. case CDFFUFE_TextReg:
  1623. _fTextReg = VariantToBOOL(vValue);
  1624. break;
  1625. case CDFFUFE_SearchSlowFiles:
  1626. _fSearchSlowFiles = VariantToBOOL(vValue);
  1627. break;
  1628. case CDFFUFE_QueryDialect:
  1629. _ulQueryDialect = VariantToUINT(vValue);
  1630. break;
  1631. case CDFFUFE_WarningFlags:
  1632. _dwWarningFlags = VariantToUINT(vValue);
  1633. break;
  1634. case CDFFUFE_SearchSystemDirs:
  1635. _fSearchSystemDirs = VariantToBOOL(vValue);
  1636. break;
  1637. case CDFFUFE_SearchHidden:
  1638. _fSearchHidden = VariantToBOOL(vValue);
  1639. break;
  1640. }
  1641. return S_OK;
  1642. }
  1643. void CFindFilter::_ResetRoots()
  1644. {
  1645. _szPath[0] = 0;
  1646. ATOMICRELEASE(_penumRoots);
  1647. }
  1648. HRESULT CFindFilter::ResetFieldsToDefaults()
  1649. {
  1650. // Try to reset everything that our UpdateFields may touch to make sure next search gets all
  1651. _ResetRoots();
  1652. _fTopLevelOnly = FALSE;
  1653. _szUserInputFileSpec[0] = 0;
  1654. _szText[0] = 0;
  1655. if (_pszIndexedSearch)
  1656. *_pszIndexedSearch = 0;
  1657. _strTypeFilePatterns.Reset();
  1658. ZeroMemory(&_scidDate, sizeof(_scidDate));
  1659. _scidSize = SCID_SIZE;
  1660. _fFoldersOnly = FALSE;
  1661. _wDateType = 0;
  1662. _dateModifiedBefore = 0;
  1663. _dateModifiedAfter = 0;
  1664. _iSizeType = 0;
  1665. _ullSize = 0;
  1666. _fTextCaseSen = FALSE;
  1667. _fTextReg = FALSE;
  1668. _fSearchSlowFiles = FALSE;
  1669. _ulQueryDialect = ISQLANG_V2;
  1670. _dwWarningFlags = DFW_DEFAULT;
  1671. _fSearchSystemDirs = FALSE;
  1672. // the search UI will usually override this, but if that us has not been updated
  1673. // we need to set out state the same was as before here
  1674. SHELLSTATE ss;
  1675. SHGetSetSettings(&ss, SSF_SHOWALLOBJECTS, FALSE);
  1676. _fSearchHidden = ss.fShowAllObjects;
  1677. return S_OK;
  1678. }
  1679. HRESULT CFindFilter::GetNextConstraint(VARIANT_BOOL fReset, BSTR *pName, VARIANT *pValue, VARIANT_BOOL *pfFound)
  1680. {
  1681. *pName = NULL;
  1682. VariantClear(pValue);
  1683. *pfFound = FALSE;
  1684. if (fReset)
  1685. _iNextConstraint = 0;
  1686. HRESULT hr = S_FALSE; // not found
  1687. // we don't go to array size as the last entry is an empty item...
  1688. while (_iNextConstraint < ARRAYSIZE(s_cdffuf))
  1689. {
  1690. switch (s_cdffuf[_iNextConstraint].cdffufe)
  1691. {
  1692. case CDFFUFE_IndexedSearch:
  1693. hr = InitVariantFromStr(pValue, _pszIndexedSearch);
  1694. break;
  1695. case CDFFUFE_LookIn:
  1696. hr = InitVariantFromStr(pValue, _szPath);
  1697. break;
  1698. case CDFFUFE_IncludeSubFolders:
  1699. hr = InitVariantFromInt(pValue, _fTopLevelOnly ? 0 : 1);
  1700. break;
  1701. case CDFFUFE_Named:
  1702. hr = InitVariantFromStr(pValue, _szUserInputFileSpec);
  1703. break;
  1704. case CDFFUFE_ContainingText:
  1705. hr = InitVariantFromStr(pValue, _szText);
  1706. break;
  1707. case CDFFUFE_FileType:
  1708. hr = InitVariantFromStr(pValue, _strTypeFilePatterns);
  1709. break;
  1710. case CDFFUFE_WhichDate:
  1711. pValue->lVal = MapDateSCIDToValue(&_scidDate);
  1712. if (pValue->lVal)
  1713. hr = InitVariantFromInt(pValue, pValue->lVal);
  1714. break;
  1715. case CDFFUFE_DateLE:
  1716. if ((_wDateType & DFF_DATE_RANGEMASK) == DFF_DATE_BETWEEN)
  1717. hr = InitVariantFromDosDateTime(pValue, _dateModifiedBefore, 0);
  1718. break;
  1719. case CDFFUFE_DateGE:
  1720. if ((_wDateType & DFF_DATE_RANGEMASK) == DFF_DATE_BETWEEN)
  1721. hr = InitVariantFromDosDateTime(pValue, _dateModifiedAfter, 0);
  1722. break;
  1723. case CDFFUFE_DateNDays:
  1724. if ((_wDateType & DFF_DATE_RANGEMASK) == DFF_DATE_DAYS)
  1725. hr = InitVariantFromInt(pValue, _wDateValue);
  1726. break;
  1727. case CDFFUFE_DateNMonths:
  1728. if ((_wDateType & DFF_DATE_RANGEMASK) == DFF_DATE_MONTHS)
  1729. hr = InitVariantFromInt(pValue, _wDateValue);
  1730. break;
  1731. case CDFFUFE_SizeLE:
  1732. if (_iSizeType == 2)
  1733. hr = InitVariantFromULONGLONG(pValue, _ullSize);
  1734. break;
  1735. case CDFFUFE_SizeGE:
  1736. if (_iSizeType == 1)
  1737. hr = InitVariantFromULONGLONG(pValue, _ullSize);
  1738. break;
  1739. case CDFFUFE_TextCaseSen:
  1740. hr = InitVariantFromInt(pValue, _fTextCaseSen ? 1 : 0);
  1741. break;
  1742. case CDFFUFE_TextReg:
  1743. hr = InitVariantFromInt(pValue, _fTextReg ? 1 : 0);
  1744. break;
  1745. case CDFFUFE_SearchSlowFiles:
  1746. hr = InitVariantFromInt(pValue, _fSearchSlowFiles ? 1 : 0);
  1747. break;
  1748. case CDFFUFE_QueryDialect:
  1749. hr = InitVariantFromUINT(pValue, _ulQueryDialect);
  1750. break;
  1751. case CDFFUFE_WarningFlags:
  1752. hr = InitVariantFromUINT(pValue, _dwWarningFlags);
  1753. break;
  1754. case CDFFUFE_SearchSystemDirs:
  1755. hr = InitVariantFromUINT(pValue, _fSearchSystemDirs ? 1 : 0);
  1756. break;
  1757. case CDFFUFE_SearchHidden:
  1758. hr = InitVariantFromUINT(pValue, _fSearchHidden ? 1 : 0);
  1759. break;
  1760. }
  1761. if (S_OK == hr)
  1762. break;
  1763. if (SUCCEEDED(hr))
  1764. VariantClear(pValue);
  1765. _iNextConstraint += 1;
  1766. }
  1767. if (S_OK == hr)
  1768. {
  1769. *pName = SysAllocString(s_cdffuf[_iNextConstraint].pwszField);
  1770. if (NULL == *pName)
  1771. {
  1772. VariantClear(pValue);
  1773. hr = E_OUTOFMEMORY;
  1774. }
  1775. else
  1776. *pfFound = TRUE;
  1777. _iNextConstraint += 1; // for the next call here
  1778. }
  1779. return hr; // no error let script use the found field...
  1780. }
  1781. DWORD CFindFilter::_AddToQuery(LPWSTR *ppszBuf, DWORD *pcchBuf, LPWSTR pszAdd)
  1782. {
  1783. DWORD cchAdd = lstrlenW(pszAdd);
  1784. if (*ppszBuf && *pcchBuf > cchAdd)
  1785. {
  1786. StrCpyNW(*ppszBuf, pszAdd, *pcchBuf);
  1787. *pcchBuf -= cchAdd;
  1788. *ppszBuf += cchAdd;
  1789. }
  1790. return cchAdd;
  1791. }
  1792. DWORD AddQuerySep(DWORD *pcchBuf, LPWSTR *ppszCurrent, WCHAR bSep)
  1793. {
  1794. LPWSTR pszCurrent = *ppszCurrent;
  1795. // make sure we have room for us plus terminator...
  1796. if (*ppszCurrent && *pcchBuf >= 4)
  1797. {
  1798. *pszCurrent++ = L' ';
  1799. *pszCurrent++ = bSep;
  1800. *pszCurrent++ = L' ';
  1801. *ppszCurrent = pszCurrent;
  1802. *pcchBuf -= 3;
  1803. }
  1804. return 3; // size necessary
  1805. }
  1806. DWORD PrepareQueryParam(BOOL *pbFirst, DWORD *pcchBuf, LPWSTR *ppszCurrent)
  1807. {
  1808. if (*pbFirst)
  1809. {
  1810. *pbFirst = FALSE;
  1811. return 0; // no size necessary
  1812. }
  1813. // we're not the first property
  1814. return AddQuerySep(pcchBuf, ppszCurrent, L'&');
  1815. }
  1816. // pick the longest date query so we can avoid checking the buffer size each time we
  1817. // add something to the string
  1818. #define LONGEST_DATE 50 //lstrlen(TEXT("{prop name=access} <= 2000/12/31 23:59:59{/prop}"))+2
  1819. DWORD CFindFilter::_QueryDosDate(BOOL *pbFirst, DWORD *pcchBuf, LPWSTR *ppszCurrent, WORD wDate, BOOL bBefore)
  1820. {
  1821. LPWSTR pszCurrent = *ppszCurrent;
  1822. DWORD cchNeeded = PrepareQueryParam(pbFirst, pcchBuf, &pszCurrent);
  1823. if (pszCurrent && *pcchBuf > LONGEST_DATE)
  1824. {
  1825. FILETIME ftLocal;
  1826. DosDateTimeToFileTime(wDate, 0, &ftLocal);
  1827. FILETIME ftGMT;
  1828. LocalFileTimeToFileTime(&ftLocal, &ftGMT);
  1829. SYSTEMTIME st;
  1830. FileTimeToSystemTime(&ftGMT, &st);
  1831. IPropertyUI *ppui;
  1832. if (SUCCEEDED(_GetPropertyUI(&ppui)))
  1833. {
  1834. WCHAR szName[128];
  1835. if (SUCCEEDED(ppui->GetCannonicalName(_scidDate.fmtid, _scidDate.pid, szName, ARRAYSIZE(szName))))
  1836. {
  1837. wnsprintfW(pszCurrent, *pcchBuf, L"{prop name=%s} ", szName);
  1838. // the date syntax we use is V2, so force this dialect
  1839. _ulQueryDialect = ISQLANG_V2;
  1840. }
  1841. ppui->Release();
  1842. }
  1843. pszCurrent += lstrlenW(pszCurrent);
  1844. if (bBefore)
  1845. {
  1846. *pszCurrent++ = L'<';
  1847. // if you ask for a range like: 2/20/98 - 2/20/98 then we get no time at all
  1848. // So for before, convert H:m:ss to 23:59:59...
  1849. st.wHour = 23;
  1850. st.wMinute = 59;
  1851. st.wSecond = 59;
  1852. }
  1853. else
  1854. {
  1855. *pszCurrent++ = L'>';
  1856. }
  1857. *pszCurrent++ = L'=';
  1858. wnsprintfW(pszCurrent, *pcchBuf, L" %d/%d/%d %d:%d:%d{/prop}", st.wYear, st.wMonth, st.wDay,
  1859. st.wHour, st.wMinute, st.wSecond);
  1860. pszCurrent += lstrlenW(pszCurrent);
  1861. *ppszCurrent = pszCurrent;
  1862. *pcchBuf -= LONGEST_DATE;
  1863. }
  1864. return cchNeeded + LONGEST_DATE;
  1865. }
  1866. DWORD CFindFilter::_CIQueryFilePatterns(BOOL *pbFirst, DWORD *pcchBuf, LPWSTR *ppszCurrent, LPCWSTR pszFilePatterns)
  1867. {
  1868. WCHAR szNextPattern[MAX_PATH]; // overkill in size
  1869. BOOL fFirst = TRUE;
  1870. LPCWSTR pszNextPattern = pszFilePatterns;
  1871. DWORD cchNeeded = PrepareQueryParam(pbFirst, pcchBuf, ppszCurrent);
  1872. // Currently will have to long hand the query, may try to find shorter format once bugs
  1873. // are fixed...
  1874. cchNeeded += _AddToQuery(ppszCurrent, pcchBuf, L"(");
  1875. while ((pszNextPattern = NextPathW(pszNextPattern, szNextPattern, ARRAYSIZE(szNextPattern))) != NULL)
  1876. {
  1877. if (!fFirst)
  1878. {
  1879. cchNeeded += _AddToQuery(ppszCurrent, pcchBuf, L" | ");
  1880. }
  1881. fFirst = FALSE;
  1882. cchNeeded += _AddToQuery(ppszCurrent, pcchBuf, L"#filename ");
  1883. cchNeeded += _AddToQuery(ppszCurrent, pcchBuf, szNextPattern);
  1884. }
  1885. cchNeeded += _AddToQuery(ppszCurrent, pcchBuf, L")");
  1886. return cchNeeded;
  1887. }
  1888. DWORD CFindFilter::_CIQueryTextPatterns(BOOL *pbFirst, DWORD *pcchBuf, LPWSTR *ppszCurrent, LPWSTR pszText, BOOL bTextReg)
  1889. {
  1890. DWORD cchNeeded = PrepareQueryParam(pbFirst, pcchBuf, ppszCurrent);
  1891. cchNeeded += _AddToQuery(ppszCurrent, pcchBuf, L"{prop name=all}");
  1892. cchNeeded += _AddToQuery(ppszCurrent, pcchBuf, bTextReg? L"{regex}" : L"{phrase}");
  1893. cchNeeded += _AddToQuery(ppszCurrent, pcchBuf, pszText);
  1894. cchNeeded += _AddToQuery(ppszCurrent, pcchBuf, bTextReg? L"{/regex}{/prop}" : L"{/phrase}{/prop}");
  1895. return cchNeeded;
  1896. }
  1897. DWORD CFindFilter::_CIQuerySize(BOOL *pbFirst, DWORD *pcchBuf, LPWSTR *ppszCurrent, ULONGLONG ullSize, int iSizeType)
  1898. {
  1899. WCHAR szSize[MAX_ULONGLONG_LEN+8]; // +8 for " {/prop}"
  1900. DWORD cchNeeded = PrepareQueryParam(pbFirst, pcchBuf, ppszCurrent);
  1901. cchNeeded += _AddToQuery(ppszCurrent, pcchBuf, L"{prop name=size} ");
  1902. cchNeeded += _AddToQuery(ppszCurrent, pcchBuf, iSizeType == 1? L">" : L"<");
  1903. WCHAR szNum[MAX_ULONGLONG_LEN];
  1904. Int64ToString(ullSize, szNum, ARRAYSIZE(szNum), FALSE, NULL, 0);
  1905. wnsprintfW(szSize, *pcchBuf, L" %ws{/prop}", szNum);
  1906. cchNeeded += _AddToQuery(ppszCurrent, pcchBuf, szSize);
  1907. return cchNeeded;
  1908. }
  1909. DWORD CFindFilter::_CIQueryIndex(BOOL *pbFirst, DWORD *pcchBuf, LPWSTR *ppszCurrent, LPWSTR pszText)
  1910. {
  1911. DWORD cchNeeded = PrepareQueryParam(pbFirst, pcchBuf, ppszCurrent);
  1912. cchNeeded += _AddToQuery(ppszCurrent, pcchBuf, pszText);
  1913. return cchNeeded;
  1914. }
  1915. DWORD CFindFilter::_CIQueryShellSettings(BOOL *pbFirst, DWORD *pcchBuf, LPWSTR *ppszCurrent)
  1916. {
  1917. DWORD cchNeeded = 0;
  1918. if (!ShowSuperHidden())
  1919. {
  1920. cchNeeded += PrepareQueryParam(pbFirst, pcchBuf, ppszCurrent);
  1921. cchNeeded += _AddToQuery(ppszCurrent, pcchBuf, L"NOT @attrib ^a 0x6 ");// don't show files w/ hidden and system bit on
  1922. }
  1923. SHELLSTATE ss;
  1924. SHGetSetSettings(&ss, SSF_SHOWALLOBJECTS, FALSE);
  1925. if (!ss.fShowAllObjects)
  1926. {
  1927. cchNeeded += PrepareQueryParam(pbFirst, pcchBuf, ppszCurrent);
  1928. cchNeeded += _AddToQuery(ppszCurrent, pcchBuf, L"NOT @attrib ^a 0x2 "); // don't show files w/ hidden bit on
  1929. }
  1930. return cchNeeded;
  1931. }
  1932. // Helper function to add the PIDL from theCsidl to the exclude tree.
  1933. void _AddSystemDirCSIDLToPidlTree(int csidl, CIDLTree *ppidlTree)
  1934. {
  1935. LPITEMIDLIST pidl = SHCloneSpecialIDList(NULL, csidl, TRUE);
  1936. if (pidl)
  1937. {
  1938. ppidlTree->AddData(IDLDATAF_MATCH_RECURSIVE, pidl, EXCLUDE_SYSTEMDIR);
  1939. ILFree(pidl);
  1940. }
  1941. }
  1942. CNamespaceEnum::CNamespaceEnum(IFindFilter *pfilter, IShellFolder* psf,
  1943. IFindEnum *penumAsync, IEnumIDList *penumScopes,
  1944. HWND hwnd, DWORD grfFlags, LPTSTR pszProgressText) :
  1945. _cRef(1), _pfilter(pfilter), _iFolder(-1), _hwnd(hwnd), _grfFlags(grfFlags),
  1946. _pszProgressText(pszProgressText), _penumAsync(penumAsync)
  1947. {
  1948. ASSERT(NULL == _psf);
  1949. ASSERT(NULL == _pidlFolder);
  1950. ASSERT(NULL == _pidlCurrentRootScope);
  1951. ASSERT(NULL == _penum);
  1952. if (penumScopes)
  1953. penumScopes->Clone(&_penumScopes);
  1954. _pfilter->AddRef();
  1955. psf->QueryInterface(IID_PPV_ARG(IFindFolder, &_pff));
  1956. ASSERT(_pff);
  1957. if (_penumAsync)
  1958. _penumAsync->AddRef();
  1959. // Setup the exclude system directories:
  1960. if (!(_grfFlags & DFOO_SEARCHSYSTEMDIRS))
  1961. {
  1962. // IE History and cache are excluded based on the CLSID.
  1963. _AddSystemDirCSIDLToPidlTree(CSIDL_WINDOWS, &_treeExcludeFolders);
  1964. _AddSystemDirCSIDLToPidlTree(CSIDL_PROGRAM_FILES, &_treeExcludeFolders);
  1965. // Exclude Temp folder
  1966. TCHAR szPath[MAX_PATH];
  1967. if (GetTempPath(ARRAYSIZE(szPath), szPath))
  1968. {
  1969. LPITEMIDLIST pidl = ILCreateFromPath(szPath);
  1970. if (pidl)
  1971. {
  1972. _treeExcludeFolders.AddData(IDLDATAF_MATCH_RECURSIVE, pidl, EXCLUDE_SYSTEMDIR);
  1973. ILFree(pidl);
  1974. }
  1975. }
  1976. }
  1977. }
  1978. CNamespaceEnum::~CNamespaceEnum()
  1979. {
  1980. ATOMICRELEASE(_penumScopes);
  1981. ATOMICRELEASE(_pfilter);
  1982. ATOMICRELEASE(_psf);
  1983. ATOMICRELEASE(_penumAsync);
  1984. ATOMICRELEASE(_pff);
  1985. ILFree(_pidlFolder); // accepts NULL
  1986. ILFree(_pidlCurrentRootScope);
  1987. }
  1988. STDMETHODIMP CNamespaceEnum::QueryInterface(REFIID riid, void **ppv)
  1989. {
  1990. static const QITAB qit[] = {
  1991. // QITABENT(CNamespaceEnum, IFindEnum),
  1992. { 0 },
  1993. };
  1994. return QISearch(this, qit, riid, ppv);
  1995. }
  1996. STDMETHODIMP_(ULONG) CNamespaceEnum::AddRef()
  1997. {
  1998. return InterlockedIncrement(&_cRef);
  1999. }
  2000. STDMETHODIMP_(ULONG) CNamespaceEnum::Release()
  2001. {
  2002. ASSERT( 0 != _cRef );
  2003. ULONG cRef = InterlockedDecrement(&_cRef);
  2004. if ( 0 == cRef )
  2005. {
  2006. delete this;
  2007. }
  2008. return cRef;
  2009. }
  2010. // Check if the relative pidl passed is to a folder that we are going
  2011. // skip based on its CLSID:
  2012. // This will be used to skip IE History and IE Cache.
  2013. BOOL CNamespaceEnum::_IsSystemFolderByCLSID(LPCITEMIDLIST pidl)
  2014. {
  2015. BOOL bRetVal = FALSE;
  2016. IShellFolder2 *psf2;
  2017. if (_psf && SUCCEEDED(_psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2))))
  2018. {
  2019. CLSID clsid;
  2020. if (SUCCEEDED(GetItemCLSID(psf2, pidl, &clsid)))
  2021. {
  2022. if (IsEqualCLSID(clsid, CLSID_CacheFolder) ||
  2023. IsEqualCLSID(clsid, CLSID_CacheFolder2) ||
  2024. IsEqualCLSID(clsid, CLSID_HistFolder))
  2025. {
  2026. bRetVal = TRUE;
  2027. }
  2028. }
  2029. psf2->Release();
  2030. }
  2031. return bRetVal;
  2032. }
  2033. // given the fact that a file is a directory, is this one we should search???
  2034. BOOL CNamespaceEnum::_ShouldPushItem(LPCITEMIDLIST pidl)
  2035. {
  2036. BOOL bShouldPush = FALSE;
  2037. TCHAR szName[MAX_PATH];
  2038. // folders only, not folder shortcuts (note, this includes SFGAO_STREAM objects, .zip/.cab files)
  2039. // skip all folders that start with '?'. these are folders that the names got trashed in some
  2040. // ansi/unicode round trip. this avoids problems in the web folders name space
  2041. if (SFGAO_FOLDER == SHGetAttributes(_psf, pidl, SFGAO_FOLDER | SFGAO_LINK) &&
  2042. SUCCEEDED(DisplayNameOf(_psf, pidl, SHGDN_INFOLDER | SHGDN_FORPARSING, szName, ARRAYSIZE(szName))) &&
  2043. (TEXT('?') != szName[0]))
  2044. {
  2045. LPITEMIDLIST pidlFull = ILCombine(_pidlFolder, pidl);
  2046. if (pidlFull)
  2047. {
  2048. INT_PTR i = 0;
  2049. // Check if the folder is in the exclude list because it has been searched
  2050. HRESULT hr = _treeExcludeFolders.MatchOne(IDLDATAF_MATCH_RECURSIVE, pidlFull, &i, NULL);
  2051. if (FAILED(hr))
  2052. {
  2053. // see if an alias version of this pidl is exists
  2054. LPITEMIDLIST pidlAlias = SHLogILFromFSIL(pidlFull);
  2055. if (pidlAlias)
  2056. {
  2057. hr = _treeExcludeFolders.MatchOne(IDLDATAF_MATCH_RECURSIVE, pidlAlias, &i, NULL);
  2058. ILFree(pidlAlias);
  2059. }
  2060. }
  2061. if (FAILED(hr))
  2062. {
  2063. // If we still think it should be added, check if we can reject it based
  2064. // on its CSILD. We will only exclude system folders this way.
  2065. bShouldPush = (_grfFlags & DFOO_SEARCHSYSTEMDIRS) ||
  2066. (!_IsSystemFolderByCLSID(pidl));
  2067. }
  2068. else if (i == EXCLUDE_SYSTEMDIR)
  2069. {
  2070. // If it is under a system directory exclude, check to see if it is the
  2071. // directory or a sub directory. We want to exclude the exact directory
  2072. // so that we don't add a system directory to the list of things to search.
  2073. // Since we may have specified the directory in the list of places to search
  2074. // and therefore want to search it, we don't want to exclude sub directories
  2075. // that way.
  2076. hr = _treeExcludeFolders.MatchOne(IDLDATAF_MATCH_EXACT, pidlFull, &i, NULL);
  2077. if (FAILED(hr))
  2078. {
  2079. // If we get here, it means that pidlFull is a sub directory of a
  2080. // system directory which was searched because it was explicitly
  2081. // specified in the list of scopes to search. Therefore we want
  2082. // to continue to search the sub directories.
  2083. bShouldPush = TRUE;
  2084. }
  2085. }
  2086. else
  2087. {
  2088. // It matched an item in the tree and was not EXCLUDE_SYSTEMDIR:
  2089. ASSERT(i == EXCLUDE_SEARCHED);
  2090. }
  2091. ILFree(pidlFull);
  2092. }
  2093. }
  2094. return bShouldPush;
  2095. }
  2096. IShellFolder *CNamespaceEnum::_NextRootScope()
  2097. {
  2098. IShellFolder *psf = NULL;
  2099. if (_penumScopes)
  2100. {
  2101. LPITEMIDLIST pidl;
  2102. if (S_OK == _penumScopes->Next(1, &pidl, NULL))
  2103. {
  2104. SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, pidl, &psf));
  2105. ILFree(pidl);
  2106. }
  2107. }
  2108. return psf;
  2109. }
  2110. STDMETHODIMP CNamespaceEnum::Next(LPITEMIDLIST *ppidl, int *pcObjectSearched,
  2111. int *pcFoldersSearched, BOOL *pfContinue, int *piState)
  2112. {
  2113. *ppidl = NULL;
  2114. *piState = GNF_NOMATCH;
  2115. HRESULT hrRet = S_FALSE;
  2116. while (S_OK != hrRet && *pfContinue)
  2117. {
  2118. // Retrieve an enumerator if we don't already have one.
  2119. while (NULL == _penum)
  2120. {
  2121. // Indicates that we have taken scope from _penumScopes
  2122. BOOL fUseRootScope = FALSE;
  2123. ASSERT(NULL == _psf);
  2124. // first try popping subdirs off folder stack
  2125. _psf = _queSubFolders.Remove();
  2126. // If there are no folders pushed, then try to get on from the caller.. (root scopes)
  2127. if (NULL == _psf)
  2128. {
  2129. // Since we are getting a new root scope, add old one to exclude list
  2130. if (_pidlCurrentRootScope)
  2131. {
  2132. // Add to exclude List.
  2133. if (_grfFlags & DFOO_INCLUDESUBDIRS)
  2134. {
  2135. // Since all sub directories will be search, don't search again.
  2136. _treeExcludeFolders.AddData(IDLDATAF_MATCH_RECURSIVE, _pidlCurrentRootScope, EXCLUDE_SEARCHED);
  2137. }
  2138. else
  2139. {
  2140. // Since sub directories have not been search, allow them to be searched.
  2141. _treeExcludeFolders.AddData(IDLDATAF_MATCH_EXACT, _pidlCurrentRootScope, EXCLUDE_SEARCHED);
  2142. }
  2143. ILFree(_pidlCurrentRootScope);
  2144. _pidlCurrentRootScope = NULL;
  2145. }
  2146. // Get scope from list passed in from caller (root scopes)
  2147. _psf = _NextRootScope();
  2148. fUseRootScope = TRUE;
  2149. }
  2150. if (_psf)
  2151. {
  2152. HRESULT hrT = SHGetIDListFromUnk(_psf, &_pidlFolder);
  2153. if (SUCCEEDED(hrT) && fUseRootScope)
  2154. {
  2155. // Check if the pidl is in the tree
  2156. INT_PTR i = 0;
  2157. HRESULT hrMatch = _treeExcludeFolders.MatchOne(IDLDATAF_MATCH_RECURSIVE, _pidlFolder, &i, NULL);
  2158. // If we have a new root scope, set that up as current root scope pidl:
  2159. // We only want to skip pidls from the queue of "root" search scopes
  2160. // if they have already been searched. We do not want to exclude a directory
  2161. // (from exclude system directories) if it is an explicit search scope.
  2162. if (FAILED(hrMatch) || i == EXCLUDE_SYSTEMDIR)
  2163. {
  2164. ASSERT(_pidlCurrentRootScope == NULL);
  2165. _pidlCurrentRootScope = ILClone(_pidlFolder);
  2166. }
  2167. else
  2168. {
  2169. // Since the PIDL is in the exclude tree, we do not want to search it.
  2170. hrT = E_FAIL;
  2171. }
  2172. }
  2173. if (SUCCEEDED(hrT))
  2174. {
  2175. // Check that we have a pidl, that it is not to be excluded, and that it can
  2176. // be enumerated.
  2177. SHCONTF contf = SHCONTF_FOLDERS | SHCONTF_NONFOLDERS;
  2178. if (_grfFlags & DFOO_SHOWALLOBJECTS)
  2179. contf |= SHCONTF_INCLUDEHIDDEN;
  2180. hrT = _psf->EnumObjects(_hwnd, contf, &_penum);
  2181. // only do UI on the first enum, all others are silent
  2182. // this makes doing search on A:\ produce the insert media
  2183. // prompt, but searching MyComputer does not prompt for all
  2184. // empty media
  2185. _hwnd = NULL;
  2186. if (S_OK == hrT)
  2187. {
  2188. SHGetNameAndFlags(_pidlFolder, SHGDN_FORPARSING | SHGDN_FORADDRESSBAR, _pszProgressText, MAX_PATH, NULL);
  2189. (*pcFoldersSearched)++;
  2190. }
  2191. }
  2192. // Check for cleaning up...
  2193. if (hrT != S_OK)
  2194. {
  2195. ASSERT(NULL == _penum);
  2196. ATOMICRELEASE(_psf); // and continue...
  2197. ILFree(_pidlFolder);
  2198. _pidlFolder = NULL;
  2199. }
  2200. }
  2201. else // no scope
  2202. {
  2203. *piState = GNF_DONE;
  2204. return hrRet;
  2205. }
  2206. }
  2207. // Retrieve item
  2208. LPITEMIDLIST pidl;
  2209. HRESULT hrEnum = S_FALSE;
  2210. while ((S_OK != hrRet) && *pfContinue && (S_OK == (hrEnum = _penum->Next(1, &pidl, NULL))))
  2211. {
  2212. (*pcObjectSearched)++;
  2213. // Determine if this is a subfolder that should be recursively searched.
  2214. if (_grfFlags & DFOO_INCLUDESUBDIRS)
  2215. {
  2216. if (_ShouldPushItem(pidl))
  2217. {
  2218. // queue folder to stack of subfolders to be searched in the next round.
  2219. _queSubFolders.Add(_psf, pidl);
  2220. }
  2221. }
  2222. // Test item against search criteria:
  2223. if (_pfilter->MatchFilter(_psf, pidl))
  2224. {
  2225. // folder has not been registered with docfind folder?
  2226. if (_iFolder < 0)
  2227. {
  2228. // add folder to folder list.
  2229. _pff->AddFolder(_pidlFolder, TRUE, &_iFolder);
  2230. ASSERT(_iFolder >= 0);
  2231. }
  2232. // add docfind goo to pidl
  2233. hrRet = _pff->AddDataToIDList(pidl, _iFolder, _pidlFolder, DFDF_NONE, 0, 0, 0, ppidl);
  2234. if (SUCCEEDED(hrRet))
  2235. *piState = GNF_MATCH;
  2236. }
  2237. else
  2238. {
  2239. ASSERT(GNF_NOMATCH == *piState);
  2240. hrRet = S_OK; // exit loop, without a match
  2241. }
  2242. ILFree(pidl);
  2243. }
  2244. if (!*pfContinue)
  2245. {
  2246. *piState = GNF_DONE;
  2247. hrEnum = S_FALSE;
  2248. }
  2249. if (S_OK != hrEnum)
  2250. {
  2251. // Done enumerating this folder - clean up for next iteration; or..
  2252. // Failed miserably - clean up prior to bail.
  2253. ATOMICRELEASE(_penum);
  2254. ATOMICRELEASE(_psf);
  2255. ILFree(_pidlFolder);
  2256. _pidlFolder = NULL;
  2257. _iFolder = -1;
  2258. }
  2259. }
  2260. return hrRet;
  2261. }
  2262. STDMETHODIMP CNamespaceEnum::StopSearch()
  2263. {
  2264. if (_penumAsync)
  2265. return _penumAsync->StopSearch();
  2266. return E_NOTIMPL;
  2267. }
  2268. STDMETHODIMP_(BOOL) CNamespaceEnum::FQueryIsAsync()
  2269. {
  2270. if (_penumAsync)
  2271. return DF_QUERYISMIXED; // non-zero special number to say both...
  2272. return FALSE;
  2273. }
  2274. STDMETHODIMP CNamespaceEnum::GetAsyncCount(DBCOUNTITEM *pdwTotalAsync, int *pnPercentComplete, BOOL *pfQueryDone)
  2275. {
  2276. if (_penumAsync)
  2277. return _penumAsync->GetAsyncCount(pdwTotalAsync, pnPercentComplete, pfQueryDone);
  2278. *pdwTotalAsync = 0;
  2279. return E_NOTIMPL;
  2280. }
  2281. STDMETHODIMP CNamespaceEnum::GetItemIDList(UINT iItem, LPITEMIDLIST *ppidl)
  2282. {
  2283. if (_penumAsync)
  2284. return _penumAsync->GetItemIDList(iItem, ppidl);
  2285. *ppidl = NULL;
  2286. return E_NOTIMPL;
  2287. }
  2288. STDMETHODIMP CNamespaceEnum::GetItemID(UINT iItem, DWORD *puWorkID)
  2289. {
  2290. if (_penumAsync)
  2291. return _penumAsync->GetItemID(iItem, puWorkID);
  2292. *puWorkID = (UINT)-1;
  2293. return E_NOTIMPL;
  2294. }
  2295. STDMETHODIMP CNamespaceEnum::SortOnColumn(UINT iCol, BOOL fAscending)
  2296. {
  2297. if (_penumAsync)
  2298. return _penumAsync->SortOnColumn(iCol, fAscending);
  2299. return E_NOTIMPL;
  2300. }
  2301. // Masks used to indicate which search operation we are doing.
  2302. #define AND_MASK 0x01
  2303. #define OR_MASK 0x02
  2304. #define SEMICOLON_MASK 0x04
  2305. #define COMMA_MASK 0x08
  2306. #define EXTENSION_MASK 0x10
  2307. // Both "*" and "?" are treated as wildcards.
  2308. BOOL SetupWildCardingOnFileSpec(LPTSTR pszSpecIn, LPTSTR *ppszSpecOut)
  2309. {
  2310. LPTSTR pszIn = pszSpecIn;
  2311. BOOL fQuote;
  2312. TCHAR szSpecOut[3*MAX_PATH]; // Rather large...
  2313. // Read in localized versions of AND/OR used for searching
  2314. TCHAR szAND[20];
  2315. LoadString(HINST_THISDLL, IDS_FIND_AND, szAND, ARRAYSIZE(szAND));
  2316. TCHAR szOR[20];
  2317. LoadString(HINST_THISDLL, IDS_FIND_OR, szOR, ARRAYSIZE(szOR));
  2318. // Masks and variable to indicate what operation we are going to perform.
  2319. UINT iOperation = 0; // Bitmask to store which operation we have selected.
  2320. // allocate a buffer that should be able to hold the resultant
  2321. // string. When all is said and done we'll re-allocate to the
  2322. // correct size.
  2323. LPTSTR pszOut = szSpecOut;
  2324. while (*pszIn != 0)
  2325. {
  2326. TCHAR c; // The delimiter.
  2327. LPTSTR pszT;
  2328. int ich;
  2329. // Strip in leading spaces out of there
  2330. while (*pszIn == TEXT(' '))
  2331. pszIn++;
  2332. if (*pszIn == 0)
  2333. break;
  2334. // If we are beyond the first item, add a seperator between the tokens
  2335. if (pszOut != szSpecOut)
  2336. *pszOut++ = TEXT(';');
  2337. fQuote = (*pszIn == TEXT('"'));
  2338. if (fQuote)
  2339. {
  2340. // The user asked for something litteral.
  2341. pszT = pszIn = CharNext(pszIn);
  2342. while (*pszT && (*pszT != TEXT('"')))
  2343. pszT = CharNext(pszT);
  2344. }
  2345. else
  2346. {
  2347. pszT = pszIn + (ich = StrCSpn(pszIn, TEXT(",; \""))); // Find end of token
  2348. }
  2349. c = *pszT; // Save away the seperator character that was found
  2350. *pszT = 0; // Add null so string functions will work and only extract the token
  2351. // Put in a couple of tests for * and *.*
  2352. if ((lstrcmp(pszIn, c_szStar) == 0) ||
  2353. (lstrcmp(pszIn, c_szStarDotStar) == 0))
  2354. {
  2355. // Complete wild card so set a null criteria
  2356. *pszT = c; // Restore char;
  2357. pszOut = szSpecOut; // Set to start of string
  2358. break;
  2359. }
  2360. if (fQuote)
  2361. {
  2362. lstrcpy(pszOut, pszIn);
  2363. pszOut += lstrlen(pszIn);
  2364. }
  2365. else if (lstrcmpi(pszIn, szAND) == 0)
  2366. {
  2367. iOperation |= AND_MASK;
  2368. // If we don't move back one character, then "New and folder" will give:
  2369. // "*New*;;*folder*"
  2370. if (pszOut != szSpecOut)
  2371. --pszOut;
  2372. }
  2373. else if (lstrcmpi(pszIn, szOR) == 0)
  2374. {
  2375. iOperation |= OR_MASK;
  2376. // If we don't move back one character, then "New or folder" will give:
  2377. // "*New*;;*folder*"
  2378. if (pszOut != szSpecOut)
  2379. --pszOut;
  2380. }
  2381. else if (*pszIn == 0)
  2382. {
  2383. // If we don't move back one character, then "New ; folder" will give:
  2384. // "*New*;**;*folder*"
  2385. if (pszOut != szSpecOut)
  2386. --pszOut;
  2387. // Check what the seperator is. This handles instances like
  2388. // ("abba" ; "abba2") where we want an OR search.
  2389. if (c == TEXT(','))
  2390. {
  2391. iOperation |= COMMA_MASK;
  2392. }
  2393. else if (c == TEXT(';'))
  2394. {
  2395. iOperation |= SEMICOLON_MASK;
  2396. }
  2397. }
  2398. else
  2399. {
  2400. // Check what the seperator is:
  2401. if (c == TEXT(','))
  2402. {
  2403. iOperation |= COMMA_MASK;
  2404. }
  2405. else if (c == TEXT(';'))
  2406. {
  2407. iOperation |= SEMICOLON_MASK;
  2408. }
  2409. // both "*" and "?" are wildcards. When checking for wildcards check
  2410. // for both before we conclude there are no wildcards. If a search
  2411. // string contains both "*" and "?" then we need for pszStar to point
  2412. // to the last occorance of either one (this is assumed in the code
  2413. // below which will add a ".*" when pszStar is the last character).
  2414. // NOTE: I wish there was a StrRPBrk function to do this for me.
  2415. LPTSTR pszStar = StrRChr(pszIn, NULL, TEXT('*'));
  2416. LPTSTR pszAnyC = StrRChr(pszIn, NULL, TEXT('?'));
  2417. if (pszAnyC > pszStar)
  2418. pszStar = pszAnyC;
  2419. if (pszStar == NULL)
  2420. {
  2421. // No wildcards were used:
  2422. *pszOut++ = TEXT('*');
  2423. lstrcpy(pszOut, pszIn);
  2424. pszOut += ich;
  2425. *pszOut++ = TEXT('*');
  2426. }
  2427. else
  2428. {
  2429. // Includes wild cards
  2430. lstrcpy(pszOut, pszIn);
  2431. pszOut += ich;
  2432. pszAnyC = StrRChr(pszIn, NULL, TEXT('.'));
  2433. if (pszAnyC)
  2434. {
  2435. // extension present, that implies OR search
  2436. iOperation |= EXTENSION_MASK;
  2437. }
  2438. else
  2439. {
  2440. // No extension is given
  2441. if ((*(pszStar+1) == 0) && (*pszStar == TEXT('*')))
  2442. {
  2443. // The last character is an "*" so this single string will
  2444. // match everything you would expect.
  2445. }
  2446. else
  2447. {
  2448. // Before, given "a*a" we searched for "a*a" as well
  2449. // as "a*a.*". We can no longer do that because if we are
  2450. // doing an AND search, it will exclude any item that does not
  2451. // match both of the criterial. For example, "abba" mattches
  2452. // "a*a" but not "a*a.*" and "abba.txt" matches "a*a.*" but
  2453. // not "a*a". Therefore, we append an * to get "a*a*". This
  2454. // will match files like "abba2.wav" which wouldn't previously
  2455. // have been matched, but it is a small price to pay.
  2456. *pszOut++ = TEXT('*');
  2457. }
  2458. }
  2459. }
  2460. }
  2461. *pszT = c; // Restore char;
  2462. if (c == 0)
  2463. break;
  2464. // Skip the seperator except if we weren't quoting and the seperator is
  2465. // a '"' then we found something like (blah"next tag")
  2466. if (*pszT != 0 && !(*pszT == TEXT('"') && !fQuote))
  2467. pszT++;
  2468. pszIn = pszT; // setup for the next item
  2469. }
  2470. // Ensure the string is terminated
  2471. *pszOut++ = 0;
  2472. // re-alloc the buffer down to the actual size of the string...
  2473. Str_SetPtr(ppszSpecOut, szSpecOut);
  2474. // Precidence rules to be applied in order:
  2475. // 1. ; -> OR search
  2476. // 2. AND -> AND search
  2477. // 3. , or OR -> OR search
  2478. // 4. none & explict file extensions -> OR search (files can only have one extension)
  2479. // 5. none -> AND search
  2480. //
  2481. //
  2482. // AND OR ; , | AND Search
  2483. // X X 1 X | 0
  2484. // 1 X 0 X | 1
  2485. // 0 \ 0 \ | 0 Where one if the '\'s is 1
  2486. // 0 0 0 0 | 1
  2487. return (!(iOperation & SEMICOLON_MASK) && (iOperation & AND_MASK)) || iOperation == 0;
  2488. }
  2489. WORD CFindFilter::_GetTodaysDosDateMinusNDays(int nDays)
  2490. {
  2491. SYSTEMTIME st;
  2492. union
  2493. {
  2494. FILETIME ft;
  2495. LARGE_INTEGER li;
  2496. }ftli;
  2497. WORD FatTime = 0, FatDate = 0;
  2498. // Now we need to
  2499. GetSystemTime(&st);
  2500. SystemTimeToFileTime(&st, &ftli.ft);
  2501. FileTimeToLocalFileTime(&ftli.ft, &ftli.ft);
  2502. // Now decrement the file time by the count of days * the number of
  2503. // 100NS time units per day. Assume that nDays is positive.
  2504. if (nDays > 0)
  2505. {
  2506. #define NANO_SECONDS_PER_DAY 864000000000
  2507. ftli.li.QuadPart = ftli.li.QuadPart - ((__int64)nDays * NANO_SECONDS_PER_DAY);
  2508. }
  2509. FileTimeToDosDateTime(&ftli.ft, &FatDate, &FatTime);
  2510. DebugMsg(DM_TRACE, TEXT("DocFind %d days = %x"), nDays, FatDate);
  2511. return FatDate;
  2512. }
  2513. WORD CFindFilter::_GetTodaysDosDateMinusNMonths(int nMonths)
  2514. {
  2515. SYSTEMTIME st;
  2516. FILETIME ft;
  2517. WORD FatTime, FatDate;
  2518. GetSystemTime(&st);
  2519. st.wYear -= (WORD) nMonths / 12;
  2520. nMonths = nMonths % 12;
  2521. if (nMonths < st.wMonth)
  2522. st.wMonth -= (WORD) nMonths;
  2523. else
  2524. {
  2525. st.wYear--;
  2526. st.wMonth = (WORD)(12 - (nMonths - st.wMonth));
  2527. }
  2528. // Now normalize back to a valid date.
  2529. while (!SystemTimeToFileTime(&st, &ft))
  2530. {
  2531. st.wDay--; // must not be valid date for month...
  2532. }
  2533. if (!FileTimeToLocalFileTime(&ft, &ft) || !FileTimeToDosDateTime(&ft, &FatDate,&FatTime))
  2534. FatDate = 0; //search for all the files from beginning of time (better to find more than less)
  2535. DebugMsg(DM_TRACE, TEXT("DocFind %d months = %x"), nMonths, FatDate);
  2536. return FatDate;
  2537. }