copier.cpp 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027
  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 <kopano/ECConfig.h>
  21. #include <kopano/ECRestriction.h>
  22. #include "ECArchiverLogger.h"
  23. #include "copier.h"
  24. #include "deleter.h"
  25. #include "stubber.h"
  26. #include "instanceidmapper.h"
  27. #include "transaction.h"
  28. #include "postsaveiidupdater.h"
  29. #include "helpers/MAPIPropHelper.h"
  30. #include "helpers/ArchiveHelper.h"
  31. #include "ArchiverSession.h"
  32. #include <kopano/Util.h>
  33. #include <kopano/mapiguidext.h>
  34. #include <list>
  35. #include <string>
  36. using namespace std;
  37. using namespace KC::helpers;
  38. namespace KC { namespace operations {
  39. Copier::Helper::Helper(ArchiverSessionPtr ptrSession, ECLogger *lpLogger,
  40. const InstanceIdMapperPtr &ptrMapper, const SPropTagArray *lpExcludeProps,
  41. LPMAPIFOLDER lpFolder) :
  42. m_ptrSession(ptrSession), m_lpLogger(lpLogger),
  43. m_lpExcludeProps(lpExcludeProps), m_ptrFolder(lpFolder, true),
  44. // do an AddRef so we don't take ownership of the folder
  45. m_ptrMapper(ptrMapper)
  46. {
  47. m_lpLogger->AddRef();
  48. }
  49. Copier::Helper::~Helper(void)
  50. {
  51. m_lpLogger->Release();
  52. }
  53. HRESULT Copier::Helper::CreateArchivedMessage(LPMESSAGE lpSource, const SObjectEntry &archiveEntry, const SObjectEntry &refMsgEntry, LPMESSAGE *lppArchivedMsg, PostSaveActionPtr *lpptrPSAction)
  54. {
  55. HRESULT hr;
  56. MAPIFolderPtr ptrArchiveFolder;
  57. MessagePtr ptrNewMessage;
  58. PostSaveActionPtr ptrPSAction;
  59. hr = GetArchiveFolder(archiveEntry, &~ptrArchiveFolder);
  60. if (hr != hrSuccess)
  61. return hr;
  62. hr = ptrArchiveFolder->CreateMessage(&ptrNewMessage.iid(), fMapiDeferredErrors, &~ptrNewMessage);
  63. if (hr != hrSuccess) {
  64. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to create archive message. (hr=%s)", stringify(hr, true).c_str());
  65. return hr;
  66. }
  67. hr = ArchiveMessage(lpSource, &refMsgEntry, ptrNewMessage, &ptrPSAction);
  68. if (hr != hrSuccess)
  69. return hr;
  70. hr = ptrNewMessage->QueryInterface(IID_IMessage, (LPVOID*)lppArchivedMsg);
  71. if (hr != hrSuccess)
  72. return hr;
  73. lpptrPSAction->swap(ptrPSAction);
  74. return hrSuccess;
  75. }
  76. HRESULT Copier::Helper::GetArchiveFolder(const SObjectEntry &archiveEntry, LPMAPIFOLDER *lppArchiveFolder)
  77. {
  78. HRESULT hr;
  79. ArchiveFolderMap::const_iterator iArchiveFolder;
  80. MAPIFolderPtr ptrArchiveFolder;
  81. if (lppArchiveFolder == NULL)
  82. return MAPI_E_INVALID_PARAMETER;
  83. iArchiveFolder = m_mapArchiveFolders.find(archiveEntry.sStoreEntryId);
  84. if (iArchiveFolder == m_mapArchiveFolders.cend()) {
  85. ArchiveHelperPtr ptrArchiveHelper;
  86. m_lpLogger->Log(EC_LOGLEVEL_DEBUG, "Archive folder not found in cache");
  87. // Find the associated archive folder
  88. hr = ArchiveHelper::Create(m_ptrSession, archiveEntry, m_lpLogger, &ptrArchiveHelper);
  89. if (hr != hrSuccess)
  90. return hr;
  91. hr = ptrArchiveHelper->GetArchiveFolderFor(m_ptrFolder, m_ptrSession, &~ptrArchiveFolder);
  92. if (hr != hrSuccess) {
  93. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to get archive folder. (hr=%s)", stringify(hr, true).c_str());
  94. return hr;
  95. }
  96. m_mapArchiveFolders.insert(ArchiveFolderMap::value_type(archiveEntry.sStoreEntryId, ptrArchiveFolder));
  97. } else {
  98. m_lpLogger->Log(EC_LOGLEVEL_DEBUG, "Archive folder found in cache");
  99. ptrArchiveFolder = iArchiveFolder->second;
  100. }
  101. static constexpr const SizedSPropTagArray(2, sptaProps) =
  102. {2, {PR_DISPLAY_NAME_A, PR_ENTRYID}};
  103. SPropArrayPtr props;
  104. ULONG cb;
  105. HRESULT hrTmp = ptrArchiveFolder->GetProps(sptaProps, 0, &cb, &~props);
  106. if (!FAILED(hrTmp)) {
  107. if (PROP_TYPE(props[0].ulPropTag) != PT_ERROR)
  108. m_lpLogger->Log(EC_LOGLEVEL_DEBUG, "Archive folder: %s", props[0].Value.lpszA);
  109. else
  110. m_lpLogger->Log(EC_LOGLEVEL_DEBUG, "Archive folder: has no name");
  111. m_lpLogger->Log(EC_LOGLEVEL_DEBUG, "Archive folder entryid: %s", bin2hex(props[1].Value.bin.cb, props[1].Value.bin.lpb).c_str());
  112. }
  113. return ptrArchiveFolder->QueryInterface(IID_IMAPIFolder,
  114. reinterpret_cast<LPVOID *>(lppArchiveFolder));
  115. }
  116. HRESULT Copier::Helper::ArchiveMessage(LPMESSAGE lpSource, const SObjectEntry *lpMsgEntry, LPMESSAGE lpDest, PostSaveActionPtr *lpptrPSAction)
  117. {
  118. HRESULT hr = hrSuccess;
  119. MAPIPropHelperPtr ptrMsgHelper;
  120. SPropValuePtr ptrEntryId;
  121. SPropValue sPropArchFlags = {0};
  122. PostSaveActionPtr ptrPSAction;
  123. if (lpSource == NULL || lpDest == NULL)
  124. return MAPI_E_INVALID_PARAMETER; // Don't use goto so we can use the PROPMAP macros after checking lpDest
  125. PROPMAP_START(1)
  126. PROPMAP_NAMED_ID(FLAGS, PT_LONG, PSETID_Archive, dispidFlags)
  127. PROPMAP_INIT(lpDest)
  128. hr = lpSource->CopyTo(0, NULL, m_lpExcludeProps, 0, NULL, &IID_IMessage, lpDest, 0, NULL);
  129. // @todo: What to do with warnings?
  130. if (FAILED(hr)) {
  131. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to copy message. (hr=%s)", stringify(hr, true).c_str());
  132. goto exitpm;
  133. }
  134. hr = UpdateIIDs(lpSource, lpDest, &ptrPSAction);
  135. if (hr != hrSuccess) {
  136. m_lpLogger->Log(EC_LOGLEVEL_ERROR, "Failed to update single instance IDs, continuing with copies. (hr=0x%08x)", hr);
  137. hr = hrSuccess;
  138. }
  139. sPropArchFlags.ulPropTag = PROP_FLAGS;
  140. sPropArchFlags.Value.ul = ARCH_NEVER_DELETE | ARCH_NEVER_STUB;
  141. hr = lpDest->SetProps(1, &sPropArchFlags, NULL);
  142. if (hr != hrSuccess) {
  143. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to set flags on archive message. (hr=%s)", stringify(hr, true).c_str());
  144. goto exitpm;
  145. }
  146. hr = MAPIPropHelper::Create(MAPIPropPtr(lpDest, true), &ptrMsgHelper);
  147. if (hr != hrSuccess) {
  148. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to create prop helper. (hr=%s)", stringify(hr, true).c_str());
  149. goto exitpm;
  150. }
  151. if (lpMsgEntry) {
  152. hr = ptrMsgHelper->SetReference(*lpMsgEntry);
  153. if (hr != hrSuccess) {
  154. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to set reference to original message. (hr=%s)", stringify(hr, true).c_str());
  155. goto exitpm;
  156. }
  157. }
  158. lpptrPSAction->swap(ptrPSAction);
  159. exitpm:
  160. return hr;
  161. }
  162. HRESULT Copier::Helper::UpdateIIDs(LPMESSAGE lpSource, LPMESSAGE lpDest, PostSaveActionPtr *lpptrPSAction)
  163. {
  164. HRESULT hr;
  165. MAPITablePtr ptrSourceTable;
  166. MAPITablePtr ptrDestTable;
  167. ULONG ulSourceRows = 0;
  168. ULONG ulDestRows = 0;
  169. SPropValuePtr ptrSourceServerUID;
  170. SPropValuePtr ptrDestServerUID;
  171. TaskList lstDeferred;
  172. static constexpr const SizedSPropTagArray(1, sptaAttachProps) = {1, {PR_ATTACH_NUM}};
  173. enum {IDX_ATTACH_NUM};
  174. if (lpSource == NULL || lpDest == NULL)
  175. return MAPI_E_INVALID_PARAMETER;
  176. hr = HrGetOneProp(lpSource, PR_EC_SERVER_UID, &~ptrSourceServerUID);
  177. if (hr != hrSuccess) {
  178. m_lpLogger->Log(EC_LOGLEVEL_ERROR, "Failed to get source server UID, hr=0x%08x", hr);
  179. return hr;
  180. }
  181. hr = HrGetOneProp(lpDest, PR_EC_SERVER_UID, &~ptrDestServerUID);
  182. if (hr != hrSuccess) {
  183. m_lpLogger->Log(EC_LOGLEVEL_ERROR, "Failed to get dest server UID, hr=0x%08x", hr);
  184. return hr;
  185. }
  186. if (Util::CompareSBinary(ptrSourceServerUID->Value.bin, ptrDestServerUID->Value.bin) == 0) {
  187. m_lpLogger->Log(EC_LOGLEVEL_DEBUG, "Source and destination live on the same server, no explicit deduplication required.");
  188. return hr;
  189. }
  190. hr = lpSource->GetAttachmentTable(0, &~ptrSourceTable);
  191. if (hr != hrSuccess) {
  192. m_lpLogger->Log(EC_LOGLEVEL_ERROR, "Failed to get source attachment table. hr=0x%08x", hr);
  193. return hr;
  194. }
  195. hr = ptrSourceTable->SetColumns(sptaAttachProps, TBL_BATCH);
  196. if (hr != hrSuccess) {
  197. m_lpLogger->Log(EC_LOGLEVEL_ERROR, "Failed to set source attachment columns. hr=0x%08x", hr);
  198. return hr;
  199. }
  200. hr = ptrSourceTable->GetRowCount(0, &ulSourceRows);
  201. if (hr != hrSuccess) {
  202. m_lpLogger->Log(EC_LOGLEVEL_ERROR, "Failed to get source attachment count. hr=0x%08x", hr);
  203. return hr;
  204. }
  205. if (ulSourceRows == 0) {
  206. m_lpLogger->Log(EC_LOGLEVEL_DEBUG, "No attachments in source message, nothing to deduplicate.");
  207. return hr;
  208. }
  209. hr = lpDest->GetAttachmentTable(0, &~ptrDestTable);
  210. if (hr != hrSuccess) {
  211. m_lpLogger->Log(EC_LOGLEVEL_ERROR, "Failed to get dest attachment table. hr=0x%08x", hr);
  212. return hr;
  213. }
  214. hr = ptrDestTable->SetColumns(sptaAttachProps, TBL_BATCH);
  215. if (hr != hrSuccess) {
  216. m_lpLogger->Log(EC_LOGLEVEL_ERROR, "Failed to set dest attachment columns. hr=0x%08x", hr);
  217. return hr;
  218. }
  219. hr = ptrDestTable->GetRowCount(0, &ulDestRows);
  220. if (hr != hrSuccess) {
  221. m_lpLogger->Log(EC_LOGLEVEL_ERROR, "Failed to get dest attachment count. hr=0x%08x", hr);
  222. return hr;
  223. }
  224. if (ulSourceRows != ulDestRows) {
  225. m_lpLogger->Log(EC_LOGLEVEL_WARNING, "Source has %u attachments, destination has %u. No idea how to match them...", ulSourceRows, ulDestRows);
  226. return MAPI_E_NO_SUPPORT;
  227. }
  228. // We'll go through the table one row at a time (from each table) and assume the attachments
  229. // are sorted the same. We will do a sanity check on the size property, though.
  230. while (true) {
  231. SRowSetPtr ptrSourceRows;
  232. SRowSetPtr ptrDestRows;
  233. hr = ptrSourceTable->QueryRows(16, 0, &ptrSourceRows);
  234. if (hr != hrSuccess) {
  235. m_lpLogger->Log(EC_LOGLEVEL_ERROR, "Failed to query source rows. hr=0x%08x", hr);
  236. return hr;
  237. }
  238. if (ptrSourceRows.empty())
  239. break;
  240. hr = ptrDestTable->QueryRows(16, 0, &ptrDestRows);
  241. if (hr != hrSuccess) {
  242. m_lpLogger->Log(EC_LOGLEVEL_ERROR, "Failed to query source rows. hr=0x%08x", hr);
  243. return hr;
  244. }
  245. assert(ptrSourceRows.size() == ptrDestRows.size());
  246. for (SRowSetPtr::size_type i = 0; i < ptrSourceRows.size(); ++i) {
  247. HRESULT hrTmp = hrSuccess;
  248. AttachPtr ptrSourceAttach;
  249. SPropValuePtr ptrAttachMethod;
  250. AttachPtr ptrDestAttach;
  251. ECSingleInstancePtr ptrInstance;
  252. ULONG cbSourceSIID;
  253. EntryIdPtr ptrSourceSIID;
  254. ULONG cbDestSIID;
  255. EntryIdPtr ptrDestSIID;
  256. hrTmp = lpSource->OpenAttach(ptrSourceRows[i].lpProps[IDX_ATTACH_NUM].Value.ul, nullptr, MAPI_DEFERRED_ERRORS, &~ptrSourceAttach);
  257. if (hrTmp != hrSuccess) {
  258. m_lpLogger->Log(EC_LOGLEVEL_ERROR, "Failed to open source attachment %u. Skipping attachment. hr=0x%08x", i, hrTmp);
  259. continue;
  260. }
  261. hrTmp = HrGetOneProp(ptrSourceAttach, PR_ATTACH_METHOD, &~ptrAttachMethod);
  262. if (hrTmp == MAPI_E_NOT_FOUND) {
  263. m_lpLogger->Log(EC_LOGLEVEL_DEBUG, "No PR_ATTACH_METHOD found for attachment %u, assuming NO_ATTACHMENT. So nothing to deduplicate.", i);
  264. continue;
  265. }
  266. if (hrTmp != hrSuccess) {
  267. m_lpLogger->Log(EC_LOGLEVEL_ERROR, "Failed to get PR_ATTACH_METHOD for attachment %u. Skipping attachment. hr=0x%08x", i, hrTmp);
  268. continue;
  269. }
  270. if (ptrAttachMethod->Value.ul != ATTACH_BY_VALUE) {
  271. m_lpLogger->Log(EC_LOGLEVEL_DEBUG, "Attachment method for attachment %u is not ATTACH_BY_VALUE. So nothing to deduplicate.", i);
  272. continue;
  273. }
  274. hrTmp = ptrSourceAttach->QueryInterface(ptrInstance.iid(), &~ptrInstance);
  275. if (hrTmp != hrSuccess) {
  276. m_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to get single instance interface for source attachment %u. Skipping attachment. hr=0x%08x", i, hrTmp);
  277. continue;
  278. }
  279. hrTmp = ptrInstance->GetSingleInstanceId(&cbSourceSIID, &~ptrSourceSIID);
  280. if (hrTmp != hrSuccess) {
  281. m_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to get single instance ID for source attachment %u. Skipping attachment. hr=0x%08x", i, hrTmp);
  282. continue;
  283. }
  284. if (cbSourceSIID == 0 || !ptrSourceSIID) {
  285. m_lpLogger->Log(EC_LOGLEVEL_WARNING, "Got empty single instance ID for attachment %u. That's not suitable for deduplication.", i);
  286. continue;
  287. }
  288. hrTmp = m_ptrMapper->GetMappedInstanceId(ptrSourceServerUID->Value.bin, cbSourceSIID, ptrSourceSIID, ptrDestServerUID->Value.bin, &cbDestSIID, &~ptrDestSIID);
  289. if (hrTmp == MAPI_E_NOT_FOUND) {
  290. m_lpLogger->Log(EC_LOGLEVEL_DEBUG, "No mapped IID found, list message for deferred creation of mapping");
  291. lstDeferred.push_back(TaskPtr(new TaskMapInstanceId(ptrSourceAttach, MessagePtr(lpDest, true), i)));
  292. continue;
  293. }
  294. if (hrTmp != hrSuccess) {
  295. m_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to get mapped instance ID for attachment %u. Skipping attachment. hr=0x%08x", i, hrTmp);
  296. continue;
  297. }
  298. hrTmp = lpDest->OpenAttach(ptrDestRows[i].lpProps[IDX_ATTACH_NUM].Value.ul, nullptr, MAPI_DEFERRED_ERRORS, &~ptrDestAttach);
  299. if (hrTmp != hrSuccess) {
  300. m_lpLogger->Log(EC_LOGLEVEL_ERROR, "Failed to open dest attachment %u. Skipping attachment. hr=0x%08x", i, hrTmp);
  301. continue;
  302. }
  303. hrTmp = ptrDestAttach->QueryInterface(ptrInstance.iid(), &~ptrInstance);
  304. if (hrTmp != hrSuccess) {
  305. m_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to get single instance interface for dest attachment %u. Skipping attachment. hr=0x%08x", i, hrTmp);
  306. continue;
  307. }
  308. hrTmp = ptrInstance->SetSingleInstanceId(cbDestSIID, ptrDestSIID);
  309. if (hrTmp != hrSuccess) {
  310. m_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to set single instance ID for dest attachment %u. hr=0x%08x", i, hrTmp);
  311. continue;
  312. }
  313. hrTmp = ptrDestAttach->SaveChanges(0);
  314. if (hrTmp != hrSuccess) {
  315. m_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to save single instance ID for dest attachment %u. hr=0x%08x", i, hrTmp);
  316. continue;
  317. }
  318. lstDeferred.push_back(TaskPtr(new TaskVerifyAndUpdateInstanceId(ptrSourceAttach, MessagePtr(lpDest, true), i, cbDestSIID, ptrDestSIID)));
  319. }
  320. }
  321. static_assert(sizeof(PostSaveInstanceIdUpdater) || true, "incomplete type must not be used");
  322. if (lstDeferred.empty())
  323. lpptrPSAction->reset();
  324. else
  325. lpptrPSAction->reset(new PostSaveInstanceIdUpdater(PR_ATTACH_DATA_BIN, m_ptrMapper, lstDeferred));
  326. return hrSuccess;
  327. }
  328. /**
  329. * @param[in] lpSession
  330. * Pointer to the session.
  331. * @param[in] lpLogger
  332. * Pointer to the logger.
  333. * @param[in] lstArchives
  334. * The list of attached archives for this store.
  335. * @param[in] lpExcludeProps
  336. * The list of properties that will not be copied during the archive operation.
  337. */
  338. Copier::Copier(ArchiverSessionPtr ptrSession, ECConfig *lpConfig,
  339. ECArchiverLogger *lpLogger, const ObjectEntryList &lstArchives,
  340. const SPropTagArray *lpExcludeProps, int ulAge, bool bProcessUnread) :
  341. ArchiveOperationBaseEx(lpLogger, ulAge, bProcessUnread, ARCH_NEVER_ARCHIVE),
  342. m_ptrSession(ptrSession), m_lpConfig(lpConfig),
  343. m_lstArchives(lstArchives),
  344. m_ptrTransaction(new Transaction(SObjectEntry()))
  345. {
  346. if (MAPIAllocateBuffer(CbNewSPropTagArray(lpExcludeProps->cValues), &~m_ptrExcludeProps) != hrSuccess)
  347. throw std::bad_alloc();
  348. memcpy(m_ptrExcludeProps, lpExcludeProps, CbNewSPropTagArray(lpExcludeProps->cValues));
  349. // If the next call fails, m_ptrMapper will have NULL ptr, which we'll check later.
  350. InstanceIdMapper::Create(lpLogger, lpConfig, &m_ptrMapper);
  351. }
  352. Copier::~Copier()
  353. {
  354. m_ptrTransaction->PurgeDeletes(m_ptrSession);
  355. }
  356. HRESULT Copier::GetRestriction(LPMAPIPROP lpMapiProp, LPSRestriction *lppRestriction)
  357. {
  358. HRESULT hr = hrSuccess;
  359. ECOrRestriction resResult;
  360. SRestrictionPtr ptrRestriction;
  361. PROPMAP_START(1)
  362. PROPMAP_NAMED_ID(ORIGINAL_SOURCE_KEY, PT_BINARY, PSETID_Archive, dispidOrigSourceKey)
  363. PROPMAP_INIT(lpMapiProp)
  364. // Start out with the base restriction, which checks if a message is
  365. // old enough to be processed.
  366. hr = ArchiveOperationBaseEx::GetRestriction(lpMapiProp, &~ptrRestriction);
  367. if (hr != hrSuccess)
  368. goto exitpm;
  369. resResult += ECRawRestriction(ptrRestriction, ECRestriction::Cheap);
  370. // A reason to process a message before being old enough is when
  371. // it's already archived (by archive-on-delivery or because the required
  372. // age has changed). We'll check that by checking if PROP_ORIGINAL_SOURCE_KEY
  373. // is present.
  374. resResult += ECExistRestriction(PROP_ORIGINAL_SOURCE_KEY);
  375. hr = resResult.CreateMAPIRestriction(lppRestriction, ECRestriction::Full);
  376. exitpm:
  377. return hr;
  378. }
  379. HRESULT Copier::EnterFolder(LPMAPIFOLDER lpFolder)
  380. {
  381. if (!m_ptrMapper)
  382. return MAPI_E_UNCONFIGURED;
  383. m_ptrHelper.reset(new Helper(m_ptrSession, Logger(), m_ptrMapper, m_ptrExcludeProps, lpFolder));
  384. return hrSuccess;
  385. }
  386. HRESULT Copier::LeaveFolder()
  387. {
  388. if (!m_ptrMapper)
  389. return MAPI_E_UNCONFIGURED;
  390. m_ptrHelper.reset();
  391. return hrSuccess;
  392. }
  393. HRESULT Copier::DoProcessEntry(ULONG cProps, const LPSPropValue &lpProps)
  394. {
  395. HRESULT hr;
  396. SObjectEntry refObjectEntry;
  397. MessagePtr ptrMessageRaw;
  398. MessagePtr ptrMessage;
  399. ULONG ulType = 0;
  400. MAPIPropHelperPtr ptrMsgHelper;
  401. MessageState state;
  402. ObjectEntryList lstMsgArchives;
  403. HRESULT hrTemp;
  404. ObjectEntryList lstNewMsgArchives;
  405. TransactionList lstTransactions;
  406. RollbackList lstRollbacks;
  407. if (!m_ptrMapper)
  408. return MAPI_E_UNCONFIGURED;
  409. auto lpEntryId = PCpropFindProp(lpProps, cProps, PR_ENTRYID);
  410. if (lpEntryId == NULL) {
  411. Logger()->Log(EC_LOGLEVEL_FATAL, "PR_ENTRYID missing");
  412. return MAPI_E_NOT_FOUND;
  413. }
  414. auto lpStoreEntryId = PCpropFindProp(lpProps, cProps, PR_STORE_ENTRYID);
  415. if (lpStoreEntryId == NULL) {
  416. Logger()->Log(EC_LOGLEVEL_FATAL, "PR_STORE_ENTRYID missing");
  417. return MAPI_E_NOT_FOUND;
  418. }
  419. // Set the reference to the original message
  420. refObjectEntry.sStoreEntryId.assign(lpStoreEntryId->Value.bin);
  421. refObjectEntry.sItemEntryId.assign(lpEntryId->Value.bin);
  422. Logger()->Log(EC_LOGLEVEL_DEBUG, "Opening message (%s)", bin2hex(lpEntryId->Value.bin.cb, lpEntryId->Value.bin.lpb).c_str());
  423. hr = CurrentFolder()->OpenEntry(lpEntryId->Value.bin.cb, reinterpret_cast<ENTRYID *>(lpEntryId->Value.bin.lpb), &IID_IECMessageRaw, MAPI_MODIFY|fMapiDeferredErrors, &ulType, &~ptrMessageRaw);
  424. if (hr != hrSuccess) {
  425. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to open message. (hr=%s)", stringify(hr, true).c_str());
  426. return hr;
  427. }
  428. hr = VerifyRestriction(ptrMessageRaw);
  429. if (hr == MAPI_E_NOT_FOUND) {
  430. Logger()->Log(EC_LOGLEVEL_WARNING, "Ignoring message because it doesn't match the criteria for begin archived.");
  431. Logger()->Log(EC_LOGLEVEL_WARNING, "This can happen when huge amounts of message are being processed.");
  432. // This is not an error
  433. hr = hrSuccess;
  434. // We could process subrestrictions for this message, but that will be picked up at a later stage anyway.
  435. return hr;
  436. } else if (hr != hrSuccess) {
  437. Logger()->Log(EC_LOGLEVEL_WARNING, "Failed to verify message criteria. (hr=%s)", stringify(hr, true).c_str());
  438. return hr;
  439. }
  440. hr = MAPIPropHelper::Create(ptrMessageRaw.as<MAPIPropPtr>(), &ptrMsgHelper);
  441. if (hr != hrSuccess) {
  442. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to create prop helper. (hr=%s)", stringify(hr, true).c_str());
  443. return hr;
  444. }
  445. hr = ptrMsgHelper->GetMessageState(m_ptrSession, &state);
  446. if (hr != hrSuccess) {
  447. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to determine message state. (hr=%s)", stringify(hr, true).c_str());
  448. return hr;
  449. }
  450. if (state.isCopy() || state.isMove()) {
  451. Logger()->Log(EC_LOGLEVEL_INFO, "Found %s archive, treating it as a new message.", (state.isCopy() ? "copied" : "moved"));
  452. // Here we reopen the source message with destubbing enabled. This message
  453. // will be used as source for creating the new archive message. Note that
  454. // we leave the ptrMsgHelper as is, operating on the raw message.
  455. hr = CurrentFolder()->OpenEntry(lpEntryId->Value.bin.cb, reinterpret_cast<ENTRYID *>(lpEntryId->Value.bin.lpb), &IID_IMessage, fMapiDeferredErrors, &ulType, &~ptrMessage);
  456. if (hr != hrSuccess) {
  457. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to reopen message. (hr=%s)", stringify(hr, true).c_str());
  458. return hr;
  459. }
  460. } else
  461. ptrMessage = ptrMessageRaw;
  462. // From here on we work on ptrMessage, except for ExecuteSubOperations.
  463. if (!state.isCopy()) { // Include state.isMove()
  464. hr = ptrMsgHelper->GetArchiveList(&lstMsgArchives);
  465. if (hr != hrSuccess) {
  466. if (hr == MAPI_E_CORRUPT_DATA) {
  467. Logger()->Log(EC_LOGLEVEL_ERROR, "Existing list of archives is corrupt, skipping message.");
  468. hr = hrSuccess;
  469. } else
  470. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to get list of archives. (hr=%s)", stringify(hr, true).c_str());
  471. return hr;
  472. }
  473. }
  474. for (const auto &arc : m_lstArchives) {
  475. TransactionPtr ptrTransaction;
  476. auto iArchivedMsg = find_if(lstMsgArchives.cbegin(), lstMsgArchives.cend(), StoreCompare(arc));
  477. // Check if this message is archived to the current archive.
  478. if (iArchivedMsg == lstMsgArchives.cend()) {
  479. // It's possible to have a dirty message that has not been archived to the current archive if at the time of the
  480. // previous archiver run, this archive was unavailable for some reason.
  481. // If this happens, we'll can't do much more than just archive the current version of the message to the archive.
  482. //
  483. // Alternatively we could get the previous version from another archive and copy that to this archive if the
  484. // configuration dictates that old archives are linked. We won't do that though!
  485. Logger()->Log(EC_LOGLEVEL_DEBUG, "Message not yet archived to archive (%s)", arc.sStoreEntryId.tostring().c_str());
  486. hr = DoInitialArchive(ptrMessage, arc, refObjectEntry, &ptrTransaction);
  487. } else if (state.isDirty()) {
  488. // We found an archived version for the current message. However, the message is marked dirty...
  489. // There are two things we can do:
  490. // 1. Update the current archived version.
  491. // 2. Make a new archived message and link that to the old version, basically tracking history.
  492. //
  493. // This will be configurable through the track_history option.
  494. Logger()->Log(EC_LOGLEVEL_INFO, "Found archive for dirty message.");
  495. if (parseBool(m_lpConfig->GetSetting("track_history"))) {
  496. Logger()->Log(EC_LOGLEVEL_DEBUG, "Creating new archived message.");
  497. // DoTrackAndRearchive will always place the new archive in the
  498. // correct folder and place the old ones in the history folder.
  499. // However, when the message has moved, the ref entryids need to
  500. // be updated in the old messages.
  501. // DoTrackAndRearchive will do that when it detects that the passed
  502. // refObjectEntry is different than the stored reference it the most
  503. // recent archive.
  504. hr = DoTrackAndRearchive(ptrMessage, arc, *iArchivedMsg, refObjectEntry, state.isMove(), &ptrTransaction);
  505. } else if (!state.isMove()) {
  506. Logger()->Log(EC_LOGLEVEL_DEBUG, "Updating archived message.");
  507. hr = DoUpdateArchive(ptrMessage, *iArchivedMsg, refObjectEntry, &ptrTransaction);
  508. } else { // Moved, dirty and not tracking history
  509. Logger()->Log(EC_LOGLEVEL_DEBUG, "Updating and moving archived message.");
  510. // We could do a move and update here, but since we're not tracking history
  511. // we can just make a new archived message and get rid of the old one.
  512. hr = DoInitialArchive(ptrMessage, arc, refObjectEntry, &ptrTransaction);
  513. if (hr == hrSuccess)
  514. hr = ptrTransaction->Delete(*iArchivedMsg);
  515. }
  516. } else if (!state.isMove()) {
  517. Logger()->Log(EC_LOGLEVEL_WARNING, "Ignoring already archived message.");
  518. ptrTransaction.reset(new Transaction(*iArchivedMsg));
  519. } else { // Moved
  520. Logger()->Log(EC_LOGLEVEL_DEBUG, "Moving archived message.");
  521. hr = DoMoveArchive(arc, *iArchivedMsg, refObjectEntry, &ptrTransaction);
  522. }
  523. if (hr != hrSuccess)
  524. return hr;
  525. lstTransactions.push_back(ptrTransaction);
  526. }
  527. // Once we reach this point all messages have been created and/or updated. We need to
  528. // save them now. When a transaction is saved it will return a Rollback object we can
  529. // use to undo the changes when a later save fails.
  530. for (const auto &ta : lstTransactions) {
  531. RollbackPtr ptrRollback;
  532. hr = ta->SaveChanges(m_ptrSession, &ptrRollback);
  533. if (FAILED(hr)) {
  534. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to save changes into archive, Rolling back. hr=0x%08x", hr);
  535. // Rollback
  536. for (const auto &rb : lstRollbacks) {
  537. HRESULT hrTmp = rb->Execute(m_ptrSession);
  538. if (hrTmp != hrSuccess)
  539. Logger()->Log(EC_LOGLEVEL_ERROR, "Failed to rollback transaction. The archive is consistent, but possibly cluttered. hr=0x%08x", hrTmp);
  540. }
  541. return hr;
  542. }
  543. hr = hrSuccess;
  544. lstRollbacks.push_back(ptrRollback);
  545. lstNewMsgArchives.push_back(ta->GetObjectEntry());
  546. }
  547. if (state.isDirty()) {
  548. hr = ptrMsgHelper->SetClean();
  549. if (hr != hrSuccess) {
  550. Logger()->Log(EC_LOGLEVEL_WARNING, "Failed to unmark message as dirty.");
  551. return hr;
  552. }
  553. }
  554. lstNewMsgArchives.sort();
  555. lstNewMsgArchives.unique();
  556. hr = ptrMsgHelper->SetArchiveList(lstNewMsgArchives, true);
  557. if (hr != hrSuccess) {
  558. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to save list of archives for this message. (hr=0x%08x)", hr);
  559. return hr;
  560. }
  561. for (const auto &ta : lstTransactions) {
  562. HRESULT hrTmp = ta->PurgeDeletes(m_ptrSession, m_ptrTransaction);
  563. if (hrTmp != hrSuccess)
  564. Logger()->Log(EC_LOGLEVEL_ERROR, "Failed to remove old archives. (hr=0x%08x)", hrTmp);
  565. }
  566. hrTemp = ExecuteSubOperations(ptrMessageRaw, CurrentFolder(), cProps, lpProps);
  567. if (hrTemp != hrSuccess)
  568. Logger()->Log(EC_LOGLEVEL_WARNING, "Unable to execute next operation, hr=%08x. The operation is postponed, not cancelled", hrTemp);
  569. return hrSuccess;
  570. }
  571. void Copier::SetDeleteOperation(DeleterPtr ptrDeleteOp)
  572. {
  573. m_ptrDeleteOp = ptrDeleteOp;
  574. }
  575. void Copier::SetStubOperation(StubberPtr ptrStubOp)
  576. {
  577. m_ptrStubOp = ptrStubOp;
  578. }
  579. HRESULT Copier::DoInitialArchive(LPMESSAGE lpMessage, const SObjectEntry &archiveRootEntry, const SObjectEntry &refMsgEntry, TransactionPtr *lpptrTransaction)
  580. {
  581. HRESULT hr;
  582. MessagePtr ptrNewArchive;
  583. SPropValuePtr ptrEntryId;
  584. SObjectEntry objectEntry;
  585. PostSaveActionPtr ptrPSAction;
  586. TransactionPtr ptrTransaction;
  587. assert(lpMessage != NULL);
  588. assert(lpptrTransaction != NULL);
  589. hr = m_ptrHelper->CreateArchivedMessage(lpMessage, archiveRootEntry, refMsgEntry, &~ptrNewArchive, &ptrPSAction);
  590. if (hr != hrSuccess)
  591. return hr;
  592. hr = HrGetOneProp(ptrNewArchive, PR_ENTRYID, &~ptrEntryId);
  593. if (hr != hrSuccess) {
  594. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to get entry id of archive message. (hr=0x%08x", hr);
  595. return hr;
  596. }
  597. // Update the list of archive messages for this message.
  598. objectEntry.sStoreEntryId.assign(archiveRootEntry.sStoreEntryId);
  599. objectEntry.sItemEntryId.assign(ptrEntryId->Value.bin);
  600. ptrTransaction.reset(new Transaction(objectEntry));
  601. hr = ptrTransaction->Save(ptrNewArchive, true, ptrPSAction);
  602. if (hr != hrSuccess) {
  603. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to add archive message to transaction. (hr=0x%08x", hr);
  604. return hr;
  605. }
  606. *lpptrTransaction = std::move(ptrTransaction);
  607. return hrSuccess;
  608. }
  609. HRESULT Copier::DoTrackAndRearchive(LPMESSAGE lpMessage, const SObjectEntry &archiveRootEntry, const SObjectEntry &archiveMsgEntry, const SObjectEntry &refMsgEntry, bool bUpdateHistory, TransactionPtr *lpptrTransaction)
  610. {
  611. HRESULT hr;
  612. MessagePtr ptrNewArchive;
  613. SObjectEntry newArchiveEntry;
  614. MAPIPropHelperPtr ptrMsgHelper;
  615. SPropValuePtr ptrEntryId;
  616. SObjectEntry movedEntry;
  617. MessagePtr ptrMovedMessage;
  618. PostSaveActionPtr ptrPSAction;
  619. TransactionPtr ptrTransaction;
  620. assert(lpMessage != NULL);
  621. assert(lpptrTransaction != NULL);
  622. hr = m_ptrHelper->CreateArchivedMessage(lpMessage, archiveRootEntry, refMsgEntry, &~ptrNewArchive, &ptrPSAction);
  623. if (hr != hrSuccess)
  624. return hr;
  625. hr = HrGetOneProp(ptrNewArchive, PR_ENTRYID, &~ptrEntryId);
  626. if (hr != hrSuccess) {
  627. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to get entry id of archive message. (hr=0x%08x)", hr);
  628. return hr;
  629. }
  630. // Create the transaction, which is needed by CopyToHistory, now.
  631. newArchiveEntry.sStoreEntryId.assign(archiveRootEntry.sStoreEntryId);
  632. newArchiveEntry.sItemEntryId.assign(ptrEntryId->Value.bin);
  633. ptrTransaction.reset(new Transaction(newArchiveEntry));
  634. hr = MoveToHistory(archiveRootEntry, archiveMsgEntry, ptrTransaction, &movedEntry, &~ptrMovedMessage);
  635. if (hr != hrSuccess) {
  636. Logger()->Log(EC_LOGLEVEL_ERROR, "Failed to move old archive to history folder. (hr=0x%08x)", hr);
  637. return hr;
  638. }
  639. hr = MAPIPropHelper::Create(ptrNewArchive.as<MAPIPropPtr>(), &ptrMsgHelper);
  640. if (hr != hrSuccess) {
  641. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to create prop helper. (hr=0x%08x)", hr);
  642. return hr;
  643. }
  644. hr = ptrMsgHelper->ReferencePrevious(movedEntry);
  645. if (hr != hrSuccess) {
  646. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to set reference to previous archive. (hr=0x%08x)", hr);
  647. return hr;
  648. }
  649. hr = ptrTransaction->Save(ptrNewArchive, true, ptrPSAction);
  650. if (hr != hrSuccess) {
  651. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to add new archive message to transaction. (hr=0x%08x", hr);
  652. return hr;
  653. }
  654. if (bUpdateHistory) {
  655. assert(ptrMovedMessage);
  656. // Since the first history message was just moved but not yet saved, we'll set that
  657. // reference here. The other history messages do exist on the server, so those can
  658. // be updated through UpdateHistoryRefs.
  659. hr = MAPIPropHelper::Create(ptrMovedMessage.as<MAPIPropPtr>(), &ptrMsgHelper);
  660. if (hr != hrSuccess) {
  661. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to create prop helper. (hr=0x%08x)", hr);
  662. return hr;
  663. }
  664. hr = ptrMsgHelper->SetReference(refMsgEntry);
  665. if (hr != hrSuccess) {
  666. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to set reference. (hr=0x%08x)", hr);
  667. return hr;
  668. }
  669. hr = UpdateHistoryRefs(ptrMovedMessage, refMsgEntry, ptrTransaction);
  670. if (hr != hrSuccess)
  671. return hr;
  672. }
  673. *lpptrTransaction = std::move(ptrTransaction);
  674. return hrSuccess;
  675. }
  676. HRESULT Copier::DoUpdateArchive(LPMESSAGE lpMessage, const SObjectEntry &archiveMsgEntry, const SObjectEntry &refMsgEntry, TransactionPtr *lpptrTransaction)
  677. {
  678. HRESULT hr;
  679. MsgStorePtr ptrArchiveStore;
  680. ULONG ulType;
  681. MessagePtr ptrArchivedMsg;
  682. SPropTagArrayPtr ptrPropList;
  683. PostSaveActionPtr ptrPSAction;
  684. TransactionPtr ptrTransaction;
  685. assert(lpMessage != NULL);
  686. assert(lpptrTransaction != NULL);
  687. hr = m_ptrSession->OpenStore(archiveMsgEntry.sStoreEntryId, &~ptrArchiveStore);
  688. if (hr != hrSuccess) {
  689. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to open archive store. (hr=%s)", stringify(hr, true).c_str());
  690. return hr;
  691. }
  692. /**
  693. * We used to get the raw message here to ensure we're not getting the extra destubbing layer here. However, the messages
  694. * in the archive should never be stubbed, so there's no problem.
  695. * The main reason to not try to get the raw message through IID_IECMessageRaw is that archive stores don't support it.
  696. * @todo Should we verify if the item is really not stubbed?
  697. */
  698. hr = ptrArchiveStore->OpenEntry(archiveMsgEntry.sItemEntryId.size(), archiveMsgEntry.sItemEntryId, &ptrArchivedMsg.iid(), MAPI_BEST_ACCESS | fMapiDeferredErrors, &ulType, &~ptrArchivedMsg);
  699. if (hr != hrSuccess) {
  700. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to open existing archived message. (hr=%s)", stringify(hr, true).c_str());
  701. return hr;
  702. }
  703. hr = ptrArchivedMsg->GetPropList(fMapiUnicode, &~ptrPropList);
  704. if (hr != hrSuccess) {
  705. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to get property list. (hr=%s)", stringify(hr, true).c_str());
  706. return hr;
  707. }
  708. hr = ptrArchivedMsg->DeleteProps(ptrPropList, NULL);
  709. if (hr != hrSuccess) {
  710. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to delete properties. (hr=%s)", stringify(hr, true).c_str());
  711. return hr;
  712. }
  713. hr = Util::HrDeleteAttachments(ptrArchivedMsg);
  714. if (hr != hrSuccess) {
  715. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to delete attachments. (hr=%s)", stringify(hr, true).c_str());
  716. return hr;
  717. }
  718. hr = Util::HrDeleteRecipients(ptrArchivedMsg);
  719. if (hr != hrSuccess) {
  720. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to delete recipients. (hr=%s)", stringify(hr, true).c_str());
  721. return hr;
  722. }
  723. hr = m_ptrHelper->ArchiveMessage(lpMessage, &refMsgEntry, ptrArchivedMsg, &ptrPSAction);
  724. if (hr != hrSuccess)
  725. return hr;
  726. ptrTransaction.reset(new Transaction(archiveMsgEntry));
  727. hr = ptrTransaction->Save(ptrArchivedMsg, false, ptrPSAction);
  728. if (hr != hrSuccess) {
  729. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to add archive message to transaction. (hr=0x%08x", hr);
  730. return hr;
  731. }
  732. *lpptrTransaction = std::move(ptrTransaction);
  733. return hrSuccess;
  734. }
  735. HRESULT Copier::DoMoveArchive(const SObjectEntry &archiveRootEntry, const SObjectEntry &archiveMsgEntry, const SObjectEntry &refMsgEntry, TransactionPtr *lpptrTransaction)
  736. {
  737. HRESULT hr;
  738. MAPIFolderPtr ptrArchiveFolder;
  739. MsgStorePtr ptrArchiveStore;
  740. ULONG ulType;
  741. MessagePtr ptrArchive;
  742. MessagePtr ptrArchiveCopy;
  743. MAPIPropHelperPtr ptrPropHelper;
  744. SPropValuePtr ptrEntryId;
  745. SObjectEntry objectEntry;
  746. TransactionPtr ptrTransaction;
  747. assert(lpptrTransaction != NULL);
  748. hr = m_ptrHelper->GetArchiveFolder(archiveRootEntry, &~ptrArchiveFolder);
  749. if (hr != hrSuccess)
  750. return hr;
  751. hr = m_ptrSession->OpenStore(archiveMsgEntry.sStoreEntryId, &~ptrArchiveStore);
  752. if (hr != hrSuccess)
  753. return hr;
  754. hr = ptrArchiveStore->OpenEntry(archiveMsgEntry.sItemEntryId.size(), archiveMsgEntry.sItemEntryId, &ptrArchive.iid(), 0, &ulType, &~ptrArchive);
  755. if (hr != hrSuccess)
  756. return hr;
  757. hr = ptrArchiveFolder->CreateMessage(&ptrArchiveCopy.iid(), 0, &~ptrArchiveCopy);
  758. if (hr != hrSuccess)
  759. return hr;
  760. hr = ptrArchive->CopyTo(0, NULL, NULL, 0, NULL, &ptrArchiveCopy.iid(), ptrArchiveCopy, 0, NULL);
  761. if (hr != hrSuccess)
  762. return hr;
  763. hr = MAPIPropHelper::Create(ptrArchiveCopy.as<MAPIPropPtr>(), &ptrPropHelper);
  764. if (hr != hrSuccess)
  765. return hr;
  766. hr = ptrPropHelper->SetReference(refMsgEntry);
  767. if (hr != hrSuccess)
  768. return hr;
  769. hr = HrGetOneProp(ptrArchiveCopy, PR_ENTRYID, &~ptrEntryId);
  770. if (hr != hrSuccess) {
  771. Logger()->Log(EC_LOGLEVEL_FATAL, "Failed to get entry id of archive message. (hr=0x%08x)", hr);
  772. return hr;
  773. }
  774. // Create the transaction, which is needed by CopyToHistory, now.
  775. objectEntry.sStoreEntryId.assign(archiveRootEntry.sStoreEntryId);
  776. objectEntry.sItemEntryId.assign(ptrEntryId->Value.bin);
  777. ptrTransaction.reset(new Transaction(objectEntry));
  778. hr = ptrTransaction->Save(ptrArchiveCopy, true);
  779. if (hr == hrSuccess)
  780. hr = ptrTransaction->Delete(archiveMsgEntry);
  781. if (hr != hrSuccess)
  782. return hr;
  783. hr = UpdateHistoryRefs(ptrArchiveCopy, refMsgEntry, ptrTransaction);
  784. if (hr != hrSuccess)
  785. return hr;
  786. *lpptrTransaction = std::move(ptrTransaction);
  787. return hrSuccess;
  788. }
  789. HRESULT Copier::ExecuteSubOperations(LPMESSAGE lpMessage, LPMAPIFOLDER lpFolder, ULONG cProps, const LPSPropValue lpProps)
  790. {
  791. HRESULT hr = hrSuccess;
  792. assert(lpMessage != NULL);
  793. assert(lpFolder != NULL);
  794. if (lpMessage == NULL || lpFolder == NULL)
  795. return MAPI_E_INVALID_PARAMETER;
  796. if (!m_ptrDeleteOp && !m_ptrStubOp)
  797. return hrSuccess;
  798. // First see if the deleter restriction matches, in that case we run the deleter
  799. // and be done with it.
  800. if (m_ptrDeleteOp) {
  801. hr = m_ptrDeleteOp->VerifyRestriction(lpMessage);
  802. if (hr == hrSuccess) {
  803. Logger()->Log(EC_LOGLEVEL_DEBUG, "Executing delete operation.");
  804. hr = m_ptrDeleteOp->ProcessEntry(lpFolder, cProps, lpProps);
  805. if (hr != hrSuccess)
  806. Logger()->Log(EC_LOGLEVEL_WARNING, "Delete operation failed, postponing next attempt. hr=0x%08x", hr);
  807. else
  808. Logger()->Log(EC_LOGLEVEL_DEBUG, "Delete operation executed.");
  809. return hr; /* No point in trying to stub a deleted message. */
  810. } else if (hr != MAPI_E_NOT_FOUND)
  811. return hr;
  812. hr = hrSuccess;
  813. Logger()->Log(EC_LOGLEVEL_DEBUG, "Message is not eligible for deletion.");
  814. }
  815. // Now see if we need to stub the message.
  816. if (m_ptrStubOp) {
  817. hr = m_ptrStubOp->VerifyRestriction(lpMessage);
  818. if (hr == hrSuccess) {
  819. Logger()->Log(EC_LOGLEVEL_DEBUG, "Executing stub operation.");
  820. hr = m_ptrStubOp->ProcessEntry(lpMessage);
  821. if (hr != hrSuccess)
  822. Logger()->Log(EC_LOGLEVEL_WARNING, "Stub operation failed, postponing next attempt. hr=0x%08x", hr);
  823. else
  824. Logger()->Log(EC_LOGLEVEL_DEBUG, "Stub operation executed.");
  825. } else if (hr == MAPI_E_NOT_FOUND) {
  826. hr = hrSuccess;
  827. Logger()->Log(EC_LOGLEVEL_DEBUG, "Message is not eligible for stubbing.");
  828. }
  829. }
  830. return hr;
  831. }
  832. HRESULT Copier::MoveToHistory(const SObjectEntry &sourceArchiveRoot, const SObjectEntry &sourceMsgEntry, TransactionPtr ptrTransaction, SObjectEntry *lpNewEntry, LPMESSAGE *lppNewMessage)
  833. {
  834. HRESULT hr;
  835. ArchiveHelperPtr ptrArchiveHelper;
  836. MAPIFolderPtr ptrHistoryFolder;
  837. ULONG ulType;
  838. MsgStorePtr ptrArchiveStore;
  839. MessagePtr ptrArchive;
  840. MessagePtr ptrArchiveCopy;
  841. SPropValuePtr ptrEntryID;
  842. hr = ArchiveHelper::Create(m_ptrSession, sourceArchiveRoot, Logger(), &ptrArchiveHelper);
  843. if (hr != hrSuccess)
  844. return hr;
  845. hr = ptrArchiveHelper->GetHistoryFolder(&~ptrHistoryFolder);
  846. if (hr != hrSuccess)
  847. return hr;
  848. hr = m_ptrSession->OpenStore(sourceMsgEntry.sStoreEntryId, &~ptrArchiveStore);
  849. if (hr != hrSuccess)
  850. return hr;
  851. hr = ptrArchiveStore->OpenEntry(sourceMsgEntry.sItemEntryId.size(), sourceMsgEntry.sItemEntryId, &ptrArchive.iid(), 0, &ulType, &~ptrArchive);
  852. if (hr != hrSuccess)
  853. return hr;
  854. hr = ptrHistoryFolder->CreateMessage(&ptrArchiveCopy.iid(), 0, &~ptrArchiveCopy);
  855. if (hr != hrSuccess)
  856. return hr;
  857. hr = HrGetOneProp(ptrArchiveCopy, PR_ENTRYID, &~ptrEntryID);
  858. if (hr != hrSuccess)
  859. return hr;
  860. hr = ptrArchive->CopyTo(0, NULL, NULL, 0, NULL, &ptrArchiveCopy.iid(), ptrArchiveCopy, 0, NULL);
  861. if (hr != hrSuccess)
  862. return hr;
  863. hr = ptrTransaction->Save(ptrArchiveCopy, true);
  864. if (hr == hrSuccess)
  865. hr = ptrTransaction->Delete(sourceMsgEntry, true);
  866. if (hr != hrSuccess)
  867. return hr;
  868. if (lppNewMessage) {
  869. hr = ptrArchiveCopy->QueryInterface(IID_IMessage, (LPVOID*)lppNewMessage);
  870. if (hr != hrSuccess)
  871. return hr;
  872. }
  873. lpNewEntry->sStoreEntryId.assign(sourceMsgEntry.sStoreEntryId);
  874. lpNewEntry->sItemEntryId.assign(ptrEntryID->Value.bin);
  875. return hrSuccess;
  876. }
  877. HRESULT Copier::UpdateHistoryRefs(LPMESSAGE lpArchivedMsg, const SObjectEntry &refMsgEntry, TransactionPtr ptrTransaction)
  878. {
  879. HRESULT hr;
  880. MAPIPropHelperPtr ptrPropHelper;
  881. MessagePtr ptrMessage;
  882. hr = MAPIPropHelper::Create(MAPIPropPtr(lpArchivedMsg, true), &ptrPropHelper);
  883. if (hr != hrSuccess)
  884. return hr;
  885. hr = ptrPropHelper->OpenPrevious(m_ptrSession, &~ptrMessage);
  886. if (hr == MAPI_E_NOT_FOUND)
  887. return hrSuccess;
  888. else if (hr != hrSuccess)
  889. return hr;
  890. hr = MAPIPropHelper::Create(ptrMessage.as<MAPIPropPtr>(), &ptrPropHelper);
  891. if (hr != hrSuccess)
  892. return hr;
  893. hr = ptrPropHelper->SetReference(refMsgEntry);
  894. if (hr != hrSuccess)
  895. return hr;
  896. hr = ptrTransaction->Save(ptrMessage, false);
  897. if (hr != hrSuccess)
  898. return hr;
  899. return UpdateHistoryRefs(ptrMessage, refMsgEntry, ptrTransaction);
  900. }
  901. }} /* namespace */