ArchiveStateCollector.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  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/zcdefs.h>
  18. #include <new>
  19. #include <utility>
  20. #include <kopano/platform.h>
  21. #include <kopano/userutil.h>
  22. #include "ArchiveStateCollector.h"
  23. #include <kopano/CommonUtil.h>
  24. #include "ArchiverSession.h"
  25. #include "ArchiveStateUpdater.h"
  26. #include "ECIterators.h"
  27. #include "HrException.h"
  28. #include <kopano/ECRestriction.h>
  29. namespace KC {
  30. namespace details {
  31. /**
  32. * Subclass of DataCollector that is used to get the current state
  33. * through the MailboxTable.
  34. */
  35. class MailboxDataCollector _kc_final : public DataCollector {
  36. public:
  37. MailboxDataCollector(ArchiveStateCollector::ArchiveInfoMap &mapArchiveInfo, ECLogger *lpLogger);
  38. ~MailboxDataCollector();
  39. HRESULT GetRequiredPropTags(LPMAPIPROP lpProp, LPSPropTagArray *lppPropTagArray) const _kc_override;
  40. HRESULT CollectData(LPMAPITABLE lpStoreTable) _kc_override;
  41. private:
  42. ArchiveStateCollector::ArchiveInfoMap &m_mapArchiveInfo;
  43. ECLogger *m_lpLogger;
  44. };
  45. MailboxDataCollector::MailboxDataCollector(ArchiveStateCollector::ArchiveInfoMap &mapArchiveInfo, ECLogger *lpLogger): m_mapArchiveInfo(mapArchiveInfo), m_lpLogger(lpLogger)
  46. {
  47. m_lpLogger->AddRef();
  48. }
  49. MailboxDataCollector::~MailboxDataCollector()
  50. {
  51. m_lpLogger->Release();
  52. }
  53. HRESULT MailboxDataCollector::GetRequiredPropTags(LPMAPIPROP lpProp, LPSPropTagArray *lppPropTagArray) const
  54. {
  55. HRESULT hr = hrSuccess;
  56. SPropTagArrayPtr ptrPropTagArray;
  57. PROPMAP_START(2)
  58. PROPMAP_NAMED_ID(STORE_ENTRYIDS, PT_MV_BINARY, PSETID_Archive, "store-entryids")
  59. PROPMAP_NAMED_ID(ITEM_ENTRYIDS, PT_MV_BINARY, PSETID_Archive, "item-entryids")
  60. PROPMAP_INIT(lpProp);
  61. hr = MAPIAllocateBuffer(CbNewSPropTagArray(4), &~ptrPropTagArray);
  62. if (hr != hrSuccess)
  63. goto exitpm;
  64. ptrPropTagArray->cValues = 4;
  65. ptrPropTagArray->aulPropTag[0] = PR_ENTRYID;
  66. ptrPropTagArray->aulPropTag[1] = PR_MAILBOX_OWNER_ENTRYID;
  67. ptrPropTagArray->aulPropTag[2] = PROP_STORE_ENTRYIDS;
  68. ptrPropTagArray->aulPropTag[3] = PROP_ITEM_ENTRYIDS;
  69. *lppPropTagArray = ptrPropTagArray.release();
  70. exitpm:
  71. return hr;
  72. }
  73. HRESULT MailboxDataCollector::CollectData(LPMAPITABLE lpStoreTable)
  74. {
  75. HRESULT hr;
  76. SRowSetPtr ptrRows;
  77. enum {IDX_ENTRYID, IDX_MAILBOX_OWNER_ENTRYID, IDX_STORE_ENTRYIDS, IDX_ITEM_ENTRYIDS, IDX_MAX};
  78. while (true) {
  79. hr = lpStoreTable->QueryRows(50, 0, &ptrRows);
  80. if (hr != hrSuccess)
  81. return hr;
  82. if (ptrRows.size() == 0)
  83. break;
  84. for (SRowSetPtr::size_type i = 0; i < ptrRows.size(); ++i) {
  85. bool bComplete = true;
  86. abentryid_t userId;
  87. for (unsigned j = 0; bComplete && j < IDX_MAX; ++j) {
  88. if (PROP_TYPE(ptrRows[i].lpProps[j].ulPropTag) == PT_ERROR) {
  89. m_lpLogger->Log(EC_LOGLEVEL_WARNING, "Got incomplete row, row %u, column %u contains error 0x%08x", i, j, ptrRows[i].lpProps[j].Value.err);
  90. bComplete = false;
  91. }
  92. }
  93. if (!bComplete)
  94. continue;
  95. if (ptrRows[i].lpProps[IDX_STORE_ENTRYIDS].Value.MVbin.cValues != ptrRows[i].lpProps[IDX_ITEM_ENTRYIDS].Value.MVbin.cValues) {
  96. m_lpLogger->Log(EC_LOGLEVEL_DEBUG, "Mismatch in archive prop count, %u vs. %u", ptrRows[i].lpProps[IDX_STORE_ENTRYIDS].Value.MVbin.cValues, ptrRows[i].lpProps[IDX_ITEM_ENTRYIDS].Value.MVbin.cValues);
  97. continue;
  98. }
  99. userId.assign(ptrRows[i].lpProps[IDX_MAILBOX_OWNER_ENTRYID].Value.bin);
  100. auto res = m_mapArchiveInfo.insert(std::make_pair(userId, ArchiveStateCollector::ArchiveInfo()));
  101. if (res.second == true)
  102. m_lpLogger->Log(EC_LOGLEVEL_DEBUG, "Inserting row for user id %s", userId.tostring().c_str());
  103. else
  104. m_lpLogger->Log(EC_LOGLEVEL_DEBUG, "Updating row for user '" TSTRING_PRINTF "'", res.first->second.userName.c_str());
  105. // Assign entryid
  106. res.first->second.storeId.assign(ptrRows[i].lpProps[IDX_ENTRYID].Value.bin);
  107. // Assign archives
  108. m_lpLogger->Log(EC_LOGLEVEL_DEBUG, "Adding %u archive(s)", ptrRows[i].lpProps[IDX_STORE_ENTRYIDS].Value.MVbin.cValues);
  109. for (ULONG j = 0; j < ptrRows[i].lpProps[IDX_STORE_ENTRYIDS].Value.MVbin.cValues; ++j) {
  110. SObjectEntry objEntry;
  111. objEntry.sStoreEntryId.assign(entryid_t(ptrRows[i].lpProps[IDX_STORE_ENTRYIDS].Value.MVbin.lpbin[j]));
  112. objEntry.sItemEntryId.assign(entryid_t(ptrRows[i].lpProps[IDX_ITEM_ENTRYIDS].Value.MVbin.lpbin[j]));
  113. res.first->second.lstArchives.push_back(std::move(objEntry));
  114. }
  115. }
  116. }
  117. return hrSuccess;
  118. }
  119. }
  120. /**
  121. * Create an ArchiveStateCollector instance.
  122. * @param[in] ArchiverSessionPtr The archive session
  123. * @param[in] lpLogger The logger.
  124. * @param[out] lpptrCollector The new ArchiveStateCollector instance.
  125. */
  126. HRESULT ArchiveStateCollector::Create(const ArchiverSessionPtr &ptrSession, ECLogger *lpLogger, ArchiveStateCollectorPtr *lpptrCollector)
  127. {
  128. ArchiveStateCollectorPtr ptrCollector(
  129. new(std::nothrow) ArchiveStateCollector(ptrSession, lpLogger));
  130. if (ptrCollector == nullptr)
  131. return MAPI_E_NOT_ENOUGH_MEMORY;
  132. *lpptrCollector = std::move(ptrCollector);
  133. return hrSuccess;
  134. }
  135. /**
  136. * @param[in] ArchiverSessionPtr The archive session
  137. * @param[in] lpLogger The logger.
  138. */
  139. ArchiveStateCollector::ArchiveStateCollector(const ArchiverSessionPtr &ptrSession, ECLogger *lpLogger)
  140. : m_ptrSession(ptrSession)
  141. , m_lpLogger(new ECArchiverLogger(lpLogger))
  142. { }
  143. ArchiveStateCollector::~ArchiveStateCollector()
  144. {
  145. m_lpLogger->Release();
  146. }
  147. /**
  148. * Return an ArchiveStateUpdater instance that can update the current state
  149. * to the required state.
  150. * @param[out] lpptrUpdate The new ArchiveStateUpdater instance.
  151. */
  152. HRESULT ArchiveStateCollector::GetArchiveStateUpdater(ArchiveStateUpdaterPtr *lpptrUpdater)
  153. {
  154. HRESULT hr;
  155. details::MailboxDataCollector mdc(m_mapArchiveInfo, m_lpLogger);
  156. hr = PopulateUserList();
  157. if (hr != hrSuccess)
  158. return hr;
  159. hr = GetMailboxData(m_ptrSession->GetMAPISession(),
  160. m_ptrSession->GetSSLPath(), m_ptrSession->GetSSLPass(),
  161. false, &mdc);
  162. if (hr != hrSuccess) {
  163. m_lpLogger->Log(EC_LOGLEVEL_DEBUG, "Failed to get mailbox data. hr=0x%08x", hr);
  164. return hr;
  165. }
  166. hr = ArchiveStateUpdater::Create(m_ptrSession, m_lpLogger, m_mapArchiveInfo, lpptrUpdater);
  167. if (hr != hrSuccess)
  168. return hr;
  169. m_mapArchiveInfo.clear();
  170. return hrSuccess;
  171. }
  172. /**
  173. * Populate the user list through the GAL.
  174. * When this method completes, a list will be available of all users that
  175. * should have one or more archives attached to their primary store.
  176. */
  177. HRESULT ArchiveStateCollector::PopulateUserList()
  178. {
  179. HRESULT hr;
  180. ABContainerPtr ptrABContainer;
  181. hr = m_ptrSession->GetGAL(&~ptrABContainer);
  182. if (hr != hrSuccess)
  183. return hr;
  184. hr = PopulateFromContainer(ptrABContainer);
  185. if (hr != hrSuccess)
  186. return hr;
  187. try {
  188. for (ECABContainerIterator iter(ptrABContainer, 0); iter != ECABContainerIterator(); ++iter) {
  189. hr = PopulateFromContainer(*iter);
  190. if (hr != hrSuccess)
  191. return hr;
  192. }
  193. } catch (const HrException &he) {
  194. hr = he.hr();
  195. m_lpLogger->Log(EC_LOGLEVEL_FATAL, "Failed to iterate addressbook containers. (hr=0x%08x)", hr);
  196. return hr;;
  197. }
  198. return hrSuccess;
  199. }
  200. /**
  201. * Populate the user list through one AB container.
  202. * When this method completes, the userlist will be available for all users
  203. * from the passed container that should have one or more archives attached to
  204. * their primary store.
  205. * @param[in] lpContainer The addressbook container to process.
  206. */
  207. HRESULT ArchiveStateCollector::PopulateFromContainer(LPABCONT lpContainer)
  208. {
  209. HRESULT hr;
  210. SPropValue sPropObjType;
  211. SPropValue sPropDispType;
  212. MAPITablePtr ptrTable;
  213. SRowSetPtr ptrRows;
  214. static constexpr const SizedSPropTagArray(4, sptaUserProps) =
  215. {4, {PR_ENTRYID, PR_ACCOUNT, PR_EC_ARCHIVE_SERVERS,
  216. PR_EC_ARCHIVE_COUPLINGS}};
  217. enum {IDX_ENTRYID, IDX_ACCOUNT, IDX_EC_ARCHIVE_SERVERS, IDX_EC_ARCHIVE_COUPLINGS};
  218. sPropObjType.ulPropTag = PR_OBJECT_TYPE;
  219. sPropObjType.Value.ul = MAPI_MAILUSER;
  220. sPropDispType.ulPropTag = PR_DISPLAY_TYPE;
  221. sPropDispType.Value.ul = DT_MAILUSER;;
  222. hr = lpContainer->GetContentsTable(0, &~ptrTable);
  223. if (hr != hrSuccess)
  224. return hr;
  225. hr = ptrTable->SetColumns(sptaUserProps, TBL_BATCH);
  226. if (hr != hrSuccess)
  227. return hr;
  228. hr = ECAndRestriction(
  229. ECPropertyRestriction(RELOP_EQ, PR_OBJECT_TYPE, &sPropObjType, ECRestriction::Cheap) +
  230. ECPropertyRestriction(RELOP_EQ, PR_DISPLAY_TYPE, &sPropDispType, ECRestriction::Cheap) +
  231. ECOrRestriction(
  232. ECExistRestriction(PR_EC_ARCHIVE_SERVERS) +
  233. ECExistRestriction(PR_EC_ARCHIVE_COUPLINGS)
  234. )
  235. ).RestrictTable(ptrTable, TBL_BATCH);
  236. if (hr != hrSuccess)
  237. return hr;
  238. while (true) {
  239. hr = ptrTable->QueryRows(50, 0, &ptrRows);
  240. if (hr != hrSuccess)
  241. return hr;
  242. if (ptrRows.size() == 0)
  243. break;
  244. for (SRowSetPtr::size_type i = 0; i < ptrRows.size(); ++i) {
  245. if (ptrRows[i].lpProps[IDX_ENTRYID].ulPropTag != PR_ENTRYID) {
  246. m_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to get entryid from address list. hr=0x%08x", ptrRows[i].lpProps[IDX_ACCOUNT].Value.err);
  247. continue;
  248. }
  249. if (ptrRows[i].lpProps[IDX_ACCOUNT].ulPropTag != PR_ACCOUNT) {
  250. m_lpLogger->Log(EC_LOGLEVEL_ERROR, "Unable to get username from address list. hr=0x%08x", ptrRows[i].lpProps[IDX_ACCOUNT].Value.err);
  251. continue;
  252. }
  253. m_lpLogger->Log(EC_LOGLEVEL_DEBUG, "Inserting row for user '" TSTRING_PRINTF "'", ptrRows[i].lpProps[IDX_ACCOUNT].Value.LPSZ);
  254. auto iterator = m_mapArchiveInfo.insert(std::make_pair(abentryid_t(ptrRows[i].lpProps[IDX_ENTRYID].Value.bin), ArchiveInfo())).first;
  255. iterator->second.userName.assign(ptrRows[i].lpProps[IDX_ACCOUNT].Value.LPSZ);
  256. if (ptrRows[i].lpProps[IDX_EC_ARCHIVE_SERVERS].ulPropTag == PR_EC_ARCHIVE_SERVERS) {
  257. m_lpLogger->Log(EC_LOGLEVEL_DEBUG, "Adding %u archive server(s)", ptrRows[i].lpProps[IDX_EC_ARCHIVE_SERVERS].Value.MVSZ.cValues);
  258. for (ULONG j = 0; j < ptrRows[i].lpProps[IDX_EC_ARCHIVE_SERVERS].Value.MVSZ.cValues; ++j)
  259. iterator->second.lstServers.push_back(ptrRows[i].lpProps[IDX_EC_ARCHIVE_SERVERS].Value.MVSZ.LPPSZ[j]);
  260. }
  261. if (ptrRows[i].lpProps[IDX_EC_ARCHIVE_COUPLINGS].ulPropTag == PR_EC_ARCHIVE_COUPLINGS) {
  262. m_lpLogger->Log(EC_LOGLEVEL_DEBUG, "Adding %u archive coupling(s)", ptrRows[i].lpProps[IDX_EC_ARCHIVE_COUPLINGS].Value.MVSZ.cValues);
  263. for (ULONG j = 0; j < ptrRows[i].lpProps[IDX_EC_ARCHIVE_COUPLINGS].Value.MVSZ.cValues; ++j)
  264. iterator->second.lstCouplings.push_back(ptrRows[i].lpProps[IDX_EC_ARCHIVE_COUPLINGS].Value.MVSZ.LPPSZ[j]);
  265. }
  266. }
  267. }
  268. return hrSuccess;
  269. }
  270. } /* namespace */