StoreHelper.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786
  1. /*
  2. * Copyright 2005 - 2016 Zarafa and its licensors
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU Affero General Public License, version 3,
  6. * as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. *
  16. */
  17. #include <kopano/platform.h>
  18. #include <memory>
  19. #include <new>
  20. #include <utility>
  21. #include <mapix.h>
  22. #include <mapiutil.h>
  23. #include "StoreHelper.h"
  24. using namespace std;
  25. #include <kopano/mapi_ptr.h>
  26. #include <kopano/mapiguidext.h>
  27. #include <kopano/archiver-common.h>
  28. #include <kopano/ECRestriction.h>
  29. namespace KC { namespace helpers {
  30. /**
  31. * The version of the current search folder restrictions. The version is the same
  32. * for all three folders, so if one restriction changes, they all get a new version.
  33. *
  34. * version 1: The search used as in V1 of the archiver.
  35. * version 2: Check if the original source key and current sourcekey match when stubbed equals true.
  36. */
  37. #define ARCHIVE_SEARCH_VERSION 2
  38. const StoreHelper::search_folder_info_t StoreHelper::s_infoSearchFolders[] = {
  39. {_T("Archive"), _T("This folder contains messages that are eligible for archiving"), &StoreHelper::SetupSearchArchiveFolder},
  40. {_T("Delete"), _T("This folder contains messages that are eligible for deletion"), &StoreHelper::SetupSearchDeleteFolder},
  41. {_T("Stub"), _T("This folder contains messages that are eligible for stubbing"), &StoreHelper::SetupSearchStubFolder}
  42. };
  43. /**
  44. * Create a new StoreHelper object.
  45. *
  46. * @param[in] ptrMsgStore
  47. * A MsgStorePtr that points to the message store for which to
  48. * create a StoreHelper.
  49. * @param[out] lpptrStoreHelper
  50. * Pointer to a StoreHelperPtr that will be assigned the address
  51. * of the new StoreHelper object.
  52. */
  53. HRESULT StoreHelper::Create(MsgStorePtr &ptrMsgStore, StoreHelperPtr *lpptrStoreHelper)
  54. {
  55. HRESULT hr;
  56. StoreHelperPtr ptrStoreHelper(new(std::nothrow) StoreHelper(ptrMsgStore));
  57. if (ptrStoreHelper == nullptr)
  58. return MAPI_E_NOT_ENOUGH_MEMORY;
  59. hr = ptrStoreHelper->Init();
  60. if (hr != hrSuccess)
  61. return hr;
  62. *lpptrStoreHelper = std::move(ptrStoreHelper);
  63. return hrSuccess;
  64. }
  65. /**
  66. * Constructor.
  67. */
  68. StoreHelper::StoreHelper(MsgStorePtr &ptrMsgStore)
  69. : MAPIPropHelper(ptrMsgStore.as<MAPIPropPtr>())
  70. , m_ptrMsgStore(ptrMsgStore), __propmap(8)
  71. { }
  72. /**
  73. * Initialize a StoreHelper object.
  74. */
  75. HRESULT StoreHelper::Init()
  76. {
  77. HRESULT hr;
  78. hr = MAPIPropHelper::Init();
  79. if (hr != hrSuccess)
  80. goto exitpm;
  81. PROPMAP_INIT_NAMED_ID(ARCHIVE_STORE_ENTRYIDS, PT_MV_BINARY, PSETID_Archive, dispidStoreEntryIds)
  82. PROPMAP_INIT_NAMED_ID(ARCHIVE_ITEM_ENTRYIDS, PT_MV_BINARY, PSETID_Archive, dispidItemEntryIds)
  83. PROPMAP_INIT_NAMED_ID(ORIGINAL_SOURCEKEY, PT_BINARY, PSETID_Archive, dispidOrigSourceKey)
  84. PROPMAP_INIT_NAMED_ID(SEARCH_FOLDER_ENTRYIDS, PT_MV_BINARY, PSETID_Archive, dispidSearchFolderEntryIds)
  85. PROPMAP_INIT_NAMED_ID(STUBBED, PT_BOOLEAN, PSETID_Archive, dispidStubbed)
  86. PROPMAP_INIT_NAMED_ID(DIRTY, PT_BOOLEAN, PSETID_Archive, dispidDirty)
  87. PROPMAP_INIT_NAMED_ID(FLAGS, PT_LONG, PSETID_Archive, dispidFlags)
  88. PROPMAP_INIT_NAMED_ID(VERSION, PT_LONG, PSETID_Archive, dispidVersion)
  89. PROPMAP_INIT(m_ptrMsgStore)
  90. exitpm:
  91. return hr;
  92. }
  93. /**
  94. * Open or optionaly create a folder by name from the IPM subtree.
  95. *
  96. * @param[in] strFolder
  97. * The name of the folder to open or create.
  98. * @param[in] bCreate
  99. * Set to true to create the folder if it doesn't exist.
  100. * @param[out] lppFolder
  101. * Pointer to a IMapiFolder pointer that will be assigned the address
  102. * of the returned folderd.
  103. */
  104. HRESULT StoreHelper::GetFolder(const tstring &strFolder, bool bCreate, LPMAPIFOLDER *lppFolder)
  105. {
  106. HRESULT hr;
  107. MAPIFolderPtr ptrIpmSubtree;
  108. MAPIFolderPtr ptrFolder;
  109. hr = GetIpmSubtree(&~ptrIpmSubtree);
  110. if (hr != hrSuccess)
  111. return hr;
  112. hr = GetSubFolder(ptrIpmSubtree, strFolder, bCreate, &~ptrFolder);
  113. if (hr != hrSuccess)
  114. return hr;
  115. return ptrFolder->QueryInterface(IID_IMAPIFolder,
  116. reinterpret_cast<LPVOID *>(lppFolder));
  117. }
  118. /**
  119. * Create or reinitialize the searchfolders used by the archiver. The search folders
  120. * need to be updated whenever the set of attached archives for a store change.
  121. */
  122. HRESULT StoreHelper::UpdateSearchFolders()
  123. {
  124. HRESULT hr;
  125. MAPIFolderPtr ptrSearchArchiveFolder;
  126. MAPIFolderPtr ptrSearchDeleteFolder;
  127. MAPIFolderPtr ptrSearchStubFolder;
  128. SPropValuePtr ptrSearchArchiveEntryId;
  129. SPropValuePtr ptrSearchDeleteEntryId;
  130. SPropValuePtr ptrSearchStubEntryId;
  131. SPropValuePtr ptrPropValue;
  132. hr = CreateSearchFolders(&~ptrSearchArchiveFolder, &~ptrSearchDeleteFolder, &~ptrSearchStubFolder);
  133. if (hr != hrSuccess)
  134. return hr;
  135. assert(ptrSearchArchiveFolder);
  136. assert(ptrSearchDeleteFolder);
  137. assert(ptrSearchStubFolder);
  138. hr = HrGetOneProp(ptrSearchArchiveFolder, PR_ENTRYID, &~ptrSearchArchiveEntryId);
  139. if (hr != hrSuccess)
  140. return hr;
  141. hr = HrGetOneProp(ptrSearchDeleteFolder, PR_ENTRYID, &~ptrSearchDeleteEntryId);
  142. if (hr != hrSuccess)
  143. return hr;
  144. hr = HrGetOneProp(ptrSearchStubFolder, PR_ENTRYID, &~ptrSearchStubEntryId);
  145. if (hr != hrSuccess)
  146. return hr;
  147. hr = MAPIAllocateBuffer(sizeof(SPropValue), &~ptrPropValue);
  148. if (hr != hrSuccess)
  149. return hr;
  150. ptrPropValue->ulPropTag = PROP_SEARCH_FOLDER_ENTRYIDS;
  151. ptrPropValue->Value.MVbin.cValues = 3;
  152. hr = MAPIAllocateMore(3 * sizeof(*ptrPropValue->Value.MVbin.lpbin), ptrPropValue, (LPVOID*)&ptrPropValue->Value.MVbin.lpbin);
  153. if (hr != hrSuccess)
  154. return hr;
  155. ptrPropValue->Value.MVbin.lpbin[0].cb = ptrSearchArchiveEntryId->Value.bin.cb;
  156. ptrPropValue->Value.MVbin.lpbin[0].lpb = ptrSearchArchiveEntryId->Value.bin.lpb;
  157. ptrPropValue->Value.MVbin.lpbin[1].cb = ptrSearchDeleteEntryId->Value.bin.cb;
  158. ptrPropValue->Value.MVbin.lpbin[1].lpb = ptrSearchDeleteEntryId->Value.bin.lpb;
  159. ptrPropValue->Value.MVbin.lpbin[2].cb = ptrSearchStubEntryId->Value.bin.cb;
  160. ptrPropValue->Value.MVbin.lpbin[2].lpb = ptrSearchStubEntryId->Value.bin.lpb;
  161. hr = HrSetOneProp(m_ptrMsgStore, ptrPropValue);
  162. if (hr != hrSuccess)
  163. return hr;
  164. return hrSuccess;
  165. }
  166. /**
  167. * Get the IPM subtree.
  168. *
  169. * @param[out] lppFolder
  170. * Pointer to an IMAPIFolder pointer that will be assigned the
  171. * address of the returned folder.
  172. */
  173. HRESULT StoreHelper::GetIpmSubtree(LPMAPIFOLDER *lppFolder)
  174. {
  175. HRESULT hr;
  176. SPropValuePtr ptrPropValue;
  177. ULONG ulType = 0;
  178. if (!m_ptrIpmSubtree) {
  179. hr = HrGetOneProp(m_ptrMsgStore, PR_IPM_SUBTREE_ENTRYID, &~ptrPropValue);
  180. if (hr != hrSuccess)
  181. return hr;
  182. hr = m_ptrMsgStore->OpenEntry(ptrPropValue->Value.bin.cb, reinterpret_cast<ENTRYID *>(ptrPropValue->Value.bin.lpb), &m_ptrIpmSubtree.iid(), MAPI_BEST_ACCESS|fMapiDeferredErrors, &ulType, &~m_ptrIpmSubtree);
  183. if (hr != hrSuccess)
  184. return hr;
  185. }
  186. return m_ptrIpmSubtree->QueryInterface(IID_IMAPIFolder,
  187. reinterpret_cast<LPVOID *>(lppFolder));
  188. }
  189. /**
  190. * Get the three search folders that are created with StoreHelper::UpdateSearchFolders.
  191. *
  192. * @param[out] lppSearchArchiveFolder
  193. * Pointer to an IMAPIFolder pointer that will be assigned the
  194. * address of the searcholder containing the messages that are eligible
  195. * for archiving. These are basically unarchived messages that are not
  196. * flagged to be never archived.
  197. * @param[out] lppSearchDeleteFolder
  198. * Pointer to an IMAPIFolder pointer that will be assigned the
  199. * address of the searcholder containing the messages that are eligible
  200. * for deletion. These are basically archived messages that are not flagged
  201. * to be never deleted.
  202. * @param[out] lppSearchStubFolder
  203. * Pointer to an IMAPIFolder pointer that will be assigned the
  204. * address of the searcholder containing the messages that are eligible
  205. * for stubbing. These are basically all non-stubbed, archived messages that
  206. * are not flagged to be never stubbed.
  207. */
  208. HRESULT StoreHelper::GetSearchFolders(LPMAPIFOLDER *lppSearchArchiveFolder, LPMAPIFOLDER *lppSearchDeleteFolder, LPMAPIFOLDER *lppSearchStubFolder)
  209. {
  210. HRESULT hr;
  211. SPropValuePtr ptrPropValue;
  212. MAPIFolderPtr ptrSearchArchiveFolder;
  213. MAPIFolderPtr ptrSearchDeleteFolder;
  214. MAPIFolderPtr ptrSearchStubFolder; // subset of SearchDeleteFolder.
  215. ULONG ulType = 0;
  216. bool bUpdateRef = false;
  217. hr = HrGetOneProp(m_ptrMsgStore, PROP_SEARCH_FOLDER_ENTRYIDS, &~ptrPropValue);
  218. if (hr != hrSuccess && hr != MAPI_E_NOT_FOUND)
  219. return hr;
  220. if (hr == hrSuccess && ptrPropValue->Value.MVbin.cValues == 3) {
  221. hr = m_ptrMsgStore->OpenEntry(ptrPropValue->Value.MVbin.lpbin[0].cb, reinterpret_cast<ENTRYID *>(ptrPropValue->Value.MVbin.lpbin[0].lpb), &ptrSearchArchiveFolder.iid(), fMapiDeferredErrors|MAPI_BEST_ACCESS, &ulType, &~ptrSearchArchiveFolder);
  222. if (hr == hrSuccess)
  223. hr = CheckAndUpdateSearchFolder(ptrSearchArchiveFolder, esfArchive);
  224. else if (hr == MAPI_E_NOT_FOUND) {
  225. bUpdateRef = true;
  226. hr = CreateSearchFolder(esfArchive, &~ptrSearchArchiveFolder);
  227. }
  228. if (hr != hrSuccess)
  229. return hr;
  230. hr = m_ptrMsgStore->OpenEntry(ptrPropValue->Value.MVbin.lpbin[1].cb, reinterpret_cast<ENTRYID *>(ptrPropValue->Value.MVbin.lpbin[1].lpb), &ptrSearchDeleteFolder.iid(), fMapiDeferredErrors|MAPI_BEST_ACCESS, &ulType, &~ptrSearchDeleteFolder);
  231. if (hr == hrSuccess)
  232. hr = CheckAndUpdateSearchFolder(ptrSearchDeleteFolder, esfDelete);
  233. else if (hr == MAPI_E_NOT_FOUND) {
  234. bUpdateRef = true;
  235. hr = CreateSearchFolder(esfDelete, &~ptrSearchDeleteFolder);
  236. }
  237. if (hr != hrSuccess)
  238. return hr;
  239. hr = m_ptrMsgStore->OpenEntry(ptrPropValue->Value.MVbin.lpbin[2].cb, reinterpret_cast<ENTRYID *>(ptrPropValue->Value.MVbin.lpbin[2].lpb), &ptrSearchStubFolder.iid(), fMapiDeferredErrors|MAPI_BEST_ACCESS, &ulType, &~ptrSearchStubFolder);
  240. if (hr == hrSuccess)
  241. hr = CheckAndUpdateSearchFolder(ptrSearchStubFolder, esfStub);
  242. else if (hr == MAPI_E_NOT_FOUND) {
  243. bUpdateRef = true;
  244. hr = CreateSearchFolder(esfStub, &~ptrSearchStubFolder);
  245. }
  246. if (hr != hrSuccess)
  247. return hr;
  248. } else {
  249. bUpdateRef = true;
  250. hr = CreateSearchFolders(&~ptrSearchArchiveFolder, &~ptrSearchDeleteFolder, &~ptrSearchStubFolder);
  251. if (hr != hrSuccess)
  252. return hr;
  253. }
  254. if (bUpdateRef) {
  255. SPropValuePtr ptrSearchArchiveEntryId;
  256. SPropValuePtr ptrSearchDeleteEntryId;
  257. SPropValuePtr ptrSearchStubEntryId;
  258. assert(ptrSearchArchiveFolder);
  259. assert(ptrSearchDeleteFolder);
  260. assert(ptrSearchStubFolder);
  261. hr = HrGetOneProp(ptrSearchArchiveFolder, PR_ENTRYID, &~ptrSearchArchiveEntryId);
  262. if (hr != hrSuccess)
  263. return hr;
  264. hr = HrGetOneProp(ptrSearchDeleteFolder, PR_ENTRYID, &~ptrSearchDeleteEntryId);
  265. if (hr != hrSuccess)
  266. return hr;
  267. hr = HrGetOneProp(ptrSearchStubFolder, PR_ENTRYID, &~ptrSearchStubEntryId);
  268. if (hr != hrSuccess)
  269. return hr;
  270. hr = MAPIAllocateBuffer(sizeof(SPropValue), &~ptrPropValue);
  271. if (hr != hrSuccess)
  272. return hr;
  273. ptrPropValue->ulPropTag = PROP_SEARCH_FOLDER_ENTRYIDS;
  274. ptrPropValue->Value.MVbin.cValues = 3;
  275. hr = MAPIAllocateMore(3 * sizeof(*ptrPropValue->Value.MVbin.lpbin), ptrPropValue, (LPVOID*)&ptrPropValue->Value.MVbin.lpbin);
  276. if (hr != hrSuccess)
  277. return hr;
  278. ptrPropValue->Value.MVbin.lpbin[0].cb = ptrSearchArchiveEntryId->Value.bin.cb;
  279. ptrPropValue->Value.MVbin.lpbin[0].lpb = ptrSearchArchiveEntryId->Value.bin.lpb;
  280. ptrPropValue->Value.MVbin.lpbin[1].cb = ptrSearchDeleteEntryId->Value.bin.cb;
  281. ptrPropValue->Value.MVbin.lpbin[1].lpb = ptrSearchDeleteEntryId->Value.bin.lpb;
  282. ptrPropValue->Value.MVbin.lpbin[2].cb = ptrSearchStubEntryId->Value.bin.cb;
  283. ptrPropValue->Value.MVbin.lpbin[2].lpb = ptrSearchStubEntryId->Value.bin.lpb;
  284. hr = HrSetOneProp(m_ptrMsgStore, ptrPropValue);
  285. if (hr != hrSuccess)
  286. return hr;
  287. }
  288. hr = ptrSearchArchiveFolder->QueryInterface(IID_IMAPIFolder, (LPVOID*)lppSearchArchiveFolder);
  289. if (hr != hrSuccess)
  290. return hr;
  291. hr = ptrSearchDeleteFolder->QueryInterface(IID_IMAPIFolder, (LPVOID*)lppSearchDeleteFolder);
  292. if (hr != hrSuccess)
  293. return hr;
  294. return ptrSearchStubFolder->QueryInterface(IID_IMAPIFolder,
  295. reinterpret_cast<LPVOID *>(lppSearchStubFolder));
  296. }
  297. /**
  298. * Open or create a subfolder by name from another folder.
  299. *
  300. * @param[in] ptrFolder
  301. * MAPIFolderPtr that points to the folder from which a subfolder
  302. * will be opened.
  303. * @param[in] strFolder
  304. * The name of the subfolder to open or create.
  305. * @param[in] bCreate
  306. * Set to true to create the subfolder if it doesn't exist.
  307. * @param[out] lppFolder
  308. * Pointer to an IMAPIFolder pointer that will be assigned the
  309. * address of the returned folder.
  310. */
  311. HRESULT StoreHelper::GetSubFolder(MAPIFolderPtr &ptrFolder, const tstring &strFolder, bool bCreate, LPMAPIFOLDER *lppFolder)
  312. {
  313. HRESULT hr;
  314. MAPITablePtr ptrTable;
  315. SRowSetPtr ptrRowSet;
  316. MAPIFolderPtr ptrSubFolder;
  317. ULONG ulType = 0;
  318. static constexpr const SizedSPropTagArray(1, sptaFolderProps) = {1, {PR_ENTRYID}};
  319. SPropValue sResPropValue = {0};
  320. sResPropValue.ulPropTag = PR_DISPLAY_NAME;
  321. sResPropValue.Value.LPSZ = (LPTSTR)strFolder.c_str();
  322. hr = ptrFolder->GetHierarchyTable(fMapiDeferredErrors, &~ptrTable);
  323. if (hr != hrSuccess)
  324. return hr;
  325. hr = ptrTable->SetColumns(sptaFolderProps, TBL_BATCH);
  326. if (hr != hrSuccess)
  327. return hr;
  328. hr = ECPropertyRestriction(RELOP_EQ, PR_DISPLAY_NAME, &sResPropValue, ECRestriction::Cheap)
  329. .FindRowIn(ptrTable, BOOKMARK_BEGINNING, 0);
  330. if (hr == hrSuccess) {
  331. hr = ptrTable->QueryRows(1, TBL_NOADVANCE, &ptrRowSet);
  332. if (hr != hrSuccess)
  333. return hr;
  334. if (ptrRowSet.size() != 1)
  335. return MAPI_E_NOT_FOUND;
  336. hr = m_ptrMsgStore->OpenEntry(ptrRowSet[0].lpProps[0].Value.bin.cb, reinterpret_cast<ENTRYID *>(ptrRowSet[0].lpProps[0].Value.bin.lpb), &ptrSubFolder.iid(), MAPI_BEST_ACCESS, &ulType, &~ptrSubFolder);
  337. } else if (hr == MAPI_E_NOT_FOUND && bCreate == true) {
  338. hr = ptrFolder->CreateFolder(FOLDER_GENERIC, (LPTSTR)strFolder.c_str(), nullptr, &ptrSubFolder.iid(), fMapiUnicode, &~ptrSubFolder);
  339. if (hr != hrSuccess)
  340. return hr;
  341. // Maybe set some class thingy!
  342. }
  343. if (hr != hrSuccess)
  344. return hr;
  345. return ptrSubFolder->QueryInterface(IID_IMAPIFolder,
  346. reinterpret_cast<LPVOID *>(lppFolder));
  347. }
  348. HRESULT StoreHelper::CheckAndUpdateSearchFolder(LPMAPIFOLDER lpSearchFolder, eSearchFolder esfWhich)
  349. {
  350. HRESULT hr;
  351. SPropValuePtr ptrVersion;
  352. if (lpSearchFolder == NULL)
  353. return MAPI_E_INVALID_PARAMETER;
  354. hr = HrGetOneProp(lpSearchFolder, PROP_VERSION, &~ptrVersion);
  355. if (hr != hrSuccess && hr != MAPI_E_NOT_FOUND)
  356. return hr;
  357. if (hr == hrSuccess && ptrVersion->Value.ul == ARCHIVE_SEARCH_VERSION)
  358. return hr;
  359. return (this->*s_infoSearchFolders[esfWhich].fnSetup)(lpSearchFolder, NULL, NULL);
  360. }
  361. HRESULT StoreHelper::CreateSearchFolder(eSearchFolder esfWhich, LPMAPIFOLDER *lppSearchFolder)
  362. {
  363. HRESULT hr;
  364. ULONG ulType = 0;
  365. MAPIFolderPtr ptrRootContainer;
  366. MAPIFolderPtr ptrArchiveSearchRoot;
  367. if (lppSearchFolder == NULL)
  368. return MAPI_E_INVALID_PARAMETER;
  369. hr = m_ptrMsgStore->OpenEntry(0, nullptr, &ptrRootContainer.iid(), MAPI_BEST_ACCESS | fMapiDeferredErrors, &ulType, &~ptrRootContainer);
  370. if (hr != hrSuccess)
  371. return hr;
  372. hr = ptrRootContainer->CreateFolder(FOLDER_GENERIC, (LPTSTR)"Zarafa-Archive-Search-Root", nullptr, &ptrArchiveSearchRoot.iid(), fMapiDeferredErrors | OPEN_IF_EXISTS, &~ptrArchiveSearchRoot);
  373. if (hr != hrSuccess)
  374. return hr;
  375. return DoCreateSearchFolder(ptrArchiveSearchRoot, esfWhich, lppSearchFolder);
  376. }
  377. HRESULT StoreHelper::CreateSearchFolders(LPMAPIFOLDER *lppSearchArchiveFolder, LPMAPIFOLDER *lppSearchDeleteFolder, LPMAPIFOLDER *lppSearchStubFolder)
  378. {
  379. HRESULT hr;
  380. ULONG ulType = 0;
  381. MAPIFolderPtr ptrRootContainer;
  382. MAPIFolderPtr ptrArchiveSearchRoot;
  383. bool bWithErrors = false;
  384. if (lppSearchArchiveFolder == NULL || lppSearchDeleteFolder == NULL ||
  385. lppSearchStubFolder == NULL)
  386. return MAPI_E_INVALID_PARAMETER;
  387. hr = m_ptrMsgStore->OpenEntry(0, nullptr, &ptrRootContainer.iid(), MAPI_BEST_ACCESS | fMapiDeferredErrors, &ulType, &~ptrRootContainer);
  388. if (hr != hrSuccess)
  389. return hr;
  390. hr = ptrRootContainer->CreateFolder(FOLDER_GENERIC, (LPTSTR)"Zarafa-Archive-Search-Root", nullptr, &ptrArchiveSearchRoot.iid(), fMapiDeferredErrors | OPEN_IF_EXISTS, &~ptrArchiveSearchRoot);
  391. if (hr != hrSuccess)
  392. return hr;
  393. // Create the common restrictions here and pass them through DoCreateSearchFolder
  394. hr = DoCreateSearchFolder(ptrArchiveSearchRoot, esfArchive, lppSearchArchiveFolder);
  395. if (hr != hrSuccess)
  396. bWithErrors = true;
  397. hr = DoCreateSearchFolder(ptrArchiveSearchRoot, esfDelete, lppSearchDeleteFolder);
  398. if (hr != hrSuccess)
  399. bWithErrors = true;
  400. hr = DoCreateSearchFolder(ptrArchiveSearchRoot, esfStub, lppSearchStubFolder);
  401. if (hr != hrSuccess)
  402. bWithErrors = true;
  403. return bWithErrors ? MAPI_W_ERRORS_RETURNED : hrSuccess;
  404. }
  405. HRESULT StoreHelper::DoCreateSearchFolder(LPMAPIFOLDER lpParent, eSearchFolder esfWhich, LPMAPIFOLDER *lppSearchFolder)
  406. {
  407. HRESULT hr;
  408. MAPIFolderPtr ptrSearchFolder;
  409. if (lpParent == NULL || lppSearchFolder == NULL)
  410. return MAPI_E_INVALID_PARAMETER;
  411. hr = lpParent->CreateFolder(FOLDER_SEARCH, (LPTSTR)s_infoSearchFolders[esfWhich].lpszName, (LPTSTR)s_infoSearchFolders[esfWhich].lpszDescription, &ptrSearchFolder.iid(), OPEN_IF_EXISTS | fMapiUnicode, &~ptrSearchFolder);
  412. if (hr != hrSuccess)
  413. return hr;
  414. hr = (this->*s_infoSearchFolders[esfWhich].fnSetup)(ptrSearchFolder, NULL, NULL);
  415. if (hr != hrSuccess)
  416. return hr;
  417. return ptrSearchFolder->QueryInterface(IID_IMAPIFolder,
  418. reinterpret_cast<LPVOID *>(lppSearchFolder));
  419. }
  420. HRESULT StoreHelper::SetupSearchArchiveFolder(LPMAPIFOLDER lpSearchFolder, const ECRestriction *lpresClassCheck, const ECRestriction *lpresArchiveCheck)
  421. {
  422. HRESULT hr;
  423. ECOrRestriction resClassCheck;
  424. ECAndRestriction resArchiveCheck;
  425. MAPIFolderPtr ptrIpmSubtree;
  426. SPropValuePtr ptrPropEntryId;
  427. SPropValue sPropStubbed;
  428. EntryListPtr ptrEntryList;
  429. ECAndRestriction resArchiveFolder;
  430. SRestrictionPtr ptrRestriction;
  431. SPropValue sPropVersion;
  432. if (lpSearchFolder == NULL)
  433. return MAPI_E_INVALID_PARAMETER;
  434. if (!lpresClassCheck) {
  435. hr = GetClassCheckRestriction(&resClassCheck);
  436. if (hr != hrSuccess)
  437. return hr;
  438. lpresClassCheck = &resClassCheck;
  439. }
  440. if (!lpresArchiveCheck) {
  441. hr = GetArchiveCheckRestriction(&resArchiveCheck);
  442. if (hr != hrSuccess)
  443. return hr;
  444. lpresArchiveCheck = &resArchiveCheck;
  445. }
  446. hr = GetIpmSubtree(&~ptrIpmSubtree);
  447. if (hr != hrSuccess)
  448. return hr;
  449. hr = HrGetOneProp(ptrIpmSubtree, PR_ENTRYID, &~ptrPropEntryId);
  450. if (hr != hrSuccess)
  451. return hr;
  452. hr = MAPIAllocateBuffer(sizeof(ENTRYLIST), &~ptrEntryList);
  453. if (hr != hrSuccess)
  454. return hr;
  455. ptrEntryList->cValues = 1;
  456. hr = MAPIAllocateMore(sizeof(SBinary), ptrEntryList, (LPVOID*)&ptrEntryList->lpbin);
  457. if (hr != hrSuccess)
  458. return hr;
  459. ptrEntryList->lpbin[0].cb = ptrPropEntryId->Value.bin.cb;
  460. ptrEntryList->lpbin[0].lpb = ptrPropEntryId->Value.bin.lpb;
  461. sPropStubbed.ulPropTag = PROP_STUBBED; sPropStubbed.Value.b = 1;
  462. // Create/Update the search folder that tracks unarchived message that are not flagged to be never archived
  463. resArchiveFolder +=
  464. *lpresClassCheck +
  465. ECNotRestriction(
  466. ECAndRestriction(
  467. ECExistRestriction(PROP_STUBBED) +
  468. ECPropertyRestriction(RELOP_EQ, PROP_STUBBED, &sPropStubbed, ECRestriction::Cheap) +
  469. // The prop stub is only valid if the stub was not copied.
  470. ECExistRestriction(PROP_ORIGINAL_SOURCEKEY) +
  471. ECComparePropsRestriction(RELOP_EQ, PROP_ORIGINAL_SOURCEKEY, PR_SOURCE_KEY)
  472. )
  473. ) +
  474. ECNotRestriction(
  475. ECAndRestriction(
  476. ECExistRestriction(PROP_FLAGS) +
  477. ECBitMaskRestriction(BMR_NEZ, PROP_FLAGS, ARCH_NEVER_ARCHIVE)
  478. )
  479. ) +
  480. ECNotRestriction(*lpresArchiveCheck);
  481. hr = resArchiveFolder.CreateMAPIRestriction(&~ptrRestriction, ECRestriction::Cheap);
  482. if (hr != hrSuccess)
  483. return hr;
  484. // Set the search criteria
  485. hr = lpSearchFolder->SetSearchCriteria(ptrRestriction, ptrEntryList, BACKGROUND_SEARCH|RECURSIVE_SEARCH);
  486. if (hr != hrSuccess)
  487. return hr;
  488. sPropVersion.ulPropTag = PROP_VERSION;
  489. sPropVersion.Value.ul = ARCHIVE_SEARCH_VERSION;
  490. return HrSetOneProp(lpSearchFolder, &sPropVersion);
  491. }
  492. HRESULT StoreHelper::SetupSearchDeleteFolder(LPMAPIFOLDER lpSearchFolder, const ECRestriction *lpresClassCheck, const ECRestriction *lpresArchiveCheck)
  493. {
  494. HRESULT hr;
  495. ECOrRestriction resClassCheck;
  496. ECAndRestriction resArchiveCheck;
  497. MAPIFolderPtr ptrIpmSubtree;
  498. SPropValuePtr ptrPropEntryId;
  499. EntryListPtr ptrEntryList;
  500. ECAndRestriction resDeleteFolder;
  501. SRestrictionPtr ptrRestriction;
  502. SPropValue sPropVersion;
  503. if (lpSearchFolder == NULL)
  504. return MAPI_E_INVALID_PARAMETER;
  505. if (!lpresClassCheck) {
  506. hr = GetClassCheckRestriction(&resClassCheck);
  507. if (hr != hrSuccess)
  508. return hr;
  509. lpresClassCheck = &resClassCheck;
  510. }
  511. if (!lpresArchiveCheck) {
  512. hr = GetArchiveCheckRestriction(&resArchiveCheck);
  513. if (hr != hrSuccess)
  514. return hr;
  515. lpresArchiveCheck = &resArchiveCheck;
  516. }
  517. hr = GetIpmSubtree(&~ptrIpmSubtree);
  518. if (hr != hrSuccess)
  519. return hr;
  520. hr = HrGetOneProp(ptrIpmSubtree, PR_ENTRYID, &~ptrPropEntryId);
  521. if (hr != hrSuccess)
  522. return hr;
  523. hr = MAPIAllocateBuffer(sizeof(ENTRYLIST), &~ptrEntryList);
  524. if (hr != hrSuccess)
  525. return hr;
  526. ptrEntryList->cValues = 1;
  527. hr = MAPIAllocateMore(sizeof(SBinary), ptrEntryList, (LPVOID*)&ptrEntryList->lpbin);
  528. if (hr != hrSuccess)
  529. return hr;
  530. ptrEntryList->lpbin[0].cb = ptrPropEntryId->Value.bin.cb;
  531. ptrEntryList->lpbin[0].lpb = ptrPropEntryId->Value.bin.lpb;
  532. // Create/Update the search folder that tracks all archived message that are not flagged to be never deleted or never stubbed
  533. resDeleteFolder +=
  534. *lpresClassCheck +
  535. ECNotRestriction(
  536. ECAndRestriction(
  537. ECExistRestriction(PROP_FLAGS) +
  538. ECBitMaskRestriction(BMR_NEZ, PROP_FLAGS, ARCH_NEVER_DELETE)
  539. )
  540. ) +
  541. *lpresArchiveCheck;
  542. hr = resDeleteFolder.CreateMAPIRestriction(&~ptrRestriction, ECRestriction::Cheap);
  543. if (hr != hrSuccess)
  544. return hr;
  545. // Set the search criteria
  546. hr = lpSearchFolder->SetSearchCriteria(ptrRestriction, ptrEntryList, BACKGROUND_SEARCH|RECURSIVE_SEARCH);
  547. if (hr != hrSuccess)
  548. return hr;
  549. sPropVersion.ulPropTag = PROP_VERSION;
  550. sPropVersion.Value.ul = ARCHIVE_SEARCH_VERSION;
  551. return HrSetOneProp(lpSearchFolder, &sPropVersion);
  552. }
  553. HRESULT StoreHelper::SetupSearchStubFolder(LPMAPIFOLDER lpSearchFolder, const ECRestriction* /*lpresClassCheck*/, const ECRestriction *lpresArchiveCheck)
  554. {
  555. HRESULT hr;
  556. ECAndRestriction resArchiveCheck;
  557. MAPIFolderPtr ptrIpmSubtree;
  558. SPropValuePtr ptrPropEntryId;
  559. SPropValue sPropStubbed;
  560. SPropValue sPropMsgClass[2];
  561. EntryListPtr ptrEntryList;
  562. ECAndRestriction resStubFolder;
  563. SRestrictionPtr ptrRestriction;
  564. SPropValue sPropVersion;
  565. if (lpSearchFolder == NULL)
  566. return MAPI_E_INVALID_PARAMETER;
  567. if (!lpresArchiveCheck) {
  568. hr = GetArchiveCheckRestriction(&resArchiveCheck);
  569. if (hr != hrSuccess)
  570. return hr;
  571. lpresArchiveCheck = &resArchiveCheck;
  572. }
  573. hr = GetIpmSubtree(&~ptrIpmSubtree);
  574. if (hr != hrSuccess)
  575. return hr;
  576. hr = HrGetOneProp(ptrIpmSubtree, PR_ENTRYID, &~ptrPropEntryId);
  577. if (hr != hrSuccess)
  578. return hr;
  579. hr = MAPIAllocateBuffer(sizeof(ENTRYLIST), &~ptrEntryList);
  580. if (hr != hrSuccess)
  581. return hr;
  582. ptrEntryList->cValues = 1;
  583. hr = MAPIAllocateMore(sizeof(SBinary), ptrEntryList, (LPVOID*)&ptrEntryList->lpbin);
  584. if (hr != hrSuccess)
  585. return hr;
  586. ptrEntryList->lpbin[0].cb = ptrPropEntryId->Value.bin.cb;
  587. ptrEntryList->lpbin[0].lpb = ptrPropEntryId->Value.bin.lpb;
  588. sPropStubbed.ulPropTag = PROP_STUBBED;
  589. sPropStubbed.Value.b = 1;
  590. sPropMsgClass[0].ulPropTag = PR_MESSAGE_CLASS;
  591. sPropMsgClass[0].Value.LPSZ = const_cast<TCHAR *>(_T("IPM.Note"));
  592. sPropMsgClass[1].ulPropTag = PR_MESSAGE_CLASS;
  593. sPropMsgClass[1].Value.LPSZ = const_cast<TCHAR *>(_T("IPM.Note.")); // RES_CONTENT w FL_PREFIX
  594. // Create/Update the search folder that tracks non-stubbed archived message that are not flagged to be never stubbed.
  595. resStubFolder +=
  596. ECOrRestriction(
  597. ECPropertyRestriction(RELOP_EQ, PR_MESSAGE_CLASS, &sPropMsgClass[0], ECRestriction::Cheap) +
  598. ECContentRestriction(FL_PREFIX, PR_MESSAGE_CLASS, &sPropMsgClass[1], ECRestriction::Cheap)
  599. ) +
  600. ECNotRestriction(
  601. ECAndRestriction(
  602. ECExistRestriction(PROP_STUBBED) +
  603. ECPropertyRestriction(RELOP_EQ, PROP_STUBBED, &sPropStubbed, ECRestriction::Cheap)
  604. )
  605. ) +
  606. ECNotRestriction(
  607. ECAndRestriction(
  608. ECExistRestriction(PROP_FLAGS) +
  609. ECBitMaskRestriction(BMR_NEZ, PROP_FLAGS, ARCH_NEVER_STUB)
  610. )
  611. ) +
  612. *lpresArchiveCheck;
  613. hr = resStubFolder.CreateMAPIRestriction(&~ptrRestriction, ECRestriction::Cheap);
  614. if (hr != hrSuccess)
  615. return hr;
  616. // Set the search criteria
  617. hr = lpSearchFolder->SetSearchCriteria(ptrRestriction, ptrEntryList, BACKGROUND_SEARCH|RECURSIVE_SEARCH);
  618. if (hr != hrSuccess)
  619. return hr;
  620. sPropVersion.ulPropTag = PROP_VERSION;
  621. sPropVersion.Value.ul = ARCHIVE_SEARCH_VERSION;
  622. return HrSetOneProp(lpSearchFolder, &sPropVersion);
  623. }
  624. HRESULT StoreHelper::GetClassCheckRestriction(ECOrRestriction *lpresClassCheck)
  625. {
  626. SPropValue sPropMsgClass[9];
  627. ECOrRestriction resClassCheck;
  628. sPropMsgClass[0].ulPropTag = PR_MESSAGE_CLASS;
  629. sPropMsgClass[0].Value.LPSZ = const_cast<TCHAR *>(_T("IPM.Note"));
  630. sPropMsgClass[1].ulPropTag = PR_MESSAGE_CLASS;
  631. sPropMsgClass[1].Value.LPSZ = const_cast<TCHAR *>(_T("IPM.Note.")); // RES_CONTENT w FL_PREFIX
  632. sPropMsgClass[2].ulPropTag = PR_MESSAGE_CLASS;
  633. sPropMsgClass[2].Value.LPSZ = const_cast<TCHAR *>(_T("IPM.Schedule.Meeting.Request"));
  634. sPropMsgClass[3].ulPropTag = PR_MESSAGE_CLASS;
  635. sPropMsgClass[3].Value.LPSZ = const_cast<TCHAR *>(_T("IPM.Schedule.Meeting.Resp.Pos"));
  636. sPropMsgClass[4].ulPropTag = PR_MESSAGE_CLASS;
  637. sPropMsgClass[4].Value.LPSZ = const_cast<TCHAR *>(_T("IPM.Schedule.Meeting.Resp.Neg"));
  638. sPropMsgClass[5].ulPropTag = PR_MESSAGE_CLASS;
  639. sPropMsgClass[5].Value.LPSZ = const_cast<TCHAR *>(_T("IPM.Schedule.Meeting.Resp.Tent"));
  640. sPropMsgClass[6].ulPropTag = PR_MESSAGE_CLASS;
  641. sPropMsgClass[6].Value.LPSZ = const_cast<TCHAR *>(_T("IPM.Schedule.Meeting.Canceled"));
  642. sPropMsgClass[7].ulPropTag = PR_MESSAGE_CLASS;
  643. sPropMsgClass[7].Value.LPSZ = const_cast<TCHAR *>(_T("Report.IPM.Note.NDR"));
  644. sPropMsgClass[8].ulPropTag = PR_MESSAGE_CLASS;
  645. sPropMsgClass[8].Value.LPSZ = const_cast<TCHAR *>(_T("Report.IPM.Note.IPNRN"));
  646. // Build the message class restriction.
  647. resClassCheck +=
  648. ECPropertyRestriction(RELOP_EQ, PR_MESSAGE_CLASS, &sPropMsgClass[0], ECRestriction::Shallow) +
  649. ECContentRestriction(FL_PREFIX, PR_MESSAGE_CLASS, &sPropMsgClass[1], ECRestriction::Shallow) +
  650. ECPropertyRestriction(RELOP_EQ, PR_MESSAGE_CLASS, &sPropMsgClass[2], ECRestriction::Shallow) +
  651. ECPropertyRestriction(RELOP_EQ, PR_MESSAGE_CLASS, &sPropMsgClass[3], ECRestriction::Shallow) +
  652. ECPropertyRestriction(RELOP_EQ, PR_MESSAGE_CLASS, &sPropMsgClass[4], ECRestriction::Shallow) +
  653. ECPropertyRestriction(RELOP_EQ, PR_MESSAGE_CLASS, &sPropMsgClass[5], ECRestriction::Shallow) +
  654. ECPropertyRestriction(RELOP_EQ, PR_MESSAGE_CLASS, &sPropMsgClass[6], ECRestriction::Shallow) +
  655. ECPropertyRestriction(RELOP_EQ, PR_MESSAGE_CLASS, &sPropMsgClass[7], ECRestriction::Shallow) +
  656. ECPropertyRestriction(RELOP_EQ, PR_MESSAGE_CLASS, &sPropMsgClass[8], ECRestriction::Shallow);
  657. *lpresClassCheck = std::move(resClassCheck);
  658. return hrSuccess;
  659. }
  660. HRESULT StoreHelper::GetArchiveCheckRestriction(ECAndRestriction *lpresArchiveCheck)
  661. {
  662. HRESULT hr;
  663. SPropValue sPropDirty = {0};
  664. ObjectEntryList lstArchives;
  665. ECAndRestriction resArchiveCheck;
  666. sPropDirty.ulPropTag = PROP_DIRTY;
  667. sPropDirty.Value.b = 1;
  668. resArchiveCheck +=
  669. ECAndRestriction(
  670. ECExistRestriction(PROP_ORIGINAL_SOURCEKEY) +
  671. ECComparePropsRestriction(RELOP_EQ, PROP_ORIGINAL_SOURCEKEY, PR_SOURCE_KEY)
  672. ) +
  673. ECNotRestriction(
  674. ECAndRestriction(
  675. ECExistRestriction(PROP_DIRTY) +
  676. ECPropertyRestriction(RELOP_EQ, PROP_DIRTY, &sPropDirty, ECRestriction::Shallow)
  677. )
  678. );
  679. hr = GetArchiveList(&lstArchives);
  680. if (hr != hrSuccess)
  681. return hr;
  682. // Build the restriction that checks that the message has been archived to all archives.
  683. resArchiveCheck += ECExistRestriction(PROP_ARCHIVE_STORE_ENTRYIDS);
  684. for (const auto &arc : lstArchives) {
  685. SPropValue sPropFolderEntryId;
  686. sPropFolderEntryId.ulPropTag = (PROP_ARCHIVE_STORE_ENTRYIDS & ~MV_FLAG);
  687. sPropFolderEntryId.Value.bin.cb = arc.sStoreEntryId.size();
  688. sPropFolderEntryId.Value.bin.lpb = arc.sStoreEntryId;
  689. resArchiveCheck += ECPropertyRestriction(RELOP_EQ, PROP_ARCHIVE_STORE_ENTRYIDS, &sPropFolderEntryId, ECRestriction::Full);
  690. }
  691. *lpresArchiveCheck = std::move(resArchiveCheck);
  692. return hrSuccess;
  693. }
  694. }} /* namespace */