ECICS.cpp 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200
  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 <memory>
  19. #include <kopano/tie.hpp>
  20. #include "kcore.hpp"
  21. #include <mapidefs.h>
  22. #include <edkmdb.h>
  23. #include <kopano/stringutil.h>
  24. #include "ECSessionManager.h"
  25. #include "ECSecurity.h"
  26. #include "ics.h"
  27. #include "cmdutil.hpp"
  28. #include "ECStoreObjectTable.h"
  29. #include <kopano/ECGuid.h>
  30. #include "ECICS.h"
  31. #include "ECICSHelpers.h"
  32. #include "ECMAPI.h"
  33. #include "soapH.h"
  34. #include "SOAPUtils.h"
  35. using namespace KCHL;
  36. namespace KC {
  37. extern ECSessionManager* g_lpSessionManager;
  38. struct ABChangeRecord {
  39. ULONG id;
  40. std::string strItem;
  41. std::string strParent;
  42. ULONG change_type;
  43. ABChangeRecord(ULONG id, const std::string &strItem, const std::string &strParent, ULONG change_type);
  44. };
  45. inline ABChangeRecord::ABChangeRecord(ULONG _id, const std::string &_strItem, const std::string &_strParent, ULONG _change_type)
  46. : id(_id)
  47. , strItem(_strItem)
  48. , strParent(_strParent)
  49. , change_type(_change_type)
  50. { }
  51. typedef std::list<ABChangeRecord> ABChangeRecordList;
  52. static bool isICSChange(unsigned int ulChange)
  53. {
  54. switch(ulChange){
  55. case ICS_MESSAGE_CHANGE:
  56. case ICS_MESSAGE_FLAG:
  57. case ICS_MESSAGE_SOFT_DELETE:
  58. case ICS_MESSAGE_HARD_DELETE:
  59. case ICS_MESSAGE_NEW:
  60. case ICS_FOLDER_CHANGE:
  61. case ICS_FOLDER_SOFT_DELETE:
  62. case ICS_FOLDER_HARD_DELETE:
  63. case ICS_FOLDER_NEW:
  64. return true;
  65. default:
  66. return false;
  67. }
  68. }
  69. static ECRESULT FilterUserIdsByCompany(ECDatabase *lpDatabase, const std::set<unsigned int> &sUserIds, unsigned int ulCompanyFilter, std::set<unsigned int> *lpsFilteredIds)
  70. {
  71. ECRESULT er = erSuccess;
  72. DB_RESULT lpDBResult;
  73. std::string strQuery;
  74. unsigned int ulRows = 0;
  75. assert(!sUserIds.empty());
  76. strQuery = "SELECT id FROM users where company=" + stringify(ulCompanyFilter) + " AND id IN (";
  77. for (const auto i : sUserIds)
  78. strQuery.append(stringify(i) + ",");
  79. strQuery.resize(strQuery.size() - 1);
  80. strQuery.append(1, ')');
  81. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  82. if (er != erSuccess)
  83. return er;
  84. ulRows = lpDatabase->GetNumRows(lpDBResult);
  85. if (ulRows > 0) {
  86. DB_ROW lpDBRow = NULL;
  87. std::set<unsigned int> sFilteredIds;
  88. for (unsigned int i = 0; i < ulRows; ++i) {
  89. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  90. if (lpDBRow == NULL || lpDBRow[0] == NULL) {
  91. ec_log_crit("%s:%d unexpected null pointer", __FUNCTION__, __LINE__);
  92. return KCERR_DATABASE_ERROR;
  93. }
  94. sFilteredIds.insert(atoui(lpDBRow[0]));
  95. }
  96. lpsFilteredIds->swap(sFilteredIds);
  97. } else
  98. lpsFilteredIds->clear();
  99. return erSuccess;
  100. }
  101. static ECRESULT ConvertABEntryIDToSoapSourceKey(struct soap *soap,
  102. ECSession *lpSession, bool bUseV1, unsigned int cbEntryId,
  103. char *lpEntryId, xsd__base64Binary *lpSourceKey)
  104. {
  105. ECRESULT er;
  106. unsigned int cbAbeid = cbEntryId;
  107. auto lpAbeid = reinterpret_cast<ABEID *>(lpEntryId);
  108. entryId sEntryId = {0};
  109. SOURCEKEY sSourceKey;
  110. objectid_t sExternId;
  111. if (cbEntryId < CbNewABEID("") || lpEntryId == NULL ||
  112. lpSourceKey == NULL)
  113. return KCERR_INVALID_PARAMETER;
  114. if (lpAbeid->ulVersion == 1 && !bUseV1)
  115. {
  116. er = MAPITypeToType(lpAbeid->ulType, &sExternId.objclass);
  117. if (er != erSuccess)
  118. return er;
  119. er = ABIDToEntryID(soap, lpAbeid->ulId, sExternId, &sEntryId); // Creates a V0 EntryID, because sExternId.id is empty
  120. if (er != erSuccess)
  121. return er;
  122. lpAbeid = reinterpret_cast<ABEID *>(sEntryId.__ptr);
  123. cbAbeid = sEntryId.__size;
  124. }
  125. else if (lpAbeid->ulVersion == 0 && bUseV1)
  126. {
  127. er = lpSession->GetUserManagement()->GetABSourceKeyV1(lpAbeid->ulId, &sSourceKey);
  128. if (er != erSuccess)
  129. return er;
  130. lpAbeid = reinterpret_cast<ABEID *>(static_cast<unsigned char *>(sSourceKey));
  131. cbAbeid = sSourceKey.size();
  132. }
  133. lpSourceKey->__size = cbAbeid;
  134. lpSourceKey->__ptr = (unsigned char*)s_memcpy(soap, (char*)lpAbeid, cbAbeid);
  135. return erSuccess;
  136. }
  137. static void AddChangeKeyToChangeList(std::string *strChangeList,
  138. ULONG cbChangeKey, const char *lpChangeKey)
  139. {
  140. if(cbChangeKey <= sizeof(GUID) || cbChangeKey > 255)
  141. return;
  142. ULONG ulPos = 0;
  143. ULONG ulSize = 0;
  144. bool bFound = false;
  145. std::string strChangeKey;
  146. if(cbChangeKey > 0xFF)
  147. return;
  148. strChangeKey.assign((char *)&cbChangeKey,1);
  149. strChangeKey.append(lpChangeKey, cbChangeKey);
  150. while(ulPos < strChangeList->size()){
  151. ulSize = strChangeList->at(ulPos);
  152. if(ulSize <= sizeof(GUID) || ulSize == (ULONG)-1){
  153. break;
  154. }else if(memcmp(strChangeList->substr(ulPos+1, ulSize).c_str(), lpChangeKey, sizeof(GUID)) == 0){
  155. strChangeList->replace(ulPos, ulSize+1, strChangeKey);
  156. bFound = true;
  157. }
  158. ulPos += ulSize + 1;
  159. }
  160. if (!bFound)
  161. strChangeList->append(strChangeKey);
  162. }
  163. ECRESULT AddChange(BTSession *lpSession, unsigned int ulSyncId,
  164. const SOURCEKEY &sSourceKey, const SOURCEKEY &sParentSourceKey,
  165. unsigned int ulChange, unsigned int ulFlags, bool fForceNewChangeKey,
  166. std::string *lpstrChangeKey, std::string *lpstrChangeList)
  167. {
  168. ECRESULT er = erSuccess;
  169. std::string strQuery;
  170. ECDatabase* lpDatabase = NULL;
  171. DB_RESULT lpDBResult;
  172. DB_ROW lpDBRow = NULL;
  173. DB_LENGTHS lpDBLen = NULL;
  174. unsigned int changeid = 0;
  175. unsigned int ulObjId = 0;
  176. char szChangeKey[20];
  177. std::string strChangeList;
  178. std::set<unsigned int> syncids;
  179. bool bIgnored = false;
  180. if(!isICSChange(ulChange)){
  181. er = KCERR_INVALID_TYPE;
  182. goto exit;
  183. }
  184. if(sSourceKey == sParentSourceKey || sSourceKey.empty() || sParentSourceKey.empty()) {
  185. er = KCERR_INVALID_PARAMETER;
  186. goto exit;
  187. }
  188. er = lpSession->GetDatabase(&lpDatabase);
  189. if (er != erSuccess)
  190. goto exit;
  191. // Always log folder changes
  192. if(ulChange & ICS_MESSAGE) {
  193. // See if anybody is interested in this change. If nobody has subscribed to this folder (ie nobody has got a state on this folder)
  194. // then we can ignore the change.
  195. strQuery = "SELECT id FROM syncs "
  196. "WHERE sourcekey=" + lpDatabase->EscapeBinary(sParentSourceKey, sParentSourceKey.size());
  197. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  198. if(er != erSuccess)
  199. goto exit;
  200. while (true) {
  201. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  202. if (lpDBRow == NULL)
  203. break;
  204. unsigned int ulTmp = atoui((char*)lpDBRow[0]);
  205. if (ulTmp != ulSyncId)
  206. syncids.insert(ulTmp);
  207. else
  208. bIgnored = true;
  209. }
  210. }
  211. // Record the change
  212. strQuery = "REPLACE INTO changes(change_type, sourcekey, parentsourcekey, sourcesync, flags) "
  213. "VALUES (" + stringify(ulChange) +
  214. ", " + lpDatabase->EscapeBinary(sSourceKey, sSourceKey.size()) +
  215. ", " + lpDatabase->EscapeBinary(sParentSourceKey, sParentSourceKey.size()) +
  216. ", " + stringify(ulSyncId) +
  217. ", " + stringify(ulFlags) +
  218. ")";
  219. er = lpDatabase->DoInsert(strQuery, &changeid , NULL);
  220. if(er != erSuccess)
  221. goto exit;
  222. if ((ulChange & ICS_HARD_DELETE) == ICS_HARD_DELETE || (ulChange & ICS_SOFT_DELETE) == ICS_SOFT_DELETE) {
  223. if (ulSyncId != 0)
  224. er = RemoveFromLastSyncedMessagesSet(lpDatabase, ulSyncId, sSourceKey, sParentSourceKey);
  225. // The real item is removed from the database, So no further actions needed
  226. goto exit;
  227. }
  228. if (ulChange == ICS_MESSAGE_NEW && ulSyncId != 0) {
  229. er = AddToLastSyncedMessagesSet(lpDatabase, ulSyncId, sSourceKey, sParentSourceKey);
  230. if (er != erSuccess)
  231. goto exit;
  232. }
  233. // Add change key and predecessor change list
  234. er = g_lpSessionManager->GetCacheManager()->GetObjectFromProp(PROP_ID(PR_SOURCE_KEY), sSourceKey.size(), sSourceKey, &ulObjId);
  235. if(er == KCERR_NOT_FOUND){
  236. er = erSuccess; //hard deleted items can't be updated
  237. goto exit;
  238. }
  239. if(er != erSuccess)
  240. goto exit;
  241. strChangeList = "";
  242. strQuery = "SELECT val_binary FROM properties "
  243. "WHERE tag = " + stringify(PROP_ID(PR_PREDECESSOR_CHANGE_LIST)) +
  244. " AND type = " + stringify(PROP_TYPE(PR_PREDECESSOR_CHANGE_LIST)) +
  245. " AND hierarchyid = " + stringify(ulObjId) + " LIMIT 1";
  246. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  247. if(er != erSuccess)
  248. goto exit;
  249. if(lpDatabase->GetNumRows(lpDBResult) > 0){
  250. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  251. lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
  252. if (lpDBRow != nullptr && lpDBRow[0] != nullptr &&
  253. lpDBLen != nullptr && lpDBLen[0] > 16)
  254. strChangeList.assign(lpDBRow[0], lpDBLen[0]);
  255. }
  256. strQuery = "SELECT val_binary FROM properties "
  257. "WHERE tag = " + stringify(PROP_ID(PR_CHANGE_KEY)) +
  258. " AND type = " + stringify(PROP_TYPE(PR_CHANGE_KEY)) +
  259. " AND hierarchyid = " + stringify(ulObjId) + " LIMIT 1";
  260. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  261. if(er != erSuccess)
  262. goto exit;
  263. if(lpDatabase->GetNumRows(lpDBResult) > 0){
  264. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  265. lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
  266. if(lpDBRow && lpDBRow[0] && lpDBLen && lpDBLen[0] >16){
  267. AddChangeKeyToChangeList(&strChangeList, lpDBLen[0], lpDBRow[0]);
  268. }
  269. }
  270. /**
  271. * There are two reasons for generating a new change key:
  272. * 1. ulSyncId == 0. When this happens we have a modification initiated from the online server.
  273. * Since we got this far, there are other servers/devices monitoring this object, so a new
  274. * change key needs to be generated.
  275. * 2. fForceNewChangeKey == true. When this happens the caller has determined that a new change key
  276. * needs to be generated. (We can't do this much earlier as we need the changeid, which we only
  277. * get once the change has been registed in the database.) At this time this will only happen when
  278. * no change key was send by the client when calling ns__saveObject. This is the case when a change
  279. * occurs on the local server (actually making check 1 superfluous) or when a change is received
  280. * through z-push.
  281. **/
  282. if(ulSyncId == 0 || fForceNewChangeKey == true)
  283. {
  284. struct propVal sProp;
  285. struct xsd__base64Binary sBin;
  286. struct sObjectTableKey key;
  287. // Add the new change key to the predecessor change list
  288. lpSession->GetServerGUID((GUID*)szChangeKey);
  289. memcpy(szChangeKey + sizeof(GUID), &changeid, 4);
  290. AddChangeKeyToChangeList(&strChangeList, sizeof(szChangeKey), szChangeKey);
  291. // Write PR_PREDECESSOR_CHANGE_LIST and PR_CHANGE_KEY
  292. sProp.ulPropTag = PR_PREDECESSOR_CHANGE_LIST;
  293. sProp.__union = SOAP_UNION_propValData_bin;
  294. sProp.Value.bin = &sBin;
  295. sProp.Value.bin->__ptr = (BYTE *)strChangeList.c_str();
  296. sProp.Value.bin->__size = strChangeList.size();
  297. er = WriteProp(lpDatabase, ulObjId, 0, &sProp);
  298. if(er != erSuccess)
  299. goto exit;
  300. key.ulObjId = ulObjId;
  301. key.ulOrderId = 0;
  302. lpSession->GetSessionManager()->GetCacheManager()->SetCell(&key, PR_PREDECESSOR_CHANGE_LIST, &sProp);
  303. sProp.ulPropTag = PR_CHANGE_KEY;
  304. sProp.__union = SOAP_UNION_propValData_bin;
  305. sProp.Value.bin = &sBin;
  306. sProp.Value.bin->__ptr = (BYTE *)szChangeKey;
  307. sProp.Value.bin->__size = sizeof(szChangeKey);
  308. er = WriteProp(lpDatabase, ulObjId, 0, &sProp);
  309. if(er != erSuccess)
  310. goto exit;
  311. key.ulObjId = ulObjId;
  312. key.ulOrderId = 0;
  313. lpSession->GetSessionManager()->GetCacheManager()->SetCell(&key, PR_CHANGE_KEY, &sProp);
  314. if (lpstrChangeKey != nullptr)
  315. lpstrChangeKey->assign(szChangeKey, sizeof(szChangeKey));
  316. if (lpstrChangeList != nullptr)
  317. lpstrChangeList->assign(strChangeList);
  318. }
  319. exit:
  320. // FIXME: We should not send notifications while we're in the middle of a transaction.
  321. if (er == erSuccess && !syncids.empty())
  322. g_lpSessionManager->NotificationChange(syncids, changeid, ulChange);
  323. return er;
  324. }
  325. void* CleanupSyncsTable(void* lpTmpMain){
  326. ECRESULT er = erSuccess;
  327. std::string strQuery;
  328. ECDatabase* lpDatabase = NULL;
  329. ECSession* lpSession = NULL;
  330. unsigned int ulSyncLifeTime = 0;
  331. unsigned int ulDeletedSyncs = 0;
  332. ec_log_info("Start syncs table clean up");
  333. ulSyncLifeTime = atoui(g_lpSessionManager->GetConfig()->GetSetting("sync_lifetime"));
  334. if(ulSyncLifeTime == 0)
  335. goto exit;
  336. er = g_lpSessionManager->CreateSessionInternal(&lpSession);
  337. if(er != erSuccess)
  338. goto exit;
  339. lpSession->Lock();
  340. er = lpSession->GetDatabase(&lpDatabase);
  341. if (er != erSuccess)
  342. goto exit;
  343. strQuery = "DELETE FROM syncs WHERE sync_time < DATE_SUB(FROM_UNIXTIME("+stringify(time(NULL))+"), INTERVAL " + stringify(ulSyncLifeTime) + " DAY)";
  344. er = lpDatabase->DoDelete(strQuery, &ulDeletedSyncs);
  345. if(er != erSuccess)
  346. goto exit;
  347. exit:
  348. if(er == erSuccess)
  349. ec_log_info("syncs table clean up done: removed syncs: %d", ulDeletedSyncs);
  350. else
  351. ec_log_info("syncs table clean up failed: error 0x%08X, removed syncs: %d", er, ulDeletedSyncs);
  352. if(lpSession) {
  353. lpSession->Unlock(); // Lock the session
  354. g_lpSessionManager->RemoveSessionInternal(lpSession);
  355. }
  356. // ECScheduler does nothing with the returned value
  357. return NULL;
  358. }
  359. void *CleanupSyncedMessagesTable(void *lpTmpMain)
  360. {
  361. ECRESULT er = erSuccess;
  362. std::string strQuery;
  363. ECDatabase* lpDatabase = NULL;
  364. ECSession* lpSession = NULL;
  365. unsigned int ulDeleted = 0;
  366. ec_log_info("Start syncedmessages table clean up");
  367. er = g_lpSessionManager->CreateSessionInternal(&lpSession);
  368. if(er != erSuccess)
  369. goto exit;
  370. lpSession->Lock();
  371. er = lpSession->GetDatabase(&lpDatabase);
  372. if (er != erSuccess)
  373. goto exit;
  374. strQuery = "DELETE syncedmessages.* FROM syncedmessages "
  375. "LEFT JOIN syncs ON syncs.id = syncedmessages.sync_id " /* join with syncs */
  376. "WHERE syncs.id IS NULL"; /* with no existing sync, and therefore not being tracked */
  377. er = lpDatabase->DoDelete(strQuery, &ulDeleted);
  378. if(er != erSuccess)
  379. goto exit;
  380. exit:
  381. if(er == erSuccess)
  382. ec_log_info("syncedmessages table clean up done, %d entries removed", ulDeleted);
  383. else
  384. ec_log_info("syncedmessages table clean up failed, error: 0x%08X, %d entries removed", er, ulDeleted);
  385. if(lpSession) {
  386. lpSession->Unlock(); // Lock the session
  387. g_lpSessionManager->RemoveSessionInternal(lpSession);
  388. }
  389. // ECScheduler does nothing with the returned value
  390. return NULL;
  391. }
  392. ECRESULT GetChanges(struct soap *soap, ECSession *lpSession, SOURCEKEY sFolderSourceKey, unsigned int ulSyncId, unsigned int ulChangeId, unsigned int ulChangeType, unsigned int ulFlags, struct restrictTable *lpsRestrict, unsigned int *lpulMaxChangeId, icsChangesArray **lppChanges){
  393. class sfree_delete {
  394. public:
  395. void operator()(void *x) { s_free(nullptr, x); }
  396. };
  397. unsigned int dummy;
  398. ECRESULT er = erSuccess;
  399. ECDatabase* lpDatabase = NULL;
  400. DB_RESULT lpDBResult;
  401. DB_ROW lpDBRow;
  402. DB_LENGTHS lpDBLen;
  403. unsigned int ulMaxChange = 0;
  404. unsigned int i = 0;
  405. unsigned int ulChanges = 0;
  406. std::string strQuery;
  407. unsigned int ulFolderId = 0;
  408. icsChangesArray*lpChanges = NULL;
  409. bool bAcceptABEID = false;
  410. unsigned int cbSourceKeyData = 0;
  411. list<unsigned int> lstFolderIds;
  412. // Contains a list of change IDs
  413. list<unsigned int> lstChanges;
  414. std::unique_ptr<ECGetContentChangesHelper, sfree_delete> lpHelper;
  415. ec_log(EC_LOGLEVEL_ICS, "GetChanges(): sourcekey=%s, syncid=%d, changetype=%d, flags=%d", bin2hex(sFolderSourceKey).c_str(), ulSyncId, ulChangeType, ulFlags);
  416. // Get database object
  417. er = lpSession->GetDatabase(&lpDatabase);
  418. if (er != erSuccess)
  419. goto exit;
  420. er = lpDatabase->Begin();
  421. if (er != erSuccess)
  422. goto exit;
  423. // CHeck if the client understands the new ABEID.
  424. if (lpSession->GetCapabilities() & KOPANO_CAP_MULTI_SERVER)
  425. bAcceptABEID = true;
  426. if(ulChangeType != ICS_SYNC_AB) {
  427. // You must first register your sync via setSyncStatus()
  428. if(ulSyncId == 0) {
  429. er = KCERR_INVALID_PARAMETER;
  430. goto exit;
  431. }
  432. // Add change key and predecessor change list
  433. if(ulChangeId > 0) {
  434. // Change ID > 0, which means we will be doing a real differential sync starting from the
  435. // given change ID.
  436. strQuery =
  437. "SELECT change_id,sourcekey,sync_type "
  438. "FROM syncs "
  439. "WHERE id=" + stringify(ulSyncId) + " FOR UPDATE";
  440. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  441. if(er != erSuccess)
  442. goto exit;
  443. if(lpDatabase->GetNumRows(lpDBResult) == 0){
  444. er = KCERR_NOT_FOUND;
  445. goto exit;
  446. }
  447. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  448. lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
  449. if( lpDBRow == NULL || lpDBRow[0] == NULL || lpDBRow[1] == NULL || lpDBRow[2] == NULL){
  450. er = KCERR_DATABASE_ERROR; // this should never happen
  451. ec_log_crit("%s:%d unexpected null pointer", __FUNCTION__, __LINE__);
  452. goto exit;
  453. }
  454. if((dummy = atoui(lpDBRow[2])) != ulChangeType){
  455. er = KCERR_COLLISION;
  456. ec_log_crit("GetChanges(): unexpected change type %u/%u", dummy, ulChangeType);
  457. goto exit;
  458. }
  459. } else {
  460. strQuery =
  461. "SELECT change_id,sourcekey "
  462. "FROM syncs "
  463. "WHERE id=" + stringify(ulSyncId) + " FOR UPDATE";
  464. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  465. if(er != erSuccess)
  466. goto exit;
  467. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  468. lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
  469. if( lpDBRow == NULL || lpDBRow[0] == NULL || lpDBRow[1] == NULL) {
  470. std::string username;
  471. er = lpSession->GetSecurity()->GetUsername(&username);
  472. er = KCERR_DATABASE_ERROR;
  473. ec_log_warn(
  474. "%s:%d The sync ID %u does not exist. "
  475. "(unexpected null pointer) "
  476. "session user name: %s.",
  477. __FUNCTION__, __LINE__, ulSyncId,
  478. username.c_str());
  479. goto exit;
  480. }
  481. }
  482. ulMaxChange = atoui(lpDBRow[0]);
  483. sFolderSourceKey = SOURCEKEY(lpDBLen[1], lpDBRow[1]);
  484. if(!sFolderSourceKey.empty()) {
  485. er = g_lpSessionManager->GetCacheManager()->GetObjectFromProp(PROP_ID(PR_SOURCE_KEY), sFolderSourceKey.size(), sFolderSourceKey, &ulFolderId);
  486. if(er != erSuccess)
  487. goto exit;
  488. } else {
  489. ulFolderId = 0;
  490. }
  491. if(ulFolderId == 0) {
  492. if(lpSession->GetSecurity()->GetAdminLevel() != ADMIN_LEVEL_SYSADMIN)
  493. er = KCERR_NO_ACCESS;
  494. } else {
  495. if (ulChangeType == ICS_SYNC_CONTENTS)
  496. er = lpSession->GetSecurity()->CheckPermission(ulFolderId, ecSecurityRead);
  497. else if (ulChangeType == ICS_SYNC_HIERARCHY)
  498. er = lpSession->GetSecurity()->CheckPermission(ulFolderId, ecSecurityFolderVisible);
  499. else
  500. er = KCERR_INVALID_TYPE;
  501. }
  502. if(er != erSuccess)
  503. goto exit;
  504. }
  505. if(ulChangeType == ICS_SYNC_CONTENTS){
  506. er = ECGetContentChangesHelper::Create(soap, lpSession,
  507. lpDatabase, sFolderSourceKey, ulSyncId, ulChangeId,
  508. ulFlags, lpsRestrict, &unique_tie(lpHelper));
  509. if (er != erSuccess)
  510. goto exit;
  511. er = lpHelper->QueryDatabase(&lpDBResult);
  512. if (er != erSuccess)
  513. goto exit;
  514. std::vector<DB_ROW> db_rows;
  515. std::vector<DB_LENGTHS> db_lengths;
  516. static constexpr unsigned int ncols = 7;
  517. unsigned long col_lengths[1000*ncols];
  518. unsigned int length_counter = 0;
  519. while (lpDBResult && (lpDBRow = lpDatabase->FetchRow(lpDBResult))) {
  520. lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
  521. if (lpDBLen == NULL)
  522. continue;
  523. memcpy(&col_lengths[length_counter*ncols], lpDBLen, ncols * sizeof(*col_lengths));
  524. lpDBLen = &col_lengths[length_counter*ncols];
  525. ++length_counter;
  526. if (lpDBRow[icsSourceKey] == NULL || lpDBRow[icsParentSourceKey] == NULL) {
  527. er = KCERR_DATABASE_ERROR;
  528. ec_log_crit("ECGetContentChangesHelper::ProcessRow(): row null");
  529. goto exit;
  530. }
  531. db_rows.push_back(lpDBRow);
  532. db_lengths.push_back(lpDBLen);
  533. if (db_rows.size() == 1000) {
  534. er = lpHelper->ProcessRows(db_rows, db_lengths);
  535. if (er != erSuccess)
  536. goto exit;
  537. db_rows.clear();
  538. db_lengths.clear();
  539. length_counter = 0;
  540. }
  541. }
  542. if (!db_rows.empty()) {
  543. er = lpHelper->ProcessRows(db_rows, db_lengths);
  544. if (er != erSuccess)
  545. goto exit;
  546. }
  547. er = lpHelper->ProcessResidualMessages();
  548. if (er != erSuccess)
  549. goto exit;
  550. er = lpHelper->Finalize(&ulMaxChange, &lpChanges);
  551. if (er != erSuccess)
  552. goto exit;
  553. // Do stuff with lppChanges etc.
  554. }else if(ulChangeType == ICS_SYNC_HIERARCHY){
  555. // We traverse the tree by just looking at the current hierarchy. This means we will not traverse into deleted
  556. // folders and changes within those deleted folders will therefore never reach whoever is requesting changes. In
  557. // practice this shouldn't matter because the folder above will be deleted correctly.
  558. lstFolderIds.push_back(ulFolderId);
  559. // Recursive loop through all folders
  560. for (auto folder_id : lstFolderIds) {
  561. if (folder_id == 0) {
  562. if(lpSession->GetSecurity()->GetAdminLevel() != ADMIN_LEVEL_SYSADMIN) {
  563. er = KCERR_NO_ACCESS;
  564. goto exit;
  565. }
  566. } else if (lpSession->GetSecurity()->CheckPermission(folder_id, ecSecurityFolderVisible) != erSuccess) {
  567. // We cannot traverse folders that we have no permission to. This means that changes under the inaccessible folder
  568. // will disappear - this will cause the sync peer to go out-of-sync. Fix would be to remember changes in security
  569. // as deletes and adds.
  570. continue;
  571. }
  572. if(ulChangeId != 0){
  573. std::unique_ptr<unsigned char[]> lpSourceKeyData;
  574. if (folder_id != 0 && g_lpSessionManager->GetCacheManager()->GetPropFromObject(PROP_ID(PR_SOURCE_KEY), folder_id, nullptr, &cbSourceKeyData, &unique_tie(lpSourceKeyData)) != erSuccess)
  575. continue; // Item is hard deleted?
  576. // Search folder changed folders
  577. strQuery = "SELECT changes.id "
  578. "FROM changes "
  579. "WHERE "
  580. " changes.id > " + stringify(ulChangeId) + // Change ID is N or later
  581. " AND changes.change_type >= " + stringify(ICS_FOLDER) + // query optimizer
  582. " AND changes.change_type & " + stringify(ICS_FOLDER) + " != 0" + // Change is a folder change
  583. " AND changes.sourcesync != " + stringify(ulSyncId);
  584. if (folder_id != 0)
  585. strQuery += " AND parentsourcekey=" + lpDatabase->EscapeBinary(lpSourceKeyData.get(), cbSourceKeyData);
  586. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  587. if(er != erSuccess)
  588. goto exit;
  589. while( (lpDBRow = lpDatabase->FetchRow(lpDBResult)) ){
  590. if( lpDBRow == NULL || lpDBRow[0] == NULL){
  591. er = KCERR_DATABASE_ERROR; // this should never happen
  592. ec_log_crit("%s:%d unexpected null pointer", __FUNCTION__, __LINE__);
  593. goto exit;
  594. }
  595. lstChanges.push_back(atoui(lpDBRow[0]));
  596. }
  597. }
  598. if (folder_id != 0) {
  599. // Get subfolders for recursion
  600. strQuery = "SELECT id FROM hierarchy WHERE parent = " + stringify(folder_id) + " AND type = " + stringify(MAPI_FOLDER) + " AND flags = " + stringify(FOLDER_GENERIC);
  601. if (ulFlags & SYNC_NO_SOFT_DELETIONS)
  602. strQuery += " AND hierarchy.flags & 1024 = 0";
  603. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  604. if(er != erSuccess)
  605. goto exit;
  606. while( (lpDBRow = lpDatabase->FetchRow(lpDBResult)) ){
  607. if( lpDBRow == NULL || lpDBRow[0] == NULL){
  608. er = KCERR_DATABASE_ERROR; // this should never happen
  609. ec_log_crit("%s:%d unexpected null pointer", __FUNCTION__, __LINE__);
  610. goto exit;
  611. }
  612. lstFolderIds.push_back(atoui(lpDBRow[0]));
  613. }
  614. }
  615. }
  616. lstChanges.sort();
  617. lstChanges.unique();
  618. // We now have both a list of all folders, and and list of changes.
  619. if(ulChangeId == 0) {
  620. if(ulFolderId == 0 && (ulFlags & SYNC_CATCHUP) == 0) {
  621. // Do not allow initial sync of all server folders
  622. er = KCERR_NO_SUPPORT;
  623. goto exit;
  624. }
  625. // New sync, just return all the folders as changes
  626. lpChanges = (icsChangesArray *)soap_malloc(soap, sizeof(icsChangesArray));
  627. lpChanges->__ptr = (icsChange *)soap_malloc(soap, sizeof(icsChange) * lstFolderIds.size());
  628. // Search folder changed folders
  629. strQuery = "SELECT MAX(id) FROM changes";
  630. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  631. if(er != erSuccess)
  632. goto exit;
  633. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  634. if( lpDBRow == NULL){
  635. er = KCERR_DATABASE_ERROR; // this should never happen
  636. ec_log_crit("%s:%d unexpected null pointer", __FUNCTION__, __LINE__);
  637. goto exit;
  638. }
  639. ulMaxChange = (lpDBRow[0] == NULL ? 0 : atoui(lpDBRow[0]));
  640. i = 0;
  641. if((ulFlags & SYNC_CATCHUP) == 0) {
  642. for (auto folder_id : lstFolderIds) {
  643. if (folder_id == ulFolderId)
  644. continue; // don't send the folder itself as a change
  645. strQuery = "SELECT sourcekey.val_binary, parentsourcekey.val_binary "
  646. "FROM hierarchy "
  647. "JOIN indexedproperties AS sourcekey ON hierarchy.id=sourcekey.hierarchyid AND sourcekey.tag=" + stringify(PROP_ID(PR_SOURCE_KEY)) + " "
  648. "JOIN indexedproperties AS parentsourcekey ON hierarchy.parent=parentsourcekey.hierarchyid AND parentsourcekey.tag=" + stringify(PROP_ID(PR_SOURCE_KEY)) + " "
  649. "WHERE hierarchy.id=" + stringify(folder_id) + " LIMIT 1";
  650. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  651. if(er != erSuccess)
  652. goto exit;
  653. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  654. lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
  655. if(lpDBRow == NULL || lpDBRow[0] == NULL || lpDBRow[1] == NULL)
  656. continue;
  657. lpChanges->__ptr[i].ulChangeId = ulMaxChange; // All items have the latest change ID because this is an initial sync
  658. lpChanges->__ptr[i].sSourceKey.__ptr = (unsigned char *)soap_malloc(soap, lpDBLen[0]);
  659. lpChanges->__ptr[i].sSourceKey.__size = lpDBLen[0];
  660. memcpy(lpChanges->__ptr[i].sSourceKey.__ptr, lpDBRow[0], lpDBLen[0]);
  661. lpChanges->__ptr[i].sParentSourceKey.__ptr = (unsigned char *)soap_malloc(soap, lpDBLen[1]);
  662. lpChanges->__ptr[i].sParentSourceKey.__size = lpDBLen[1];
  663. memcpy(lpChanges->__ptr[i].sParentSourceKey.__ptr, lpDBRow[1], lpDBLen[1]);
  664. lpChanges->__ptr[i].ulChangeType = ICS_FOLDER_NEW;
  665. lpChanges->__ptr[i].ulFlags = 0;
  666. ++i;
  667. }
  668. }
  669. lpChanges->__size = i;
  670. } else {
  671. // Return all the found changes
  672. lpChanges = (icsChangesArray *)soap_malloc(soap, sizeof(icsChangesArray));
  673. lpChanges->__ptr = (icsChange *)soap_malloc(soap, sizeof(icsChange) * lstChanges.size());
  674. lpChanges->__size = lstChanges.size();
  675. i = 0;
  676. for (auto chg_id : lstChanges) {
  677. strQuery = "SELECT changes.id, changes.sourcekey, changes.parentsourcekey, changes.change_type, changes.flags FROM changes WHERE changes.id=" + stringify(chg_id) + " LIMIT 1";
  678. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  679. if(er != erSuccess)
  680. goto exit;
  681. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  682. lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
  683. if(lpDBRow == NULL || lpDBRow[0] == NULL || lpDBRow[1] == NULL || lpDBRow[2] == NULL || lpDBRow[3] == NULL) {
  684. er = KCERR_DATABASE_ERROR;
  685. ec_log_crit("%s:%d unexpected null pointer", __FUNCTION__, __LINE__);
  686. goto exit;
  687. }
  688. lpChanges->__ptr[i].ulChangeId = atoui(lpDBRow[0]);
  689. if(lpChanges->__ptr[i].ulChangeId > ulMaxChange)
  690. ulMaxChange = lpChanges->__ptr[i].ulChangeId;
  691. lpChanges->__ptr[i].sSourceKey.__ptr = (unsigned char *)soap_malloc(soap, lpDBLen[1]);
  692. lpChanges->__ptr[i].sSourceKey.__size = lpDBLen[1];
  693. memcpy(lpChanges->__ptr[i].sSourceKey.__ptr, lpDBRow[1], lpDBLen[1]);
  694. lpChanges->__ptr[i].sParentSourceKey.__ptr = (unsigned char *)soap_malloc(soap, lpDBLen[2]);
  695. lpChanges->__ptr[i].sParentSourceKey.__size = lpDBLen[2];
  696. memcpy(lpChanges->__ptr[i].sParentSourceKey.__ptr, lpDBRow[2], lpDBLen[2]);
  697. lpChanges->__ptr[i].ulChangeType = atoui(lpDBRow[3]);
  698. if (lpDBRow[4] != nullptr)
  699. lpChanges->__ptr[i].ulFlags = atoui(lpDBRow[4]);
  700. else
  701. lpChanges->__ptr[i].ulFlags = 0;
  702. ++i;
  703. }
  704. lpChanges->__size = i;
  705. }
  706. } else if(ulChangeType == ICS_SYNC_AB) {
  707. // filter on the current logged on company (0 when non-hosted server)
  708. // since we need to filter, we can't filter on "viewable
  709. // companies" because after updating the allowed viewable, we
  710. // won't see the changes as newly created anymore, and thus
  711. // still don't have that company in the offline gab.
  712. unsigned int ulCompanyId = 0;
  713. // returns 0 for KOPANO_UID_SYSTEM on hosted environments, and then the filter correctly doesn't filter anything.
  714. lpSession->GetSecurity()->GetUserCompany(&ulCompanyId);
  715. if(ulChangeId > 0) {
  716. std::set<unsigned int> sUserIds;
  717. ABChangeRecordList lstChanges;
  718. // sourcekey is actually an entryid .. don't let the naming confuse you
  719. strQuery = "SELECT id, sourcekey, parentsourcekey, change_type FROM abchanges WHERE change_type & " + stringify(ICS_AB) + " AND id > " + stringify(ulChangeId) + " ORDER BY id";
  720. er = lpDatabase->DoSelect(strQuery, &lpDBResult, true);
  721. if(er != erSuccess)
  722. goto exit;
  723. while ((lpDBRow = lpDatabase->FetchRow(lpDBResult)) != NULL) {
  724. lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
  725. if (lpDBRow == NULL || lpDBRow[0] == NULL || lpDBRow[1] == NULL || lpDBRow[2] == NULL || lpDBRow[3] == NULL) {
  726. er = KCERR_DATABASE_ERROR; // this should never happen
  727. ec_log_crit("%s:%d unexpected null pointer", __FUNCTION__, __LINE__);
  728. goto exit;
  729. }
  730. if (lpDBLen[1] < CbNewABEID("")) {
  731. er = KCERR_DATABASE_ERROR; // this should never happen
  732. ec_log_crit("%s:%d invalid size for ab entryid %lu", __FUNCTION__, __LINE__, static_cast<unsigned long>(lpDBLen[1]));
  733. goto exit;
  734. }
  735. lstChanges.push_back(ABChangeRecord(atoui(lpDBRow[0]), std::string(lpDBRow[1], lpDBLen[1]), std::string(lpDBRow[2], lpDBLen[2]), atoui(lpDBRow[3])));
  736. sUserIds.insert(reinterpret_cast<ABEID *>(lpDBRow[1])->ulId);
  737. }
  738. if (!sUserIds.empty() && ulCompanyId != 0 && (lpSession->GetCapabilities() & KOPANO_CAP_MAX_ABCHANGEID)) {
  739. er = FilterUserIdsByCompany(lpDatabase, sUserIds, ulCompanyId, &sUserIds);
  740. if (er != erSuccess)
  741. goto exit;
  742. }
  743. // We'll reserve enough space for all the changes. We just might not use it all
  744. ulChanges = lstChanges.size();
  745. lpChanges = (icsChangesArray *)soap_malloc(soap, sizeof(icsChangesArray));
  746. lpChanges->__ptr = (icsChange *)soap_malloc(soap, sizeof(icsChange) * ulChanges);
  747. lpChanges->__size = 0;
  748. memset(lpChanges->__ptr, 0, sizeof(icsChange) * ulChanges);
  749. i=0;
  750. for (const auto &iter : lstChanges) {
  751. unsigned int ulUserId = reinterpret_cast<const ABEID *>(iter.strItem.data())->ulId;
  752. if (iter.change_type != ICS_AB_DELETE && sUserIds.find(ulUserId) == sUserIds.end())
  753. continue;
  754. er = ConvertABEntryIDToSoapSourceKey(soap, lpSession, bAcceptABEID, iter.strItem.size(), const_cast<char *>(iter.strItem.data()), &lpChanges->__ptr[i].sSourceKey);
  755. if (er != erSuccess) {
  756. er = erSuccess;
  757. continue;
  758. }
  759. lpChanges->__ptr[i].ulChangeId = iter.id;
  760. lpChanges->__ptr[i].sParentSourceKey.__ptr = reinterpret_cast<unsigned char *>(s_memcpy(soap, iter.strParent.data(), iter.strParent.size()));
  761. lpChanges->__ptr[i].sParentSourceKey.__size = iter.strParent.size();
  762. lpChanges->__ptr[i].ulChangeType = iter.change_type;
  763. if (iter.id > ulMaxChange)
  764. ulMaxChange = iter.id;
  765. ++i;
  766. }
  767. lpChanges->__size = i;
  768. } else {
  769. // During initial sync, just get all the current users from the database and output them as ICS_AB_NEW changes
  770. ABEID eid(MAPI_ABCONT, MUIDECSAB, 1);
  771. strQuery = "SELECT max(id) FROM abchanges";
  772. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  773. if (er != erSuccess)
  774. goto exit;
  775. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  776. if (lpDBRow == nullptr || lpDBRow[0] == nullptr)
  777. // You *should* always have something there if you have more than 0 users. However, just to make us compatible with
  778. // people truncating the table and then doing a resync, we'll go to change ID 0. This means that at the next sync,
  779. // we will do another full sync. We can't really fix that at the moment since going to change ID 1 would be even worse,
  780. // since it would skip the first change in the table. This could cause the problem that deletes after the first initial
  781. // sync and before the second would skip any deletes. This is acceptable for now since it is rare to do this, and it's
  782. // better than any other alternative.
  783. ulMaxChange = 0;
  784. else
  785. ulMaxChange = atoui(lpDBRow[0]);
  786. // Skip 'everyone', 'system' and users outside our company space, except when we're system
  787. strQuery = "SELECT id, objectclass, externid FROM users WHERE id not in (1,2)";
  788. if (lpSession->GetSecurity()->GetUserId() != KOPANO_UID_SYSTEM)
  789. strQuery += " AND company = " + stringify(ulCompanyId);
  790. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  791. if (er != erSuccess)
  792. goto exit;
  793. ulChanges = lpDatabase->GetNumRows(lpDBResult);
  794. lpChanges = (icsChangesArray *)soap_malloc(soap, sizeof(icsChangesArray));
  795. lpChanges->__ptr = (icsChange *)soap_malloc(soap, sizeof(icsChange) * ulChanges);
  796. lpChanges->__size = 0;
  797. memset(lpChanges->__ptr, 0, sizeof(icsChange) * ulChanges);
  798. i=0;
  799. while(1) {
  800. objectid_t id;
  801. unsigned int ulType;
  802. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  803. lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
  804. if(lpDBRow == NULL)
  805. break;
  806. if(lpDBRow[0] == NULL || lpDBRow[1] == NULL || lpDBRow[2] == NULL) {
  807. er = KCERR_DATABASE_ERROR;
  808. ec_log_crit("%s:%d unexpected null pointer", __FUNCTION__, __LINE__);
  809. goto exit;
  810. }
  811. id.id.assign(lpDBRow[2], lpDBLen[2]);
  812. id.objclass = (objectclass_t)atoui(lpDBRow[1]);
  813. er = TypeToMAPIType((objectclass_t)atoui(lpDBRow[1]), (ULONG *)&ulType);
  814. if(er != erSuccess)
  815. goto exit;
  816. lpChanges->__ptr[i].ulChangeId = ulMaxChange;
  817. er = lpSession->GetUserManagement()->CreateABEntryID(soap,
  818. bAcceptABEID ? 1 : 0, atoui(lpDBRow[0]), ulType, &id,
  819. &lpChanges->__ptr[i].sSourceKey.__size,
  820. reinterpret_cast<ABEID **>(&lpChanges->__ptr[i].sSourceKey.__ptr));
  821. if (er != erSuccess)
  822. goto exit;
  823. lpChanges->__ptr[i].sParentSourceKey.__size = sizeof(eid);
  824. lpChanges->__ptr[i].sParentSourceKey.__ptr = s_alloc<unsigned char>(soap, sizeof(eid));
  825. memcpy(lpChanges->__ptr[i].sParentSourceKey.__ptr, &eid, sizeof(eid));
  826. lpChanges->__ptr[i].ulChangeType = ICS_AB_NEW;
  827. ++i;
  828. }
  829. lpChanges->__size = i;
  830. }
  831. }
  832. er = lpDatabase->Commit();
  833. if (er != erSuccess)
  834. goto exit;
  835. *lpulMaxChangeId = ulMaxChange;
  836. *lppChanges = lpChanges;
  837. exit:
  838. if (lpDatabase && er != erSuccess)
  839. lpDatabase->Rollback();
  840. return er;
  841. }
  842. ECRESULT AddABChange(BTSession *lpSession, unsigned int ulChange, SOURCEKEY sSourceKey, SOURCEKEY sParentSourceKey)
  843. {
  844. ECRESULT er;
  845. std::string strQuery;
  846. ECDatabase* lpDatabase = NULL;
  847. er = lpSession->GetDatabase(&lpDatabase);
  848. if (er != erSuccess)
  849. return er;
  850. // Add/Replace new change
  851. strQuery = "REPLACE INTO abchanges (sourcekey, parentsourcekey, change_type) VALUES(" + lpDatabase->EscapeBinary(sSourceKey, sSourceKey.size()) + "," + lpDatabase->EscapeBinary(sParentSourceKey, sParentSourceKey.size()) + "," + stringify(ulChange) + ")";
  852. return lpDatabase->DoInsert(strQuery);
  853. }
  854. ECRESULT GetSyncStates(struct soap *soap, ECSession *lpSession, mv_long ulaSyncId, syncStateArray *lpsaSyncState)
  855. {
  856. ECRESULT er = erSuccess;
  857. std::string strQuery;
  858. ECDatabase* lpDatabase = NULL;
  859. DB_RESULT lpDBResult;
  860. unsigned int ulResults = 0;
  861. DB_ROW lpDBRow;
  862. if (ulaSyncId.__size == 0) {
  863. memset(lpsaSyncState, 0, sizeof *lpsaSyncState);
  864. return erSuccess;
  865. }
  866. er = lpSession->GetDatabase(&lpDatabase);
  867. if (er != erSuccess)
  868. return er;
  869. strQuery = "SELECT id,change_id FROM syncs WHERE id IN (" + stringify(ulaSyncId.__ptr[0]);
  870. for (gsoap_size_t i = 1; i < ulaSyncId.__size; ++i)
  871. strQuery += "," + stringify(ulaSyncId.__ptr[i]);
  872. strQuery += ")";
  873. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  874. if (er != erSuccess)
  875. return er;
  876. ulResults = lpDatabase->GetNumRows(lpDBResult);
  877. if (ulResults == 0){
  878. memset(lpsaSyncState, 0, sizeof *lpsaSyncState);
  879. return erSuccess;
  880. }
  881. lpsaSyncState->__size = 0;
  882. lpsaSyncState->__ptr = s_alloc<syncState>(soap, ulResults);
  883. while ((lpDBRow = lpDatabase->FetchRow(lpDBResult)) != NULL) {
  884. if (lpDBRow[0] == NULL || lpDBRow[1] == NULL) {
  885. ec_log_crit("%s:%d unexpected null pointer", __FUNCTION__, __LINE__);
  886. return KCERR_DATABASE_ERROR;
  887. }
  888. lpsaSyncState->__ptr[lpsaSyncState->__size].ulSyncId = atoui(lpDBRow[0]);
  889. lpsaSyncState->__ptr[lpsaSyncState->__size++].ulChangeId = atoui(lpDBRow[1]);
  890. }
  891. assert(lpsaSyncState->__size == ulResults);
  892. return erSuccess;
  893. }
  894. ECRESULT AddToLastSyncedMessagesSet(ECDatabase *lpDatabase, unsigned int ulSyncId, const SOURCEKEY &sSourceKey, const SOURCEKEY &sParentSourceKey)
  895. {
  896. ECRESULT er = erSuccess;
  897. std::string strQuery;
  898. DB_RESULT lpDBResult;
  899. DB_ROW lpDBRow;
  900. strQuery = "SELECT MAX(change_id) FROM syncedmessages WHERE sync_id=" + stringify(ulSyncId);
  901. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  902. if (er != erSuccess)
  903. return er;
  904. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  905. if (lpDBRow == NULL) {
  906. ec_log_crit("%s:%d unexpected null pointer", __FUNCTION__, __LINE__);
  907. return KCERR_DATABASE_ERROR;
  908. }
  909. if (lpDBRow[0] == NULL) // none set for this sync id.
  910. return erSuccess;
  911. strQuery = "INSERT INTO syncedmessages (sync_id,change_id,sourcekey,parentsourcekey) VALUES (" +
  912. stringify(ulSyncId) + "," +
  913. lpDBRow[0] + "," +
  914. lpDatabase->EscapeBinary(sSourceKey, sSourceKey.size()) + "," +
  915. lpDatabase->EscapeBinary(sParentSourceKey, sParentSourceKey.size()) + ")";
  916. return lpDatabase->DoInsert(strQuery);
  917. }
  918. /**
  919. * Check if the object to remove is in the sync set. If it is outside,
  920. * we don't need to remove it at all.
  921. *
  922. * @param lpDatabase Database
  923. * @param ulSyncId sync id, must be != 0
  924. * @param sSourceKey sourcekey of the object
  925. *
  926. * @return Kopano error code
  927. */
  928. ECRESULT CheckWithinLastSyncedMessagesSet(ECDatabase *lpDatabase, unsigned int ulSyncId, const SOURCEKEY &sSourceKey)
  929. {
  930. ECRESULT er = erSuccess;
  931. std::string strQuery;
  932. DB_RESULT lpDBResult;
  933. DB_ROW lpDBRow;
  934. strQuery = "SELECT MAX(change_id) FROM syncedmessages WHERE sync_id=" + stringify(ulSyncId);
  935. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  936. if (er != erSuccess)
  937. return er;
  938. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  939. if (lpDBRow == NULL) {
  940. ec_log_crit("%s:%d unexpected null pointer", __FUNCTION__, __LINE__);
  941. return KCERR_DATABASE_ERROR;
  942. }
  943. if (lpDBRow[0] == NULL) // none set for this sync id, not an error
  944. return erSuccess;
  945. // check if the delete would remove the message
  946. strQuery = "SELECT 0 FROM syncedmessages WHERE sync_id=" + stringify(ulSyncId) +
  947. " AND change_id=" + lpDBRow[0] +
  948. " AND sourcekey=" + lpDatabase->EscapeBinary(sSourceKey, sSourceKey.size()) + " LIMIT 1";
  949. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  950. if (er != erSuccess)
  951. return er;
  952. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  953. if (lpDBRow == NULL)
  954. return KCERR_NOT_FOUND;
  955. return erSuccess;
  956. }
  957. ECRESULT RemoveFromLastSyncedMessagesSet(ECDatabase *lpDatabase, unsigned int ulSyncId, const SOURCEKEY &sSourceKey, const SOURCEKEY &sParentSourceKey)
  958. {
  959. ECRESULT er = erSuccess;
  960. std::string strQuery;
  961. DB_RESULT lpDBResult;
  962. DB_ROW lpDBRow;
  963. strQuery = "SELECT MAX(change_id) FROM syncedmessages WHERE sync_id=" + stringify(ulSyncId);
  964. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  965. if (er != erSuccess)
  966. return er;
  967. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  968. if (lpDBRow == NULL) {
  969. ec_log_crit("RemoveFromLastSyncedMessagesSet(): fetchrow return null");
  970. return KCERR_DATABASE_ERROR;
  971. }
  972. if (lpDBRow[0] == NULL) // none set for this sync id.
  973. return erSuccess;
  974. strQuery = "DELETE FROM syncedmessages WHERE sync_id=" + stringify(ulSyncId) +
  975. " AND change_id=" + lpDBRow[0] +
  976. " AND sourcekey=" + lpDatabase->EscapeBinary(sSourceKey, sSourceKey.size());
  977. return lpDatabase->DoDelete(strQuery);
  978. }
  979. } /* namespace */