ECExportAddressbookChanges.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  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 <kopano/ECGuid.h>
  19. #include <kopano/ECInterfaceDefs.h>
  20. #include "ics.h"
  21. #include "pcutil.hpp"
  22. #include "ECABContainer.h"
  23. #include "IECImportAddressbookChanges.h"
  24. #include "ECMsgStore.h"
  25. #include "ECExportAddressbookChanges.h"
  26. #include <kopano/ECLogger.h>
  27. #include <ECSyncLog.h>
  28. #include <kopano/stringutil.h>
  29. #include <kopano/Util.h>
  30. #include <edkmdb.h>
  31. ECExportAddressbookChanges::ECExportAddressbookChanges(ECMsgStore *lpStore) :
  32. m_lpMsgStore(lpStore)
  33. {
  34. ECSyncLog::GetLogger(&m_lpLogger);
  35. }
  36. ECExportAddressbookChanges::~ECExportAddressbookChanges() {
  37. MAPIFreeBuffer(m_lpRawChanges);
  38. MAPIFreeBuffer(m_lpChanges);
  39. if(m_lpImporter)
  40. m_lpImporter->Release();
  41. if (m_lpLogger)
  42. m_lpLogger->Release();
  43. }
  44. HRESULT ECExportAddressbookChanges::QueryInterface(REFIID refiid, void **lppInterface) {
  45. REGISTER_INTERFACE2(ECUnknown, this);
  46. REGISTER_INTERFACE2(IECExportAddressbookChanges, &this->m_xECExportAddressbookChanges);
  47. REGISTER_INTERFACE2(IUnknown, &this->m_xECExportAddressbookChanges);
  48. return MAPI_E_INTERFACE_NOT_SUPPORTED;
  49. }
  50. HRESULT ECExportAddressbookChanges::Config(LPSTREAM lpStream, ULONG ulFlags, IECImportAddressbookChanges *lpCollector)
  51. {
  52. HRESULT hr;
  53. LARGE_INTEGER lint = {{ 0, 0 }};
  54. ABEID abeid;
  55. STATSTG sStatStg;
  56. ULONG ulCount = 0;
  57. ULONG ulProcessed = 0;
  58. ULONG ulRead = 0;
  59. ICSCHANGE *lpLastChange = NULL;
  60. int n = 0;
  61. // Read state from stream
  62. hr = lpStream->Stat(&sStatStg, 0);
  63. if(hr != hrSuccess)
  64. return hr;
  65. hr = lpStream->Seek(lint, STREAM_SEEK_SET, NULL);
  66. if (hr != hrSuccess)
  67. return hr;
  68. if(sStatStg.cbSize.QuadPart < 8) {
  69. m_ulChangeId = 0;
  70. m_setProcessed.clear();
  71. } else {
  72. m_setProcessed.clear();
  73. hr = lpStream->Read(&m_ulChangeId, sizeof(ULONG), &ulRead);
  74. if(hr != hrSuccess)
  75. return hr;
  76. hr = lpStream->Read(&ulCount, sizeof(ULONG), &ulRead);
  77. if(hr != hrSuccess)
  78. return hr;
  79. while(ulCount) {
  80. hr = lpStream->Read(&ulProcessed, sizeof(ULONG), &ulRead);
  81. if(hr != hrSuccess)
  82. return hr;
  83. m_setProcessed.insert(ulProcessed);
  84. --ulCount;
  85. }
  86. }
  87. // ulFlags ignored
  88. m_lpImporter = lpCollector;
  89. m_lpImporter->AddRef();
  90. memset(&abeid, 0, sizeof(ABEID));
  91. abeid.ulType = MAPI_ABCONT;
  92. memcpy(&abeid.guid, &MUIDECSAB, sizeof(GUID));
  93. abeid.ulId = 1; // 1 is the first container
  94. // The parent source key is the entryid of the AB container that we're sync'ing
  95. MAPIFreeBuffer(m_lpChanges);
  96. m_lpChanges = NULL;
  97. MAPIFreeBuffer(m_lpRawChanges);
  98. m_lpRawChanges = NULL;
  99. hr = m_lpMsgStore->lpTransport->HrGetChanges(std::string((char *)&abeid, sizeof(ABEID)), 0, m_ulChangeId, ICS_SYNC_AB, 0, NULL, &m_ulMaxChangeId, &m_ulChanges, &m_lpRawChanges);
  100. if(hr != hrSuccess)
  101. return hr;
  102. ZLOG_DEBUG(m_lpLogger, "Got %u address book changes from server.", m_ulChanges);
  103. if (m_ulChanges > 0) {
  104. /*
  105. * Sort the changes:
  106. * Users must exist before the company, but before the group they are member of
  107. * Groups must exist after the company and the users which are the members
  108. * Companies must exist after the users and groups, so create that last
  109. *
  110. * MSw: The above comment seems to make this impossible, but the way things are ordered are:
  111. * 1. Users
  112. * 2. Groups
  113. * 3. Companies
  114. *
  115. * We also want to group changes for each unique object together so we can easily
  116. * perform some filtering on them:
  117. * - Add + Change = Add
  118. * - Change + Delete = Delete
  119. * - Add + (Change +) Delete = -
  120. * - Delete + Add = Delete + Add (eg. user changes from object class from/to contact)
  121. */
  122. std::stable_sort(m_lpRawChanges, m_lpRawChanges + m_ulChanges, LeftPrecedesRight);
  123. // Now go through the changes to remove multiple changes for the same object.
  124. if ((hr = MAPIAllocateBuffer(sizeof(ICSCHANGE) * m_ulChanges, (void **)&m_lpChanges)) != hrSuccess)
  125. return hr;
  126. for (ULONG i = 0; i < m_ulChanges; ++i) {
  127. // First check if this change hasn't been processed yet
  128. if (m_setProcessed.find(m_lpRawChanges[i].ulChangeId) != m_setProcessed.end())
  129. continue;
  130. // Now check if we changed objects. if so we write the last change to the changes list
  131. if (lpLastChange && CompareABEID(lpLastChange->sSourceKey.cb, (LPENTRYID)lpLastChange->sSourceKey.lpb,
  132. m_lpRawChanges[i].sSourceKey.cb, (LPENTRYID)m_lpRawChanges[i].sSourceKey.lpb) == false) {
  133. m_lpChanges[n++] = *lpLastChange;
  134. lpLastChange = NULL;
  135. }
  136. if (!lpLastChange)
  137. lpLastChange = &m_lpRawChanges[i];
  138. else {
  139. switch (m_lpRawChanges[i].ulChangeType) {
  140. case ICS_AB_NEW:
  141. // This shouldn't happen since apparently we have another change for the same object.
  142. if (lpLastChange->ulChangeType == ICS_AB_DELETE) {
  143. // user was deleted and re-created (probably as different object class), so keep both events
  144. ZLOG_DEBUG(m_lpLogger, "Got an ICS_AB_NEW change for an object that was deleted, modifying into CHANGE. sourcekey=%s", bin2hex(m_lpRawChanges[i].sSourceKey.cb, m_lpRawChanges[i].sSourceKey.lpb).c_str());
  145. lpLastChange->ulChangeType = ICS_AB_CHANGE;
  146. } else
  147. ZLOG_DEBUG(m_lpLogger, "Got an ICS_AB_NEW change for an object we've seen before. sourcekey=%s", bin2hex(m_lpRawChanges[i].sSourceKey.cb, m_lpRawChanges[i].sSourceKey.lpb).c_str());
  148. break;
  149. case ICS_AB_CHANGE:
  150. // The only valid previous change could have been an add, since the server doesn't track
  151. // multiple changes and we can't change an object that was just deleted.
  152. // We'll ignore this in any case.
  153. if (lpLastChange->ulChangeType != ICS_AB_NEW)
  154. ZLOG_DEBUG(m_lpLogger, "Got an ICS_AB_CHANGE with something else than a ICS_AB_NEW as the previous changes. prev_change=%04x, sourcekey=%s", lpLastChange->ulChangeType, bin2hex(m_lpRawChanges[i].sSourceKey.cb, m_lpRawChanges[i].sSourceKey.lpb).c_str());
  155. ZLOG_DEBUG(m_lpLogger, "Ignoring ICS_AB_CHANGE due to previous ICS_AB_NEW. sourcekey=%s", bin2hex(m_lpRawChanges[i].sSourceKey.cb, m_lpRawChanges[i].sSourceKey.lpb).c_str());
  156. break;
  157. case ICS_AB_DELETE:
  158. if (lpLastChange->ulChangeType == ICS_AB_NEW) {
  159. ZLOG_DEBUG(m_lpLogger, "Ignoring previous ICS_AB_NEW due to current ICS_AB_DELETE. sourcekey=%s", bin2hex(m_lpRawChanges[i].sSourceKey.cb, m_lpRawChanges[i].sSourceKey.lpb).c_str());
  160. lpLastChange = NULL; // An add and a delete results in nothing.
  161. }
  162. else if (lpLastChange->ulChangeType == ICS_AB_CHANGE) {
  163. // We'll ignore the previous change and write the delete now. This way we allow another object
  164. // with the same ID to be added after this object.
  165. ZLOG_DEBUG(m_lpLogger, "Replacing previous ICS_AB_CHANGE with current ICS_AB_DELETE. sourcekey=%s", bin2hex(m_lpRawChanges[i].sSourceKey.cb, m_lpRawChanges[i].sSourceKey.lpb).c_str());
  166. m_lpChanges[n++] = m_lpRawChanges[i];
  167. lpLastChange = NULL;
  168. }
  169. break;
  170. default:
  171. ZLOG_DEBUG(m_lpLogger, "Got an unknown change. change=%04x, sourcekey=%s", m_lpRawChanges[i].ulChangeType, bin2hex(m_lpRawChanges[i].sSourceKey.cb, m_lpRawChanges[i].sSourceKey.lpb).c_str());
  172. break;
  173. }
  174. }
  175. }
  176. if (lpLastChange) {
  177. m_lpChanges[n++] = *lpLastChange;
  178. lpLastChange = NULL;
  179. }
  180. }
  181. m_ulChanges = n;
  182. ZLOG_DEBUG(m_lpLogger, "Got %u address book changes after sorting/filtering.", m_ulChanges);
  183. // Next change is first one
  184. m_ulThisChange = 0;
  185. return hr;
  186. }
  187. HRESULT ECExportAddressbookChanges::Synchronize(ULONG *lpulSteps, ULONG *lpulProgress)
  188. {
  189. HRESULT hr = hrSuccess;
  190. // Check if we're already done
  191. if (m_ulThisChange >= m_ulChanges)
  192. return hrSuccess;
  193. if (m_lpChanges[m_ulThisChange].sSourceKey.cb < sizeof(ABEID))
  194. return MAPI_E_INVALID_PARAMETER;
  195. auto eid = reinterpret_cast<const ABEID *>(m_lpChanges[m_ulThisChange].sSourceKey.lpb);
  196. ZLOG_DEBUG(m_lpLogger, "abchange type=%04x, sourcekey=%s", m_lpChanges[m_ulThisChange].ulChangeType, bin2hex(m_lpChanges[m_ulThisChange].sSourceKey.cb, m_lpChanges[m_ulThisChange].sSourceKey.lpb).c_str());
  197. switch(m_lpChanges[m_ulThisChange].ulChangeType) {
  198. case ICS_AB_CHANGE:
  199. case ICS_AB_NEW: {
  200. hr = m_lpImporter->ImportABChange(eid->ulType, m_lpChanges[m_ulThisChange].sSourceKey.cb, (LPENTRYID)m_lpChanges[m_ulThisChange].sSourceKey.lpb);
  201. break;
  202. }
  203. case ICS_AB_DELETE: {
  204. hr = m_lpImporter->ImportABDeletion(eid->ulType, m_lpChanges[m_ulThisChange].sSourceKey.cb, (LPENTRYID)m_lpChanges[m_ulThisChange].sSourceKey.lpb);
  205. break;
  206. }
  207. default:
  208. return MAPI_E_INVALID_PARAMETER;
  209. }
  210. if (hr == SYNC_E_IGNORE)
  211. hr = hrSuccess;
  212. else if (hr == MAPI_E_INVALID_TYPE) {
  213. m_lpLogger->Log(EC_LOGLEVEL_WARNING, "Ignoring invalid entry, type=%04x, sourcekey=%s", m_lpChanges[m_ulThisChange].ulChangeType, bin2hex(m_lpChanges[m_ulThisChange].sSourceKey.cb, m_lpChanges[m_ulThisChange].sSourceKey.lpb).c_str());
  214. hr = hrSuccess;
  215. }
  216. else if (hr != hrSuccess) {
  217. ZLOG_DEBUG(m_lpLogger, "failed type=%04x, hr=%s, sourcekey=%s", m_lpChanges[m_ulThisChange].ulChangeType, stringify(hr, true).c_str(), bin2hex(m_lpChanges[m_ulThisChange].sSourceKey.cb, m_lpChanges[m_ulThisChange].sSourceKey.lpb).c_str());
  218. return hr;
  219. }
  220. // Mark the change as processed
  221. m_setProcessed.insert(m_lpChanges[m_ulThisChange].ulChangeId);
  222. ++m_ulThisChange;
  223. if(lpulSteps)
  224. *lpulSteps = m_ulChanges;
  225. if(lpulProgress)
  226. *lpulProgress = m_ulThisChange;
  227. if(m_ulThisChange >= m_ulChanges)
  228. hr = hrSuccess;
  229. else
  230. hr = SYNC_W_PROGRESS;
  231. return hr;
  232. }
  233. HRESULT ECExportAddressbookChanges::UpdateState(LPSTREAM lpStream)
  234. {
  235. HRESULT hr;
  236. LARGE_INTEGER zero = {{0,0}};
  237. ULARGE_INTEGER uzero = {{0,0}};
  238. ULONG ulCount = 0;
  239. ULONG ulWritten = 0;
  240. ULONG ulProcessed = 0;
  241. if(m_ulThisChange == m_ulChanges) {
  242. // All changes have been processed, we can discard processed changes and go to the next server change ID
  243. m_setProcessed.clear();
  244. // The last change ID we received is always the highest change ID
  245. if(m_ulMaxChangeId > 0)
  246. m_ulChangeId = m_ulMaxChangeId;
  247. }
  248. hr = lpStream->Seek(zero, STREAM_SEEK_SET, NULL);
  249. if(hr != hrSuccess)
  250. return hr;
  251. hr = lpStream->SetSize(uzero);
  252. if(hr != hrSuccess)
  253. return hr;
  254. // Write the change ID
  255. hr = lpStream->Write(&m_ulChangeId, sizeof(ULONG), &ulWritten);
  256. if(hr != hrSuccess)
  257. return hr;
  258. ulCount = m_setProcessed.size();
  259. // Write the number of processed IDs to follow
  260. hr = lpStream->Write(&ulCount, sizeof(ULONG), &ulWritten);
  261. if(hr != hrSuccess)
  262. return hr;
  263. // Write the processed IDs
  264. for (const auto &pc : m_setProcessed) {
  265. ulProcessed = pc;
  266. hr = lpStream->Write(&ulProcessed, sizeof(ULONG), &ulWritten);
  267. if(hr != hrSuccess)
  268. return hr;
  269. }
  270. lpStream->Seek(zero, STREAM_SEEK_SET, NULL);
  271. // All done
  272. return hrSuccess;
  273. }
  274. /**
  275. * Compares two ICSCHANGE objects and determines which of the two should precede the other.
  276. * This function is supposed to be used as the predicate in std::stable_sort.
  277. *
  278. * The rules for determining if a certain change should precede another are as follows:
  279. * - Users take precendence over Groups and Companies.
  280. * - Groups take precedence over Companies only.
  281. * - Companies never take precedence.
  282. * - If the changes are of the same type, precedence is determined by comparing the binary
  283. * values using SortCompareABEntryID. If the compare returns less than 0, the left change
  284. * should take precedence, otherwise it shouldn't. Note that that doesn't imply that the right
  285. * change takes precedence.
  286. *
  287. * For the ICS system it is only important to group the users, groups and companies. But for the
  288. * dupliacte change filter it is necessary to group changes for a single object as well. Hence the
  289. * 4th rule.
  290. *
  291. * @param[in] left
  292. * An ICSCHANGE structure who's sSourceKey is interpreted as an ABEID stucture. It is compared to
  293. * the right parameter.
  294. * @param[in] right
  295. * An ICSCHANGE structure who's sSourceKey is interpreted as an ABEID stucture. It is compared to
  296. * the left parameter.
  297. *
  298. * @return boolean
  299. * @retval true
  300. * The ICSCHANGE specified by left must be processed before the ICSCHANGE
  301. * specified bu right.
  302. * @retval false
  303. * The ICSCHANGE specified by left must NOT be processed before the ICSCHANGE
  304. * specified by right. Note that this does not mean that the ICSCHANGE specified
  305. * by right must be processed before the ICSCHANGE specified by left.
  306. */
  307. bool ECExportAddressbookChanges::LeftPrecedesRight(const ICSCHANGE &left, const ICSCHANGE &right)
  308. {
  309. ULONG ulTypeLeft = ((ABEID*)left.sSourceKey.lpb)->ulType;
  310. assert(ulTypeLeft == MAPI_MAILUSER || ulTypeLeft == MAPI_DISTLIST || ulTypeLeft == MAPI_ABCONT);
  311. ULONG ulTypeRight = ((ABEID*)right.sSourceKey.lpb)->ulType;
  312. assert(ulTypeRight == MAPI_MAILUSER || ulTypeRight == MAPI_DISTLIST || ulTypeRight == MAPI_ABCONT);
  313. if (ulTypeLeft == ulTypeRight)
  314. return SortCompareABEID(left.sSourceKey.cb, (LPENTRYID)left.sSourceKey.lpb, right.sSourceKey.cb, (LPENTRYID)right.sSourceKey.lpb) < 0;
  315. if (ulTypeRight == MAPI_ABCONT) // Company is always bigger.
  316. return true;
  317. if (ulTypeRight == MAPI_DISTLIST && ulTypeLeft == MAPI_MAILUSER) // Group is only bigger than user.
  318. return true;
  319. return false;
  320. }
  321. DEF_ULONGMETHOD1(TRACE_MAPI, ECExportAddressbookChanges, ECExportAddressbookChanges, AddRef, (void))
  322. DEF_ULONGMETHOD1(TRACE_MAPI, ECExportAddressbookChanges, ECExportAddressbookChanges, Release, (void))
  323. DEF_HRMETHOD1(TRACE_MAPI, ECExportAddressbookChanges, ECExportAddressbookChanges, QueryInterface, (REFIID, refiid), (void **, lppInterface))
  324. DEF_HRMETHOD1(TRACE_MAPI, ECExportAddressbookChanges, ECExportAddressbookChanges, Config, (LPSTREAM, lpStream), (ULONG, ulFlags), (IECImportAddressbookChanges *, lpCollector))
  325. DEF_HRMETHOD1(TRACE_MAPI, ECExportAddressbookChanges, ECExportAddressbookChanges, Synchronize, (ULONG *, lpulSteps), (ULONG *, lpulProgress))
  326. DEF_HRMETHOD1(TRACE_MAPI, ECExportAddressbookChanges, ECExportAddressbookChanges, UpdateState, (LPSTREAM, lpStream))