ArchiveManageImpl.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843
  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 <string>
  18. #include <list>
  19. #include <memory>
  20. #include <new>
  21. #include <ostream>
  22. #include <utility>
  23. #include "ArchiveManage.h"
  24. #include "ArchiveManageImpl.h"
  25. #include "ArchiverSession.h"
  26. #include "helpers/StoreHelper.h"
  27. #include <kopano/charset/convert.h>
  28. #include "ECACL.h"
  29. #include <kopano/ECConfig.h>
  30. #include <kopano/userutil.h>
  31. #include "ArchiveStateUpdater.h"
  32. #include <kopano/ECRestriction.h>
  33. #include <kopano/MAPIErrors.h>
  34. using namespace KC::helpers;
  35. namespace KC {
  36. inline UserEntry ArchiveManageImpl::MakeUserEntry(const std::string &strUser) {
  37. UserEntry entry;
  38. entry.UserName = strUser;
  39. return entry;
  40. }
  41. /**
  42. * Create an ArchiveManageImpl object.
  43. *
  44. * @param[in] ptrSession
  45. * Pointer to a Session object.
  46. * @param[in] lpConfig
  47. * Pointer to an ECConfig object.
  48. * @param[in] lpszUser
  49. * The username of the user for which to create the archive manager.
  50. * @param[in] lpLogger
  51. * Pointer to an ECLogger object to which message will be logged.
  52. * @param[out] lpptrArchiveManager
  53. * Pointer to an ArchiveManagePtr that will be assigned the address of the returned object.
  54. */
  55. HRESULT ArchiveManageImpl::Create(ArchiverSessionPtr ptrSession, ECConfig *lpConfig, const TCHAR *lpszUser, ECLogger *lpLogger, ArchiveManagePtr *lpptrArchiveManage)
  56. {
  57. HRESULT hr;
  58. if (lpszUser == NULL)
  59. return MAPI_E_INVALID_PARAMETER;
  60. std::unique_ptr<ArchiveManageImpl> ptrArchiveManage(
  61. new(std::nothrow) ArchiveManageImpl(ptrSession, lpConfig, lpszUser, lpLogger));
  62. if (ptrArchiveManage == nullptr)
  63. return MAPI_E_NOT_ENOUGH_MEMORY;
  64. hr = ptrArchiveManage->Init();
  65. if (hr != hrSuccess)
  66. return hr;
  67. *lpptrArchiveManage = std::move(ptrArchiveManage);
  68. return hrSuccess;
  69. }
  70. HRESULT ArchiveManage::Create(LPMAPISESSION lpSession, ECLogger *lpLogger, const TCHAR *lpszUser, ArchiveManagePtr *lpptrManage)
  71. {
  72. HRESULT hr;
  73. ArchiverSessionPtr ptrArchiverSession;
  74. hr = ArchiverSession::Create(MAPISessionPtr(lpSession, true), NULL, lpLogger, &ptrArchiverSession);
  75. if (hr != hrSuccess)
  76. return hr;
  77. return ArchiveManageImpl::Create(ptrArchiverSession, NULL, lpszUser,
  78. lpLogger, lpptrManage);
  79. }
  80. /**
  81. * @param[in] ptrSession
  82. * Pointer to a Session object.
  83. * @param[in] lpConfig
  84. * Pointer to an ECConfig object.
  85. * @param[in] lpszUser
  86. * The username of the user for which to create the archive manager.
  87. * @param[in] lpLogger
  88. * Pointer to an ECLogger object to which message will be logged.
  89. */
  90. ArchiveManageImpl::ArchiveManageImpl(ArchiverSessionPtr ptrSession, ECConfig *lpConfig, const tstring &strUser, ECLogger *lpLogger) :
  91. m_ptrSession(ptrSession),
  92. m_lpConfig(lpConfig),
  93. m_strUser(strUser)
  94. {
  95. m_lpLogger = new(std::nothrow) ECArchiverLogger(lpLogger);
  96. if (m_lpLogger == nullptr)
  97. return;
  98. m_lpLogger->SetUser(strUser);
  99. if (lpConfig == nullptr)
  100. return;
  101. const char *loglevelstring = lpConfig->GetSetting("log_level");
  102. if (loglevelstring == nullptr)
  103. return;
  104. unsigned int loglevel = strtoul(loglevelstring, NULL, 0);
  105. m_lpLogger->SetLoglevel(loglevel);
  106. }
  107. ArchiveManageImpl::~ArchiveManageImpl()
  108. {
  109. m_lpLogger->Release();
  110. }
  111. /**
  112. * Initialize the ArchiveManager object.
  113. */
  114. HRESULT ArchiveManageImpl::Init()
  115. {
  116. HRESULT hr;
  117. hr = m_ptrSession->OpenStoreByName(m_strUser, &~m_ptrUserStore);
  118. if (hr != hrSuccess) {
  119. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to open user store '" TSTRING_PRINTF "' (hr=%s).", m_strUser.c_str(), stringify(hr, true).c_str());
  120. return hr;
  121. }
  122. return hrSuccess;
  123. }
  124. /**
  125. * Attach an archive to the store of the user for which the ArchiveManger was created.
  126. *
  127. * @param[in] lpszArchiveServer
  128. * If not NULL, this argument specifies the path of the server on which to create the archive.
  129. * @param[in] lpszArchive
  130. * The username of the non-active user that's the placeholder for the archive.
  131. * @param[in] lpszFolder
  132. * The name of the folder that will be used as the root of the archive. If ATT_USE_IPM_SUBTREE is passed
  133. * in the ulFlags argument, the lpszFolder argument is ignored. If this argument is NULL, the username of
  134. * the user is used as the root foldername.
  135. * @param[in] ulFlags
  136. * @ref flags specifying the options used for attaching the archive.
  137. *
  138. * @section flags Flags
  139. * @li \b ATT_USE_IPM_SUBTREE Use the IPM subtree of the archive store as the root of the archive.
  140. * @li \b ATT_WRITABLE Make the archive writable for the user.
  141. */
  142. eResult ArchiveManageImpl::AttachTo(const char *lpszArchiveServer, const TCHAR *lpszArchive, const TCHAR *lpszFolder, unsigned ulFlags)
  143. {
  144. return MAPIErrorToArchiveError(AttachTo(lpszArchiveServer, lpszArchive, lpszFolder, ulFlags, ExplicitAttach));
  145. }
  146. HRESULT ArchiveManageImpl::AttachTo(const char *lpszArchiveServer, const TCHAR *lpszArchive, const TCHAR *lpszFolder, unsigned ulFlags, AttachType attachType)
  147. {
  148. HRESULT hr;
  149. MsgStorePtr ptrArchiveStore;
  150. tstring strFoldername;
  151. abentryid_t sUserEntryId;
  152. ArchiverSessionPtr ptrArchiveSession(m_ptrSession);
  153. ArchiverSessionPtr ptrRemoteSession;
  154. // Resolve the requested user.
  155. hr = m_ptrSession->GetUserInfo(m_strUser, &sUserEntryId, &strFoldername, NULL);
  156. if (hr != hrSuccess) {
  157. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to resolve user information for '" TSTRING_PRINTF "' (hr=%s).", m_strUser.c_str(), stringify(hr, true).c_str());
  158. return hr;
  159. }
  160. if (ulFlags & UseIpmSubtree)
  161. strFoldername.clear(); // Empty folder name indicates the IPM subtree.
  162. else if (lpszFolder)
  163. strFoldername.assign(lpszFolder);
  164. if (lpszArchiveServer) {
  165. hr = m_ptrSession->CreateRemote(lpszArchiveServer, m_lpLogger, &ptrRemoteSession);
  166. if (hr != hrSuccess) {
  167. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to connect to archive server '%s' (hr=%s).", lpszArchiveServer, stringify(hr, true).c_str());
  168. return hr;
  169. }
  170. ptrArchiveSession = ptrRemoteSession;
  171. }
  172. // Find the requested archive.
  173. hr = ptrArchiveSession->OpenStoreByName(lpszArchive, &~ptrArchiveStore);
  174. if (hr != hrSuccess) {
  175. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to open archive store '" TSTRING_PRINTF "' (hr=%s).", lpszArchive, stringify(hr, true).c_str());
  176. return hr;
  177. }
  178. return AttachTo(ptrArchiveStore, strFoldername, lpszArchiveServer,
  179. sUserEntryId, ulFlags, attachType);
  180. }
  181. HRESULT ArchiveManageImpl::AttachTo(LPMDB lpArchiveStore, const tstring &strFoldername, const char *lpszArchiveServer, const abentryid_t &sUserEntryId, unsigned ulFlags, AttachType attachType)
  182. {
  183. HRESULT hr;
  184. ArchiveHelperPtr ptrArchiveHelper;
  185. abentryid_t sAttachedUserEntryId;
  186. StoreHelperPtr ptrStoreHelper;
  187. ObjectEntryList lstArchives;
  188. SObjectEntry objectEntry;
  189. bool bEqual = false;
  190. ArchiveType aType = UndefArchive;
  191. SPropValuePtr ptrArchiveName;
  192. SPropValuePtr ptrArchiveStoreId;
  193. // Check if we're not trying to attach a store to itself.
  194. hr = m_ptrSession->CompareStoreIds(m_ptrUserStore, lpArchiveStore, &bEqual);
  195. if (hr != hrSuccess) {
  196. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to compare user and archive store (hr=%s).", stringify(hr, true).c_str());
  197. return hr;
  198. }
  199. if (bEqual) {
  200. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "User and archive store are the same.");
  201. return MAPI_E_INVALID_PARAMETER;
  202. }
  203. hr = HrGetOneProp(lpArchiveStore, PR_ENTRYID, &~ptrArchiveStoreId);
  204. if (hr != hrSuccess) {
  205. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to get archive store entryid (hr=%s).", stringify(hr, true).c_str());
  206. return hr;
  207. }
  208. hr = StoreHelper::Create(m_ptrUserStore, &ptrStoreHelper);
  209. if (hr != hrSuccess) {
  210. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to create store helper (hr=%s).", stringify(hr, true).c_str());
  211. return hr;
  212. }
  213. hr = ptrStoreHelper->GetArchiveList(&lstArchives);
  214. if (hr != hrSuccess) {
  215. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to get archive list (hr=%s).", stringify(hr, true).c_str());
  216. return hr;
  217. }
  218. // Find ptrArchiveStoreId in lstArchives
  219. for (const auto &arc : lstArchives) {
  220. bool bEqual;
  221. if (m_ptrSession->CompareStoreIds(arc.sStoreEntryId, ptrArchiveStoreId->Value.bin, &bEqual) == hrSuccess && bEqual) {
  222. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "An archive for this '" TSTRING_PRINTF "' is already present in this store.", m_strUser.c_str());
  223. return MAPI_E_UNABLE_TO_COMPLETE;
  224. }
  225. }
  226. hr = ArchiveHelper::Create(lpArchiveStore, strFoldername, lpszArchiveServer, &ptrArchiveHelper);
  227. if (hr != hrSuccess) {
  228. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to create archive helper (hr=%s).", stringify(hr, true).c_str());
  229. return hr;
  230. }
  231. // Check if the archive is usable for the requested type
  232. hr = ptrArchiveHelper->GetArchiveType(&aType, NULL);
  233. if (hr != hrSuccess) {
  234. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to get archive type (hr=%s).", stringify(hr, true).c_str());
  235. return hr;
  236. }
  237. m_lpLogger->Log(EC_LOGLEVEL_DEBUG, "Archive Type: %d", (int)aType);
  238. if (aType == UndefArchive) {
  239. m_lpLogger->Log(EC_LOGLEVEL_NOTICE, "Preparing archive for first use");
  240. hr = ptrArchiveHelper->PrepareForFirstUse(m_lpLogger);
  241. if (hr != hrSuccess) {
  242. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to prepare archive (hr=0x%08x).", hr);
  243. return hr;
  244. }
  245. } else if (aType == SingleArchive && !strFoldername.empty()) {
  246. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Attempted to create an archive folder in an archive store that has an archive in its root.");
  247. return MAPI_E_COLLISION;
  248. } else if (aType == MultiArchive && strFoldername.empty()) {
  249. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Attempted to create an archive in the root of an archive store that has archive folders.");
  250. return MAPI_E_COLLISION;
  251. }
  252. // Check if the archive is attached yet.
  253. hr = ptrArchiveHelper->GetAttachedUser(&sAttachedUserEntryId);
  254. if (hr == MAPI_E_NOT_FOUND)
  255. hr = hrSuccess;
  256. else if ( hr == hrSuccess && (!sAttachedUserEntryId.empty() && sAttachedUserEntryId != sUserEntryId)) {
  257. tstring strUser;
  258. tstring strFullname;
  259. hr = m_ptrSession->GetUserInfo(sAttachedUserEntryId, &strUser, &strFullname);
  260. if (hr == hrSuccess)
  261. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Archive is already used by " TSTRING_PRINTF " (" TSTRING_PRINTF ").", strUser.c_str(), strFullname.c_str());
  262. else
  263. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Archive is already used (user entry: %s).", sAttachedUserEntryId.tostring().c_str());
  264. return MAPI_E_COLLISION;
  265. } else if (hr != hrSuccess) {
  266. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to get attached user for the requested archive (hr=%s).", stringify(hr, true).c_str());
  267. return hr;
  268. }
  269. // Add new archive to list of archives.
  270. hr = ptrArchiveHelper->GetArchiveEntry(true, &objectEntry);
  271. if (hr != hrSuccess) {
  272. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to get archive entry (hr=%s).", stringify(hr, true).c_str());
  273. return hr;
  274. }
  275. lstArchives.push_back(std::move(objectEntry));
  276. lstArchives.sort();
  277. lstArchives.unique();
  278. hr = ptrStoreHelper->SetArchiveList(lstArchives);
  279. if (hr != hrSuccess) {
  280. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to update archive list (hr=%s).", stringify(hr, true).c_str());
  281. return hr;
  282. }
  283. hr = ptrArchiveHelper->SetAttachedUser(sUserEntryId);
  284. if (hr != hrSuccess) {
  285. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to mark archive used (hr=%s).", stringify(hr, true).c_str());
  286. return hr;
  287. }
  288. hr = ptrArchiveHelper->SetArchiveType(strFoldername.empty() ? SingleArchive : MultiArchive, attachType);
  289. if (hr != hrSuccess) {
  290. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to set archive type to %d (hr=%s).", (int)(strFoldername.empty() ? SingleArchive : MultiArchive), stringify(hr, true).c_str());
  291. return hr;
  292. }
  293. // Update permissions
  294. if (!lpszArchiveServer) { // No need to set permissions on a remote archive.
  295. hr = ptrArchiveHelper->SetPermissions(sUserEntryId, (ulFlags & Writable) == Writable);
  296. if (hr != hrSuccess) {
  297. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to set permissions on archive (hr=%s).", stringify(hr, true).c_str());
  298. return hr;
  299. }
  300. }
  301. // Create search folder
  302. hr = ptrStoreHelper->UpdateSearchFolders();
  303. if (hr != hrSuccess) {
  304. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to set search folders (hr=%s).", stringify(hr, true).c_str());
  305. return hr;
  306. }
  307. hr = HrGetOneProp(lpArchiveStore, PR_DISPLAY_NAME, &~ptrArchiveName);
  308. if (hr != hrSuccess) {
  309. hr = hrSuccess;
  310. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Successfully attached '" TSTRING_PRINTF "' in 'Unknown' for user '" TSTRING_PRINTF "'.", strFoldername.empty() ? _T("Root") : strFoldername.c_str(), m_strUser.c_str());
  311. } else
  312. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Successfully attached '" TSTRING_PRINTF "' in '" TSTRING_PRINTF "' for user '" TSTRING_PRINTF "'.", strFoldername.empty() ? _T("Root") : strFoldername.c_str(), ptrArchiveName->Value.LPSZ, m_strUser.c_str());
  313. return hrSuccess;
  314. }
  315. /**
  316. * Detach an archive from a users store.
  317. *
  318. * @param[in] lpszArchiveServer
  319. * If not NULL, this argument specifies the path of the server on which to create the archive.
  320. * @param[in] lpszArchive
  321. * The username of the non-active user that's the placeholder for the archive.
  322. * @param[in] lpszFolder
  323. * The name of the folder that's be used as the root of the archive. If this paramater
  324. * is set to NULL and the user has only one archive in the archive store, which
  325. * is usually the case, that archive will be detached. If a user has multiple archives
  326. * in the archive store, the exact folder need to be specified.
  327. * If the archive root was placed in the IPM subtree of the archive store, this parameter
  328. * must be set to NULL.
  329. */
  330. eResult ArchiveManageImpl::DetachFrom(const char *lpszArchiveServer, const TCHAR *lpszArchive, const TCHAR *lpszFolder)
  331. {
  332. HRESULT hr;
  333. entryid_t sUserEntryId;
  334. StoreHelperPtr ptrStoreHelper;
  335. ObjectEntryList lstArchives;
  336. ObjectEntryList::iterator iArchive;
  337. MsgStorePtr ptrArchiveStore;
  338. ArchiveHelperPtr ptrArchiveHelper;
  339. SPropValuePtr ptrArchiveStoreEntryId;
  340. MAPIFolderPtr ptrArchiveFolder;
  341. SPropValuePtr ptrDisplayName;
  342. ULONG ulType = 0;
  343. ArchiverSessionPtr ptrArchiveSession(m_ptrSession);
  344. ArchiverSessionPtr ptrRemoteSession;
  345. hr = StoreHelper::Create(m_ptrUserStore, &ptrStoreHelper);
  346. if (hr != hrSuccess) {
  347. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to create store helper (hr=%s).", stringify(hr, true).c_str());
  348. return MAPIErrorToArchiveError(hr);
  349. }
  350. hr = ptrStoreHelper->GetArchiveList(&lstArchives);
  351. if (hr != hrSuccess) {
  352. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to get archive list (hr=%s).", stringify(hr, true).c_str());
  353. return MAPIErrorToArchiveError(hr);
  354. }
  355. if (lpszArchiveServer) {
  356. hr = m_ptrSession->CreateRemote(lpszArchiveServer, m_lpLogger, &ptrRemoteSession);
  357. if (hr != hrSuccess) {
  358. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to connect to archive server '%s' (hr=%s).", lpszArchiveServer, stringify(hr, true).c_str());
  359. return MAPIErrorToArchiveError(hr);
  360. }
  361. ptrArchiveSession = ptrRemoteSession;
  362. }
  363. hr = ptrArchiveSession->OpenStoreByName(lpszArchive, &~ptrArchiveStore);
  364. if (hr != hrSuccess) {
  365. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to open archive store '" TSTRING_PRINTF "' (hr=%s).", lpszArchive, stringify(hr, true).c_str());
  366. return MAPIErrorToArchiveError(hr);
  367. }
  368. hr = HrGetOneProp(ptrArchiveStore, PR_ENTRYID, &~ptrArchiveStoreEntryId);
  369. if (hr != hrSuccess) {
  370. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to get archive entryid (hr=%s).", stringify(hr, true).c_str());
  371. return MAPIErrorToArchiveError(hr);
  372. }
  373. // Find an archives on the passed store.
  374. iArchive = find_if(lstArchives.begin(), lstArchives.end(), StoreCompare(ptrArchiveStoreEntryId->Value.bin));
  375. if (iArchive == lstArchives.end()) {
  376. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "'" TSTRING_PRINTF "' has no archive on '" TSTRING_PRINTF "'", m_strUser.c_str(), lpszArchive);
  377. return MAPIErrorToArchiveError(MAPI_E_NOT_FOUND);
  378. }
  379. // If no folder name was passed and there are more archives for this user on this archive, we abort.
  380. if (lpszFolder == NULL) {
  381. ObjectEntryList::iterator iNextArchive(iArchive);
  382. ++iNextArchive;
  383. if (find_if(iNextArchive, lstArchives.end(), StoreCompare(ptrArchiveStoreEntryId->Value.bin)) != lstArchives.end()) {
  384. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "'" TSTRING_PRINTF "' has multiple archives on '" TSTRING_PRINTF "'", m_strUser.c_str(), lpszArchive);
  385. return MAPIErrorToArchiveError(MAPI_E_COLLISION);
  386. }
  387. }
  388. // If a folder name was passed, we need to find the correct folder.
  389. if (lpszFolder) {
  390. while (iArchive != lstArchives.end()) {
  391. hr = ptrArchiveStore->OpenEntry(iArchive->sItemEntryId.size(), iArchive->sItemEntryId, &ptrArchiveFolder.iid(), fMapiDeferredErrors, &ulType, &~ptrArchiveFolder);
  392. if (hr != hrSuccess) {
  393. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to open archive folder (hr=%s).", stringify(hr, true).c_str());
  394. return MAPIErrorToArchiveError(hr);
  395. }
  396. hr = HrGetOneProp(ptrArchiveFolder, PR_DISPLAY_NAME, &~ptrDisplayName);
  397. if (hr != hrSuccess) {
  398. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to get archive folder name (hr=%s).", stringify(hr, true).c_str());
  399. return MAPIErrorToArchiveError(hr);
  400. }
  401. if (_tcscmp(ptrDisplayName->Value.LPSZ, lpszFolder) == 0)
  402. break;
  403. iArchive = find_if(++iArchive, lstArchives.end(), StoreCompare(ptrArchiveStoreEntryId->Value.bin));
  404. }
  405. if (iArchive == lstArchives.end()) {
  406. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "'" TSTRING_PRINTF "' has no archive named '" TSTRING_PRINTF "' on '" TSTRING_PRINTF "'", m_strUser.c_str(), lpszFolder, lpszArchive);
  407. return MAPIErrorToArchiveError(MAPI_E_NOT_FOUND);
  408. }
  409. }
  410. assert(iArchive != lstArchives.end());
  411. lstArchives.erase(iArchive);
  412. hr = ptrStoreHelper->SetArchiveList(lstArchives);
  413. if (hr != hrSuccess) {
  414. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to update archive list (hr=%s).", stringify(hr, true).c_str());
  415. return MAPIErrorToArchiveError(hr);
  416. }
  417. // Update search folders
  418. hr = ptrStoreHelper->UpdateSearchFolders();
  419. if (hr != hrSuccess) {
  420. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to set search folders (hr=%s).", stringify(hr, true).c_str());
  421. return MAPIErrorToArchiveError(hr);
  422. }
  423. if (lpszFolder)
  424. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Successfully detached '" TSTRING_PRINTF "' in '" TSTRING_PRINTF "' from '" TSTRING_PRINTF "'.", lpszFolder, lpszArchive, m_strUser.c_str());
  425. else
  426. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Successfully detached '" TSTRING_PRINTF "' from '" TSTRING_PRINTF "'.", lpszArchive, m_strUser.c_str());
  427. return MAPIErrorToArchiveError(hrSuccess);
  428. }
  429. /**
  430. * Detach an archive from a users store based on its index
  431. *
  432. * @param[in] ulArchive
  433. * The index of the archive in the list of archives.
  434. */
  435. eResult ArchiveManageImpl::DetachFrom(unsigned int ulArchive)
  436. {
  437. HRESULT hr;
  438. StoreHelperPtr ptrStoreHelper;
  439. ObjectEntryList lstArchives;
  440. ObjectEntryList::iterator iArchive;
  441. hr = StoreHelper::Create(m_ptrUserStore, &ptrStoreHelper);
  442. if (hr != hrSuccess) {
  443. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to create store helper (hr=%s).", stringify(hr, true).c_str());
  444. return MAPIErrorToArchiveError(hr);
  445. }
  446. hr = ptrStoreHelper->GetArchiveList(&lstArchives);
  447. if (hr != hrSuccess) {
  448. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to get archive list (hr=%s).", stringify(hr, true).c_str());
  449. return MAPIErrorToArchiveError(hr);
  450. }
  451. iArchive = lstArchives.begin();
  452. for (unsigned int i = 0; i < ulArchive && iArchive != lstArchives.end(); ++i, ++iArchive);
  453. if (iArchive == lstArchives.end()) {
  454. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Archive %u does not exist.", ulArchive);
  455. return MAPIErrorToArchiveError(MAPI_E_NOT_FOUND);
  456. }
  457. lstArchives.erase(iArchive);
  458. hr = ptrStoreHelper->SetArchiveList(lstArchives);
  459. if (hr != hrSuccess) {
  460. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to update archive list (hr=%s).", stringify(hr, true).c_str());
  461. return MAPIErrorToArchiveError(hr);
  462. }
  463. // Update search folders
  464. hr = ptrStoreHelper->UpdateSearchFolders();
  465. if (hr != hrSuccess) {
  466. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to set search folders (hr=%s).", stringify(hr, true).c_str());
  467. return MAPIErrorToArchiveError(hr);
  468. }
  469. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Successfully detached archive %u from '" TSTRING_PRINTF "'.", ulArchive, m_strUser.c_str());
  470. return MAPIErrorToArchiveError(hrSuccess);
  471. }
  472. /**
  473. * List the attached archives for a user.
  474. *
  475. * @param[in] ostr
  476. * The std::ostream to which the list will be outputted.
  477. */
  478. eResult ArchiveManageImpl::ListArchives(std::ostream &ostr)
  479. {
  480. eResult er;
  481. ArchiveList lstArchives;
  482. ULONG ulIdx = 0;
  483. er = ListArchives(&lstArchives, "Root Folder");
  484. if (er != Success)
  485. return er;
  486. ostr << "User '" << convert_to<std::string>(m_strUser) << "' has " << lstArchives.size() << " attached archives:" << std::endl;
  487. for (const auto &arc : lstArchives) {
  488. ostr << "\t" << ulIdx
  489. << ": Store: " << arc.StoreName
  490. << ", Folder: " << arc.FolderName;
  491. if (arc.Rights != ARCHIVE_RIGHTS_ABSENT) {
  492. ostr << ", Rights: ";
  493. if (arc.Rights == ROLE_OWNER)
  494. ostr << "Read Write";
  495. else if (arc.Rights == ROLE_REVIEWER)
  496. ostr << "Read Only";
  497. else
  498. ostr << "Modified: " << AclRightsToString(arc.Rights);
  499. }
  500. ostr << std::endl;
  501. }
  502. return Success;
  503. }
  504. eResult ArchiveManageImpl::ListArchives(ArchiveList *lplstArchives, const char *lpszIpmSubtreeSubstitude)
  505. {
  506. HRESULT hr;
  507. StoreHelperPtr ptrStoreHelper;
  508. bool bAclCapable = true;
  509. ObjectEntryList lstArchives;
  510. MsgStorePtr ptrArchiveStore;
  511. ULONG ulType = 0;
  512. ArchiveList lstEntries;
  513. hr = StoreHelper::Create(m_ptrUserStore, &ptrStoreHelper);
  514. if (hr != hrSuccess)
  515. return MAPIErrorToArchiveError(hr);
  516. hr = m_ptrSession->GetUserInfo(m_strUser, NULL, NULL, &bAclCapable);
  517. if (hr != hrSuccess)
  518. return MAPIErrorToArchiveError(hr);
  519. hr = ptrStoreHelper->GetArchiveList(&lstArchives);
  520. if (hr != hrSuccess)
  521. return MAPIErrorToArchiveError(hr);
  522. for (const auto &arc : lstArchives) {
  523. HRESULT hrTmp = hrSuccess;
  524. ULONG cStoreProps = 0;
  525. SPropArrayPtr ptrStoreProps;
  526. ArchiveEntry entry;
  527. MAPIFolderPtr ptrArchiveFolder;
  528. SPropValuePtr ptrPropValue;
  529. ULONG ulCompareResult = FALSE;
  530. static constexpr const SizedSPropTagArray(4, sptaStoreProps) = {4, {PR_DISPLAY_NAME_A, PR_MAILBOX_OWNER_ENTRYID, PR_IPM_SUBTREE_ENTRYID, PR_STORE_RECORD_KEY}};
  531. enum {IDX_DISPLAY_NAME, IDX_MAILBOX_OWNER_ENTRYID, IDX_IPM_SUBTREE_ENTRYID, IDX_STORE_RECORD_KEY};
  532. entry.Rights = ARCHIVE_RIGHTS_ERROR;
  533. hrTmp = m_ptrSession->OpenStore(arc.sStoreEntryId, &~ptrArchiveStore);
  534. if (hrTmp != hrSuccess) {
  535. m_lpLogger->Log(EC_LOGLEVEL_ERROR, "Failed to open store (hr=%s)", stringify(hrTmp, true).c_str());
  536. entry.StoreName = "Failed id=" + arc.sStoreEntryId.tostring() + ", hr=" + stringify(hrTmp, true);
  537. lstEntries.push_back(std::move(entry));
  538. continue;
  539. }
  540. hrTmp = ptrArchiveStore->GetProps(sptaStoreProps, 0, &cStoreProps, &~ptrStoreProps);
  541. if (FAILED(hrTmp))
  542. entry.StoreName = entry.StoreOwner = "Unknown (" + stringify(hrTmp, true) + ")";
  543. else {
  544. if (ptrStoreProps[IDX_DISPLAY_NAME].ulPropTag == PR_DISPLAY_NAME_A)
  545. entry.StoreName = ptrStoreProps[IDX_DISPLAY_NAME].Value.lpszA;
  546. else
  547. entry.StoreName = "Unknown (" + stringify(ptrStoreProps[IDX_DISPLAY_NAME].Value.err, true) + ")";
  548. if (ptrStoreProps[IDX_MAILBOX_OWNER_ENTRYID].ulPropTag == PR_MAILBOX_OWNER_ENTRYID) {
  549. MAPIPropPtr ptrOwner;
  550. hrTmp = m_ptrSession->OpenMAPIProp(ptrStoreProps[IDX_MAILBOX_OWNER_ENTRYID].Value.bin.cb,
  551. reinterpret_cast<ENTRYID *>(ptrStoreProps[IDX_MAILBOX_OWNER_ENTRYID].Value.bin.lpb),
  552. &~ptrOwner);
  553. if (hrTmp == hrSuccess)
  554. hrTmp = HrGetOneProp(ptrOwner, PR_ACCOUNT_A, &~ptrPropValue);
  555. if (hrTmp == hrSuccess)
  556. entry.StoreOwner = ptrPropValue->Value.lpszA;
  557. else
  558. entry.StoreOwner = "Unknown (" + stringify(hrTmp, true) + ")";
  559. } else
  560. entry.StoreOwner = "Unknown (" + stringify(ptrStoreProps[IDX_MAILBOX_OWNER_ENTRYID].Value.err, true) + ")";
  561. if (lpszIpmSubtreeSubstitude) {
  562. if (ptrStoreProps[IDX_IPM_SUBTREE_ENTRYID].ulPropTag != PR_IPM_SUBTREE_ENTRYID)
  563. hrTmp = MAPI_E_NOT_FOUND;
  564. else
  565. hrTmp = ptrArchiveStore->CompareEntryIDs(arc.sItemEntryId.size(), arc.sItemEntryId,
  566. ptrStoreProps[IDX_IPM_SUBTREE_ENTRYID].Value.bin.cb, (LPENTRYID)ptrStoreProps[IDX_IPM_SUBTREE_ENTRYID].Value.bin.lpb,
  567. 0, &ulCompareResult);
  568. if (hrTmp != hrSuccess) {
  569. m_lpLogger->Log(EC_LOGLEVEL_ERROR, "Failed to compare entry ids (hr=%s)", stringify(hrTmp, true).c_str());
  570. ulCompareResult = FALSE; // Let's assume it's not the IPM Subtree.
  571. }
  572. }
  573. if (ptrStoreProps[IDX_STORE_RECORD_KEY].ulPropTag == PR_STORE_RECORD_KEY)
  574. entry.StoreGuid = bin2hex(ptrStoreProps[IDX_STORE_RECORD_KEY].Value.bin.cb, ptrStoreProps[IDX_STORE_RECORD_KEY].Value.bin.lpb);
  575. }
  576. hrTmp = ptrArchiveStore->OpenEntry(arc.sItemEntryId.size(), arc.sItemEntryId, &ptrArchiveFolder.iid(), fMapiDeferredErrors, &ulType, &~ptrArchiveFolder);
  577. if (hrTmp != hrSuccess) {
  578. m_lpLogger->Log(EC_LOGLEVEL_ERROR, "Failed to open folder (hr=%s)", stringify(hrTmp, true).c_str());
  579. entry.FolderName = "Failed id=" + arc.sStoreEntryId.tostring() + ", hr=" + stringify(hrTmp, true);
  580. lstEntries.push_back(std::move(entry));
  581. continue;
  582. }
  583. if (lpszIpmSubtreeSubstitude && ulCompareResult == TRUE) {
  584. assert(lpszIpmSubtreeSubstitude != NULL);
  585. entry.FolderName = lpszIpmSubtreeSubstitude;
  586. } else {
  587. hrTmp = HrGetOneProp(ptrArchiveFolder, PR_DISPLAY_NAME_A, &~ptrPropValue);
  588. if (hrTmp != hrSuccess)
  589. entry.FolderName = "Unknown (" + stringify(hrTmp, true) + ")";
  590. else
  591. entry.FolderName = ptrPropValue->Value.lpszA ;
  592. }
  593. if (bAclCapable && !arc.sStoreEntryId.isWrapped()) {
  594. hrTmp = GetRights(ptrArchiveFolder, &entry.Rights);
  595. if (hrTmp != hrSuccess)
  596. m_lpLogger->Log(EC_LOGLEVEL_ERROR, "Failed to get archive rights (hr=%s)", stringify(hrTmp, true).c_str());
  597. } else
  598. entry.Rights = ARCHIVE_RIGHTS_ABSENT;
  599. lstEntries.push_back(std::move(entry));
  600. }
  601. lplstArchives->swap(lstEntries);
  602. return MAPIErrorToArchiveError(hrSuccess);
  603. }
  604. /**
  605. * Print a list of users with an attached archive store
  606. *
  607. * @param[in] ostr
  608. * Output stream to write results to.
  609. *
  610. * @return eResult
  611. */
  612. eResult ArchiveManageImpl::ListAttachedUsers(std::ostream &ostr)
  613. {
  614. eResult er;
  615. UserList lstUsers;
  616. er = ListAttachedUsers(&lstUsers);
  617. if (er != Success)
  618. return er;
  619. if (lstUsers.empty()) {
  620. ostr << "No users have an archive attached." << std::endl;
  621. return Success;
  622. }
  623. ostr << "Users with an attached archive:" << std::endl;
  624. for (const auto &user : lstUsers)
  625. ostr << "\t" << user.UserName << std::endl;
  626. return Success;
  627. }
  628. eResult ArchiveManageImpl::ListAttachedUsers(UserList *lplstUsers)
  629. {
  630. HRESULT hr;
  631. std::list<std::string> lstUsers;
  632. UserList lstUserEntries;
  633. if (lplstUsers == NULL)
  634. return MAPIErrorToArchiveError(MAPI_E_INVALID_PARAMETER);
  635. hr = GetArchivedUserList(m_ptrSession->GetMAPISession(),
  636. m_ptrSession->GetSSLPath(), m_ptrSession->GetSSLPass(), &lstUsers);
  637. if (hr != hrSuccess)
  638. return MAPIErrorToArchiveError(hr);
  639. std::transform(lstUsers.begin(), lstUsers.end(), std::back_inserter(lstUserEntries), &MakeUserEntry);
  640. lplstUsers->swap(lstUserEntries);
  641. return MAPIErrorToArchiveError(hr);
  642. }
  643. /**
  644. * Auto attach and detach archives to user stores based on the addressbook
  645. * settings.
  646. */
  647. eResult ArchiveManageImpl::AutoAttach(unsigned int ulFlags)
  648. {
  649. HRESULT hr = hrSuccess;
  650. m_lpLogger->Log(EC_LOGLEVEL_DEBUG, "ArchiveManageImpl::AutoAttach(): function entry");
  651. ArchiveStateCollectorPtr ptrArchiveStateCollector;
  652. ArchiveStateUpdaterPtr ptrArchiveStateUpdater;
  653. if (ulFlags != ArchiveManage::Writable && ulFlags != ArchiveManage::ReadOnly && ulFlags != 0) {
  654. hr = MAPI_E_INVALID_PARAMETER;
  655. goto exit;
  656. }
  657. m_lpLogger->Log(EC_LOGLEVEL_DEBUG, "ArchiveManageImpl::AutoAttach(): about to create ArchiveStateCollector");
  658. hr = ArchiveStateCollector::Create(m_ptrSession, m_lpLogger, &ptrArchiveStateCollector);
  659. if (hr != hrSuccess)
  660. goto exit;
  661. m_lpLogger->Log(EC_LOGLEVEL_DEBUG, "ArchiveManageImpl::AutoAttach(): about to get ArchiveStateUpdater");
  662. hr = ptrArchiveStateCollector->GetArchiveStateUpdater(&ptrArchiveStateUpdater);
  663. if (hr != hrSuccess)
  664. goto exit;
  665. if (ulFlags == 0) {
  666. if (!m_lpConfig || parseBool(m_lpConfig->GetSetting("auto_attach_writable")))
  667. ulFlags = ArchiveManage::Writable;
  668. else
  669. ulFlags = ArchiveManage::ReadOnly;
  670. }
  671. m_lpLogger->Log(EC_LOGLEVEL_DEBUG, "ArchiveManageImpl::AutoAttach(): about to call ArchiveStateUpdater::Update");
  672. hr = ptrArchiveStateUpdater->Update(m_strUser, ulFlags);
  673. exit:
  674. m_lpLogger->Log(EC_LOGLEVEL_DEBUG, "ArchiveManageImpl::AutoAttach(): function exit. Result: 0x%08X (%s)", hr, GetMAPIErrorMessage(hr));
  675. return MAPIErrorToArchiveError(hr);
  676. }
  677. /**
  678. * Obtain the rights for the user for which the instance of ArhiceveManageImpl
  679. * was created on the passed folder.
  680. *
  681. * @param[in] lpFolder The folder to get the rights from
  682. * @param[out] lpulRights The rights the current user has on the folder.
  683. */
  684. HRESULT ArchiveManageImpl::GetRights(LPMAPIFOLDER lpFolder, unsigned *lpulRights)
  685. {
  686. HRESULT hr;
  687. SPropValuePtr ptrName;
  688. ExchangeModifyTablePtr ptrACLModifyTable;
  689. MAPITablePtr ptrACLTable;
  690. SPropValue sPropUser;
  691. SRowSetPtr ptrRows;
  692. static constexpr const SizedSPropTagArray(1, sptaTableProps) = {1, {PR_MEMBER_RIGHTS}};
  693. if (lpFolder == NULL || lpulRights == NULL)
  694. return MAPI_E_INVALID_PARAMETER;
  695. // In an ideal world we would use the user entryid for the restriction.
  696. // However, the ACL table is a client side table, which doesn't implement
  697. // comparing AB entryids correctly over multiple servers. Since we're
  698. // most likely dealing with multiple servers here, we'll use the users
  699. // fullname instead.
  700. hr = HrGetOneProp(m_ptrUserStore, PR_MAILBOX_OWNER_NAME, &~ptrName);
  701. if (hr != hrSuccess)
  702. return hr;
  703. hr = lpFolder->OpenProperty(PR_ACL_TABLE, &IID_IExchangeModifyTable, 0, 0, &~ptrACLModifyTable);
  704. if (hr != hrSuccess)
  705. return hr;
  706. hr = ptrACLModifyTable->GetTable(0, &~ptrACLTable);
  707. if (hr != hrSuccess)
  708. return hr;
  709. hr = ptrACLTable->SetColumns(sptaTableProps, TBL_BATCH);
  710. if (hr != hrSuccess)
  711. return hr;
  712. sPropUser.ulPropTag = PR_MEMBER_NAME;
  713. sPropUser.Value.LPSZ = ptrName->Value.LPSZ;
  714. hr = ECPropertyRestriction(RELOP_EQ, PR_MEMBER_NAME, &sPropUser, ECRestriction::Cheap)
  715. .FindRowIn(ptrACLTable, BOOKMARK_BEGINNING, 0);
  716. if (hr != hrSuccess)
  717. return hr;
  718. hr = ptrACLTable->QueryRows(1, 0, &ptrRows);
  719. if (hr != hrSuccess)
  720. return hr;
  721. if (ptrRows.empty()) {
  722. assert(false);
  723. return MAPI_E_NOT_FOUND;
  724. }
  725. *lpulRights = ptrRows[0].lpProps[0].Value.ul;
  726. return hrSuccess;
  727. }
  728. } /* namespace */