ArchiveHelper.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926
  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 <new>
  19. #include <utility>
  20. #include "ArchiveHelper.h"
  21. #include "ArchiverSession.h"
  22. #include <kopano/ECLogger.h>
  23. #include "StoreHelper.h"
  24. #include "ECACL.h"
  25. #include <kopano/ECABEntryID.h>
  26. #include <kopano/ECGetText.h> // defines the wonderful macro "_"
  27. #include <kopano/Util.h>
  28. namespace KC { namespace helpers {
  29. /**
  30. * Create an ArchiveHelper object based on a message store and a folder name.
  31. *
  32. * @param[in] ptrArchiveStore
  33. * A MsgStorePtr that points to the message store that's used as an archive.
  34. * @param[in] strFolder
  35. * The folder name that's used as the root of the archive. If left empty, the IPM subtree
  36. * of the message store is used as the archive folder.
  37. * @param[in] lpszServerPath
  38. * When set and not empty, it specifies the serverpath to the server containing the archive. So it
  39. * implies that the archive is remote (other server/cluster).
  40. * @param[out] lpptrArchiveHelper
  41. * Pointer to a ArchiveHelperPtr that assigned the address of the returned ArchiveHelper.
  42. */
  43. HRESULT ArchiveHelper::Create(LPMDB lpArchiveStore, const tstring &strFolder, const char *lpszServerPath, ArchiveHelperPtr *lpptrArchiveHelper)
  44. {
  45. HRESULT hr;
  46. ArchiveHelperPtr ptrArchiveHelper(
  47. new(std::nothrow) ArchiveHelper(lpArchiveStore, strFolder,
  48. lpszServerPath ? lpszServerPath : std::string()));
  49. if (ptrArchiveHelper == nullptr)
  50. return MAPI_E_NOT_ENOUGH_MEMORY;
  51. hr = ptrArchiveHelper->Init();
  52. if (hr != hrSuccess)
  53. return hr;
  54. *lpptrArchiveHelper = std::move(ptrArchiveHelper);
  55. return hrSuccess;
  56. }
  57. /**
  58. * Create an ArchiveHelper object based on a message store and a folder.
  59. *
  60. * @param[in] ptrArchiveStore
  61. * A MsgStorePtr that points to the message store that's used as an archive.
  62. * @param[in] ptrArchiveFolder
  63. * A MAPIFolderPtr that points to the folder that will be used as the root of the archive.
  64. * @param[in] lpszServerPath
  65. * When set and not empty, it specifies the serverpath to the server containing the archive. So it
  66. * implies that the archive is remote (other server/cluster).
  67. * @param[out] lpptrArchiveHelper
  68. * Pointer to a ArchiveHelperPtr that assigned the address of the returned ArchiveHelper.
  69. */
  70. HRESULT ArchiveHelper::Create(LPMDB lpArchiveStore, LPMAPIFOLDER lpArchiveFolder, const char *lpszServerPath, ArchiveHelperPtr *lpptrArchiveHelper)
  71. {
  72. HRESULT hr;
  73. ArchiveHelperPtr ptrArchiveHelper(
  74. new(std::nothrow) ArchiveHelper(lpArchiveStore, lpArchiveFolder,
  75. lpszServerPath ? lpszServerPath : std::string()));
  76. if (ptrArchiveHelper == nullptr)
  77. return MAPI_E_NOT_ENOUGH_MEMORY;
  78. hr = ptrArchiveHelper->Init();
  79. if (hr != hrSuccess)
  80. return hr;
  81. *lpptrArchiveHelper = std::move(ptrArchiveHelper);
  82. return hrSuccess;
  83. }
  84. HRESULT ArchiveHelper::Create(ArchiverSessionPtr ptrSession, const SObjectEntry &archiveEntry, ECLogger *lpLogger, ArchiveHelperPtr *lpptrArchiveHelper)
  85. {
  86. HRESULT hr;
  87. MsgStorePtr ptrArchiveStore;
  88. ULONG ulType;
  89. MAPIFolderPtr ptrArchiveRootFolder;
  90. ArchiveHelperPtr ptrArchiveHelper;
  91. if (lpptrArchiveHelper == NULL)
  92. return MAPI_E_INVALID_PARAMETER;
  93. hr = ptrSession->OpenStore(archiveEntry.sStoreEntryId, &~ptrArchiveStore);
  94. if (hr != hrSuccess) {
  95. if (lpLogger)
  96. lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to open archive store. (hr=%s)", stringify(hr, true).c_str());
  97. return hr;
  98. }
  99. hr = ptrArchiveStore->OpenEntry(archiveEntry.sItemEntryId.size(), archiveEntry.sItemEntryId, &ptrArchiveRootFolder.iid(), MAPI_BEST_ACCESS | fMapiDeferredErrors, &ulType, &~ptrArchiveRootFolder);
  100. if (hr != hrSuccess) {
  101. if (lpLogger)
  102. lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to open archive root folder. (hr=%s)", stringify(hr, true).c_str());
  103. return hr;
  104. }
  105. // We pass a NULL to the lpszServerPath argument, indicating that it's local. However, it was already
  106. // remotely opened by ptrSession->OpenStore(). Effectively this causes ptrArchiveHelper->GetArchiveEntry()
  107. // to malfunction (it won't wrap the entryid with the serverpath). This is not an issue as we don't use
  108. // that here anyway.
  109. hr = ArchiveHelper::Create(ptrArchiveStore, ptrArchiveRootFolder, NULL, &ptrArchiveHelper);
  110. if (hr != hrSuccess) {
  111. if (lpLogger)
  112. lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to create archive helper. (hr=%s)", stringify(hr, true).c_str());
  113. return hr;
  114. }
  115. *lpptrArchiveHelper = std::move(ptrArchiveHelper);
  116. return hrSuccess;
  117. }
  118. ArchiveHelper::ArchiveHelper(LPMDB lpArchiveStore, const tstring &strFolder, const std::string &strServerPath)
  119. : m_ptrArchiveStore(lpArchiveStore, true)
  120. , m_strFolder(strFolder)
  121. , m_strServerPath(strServerPath), __propmap(4)
  122. { }
  123. ArchiveHelper::ArchiveHelper(LPMDB lpArchiveStore, LPMAPIFOLDER lpArchiveFolder, const std::string &strServerPath)
  124. : m_ptrArchiveStore(lpArchiveStore, true)
  125. , m_ptrArchiveFolder(lpArchiveFolder, true)
  126. , m_strServerPath(strServerPath)
  127. { }
  128. /**
  129. * Initialize an ArchiveHelper object.
  130. */
  131. HRESULT ArchiveHelper::Init()
  132. {
  133. HRESULT hr = hrSuccess;
  134. PROPMAP_INIT_NAMED_ID(ATTACHED_USER_ENTRYID, PT_BINARY, PSETID_Archive, dispidAttachedUser)
  135. PROPMAP_INIT_NAMED_ID(ARCHIVE_TYPE, PT_LONG, PSETID_Archive, dispidType)
  136. PROPMAP_INIT_NAMED_ID(ATTACH_TYPE, PT_LONG, PSETID_Archive, dispidAttachType)
  137. PROPMAP_INIT_NAMED_ID(SPECIAL_FOLDER_ENTRYIDS, PT_MV_BINARY, PSETID_Archive, dispidSpecialFolderEntryIds);
  138. PROPMAP_INIT(m_ptrArchiveStore)
  139. exitpm:
  140. return hr;
  141. }
  142. /**
  143. * Get the user that's attached to this archive.
  144. *
  145. * @param[out] lpsUserEntryId
  146. * Pointer to a entryid_t that will be populated with the entryid of the user
  147. * who's store is attached to this archive.
  148. */
  149. HRESULT ArchiveHelper::GetAttachedUser(abentryid_t *lpsUserEntryId)
  150. {
  151. HRESULT hr;
  152. MAPIFolderPtr ptrFolder;
  153. SPropValuePtr ptrPropValue;
  154. hr = GetArchiveFolder(false, &~ptrFolder);
  155. if (hr != hrSuccess)
  156. return hr;
  157. hr = HrGetOneProp(ptrFolder, PROP_ATTACHED_USER_ENTRYID, &~ptrPropValue);
  158. if (hr != hrSuccess)
  159. return hr;
  160. lpsUserEntryId->assign(ptrPropValue->Value.bin);
  161. return hrSuccess;
  162. }
  163. /**
  164. * Set the user that's attached to this archive.
  165. *
  166. * @param[in] sUserEntryId
  167. * The entryid of the user who's store is attached to this
  168. * archive.
  169. */
  170. HRESULT ArchiveHelper::SetAttachedUser(const abentryid_t &sUserEntryId)
  171. {
  172. HRESULT hr;
  173. MAPIFolderPtr ptrFolder;
  174. SPropValue sPropValue = {0};
  175. hr = GetArchiveFolder(true, &~ptrFolder);
  176. if (hr != hrSuccess)
  177. return hr;
  178. sPropValue.ulPropTag = PROP_ATTACHED_USER_ENTRYID;
  179. sPropValue.Value.bin.cb = sUserEntryId.size();
  180. sPropValue.Value.bin.lpb = sUserEntryId;
  181. return HrSetOneProp(ptrFolder, &sPropValue);
  182. }
  183. /**
  184. * Get an SObjectEntry that uniquely identifies this archive.
  185. *
  186. * @param[in] bCreate
  187. * Create the folder if it doesn't exist.
  188. * @param[out] lpSObjectEntry
  189. * Pointer to a SObjectEntry structure that will be populated with the unique
  190. * reference to this archive.
  191. */
  192. HRESULT ArchiveHelper::GetArchiveEntry(bool bCreate, SObjectEntry *lpsObjectEntry)
  193. {
  194. HRESULT hr;
  195. SPropValuePtr ptrStoreEntryId;
  196. MAPIFolderPtr ptrFolder;
  197. SPropValuePtr ptrFolderEntryId;
  198. hr = HrGetOneProp(m_ptrArchiveStore, PR_ENTRYID, &~ptrStoreEntryId);
  199. if (hr != hrSuccess)
  200. return hr;
  201. hr = GetArchiveFolder(bCreate, &~ptrFolder);
  202. if (hr != hrSuccess)
  203. return hr;
  204. hr = HrGetOneProp(ptrFolder, PR_ENTRYID, &~ptrFolderEntryId);
  205. if (hr != hrSuccess)
  206. return hr;
  207. lpsObjectEntry->sStoreEntryId.assign(ptrStoreEntryId->Value.bin);
  208. if (!m_strServerPath.empty())
  209. lpsObjectEntry->sStoreEntryId.wrap(m_strServerPath);
  210. lpsObjectEntry->sItemEntryId.assign(ptrFolderEntryId->Value.bin);
  211. return hrSuccess;
  212. }
  213. /**
  214. * Get the archive type of this archive.
  215. *
  216. * @param[out] lparchType
  217. * Pointer to a ArchiveType enum that will be set to the type
  218. * of this archive.
  219. * @param[out] lpattachType
  220. * Pointer to a AttachType enum that will be set to the way this
  221. * archive was attached (explicit / implicit).
  222. */
  223. HRESULT ArchiveHelper::GetArchiveType(ArchiveType *lparchType, AttachType *lpattachType)
  224. {
  225. HRESULT hr;
  226. SPropValuePtr ptrPropVal;
  227. MAPIFolderPtr ptrArchiveFolder;
  228. ArchiveType archType;
  229. AttachType attachType = UnknownAttach;
  230. hr = HrGetOneProp(m_ptrArchiveStore, PROP_ARCHIVE_TYPE, &~ptrPropVal);
  231. if (hr == MAPI_E_NOT_FOUND) {
  232. archType = UndefArchive;
  233. hr = hrSuccess;
  234. } else if (hr == hrSuccess) {
  235. switch (ptrPropVal->Value.l) {
  236. case SingleArchive:
  237. case MultiArchive:
  238. archType = (ArchiveType)ptrPropVal->Value.l;
  239. break;
  240. default:
  241. hr = MAPI_E_CORRUPT_DATA;
  242. break;
  243. }
  244. }
  245. if (hr != hrSuccess)
  246. return hr;
  247. if (lpattachType) {
  248. hr = GetArchiveFolder(true, &~ptrArchiveFolder);
  249. if (hr != hrSuccess)
  250. return hr;
  251. hr = HrGetOneProp(ptrArchiveFolder, PROP_ATTACH_TYPE, &~ptrPropVal);
  252. if (hr == MAPI_E_NOT_FOUND) {
  253. attachType = ExplicitAttach;
  254. hr = hrSuccess;
  255. } else if (hr == hrSuccess) {
  256. switch (ptrPropVal->Value.l) {
  257. case ExplicitAttach:
  258. case ImplicitAttach:
  259. attachType = (AttachType)ptrPropVal->Value.l;
  260. break;
  261. default:
  262. hr = MAPI_E_CORRUPT_DATA;
  263. break;
  264. }
  265. }
  266. if (hr != hrSuccess)
  267. return hr;
  268. }
  269. if (lparchType)
  270. *lparchType = archType;
  271. if (lpattachType)
  272. *lpattachType = attachType;
  273. return hrSuccess;
  274. }
  275. /**
  276. * Set the archive type for this archive.
  277. *
  278. * @param[in] aType
  279. * The ArchiveType type of this archive.
  280. */
  281. HRESULT ArchiveHelper::SetArchiveType(ArchiveType archType, AttachType attachType)
  282. {
  283. HRESULT hr;
  284. MAPIFolderPtr ptrArchiveFolder;
  285. SPropValue sPropVal = {0};
  286. sPropVal.ulPropTag = PROP_ARCHIVE_TYPE;
  287. sPropVal.Value.l = archType;
  288. hr = HrSetOneProp(m_ptrArchiveStore, &sPropVal);
  289. if (hr != hrSuccess)
  290. return hr;
  291. hr = GetArchiveFolder(true, &~ptrArchiveFolder);
  292. if (hr != hrSuccess)
  293. return hr;
  294. sPropVal.ulPropTag = PROP_ATTACH_TYPE;
  295. sPropVal.Value.l = attachType;
  296. return HrSetOneProp(ptrArchiveFolder, &sPropVal);
  297. }
  298. /**
  299. * Set permissions on the archive for the user specified by sUserEntryId. This makes sure that the archive is
  300. * at least readable for the user. If the archive is in a subfolder, only the subfolder that is the archive will
  301. * be visible. The other folders will be hidden.
  302. *
  303. * @param[in] sUserEntryId
  304. * The entryid of the user for who permissions are being set.
  305. * @param[in] bWriteable.
  306. * If set to true, the archive will be writable by the user.
  307. */
  308. HRESULT ArchiveHelper::SetPermissions(const abentryid_t &sUserEntryId, bool bWritable)
  309. {
  310. HRESULT hr;
  311. MAPIFolderPtr ptrFolder;
  312. ExchangeModifyTablePtr ptrEMT;
  313. MAPITablePtr ptrTable;
  314. SPropValue sUserProps[2];
  315. SPropValue sOtherProps[2];
  316. RowListPtr ptrRowList;
  317. StoreHelperPtr ptrStoreHelper;
  318. hr = MAPIAllocateBuffer(CbNewROWLIST(2), &~ptrRowList);
  319. if (hr != hrSuccess)
  320. return hr;
  321. // First set permissions on the IPM Subtree since we'll simply overwrite
  322. // them if the archive folder IS the IPM Subtree.
  323. // Grant folder visible permissions on the IPM Subtree for this user.
  324. hr = StoreHelper::Create(m_ptrArchiveStore, &ptrStoreHelper);
  325. if (hr != hrSuccess)
  326. return hr;
  327. hr = ptrStoreHelper->GetIpmSubtree(&~ptrFolder);
  328. if (hr != hrSuccess)
  329. return hr;
  330. hr = ptrFolder->OpenProperty(PR_ACL_TABLE, &ptrEMT.iid(), 0, fMapiDeferredErrors, &~ptrEMT);
  331. if (hr != hrSuccess)
  332. return hr;
  333. sUserProps[0].ulPropTag = PR_MEMBER_ENTRYID;
  334. sUserProps[0].Value.bin.cb = sUserEntryId.size();
  335. sUserProps[0].Value.bin.lpb = sUserEntryId;
  336. sUserProps[1].ulPropTag = PR_MEMBER_RIGHTS;
  337. sUserProps[1].Value.l = RIGHTS_FOLDER_VISIBLE;
  338. ptrRowList->cEntries = 1;
  339. ptrRowList->aEntries[0].ulRowFlags = ROW_MODIFY;
  340. ptrRowList->aEntries[0].cValues = 2;
  341. ptrRowList->aEntries[0].rgPropVals = sUserProps;
  342. hr = ptrEMT->ModifyTable(0, ptrRowList);
  343. if (hr != hrSuccess && hr != MAPI_E_INVALID_PARAMETER) // We can't set rights for non-active users.
  344. return hr;
  345. // Grant read only permissions on the archive folder for this user (unless bWritable is requested).
  346. // Grant no access for all other users (everyone)
  347. hr = GetArchiveFolder(true, &~ptrFolder);
  348. if (hr != hrSuccess)
  349. return hr;
  350. hr = ptrFolder->OpenProperty(PR_ACL_TABLE, &ptrEMT.iid(), 0, fMapiDeferredErrors, &~ptrEMT);
  351. if (hr != hrSuccess)
  352. return hr;
  353. sUserProps[0].ulPropTag = PR_MEMBER_ENTRYID;
  354. sUserProps[0].Value.bin.cb = sUserEntryId.size();
  355. sUserProps[0].Value.bin.lpb = sUserEntryId;
  356. sUserProps[1].ulPropTag = PR_MEMBER_RIGHTS;
  357. sUserProps[1].Value.l = (bWritable ? ROLE_OWNER : ROLE_REVIEWER);
  358. sOtherProps[0].ulPropTag = PR_MEMBER_ENTRYID;
  359. sOtherProps[0].Value.bin.cb = g_cbEveryoneEid;
  360. sOtherProps[0].Value.bin.lpb = g_lpEveryoneEid;
  361. sOtherProps[1].ulPropTag = PR_MEMBER_RIGHTS;
  362. sOtherProps[1].Value.l = RIGHTS_NONE;
  363. ptrRowList->cEntries = 2;
  364. ptrRowList->aEntries[0].ulRowFlags = ROW_MODIFY;
  365. ptrRowList->aEntries[0].cValues = 2;
  366. ptrRowList->aEntries[0].rgPropVals = sUserProps;
  367. ptrRowList->aEntries[1].ulRowFlags = ROW_MODIFY;
  368. ptrRowList->aEntries[1].cValues = 2;
  369. ptrRowList->aEntries[1].rgPropVals = sOtherProps;
  370. hr = ptrEMT->ModifyTable(0, ptrRowList);
  371. if (hr == MAPI_W_PARTIAL_COMPLETION) // We can't set rights for non-active users.
  372. hr = hrSuccess;
  373. return hr;
  374. }
  375. /**
  376. * Open or create the folder in the archive that's linked to the non-archive folder referenced by ptrSourceFolder.
  377. *
  378. * @param[in] ptrSourceFolder
  379. * The MAPIFolderPtr that points to the folder for which the attached archive folder
  380. * is being requested.
  381. * @param[in] ptrSession
  382. * The Session pointer that's used to open folders with.
  383. * @param[out] lppDestinationFolder
  384. * Pointer to a MAPIFolder pointer that's assigned the address of the returned folder.
  385. */
  386. HRESULT ArchiveHelper::GetArchiveFolderFor(MAPIFolderPtr &ptrSourceFolder, ArchiverSessionPtr ptrSession, LPMAPIFOLDER *lppDestinationFolder)
  387. {
  388. HRESULT hr;
  389. SPropValuePtr ptrStoreEntryId;
  390. SPropValuePtr ptrFolderType;
  391. SPropValuePtr ptrFolderEntryId;
  392. MAPIPropHelperPtr ptrSourceFolderHelper;
  393. MAPIPropHelperPtr ptrArchiveFolderHelper;
  394. ObjectEntryList lstFolderArchives;
  395. ObjectEntryList::const_iterator iArchiveFolder;
  396. MAPIFolderPtr ptrArchiveFolder;
  397. MAPIFolderPtr ptrParentFolder;
  398. MAPIFolderPtr ptrArchiveParentFolder;
  399. ULONG ulType = 0;
  400. ULONG cValues = 0;
  401. SPropArrayPtr ptrPropArray;
  402. SObjectEntry objectEntry;
  403. static constexpr const SizedSPropTagArray(3, sptaFolderPropsForCreate) =
  404. {3, {PR_CONTAINER_CLASS, PR_DISPLAY_NAME, PR_COMMENT}};
  405. static constexpr const SizedSPropTagArray(2, sptaFolderPropsForReference) =
  406. {2, {PR_ENTRYID, PR_STORE_ENTRYID}};
  407. hr = HrGetOneProp(m_ptrArchiveStore, PR_ENTRYID, &~ptrStoreEntryId);
  408. if (hr != hrSuccess)
  409. return hr;
  410. hr = MAPIPropHelper::Create(ptrSourceFolder.as<MAPIPropPtr>(), &ptrSourceFolderHelper);
  411. if (hr != hrSuccess)
  412. return hr;
  413. hr = ptrSourceFolderHelper->GetArchiveList(&lstFolderArchives);
  414. if (hr == MAPI_E_CORRUPT_DATA) {
  415. // If the list is corrupt, the folder will become unusable. We'll just create a new folder, which will most
  416. // likely become the same folder (if the name hasn't changed).
  417. hr = hrSuccess;
  418. } else if (hr != hrSuccess)
  419. return hr;
  420. iArchiveFolder = find_if(lstFolderArchives.cbegin(), lstFolderArchives.cend(), StoreCompare(ptrStoreEntryId->Value.bin));
  421. if (iArchiveFolder != lstFolderArchives.cend())
  422. hr = m_ptrArchiveStore->OpenEntry(iArchiveFolder->sItemEntryId.size(), iArchiveFolder->sItemEntryId, &ptrArchiveFolder.iid(), MAPI_BEST_ACCESS | fMapiDeferredErrors, &ulType, &~ptrArchiveFolder);
  423. else {
  424. hr = ptrSourceFolderHelper->GetParentFolder(ptrSession, &~ptrParentFolder);
  425. if (hr != hrSuccess)
  426. return hr;
  427. // If the parent is the root folder, we're currently working on the IPM-SUBTREE. This means
  428. // we can just return the root archive folder in that case.
  429. hr = HrGetOneProp(ptrParentFolder, PR_FOLDER_TYPE, &~ptrFolderType);
  430. if (hr != hrSuccess)
  431. return hr;
  432. if (ptrFolderType->Value.l == FOLDER_ROOT)
  433. hr = GetArchiveFolder(true, &~ptrArchiveFolder);
  434. else {
  435. bool bIsArchiveRoot = false;
  436. hr = GetArchiveFolderFor(ptrParentFolder, ptrSession, &~ptrArchiveParentFolder);
  437. if (hr != hrSuccess)
  438. return hr;
  439. hr = IsArchiveFolder(ptrArchiveParentFolder, &bIsArchiveRoot);
  440. if (hr != hrSuccess)
  441. return hr;
  442. // We now have the parent of the folder we're looking for. Se we can just create the folder we need.
  443. hr = ptrSourceFolder->GetProps(sptaFolderPropsForCreate, 0, &cValues, &~ptrPropArray);
  444. if (FAILED(hr))
  445. return hr;
  446. hr = ptrArchiveParentFolder->CreateFolder(FOLDER_GENERIC,
  447. (LPTSTR)(PROP_TYPE(ptrPropArray[1].ulPropTag) == PT_ERROR ? _T("") : ptrPropArray[1].Value.LPSZ),
  448. (LPTSTR)(PROP_TYPE(ptrPropArray[2].ulPropTag) == PT_ERROR ? _T("") : ptrPropArray[2].Value.LPSZ),
  449. &ptrArchiveFolder.iid(), OPEN_IF_EXISTS | fMapiUnicode,
  450. &~ptrArchiveFolder);
  451. if (hr != hrSuccess)
  452. return hr;
  453. if (bIsArchiveRoot) {
  454. bool bIsSpecialFolder = false;
  455. hr = IsSpecialFolder(sfBase, ptrArchiveFolder, &bIsSpecialFolder);
  456. if (hr != hrSuccess)
  457. return hr;
  458. if (bIsSpecialFolder) {
  459. ULONG ulCollisionCount = 0;
  460. do {
  461. tstring strFolderName((LPTSTR)(PROP_TYPE(ptrPropArray[1].ulPropTag) == PT_ERROR ? _T("") : ptrPropArray[1].Value.LPSZ));
  462. if (ulCollisionCount > 0) {
  463. strFolderName.append(_T(" ("));
  464. strFolderName.append(tstringify(ulCollisionCount));
  465. strFolderName.append(1, ')');
  466. }
  467. hr = ptrArchiveParentFolder->CreateFolder(FOLDER_GENERIC, (LPTSTR)strFolderName.c_str(), (LPTSTR)(PROP_TYPE(ptrPropArray[2].ulPropTag) == PT_ERROR ? _T("") : ptrPropArray[2].Value.LPSZ), &ptrArchiveFolder.iid(), fMapiUnicode, &~ptrArchiveFolder);
  468. if (hr != hrSuccess && hr != MAPI_E_COLLISION)
  469. return hr;
  470. ++ulCollisionCount;
  471. } while (hr == MAPI_E_COLLISION && ulCollisionCount < 0xffff); // We need to stop counting at some point.
  472. if (hr != hrSuccess)
  473. return hr;
  474. }
  475. }
  476. if (PROP_TYPE(ptrPropArray[0].ulPropTag) != PT_ERROR)
  477. hr = HrSetOneProp(ptrArchiveFolder, &ptrPropArray[0]);
  478. }
  479. if (hr != hrSuccess)
  480. return hr;
  481. // Update the archive list for the source folder
  482. hr = HrGetOneProp(ptrArchiveFolder, PR_ENTRYID, &~ptrFolderEntryId);
  483. if (hr != hrSuccess)
  484. return hr;
  485. objectEntry.sStoreEntryId.assign(ptrStoreEntryId->Value.bin);
  486. objectEntry.sItemEntryId.assign(ptrFolderEntryId->Value.bin);
  487. lstFolderArchives.push_back(objectEntry);
  488. lstFolderArchives.sort();
  489. lstFolderArchives.unique();
  490. hr = ptrSourceFolderHelper->SetArchiveList(lstFolderArchives);
  491. if (hr != hrSuccess)
  492. return hr;
  493. // Reference the source folder in the archive folder
  494. hr = ptrSourceFolder->GetProps(sptaFolderPropsForReference, 0, &cValues, &~ptrPropArray);
  495. if (hr != hrSuccess)
  496. return hr;
  497. objectEntry.sStoreEntryId.assign(ptrPropArray[1].Value.bin);
  498. objectEntry.sItemEntryId.assign(ptrPropArray[0].Value.bin);
  499. hr = MAPIPropHelper::Create(ptrArchiveFolder.as<MAPIPropPtr>(), &ptrArchiveFolderHelper);
  500. if (hr != hrSuccess)
  501. return hr;
  502. hr = ptrArchiveFolderHelper->SetReference(objectEntry);
  503. }
  504. if (hr != hrSuccess)
  505. return hr;
  506. return ptrArchiveFolder->QueryInterface(IID_IMAPIFolder,
  507. reinterpret_cast<LPVOID *>(lppDestinationFolder));
  508. }
  509. /**
  510. * Get the History folder. If the folder doesn't exist it will be created.
  511. */
  512. HRESULT ArchiveHelper::GetHistoryFolder(LPMAPIFOLDER *lppHistoryFolder)
  513. {
  514. return GetSpecialFolder(sfHistory, true, lppHistoryFolder);
  515. }
  516. /**
  517. * Get the Outgoing foler. If the folder doesn't exist it will be created.
  518. */
  519. HRESULT ArchiveHelper::GetOutgoingFolder(LPMAPIFOLDER *lppOutgoingFolder)
  520. {
  521. return GetSpecialFolder(sfOutgoing, true, lppOutgoingFolder);
  522. }
  523. /**
  524. * Get the DeletedItems folder. If the folder doesn't exist it will be created.
  525. * @note: This is not the trash folder, but a special archive folder that contains
  526. * messages that were deleted in the primary store.
  527. */
  528. HRESULT ArchiveHelper::GetDeletedItemsFolder(LPMAPIFOLDER *lppOutgoingFolder)
  529. {
  530. return GetSpecialFolder(sfDeleted, true, lppOutgoingFolder);
  531. }
  532. /**
  533. * Get the root folder of the special folders. This folder contains the history,
  534. * outgoing and deleted items folders. If the folder doesn't exist it won't be
  535. * created.
  536. */
  537. HRESULT ArchiveHelper::GetSpecialsRootFolder(LPMAPIFOLDER *lppSpecialsRootFolder)
  538. {
  539. return GetSpecialFolder(sfBase, false, lppSpecialsRootFolder);
  540. }
  541. /**
  542. * Get the archive folder. This opens the actual folder if only a name was specified.
  543. *
  544. * @param[in] bCreate
  545. * Create the folder if it doesn't exist.
  546. * @param[out] lppArchiveFolder
  547. * Pointer to the MAPIFolder pointer that will be assigned the address of the
  548. * returned folder.
  549. */
  550. HRESULT ArchiveHelper::GetArchiveFolder(bool bCreate, LPMAPIFOLDER *lppArchiveFolder)
  551. {
  552. HRESULT hr;
  553. StoreHelperPtr ptrStoreHelper;
  554. if (!m_ptrArchiveFolder) {
  555. hr = StoreHelper::Create(m_ptrArchiveStore, &ptrStoreHelper);
  556. if (hr != hrSuccess)
  557. return hr;
  558. if (m_strFolder.empty())
  559. hr = ptrStoreHelper->GetIpmSubtree(&~m_ptrArchiveFolder);
  560. else
  561. hr = ptrStoreHelper->GetFolder(m_strFolder, bCreate, &~m_ptrArchiveFolder);
  562. if (hr != hrSuccess)
  563. return hr;
  564. }
  565. return m_ptrArchiveFolder->QueryInterface(IID_IMAPIFolder,
  566. reinterpret_cast<LPVOID *>(lppArchiveFolder));
  567. }
  568. /**
  569. * Check if the passed folder is the same folder that would be returned with GetArchiveFolder.
  570. *
  571. * @param[in] lpFolder
  572. * Pointer to the MAPIFolder to check.
  573. * @param[out] lpbResult
  574. * True if the folder is the same as would be returned by GetArchiveFolder.
  575. */
  576. HRESULT ArchiveHelper::IsArchiveFolder(LPMAPIFOLDER lpFolder, bool *lpbResult)
  577. {
  578. HRESULT hr;
  579. SPropValuePtr ptrFolderEntryID;
  580. MAPIFolderPtr ptrArchiveFolder;
  581. SPropValuePtr ptrArchiveEntryID;
  582. ULONG ulResult = 0;
  583. hr = HrGetOneProp(lpFolder, PR_ENTRYID, &~ptrFolderEntryID);
  584. if (hr != hrSuccess)
  585. return hr;
  586. hr = GetArchiveFolder(false, &~ptrArchiveFolder);
  587. if (hr == MAPI_E_NOT_FOUND) {
  588. *lpbResult = false;
  589. return hrSuccess;
  590. }
  591. if (hr != hrSuccess)
  592. return hr;
  593. hr = HrGetOneProp(ptrArchiveFolder, PR_ENTRYID, &~ptrArchiveEntryID);
  594. if (hr != hrSuccess)
  595. return hr;
  596. hr = m_ptrArchiveStore->CompareEntryIDs(ptrFolderEntryID->Value.bin.cb, (LPENTRYID)ptrFolderEntryID->Value.bin.lpb,
  597. ptrArchiveEntryID->Value.bin.cb, (LPENTRYID)ptrArchiveEntryID->Value.bin.lpb,
  598. 0, &ulResult);
  599. if (hr != hrSuccess)
  600. return hr;
  601. *lpbResult = (ulResult != FALSE);
  602. return hrSuccess;
  603. }
  604. HRESULT ArchiveHelper::GetSpecialFolderEntryID(eSpecFolder sfWhich, ULONG *lpcbEntryID, LPENTRYID *lppEntryID)
  605. {
  606. HRESULT hr;
  607. MAPIFolderPtr ptrArchiveRoot;
  608. SPropValuePtr ptrSFEntryIDs;
  609. hr = GetArchiveFolder(false, &~ptrArchiveRoot);
  610. if (hr != hrSuccess)
  611. return hr;
  612. hr = HrGetOneProp(ptrArchiveRoot, PROP_SPECIAL_FOLDER_ENTRYIDS, &~ptrSFEntryIDs);
  613. if (hr != hrSuccess)
  614. return hr;
  615. if (ptrSFEntryIDs->Value.MVbin.cValues <= ULONG(sfWhich) || ptrSFEntryIDs->Value.MVbin.lpbin[sfWhich].cb == 0)
  616. return MAPI_E_NOT_FOUND;
  617. return Util::HrCopyEntryId(ptrSFEntryIDs->Value.MVbin.lpbin[sfWhich].cb,
  618. reinterpret_cast<LPENTRYID>(ptrSFEntryIDs->Value.MVbin.lpbin[sfWhich].lpb),
  619. lpcbEntryID, lppEntryID);
  620. }
  621. HRESULT ArchiveHelper::SetSpecialFolderEntryID(eSpecFolder sfWhich, ULONG cbEntryID, LPENTRYID lpEntryID)
  622. {
  623. HRESULT hr;
  624. MAPIFolderPtr ptrArchiveRoot;
  625. SPropValuePtr ptrSFEntryIDs;
  626. hr = GetArchiveFolder(false, &~ptrArchiveRoot);
  627. if (hr != hrSuccess)
  628. return hr;
  629. hr = HrGetOneProp(ptrArchiveRoot, PROP_SPECIAL_FOLDER_ENTRYIDS, &~ptrSFEntryIDs);
  630. if (hr == MAPI_E_NOT_FOUND) {
  631. hr = MAPIAllocateBuffer(sizeof(SPropValue), &~ptrSFEntryIDs);
  632. if (hr != hrSuccess)
  633. return hr;
  634. ptrSFEntryIDs->ulPropTag = PROP_SPECIAL_FOLDER_ENTRYIDS;
  635. ptrSFEntryIDs->Value.MVbin.cValues = 0;
  636. ptrSFEntryIDs->Value.MVbin.lpbin = NULL;
  637. }
  638. if (hr != hrSuccess)
  639. return hr;
  640. if (ptrSFEntryIDs->Value.MVbin.cValues <= ULONG(sfWhich)) {
  641. LPSBinary lpbinPrev = ptrSFEntryIDs->Value.MVbin.lpbin;
  642. hr = MAPIAllocateMore((sfWhich + 1) * sizeof(SBinary), ptrSFEntryIDs, (LPVOID*)&ptrSFEntryIDs->Value.MVbin.lpbin);
  643. if (hr != hrSuccess)
  644. return hr;
  645. // Copy old entries
  646. for (ULONG i = 0; i < ptrSFEntryIDs->Value.MVbin.cValues; ++i)
  647. ptrSFEntryIDs->Value.MVbin.lpbin[i] = lpbinPrev[i]; // Shallow copy
  648. // Pad entries
  649. for (ULONG i = ptrSFEntryIDs->Value.MVbin.cValues; i < ULONG(sfWhich); ++i) {
  650. ptrSFEntryIDs->Value.MVbin.lpbin[i].cb = 0;
  651. ptrSFEntryIDs->Value.MVbin.lpbin[i].lpb = NULL;
  652. }
  653. ptrSFEntryIDs->Value.MVbin.cValues = sfWhich + 1;
  654. }
  655. ptrSFEntryIDs->Value.MVbin.lpbin[sfWhich].cb = cbEntryID;
  656. ptrSFEntryIDs->Value.MVbin.lpbin[sfWhich].lpb = (LPBYTE)lpEntryID; // Shallow copy
  657. hr = HrSetOneProp(ptrArchiveRoot, ptrSFEntryIDs);
  658. if (hr != hrSuccess)
  659. return hr;
  660. return ptrArchiveRoot->SaveChanges(KEEP_OPEN_READWRITE);
  661. }
  662. /**
  663. * Open one of the special folders.
  664. *
  665. * @param[in] sfWhich The folder to open. Valid values are sfBase, sfHistory, sfOutgoing and sfDeleted.
  666. * @param[in] bCreate Specify if the folder should be created if absent.
  667. * @param[out] lppSpecialFolder Will point to the special folder on success.
  668. */
  669. HRESULT ArchiveHelper::GetSpecialFolder(eSpecFolder sfWhich, bool bCreate, LPMAPIFOLDER *lppSpecialFolder)
  670. {
  671. HRESULT hr;
  672. ULONG ulSpecialFolderID;
  673. EntryIdPtr ptrSpecialFolderID;
  674. MAPIFolderPtr ptrSpecialFolder;
  675. hr = GetSpecialFolderEntryID(sfWhich, &ulSpecialFolderID, &~ptrSpecialFolderID);
  676. if (hr == hrSuccess) {
  677. ULONG ulType;
  678. hr = m_ptrArchiveStore->OpenEntry(ulSpecialFolderID, ptrSpecialFolderID, &ptrSpecialFolder.iid(), MAPI_MODIFY, &ulType, &~ptrSpecialFolder);
  679. } else if (hr == MAPI_E_NOT_FOUND && bCreate) {
  680. hr = CreateSpecialFolder(sfWhich, &~ptrSpecialFolder);
  681. }
  682. if (hr != hrSuccess)
  683. return hr;
  684. return ptrSpecialFolder->QueryInterface(IID_IMAPIFolder,
  685. reinterpret_cast<LPVOID *>(lppSpecialFolder));
  686. }
  687. HRESULT ArchiveHelper::CreateSpecialFolder(eSpecFolder sfWhich, LPMAPIFOLDER *lppSpecialFolder)
  688. {
  689. HRESULT hr;
  690. MAPIFolderPtr ptrParent;
  691. MAPIFolderPtr ptrSpecialFolder;
  692. LPTSTR lpszName = NULL;
  693. LPTSTR lpszDesc = NULL;
  694. ULONG ulCreateFlags = OPEN_IF_EXISTS;
  695. ULONG ulCollisionCount = 0;
  696. SPropValuePtr ptrEntryID;
  697. if (sfWhich == sfBase)
  698. // We need to get the archive root to create the special root folder in
  699. hr = GetArchiveFolder(true, &~ptrParent);
  700. else
  701. // We need to get the special root to create the other special folders in
  702. hr = GetSpecialFolder(sfBase, true, &~ptrParent);
  703. if (hr != hrSuccess)
  704. return hr;
  705. switch (sfWhich) {
  706. case sfBase:
  707. lpszName = _("Kopano Archive");
  708. lpszDesc = _("This folder contains the special archive folders.");
  709. ulCreateFlags = 0;
  710. break;
  711. case sfHistory:
  712. lpszName = _("History");
  713. lpszDesc = _("This folder contains archives that have been replaced by a newer version.");
  714. break;
  715. case sfOutgoing:
  716. lpszName = _("Outgoing");
  717. lpszDesc = _("This folder contains archives of all outgoing messages.");
  718. break;
  719. case sfDeleted:
  720. lpszName = _("Deleted");
  721. lpszDesc = _("This folder contains archives of messages that have been deleted.");
  722. break;
  723. default:
  724. assert(false);
  725. return MAPI_E_INVALID_PARAMETER;
  726. }
  727. do {
  728. tstring strFolderName(lpszName);
  729. if (ulCollisionCount > 0) {
  730. strFolderName.append(_T(" ("));
  731. strFolderName.append(tstringify(ulCollisionCount));
  732. strFolderName.append(1, ')');
  733. }
  734. hr = ptrParent->CreateFolder(FOLDER_GENERIC, (LPTSTR)strFolderName.c_str(), lpszDesc, &ptrSpecialFolder.iid(), fMapiUnicode|ulCreateFlags, &~ptrSpecialFolder);
  735. if (hr != hrSuccess && hr != MAPI_E_COLLISION)
  736. return hr;
  737. ++ulCollisionCount;
  738. } while (hr == MAPI_E_COLLISION && ulCollisionCount < 0xffff); // We need to stop counting at some point.
  739. if (hr != hrSuccess)
  740. return hr;
  741. hr = HrGetOneProp(ptrSpecialFolder, PR_ENTRYID, &~ptrEntryID);
  742. if (hr != hrSuccess)
  743. return hr;
  744. hr = SetSpecialFolderEntryID(sfWhich, ptrEntryID->Value.bin.cb, (LPENTRYID)ptrEntryID->Value.bin.lpb);
  745. if (hr != hrSuccess)
  746. return hr;
  747. return ptrSpecialFolder->QueryInterface(IID_IMAPIFolder,
  748. reinterpret_cast<LPVOID *>(lppSpecialFolder));
  749. }
  750. HRESULT ArchiveHelper::IsSpecialFolder(eSpecFolder sfWhich, LPMAPIFOLDER lpFolder, bool *lpbResult)
  751. {
  752. HRESULT hr;
  753. ULONG cbSpecialEntryID;
  754. EntryIdPtr ptrSpecialEntryID;
  755. SPropValuePtr ptrFolderEntryID;
  756. ULONG ulResult = 0;
  757. hr = GetSpecialFolderEntryID(sfWhich, &cbSpecialEntryID, &~ptrSpecialEntryID);
  758. if (hr == MAPI_E_NOT_FOUND) {
  759. *lpbResult = false;
  760. return hrSuccess;
  761. }
  762. if (hr != hrSuccess)
  763. return hr;
  764. hr = HrGetOneProp(lpFolder, PR_ENTRYID, &~ptrFolderEntryID);
  765. if (hr != hrSuccess)
  766. return hr;
  767. hr = m_ptrArchiveStore->CompareEntryIDs(ptrFolderEntryID->Value.bin.cb, (LPENTRYID)ptrFolderEntryID->Value.bin.lpb,
  768. cbSpecialEntryID, ptrSpecialEntryID, 0, &ulResult);
  769. if (hr != hrSuccess)
  770. return hr;
  771. *lpbResult = (ulResult != FALSE);
  772. return hrSuccess;
  773. }
  774. HRESULT ArchiveHelper::PrepareForFirstUse(ECLogger *lpLogger)
  775. {
  776. HRESULT hr;
  777. StoreHelperPtr ptrStoreHelper;
  778. MAPIFolderPtr ptrIpmSubtree;
  779. SPropValue sEntryId;
  780. hr = StoreHelper::Create(m_ptrArchiveStore, &ptrStoreHelper);
  781. if (hr != hrSuccess) {
  782. if (lpLogger)
  783. lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to create store helper (hr=0x%08x).", hr);
  784. return hr;
  785. }
  786. hr = ptrStoreHelper->GetIpmSubtree(&~ptrIpmSubtree);
  787. if (hr != hrSuccess) {
  788. if (lpLogger)
  789. lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to get archive IPM subtree (hr=0x%08x).", hr);
  790. return hr;
  791. }
  792. hr = ptrIpmSubtree->EmptyFolder(0, NULL, DEL_ASSOCIATED|DELETE_HARD_DELETE);
  793. if (FAILED(hr)) {
  794. if (lpLogger)
  795. lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to empty archive IPM subtree (hr=0x%08x).", hr);
  796. return hr;
  797. } else if (hr != hrSuccess) {
  798. if (lpLogger)
  799. lpLogger->Log(EC_LOGLEVEL_WARNING, "archive IPM subtree was only partially emptied.");
  800. }
  801. // Now set the Wastebasket entryid to 0,NULL, so outlook won't create it
  802. sEntryId.ulPropTag = PR_IPM_WASTEBASKET_ENTRYID;
  803. sEntryId.Value.bin.cb = 0;
  804. sEntryId.Value.bin.lpb = NULL;
  805. hr = HrSetOneProp(m_ptrArchiveStore, &sEntryId);
  806. if (hr != hrSuccess) {
  807. if (lpLogger)
  808. lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to clear wasebasket entryid (hr=0x%08x)", hr);
  809. return hr;
  810. }
  811. return hrSuccess;
  812. }
  813. }} /* namespace */