ECICSHelpers.cpp 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087
  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 <utility>
  18. #include <kopano/zcdefs.h>
  19. #include <memory>
  20. #include <new>
  21. #include <kopano/platform.h>
  22. #include <memory>
  23. #include <kopano/stringutil.h>
  24. #include "ics.h"
  25. #include "ECStoreObjectTable.h"
  26. #include "ECICSHelpers.h"
  27. #include "ECSessionManager.h"
  28. #include "ECMAPI.h"
  29. #include <mapidefs.h>
  30. #include <edkmdb.h>
  31. #include <string>
  32. #include <algorithm>
  33. #include <kopano/ECLogger.h>
  34. namespace KC {
  35. extern ECLogger* g_lpLogger;
  36. extern ECSessionManager* g_lpSessionManager;
  37. /**
  38. * IDbQueryCreator: Interface to the database query creators
  39. **/
  40. class IDbQueryCreator {
  41. public:
  42. virtual ~IDbQueryCreator(void) _kc_impdtor;
  43. virtual std::string CreateQuery() = 0;
  44. };
  45. /**
  46. * CommonQueryCreator: Abstract implementation of IDBQueryCreator that handles the
  47. * common part of all queries.
  48. **/
  49. class CommonQueryCreator : public IDbQueryCreator {
  50. public:
  51. CommonQueryCreator(unsigned int ulFlags);
  52. // IDbQueryCreator
  53. std::string CreateQuery(void) _kc_override;
  54. private:
  55. virtual std::string CreateBaseQuery() = 0;
  56. virtual std::string CreateOrderQuery() = 0;
  57. unsigned int m_ulFlags;
  58. };
  59. CommonQueryCreator::CommonQueryCreator(unsigned int ulFlags)
  60. : m_ulFlags(ulFlags)
  61. { }
  62. std::string CommonQueryCreator::CreateQuery()
  63. {
  64. std::string strQuery = CreateBaseQuery();
  65. if(!strQuery.empty()) {
  66. if ((m_ulFlags & SYNC_ASSOCIATED) == 0)
  67. strQuery += " AND (ISNULL(hierarchy.flags) OR hierarchy.flags & " + stringify(MSGFLAG_ASSOCIATED) + " = 0) ";
  68. if ((m_ulFlags & SYNC_NORMAL) == 0)
  69. strQuery += " AND (ISNULL(hierarchy.flags) OR hierarchy.flags & " + stringify(MSGFLAG_ASSOCIATED) + " = " + stringify(MSGFLAG_ASSOCIATED) + ") ";
  70. strQuery += CreateOrderQuery();
  71. }
  72. return strQuery;
  73. }
  74. /**
  75. * IncrementalQueryCreator: Creates an incremental query. In other words only messages
  76. * that are new or have changed since the last check will be
  77. * returned (deleted is a change is this context).
  78. **/
  79. class IncrementalQueryCreator _kc_final : public CommonQueryCreator {
  80. public:
  81. IncrementalQueryCreator(ECDatabase *lpDatabase, unsigned int ulSyncId, unsigned int ulChangeId, const SOURCEKEY &sFolderSourceKey, unsigned int ulFlags);
  82. private:
  83. std::string CreateBaseQuery(void) _kc_override;
  84. std::string CreateOrderQuery(void) _kc_override;
  85. ECDatabase *m_lpDatabase;
  86. unsigned int m_ulSyncId;
  87. unsigned int m_ulChangeId;
  88. const SOURCEKEY &m_sFolderSourceKey;
  89. unsigned int m_ulFlags;
  90. };
  91. IncrementalQueryCreator::IncrementalQueryCreator(ECDatabase *lpDatabase, unsigned int ulSyncId, unsigned int ulChangeId, const SOURCEKEY &sFolderSourceKey, unsigned int ulFlags)
  92. : CommonQueryCreator(ulFlags)
  93. , m_lpDatabase(lpDatabase)
  94. , m_ulSyncId(ulSyncId)
  95. , m_ulChangeId(ulChangeId)
  96. , m_sFolderSourceKey(sFolderSourceKey)
  97. , m_ulFlags(ulFlags)
  98. { }
  99. std::string IncrementalQueryCreator::CreateBaseQuery()
  100. {
  101. std::string strQuery;
  102. strQuery = "SELECT changes.id, changes.sourcekey, changes.parentsourcekey, changes.change_type, changes.flags, NULL, changes.sourcesync "
  103. "FROM changes ";
  104. if ((m_ulFlags & (SYNC_ASSOCIATED | SYNC_NORMAL)) != (SYNC_ASSOCIATED | SYNC_NORMAL))
  105. strQuery += "LEFT JOIN indexedproperties ON indexedproperties.val_binary = changes.sourcekey AND indexedproperties.tag = " + stringify(PROP_ID(PR_SOURCE_KEY)) + " " +
  106. "LEFT JOIN hierarchy ON hierarchy.id = indexedproperties.hierarchyid ";
  107. strQuery += "WHERE changes.id > " + stringify(m_ulChangeId) + /* Get changes from change ID N onwards */
  108. " AND changes.change_type & " + stringify(ICS_MESSAGE) + /* And change type is message */
  109. " AND changes.sourcesync != " + stringify(m_ulSyncId); /* And we didn't generate this change ourselves */
  110. if(!m_sFolderSourceKey.empty())
  111. strQuery += " AND changes.parentsourcekey = " + m_lpDatabase->EscapeBinary(m_sFolderSourceKey, m_sFolderSourceKey.size()); /* Where change took place in Folder X */
  112. if (m_ulFlags & SYNC_NO_DELETIONS)
  113. strQuery += " AND changes.change_type & " + stringify(ICS_ACTION_MASK) + " != " + stringify(ICS_SOFT_DELETE) +
  114. " AND changes.change_type & " + stringify(ICS_ACTION_MASK) + " != " + stringify(ICS_HARD_DELETE);
  115. else if (m_ulFlags & SYNC_NO_SOFT_DELETIONS)
  116. strQuery += " AND changes.change_type & " + stringify(ICS_ACTION_MASK) + " != " + stringify(ICS_SOFT_DELETE);
  117. if ((m_ulFlags & SYNC_READ_STATE) == 0)
  118. strQuery += " AND changes.change_type & " + stringify(ICS_ACTION_MASK) + " != " + stringify(ICS_FLAG);
  119. return strQuery;
  120. }
  121. std::string IncrementalQueryCreator::CreateOrderQuery()
  122. {
  123. return " ORDER BY changes.id";
  124. }
  125. /**
  126. * FullQueryCreator: Create a query that will return all messages for a sync id. The
  127. * messages need to be processed afterwards to see what needs to be
  128. * send to the client.
  129. **/
  130. class FullQueryCreator _kc_final : public CommonQueryCreator {
  131. public:
  132. FullQueryCreator(ECDatabase *lpDatabase, const SOURCEKEY &sFolderSourceKey, unsigned int ulFlags, unsigned int ulFilteredSourceSync = 0);
  133. private:
  134. std::string CreateBaseQuery(void) _kc_override;
  135. std::string CreateOrderQuery(void) _kc_override;
  136. ECDatabase *m_lpDatabase;
  137. const SOURCEKEY &m_sFolderSourceKey;
  138. unsigned int m_ulFilteredSourceSync;
  139. };
  140. FullQueryCreator::FullQueryCreator(ECDatabase *lpDatabase, const SOURCEKEY &sFolderSourceKey, unsigned int ulFlags, unsigned int ulFilteredSourceSync)
  141. : CommonQueryCreator(ulFlags)
  142. , m_lpDatabase(lpDatabase)
  143. , m_sFolderSourceKey(sFolderSourceKey)
  144. , m_ulFilteredSourceSync(ulFilteredSourceSync)
  145. { }
  146. std::string FullQueryCreator::CreateBaseQuery()
  147. {
  148. std::string strQuery;
  149. assert(!m_sFolderSourceKey.empty());
  150. strQuery = "SELECT changes.id as id, sourcekey.val_binary as sourcekey, parentsourcekey.val_binary, " + stringify(ICS_MESSAGE_NEW) + ", NULL, hierarchy.flags, changes.sourcesync "
  151. "FROM hierarchy "
  152. "JOIN indexedproperties as sourcekey ON sourcekey.hierarchyid = hierarchy.id AND sourcekey.tag=" + stringify(PROP_ID(PR_SOURCE_KEY)) + " "
  153. "JOIN indexedproperties as parentsourcekey ON parentsourcekey.hierarchyid = hierarchy.parent AND parentsourcekey.tag=" + stringify(PROP_ID(PR_SOURCE_KEY)) +
  154. " LEFT JOIN changes on changes.sourcekey=sourcekey.val_binary AND changes.parentsourcekey=parentsourcekey.val_binary AND changes.change_type=" + stringify(ICS_MESSAGE_NEW) + " ";
  155. strQuery += "WHERE parentsourcekey.val_binary = " + m_lpDatabase->EscapeBinary(m_sFolderSourceKey, m_sFolderSourceKey.size()) +
  156. " AND hierarchy.type=" + stringify(MAPI_MESSAGE) + " AND hierarchy.flags & 1024 = 0";
  157. if (m_ulFilteredSourceSync)
  158. strQuery += " AND (changes.sourcesync is NULL OR changes.sourcesync!=" + stringify(m_ulFilteredSourceSync) + ")";
  159. return strQuery;
  160. }
  161. std::string FullQueryCreator::CreateOrderQuery()
  162. {
  163. return " ORDER BY hierarchy.id DESC";
  164. }
  165. /**
  166. * NullQueryCreator: Returns no query at all. This is only used for SYNC_CATCHUP syncs that
  167. * do not have a restriction set. (When a restriction is set, we still need to generate the message set
  168. * so we cannot optimize anything out then).
  169. **/
  170. class NullQueryCreator _kc_final : public CommonQueryCreator {
  171. public:
  172. NullQueryCreator();
  173. private:
  174. std::string CreateBaseQuery(void) _kc_override;
  175. std::string CreateOrderQuery(void) _kc_override;
  176. };
  177. NullQueryCreator::NullQueryCreator() : CommonQueryCreator(SYNC_CATCHUP)
  178. { }
  179. std::string NullQueryCreator::CreateBaseQuery()
  180. {
  181. return std::string();
  182. }
  183. std::string NullQueryCreator::CreateOrderQuery()
  184. {
  185. return std::string();
  186. }
  187. /**
  188. * IMessageProcessor: Interface to the message processors.
  189. **/
  190. class IMessageProcessor {
  191. public:
  192. virtual ~IMessageProcessor(void) _kc_impdtor;
  193. virtual ECRESULT ProcessAccepted(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType, unsigned int *lpulFlags) = 0;
  194. virtual ECRESULT ProcessRejected(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType) = 0;
  195. virtual ECRESULT GetResidualMessages(LPMESSAGESET lpsetResiduals) = 0;
  196. virtual unsigned int GetMaxChangeId() const = 0;
  197. };
  198. /**
  199. * NonLegacyIncrementalProcessor: Processes accepted and rejected messages without the burden of tracking
  200. * legacy or checking for presence of messages.
  201. * This processor expects to be used in conjunction with the IncrementalQueryCreator,
  202. * which implies that all changes are genuin changes and no messages will be
  203. * rejected through a restriction.
  204. **/
  205. class NonLegacyIncrementalProcessor _kc_final : public IMessageProcessor {
  206. public:
  207. NonLegacyIncrementalProcessor(unsigned int ulMaxChangeId);
  208. ECRESULT ProcessAccepted(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType, unsigned int *lpulFlags) _kc_override;
  209. ECRESULT ProcessRejected(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType) _kc_override;
  210. ECRESULT GetResidualMessages(LPMESSAGESET lpsetResiduals) _kc_override
  211. {
  212. /* No legacy, no residuals. */
  213. return erSuccess;
  214. }
  215. unsigned int GetMaxChangeId(void) const _kc_override { return m_ulMaxChangeId; }
  216. private:
  217. unsigned int m_ulMaxChangeId;
  218. };
  219. NonLegacyIncrementalProcessor::NonLegacyIncrementalProcessor(unsigned int ulMaxChangeId)
  220. : m_ulMaxChangeId(ulMaxChangeId)
  221. { }
  222. ECRESULT NonLegacyIncrementalProcessor::ProcessAccepted(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType, unsigned int *lpulFlags)
  223. {
  224. // Since all changes are truly new changes, we'll just set the changetype to whatever we receive
  225. assert(lpulChangeType != NULL);
  226. assert(lpDBRow != NULL && lpDBRow[icsChangeType] != NULL);
  227. assert(lpDBRow != NULL && lpDBRow[icsID] != NULL);
  228. *lpulChangeType = atoui(lpDBRow[icsChangeType]);
  229. *lpulFlags = lpDBRow[icsFlags] ? atoui(lpDBRow[icsFlags]) : 0;
  230. ec_log(EC_LOGLEVEL_ICS, "NonLegacyIncrementalAccepted: sourcekey=%s, changetype=%d", bin2hex(SOURCEKEY(lpDBLen[icsSourceKey], lpDBRow[icsSourceKey])).c_str(), *lpulChangeType);
  231. return erSuccess;
  232. }
  233. ECRESULT NonLegacyIncrementalProcessor::ProcessRejected(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType)
  234. {
  235. // Since no restriction can be applied when using this processor, we'll never get a reject.
  236. // We'll set the changetype to 0 anyway.
  237. assert(false);
  238. *lpulChangeType = 0;
  239. ec_log(EC_LOGLEVEL_ICS, "NonLegacyIncrementalRejected: sourcekey=%s, changetype=0", bin2hex(SOURCEKEY(lpDBLen[icsSourceKey], lpDBRow[icsSourceKey])).c_str());
  240. return erSuccess;
  241. }
  242. /**
  243. * NonLegacyFullProcessor: Processes accepted and rejected messages without the burden of tracking
  244. * legacy, but allowing messages to be processed that were synced to the
  245. * client previously. Since we don't have legacy, we assume all messages
  246. * up to the current changeId are on the client.
  247. **/
  248. class NonLegacyFullProcessor _kc_final : public IMessageProcessor {
  249. public:
  250. NonLegacyFullProcessor(unsigned int ulChangeId, unsigned int ulSyncId);
  251. ECRESULT ProcessAccepted(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType, unsigned int *lpulFlags) _kc_override;
  252. ECRESULT ProcessRejected(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType) _kc_override;
  253. ECRESULT GetResidualMessages(LPMESSAGESET lpsetResiduals) _kc_override
  254. {
  255. /* No legacy, no residuals. */
  256. return erSuccess;
  257. }
  258. unsigned int GetMaxChangeId(void) const _kc_override { return m_ulMaxChangeId; }
  259. private:
  260. unsigned int m_ulChangeId;
  261. unsigned int m_ulSyncId;
  262. unsigned int m_ulMaxChangeId;
  263. };
  264. NonLegacyFullProcessor::NonLegacyFullProcessor(unsigned int ulChangeId, unsigned int ulSyncId)
  265. : m_ulChangeId(ulChangeId)
  266. , m_ulSyncId(ulSyncId)
  267. , m_ulMaxChangeId(ulChangeId)
  268. { }
  269. ECRESULT NonLegacyFullProcessor::ProcessAccepted(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType, unsigned int *lpulFlags)
  270. {
  271. // This processor will always be used with the FullQueryGenerator, which means that the provided
  272. // changetype is always ICS_MESSAGE_NEW. However, we do have the message flags so we can see if
  273. // a message is deleted.
  274. assert(lpulChangeType != NULL);
  275. assert(lpDBRow != NULL && lpDBRow[icsChangeType] != NULL && lpDBRow[icsMsgFlags] != NULL);
  276. assert(atoui(lpDBRow[icsChangeType]) == ICS_MESSAGE_NEW);
  277. unsigned int ulChange = (lpDBRow[icsID] ? atoui(lpDBRow[icsID]) : 0);
  278. if (atoui(lpDBRow[icsMsgFlags]) & MSGFLAG_DELETED) {
  279. if (ulChange <= m_ulChangeId) // Only delete if present remotely.
  280. *lpulChangeType = ICS_HARD_DELETE;
  281. } else {
  282. unsigned int ulSourceSync = (lpDBRow[icsSourceSync] ? atoui(lpDBRow[icsSourceSync]) : 0);
  283. // Only add if not present remotely and not created by the current client
  284. if (ulChange > m_ulChangeId && (ulSourceSync == 0 || ulSourceSync != m_ulSyncId))
  285. *lpulChangeType = ICS_MESSAGE_NEW;
  286. }
  287. *lpulFlags = 0; // Flags are only useful for ICS_FLAG
  288. if (ulChange > m_ulMaxChangeId)
  289. m_ulMaxChangeId = ulChange;
  290. ec_log(EC_LOGLEVEL_ICS, "NonLegacyFullAccepted: sourcekey=%s, changetype=%d", bin2hex(SOURCEKEY(lpDBLen[icsSourceKey], lpDBRow[icsSourceKey])).c_str(), *lpulChangeType);
  291. return erSuccess;
  292. }
  293. ECRESULT NonLegacyFullProcessor::ProcessRejected(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType)
  294. {
  295. // We assume the client has all messages, so we need to send a delete for any non-matching message.
  296. assert(lpulChangeType != NULL);
  297. unsigned int ulChange = (lpDBRow[icsID] ? atoui(lpDBRow[icsID]) : 0);
  298. if (ulChange <= m_ulChangeId)
  299. *lpulChangeType = ICS_HARD_DELETE;
  300. if (ulChange > m_ulMaxChangeId)
  301. m_ulMaxChangeId = ulChange;
  302. ec_log(EC_LOGLEVEL_ICS, "NonLegacyFullRejected: sourcekey=%s, changetype=%d", bin2hex(SOURCEKEY(lpDBLen[icsSourceKey], lpDBRow[icsSourceKey])).c_str(), *lpulChangeType);
  303. return erSuccess;
  304. }
  305. /**
  306. * LegacyProcessor: Processes accepted and rejected messages while keeping track of legacy messages.
  307. **/
  308. class LegacyProcessor _kc_final : public IMessageProcessor {
  309. public:
  310. LegacyProcessor(unsigned int ulChangeId, unsigned int ulSyncId, const MESSAGESET &setMessages, unsigned int ulMaxFolderChange);
  311. ECRESULT ProcessAccepted(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType, unsigned int *lpulFlags) _kc_override;
  312. ECRESULT ProcessRejected(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType) _kc_override;
  313. ECRESULT GetResidualMessages(LPMESSAGESET lpsetResiduals) _kc_override;
  314. unsigned int GetMaxChangeId(void) const _kc_override { return m_ulMaxChangeId; }
  315. private:
  316. unsigned int m_ulSyncId;
  317. MESSAGESET m_setMessages;
  318. unsigned int m_ulMaxFolderChange;
  319. unsigned int m_ulMaxChangeId;
  320. };
  321. LegacyProcessor::LegacyProcessor(unsigned int ulChangeId, unsigned int ulSyncId, const MESSAGESET &setMessages, unsigned int ulMaxFolderChange)
  322. : m_ulSyncId(ulSyncId)
  323. , m_setMessages(setMessages)
  324. , m_ulMaxFolderChange(ulMaxFolderChange)
  325. , m_ulMaxChangeId(ulChangeId)
  326. {
  327. /**
  328. * We'll never get an empty set when a restriction was used in the previous run. However it is
  329. * possible that the previous run returned an empty set. In that case setMessages contains exactly
  330. * one entry with the sourcekey set to 0x00. If that's the case we'll just empty the set and
  331. * continue as usual.
  332. **/
  333. if (m_setMessages.size() == 1 && m_setMessages.find(SOURCEKEY(1, "\x00")) != m_setMessages.end())
  334. m_setMessages.clear();
  335. }
  336. ECRESULT LegacyProcessor::ProcessAccepted(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType, unsigned int *lpulFlags)
  337. {
  338. unsigned int ulMsgFlags = 0;
  339. // When we get here we're accepting a message that has matched the restriction (or if there was no
  340. // restriction). However since we have legacy, this messages might be present already, in which
  341. // case we need to do nothing unless its deleted or changed since the last check.
  342. assert(lpulChangeType != NULL);
  343. assert(lpDBRow != NULL && lpDBRow[icsSourceKey] != NULL && lpDBRow[icsChangeType] && lpDBRow[icsMsgFlags] != NULL);
  344. assert(atoui(lpDBRow[icsChangeType]) == ICS_MESSAGE_NEW);
  345. *lpulFlags = 0;
  346. ulMsgFlags = atoui(lpDBRow[icsMsgFlags]);
  347. auto iterMessage = m_setMessages.find(SOURCEKEY(lpDBLen[icsSourceKey], lpDBRow[icsSourceKey]));
  348. if (iterMessage == m_setMessages.cend()) {
  349. // The message is not synced yet!
  350. unsigned int ulSourceSync = (lpDBRow[icsSourceSync] ? atoui(lpDBRow[icsSourceSync]) : 0);
  351. if (ulMsgFlags & MSGFLAG_DELETED || (ulSourceSync != 0 && ulSourceSync == m_ulSyncId)) // Deleted or created by current client
  352. *lpulChangeType = 0; // Ignore
  353. else
  354. *lpulChangeType = ICS_MESSAGE_NEW;
  355. ec_log(EC_LOGLEVEL_ICS, "LegacyAccepted: not synced, sourcekey=%s, changetype=%d", bin2hex(SOURCEKEY(lpDBLen[icsSourceKey], lpDBRow[icsSourceKey])).c_str(), *lpulChangeType);
  356. } else {
  357. // The message is synced!
  358. if (ulMsgFlags & MSGFLAG_DELETED) // Deleted
  359. *lpulChangeType = ICS_HARD_DELETE;
  360. else if (iterMessage->second.ulChangeTypes) { // Modified
  361. if(iterMessage->second.ulChangeTypes & (ICS_CHANGE_FLAG_NEW | ICS_CHANGE_FLAG_CHANGE))
  362. *lpulChangeType = ICS_MESSAGE_CHANGE;
  363. else if(iterMessage->second.ulChangeTypes & ICS_CHANGE_FLAG_FLAG) {
  364. *lpulChangeType = ICS_MESSAGE_FLAG;
  365. *lpulFlags = iterMessage->second.ulFlags;
  366. }
  367. }
  368. else
  369. *lpulChangeType = 0; // Ignore
  370. ec_log(EC_LOGLEVEL_ICS, "LegacyAccepted: synced, sourcekey=%s , changetype=%d", bin2hex(SOURCEKEY(lpDBLen[icsSourceKey], lpDBRow[icsSourceKey])).c_str(), *lpulChangeType);
  371. m_setMessages.erase(iterMessage);
  372. }
  373. if (*lpulChangeType != 0)
  374. m_ulMaxChangeId = m_ulMaxFolderChange;
  375. return erSuccess;
  376. }
  377. ECRESULT LegacyProcessor::ProcessRejected(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType)
  378. {
  379. // When we get here we're rejecting a message that has not-matched the restriction.
  380. // However since we have legacy, this messages might not be present anyway, in which
  381. // case we need to do nothing.
  382. assert(lpulChangeType != NULL);
  383. assert(lpDBRow != NULL && lpDBRow[icsSourceKey] != NULL && lpDBRow[icsChangeType] && lpDBRow[icsMsgFlags] != NULL);
  384. assert(atoui(lpDBRow[icsChangeType]) == ICS_MESSAGE_NEW);
  385. auto iterMessage = m_setMessages.find(SOURCEKEY(lpDBLen[icsSourceKey], lpDBRow[icsSourceKey]));
  386. if (iterMessage == m_setMessages.cend()) {
  387. // The message is not synced yet!
  388. *lpulChangeType = 0; // Ignore
  389. ec_log(EC_LOGLEVEL_ICS, "LegacyRejected: not synced, sourcekey=%s, changetype=%d", bin2hex(SOURCEKEY(lpDBLen[icsSourceKey], lpDBRow[icsSourceKey])).c_str(), *lpulChangeType);
  390. } else {
  391. // The message is synced!
  392. *lpulChangeType = ICS_HARD_DELETE;
  393. m_setMessages.erase(iterMessage);
  394. ec_log(EC_LOGLEVEL_ICS, "LegacyRejected: synced, sourcekey=%s, changetype=%d", bin2hex(SOURCEKEY(lpDBLen[icsSourceKey], lpDBRow[icsSourceKey])).c_str(), *lpulChangeType);
  395. }
  396. if (*lpulChangeType != 0)
  397. m_ulMaxChangeId = m_ulMaxFolderChange;
  398. return erSuccess;
  399. }
  400. ECRESULT LegacyProcessor::GetResidualMessages(LPMESSAGESET lpsetResiduals)
  401. {
  402. assert(lpsetResiduals != NULL);
  403. std::copy(m_setMessages.begin(), m_setMessages.end(), std::inserter(*lpsetResiduals, lpsetResiduals->begin()));
  404. return erSuccess;
  405. }
  406. /**
  407. * FirstSyncProcessor: Processes accepted and rejected messages for initial syncs. And because
  408. * it is the first sync we assume there are no messages on the device yet.
  409. **/
  410. class FirstSyncProcessor _kc_final : public IMessageProcessor {
  411. public:
  412. FirstSyncProcessor(unsigned int ulMaxFolderChange);
  413. ECRESULT ProcessAccepted(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType, unsigned int *lpulFlags) _kc_override;
  414. ECRESULT ProcessRejected(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType) _kc_override;
  415. ECRESULT GetResidualMessages(LPMESSAGESET lpsetResiduals) _kc_override
  416. {
  417. /* No legacy, no residuals. */
  418. return erSuccess;
  419. }
  420. unsigned int GetMaxChangeId(void) const _kc_override { return m_ulMaxFolderChange; }
  421. private:
  422. unsigned int m_ulMaxFolderChange;
  423. };
  424. FirstSyncProcessor::FirstSyncProcessor(unsigned int ulMaxFolderChange)
  425. : m_ulMaxFolderChange(ulMaxFolderChange)
  426. { }
  427. ECRESULT FirstSyncProcessor::ProcessAccepted(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType, unsigned int *lpulFlags)
  428. {
  429. // This processor will always be used with the FullQueryGenerator, which means that the provided
  430. // changetype is always ICS_MESSAGE_NEW. However, we do have the message flags so we can see if
  431. // a message is deleted.
  432. assert(lpulChangeType != NULL);
  433. assert(lpDBRow != NULL && lpDBRow[icsChangeType] != NULL && lpDBRow[icsMsgFlags] != NULL);
  434. assert(atoui(lpDBRow[icsChangeType]) == ICS_MESSAGE_NEW);
  435. *lpulFlags = 0; // Only useful for ICS_FLAG type changes
  436. if (atoui(lpDBRow[icsMsgFlags]) & MSGFLAG_DELETED)
  437. *lpulChangeType = 0; // Ignore
  438. else
  439. *lpulChangeType = ICS_MESSAGE_NEW;
  440. ec_log(EC_LOGLEVEL_ICS, "FirstSyncAccepted: sourcekey=%s, changetype=%d", bin2hex(SOURCEKEY(lpDBLen[icsSourceKey], lpDBRow[icsSourceKey])).c_str(), *lpulChangeType);
  441. return erSuccess;
  442. }
  443. ECRESULT FirstSyncProcessor::ProcessRejected(DB_ROW lpDBRow, DB_LENGTHS lpDBLen, unsigned int *lpulChangeType)
  444. {
  445. assert(lpulChangeType != NULL);
  446. *lpulChangeType = 0; // Ignore
  447. ec_log(EC_LOGLEVEL_ICS, "FirstSyncRejected: sourcekey=%s, changetype=0", bin2hex(SOURCEKEY(lpDBLen[icsSourceKey], lpDBRow[icsSourceKey])).c_str());
  448. return erSuccess;
  449. }
  450. /**
  451. * ECGetContentChangesHelper definitions
  452. **/
  453. ECRESULT ECGetContentChangesHelper::Create(struct soap *soap, ECSession *lpSession, ECDatabase *lpDatabase, const SOURCEKEY &sFolderSourceKey, unsigned int ulSyncId, unsigned int ulChangeId, unsigned int ulFlags, struct restrictTable *lpsRestrict, ECGetContentChangesHelper **lppHelper)
  454. {
  455. ECRESULT er = erSuccess;
  456. std::unique_ptr<ECGetContentChangesHelper> lpHelper(
  457. new(std::nothrow) ECGetContentChangesHelper(soap, lpSession, lpDatabase,
  458. sFolderSourceKey, ulSyncId, ulChangeId, ulFlags, lpsRestrict));
  459. if (lpHelper == nullptr)
  460. return KCERR_NOT_ENOUGH_MEMORY;
  461. er = lpHelper->Init();
  462. if (er != erSuccess)
  463. return er;
  464. assert(lppHelper != NULL);
  465. *lppHelper = lpHelper.release();
  466. return erSuccess;
  467. }
  468. ECGetContentChangesHelper::ECGetContentChangesHelper(struct soap *soap,
  469. ECSession *lpSession, ECDatabase *lpDatabase,
  470. const SOURCEKEY &sFolderSourceKey, unsigned int ulSyncId,
  471. unsigned int ulChangeId, unsigned int ulFlags,
  472. struct restrictTable *lpsRestrict) :
  473. m_soap(soap), m_lpSession(lpSession), m_lpDatabase(lpDatabase),
  474. m_lpsRestrict(lpsRestrict), m_sFolderSourceKey(sFolderSourceKey),
  475. m_ulSyncId(ulSyncId), m_ulChangeId(ulChangeId), m_ulFlags(ulFlags)
  476. { }
  477. ECRESULT ECGetContentChangesHelper::Init()
  478. {
  479. ECRESULT er = erSuccess;
  480. DB_RESULT lpDBResult;
  481. DB_ROW lpDBRow;
  482. std::string strQuery;
  483. assert(m_lpDatabase != NULL);
  484. if (m_sFolderSourceKey.empty() && m_ulChangeId == 0 &&
  485. !(m_ulFlags & SYNC_CATCHUP))
  486. // Disallow full initial exports on server level since they are insanely large
  487. return KCERR_NO_SUPPORT;
  488. strQuery = "SELECT MAX(id) FROM changes";
  489. if(!m_sFolderSourceKey.empty())
  490. strQuery += " WHERE parentsourcekey=" + m_lpDatabase->EscapeBinary(m_sFolderSourceKey, m_sFolderSourceKey.size());
  491. er = m_lpDatabase->DoSelect(strQuery, &lpDBResult);
  492. if (er != erSuccess)
  493. return er;
  494. if ((lpDBRow = m_lpDatabase->FetchRow(lpDBResult)) == NULL || lpDBRow == NULL) {
  495. ec_log_err("ECGetContentChangesHelper::Init(): fetchrow failed");
  496. return KCERR_DATABASE_ERROR;
  497. }
  498. if (lpDBRow[0])
  499. m_ulMaxFolderChange = atoui(lpDBRow[0]);
  500. // Here we setup the classes to delegate specific work to
  501. if (m_ulChangeId == 0) {
  502. /*
  503. * Initial sync
  504. * We want all message that were not created by the current client (m_ulSyncId).
  505. */
  506. if(m_sFolderSourceKey.empty()) {
  507. // Optimization: when doing SYNC_CATCHUP on a non-filtered sync, we can skip looking for any changes
  508. assert(m_ulFlags & SYNC_CATCHUP);
  509. m_lpQueryCreator = new NullQueryCreator();
  510. } else {
  511. m_lpQueryCreator = new FullQueryCreator(m_lpDatabase, m_sFolderSourceKey, m_ulFlags, m_ulSyncId);
  512. }
  513. m_lpMsgProcessor = new FirstSyncProcessor(m_ulMaxFolderChange);
  514. } else {
  515. /*
  516. * Incremental sync
  517. * We first need to determine if the previous sync was with or without
  518. * restriction and if a restriction is requested now.
  519. */
  520. er = GetSyncedMessages(m_ulSyncId, m_ulChangeId, &m_setLegacyMessages);
  521. if (er != erSuccess)
  522. return er;
  523. if (m_setLegacyMessages.empty()) {
  524. /*
  525. * Previous request was without restriction.
  526. */
  527. if (m_lpsRestrict == NULL) {
  528. /*
  529. * This request is also without a restriction. We can use an
  530. * incremental query.
  531. */
  532. m_lpQueryCreator = new IncrementalQueryCreator(m_lpDatabase, m_ulSyncId, m_ulChangeId, m_sFolderSourceKey, m_ulFlags);
  533. m_lpMsgProcessor = new NonLegacyIncrementalProcessor(m_ulMaxFolderChange);
  534. } else {
  535. /*
  536. * This request is WITH a restriction. This means the client
  537. * switched from using no restriction to using a restriction.
  538. * Note: In practice this won't happen very often.
  539. * We need to perform a full query to be able te decide which
  540. * messages match the restriction and which don't.
  541. * Since the previous request was without a restriction, we
  542. * assume all messages that were present during the last sync
  543. * are on the device.
  544. * We do want to filter all messages that were created since
  545. * the last sync and were created by the current client. The
  546. * processor should do that because that's too complex for the
  547. * query creator to do.
  548. */
  549. m_lpQueryCreator = new FullQueryCreator(m_lpDatabase, m_sFolderSourceKey, m_ulFlags);
  550. m_lpMsgProcessor = new NonLegacyFullProcessor(m_ulChangeId, m_ulSyncId);
  551. }
  552. } else {
  553. /*
  554. * The previous request was with a restriction, so we can't do an
  555. * incremental sync in any case, as that will only get us add's and
  556. * deletes for changes that happened after the last sync. But we
  557. * can also have adds because certain older messages might not have
  558. * matched the previous restriction, but do match the current (where
  559. * no restriction is seen as a match-all restriction).
  560. * We do want to filter all messages that were created since
  561. * the last sync and were created by the current client. The
  562. * processor should do that because that's too complex for the
  563. * query creator to do.
  564. */
  565. m_lpQueryCreator = new FullQueryCreator(m_lpDatabase, m_sFolderSourceKey, m_ulFlags);
  566. m_lpMsgProcessor = new LegacyProcessor(m_ulChangeId, m_ulSyncId, m_setLegacyMessages, m_ulMaxFolderChange);
  567. }
  568. }
  569. return erSuccess;
  570. }
  571. ECGetContentChangesHelper::~ECGetContentChangesHelper()
  572. {
  573. delete m_lpQueryCreator;
  574. delete m_lpMsgProcessor;
  575. }
  576. ECRESULT ECGetContentChangesHelper::QueryDatabase(DB_RESULT *lppDBResult)
  577. {
  578. ECRESULT er;
  579. DB_RESULT lpDBResult;
  580. std::string strQuery;
  581. unsigned int ulChanges = 0;
  582. assert(m_lpQueryCreator != NULL);
  583. strQuery = m_lpQueryCreator->CreateQuery();
  584. if(!strQuery.empty()) {
  585. assert(m_lpDatabase != NULL);
  586. er = m_lpDatabase->DoSelect(strQuery, &lpDBResult);
  587. if (er != erSuccess)
  588. return er;
  589. } else {
  590. ulChanges = 0;
  591. }
  592. if(lpDBResult)
  593. ulChanges = m_lpDatabase->GetNumRows(lpDBResult) + m_setLegacyMessages.size();
  594. else
  595. ulChanges = 0;
  596. m_lpChanges = (icsChangesArray*)soap_malloc(m_soap, sizeof *m_lpChanges);
  597. m_lpChanges->__ptr = (icsChange*)soap_malloc(m_soap, sizeof *m_lpChanges->__ptr * ulChanges);
  598. m_lpChanges->__size = 0;
  599. assert(lppDBResult != NULL);
  600. *lppDBResult = std::move(lpDBResult);
  601. return erSuccess;
  602. }
  603. ECRESULT ECGetContentChangesHelper::ProcessRows(const std::vector<DB_ROW> &db_rows, const std::vector<DB_LENGTHS> &db_lengths)
  604. {
  605. ECRESULT er = erSuccess;
  606. unsigned int ulChangeType = 0;
  607. unsigned int ulFlags = 0;
  608. DB_ROW lpDBRow;
  609. DB_LENGTHS lpDBLen;
  610. std::set<SOURCEKEY> matches;
  611. if (m_lpsRestrict) {
  612. assert(m_lpSession != NULL);
  613. er = MatchRestrictions(db_rows, db_lengths, m_lpsRestrict, &matches);
  614. if (er != erSuccess)
  615. return er;
  616. }
  617. assert(m_lpMsgProcessor != NULL);
  618. for (size_t i = 0; i < db_rows.size(); ++i) {
  619. bool fMatch = true;
  620. lpDBRow = db_rows[i];
  621. lpDBLen = db_lengths[i];
  622. if (m_lpsRestrict != NULL)
  623. fMatch = matches.find(SOURCEKEY(lpDBLen[icsSourceKey], lpDBRow[icsSourceKey])) != matches.end();
  624. ec_log(EC_LOGLEVEL_ICS, "Processing: %s, match=%d", bin2hex(SOURCEKEY(lpDBLen[icsSourceKey], lpDBRow[icsSourceKey])).c_str(), fMatch);
  625. ulChangeType = 0;
  626. ulFlags = 0;
  627. if (fMatch) {
  628. er = m_lpMsgProcessor->ProcessAccepted(lpDBRow, lpDBLen, &ulChangeType, &ulFlags);
  629. if (m_lpsRestrict != NULL)
  630. m_setNewMessages.insert(MESSAGESET::value_type(SOURCEKEY(lpDBLen[icsSourceKey],
  631. lpDBRow[icsSourceKey]), SAuxMessageData(SOURCEKEY(lpDBLen[icsParentSourceKey],
  632. lpDBRow[icsParentSourceKey]), ICS_CHANGE_FLAG_NEW, ulFlags)));
  633. } else {
  634. er = m_lpMsgProcessor->ProcessRejected(lpDBRow, lpDBLen, &ulChangeType);
  635. }
  636. if (er != erSuccess)
  637. return er;
  638. // If ulChangeType equals 0 we can skip this message
  639. if (ulChangeType == 0)
  640. continue;
  641. m_lpChanges->__ptr[m_ulChangeCnt].ulChangeId = lpDBRow[icsID] ? atoui(lpDBRow[icsID]) : 0;
  642. m_lpChanges->__ptr[m_ulChangeCnt].sSourceKey.__ptr = (unsigned char *)soap_malloc(m_soap, lpDBLen[icsSourceKey]);
  643. m_lpChanges->__ptr[m_ulChangeCnt].sSourceKey.__size = lpDBLen[icsSourceKey];
  644. memcpy(m_lpChanges->__ptr[m_ulChangeCnt].sSourceKey.__ptr, lpDBRow[icsSourceKey], lpDBLen[icsSourceKey]);
  645. m_lpChanges->__ptr[m_ulChangeCnt].sParentSourceKey.__ptr = (unsigned char *)soap_malloc(m_soap, lpDBLen[icsParentSourceKey]);
  646. m_lpChanges->__ptr[m_ulChangeCnt].sParentSourceKey.__size = lpDBLen[icsParentSourceKey];
  647. memcpy(m_lpChanges->__ptr[m_ulChangeCnt].sParentSourceKey.__ptr, lpDBRow[icsParentSourceKey], lpDBLen[icsParentSourceKey]);
  648. m_lpChanges->__ptr[m_ulChangeCnt].ulChangeType = ulChangeType;
  649. m_lpChanges->__ptr[m_ulChangeCnt].ulFlags = ulFlags;
  650. ++m_ulChangeCnt;
  651. }
  652. return erSuccess;
  653. }
  654. ECRESULT ECGetContentChangesHelper::ProcessResidualMessages()
  655. {
  656. ECRESULT er;
  657. MESSAGESET setResiduals;
  658. assert(m_lpMsgProcessor != NULL);
  659. er = m_lpMsgProcessor->GetResidualMessages(&setResiduals);
  660. if (er != erSuccess)
  661. return er;
  662. for (const auto &p : setResiduals) {
  663. if (p.first.size() == 1 && memcmp(p.first, "\0", 1) == 0)
  664. continue; // Skip empty restricted set marker,
  665. ec_log(EC_LOGLEVEL_ICS, "ProcessResidualMessages: sourcekey=%s", bin2hex(p.first).c_str());
  666. m_lpChanges->__ptr[m_ulChangeCnt].ulChangeId = 0;
  667. m_lpChanges->__ptr[m_ulChangeCnt].sSourceKey.__ptr = (unsigned char *)soap_malloc(m_soap, p.first.size());
  668. m_lpChanges->__ptr[m_ulChangeCnt].sSourceKey.__size = p.first.size();
  669. memcpy(m_lpChanges->__ptr[m_ulChangeCnt].sSourceKey.__ptr, p.first, p.first.size());
  670. m_lpChanges->__ptr[m_ulChangeCnt].sParentSourceKey.__ptr = (unsigned char *)soap_malloc(m_soap, p.second.sParentSourceKey.size());
  671. m_lpChanges->__ptr[m_ulChangeCnt].sParentSourceKey.__size = p.second.sParentSourceKey.size();
  672. memcpy(m_lpChanges->__ptr[m_ulChangeCnt].sParentSourceKey.__ptr, p.second.sParentSourceKey, p.second.sParentSourceKey.size());
  673. m_lpChanges->__ptr[m_ulChangeCnt].ulChangeType = ICS_HARD_DELETE;
  674. m_lpChanges->__ptr[m_ulChangeCnt].ulFlags = 0;
  675. ++m_ulChangeCnt;
  676. }
  677. return erSuccess;
  678. }
  679. ECRESULT ECGetContentChangesHelper::Finalize(unsigned int *lpulMaxChange, icsChangesArray **lppChanges)
  680. {
  681. ECRESULT er = erSuccess;
  682. std::string strQuery;
  683. unsigned int ulMaxChange = 0;
  684. unsigned int ulNewChange = 0;
  685. DB_RESULT lpDBResult;
  686. DB_ROW lpDBRow;
  687. assert(lppChanges != NULL);
  688. assert(lpulMaxChange != NULL);
  689. m_lpChanges->__size = m_ulChangeCnt;
  690. *lppChanges = m_lpChanges;
  691. assert(m_lpMsgProcessor != NULL);
  692. ulMaxChange = m_lpMsgProcessor->GetMaxChangeId();
  693. if (m_ulFlags & SYNC_NO_DB_CHANGES) {
  694. *lpulMaxChange = ulMaxChange;
  695. return er;
  696. }
  697. // If there were no changes and this was not the initial sync, we only need to purge all too-new-syncedmessages.
  698. // If this is the initial sync, we might need to write the empty restricted set marker, so we can't
  699. // stop doing work here. Also, if we have converted from a non-restricted to a restricted set, we have to write
  700. // the new set of messages, even if there are no changes.
  701. if (m_ulChangeCnt == 0 && m_ulChangeId > 0 && !(m_setLegacyMessages.empty() && m_lpsRestrict) ) {
  702. assert(ulMaxChange >= m_ulChangeId);
  703. *lpulMaxChange = ulMaxChange;
  704. // Delete all entries that have a changeid that are greater to the new change id.
  705. strQuery = "DELETE FROM syncedmessages WHERE sync_id=" + stringify(m_ulSyncId) + " AND change_id>" + stringify(ulMaxChange);
  706. return m_lpDatabase->DoDelete(strQuery);
  707. }
  708. if (ulMaxChange == m_ulChangeId) {
  709. /**
  710. * If we get here, we had at least one change but the max changeid for the server is the
  711. * same as the changeid in the request. This means the change was caused by either a modified
  712. * restriction.
  713. * When this happens a new changeid must be generated in order to return a unique state to the
  714. * client that can be used in subsequent requests. We do this by creating a dummy change in the
  715. * changes table.
  716. */
  717. // Bump the changeid
  718. strQuery = "REPLACE INTO changes (sourcekey,parentsourcekey,sourcesync) VALUES (0, " + m_lpDatabase->EscapeBinary(m_sFolderSourceKey, m_sFolderSourceKey.size()) + "," + stringify(m_ulSyncId) + ")";
  719. er = m_lpDatabase->DoInsert(strQuery, &ulNewChange);
  720. if (er != erSuccess)
  721. return er;
  722. assert(ulNewChange > ulMaxChange);
  723. ulMaxChange = ulNewChange;
  724. assert(ulMaxChange > m_ulChangeId);
  725. }
  726. /**
  727. * If a restriction is set, but the set of synced messages is empty we'll make a placeholder entry
  728. * so we can differentiate between having all messages and having no messages on the client.
  729. *
  730. * It's actually backwards to put in a placeholder when we have no message and put in nothing when
  731. * we have all messages, but having all message (because no restriction was set) never stores anything
  732. * in the syncedmessages table, so this scheme is compatible. On top of that, having no messages synced
  733. * at all is rare, having all messages isn't.
  734. **/
  735. if (m_lpsRestrict && m_setNewMessages.empty())
  736. m_setNewMessages.insert(MESSAGESET::value_type(SOURCEKEY(1, "\x00"), SAuxMessageData(m_sFolderSourceKey, 0, 0)));
  737. if (!m_setNewMessages.empty()) {
  738. std::set<unsigned int> setChangeIds;
  739. strQuery = "SELECT DISTINCT change_id FROM syncedmessages WHERE sync_id=" + stringify(m_ulSyncId);
  740. er = m_lpDatabase->DoSelect(strQuery, &lpDBResult);
  741. if (er != erSuccess)
  742. return er;
  743. while ((lpDBRow = m_lpDatabase->FetchRow(lpDBResult))) {
  744. if (lpDBRow == NULL || lpDBRow[0] == NULL) {
  745. ec_log_err("ECGetContentChangesHelper::Finalize(): row null or column null");
  746. return KCERR_DATABASE_ERROR; /* this should never happen */
  747. }
  748. setChangeIds.insert(atoui(lpDBRow[0]));
  749. }
  750. if (!setChangeIds.empty()) {
  751. std::set<unsigned int> setDeleteIds;
  752. /* Remove obsolete states
  753. *
  754. * rules:
  755. * 1) Remove any states that are newer than the state that was requested
  756. * We do this since if the client requests state X, it can never request state X+1
  757. * later unless X+1 is the state that was generated from this request. We can therefore
  758. * remove any state > X at this point, since state X+1 will be inserted later
  759. * 2) Remove any states that are older than the state that was requested minus nine
  760. * We cannot remove state X since the client may re-request this state (eg if the export
  761. * failed due to network error, or if the export is interrupted before ending). We also
  762. * do not remove state X-9 to X-1 so that we support some sort of rollback of the client.
  763. * This may happen if the client is restored to an old state. In practice removing X-9 to
  764. * X-1 will probably not cause any real problems though, and the number 9 is pretty
  765. * arbitrary.
  766. */
  767. // Delete any message state that is higher than the changeset that changes were
  768. // requested from (rule 1)
  769. auto iter = setChangeIds.upper_bound(m_ulChangeId);
  770. if (iter != setChangeIds.cend())
  771. std::copy(iter, setChangeIds.end(), std::inserter(setDeleteIds, setDeleteIds.begin()));
  772. // Find all message states that are equal or lower than the changeset that changes were requested from
  773. iter = setChangeIds.lower_bound(m_ulChangeId);
  774. // Reverse up to nine message states (less if they do not exist)
  775. for (int i = 0; iter != setChangeIds.begin() && i < 9; ++i, --iter);
  776. // Remove message states that are older than X-9 (rule 2)
  777. std::copy(setChangeIds.begin(), iter, std::inserter(setDeleteIds, setDeleteIds.begin()));
  778. if (!setDeleteIds.empty()) {
  779. assert(setChangeIds.size() - setDeleteIds.size() <= 9);
  780. strQuery = "DELETE FROM syncedmessages WHERE sync_id=" + stringify(m_ulSyncId) + " AND change_id IN (";
  781. for (auto del_id : setDeleteIds) {
  782. strQuery.append(stringify(del_id));
  783. strQuery.append(1, ',');
  784. }
  785. strQuery.resize(strQuery.size() - 1); // Remove trailing ','
  786. strQuery.append(1, ')');
  787. er = m_lpDatabase->DoDelete(strQuery);
  788. if (er != erSuccess)
  789. return er;
  790. }
  791. }
  792. // Create the insert query
  793. strQuery = "INSERT INTO syncedmessages (sync_id,change_id,sourcekey,parentsourcekey) VALUES ";
  794. for (const auto &p : m_setNewMessages)
  795. strQuery += "(" + stringify(m_ulSyncId) + "," + stringify(ulMaxChange) + "," +
  796. m_lpDatabase->EscapeBinary(p.first, p.first.size()) + "," +
  797. m_lpDatabase->EscapeBinary(p.second.sParentSourceKey, p.second.sParentSourceKey.size()) + "),";
  798. strQuery.resize(strQuery.size() - 1);
  799. er = m_lpDatabase->DoInsert(strQuery);
  800. if (er != erSuccess)
  801. return er;
  802. }
  803. *lpulMaxChange = ulMaxChange;
  804. return erSuccess;
  805. }
  806. ECRESULT ECGetContentChangesHelper::MatchRestrictions(const std::vector<DB_ROW> &db_rows,
  807. const std::vector<DB_LENGTHS> &db_lengths,
  808. struct restrictTable *restrict, std::set<SOURCEKEY> *matches_p)
  809. {
  810. ECRESULT er = erSuccess;
  811. unsigned int ulObjId = 0;
  812. ECObjectTableList lstRows;
  813. ECObjectTableList::value_type sRow;
  814. ECODStore sODStore;
  815. bool fMatch = false;
  816. std::vector<SOURCEKEY> source_keys;
  817. std::map<ECsIndexProp, unsigned int> index_objs;
  818. struct propTagArray *lpPropTags = NULL;
  819. struct rowSet *lpRowSet = NULL;
  820. std::set<SOURCEKEY> matches;
  821. std::vector<unsigned int> cbdata;
  822. std::vector<unsigned char *> lpdata;
  823. std::vector<unsigned int> objectids;
  824. memset(&sODStore, 0, sizeof(sODStore));
  825. ec_log(EC_LOGLEVEL_ICS, "MatchRestrictions: matching %zu rows", db_rows.size());
  826. for (size_t i = 0; i < db_rows.size(); ++i) {
  827. lpdata.push_back(reinterpret_cast<unsigned char *>(db_rows[i][icsSourceKey]));
  828. cbdata.push_back(db_lengths[i][icsSourceKey]);
  829. }
  830. er = g_lpSessionManager->GetCacheManager()->GetObjectsFromProp(PROP_ID(PR_SOURCE_KEY), cbdata, lpdata, index_objs);
  831. if (er != erSuccess)
  832. goto exit;
  833. for (const auto &i : index_objs) {
  834. sRow.ulObjId = i.second;
  835. sRow.ulOrderId = 0;
  836. lstRows.push_back(sRow);
  837. source_keys.push_back(SOURCEKEY(i.first.cbData, reinterpret_cast<const char *>(i.first.lpData)));
  838. ulObjId = i.second; /* no need to split QueryRowData call per-objtype (always same) */
  839. }
  840. er = g_lpSessionManager->GetCacheManager()->GetObject(ulObjId, NULL, NULL, NULL, &sODStore.ulObjType);
  841. if (er != erSuccess)
  842. goto exit;
  843. er = ECGenericObjectTable::GetRestrictPropTags(restrict, NULL, &lpPropTags);
  844. if (er != erSuccess)
  845. goto exit;
  846. sODStore.lpGuid = new GUID;
  847. er = g_lpSessionManager->GetCacheManager()->GetStore(ulObjId, &sODStore.ulStoreId, sODStore.lpGuid);
  848. if (er != erSuccess)
  849. goto exit;
  850. assert(m_lpSession != NULL);
  851. // NULL for soap, not m_soap. We'll free this ourselves
  852. er = ECStoreObjectTable::QueryRowData(NULL, NULL, m_lpSession, &lstRows, lpPropTags, &sODStore, &lpRowSet, false, false);
  853. if (er != erSuccess)
  854. goto exit;
  855. if (lpRowSet->__size < 0 ||
  856. static_cast<size_t>(lpRowSet->__size) != lstRows.size()) {
  857. er = KCERR_DATABASE_ERROR;
  858. ec_log_err("ECGetContentChangesHelper::MatchRestriction(): unexpected row count");
  859. goto exit;
  860. }
  861. for (gsoap_size_t j = 0; j < lpRowSet->__size; ++j) {
  862. // @todo: Get a proper locale for the case insensitive comparisons inside MatchRowRestrict
  863. er = ECGenericObjectTable::MatchRowRestrict(g_lpSessionManager->GetCacheManager(), &lpRowSet->__ptr[j], restrict, NULL, createLocaleFromName(""), &fMatch);
  864. if(er != erSuccess)
  865. goto exit;
  866. if (fMatch)
  867. matches.insert(source_keys[j]);
  868. }
  869. ec_log(EC_LOGLEVEL_ICS, "MatchRestrictions: %zu match(es) out of %d rows (%d properties)",
  870. matches.size(), lpRowSet->__size, lpPropTags->__size);
  871. *matches_p = std::move(matches);
  872. exit:
  873. delete sODStore.lpGuid;
  874. if(lpPropTags)
  875. FreePropTagArray(lpPropTags);
  876. if(lpRowSet)
  877. FreeRowSet(lpRowSet, true);
  878. return er;
  879. }
  880. ECRESULT ECGetContentChangesHelper::GetSyncedMessages(unsigned int ulSyncId, unsigned int ulChangeId, LPMESSAGESET lpsetMessages)
  881. {
  882. ECRESULT er = erSuccess;
  883. std::string strSubQuery;
  884. std::string strQuery;
  885. DB_RESULT lpDBResult;
  886. DB_ROW lpDBRow;
  887. DB_LENGTHS lpDBLen;
  888. strQuery =
  889. "SELECT m.sourcekey, m.parentsourcekey, c.change_type, c.flags "
  890. "FROM syncedmessages as m "
  891. "LEFT JOIN changes as c "
  892. "ON m.sourcekey=c.sourcekey AND m.parentsourcekey=c.parentsourcekey AND c.id > " + stringify(ulChangeId) + " AND c.sourcesync != " + stringify(ulSyncId) + " "
  893. "WHERE sync_id=" + stringify(ulSyncId) + " AND change_id=" + stringify(ulChangeId);
  894. assert(m_lpDatabase != NULL);
  895. er = m_lpDatabase->DoSelect(strQuery, &lpDBResult);
  896. if (er != erSuccess)
  897. return er;
  898. while ((lpDBRow = m_lpDatabase->FetchRow(lpDBResult))) {
  899. lpDBLen = m_lpDatabase->FetchRowLengths(lpDBResult);
  900. if (lpDBRow == NULL || lpDBLen == NULL || lpDBRow[0] == NULL || lpDBRow[1] == NULL) {
  901. ec_log_err("ECGetContentChangesHelper::GetSyncedMessages(): row or columns null");
  902. return KCERR_DATABASE_ERROR; /* this should never happen */
  903. }
  904. auto iResult = lpsetMessages->insert(MESSAGESET::value_type(SOURCEKEY(lpDBLen[0], lpDBRow[0]), SAuxMessageData(SOURCEKEY(lpDBLen[1], lpDBRow[1]), 1 << (lpDBRow[2]?atoui(lpDBRow[2]):0), lpDBRow[3]?atoui(lpDBRow[3]):0)));
  905. if (iResult.second == false && lpDBRow[2] != nullptr)
  906. iResult.first->second.ulChangeTypes |= 1 << (lpDBRow[2]?atoui(lpDBRow[2]):0);
  907. }
  908. return erSuccess;
  909. }
  910. bool ECGetContentChangesHelper::CompareMessageEntry(const MESSAGESET::value_type &lhs, const MESSAGESET::value_type &rhs)
  911. {
  912. return lhs.first == rhs.first;
  913. }
  914. bool ECGetContentChangesHelper::MessageSetsDiffer() const
  915. {
  916. if (m_setLegacyMessages.size() != m_setNewMessages.size())
  917. return true;
  918. return !std::equal(m_setLegacyMessages.begin(), m_setLegacyMessages.end(), m_setNewMessages.begin(), &CompareMessageEntry);
  919. }
  920. } /* namespaces */