ECGenericObjectTable.cpp 104 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154
  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/lockhelper.hpp>
  20. #include <kopano/tie.hpp>
  21. /* Returns the rows for a contents- or hierarchytable
  22. *
  23. * objtype == MAPI_MESSAGE, then contents table
  24. * objtype == MAPI_MESSAGE, flags == MAPI_ASSOCIATED, then associated contents table
  25. * objtype == MAPI_FOLDER, then hierarchy table
  26. *
  27. * Tables are generated from SQL in the following way:
  28. *
  29. * Tables are constructed by joining the hierarchy table with the property table multiple
  30. * times, once for each requested property (column). Because each column of each row can always have
  31. * only one or zero records, a unique index is created on the property table, indexing (hierarchyid, type, tag).
  32. *
  33. * This means that for each cell that we request, the index needs to be accessed by the SQL
  34. * engine only once, which makes the actual query extremely fast.
  35. *
  36. * In tests, this has shown to required around 60ms for 30 rows and 10 columns from a table of 10000
  37. * rows. Also, this is a O(n log n) operation and therefore not prone to large scaling problems. (Yay!)
  38. * (with respect to the amount of columns, it is O(n), but that's quite constant, and so is the
  39. * actual amount of rows requested per query (also O(n)).
  40. *
  41. */
  42. #include "soapH.h"
  43. #include <kopano/kcodes.h>
  44. #include <mapidefs.h>
  45. #include <mapitags.h>
  46. #include <kopano/mapiext.h>
  47. #include <sys/types.h>
  48. #if 1 /* change to HAVE_REGEX_H */
  49. #include <regex.h>
  50. #endif
  51. #include <iostream>
  52. #include "kcore.hpp"
  53. #include "pcutil.hpp"
  54. #include "ECSecurity.h"
  55. #include "ECDatabaseUtils.h"
  56. #include <kopano/ECKeyTable.h>
  57. #include "ECGenProps.h"
  58. #include "ECGenericObjectTable.h"
  59. #include "SOAPUtils.h"
  60. #include <kopano/stringutil.h>
  61. #include <kopano/Trace.h>
  62. #include "ECSessionManager.h"
  63. #include "ECSession.h"
  64. using namespace KCHL;
  65. namespace KC {
  66. static struct sortOrderArray sDefaultSortOrder{__gszeroinit};
  67. static const ULONG sANRProps[] = {
  68. PR_DISPLAY_NAME, PR_SMTP_ADDRESS, PR_ACCOUNT, PR_DEPARTMENT_NAME,
  69. PR_OFFICE_TELEPHONE_NUMBER, PR_OFFICE_LOCATION, PR_PRIMARY_FAX_NUMBER,
  70. PR_SURNAME
  71. };
  72. #define ISMINMAX(x) ((x) == EC_TABLE_SORT_CATEG_MIN || (x) == EC_TABLE_SORT_CATEG_MAX)
  73. /**
  74. * Apply RELOP_* rules to equality value from CompareProp
  75. *
  76. * 'equality' is a value from CompareProp which can be -1, 0 or 1. This function
  77. * returns TRUE when the passed relop matches the equality value. (Eg equality=0
  78. * and RELOP_EQ, then returns TRUE)
  79. * @param relop RELOP
  80. * @param equality Equality value from CompareProp
  81. * @return TRUE if the relop matches
  82. */
  83. static inline bool match(unsigned int relop, int equality)
  84. {
  85. bool fMatch = false;
  86. switch(relop) {
  87. case RELOP_GE:
  88. fMatch = equality >= 0;
  89. break;
  90. case RELOP_GT:
  91. fMatch = equality > 0;
  92. break;
  93. case RELOP_LE:
  94. fMatch = equality <= 0;
  95. break;
  96. case RELOP_LT:
  97. fMatch = equality < 0;
  98. break;
  99. case RELOP_NE:
  100. fMatch = equality != 0;
  101. break;
  102. case RELOP_RE:
  103. fMatch = false; // FIXME ?? how should this work ??
  104. break;
  105. case RELOP_EQ:
  106. fMatch = equality == 0;
  107. break;
  108. }
  109. return fMatch;
  110. }
  111. /**
  112. * Constructor of the Generic Object Table
  113. *
  114. * @param[in] lpSession
  115. * Reference to a session object; cannot be NULL.
  116. * @param[in] ulObjType
  117. * The Object type of the objects in the table
  118. */
  119. ECGenericObjectTable::ECGenericObjectTable(ECSession *lpSession,
  120. unsigned int ulObjType, unsigned int ulFlags, const ECLocale &locale) :
  121. m_ulObjType(ulObjType), m_ulFlags(ulFlags), m_locale(locale)
  122. {
  123. this->lpSession = lpSession;
  124. this->lpKeyTable = new ECKeyTable;
  125. // No columns by default
  126. this->lpsPropTagArray = s_alloc<propTagArray>(nullptr);
  127. this->lpsPropTagArray->__size = 0;
  128. this->lpsPropTagArray->__ptr = NULL;
  129. }
  130. /**
  131. * Destructor of the Generic Object Table
  132. */
  133. ECGenericObjectTable::~ECGenericObjectTable()
  134. {
  135. delete lpKeyTable;
  136. if(this->lpsPropTagArray)
  137. FreePropTagArray(this->lpsPropTagArray);
  138. if(this->lpsSortOrderArray)
  139. FreeSortOrderArray(this->lpsSortOrderArray);
  140. if(this->lpsRestrict)
  141. FreeRestrictTable(this->lpsRestrict);
  142. for (const auto &p : m_mapCategories)
  143. delete p.second;
  144. }
  145. /**
  146. * Moves the cursor to a specific row in the table.
  147. *
  148. * @param[in] ulBookmark
  149. * Identifying the starting position for the seek action. A bookmark can be created with
  150. * ECGenericObjectTable::CreateBookmark call, or use one of the following bookmark predefines:
  151. * \arg BOOKMARK_BEGINNING Start seeking from the beginning of the table.
  152. * \arg BOOKMARK_CURRENT Start seeking from the current position of the table.
  153. * \arg BOOKMARK_END Start seeking from the end of the table.
  154. * @param[in] lSeekTo
  155. * Positive or negative number of rows moved starting from the bookmark.
  156. * @param[in] lplRowsSought
  157. * Pointer to the number or rows that were processed in the seek action. If lplRowsSought is NULL,
  158. * the caller iss not interested in the returned output.
  159. *
  160. * @return Kopano error code
  161. */
  162. ECRESULT ECGenericObjectTable::SeekRow(unsigned int ulBookmark, int lSeekTo, int *lplRowsSought)
  163. {
  164. scoped_rlock biglock(m_hLock);
  165. ECRESULT er = Populate();
  166. if(er != erSuccess)
  167. return er;
  168. if(lpsSortOrderArray == NULL) {
  169. er = SetSortOrder(&sDefaultSortOrder, 0, 0);
  170. if(er != erSuccess)
  171. return er;
  172. }
  173. return lpKeyTable->SeekRow(ulBookmark, lSeekTo, lplRowsSought);
  174. }
  175. /**
  176. * Finds the next row in the table that matches specific search criteria.
  177. *
  178. *
  179. * @param[in] lpsRestrict
  180. * @param[in] ulBookmark
  181. * @param[in] ulFlags
  182. *
  183. * @return Kopano error code
  184. */
  185. ECRESULT ECGenericObjectTable::FindRow(struct restrictTable *lpsRestrict, unsigned int ulBookmark, unsigned int ulFlags)
  186. {
  187. bool fMatch = false;
  188. int ulSeeked = 0;
  189. unsigned int ulRow = 0;
  190. unsigned int ulCount = 0;
  191. int ulTraversed = 0;
  192. SUBRESTRICTIONRESULTS *lpSubResults = NULL;
  193. struct propTagArray *lpPropTags = NULL;
  194. struct rowSet *lpRowSet = NULL;
  195. ECObjectTableList ecRowList;
  196. sObjectTableKey sRowItem;
  197. entryId sEntryId;
  198. ulock_rec biglock(m_hLock);
  199. ECRESULT er = Populate();
  200. if(er != erSuccess)
  201. goto exit;
  202. /* We may need the table position later (ulCount is not used) */
  203. er = lpKeyTable->GetRowCount(&ulCount, &ulRow);
  204. if (er != erSuccess)
  205. goto exit;
  206. // Start searching at the right place
  207. if (ulBookmark == BOOKMARK_END && ulFlags & DIR_BACKWARD)
  208. er = SeekRow(ulBookmark, -1, NULL);
  209. else
  210. er = SeekRow(ulBookmark, 0, NULL);
  211. if (er != erSuccess)
  212. goto exit;
  213. // Special optimisation case: if you're searching the PR_INSTANCE_KEY, we can
  214. // look this up directly!
  215. if( ulBookmark == BOOKMARK_BEGINNING &&
  216. lpsRestrict->ulType == RES_PROPERTY && lpsRestrict->lpProp->ulType == RELOP_EQ &&
  217. lpsRestrict->lpProp->lpProp && lpsRestrict->lpProp->ulPropTag == PR_INSTANCE_KEY &&
  218. lpsRestrict->lpProp->lpProp->ulPropTag == PR_INSTANCE_KEY &&
  219. lpsRestrict->lpProp->lpProp->Value.bin && lpsRestrict->lpProp->lpProp->Value.bin->__size == sizeof(unsigned int)*2)
  220. {
  221. sRowItem.ulObjId = *(unsigned int *)lpsRestrict->lpProp->lpProp->Value.bin->__ptr;
  222. sRowItem.ulOrderId = *(unsigned int *)(lpsRestrict->lpProp->lpProp->Value.bin->__ptr+sizeof(LONG));
  223. er = this->lpKeyTable->SeekId(&sRowItem);
  224. goto exit;
  225. }
  226. // We can do the same with PR_ENTRYID
  227. if( ulBookmark == BOOKMARK_BEGINNING &&
  228. lpsRestrict->ulType == RES_PROPERTY && lpsRestrict->lpProp->ulType == RELOP_EQ &&
  229. lpsRestrict->lpProp->lpProp && lpsRestrict->lpProp->ulPropTag == PR_ENTRYID &&
  230. lpsRestrict->lpProp->lpProp->ulPropTag == PR_ENTRYID &&
  231. lpsRestrict->lpProp->lpProp->Value.bin && IsKopanoEntryId(lpsRestrict->lpProp->lpProp->Value.bin->__size, lpsRestrict->lpProp->lpProp->Value.bin->__ptr))
  232. {
  233. sEntryId.__ptr = lpsRestrict->lpProp->lpProp->Value.bin->__ptr;
  234. sEntryId.__size = lpsRestrict->lpProp->lpProp->Value.bin->__size;
  235. er = lpSession->GetSessionManager()->GetCacheManager()->GetObjectFromEntryId(&sEntryId, &sRowItem.ulObjId);
  236. if(er != erSuccess)
  237. goto exit;
  238. sRowItem.ulOrderId = 0; // FIXME: this is incorrect when MV_INSTANCE is specified on a column, but this won't happen often.
  239. er = this->lpKeyTable->SeekId(&sRowItem);
  240. goto exit;
  241. }
  242. // Get the columns we will be needing for this search
  243. er = GetRestrictPropTags(lpsRestrict, NULL, &lpPropTags);
  244. if(er != erSuccess)
  245. goto exit;
  246. // Loop through the rows, matching it with the search criteria
  247. while(1) {
  248. ecRowList.clear();
  249. // Get the row ID of the next row
  250. er = lpKeyTable->QueryRows(20, &ecRowList, (ulFlags & DIR_BACKWARD)?true:false, TBL_NOADVANCE);
  251. if(er != erSuccess)
  252. goto exit;
  253. if(ecRowList.empty())
  254. break;
  255. // Get the rowdata from the QueryRowData function
  256. er = m_lpfnQueryRowData(this, NULL, lpSession, &ecRowList, lpPropTags, m_lpObjectData, &lpRowSet, true, false);
  257. if(er != erSuccess)
  258. goto exit;
  259. er = RunSubRestrictions(lpSession, m_lpObjectData, lpsRestrict, &ecRowList, m_locale, &lpSubResults);
  260. if(er != erSuccess)
  261. goto exit;
  262. assert(lpRowSet->__size == static_cast<gsoap_size_t>(ecRowList.size()));
  263. for (gsoap_size_t i = 0; i < lpRowSet->__size; ++i) {
  264. // Match the row
  265. er = MatchRowRestrict(lpSession->GetSessionManager()->GetCacheManager(), &lpRowSet->__ptr[i], lpsRestrict, lpSubResults, m_locale, &fMatch);
  266. if(er != erSuccess)
  267. goto exit;
  268. if(fMatch)
  269. {
  270. // A Match, seek the cursor
  271. lpKeyTable->SeekRow(BOOKMARK_CURRENT, ulFlags & DIR_BACKWARD ? -i : i, &ulSeeked);
  272. break;
  273. }
  274. }
  275. if(fMatch)
  276. break;
  277. // No match, then advance the cursor
  278. lpKeyTable->SeekRow(BOOKMARK_CURRENT, ulFlags & DIR_BACKWARD ? -(int)ecRowList.size() : ecRowList.size(), &ulSeeked);
  279. // No advance possible, break the loop
  280. if(ulSeeked == 0)
  281. break;
  282. // Free memory
  283. FreeRowSet(lpRowSet, true);
  284. lpRowSet = NULL;
  285. if(lpSubResults)
  286. FreeSubRestrictionResults(lpSubResults);
  287. lpSubResults = NULL;
  288. }
  289. if(!fMatch) {
  290. er = KCERR_NOT_FOUND;
  291. lpKeyTable->SeekRow(ECKeyTable::EC_SEEK_SET, ulRow, &ulTraversed);
  292. }
  293. exit:
  294. biglock.unlock();
  295. if(lpSubResults)
  296. FreeSubRestrictionResults(lpSubResults);
  297. if(lpRowSet)
  298. FreeRowSet(lpRowSet, true);
  299. if(lpPropTags)
  300. FreePropTagArray(lpPropTags);
  301. return er;
  302. }
  303. /**
  304. * Returns the total number of rows in the table.
  305. *
  306. * @param[out] lpulRowCount
  307. * Pointer to the number of rows in the table.
  308. * @param[out] lpulCurrentRow
  309. * Pointer to the current row id in the table.
  310. *
  311. * @return Kopano error code
  312. */
  313. ECRESULT ECGenericObjectTable::GetRowCount(unsigned int *lpulRowCount, unsigned int *lpulCurrentRow)
  314. {
  315. scoped_rlock biglock(m_hLock);
  316. ECRESULT er = Populate();
  317. if(er != erSuccess)
  318. return er;
  319. if(lpsSortOrderArray == NULL) {
  320. er = SetSortOrder(&sDefaultSortOrder, 0, 0);
  321. if(er != erSuccess)
  322. return er;
  323. }
  324. return lpKeyTable->GetRowCount(lpulRowCount, lpulCurrentRow);
  325. }
  326. ECRESULT ECGenericObjectTable::ReloadTableMVData(ECObjectTableList* lplistRows, ECListInt* lplistMVPropTag)
  327. {
  328. // default ignore MV-view, show as an normal view
  329. return erSuccess;
  330. }
  331. /**
  332. * Returns a list of columns for the table.
  333. *
  334. * If the function is not overridden, it returns always an empty column set.
  335. *
  336. * @param[in,out] lplstProps
  337. * a list of columns for the table
  338. *
  339. * @return Kopano error code
  340. */
  341. ECRESULT ECGenericObjectTable::GetColumnsAll(ECListInt* lplstProps)
  342. {
  343. return erSuccess;
  344. }
  345. /**
  346. * Reload the table objects.
  347. *
  348. * Rebuild the whole table with the current restriction and sort order. If the sort order
  349. * includes a multi-valued property, a single row appearing in multiple rows. ReloadTable
  350. * may expand or contract expanded MVI rows if the sort order or column set have changed. If there
  351. * is no change in MVI-related expansion, it will call ReloadKeyTable which only does a
  352. * resort/refilter of the existing rows.
  353. *
  354. * @param[in] eType
  355. * The reload type determines how it should reload.
  356. *
  357. * @return Kopano error code
  358. */
  359. ECRESULT ECGenericObjectTable::ReloadTable(enumReloadType eType)
  360. {
  361. ECRESULT er = erSuccess;
  362. bool bMVColsNew = false;
  363. bool bMVSortNew = false;
  364. ECObjectTableList listRows;
  365. ECListInt listMVPropTag;
  366. scoped_rlock biglock(m_hLock);
  367. //Scan for MVI columns
  368. for (gsoap_size_t i = 0; lpsPropTagArray != NULL && i < lpsPropTagArray->__size; ++i) {
  369. if ((PROP_TYPE(lpsPropTagArray->__ptr[i]) &MVI_FLAG) != MVI_FLAG)
  370. continue;
  371. if (bMVColsNew == true)
  372. assert(false); //FIXME: error 1 mv prop set!!!
  373. bMVColsNew = true;
  374. listMVPropTag.push_back(lpsPropTagArray->__ptr[i]);
  375. }
  376. //Check for mvi props
  377. for (gsoap_size_t i = 0; lpsSortOrderArray != NULL && i < lpsSortOrderArray->__size; ++i) {
  378. if ((PROP_TYPE(lpsSortOrderArray->__ptr[i].ulPropTag) & MVI_FLAG) != MVI_FLAG)
  379. continue;
  380. if (bMVSortNew == true)
  381. assert(false);
  382. bMVSortNew = true;
  383. listMVPropTag.push_back(lpsSortOrderArray->__ptr[i].ulPropTag);
  384. }
  385. listMVPropTag.sort();
  386. listMVPropTag.unique();
  387. if((m_bMVCols == false && m_bMVSort == false && bMVColsNew == false && bMVSortNew == false) ||
  388. (listMVPropTag == m_listMVSortCols && (m_bMVCols == bMVColsNew || m_bMVSort == bMVSortNew)) )
  389. {
  390. if(eType == RELOAD_TYPE_SORTORDER)
  391. er = ReloadKeyTable();
  392. /* No MVprops or already sorted, skip MV sorts. */
  393. return er;
  394. }
  395. m_listMVSortCols = listMVPropTag;
  396. // Get all the Single Row IDs from the ID map
  397. for (const auto &p : mapObjects)
  398. if (p.first.ulOrderId == 0)
  399. listRows.push_back(p.first);
  400. if(mapObjects.empty())
  401. goto skip;
  402. if(bMVColsNew == true || bMVSortNew == true)
  403. {
  404. // Expand rows to contain all MVI expansions (listRows is appended to)
  405. er = ReloadTableMVData(&listRows, &listMVPropTag);
  406. if(er != erSuccess)
  407. return er;
  408. }
  409. // Clear row data
  410. Clear();
  411. //Add items
  412. for (const auto &row : listRows)
  413. mapObjects[row] = 1;
  414. // Load the keys with sort data from the table
  415. er = AddRowKey(&listRows, NULL, 0, true, false, NULL);
  416. skip:
  417. m_bMVCols = bMVColsNew;
  418. m_bMVSort = bMVSortNew;
  419. return er;
  420. }
  421. /**
  422. * Returns the total number of multi value rows of a specific object.
  423. *
  424. * This methode should be overridden and should return the total number of multi value rows of a specific object.
  425. *
  426. * @param[in] ulObjId
  427. * Object id to receive the number of multi value rows
  428. * @param[out] lpulCount
  429. * Pointer to the number of multi value rows of the object ulObjId
  430. *
  431. * @return Kopano error code
  432. */
  433. ECRESULT ECGenericObjectTable::GetMVRowCount(unsigned int ulObjId, unsigned int *lpulCount)
  434. {
  435. return KCERR_NO_SUPPORT;
  436. }
  437. /**
  438. * Defines the properties and order of properties to appear as columns in the table.
  439. *
  440. * @param[in] lpsPropTags
  441. * Pointer to an array of property tags with a specific order.
  442. * The lpsPropTags parameter cannot be set to NULL; table must have at least one column.
  443. *
  444. * @return Kopano error code
  445. */
  446. ECRESULT ECGenericObjectTable::SetColumns(const struct propTagArray *lpsPropTags,
  447. bool bDefaultSet)
  448. {
  449. //FIXME: check the lpsPropTags array, 0x????xxxx -> xxxx must be checked
  450. // Remember the columns for later use (in QueryRows)
  451. // This is a very very quick operation, as we only save the information.
  452. scoped_rlock biglock(m_hLock);
  453. // Delete the old column set
  454. if(this->lpsPropTagArray)
  455. FreePropTagArray(this->lpsPropTagArray);
  456. lpsPropTagArray = s_alloc<propTagArray>(nullptr);
  457. lpsPropTagArray->__size = lpsPropTags->__size;
  458. lpsPropTagArray->__ptr = s_alloc<unsigned int>(nullptr, lpsPropTags->__size);
  459. if (bDefaultSet) {
  460. for (gsoap_size_t n = 0; n < lpsPropTags->__size; ++n) {
  461. if (PROP_TYPE(lpsPropTags->__ptr[n]) == PT_STRING8 || PROP_TYPE(lpsPropTags->__ptr[n]) == PT_UNICODE)
  462. lpsPropTagArray->__ptr[n] = CHANGE_PROP_TYPE(lpsPropTags->__ptr[n], ((m_ulFlags & MAPI_UNICODE) ? PT_UNICODE : PT_STRING8));
  463. else if (PROP_TYPE(lpsPropTags->__ptr[n]) == PT_MV_STRING8 || PROP_TYPE(lpsPropTags->__ptr[n]) == PT_MV_UNICODE)
  464. lpsPropTagArray->__ptr[n] = CHANGE_PROP_TYPE(lpsPropTags->__ptr[n], ((m_ulFlags & MAPI_UNICODE) ? PT_MV_UNICODE : PT_MV_STRING8));
  465. else
  466. lpsPropTagArray->__ptr[n] = lpsPropTags->__ptr[n];
  467. }
  468. } else
  469. memcpy(lpsPropTagArray->__ptr, lpsPropTags->__ptr, sizeof(unsigned int) * lpsPropTags->__size);
  470. return ReloadTable(RELOAD_TYPE_SETCOLUMNS);
  471. }
  472. ECRESULT ECGenericObjectTable::GetColumns(struct soap *soap, ULONG ulFlags, struct propTagArray **lppsPropTags)
  473. {
  474. ECRESULT er = erSuccess;
  475. int n = 0;
  476. ECListInt lstProps;
  477. struct propTagArray *lpsPropTags;
  478. scoped_rlock biglock(m_hLock);
  479. if(ulFlags & TBL_ALL_COLUMNS) {
  480. // All columns were requested. Simply get a unique list of all the proptags used in all the objects in this table
  481. er = Populate();
  482. if(er != erSuccess)
  483. return er;
  484. er = GetColumnsAll(&lstProps);
  485. if(er != erSuccess)
  486. return er;
  487. // Make sure we have a unique list
  488. lstProps.sort();
  489. lstProps.unique();
  490. // Convert them all over to a struct propTagArray
  491. lpsPropTags = s_alloc<propTagArray>(soap);
  492. lpsPropTags->__size = lstProps.size();
  493. lpsPropTags->__ptr = s_alloc<unsigned int>(soap, lstProps.size());
  494. n = 0;
  495. for (auto prop_int : lstProps) {
  496. lpsPropTags->__ptr[n] = prop_int;
  497. if (PROP_TYPE(lpsPropTags->__ptr[n]) == PT_STRING8 || PROP_TYPE(lpsPropTags->__ptr[n]) == PT_UNICODE)
  498. lpsPropTags->__ptr[n] = CHANGE_PROP_TYPE(lpsPropTags->__ptr[n], ((m_ulFlags & MAPI_UNICODE) ? PT_UNICODE : PT_STRING8));
  499. else if (PROP_TYPE(lpsPropTags->__ptr[n]) == PT_MV_STRING8 || PROP_TYPE(lpsPropTags->__ptr[n]) == PT_MV_UNICODE)
  500. lpsPropTags->__ptr[n] = CHANGE_PROP_TYPE(lpsPropTags->__ptr[n], ((m_ulFlags & MAPI_UNICODE) ? PT_MV_UNICODE : PT_MV_STRING8));
  501. ++n;
  502. }
  503. } else {
  504. lpsPropTags = s_alloc<propTagArray>(soap);
  505. if(lpsPropTagArray) {
  506. lpsPropTags->__size = lpsPropTagArray->__size;
  507. lpsPropTags->__ptr = s_alloc<unsigned int>(soap, lpsPropTagArray->__size);
  508. memcpy(lpsPropTags->__ptr, lpsPropTagArray->__ptr, sizeof(unsigned int) * lpsPropTagArray->__size);
  509. } else {
  510. lpsPropTags->__size = 0;
  511. lpsPropTags->__ptr = NULL;
  512. }
  513. }
  514. *lppsPropTags = lpsPropTags;
  515. return er;
  516. }
  517. ECRESULT ECGenericObjectTable::ReloadKeyTable()
  518. {
  519. ECObjectTableList listRows;
  520. scoped_rlock biglock(m_hLock);
  521. // Get all the Row IDs from the ID map
  522. for (const auto &p : mapObjects)
  523. listRows.push_back(p.first);
  524. // Reset the key table
  525. lpKeyTable->Clear();
  526. m_mapLeafs.clear();
  527. for (const auto &p : m_mapCategories)
  528. delete p.second;
  529. m_mapCategories.clear();
  530. m_mapSortedCategories.clear();
  531. // Load the keys with sort data from the table
  532. return AddRowKey(&listRows, NULL, 0, true, false, NULL);
  533. }
  534. ECRESULT ECGenericObjectTable::SetSortOrder(struct sortOrderArray *lpsSortOrder, unsigned int ulCategories, unsigned int ulExpanded)
  535. {
  536. ECRESULT er = erSuccess;
  537. // Set the sort order, re-read the data from the database, and reset the current row
  538. // The current row is reset to point to the row it was pointing to in the first place.
  539. // This is pretty easy as it is pointing at the same object ID as it was before we
  540. // reloaded.
  541. scoped_rlock biglock(m_hLock);
  542. if(m_ulCategories == ulCategories && m_ulExpanded == ulExpanded && this->lpsSortOrderArray && CompareSortOrderArray(this->lpsSortOrderArray, lpsSortOrder) == 0) {
  543. // Sort requested was already set, return OK
  544. this->SeekRow(BOOKMARK_BEGINNING, 0, NULL);
  545. return er;
  546. }
  547. // Check validity of tags
  548. for (gsoap_size_t i = 0; i < lpsSortOrder->__size; ++i)
  549. if ((PROP_TYPE(lpsSortOrder->__ptr[i].ulPropTag) & MVI_FLAG) == MV_FLAG)
  550. return KCERR_TOO_COMPLEX;
  551. m_ulCategories = ulCategories;
  552. m_ulExpanded = ulExpanded;
  553. // Save the sort order requested
  554. if(this->lpsSortOrderArray)
  555. FreeSortOrderArray(this->lpsSortOrderArray);
  556. this->lpsSortOrderArray = s_alloc<sortOrderArray>(nullptr);
  557. this->lpsSortOrderArray->__size = lpsSortOrder->__size;
  558. if(lpsSortOrder->__size == 0 ) {
  559. this->lpsSortOrderArray->__ptr = NULL;
  560. } else {
  561. this->lpsSortOrderArray->__ptr = s_alloc<sortOrder>(nullptr, lpsSortOrder->__size);
  562. memcpy(this->lpsSortOrderArray->__ptr, lpsSortOrder->__ptr, sizeof(struct sortOrder) * lpsSortOrder->__size);
  563. }
  564. er = ReloadTable(RELOAD_TYPE_SORTORDER);
  565. if(er != erSuccess)
  566. return er;
  567. // FIXME When you change the sort order, current row should be equal to previous row ID
  568. return lpKeyTable->SeekRow(0, 0, NULL);
  569. }
  570. ECRESULT ECGenericObjectTable::GetBinarySortKey(struct propVal *lpsPropVal, unsigned int *lpSortLen, unsigned char **lppSortData)
  571. {
  572. ECRESULT er = erSuccess;
  573. unsigned char *lpSortData = NULL;
  574. unsigned int ulSortLen = 0;
  575. switch(PROP_TYPE(lpsPropVal->ulPropTag)) {
  576. case PT_BOOLEAN:
  577. case PT_I2:
  578. ulSortLen = 2;
  579. lpSortData = new unsigned char[2];
  580. *(unsigned short *)lpSortData = htons(lpsPropVal->Value.b);
  581. break;
  582. case PT_LONG:
  583. ulSortLen = 4;
  584. lpSortData = new unsigned char[4];
  585. *(unsigned int *)lpSortData = htonl(lpsPropVal->Value.ul);
  586. break;
  587. case PT_R4:
  588. ulSortLen = sizeof(double);
  589. lpSortData = new unsigned char[sizeof(double)];
  590. *(double *)lpSortData = lpsPropVal->Value.flt;
  591. break;
  592. case PT_APPTIME:
  593. case PT_DOUBLE:
  594. ulSortLen = sizeof(double);
  595. lpSortData = new unsigned char[sizeof(double)];
  596. *(double *)lpSortData = lpsPropVal->Value.dbl;
  597. break;
  598. case PT_CURRENCY:
  599. ulSortLen = 0;
  600. lpSortData = NULL;
  601. break;
  602. case PT_SYSTIME:
  603. ulSortLen = 8;
  604. lpSortData = new unsigned char[8];
  605. *(unsigned int *)lpSortData = htonl(lpsPropVal->Value.hilo->hi);
  606. *(unsigned int *)(lpSortData+4) = htonl(lpsPropVal->Value.hilo->lo);
  607. break;
  608. case PT_I8:
  609. ulSortLen = 8;
  610. lpSortData = new unsigned char[8];
  611. *(unsigned int *)lpSortData = htonl((unsigned int)(lpsPropVal->Value.li >> 32));
  612. *(unsigned int *)(lpSortData+4) = htonl((unsigned int)lpsPropVal->Value.li);
  613. break;
  614. case PT_STRING8:
  615. case PT_UNICODE: {
  616. // is this check needed here, or is it already checked 50 times along the way?
  617. if (!lpsPropVal->Value.lpszA) {
  618. ulSortLen = 0;
  619. lpSortData = NULL;
  620. break;
  621. }
  622. createSortKeyDataFromUTF8(lpsPropVal->Value.lpszA, 255, m_locale, &ulSortLen, &lpSortData);
  623. }
  624. break;
  625. case PT_CLSID:
  626. case PT_BINARY:
  627. ulSortLen = lpsPropVal->Value.bin->__size;
  628. lpSortData = new unsigned char [ulSortLen];
  629. memcpy(lpSortData, lpsPropVal->Value.bin->__ptr, ulSortLen); // could be optimized to one func
  630. break;
  631. case PT_ERROR:
  632. ulSortLen = 0;
  633. lpSortData = NULL;
  634. break;
  635. default:
  636. er = KCERR_INVALID_TYPE;
  637. break;
  638. }
  639. if(er != erSuccess)
  640. return er;
  641. *lpSortLen = ulSortLen;
  642. *lppSortData = lpSortData;
  643. return erSuccess;
  644. }
  645. /**
  646. * The ECGenericObjectTable::GetSortFlags method gets tablerow flags for a property.
  647. *
  648. * This flag alters the comparison behaviour of the ECKeyTable. This behaviour only needs
  649. * to be altered for float/double values and strings.
  650. *
  651. * @param[in] ulPropTag The PropTag for which to get the flags.
  652. * @param[out] lpFlags The flags needed to properly compare properties for the provided PropTag.
  653. *
  654. * @return Kopano error code
  655. */
  656. ECRESULT ECGenericObjectTable::GetSortFlags(unsigned int ulPropTag, unsigned char *lpFlags)
  657. {
  658. ECRESULT er = erSuccess;
  659. unsigned int ulFlags = 0;
  660. switch(PROP_TYPE(ulPropTag)) {
  661. case PT_DOUBLE:
  662. case PT_APPTIME:
  663. case PT_R4:
  664. ulFlags = TABLEROW_FLAG_FLOAT;
  665. break;
  666. case PT_STRING8:
  667. case PT_UNICODE:
  668. ulFlags = TABLEROW_FLAG_STRING;
  669. break;
  670. default:
  671. break;
  672. }
  673. *lpFlags = ulFlags;
  674. return er;
  675. }
  676. /**
  677. * The ECGenericObjectTable::Restrict methode applies a filter to a table
  678. *
  679. * The ECGenericObjectTable::Restrict methode applies a filter to a table, reducing
  680. * the row set to only those rows matching the specified criteria.
  681. *
  682. * @param[in] lpsRestrict
  683. * Pointer to a restrictTable structure defining the conditions of the filter.
  684. * Passing NULL in the lpsRestrict parameter removes the current filter.
  685. *
  686. * @return Kopano error code
  687. */
  688. ECRESULT ECGenericObjectTable::Restrict(struct restrictTable *lpsRestrict)
  689. {
  690. ECRESULT er = erSuccess;
  691. scoped_rlock biglock(m_hLock);
  692. if(lpsSortOrderArray == NULL) {
  693. er = SetSortOrder(&sDefaultSortOrder, 0, 0);
  694. if(er != erSuccess)
  695. return er;
  696. }
  697. // No point turning off a restriction that's already off
  698. if (this->lpsRestrict == NULL && lpsRestrict == NULL)
  699. return er;
  700. // Copy the restriction so we can remember it
  701. if(this->lpsRestrict)
  702. FreeRestrictTable(this->lpsRestrict);
  703. this->lpsRestrict = NULL; // turn off restriction
  704. if(lpsRestrict) {
  705. er = CopyRestrictTable(NULL, lpsRestrict, &this->lpsRestrict);
  706. if(er != erSuccess)
  707. return er;
  708. }
  709. er = ReloadKeyTable();
  710. if(er != erSuccess)
  711. return er;
  712. // Seek to row 0 (according to spec)
  713. this->SeekRow(BOOKMARK_BEGINNING, 0, NULL);
  714. return er;
  715. }
  716. /*
  717. * Adds a set of rows to the key table, with the correct sort keys
  718. *
  719. * This function attempts to add the set of rows passed in lpRows to the table. The rows are added according
  720. * to sorting currently set and are tested against the current restriction. If bFilter is FALSE, then rows are not
  721. * tested against the current restriction and always added. A row may also not be added if data for the row is no
  722. * longer available, in which case the row is silently ignored.
  723. *
  724. * The bLoad parameter is not used here, but may be used be subclasses to determine if the rows are being added
  725. * due to an initial load of the table, or due to a later update.
  726. *
  727. * @param[in] lpRows Candidate rows to add to the table
  728. * @param[out] lpulLoaded Number of rows added to the table
  729. * @param[in] ulFlags Type of rows being added (May be 0, MSGFLAG_ASSOCIATED, MSGFLAG_DELETED or combination)
  730. * @param[in] bLoad TRUE if the rows being added are being added for an initial load or reload of the table, false for an update
  731. * @param[in] bOverride TRUE if the restriction set by Restrict() must be ignored, and the rows in lpRows must be filtered with lpOverrideRestrict. lpOverrideRestrict
  732. * MAY be NULL indicating that all rows in lpRows are to be added without filtering.
  733. * @param[in] lpOverrideRestrict Overrides the set restriction, using this one instead; the rows passed in lpRows are filtered with this restriction
  734. */
  735. ECRESULT ECGenericObjectTable::AddRowKey(ECObjectTableList* lpRows, unsigned int *lpulLoaded, unsigned int ulFlags, bool bLoad, bool bOverride, struct restrictTable *lpOverrideRestrict)
  736. {
  737. TRACE_INTERNAL(TRACE_ENTRY, "Table call:", "ECGenericObjectTable::AddRowKey", "");
  738. ECRESULT er = erSuccess;
  739. bool fMatch = true;
  740. gsoap_size_t ulFirstCol = 0, n = 0;
  741. unsigned int ulLoaded = 0;
  742. bool bExist;
  743. bool fHidden = false;
  744. SUBRESTRICTIONRESULTS *lpSubResults = NULL;
  745. ECObjectTableList sQueryRows;
  746. struct propTagArray sPropTagArray = {0, 0};
  747. struct rowSet *lpRowSet = NULL;
  748. struct propTagArray *lpsRestrictPropTagArray = NULL;
  749. struct restrictTable *lpsRestrict = NULL;
  750. sObjectTableKey sRowItem;
  751. ECCategory *lpCategory = NULL;
  752. ulock_rec biglock(m_hLock);
  753. if (lpRows->empty()) {
  754. // nothing todo
  755. if(lpulLoaded)
  756. *lpulLoaded = 0;
  757. goto exit;
  758. }
  759. lpsRestrict = bOverride ? lpOverrideRestrict : this->lpsRestrict;
  760. // We want all columns of the sort data, plus all the columns needed for restriction, plus the ID of the row
  761. if(this->lpsSortOrderArray)
  762. sPropTagArray.__size = this->lpsSortOrderArray->__size; // sort columns
  763. else
  764. sPropTagArray.__size = 0;
  765. if(lpsRestrict) {
  766. er = GetRestrictPropTags(lpsRestrict, NULL, &lpsRestrictPropTagArray);
  767. if(er != erSuccess)
  768. goto exit;
  769. sPropTagArray.__size += lpsRestrictPropTagArray->__size; // restrict columns
  770. }
  771. ++sPropTagArray.__size; // for PR_INSTANCE_KEY
  772. ++sPropTagArray.__size; // for PR_MESSAGE_FLAGS
  773. sPropTagArray.__ptr = s_alloc<unsigned int>(nullptr, sPropTagArray.__size);
  774. sPropTagArray.__ptr[n++]= PR_INSTANCE_KEY;
  775. if(m_ulCategories > 0)
  776. sPropTagArray.__ptr[n++]= PR_MESSAGE_FLAGS;
  777. ulFirstCol = n;
  778. // Put all the proptags of the sort columns in a proptag array
  779. if(lpsSortOrderArray)
  780. for (gsoap_size_t i = 0; i < this->lpsSortOrderArray->__size; ++i)
  781. sPropTagArray.__ptr[n++] = this->lpsSortOrderArray->__ptr[i].ulPropTag;
  782. // Same for restrict columns
  783. // Check if an item already exist
  784. if(lpsRestrictPropTagArray) {
  785. for (gsoap_size_t i = 0; i < lpsRestrictPropTagArray->__size; ++i) {
  786. bExist = false;
  787. for (gsoap_size_t j = 0; j < n; ++j)
  788. if(sPropTagArray.__ptr[j] == lpsRestrictPropTagArray->__ptr[i])
  789. bExist = true;
  790. if(bExist == false)
  791. sPropTagArray.__ptr[n++] = lpsRestrictPropTagArray->__ptr[i];
  792. }
  793. }
  794. sPropTagArray.__size = n;
  795. for (auto iterRows = lpRows->cbegin(); iterRows != lpRows->cend(); ) {
  796. sQueryRows.clear();
  797. // if we use a restriction, memory usage goes up, so only fetch 20 rows at a time
  798. for (size_t i = 0; i < (lpsRestrictPropTagArray ? 20 : 256) && iterRows != lpRows->cend(); ++i) {
  799. sQueryRows.push_back(*iterRows);
  800. ++iterRows;
  801. }
  802. // Now, query the database for the actual data
  803. er = m_lpfnQueryRowData(this, NULL, lpSession, &sQueryRows, &sPropTagArray, m_lpObjectData, &lpRowSet, true, lpsRestrictPropTagArray ? false : true /* FIXME */);
  804. if(er != erSuccess)
  805. goto exit;
  806. if(lpsRestrict) {
  807. er = RunSubRestrictions(lpSession, m_lpObjectData, lpsRestrict, &sQueryRows, m_locale, &lpSubResults);
  808. if(er != erSuccess)
  809. goto exit;
  810. }
  811. // Send all this data to the internal key table
  812. for (gsoap_size_t i = 0; i < lpRowSet->__size; ++i) {
  813. lpCategory = NULL;
  814. if (lpRowSet->__ptr[i].__ptr[0].ulPropTag != PR_INSTANCE_KEY) // Row completely not found
  815. continue;
  816. // is PR_INSTANCE_KEY
  817. memcpy(&sRowItem.ulObjId, lpRowSet->__ptr[i].__ptr[0].Value.bin->__ptr, sizeof(ULONG));
  818. memcpy(&sRowItem.ulOrderId, lpRowSet->__ptr[i].__ptr[0].Value.bin->__ptr+sizeof(ULONG), sizeof(ULONG));
  819. // Match the row with the restriction, if any
  820. if(lpsRestrict) {
  821. MatchRowRestrict(lpSession->GetSessionManager()->GetCacheManager(), &lpRowSet->__ptr[i], lpsRestrict, lpSubResults, m_locale, &fMatch);
  822. if(fMatch == false) {
  823. // this row isn't in the table, as it does not match the restrict criteria. Remove it as if it had
  824. // been deleted if it was already in the table.
  825. DeleteRow(sRowItem, ulFlags);
  826. RemoveCategoryAfterRemoveRow(sRowItem, ulFlags);
  827. continue;
  828. }
  829. }
  830. if(m_ulCategories > 0) {
  831. bool bUnread = false;
  832. if((lpRowSet->__ptr[i].__ptr[1].Value.ul & MSGFLAG_READ) == 0)
  833. bUnread = true;
  834. // Update category for this row if required, and send notification if required
  835. AddCategoryBeforeAddRow(sRowItem, lpRowSet->__ptr[i].__ptr+ulFirstCol, lpsSortOrderArray->__size, ulFlags, bUnread, &fHidden, &lpCategory);
  836. }
  837. // Put the row into the key table and send notification if required
  838. AddRow(sRowItem, lpRowSet->__ptr[i].__ptr+ulFirstCol, lpsSortOrderArray->__size, ulFlags, fHidden, lpCategory);
  839. // Loaded one row
  840. ++ulLoaded;
  841. }
  842. if(lpSubResults) {
  843. FreeSubRestrictionResults(lpSubResults);
  844. lpSubResults = NULL;
  845. }
  846. FreeRowSet(lpRowSet, true);
  847. lpRowSet = NULL;
  848. }
  849. if(lpulLoaded)
  850. *lpulLoaded = ulLoaded;
  851. exit:
  852. biglock.unlock();
  853. if(lpSubResults)
  854. FreeSubRestrictionResults(lpSubResults);
  855. if(lpRowSet)
  856. FreeRowSet(lpRowSet, true);
  857. if(lpsRestrictPropTagArray != NULL)
  858. s_free(nullptr, lpsRestrictPropTagArray->__ptr);
  859. s_free(nullptr, lpsRestrictPropTagArray);
  860. s_free(nullptr, sPropTagArray.__ptr);
  861. return er;
  862. }
  863. // Actually add a row to the table
  864. ECRESULT ECGenericObjectTable::AddRow(sObjectTableKey sRowItem, struct propVal *lpProps, unsigned int cProps, unsigned int ulFlags, bool fHidden, ECCategory *lpCategory)
  865. {
  866. ECRESULT er;
  867. ECKeyTable::UpdateType ulAction;
  868. sObjectTableKey sPrevRow;
  869. UpdateKeyTableRow(lpCategory, &sRowItem, lpProps, cProps, fHidden, &sPrevRow, &ulAction);
  870. // Send notification if required
  871. if(ulAction && !fHidden && (ulFlags & OBJECTTABLE_NOTIFY)) {
  872. er = AddTableNotif(ulAction, sRowItem, &sPrevRow);
  873. if(er != erSuccess)
  874. return er;
  875. }
  876. return erSuccess;
  877. }
  878. // Actually remove a row from the table
  879. ECRESULT ECGenericObjectTable::DeleteRow(sObjectTableKey sRow, unsigned int ulFlags)
  880. {
  881. ECRESULT er;
  882. ECKeyTable::UpdateType ulAction;
  883. // Delete the row from the key table
  884. er = lpKeyTable->UpdateRow(ECKeyTable::TABLE_ROW_DELETE, &sRow, 0, NULL, NULL, NULL, NULL, false, &ulAction);
  885. if(er != erSuccess)
  886. return er;
  887. // Send notification if required
  888. if ((ulFlags & OBJECTTABLE_NOTIFY) && ulAction == ECKeyTable::TABLE_ROW_DELETE)
  889. AddTableNotif(ulAction, sRow, NULL);
  890. return erSuccess;
  891. }
  892. // Add a table notification by getting row data and sending it
  893. ECRESULT ECGenericObjectTable::AddTableNotif(ECKeyTable::UpdateType ulAction, sObjectTableKey sRowItem, sObjectTableKey *lpsPrevRow)
  894. {
  895. ECRESULT er = erSuccess;
  896. std::list<sObjectTableKey> lstItems;
  897. struct rowSet *lpRowSetNotif = NULL;
  898. if(ulAction == ECKeyTable::TABLE_ROW_ADD || ulAction == ECKeyTable::TABLE_ROW_MODIFY) {
  899. lstItems.push_back(sRowItem);
  900. er = m_lpfnQueryRowData(this, NULL, lpSession, &lstItems, this->lpsPropTagArray, m_lpObjectData, &lpRowSetNotif, true, true);
  901. if(er != erSuccess)
  902. goto exit;
  903. if(lpRowSetNotif->__size != 1) {
  904. er = KCERR_NOT_FOUND;
  905. goto exit;
  906. }
  907. lpSession->AddNotificationTable(ulAction, m_ulObjType, m_ulTableId, &sRowItem, lpsPrevRow, &lpRowSetNotif->__ptr[0]);
  908. } else if(ulAction == ECKeyTable::TABLE_ROW_DELETE) {
  909. lpSession->AddNotificationTable(ulAction, m_ulObjType, m_ulTableId, &sRowItem, NULL, NULL);
  910. } else {
  911. er = KCERR_NOT_FOUND;
  912. goto exit;
  913. }
  914. exit:
  915. if(lpRowSetNotif)
  916. FreeRowSet(lpRowSetNotif, true);
  917. return er;
  918. }
  919. ECRESULT ECGenericObjectTable::QueryRows(struct soap *soap, unsigned int ulRowCount, unsigned int ulFlags, struct rowSet **lppRowSet)
  920. {
  921. // Retrieve the keyset from our KeyTable, and use that to retrieve the other columns
  922. // specified by SetColumns
  923. struct rowSet *lpRowSet = NULL;
  924. ECObjectTableList ecRowList;
  925. scoped_rlock biglock(m_hLock);
  926. ECRESULT er = Populate();
  927. if (er != erSuccess)
  928. return er;
  929. if(lpsSortOrderArray == NULL) {
  930. er = SetSortOrder(&sDefaultSortOrder, 0, 0);
  931. if(er != erSuccess)
  932. return er;
  933. }
  934. // Get the keys per row
  935. er = lpKeyTable->QueryRows(ulRowCount, &ecRowList, false, ulFlags);
  936. if(er != erSuccess)
  937. return er;
  938. assert(ecRowList.size() <= this->mapObjects.size() + this->m_mapCategories.size());
  939. if(ecRowList.empty()) {
  940. lpRowSet = s_alloc<rowSet>(soap);
  941. lpRowSet->__size = 0;
  942. lpRowSet->__ptr = NULL;
  943. } else {
  944. // We now have the ordering of the rows, all we have to do now is get the data.
  945. er = m_lpfnQueryRowData(this, soap, lpSession, &ecRowList, this->lpsPropTagArray, m_lpObjectData, &lpRowSet, true, true);
  946. }
  947. if(er != erSuccess)
  948. return er;
  949. *lppRowSet = lpRowSet;
  950. return er;
  951. }
  952. ECRESULT ECGenericObjectTable::CreateBookmark(unsigned int* lpulbkPosition)
  953. {
  954. scoped_rlock biglock(m_hLock);
  955. return lpKeyTable->CreateBookmark(lpulbkPosition);
  956. }
  957. ECRESULT ECGenericObjectTable::FreeBookmark(unsigned int ulbkPosition)
  958. {
  959. scoped_rlock biglock(m_hLock);
  960. return lpKeyTable->FreeBookmark(ulbkPosition);
  961. }
  962. // Expand the category identified by sInstanceKey
  963. ECRESULT ECGenericObjectTable::ExpandRow(struct soap *soap, xsd__base64Binary sInstanceKey, unsigned int ulRowCount, unsigned int ulFlags, struct rowSet **lppRowSet, unsigned int *lpulRowsLeft)
  964. {
  965. sObjectTableKey sKey;
  966. sObjectTableKey sPrevRow;
  967. ECCategoryMap::const_iterator iterCategory;
  968. ECCategory *lpCategory = NULL;
  969. ECObjectTableList lstUnhidden;
  970. unsigned int ulRowsLeft = 0;
  971. struct rowSet *lpRowSet = NULL;
  972. scoped_rlock biglock(m_hLock);
  973. ECRESULT er = Populate();
  974. if(er != erSuccess)
  975. return er;
  976. if (sInstanceKey.__size != sizeof(sObjectTableKey))
  977. return KCERR_INVALID_PARAMETER;
  978. sKey.ulObjId = *((unsigned int *)sInstanceKey.__ptr);
  979. sKey.ulOrderId = *((unsigned int *)sInstanceKey.__ptr+1);
  980. iterCategory = m_mapCategories.find(sKey);
  981. if (iterCategory == m_mapCategories.cend())
  982. return KCERR_NOT_FOUND;
  983. lpCategory = iterCategory->second;
  984. // Unhide all rows under this category
  985. er = lpKeyTable->UnhideRows(&sKey, &lstUnhidden);
  986. if(er != erSuccess)
  987. return er;
  988. // Only return a maximum of ulRowCount rows
  989. if(ulRowCount < lstUnhidden.size()) {
  990. ulRowsLeft = lstUnhidden.size() - ulRowCount;
  991. lstUnhidden.resize(ulRowCount);
  992. // Put the keytable cursor just after the rows we will be returning, so the next queryrows() would return the remaining rows
  993. lpKeyTable->SeekRow(1, -ulRowsLeft, NULL);
  994. }
  995. // Get the row data to return, if required
  996. if(lppRowSet) {
  997. if(lstUnhidden.empty()){
  998. lpRowSet = s_alloc<rowSet>(soap);
  999. lpRowSet->__size = 0;
  1000. lpRowSet->__ptr = NULL;
  1001. } else {
  1002. // Get data for unhidden rows
  1003. er = m_lpfnQueryRowData(this, soap, lpSession, &lstUnhidden, this->lpsPropTagArray, m_lpObjectData, &lpRowSet, true, true);
  1004. }
  1005. if(er != erSuccess)
  1006. return er;
  1007. }
  1008. lpCategory->m_fExpanded = true;
  1009. if(lppRowSet)
  1010. *lppRowSet = lpRowSet;
  1011. if(lpulRowsLeft)
  1012. *lpulRowsLeft = ulRowsLeft;
  1013. return er;
  1014. }
  1015. // Collapse the category row identified by sInstanceKey
  1016. ECRESULT ECGenericObjectTable::CollapseRow(xsd__base64Binary sInstanceKey, unsigned int ulFlags, unsigned int *lpulRows)
  1017. {
  1018. sObjectTableKey sKey;
  1019. sObjectTableKey sPrevRow;
  1020. ECCategoryMap::const_iterator iterCategory;
  1021. ECCategory *lpCategory = NULL;
  1022. ECObjectTableList lstHidden;
  1023. scoped_rlock biglock(m_hLock);
  1024. if (sInstanceKey.__size != sizeof(sObjectTableKey))
  1025. return KCERR_INVALID_PARAMETER;
  1026. ECRESULT er = Populate();
  1027. if(er != erSuccess)
  1028. return er;
  1029. sKey.ulObjId = *((unsigned int *)sInstanceKey.__ptr);
  1030. sKey.ulOrderId = *((unsigned int *)sInstanceKey.__ptr+1);
  1031. iterCategory = m_mapCategories.find(sKey);
  1032. if (iterCategory == m_mapCategories.cend())
  1033. return KCERR_NOT_FOUND;
  1034. lpCategory = iterCategory->second;
  1035. // Hide the rows under this category
  1036. er = lpKeyTable->HideRows(&sKey, &lstHidden);
  1037. if(er != erSuccess)
  1038. return er;
  1039. // Mark the category as collapsed
  1040. lpCategory->m_fExpanded = false;
  1041. // Loop through the hidden rows to see if we have hidden any categories. If so, mark them as
  1042. // collapsed
  1043. for (auto iterHidden = lstHidden.cbegin(); iterHidden != lstHidden.cend(); ++iterHidden) {
  1044. iterCategory = m_mapCategories.find(*iterHidden);
  1045. if (iterCategory != m_mapCategories.cend())
  1046. iterCategory->second->m_fExpanded = false;
  1047. }
  1048. if(lpulRows)
  1049. *lpulRows = lstHidden.size();
  1050. return er;
  1051. }
  1052. ECRESULT ECGenericObjectTable::GetCollapseState(struct soap *soap, struct xsd__base64Binary sBookmark, struct xsd__base64Binary *lpsCollapseState)
  1053. {
  1054. ECRESULT er = erSuccess;
  1055. struct collapseState sCollapseState;
  1056. int n = 0;
  1057. std::ostringstream os;
  1058. sObjectTableKey sKey;
  1059. struct rowSet *lpsRowSet = NULL;
  1060. struct soap xmlsoap; // static, so c++ inits struct, no need for soap init
  1061. ulock_rec biglock(m_hLock);
  1062. er = Populate();
  1063. if(er != erSuccess)
  1064. goto exit;
  1065. memset(&sCollapseState, 0, sizeof(sCollapseState));
  1066. // Generate a binary collapsestate which is simply an XML stream of all categories with their collapse state
  1067. sCollapseState.sCategoryStates.__size = m_mapCategories.size();
  1068. sCollapseState.sCategoryStates.__ptr = s_alloc<struct categoryState>(soap, sCollapseState.sCategoryStates.__size);
  1069. memset(sCollapseState.sCategoryStates.__ptr, 0, sizeof(struct categoryState) * sCollapseState.sCategoryStates.__size);
  1070. for (const auto &p : m_mapCategories) {
  1071. sCollapseState.sCategoryStates.__ptr[n].fExpanded = p.second->m_fExpanded;
  1072. sCollapseState.sCategoryStates.__ptr[n].sProps.__ptr = s_alloc<struct propVal>(soap, p.second->m_cProps);
  1073. memset(sCollapseState.sCategoryStates.__ptr[n].sProps.__ptr, 0, sizeof(struct propVal) * p.second->m_cProps);
  1074. for (unsigned int i = 0; i < p.second->m_cProps; ++i) {
  1075. er = CopyPropVal(&p.second->m_lpProps[i], &sCollapseState.sCategoryStates.__ptr[n].sProps.__ptr[i], soap);
  1076. if (er != erSuccess)
  1077. goto exit;
  1078. }
  1079. sCollapseState.sCategoryStates.__ptr[n].sProps.__size = p.second->m_cProps;
  1080. ++n;
  1081. }
  1082. // We also need to save the sort keys for the given bookmark, so that we can return a bookmark when SetCollapseState is called
  1083. if(sBookmark.__size == 8) {
  1084. sKey.ulObjId = *((unsigned int *)sBookmark.__ptr);
  1085. sKey.ulOrderId = *((unsigned int *)sBookmark.__ptr+1);
  1086. // Go the the row requested
  1087. if(lpKeyTable->SeekId(&sKey) == erSuccess) {
  1088. // If the row exists, we simply get the data from the properties of this row, including all properties used
  1089. // in the current sort.
  1090. ECObjectTableList list;
  1091. list.push_back(sKey);
  1092. er = m_lpfnQueryRowData(this, &xmlsoap, lpSession, &list, lpsPropTagArray, m_lpObjectData, &lpsRowSet, false, true);
  1093. if(er != erSuccess)
  1094. goto exit;
  1095. // Copy row 1 from rowset into our bookmark props.
  1096. sCollapseState.sBookMarkProps = lpsRowSet->__ptr[0];
  1097. // Free of lpsRowSet coupled to xmlsoap so not explicitly needed
  1098. }
  1099. }
  1100. soap_set_mode(&xmlsoap, SOAP_XML_TREE | SOAP_C_UTFSTRING);
  1101. xmlsoap.os = &os;
  1102. soap_serialize_collapseState(&xmlsoap, &sCollapseState);
  1103. soap_begin_send(&xmlsoap);
  1104. soap_put_collapseState(&xmlsoap, &sCollapseState, "CollapseState", NULL);
  1105. soap_end_send(&xmlsoap);
  1106. // os.str() now contains serialized objects, copy into return structure
  1107. lpsCollapseState->__size = os.str().size();
  1108. lpsCollapseState->__ptr = s_alloc<unsigned char>(soap, os.str().size());
  1109. memcpy(lpsCollapseState->__ptr, os.str().c_str(), os.str().size());
  1110. exit:
  1111. soap_destroy(&xmlsoap);
  1112. soap_end(&xmlsoap);
  1113. // static struct, so c++ destructor frees memory
  1114. biglock.unlock();
  1115. return er;
  1116. }
  1117. ECRESULT ECGenericObjectTable::SetCollapseState(struct xsd__base64Binary sCollapseState, unsigned int *lpulBookmark)
  1118. {
  1119. ECRESULT er = erSuccess;
  1120. struct soap xmlsoap;
  1121. struct collapseState cst;
  1122. std::istringstream is(std::string((const char *)sCollapseState.__ptr, sCollapseState.__size));
  1123. sObjectTableKey sKey;
  1124. struct xsd__base64Binary sInstanceKey;
  1125. ulock_rec giblock(m_hLock);
  1126. er = Populate();
  1127. if(er != erSuccess)
  1128. goto exit;
  1129. // The collapse state is the serialized collapse state as returned by GetCollapseState(), which we need to parse here
  1130. soap_set_mode(&xmlsoap, SOAP_XML_TREE | SOAP_C_UTFSTRING);
  1131. xmlsoap.is = &is;
  1132. soap_default_collapseState(&xmlsoap, &cst);
  1133. if (soap_begin_recv(&xmlsoap) != 0) {
  1134. er = KCERR_NETWORK_ERROR;
  1135. goto exit;
  1136. }
  1137. soap_get_collapseState(&xmlsoap, &cst, "CollapseState", NULL);
  1138. if(xmlsoap.error) {
  1139. er = KCERR_DATABASE_ERROR;
  1140. ec_log_crit("ECGenericObjectTable::SetCollapseState(): xmlsoap error %d", xmlsoap.error);
  1141. goto exit;
  1142. }
  1143. /* @cst now contains the collapse state for all categories, apply them now. */
  1144. for (gsoap_size_t i = 0; i < cst.sCategoryStates.__size; ++i) {
  1145. std::unique_ptr<unsigned int[]> lpSortLen(new unsigned int[cst.sCategoryStates.__ptr[i].sProps.__size]);
  1146. std::unique_ptr<unsigned char *[]> lpSortData(new unsigned char *[cst.sCategoryStates.__ptr[i].sProps.__size]);
  1147. std::unique_ptr<unsigned char[]> lpSortFlags(new unsigned char[cst.sCategoryStates.__ptr[i].sProps.__size]);
  1148. memset(lpSortData.get(), 0, cst.sCategoryStates.__ptr[i].sProps.__size * sizeof(unsigned char *));
  1149. // Get the binary sortkeys for all properties
  1150. for (gsoap_size_t n = 0; n < cst.sCategoryStates.__ptr[i].sProps.__size; ++n) {
  1151. if (GetBinarySortKey(&cst.sCategoryStates.__ptr[i].sProps.__ptr[n], &lpSortLen[n], &lpSortData[n]) != erSuccess)
  1152. goto next;
  1153. if (GetSortFlags(cst.sCategoryStates.__ptr[i].sProps.__ptr[n].ulPropTag, &lpSortFlags[n]) != erSuccess)
  1154. goto next;
  1155. }
  1156. // Find the category and expand or collapse it. If it's not there anymore, just ignore it.
  1157. if (lpKeyTable->Find(cst.sCategoryStates.__ptr[i].sProps.__size,
  1158. reinterpret_cast<int *>(lpSortLen.get()),
  1159. lpSortData.get(), lpSortFlags.get(), &sKey) == erSuccess) {
  1160. sInstanceKey.__size = 8;
  1161. sInstanceKey.__ptr = (unsigned char *)&sKey;
  1162. if (cst.sCategoryStates.__ptr[i].fExpanded)
  1163. ExpandRow(NULL, sInstanceKey, 0, 0, NULL, NULL);
  1164. else
  1165. CollapseRow(sInstanceKey, 0, NULL);
  1166. }
  1167. next:
  1168. for (gsoap_size_t j = 0; j < cst.sCategoryStates.__ptr[i].sProps.__size; ++j)
  1169. delete[] lpSortData[j];
  1170. }
  1171. // There is also a row stored in the collapse state which we have to create a bookmark at and return that. If it is not found,
  1172. // we return a bookmark to the nearest next row.
  1173. if (cst.sBookMarkProps.__size > 0) {
  1174. std::unique_ptr<unsigned int[]> lpSortLen(new unsigned int[cst.sBookMarkProps.__size]);
  1175. std::unique_ptr<unsigned char *[]> lpSortData(new unsigned char *[cst.sBookMarkProps.__size]);
  1176. std::unique_ptr<unsigned char[]> lpSortFlags(new unsigned char[cst.sBookMarkProps.__size]);
  1177. memset(lpSortData.get(), 0, cst.sBookMarkProps.__size * sizeof(unsigned char *));
  1178. gsoap_size_t n;
  1179. for (n = 0; n < cst.sBookMarkProps.__size; ++n) {
  1180. if (GetBinarySortKey(&cst.sBookMarkProps.__ptr[n], &lpSortLen[n], &lpSortData[n]) != erSuccess)
  1181. break;
  1182. if (GetSortFlags(cst.sBookMarkProps.__ptr[n].ulPropTag, &lpSortFlags[n]) != erSuccess)
  1183. break;
  1184. }
  1185. // If an error occurred in the previous loop, just ignore the whole bookmark thing, just return bookmark 0 (BOOKMARK_BEGINNING)
  1186. if (n == cst.sBookMarkProps.__size) {
  1187. lpKeyTable->LowerBound(cst.sBookMarkProps.__size,
  1188. reinterpret_cast<int *>(lpSortLen.get()),
  1189. lpSortData.get(), lpSortFlags.get());
  1190. lpKeyTable->CreateBookmark(lpulBookmark);
  1191. }
  1192. for (gsoap_size_t j = 0; j < cst.sBookMarkProps.__size; ++j)
  1193. delete[] lpSortData[j];
  1194. }
  1195. /*
  1196. * We do not generate notifications for this event, just like
  1197. * ExpandRow and CollapseRow. You just need to reload the table
  1198. * yourself.
  1199. */
  1200. if (soap_end_recv(&xmlsoap) != 0)
  1201. er = KCERR_NETWORK_ERROR;
  1202. exit:
  1203. soap_destroy(&xmlsoap);
  1204. soap_end(&xmlsoap);
  1205. giblock.unlock();
  1206. return er;
  1207. }
  1208. ECRESULT ECGenericObjectTable::UpdateRow(unsigned int ulType, unsigned int ulObjId, unsigned int ulFlags)
  1209. {
  1210. ECRESULT er = erSuccess;
  1211. std::list<unsigned int> lstObjId;
  1212. lstObjId.push_back(ulObjId);
  1213. er = UpdateRows(ulType, &lstObjId, ulFlags, false);
  1214. return er;
  1215. }
  1216. /**
  1217. * Load a set of rows into the table
  1218. *
  1219. * This is called to populate a table initially, it is functionally equivalent to calling UpdateRow() repeatedly
  1220. * for each item in lstObjId with ulType set to ECKeyTable::TABLE_ROW_ADD.
  1221. *
  1222. * @param lstObjId List of hierarchy IDs for the objects to load
  1223. * @param ulFlags 0, MSGFLAG_DELETED, MAPI_ASSOCIATED or combination
  1224. */
  1225. ECRESULT ECGenericObjectTable::LoadRows(std::list<unsigned int> *lstObjId, unsigned int ulFlags)
  1226. {
  1227. return UpdateRows(ECKeyTable::TABLE_ROW_ADD, lstObjId, ulFlags, true);
  1228. }
  1229. /**
  1230. * Update one or more rows in a table
  1231. *
  1232. * This function adds, modifies or removes objects from a table. The normal function of this is that normally
  1233. * either multiple objects are added, or a single object is removed or updated. The result of such an update can
  1234. * be complex, for example adding an item to a table may cause multiple rows to be added when using categorization
  1235. * or multi-valued properties. In the same way, an update may generate multiple notifications if category headers are
  1236. * involved or when the update modifies the sorting position of the row.
  1237. *
  1238. * Rows are also checked for read permissions here before being added to the table.
  1239. *
  1240. * The bLoad parameter is not used in the ECGenericObjectTable implementation, but simply passed to AddRowKey to indicate
  1241. * whether the rows are being updated due to a change or due to initial loading. The parameter is also not used in AddRowKey
  1242. * but can be used by subclasses to generate different behaviour on the initial load compared to later updates.
  1243. *
  1244. * @param ulType ECKeyTable::TABLE_ROW_ADD, TABLE_ROW_DELETE or TABLE_ROW_MODIFY
  1245. * @param lstObjId List of objects to add, modify or delete
  1246. * @param ulFlags Flags for the objects in lstObjId (0, MSGFLAG_DELETED, MAPI_ASSOCIATED)
  1247. * @param bLoad Indicates that this is the initial load or reload of the table, and not an update
  1248. */
  1249. ECRESULT ECGenericObjectTable::UpdateRows(unsigned int ulType, std::list<unsigned int> *lstObjId, unsigned int ulFlags, bool bLoad)
  1250. {
  1251. ECRESULT er = erSuccess;
  1252. unsigned int ulRead = 0;
  1253. unsigned int cMVOld = 0,
  1254. cMVNew = 1;
  1255. unsigned int i;
  1256. std::list<unsigned int> lstFilteredIds;
  1257. ECObjectTableList ecRowsItem;
  1258. ECObjectTableList ecRowsDeleted;
  1259. sObjectTableKey sRow;
  1260. scoped_rlock biglock(m_hLock);
  1261. // Perform security checks for this object
  1262. switch(ulType) {
  1263. case ECKeyTable::TABLE_CHANGE:
  1264. // Accept table change in all cases
  1265. break;
  1266. case ECKeyTable::TABLE_ROW_MODIFY:
  1267. case ECKeyTable::TABLE_ROW_ADD:
  1268. // Filter out any item we cannot access (for example, in search-results tables)
  1269. for (const auto &obj_id : *lstObjId)
  1270. if (CheckPermissions(obj_id) == erSuccess)
  1271. lstFilteredIds.push_back(obj_id);
  1272. // Use our filtered list now
  1273. lstObjId = &lstFilteredIds;
  1274. break;
  1275. case ECKeyTable::TABLE_ROW_DELETE:
  1276. // You may always delete a row
  1277. break;
  1278. }
  1279. if(lpsSortOrderArray == NULL) {
  1280. er = SetSortOrder(&sDefaultSortOrder, 0, 0);
  1281. if(er != erSuccess)
  1282. return er;
  1283. }
  1284. // Update a row in the keyset as having changed. Get the data from the DB and send it to the KeyTable.
  1285. switch(ulType) {
  1286. case ECKeyTable::TABLE_ROW_DELETE:
  1287. // Delete the object ID from our object list, and all items with that object ID (including various order IDs)
  1288. for (const auto &obj_id : *lstObjId) {
  1289. auto iterMapObject = this->mapObjects.find(sObjectTableKey(obj_id, 0));
  1290. while (iterMapObject != this->mapObjects.cend()) {
  1291. if (iterMapObject->first.ulObjId == obj_id)
  1292. ecRowsItem.push_back(iterMapObject->first);
  1293. else if (iterMapObject->first.ulObjId != obj_id)
  1294. break;
  1295. ++iterMapObject;
  1296. }
  1297. for (const auto &row : ecRowsItem) {
  1298. this->mapObjects.erase(row);
  1299. /* Delete the object from the active keyset */
  1300. DeleteRow(row, ulFlags);
  1301. RemoveCategoryAfterRemoveRow(row, ulFlags);
  1302. }
  1303. }
  1304. break;
  1305. case ECKeyTable::TABLE_ROW_MODIFY:
  1306. case ECKeyTable::TABLE_ROW_ADD:
  1307. for (const auto &obj_id : *lstObjId) {
  1308. /* Add the object to our list of objects */
  1309. ecRowsItem.push_back(sObjectTableKey(obj_id, 0));
  1310. if(IsMVSet() == true) {
  1311. // get new mvprop count
  1312. er = GetMVRowCount(obj_id, &cMVNew);
  1313. if (er != erSuccess)
  1314. assert(false);// What now???
  1315. // get old mvprops count
  1316. cMVOld = 0;
  1317. auto iterMapObject = this->mapObjects.find(sObjectTableKey(obj_id, 0));
  1318. while (iterMapObject != this->mapObjects.cend()) {
  1319. if (iterMapObject->first.ulObjId == obj_id) {
  1320. ++cMVOld;
  1321. if(cMVOld > cMVNew && (ulFlags&OBJECTTABLE_NOTIFY) == OBJECTTABLE_NOTIFY) {
  1322. auto iterToDelete = iterMapObject;
  1323. --iterMapObject;
  1324. sRow = iterToDelete->first;
  1325. //Delete of map
  1326. this->mapObjects.erase(iterToDelete->first);
  1327. DeleteRow(sRow, ulFlags);
  1328. RemoveCategoryAfterRemoveRow(sRow, ulFlags);
  1329. }//if(cMVOld > cMVNew)
  1330. } else if (iterMapObject->first.ulObjId != obj_id)
  1331. break;
  1332. ++iterMapObject;
  1333. }
  1334. sRow = sObjectTableKey(obj_id, 0);
  1335. for (i = 1; i < cMVNew; ++i) { // 0 already added
  1336. sRow.ulOrderId = i;
  1337. ecRowsItem.push_back(sRow);
  1338. }
  1339. }
  1340. }
  1341. // Remember that the specified row is available
  1342. for (const auto &row : ecRowsItem)
  1343. this->mapObjects[row] = 1;
  1344. // Add/modify the key in the keytable
  1345. er = AddRowKey(&ecRowsItem, &ulRead, ulFlags, bLoad, false, NULL);
  1346. if(er != erSuccess)
  1347. return er;
  1348. break;
  1349. case ECKeyTable::TABLE_CHANGE:
  1350. // The whole table needs to be reread
  1351. this->Clear();
  1352. er = this->Load();
  1353. lpSession->AddNotificationTable(ulType, m_ulObjType, m_ulTableId, NULL, NULL, NULL);
  1354. break;
  1355. }
  1356. return er;
  1357. }
  1358. ECRESULT ECGenericObjectTable::GetRestrictPropTagsRecursive(struct restrictTable *lpsRestrict, list<ULONG> *lpPropTags, ULONG ulLevel)
  1359. {
  1360. ECRESULT er = erSuccess;
  1361. if (ulLevel > RESTRICT_MAX_DEPTH)
  1362. return KCERR_TOO_COMPLEX;
  1363. switch(lpsRestrict->ulType) {
  1364. case RES_COMMENT:
  1365. er = GetRestrictPropTagsRecursive(lpsRestrict->lpComment->lpResTable, lpPropTags, ulLevel+1);
  1366. if(er != erSuccess)
  1367. return er;
  1368. break;
  1369. case RES_OR:
  1370. for (gsoap_size_t i = 0; i < lpsRestrict->lpOr->__size; ++i) {
  1371. er = GetRestrictPropTagsRecursive(lpsRestrict->lpOr->__ptr[i], lpPropTags, ulLevel+1);
  1372. if(er != erSuccess)
  1373. return er;
  1374. }
  1375. break;
  1376. case RES_AND:
  1377. for (gsoap_size_t i = 0; i < lpsRestrict->lpAnd->__size; ++i) {
  1378. er = GetRestrictPropTagsRecursive(lpsRestrict->lpAnd->__ptr[i], lpPropTags, ulLevel+1);
  1379. if(er != erSuccess)
  1380. return er;
  1381. }
  1382. break;
  1383. case RES_NOT:
  1384. er = GetRestrictPropTagsRecursive(lpsRestrict->lpNot->lpNot, lpPropTags, ulLevel+1);
  1385. if(er != erSuccess)
  1386. return er;
  1387. break;
  1388. case RES_CONTENT:
  1389. lpPropTags->push_back(lpsRestrict->lpContent->ulPropTag);
  1390. break;
  1391. case RES_PROPERTY:
  1392. if(PROP_ID(lpsRestrict->lpProp->ulPropTag) == PROP_ID(PR_ANR))
  1393. lpPropTags->insert(lpPropTags->end(), sANRProps, sANRProps + ARRAY_SIZE(sANRProps));
  1394. else {
  1395. lpPropTags->push_back(lpsRestrict->lpProp->lpProp->ulPropTag);
  1396. lpPropTags->push_back(lpsRestrict->lpProp->ulPropTag);
  1397. }
  1398. break;
  1399. case RES_COMPAREPROPS:
  1400. lpPropTags->push_back(lpsRestrict->lpCompare->ulPropTag1);
  1401. lpPropTags->push_back(lpsRestrict->lpCompare->ulPropTag2);
  1402. break;
  1403. case RES_BITMASK:
  1404. lpPropTags->push_back(lpsRestrict->lpBitmask->ulPropTag);
  1405. break;
  1406. case RES_SIZE:
  1407. lpPropTags->push_back(lpsRestrict->lpSize->ulPropTag);
  1408. break;
  1409. case RES_EXIST:
  1410. lpPropTags->push_back(lpsRestrict->lpExist->ulPropTag);
  1411. break;
  1412. case RES_SUBRESTRICTION:
  1413. lpPropTags->push_back(PR_ENTRYID); // we need the entryid in subrestriction searches, because we need to know which object to subsearch
  1414. break;
  1415. }
  1416. return erSuccess;
  1417. }
  1418. /**
  1419. * Generate a list of all properties required to evaluate a restriction
  1420. *
  1421. * The list of properties returned are the maximum set of properties required to evaluate the given restriction. Additionally
  1422. * a list of properties can be added to the front of the property set. If the property is required both through the prefix list
  1423. * and through the restriction, it is included only once in the property list.
  1424. *
  1425. * The order of the first N properties in the returned proptag array are guaranteed to be equal to the N items in lstPrefix
  1426. *
  1427. * @param[in] lpsRestrict Restriction tree to evaluate
  1428. * @param[in] lstPrefix NULL or list of property tags to prefix
  1429. * @param[out] lppPropTags PropTagArray with proptags from lpsRestrict and lstPrefix
  1430. * @return ECRESULT
  1431. */
  1432. ECRESULT ECGenericObjectTable::GetRestrictPropTags(struct restrictTable *lpsRestrict, std::list<ULONG> *lstPrefix, struct propTagArray **lppPropTags)
  1433. {
  1434. ECRESULT er;
  1435. struct propTagArray *lpPropTagArray;
  1436. std::list<ULONG> lstPropTags;
  1437. // Just go through all the properties, adding the properties one-by-one
  1438. er = GetRestrictPropTagsRecursive(lpsRestrict, &lstPropTags, 0);
  1439. if (er != erSuccess)
  1440. return er;
  1441. // Sort and unique-ize the properties (order is not important in the returned array)
  1442. lstPropTags.sort();
  1443. lstPropTags.unique();
  1444. // Prefix if needed
  1445. if(lstPrefix)
  1446. lstPropTags.insert(lstPropTags.begin(), lstPrefix->begin(), lstPrefix->end());
  1447. lpPropTagArray = s_alloc<propTagArray>(nullptr);
  1448. // Put the data into an array
  1449. lpPropTagArray->__size = lstPropTags.size();
  1450. lpPropTagArray->__ptr = s_alloc<unsigned int>(nullptr, lpPropTagArray->__size);
  1451. copy(lstPropTags.begin(), lstPropTags.end(), lpPropTagArray->__ptr);
  1452. *lppPropTags = lpPropTagArray;
  1453. return erSuccess;
  1454. }
  1455. // Simply matches the restriction with the given data. Make sure you pass all the data
  1456. // needed for the restriction in lpPropVals. (missing columns do not match, ever.)
  1457. ECRESULT ECGenericObjectTable::MatchRowRestrict(ECCacheManager* lpCacheManager, propValArray *lpPropVals, restrictTable *lpsRestrict, SUBRESTRICTIONRESULTS *lpSubResults, const ECLocale &locale, bool *lpfMatch, unsigned int *lpulSubRestriction)
  1458. {
  1459. ECRESULT er = erSuccess;
  1460. bool fMatch = false;
  1461. int lCompare = 0;
  1462. unsigned int ulSize = 0;
  1463. struct propVal *lpProp = NULL;
  1464. struct propVal *lpProp2 = NULL;
  1465. char* lpSearchString;
  1466. char* lpSearchData;
  1467. unsigned int ulSearchDataSize;
  1468. unsigned int ulSearchStringSize;
  1469. ULONG ulPropType;
  1470. ULONG ulFuzzyLevel;
  1471. unsigned int ulSubRestrict = 0;
  1472. entryId sEntryId;
  1473. unsigned int ulResId = 0;
  1474. unsigned int ulPropTagRestrict;
  1475. unsigned int ulPropTagValue;
  1476. if(lpulSubRestriction == NULL) // called externally
  1477. lpulSubRestriction = &ulSubRestrict;
  1478. switch(lpsRestrict->ulType) {
  1479. case RES_COMMENT:
  1480. if (lpsRestrict->lpComment == NULL)
  1481. return KCERR_INVALID_TYPE;
  1482. er = MatchRowRestrict(lpCacheManager, lpPropVals, lpsRestrict->lpComment->lpResTable, lpSubResults, locale, &fMatch, lpulSubRestriction);
  1483. break;
  1484. case RES_OR:
  1485. if (lpsRestrict->lpOr == NULL)
  1486. return KCERR_INVALID_TYPE;
  1487. fMatch = false;
  1488. for (gsoap_size_t i = 0; i < lpsRestrict->lpOr->__size; ++i) {
  1489. er = MatchRowRestrict(lpCacheManager, lpPropVals, lpsRestrict->lpOr->__ptr[i], lpSubResults, locale, &fMatch, lpulSubRestriction);
  1490. if(er != erSuccess)
  1491. return er;
  1492. if(fMatch) // found a restriction in an OR which matches, ignore the rest of the query
  1493. break;
  1494. }
  1495. break;
  1496. case RES_AND:
  1497. if (lpsRestrict->lpAnd == NULL)
  1498. return KCERR_INVALID_TYPE;
  1499. fMatch = true;
  1500. for (gsoap_size_t i = 0; i < lpsRestrict->lpAnd->__size; ++i) {
  1501. er = MatchRowRestrict(lpCacheManager, lpPropVals, lpsRestrict->lpAnd->__ptr[i], lpSubResults, locale, &fMatch, lpulSubRestriction);
  1502. if(er != erSuccess)
  1503. return er;
  1504. if(!fMatch) // found a restriction in an AND which doesn't match, ignore the rest of the query
  1505. break;
  1506. }
  1507. break;
  1508. case RES_NOT:
  1509. if (lpsRestrict->lpNot == NULL)
  1510. return KCERR_INVALID_TYPE;
  1511. er = MatchRowRestrict(lpCacheManager, lpPropVals, lpsRestrict->lpNot->lpNot, lpSubResults, locale, &fMatch, lpulSubRestriction);
  1512. if(er != erSuccess)
  1513. return er;
  1514. fMatch = !fMatch;
  1515. break;
  1516. case RES_CONTENT:
  1517. if (lpsRestrict->lpContent == NULL ||
  1518. lpsRestrict->lpContent->lpProp == NULL)
  1519. return KCERR_INVALID_TYPE;
  1520. // FIXME: FL_IGNORENONSPACE and FL_LOOSE are ignored
  1521. ulPropTagRestrict = lpsRestrict->lpContent->ulPropTag;
  1522. ulPropTagValue = lpsRestrict->lpContent->lpProp->ulPropTag;
  1523. // use the same string type in compares
  1524. if ((PROP_TYPE(ulPropTagRestrict) & PT_MV_STRING8) == PT_STRING8)
  1525. ulPropTagRestrict = CHANGE_PROP_TYPE(ulPropTagRestrict, PT_TSTRING);
  1526. else if ((PROP_TYPE(ulPropTagRestrict) & PT_MV_STRING8) == PT_MV_STRING8)
  1527. ulPropTagRestrict = CHANGE_PROP_TYPE(ulPropTagRestrict, PT_MV_TSTRING);
  1528. // @todo are MV properties in the compare prop allowed?
  1529. if ((PROP_TYPE(ulPropTagValue) & PT_MV_STRING8) == PT_STRING8)
  1530. ulPropTagValue = CHANGE_PROP_TYPE(ulPropTagValue, PT_TSTRING);
  1531. else if ((PROP_TYPE(ulPropTagValue) & PT_MV_STRING8) == PT_MV_STRING8)
  1532. ulPropTagValue = CHANGE_PROP_TYPE(ulPropTagValue, PT_MV_TSTRING);
  1533. if( PROP_TYPE(ulPropTagRestrict) != PT_TSTRING &&
  1534. PROP_TYPE(ulPropTagRestrict) != PT_BINARY &&
  1535. PROP_TYPE(ulPropTagRestrict) != PT_MV_TSTRING &&
  1536. PROP_TYPE(ulPropTagRestrict) != PT_MV_BINARY &&
  1537. lpsRestrict->lpContent->lpProp != NULL)
  1538. {
  1539. assert(false);
  1540. fMatch = false;
  1541. break;
  1542. }
  1543. // find using original proptag from restriction
  1544. lpProp = FindProp(lpPropVals, lpsRestrict->lpContent->ulPropTag);
  1545. if(lpProp == NULL) {
  1546. fMatch = false;
  1547. break;
  1548. } else {
  1549. unsigned int ulScan = 1;
  1550. if(ulPropTagRestrict & MV_FLAG)
  1551. {
  1552. if(PROP_TYPE(ulPropTagRestrict) == PT_MV_TSTRING)
  1553. ulScan = lpProp->Value.mvszA.__size;
  1554. else
  1555. ulScan = lpProp->Value.mvbin.__size;
  1556. }
  1557. ulPropType = PROP_TYPE(ulPropTagRestrict)&~MVI_FLAG;
  1558. if(PROP_TYPE(ulPropTagValue) == PT_TSTRING) {
  1559. lpSearchString = lpsRestrict->lpContent->lpProp->Value.lpszA;
  1560. ulSearchStringSize = (lpSearchString)?strlen(lpSearchString):0;
  1561. }else {
  1562. lpSearchString = (char*)lpsRestrict->lpContent->lpProp->Value.bin->__ptr;
  1563. ulSearchStringSize = lpsRestrict->lpContent->lpProp->Value.bin->__size;
  1564. }
  1565. // Default match is false
  1566. fMatch = false;
  1567. for (unsigned int ulPos = 0; ulPos < ulScan; ++ulPos) {
  1568. if(ulPropTagRestrict & MV_FLAG)
  1569. {
  1570. if(PROP_TYPE(ulPropTagRestrict) == PT_MV_TSTRING) {
  1571. lpSearchData = lpProp->Value.mvszA.__ptr[ulPos];
  1572. ulSearchDataSize = (lpSearchData)?strlen(lpSearchData):0;
  1573. }else {
  1574. lpSearchData = (char*)lpProp->Value.mvbin.__ptr[ulPos].__ptr;
  1575. ulSearchDataSize = lpProp->Value.mvbin.__ptr[ulPos].__size;
  1576. }
  1577. }else {
  1578. if(PROP_TYPE(ulPropTagRestrict) == PT_TSTRING) {
  1579. lpSearchData = lpProp->Value.lpszA;
  1580. ulSearchDataSize = (lpSearchData)?strlen(lpSearchData):0;
  1581. }else {
  1582. lpSearchData = (char*)lpProp->Value.bin->__ptr;
  1583. ulSearchDataSize = lpProp->Value.bin->__size;
  1584. }
  1585. }
  1586. ulFuzzyLevel = lpsRestrict->lpContent->ulFuzzyLevel;
  1587. switch(ulFuzzyLevel & 0xFFFF) {
  1588. case FL_FULLSTRING:
  1589. if(ulSearchDataSize == ulSearchStringSize)
  1590. if ((ulPropType == PT_TSTRING && (ulFuzzyLevel & FL_IGNORECASE) && u8_iequals(lpSearchData, lpSearchString, locale)) ||
  1591. (ulPropType == PT_TSTRING && ((ulFuzzyLevel & FL_IGNORECASE) == 0) && u8_equals(lpSearchData, lpSearchString, locale)) ||
  1592. (ulPropType != PT_TSTRING && memcmp(lpSearchData, lpSearchString, ulSearchDataSize) == 0))
  1593. fMatch = true;
  1594. break;
  1595. case FL_PREFIX:
  1596. if(ulSearchDataSize >= ulSearchStringSize)
  1597. if ((ulPropType == PT_TSTRING && (ulFuzzyLevel & FL_IGNORECASE) && u8_istartswith(lpSearchData, lpSearchString, locale)) ||
  1598. (ulPropType == PT_TSTRING && ((ulFuzzyLevel & FL_IGNORECASE) == 0) && u8_startswith(lpSearchData, lpSearchString, locale)) ||
  1599. (ulPropType != PT_TSTRING && memcmp(lpSearchData, lpSearchString, ulSearchStringSize) == 0))
  1600. fMatch = true;
  1601. break;
  1602. case FL_SUBSTRING:
  1603. if ((ulPropType == PT_TSTRING && (ulFuzzyLevel & FL_IGNORECASE) && u8_icontains(lpSearchData, lpSearchString, locale)) ||
  1604. (ulPropType == PT_TSTRING && ((ulFuzzyLevel & FL_IGNORECASE) == 0) && u8_contains(lpSearchData, lpSearchString, locale)) ||
  1605. (ulPropType != PT_TSTRING && memsubstr(lpSearchData, ulSearchDataSize, lpSearchString, ulSearchStringSize) == 0))
  1606. fMatch = true;
  1607. break;
  1608. }
  1609. if(fMatch)
  1610. break;
  1611. }
  1612. }
  1613. break;
  1614. case RES_PROPERTY:
  1615. if (lpsRestrict->lpProp == NULL ||
  1616. lpsRestrict->lpProp->lpProp == NULL)
  1617. return KCERR_INVALID_TYPE;
  1618. ulPropTagRestrict = lpsRestrict->lpProp->ulPropTag;
  1619. ulPropTagValue = lpsRestrict->lpProp->lpProp->ulPropTag;
  1620. // use the same string type in compares
  1621. if ((PROP_TYPE(ulPropTagRestrict) & PT_MV_STRING8) == PT_STRING8)
  1622. ulPropTagRestrict = CHANGE_PROP_TYPE(ulPropTagRestrict, PT_TSTRING);
  1623. else if ((PROP_TYPE(ulPropTagRestrict) & PT_MV_STRING8) == PT_MV_STRING8)
  1624. ulPropTagRestrict = CHANGE_PROP_TYPE(ulPropTagRestrict, PT_MV_TSTRING);
  1625. if (PROP_TYPE(ulPropTagValue) == PT_STRING8)
  1626. ulPropTagValue = CHANGE_PROP_TYPE(ulPropTagValue, PT_TSTRING);
  1627. if((PROP_TYPE(ulPropTagRestrict) & ~MV_FLAG) != PROP_TYPE(ulPropTagValue))
  1628. // cannot compare two different types, except mvprop -> prop
  1629. return KCERR_INVALID_TYPE;
  1630. #if 1 /* HAVE_REGEX_H */
  1631. if(lpsRestrict->lpProp->ulType == RELOP_RE) {
  1632. regex_t reg;
  1633. // find using original restriction proptag
  1634. lpProp = FindProp(lpPropVals, lpsRestrict->lpProp->ulPropTag);
  1635. if(lpProp == NULL) {
  1636. fMatch = false;
  1637. break;
  1638. }
  1639. // @todo add support for ulPropTagRestrict PT_MV_TSTRING
  1640. if (PROP_TYPE(ulPropTagValue) != PT_TSTRING ||
  1641. PROP_TYPE(ulPropTagRestrict) != PT_TSTRING)
  1642. return KCERR_INVALID_TYPE;
  1643. if(regcomp(&reg, lpsRestrict->lpProp->lpProp->Value.lpszA, REG_NOSUB | REG_NEWLINE | REG_ICASE) != 0) {
  1644. fMatch = false;
  1645. break;
  1646. }
  1647. if(regexec(&reg, lpProp->Value.lpszA, 0, NULL, 0) == 0)
  1648. fMatch = true;
  1649. regfree(&reg);
  1650. // Finished for this restriction
  1651. break;
  1652. }
  1653. #endif
  1654. if(PROP_ID(ulPropTagRestrict) == PROP_ID(PR_ANR))
  1655. {
  1656. for (size_t j = 0; j < ARRAY_SIZE(sANRProps); ++j) {
  1657. lpProp = FindProp(lpPropVals, sANRProps[j]);
  1658. // We need this because CompareProp will fail if the types are not the same
  1659. if(lpProp) {
  1660. lpProp->ulPropTag = lpsRestrict->lpProp->lpProp->ulPropTag;
  1661. CompareProp(lpProp, lpsRestrict->lpProp->lpProp, locale, &lCompare); //IGNORE error
  1662. } else
  1663. continue;
  1664. // PR_ANR has special semantics, lCompare is 1 if the substring is found, 0 if not
  1665. // Note that RELOP_EQ will work as expected, but RELOP_GT and RELOP_LT will
  1666. // not work. Use of these is undefined anyway. RELOP_NE is useless since one of the
  1667. // strings will definitely not match, so RELOP_NE will almost match.
  1668. lCompare = lCompare ? 0 : -1;
  1669. fMatch = match(lpsRestrict->lpProp->ulType, lCompare);
  1670. if(fMatch)
  1671. break;
  1672. }
  1673. // Finished for this restriction
  1674. break;
  1675. }else {
  1676. // find using original restriction proptag
  1677. lpProp = FindProp(lpPropVals, lpsRestrict->lpProp->ulPropTag);
  1678. if(lpProp == NULL) {
  1679. if(lpsRestrict->lpProp->ulType == RELOP_NE)
  1680. fMatch = true;
  1681. else
  1682. fMatch = false;
  1683. break;
  1684. }
  1685. if((ulPropTagRestrict&MV_FLAG)) {
  1686. er = CompareMVPropWithProp(lpProp, lpsRestrict->lpProp->lpProp, lpsRestrict->lpProp->ulType, locale, &fMatch);
  1687. if(er != erSuccess)
  1688. {
  1689. assert(false);
  1690. er = erSuccess;
  1691. fMatch = false;
  1692. break;
  1693. }
  1694. } else {
  1695. er = CompareProp(lpProp, lpsRestrict->lpProp->lpProp, locale, &lCompare);
  1696. if(er != erSuccess)
  1697. {
  1698. assert(false);
  1699. er = erSuccess;
  1700. fMatch = false;
  1701. break;
  1702. }
  1703. fMatch = match(lpsRestrict->lpProp->ulType, lCompare);
  1704. }
  1705. }// if(ulPropTagRestrict == PR_ANR)
  1706. break;
  1707. case RES_COMPAREPROPS:
  1708. if (lpsRestrict->lpCompare == NULL)
  1709. return KCERR_INVALID_TYPE;
  1710. unsigned int ulPropTag1;
  1711. unsigned int ulPropTag2;
  1712. ulPropTag1 = lpsRestrict->lpCompare->ulPropTag1;
  1713. ulPropTag2 = lpsRestrict->lpCompare->ulPropTag2;
  1714. // use the same string type in compares
  1715. if ((PROP_TYPE(ulPropTag1) & PT_MV_STRING8) == PT_STRING8)
  1716. ulPropTag1 = CHANGE_PROP_TYPE(ulPropTag1, PT_TSTRING);
  1717. else if ((PROP_TYPE(ulPropTag1) & PT_MV_STRING8) == PT_MV_STRING8)
  1718. ulPropTag1 = CHANGE_PROP_TYPE(ulPropTag1, PT_MV_TSTRING);
  1719. // use the same string type in compares
  1720. if ((PROP_TYPE(ulPropTag2) & PT_MV_STRING8) == PT_STRING8)
  1721. ulPropTag2 = CHANGE_PROP_TYPE(ulPropTag2, PT_TSTRING);
  1722. else if ((PROP_TYPE(ulPropTag2) & PT_MV_STRING8) == PT_MV_STRING8)
  1723. ulPropTag2 = CHANGE_PROP_TYPE(ulPropTag2, PT_MV_TSTRING);
  1724. // FIXME: Is this check correct, PT_STRING8 vs PT_ERROR == false and not a error? (RELOP_NE == true)
  1725. if (PROP_TYPE(ulPropTag1) != PROP_TYPE(ulPropTag2))
  1726. // cannot compare two different types
  1727. return KCERR_INVALID_TYPE;
  1728. // find using original restriction proptag
  1729. lpProp = FindProp(lpPropVals, lpsRestrict->lpCompare->ulPropTag1);
  1730. lpProp2 = FindProp(lpPropVals, lpsRestrict->lpCompare->ulPropTag2);
  1731. if(lpProp == NULL || lpProp2 == NULL) {
  1732. fMatch = false;
  1733. break;
  1734. }
  1735. er = CompareProp(lpProp, lpProp2, locale, &lCompare);
  1736. if(er != erSuccess)
  1737. {
  1738. assert(false);
  1739. er = erSuccess;
  1740. fMatch = false;
  1741. break;
  1742. }
  1743. switch(lpsRestrict->lpCompare->ulType) {
  1744. case RELOP_GE:
  1745. fMatch = lCompare >= 0;
  1746. break;
  1747. case RELOP_GT:
  1748. fMatch = lCompare > 0;
  1749. break;
  1750. case RELOP_LE:
  1751. fMatch = lCompare <= 0;
  1752. break;
  1753. case RELOP_LT:
  1754. fMatch = lCompare < 0;
  1755. break;
  1756. case RELOP_NE:
  1757. fMatch = lCompare != 0;
  1758. break;
  1759. case RELOP_RE:
  1760. fMatch = false; // FIXME ?? how should this work ??
  1761. break;
  1762. case RELOP_EQ:
  1763. fMatch = lCompare == 0;
  1764. break;
  1765. }
  1766. break;
  1767. case RES_BITMASK:
  1768. if (lpsRestrict->lpBitmask == NULL)
  1769. return KCERR_INVALID_TYPE;
  1770. // We can only bitmask 32-bit LONG values (aka ULONG)
  1771. if (PROP_TYPE(lpsRestrict->lpBitmask->ulPropTag) != PT_LONG)
  1772. return KCERR_INVALID_TYPE;
  1773. lpProp = FindProp(lpPropVals, lpsRestrict->lpBitmask->ulPropTag);
  1774. if(lpProp == NULL) {
  1775. fMatch = false;
  1776. break;
  1777. }
  1778. fMatch = (lpProp->Value.ul & lpsRestrict->lpBitmask->ulMask) > 0;
  1779. if(lpsRestrict->lpBitmask->ulType == BMR_EQZ)
  1780. fMatch = !fMatch;
  1781. break;
  1782. case RES_SIZE:
  1783. if (lpsRestrict->lpSize == NULL)
  1784. return KCERR_INVALID_TYPE;
  1785. lpProp = FindProp(lpPropVals, lpsRestrict->lpSize->ulPropTag);
  1786. if (lpProp == NULL)
  1787. return KCERR_INVALID_TYPE;
  1788. ulSize = PropSize(lpProp);
  1789. lCompare = ulSize - lpsRestrict->lpSize->cb;
  1790. switch(lpsRestrict->lpSize->ulType) {
  1791. case RELOP_GE:
  1792. fMatch = lCompare >= 0;
  1793. break;
  1794. case RELOP_GT:
  1795. fMatch = lCompare > 0;
  1796. break;
  1797. case RELOP_LE:
  1798. fMatch = lCompare <= 0;
  1799. break;
  1800. case RELOP_LT:
  1801. fMatch = lCompare < 0;
  1802. break;
  1803. case RELOP_NE:
  1804. fMatch = lCompare != 0;
  1805. break;
  1806. case RELOP_RE:
  1807. fMatch = false; // FIXME ?? how should this work ??
  1808. break;
  1809. case RELOP_EQ:
  1810. fMatch = lCompare == 0;
  1811. break;
  1812. }
  1813. break;
  1814. case RES_EXIST:
  1815. if (lpsRestrict->lpExist == NULL)
  1816. return KCERR_INVALID_TYPE;
  1817. lpProp = FindProp(lpPropVals, lpsRestrict->lpExist->ulPropTag);
  1818. fMatch = (lpProp != NULL);
  1819. break;
  1820. case RES_SUBRESTRICTION:
  1821. lpProp = FindProp(lpPropVals, PR_ENTRYID);
  1822. if (lpProp == NULL)
  1823. return KCERR_INVALID_TYPE;
  1824. if(lpSubResults == NULL) {
  1825. fMatch = false;
  1826. } else {
  1827. // Find out if this object matches this subrestriction with the passed
  1828. // subrestriction results.
  1829. if(lpSubResults->size() <= ulSubRestrict) {
  1830. fMatch = false; // No results in the results list for this subquery ??
  1831. } else {
  1832. fMatch = false;
  1833. sEntryId.__ptr = lpProp->Value.bin->__ptr;
  1834. sEntryId.__size = lpProp->Value.bin->__size;
  1835. if(lpCacheManager->GetObjectFromEntryId(&sEntryId, &ulResId) == erSuccess)
  1836. {
  1837. auto r = (*lpSubResults)[ulSubRestrict]->find(ulResId); // If the item is in the set, it matched
  1838. if (r != (*lpSubResults)[ulSubRestrict]->cend())
  1839. fMatch = true;
  1840. }
  1841. }
  1842. }
  1843. break;
  1844. default:
  1845. return KCERR_INVALID_TYPE;
  1846. }
  1847. *lpfMatch = fMatch;
  1848. return er;
  1849. }
  1850. bool ECGenericObjectTable::IsMVSet()
  1851. {
  1852. return (m_bMVSort | m_bMVCols);
  1853. }
  1854. void ECGenericObjectTable::SetTableId(unsigned int ulTableId)
  1855. {
  1856. m_ulTableId = ulTableId;
  1857. }
  1858. ECRESULT ECGenericObjectTable::Clear()
  1859. {
  1860. scoped_rlock biglock(m_hLock);
  1861. // Clear old entries
  1862. mapObjects.clear();
  1863. lpKeyTable->Clear();
  1864. m_mapLeafs.clear();
  1865. for (const auto &p : m_mapCategories)
  1866. delete p.second;
  1867. m_mapCategories.clear();
  1868. m_mapSortedCategories.clear();
  1869. return hrSuccess;
  1870. }
  1871. ECRESULT ECGenericObjectTable::Load()
  1872. {
  1873. return hrSuccess;
  1874. }
  1875. ECRESULT ECGenericObjectTable::Populate()
  1876. {
  1877. scoped_rlock biglock(m_hLock);
  1878. if(m_bPopulated)
  1879. return erSuccess;
  1880. m_bPopulated = true;
  1881. return Load();
  1882. }
  1883. // Sort functions, overide this functions as you used a caching system
  1884. ECRESULT ECGenericObjectTable::IsSortKeyExist(const sObjectTableKey* lpsRowItem, unsigned int ulPropTag)
  1885. {
  1886. return KCERR_NOT_FOUND;
  1887. }
  1888. ECRESULT ECGenericObjectTable::GetSortKey(sObjectTableKey* lpsRowItem, unsigned int ulPropTag, unsigned int *lpSortLen, unsigned char **lppSortData)
  1889. {
  1890. assert(false);
  1891. return KCERR_NO_SUPPORT;
  1892. }
  1893. ECRESULT ECGenericObjectTable::SetSortKey(sObjectTableKey* lpsRowItem, unsigned int ulPropTag, unsigned int ulSortLen, unsigned char *lpSortData)
  1894. {
  1895. return KCERR_NO_SUPPORT;
  1896. }
  1897. // Category handling functions
  1898. /*
  1899. * GENERAL workings of categorization
  1900. *
  1901. * Due to min/max categories, the order of rows in the key table (m_lpKeyTable) in not predictable by looking
  1902. * only at a new row, since we don't know what the min/max value for the category is. We therefore have a second
  1903. * sorted list of categories which is purely for looking up if a category exists, and what its min/max values are.
  1904. * This list is m_mapSortedCategories.
  1905. *
  1906. * The order of rows in m_lpKeyTable is the actual order of rows that will be seen by the MAPI Client.
  1907. *
  1908. * quick overview:
  1909. *
  1910. * When a new row is added, we do the following:
  1911. * - Look in m_mapSortedCategories with the categorized properties to see if we already have the category
  1912. * -on new:
  1913. * - Add category to both mapSortedCategories and mapCategories
  1914. * - Initialize counters and possibly min/max value
  1915. * -on existing:
  1916. * - Update counters (unread,count)
  1917. * - Update min/max value
  1918. * -on change of min/max value:
  1919. * - reorder *all* rows of the category
  1920. * - Track the row in m_mapLeafs
  1921. *
  1922. * When a row is removed, we do the following
  1923. * - Find the row in m_mapLeafs
  1924. * - Get the category of the row
  1925. * - Update counters of the category
  1926. * - Update min/max value of the category
  1927. * -on change of min/max value and non-empty category:
  1928. * - reorder *all* rows of the category
  1929. * - If count == 0, remove category
  1930. *
  1931. * We currently support only one min/max category in the table. This is actually only enforced in ECCategory which
  1932. * tracks only one sCurMinMax, the rest of the code should be pretty much able to handle multiple levels of min/max
  1933. * categories.
  1934. */
  1935. /**
  1936. * Add or modify a category row
  1937. *
  1938. * Called just before the actual message row is added to the table.
  1939. *
  1940. * Due to min/max handling, this function may modify MANY rows in the table because the entire category needed to be relocated.
  1941. *
  1942. * @param sObjKey Object key of new (non-category) row
  1943. * @param lpProps Properties of the new or modified row
  1944. * @param cProps Number of properties in lpProps
  1945. * @param ulFlags Notification flags
  1946. * @param fUnread TRUE if the new state of the object in sObjKey is UNREAD
  1947. * @param lpfHidden Returns if the new row should be hidden because the category is collapsed
  1948. * @param lppCategory Returns a reference to the new or existing category that the item sObjKey should be added to
  1949. */
  1950. ECRESULT ECGenericObjectTable::AddCategoryBeforeAddRow(sObjectTableKey sObjKey, struct propVal *lpProps, unsigned int cProps, unsigned int ulFlags, bool fUnread, bool *lpfHidden, ECCategory **lppCategory)
  1951. {
  1952. ECRESULT er = erSuccess;
  1953. bool fPrevUnread = false;
  1954. bool fNewLeaf = false;
  1955. unsigned int i = 0;
  1956. std::unique_ptr<unsigned int[]> lpSortLen;
  1957. std::unique_ptr<unsigned char *[]> lppSortKeys;
  1958. std::unique_ptr<unsigned char[]> lpSortFlags;
  1959. sObjectTableKey sPrevRow(0,0);
  1960. ECCategory *lpCategory = NULL;
  1961. LEAFINFO sLeafInfo;
  1962. ECCategory *lpParent = NULL;
  1963. ECKeyTable::UpdateType ulAction;
  1964. sObjectTableKey sCatRow;
  1965. ECLeafMap::const_iterator iterLeafs;
  1966. int fResult = 0;
  1967. bool fCollapsed = false;
  1968. bool fHidden = false;
  1969. if(m_ulCategories == 0)
  1970. goto exit;
  1971. lpSortLen.reset(new unsigned int[cProps]);
  1972. lppSortKeys.reset(new unsigned char *[cProps]);
  1973. lpSortFlags.reset(new unsigned char[cProps]);
  1974. // Build binary sort keys
  1975. // +1 because we may have a trailing category followed by a MINMAX column
  1976. for (i = 0; i < m_ulCategories + 1 && i < cProps; ++i) {
  1977. if(GetBinarySortKey(&lpProps[i], &lpSortLen[i], &lppSortKeys[i]) != erSuccess)
  1978. lppSortKeys[i] = NULL;
  1979. if(GetSortFlags(lpProps[i].ulPropTag, &lpSortFlags[i]) != erSuccess)
  1980. lpSortFlags[i] = 0;
  1981. }
  1982. // See if we're dealing with a changed row, not a new row
  1983. iterLeafs = m_mapLeafs.find(sObjKey);
  1984. if (iterLeafs != m_mapLeafs.cend()) {
  1985. fPrevUnread = iterLeafs->second.fUnread;
  1986. // The leaf was already in the table, compare new properties of the leaf with those of the category it
  1987. // was in.
  1988. for (i = 0; i < iterLeafs->second.lpCategory->m_cProps && i < cProps; ++i) {
  1989. // If the type is different (ie PT_ERROR first, PT_STRING8 now, then it has definitely changed ..)
  1990. if(PROP_TYPE(lpProps[i].ulPropTag) != PROP_TYPE(iterLeafs->second.lpCategory->m_lpProps[i].ulPropTag))
  1991. break;
  1992. // Otherwise, compare the properties
  1993. er = CompareProp(&iterLeafs->second.lpCategory->m_lpProps[i], &lpProps[i], m_locale, &fResult);
  1994. if (er != erSuccess)
  1995. goto exit;
  1996. if(fResult != 0)
  1997. break;
  1998. }
  1999. if(iterLeafs->second.lpCategory->m_cProps && i < cProps) {
  2000. // One of the category properties has changed, remove the row from the old category
  2001. RemoveCategoryAfterRemoveRow(sObjKey, ulFlags);
  2002. fNewLeaf = true; // We're re-adding the leaf
  2003. } else if (fUnread == iterLeafs->second.fUnread) {
  2004. // Nothing to do, the leaf was already in the correct category, and the readstate has not changed
  2005. goto exit;
  2006. }
  2007. } else {
  2008. fNewLeaf = true;
  2009. }
  2010. // For each level, check if category already exists in key table (LowerBound), gives sObjectTableKey
  2011. for (i = 0; i < m_ulCategories && i < cProps; ++i) {
  2012. unsigned int ulDepth = i;
  2013. bool fCategoryMoved = false; // TRUE if the entire category has moved somewhere (due to CATEG_MIN / CATEG_MAX change)
  2014. ECTableRow row(sObjectTableKey(0, 0), i + 1, lpSortLen.get(), lpSortFlags.get(), lppSortKeys.get(), false);
  2015. // Find the actual category in our sorted category map
  2016. auto iterCategoriesSorted = m_mapSortedCategories.find(row);
  2017. // Include the next sort order if it s CATEG_MIN or CATEG_MAX
  2018. if(lpsSortOrderArray->__size > (int)i+1 && ISMINMAX(lpsSortOrderArray->__ptr[i+1].ulOrder))
  2019. ++i;
  2020. if (iterCategoriesSorted == m_mapSortedCategories.cend()) {
  2021. assert(fNewLeaf); // The leaf must be new if the category is new
  2022. // Category not available yet, add it now
  2023. sCatRow.ulObjId = 0;
  2024. sCatRow.ulOrderId = m_ulCategory;
  2025. // We are hidden if our parent was collapsed
  2026. fHidden = fCollapsed;
  2027. // This category is itself collapsed if our parent was collapsed, or if we should be collapsed due to m_ulExpanded
  2028. fCollapsed = fCollapsed || (ulDepth >= m_ulExpanded);
  2029. lpCategory = new ECCategory(m_ulCategory, lpProps, ulDepth+1, i+1, lpParent, ulDepth, !fCollapsed, m_locale);
  2030. ++m_ulCategory;
  2031. lpCategory->IncLeaf(); // New category has 1 leaf
  2032. // Make sure the category has the current row as min/max value
  2033. er = UpdateCategoryMinMax(sObjKey, lpCategory, i, lpProps, cProps, NULL);
  2034. if(er != erSuccess)
  2035. goto exit;
  2036. if(fUnread)
  2037. lpCategory->IncUnread();
  2038. // Add the category into our sorted-category list and numbered-category list
  2039. assert(m_mapSortedCategories.find(row) == m_mapSortedCategories.end());
  2040. m_mapCategories[sCatRow] = lpCategory;
  2041. lpCategory->iSortedCategory = m_mapSortedCategories.insert(std::make_pair(row, sCatRow)).first;
  2042. // Update the keytable with the effective sort columns
  2043. er = UpdateKeyTableRow(lpCategory, &sCatRow, lpProps, i+1, fHidden, &sPrevRow, &ulAction);
  2044. if (er != erSuccess)
  2045. goto exit;
  2046. lpParent = lpCategory;
  2047. } else {
  2048. // Category already available
  2049. sCatRow = iterCategoriesSorted->second;
  2050. // Get prev row for notification purposes
  2051. if(lpKeyTable->GetPreviousRow(&sCatRow, &sPrevRow) != erSuccess) {
  2052. sPrevRow.ulObjId = 0;
  2053. sPrevRow.ulOrderId = 0;
  2054. }
  2055. auto iterCategory = m_mapCategories.find(sCatRow);
  2056. if (iterCategory == m_mapCategories.cend()) {
  2057. assert(false);
  2058. er = KCERR_NOT_FOUND;
  2059. goto exit;
  2060. }
  2061. lpCategory = iterCategory->second;
  2062. // Increase leaf count of category (each level must be increased by one) for a new leaf
  2063. if(fNewLeaf) {
  2064. lpCategory->IncLeaf();
  2065. if(fUnread)
  2066. lpCategory->IncUnread();
  2067. } else {
  2068. // Increase or decrease unread counter depending on change of the leaf's unread state
  2069. if(fUnread && !fPrevUnread)
  2070. lpCategory->IncUnread();
  2071. if(!fUnread && fPrevUnread)
  2072. lpCategory->DecUnread();
  2073. }
  2074. // This category was hidden if the parent was collapsed
  2075. fHidden = fCollapsed;
  2076. // Remember if this category was collapsed
  2077. fCollapsed = !lpCategory->m_fExpanded;
  2078. // Update category min/max values
  2079. er = UpdateCategoryMinMax(sObjKey, lpCategory, i, lpProps, cProps, &fCategoryMoved);
  2080. if(er != erSuccess)
  2081. goto exit;
  2082. ulAction = ECKeyTable::TABLE_ROW_MODIFY;
  2083. }
  2084. if (fCategoryMoved) {
  2085. ECObjectTableList lstObjects;
  2086. // The min/max value of this category has changed. We have to move all the rows in the category
  2087. // somewhere else in the table.
  2088. // Get the rows that are affected
  2089. er = lpKeyTable->GetRowsBySortPrefix(&sCatRow, &lstObjects);
  2090. if (er != erSuccess)
  2091. goto exit;
  2092. // Update the keytable to reflect the new change
  2093. for (auto &obj : lstObjects) {
  2094. // Update the keytable with the new effective sort data for this column
  2095. bool bDescend = lpsSortOrderArray->__ptr[ulDepth].ulOrder == EC_TABLE_SORT_DESCEND; // Column we're updating is descending
  2096. er = lpKeyTable->UpdatePartialSortKey(&obj,
  2097. ulDepth, lppSortKeys[i], lpSortLen[i],
  2098. lpSortFlags[i] | (bDescend ? TABLEROW_FLAG_DESC : 0),
  2099. &sPrevRow, &fHidden, &ulAction);
  2100. if (er != erSuccess)
  2101. goto exit;
  2102. if ((ulFlags & OBJECTTABLE_NOTIFY) && !fHidden)
  2103. AddTableNotif(ulAction, obj, &sPrevRow);
  2104. }
  2105. }
  2106. // Send notification if required (only the category header has changed)
  2107. else if((ulFlags & OBJECTTABLE_NOTIFY) && !fHidden) {
  2108. AddTableNotif(ulAction, sCatRow, &sPrevRow);
  2109. }
  2110. }
  2111. // lpCategory is now the deepest category, and therefore the category we're adding the leaf to
  2112. // Add sObjKey to leaf list via LEAFINFO
  2113. sLeafInfo.lpCategory = lpCategory;
  2114. sLeafInfo.fUnread = fUnread;
  2115. m_mapLeafs[sObjKey] = sLeafInfo;
  2116. // The item that the request was for is hidden if the deepest category was collapsed
  2117. if (lpfHidden != NULL)
  2118. *lpfHidden = fCollapsed;
  2119. if(lppCategory)
  2120. *lppCategory = lpCategory;
  2121. assert(m_mapCategories.size() == m_mapSortedCategories.size());
  2122. exit:
  2123. if (lppSortKeys != nullptr)
  2124. for (i = 0; i < m_ulCategories + 1 && i < cProps; ++i)
  2125. delete[] lppSortKeys[i];
  2126. return er;
  2127. }
  2128. /**
  2129. * Update a category min/max value if needed
  2130. *
  2131. * This function updates the min/max value if the category is part of a min/max sorting scheme.
  2132. *
  2133. * @param sKey Key of the category
  2134. * @param lpCategory Category to update
  2135. * @param i Column id of the possible min/max sort
  2136. * @param lpProps Properties for the category
  2137. * @param cProps Number of properties in lpProps
  2138. * @param lpfModified Returns whether the category min/max value was changed
  2139. * @return result
  2140. */
  2141. ECRESULT ECGenericObjectTable::UpdateCategoryMinMax(sObjectTableKey &sKey,
  2142. ECCategory *lpCategory, size_t i, struct propVal *lpProps, size_t cProps,
  2143. bool *lpfModified)
  2144. {
  2145. if (lpsSortOrderArray->__size < 0 ||
  2146. static_cast<size_t>(lpsSortOrderArray->__size) <= i ||
  2147. !ISMINMAX(lpsSortOrderArray->__ptr[i].ulOrder))
  2148. return erSuccess;
  2149. lpCategory->UpdateMinMax(sKey, i, &lpProps[i], lpsSortOrderArray->__ptr[i].ulOrder == EC_TABLE_SORT_CATEG_MAX, lpfModified);
  2150. return erSuccess;
  2151. }
  2152. /**
  2153. * Creates a row in the key table
  2154. *
  2155. * The only complexity of this function is when doing min/max categorization; consider the sort order
  2156. *
  2157. * CONVERSATION_TOPIC ASC, DATE CATEG_MAX, DATE DESC
  2158. * with ulCategories = 1
  2159. *
  2160. * This effectively generates the following category row in the key table:
  2161. *
  2162. * MAX_DATE, CONVERSATION_TOPIC for the category and
  2163. * MAX_DATE, CONVERSATION_TOPIC, DATE for the data row
  2164. *
  2165. * This means we have to get the 'max date' part, generate a sortkey, and switch the order for the first
  2166. * two columns, and add that to the key table.
  2167. *
  2168. * @param lpCategory For a normal row, the category to which it belongs
  2169. * @param lpsRowKey The row key of the row
  2170. * @param ulDepth Number properties in lpProps/cValues to process. For normal rows, ulDepth == cValues
  2171. * @param lpProps Properties from the database of the row
  2172. * @param cValues Number of properties in lpProps
  2173. * @param fHidden TRUE if the row is to be hidden
  2174. * @param sPrevRow Previous row ID
  2175. * @param lpulAction Action performed
  2176. * @return result
  2177. */
  2178. ECRESULT ECGenericObjectTable::UpdateKeyTableRow(ECCategory *lpCategory, sObjectTableKey *lpsRowKey, struct propVal *lpProps, unsigned int cValues, bool fHidden, sObjectTableKey *lpsPrevRow, ECKeyTable::UpdateType *lpulAction)
  2179. {
  2180. ECRESULT er = erSuccess;
  2181. struct propVal sProp;
  2182. struct sortOrderArray *lpsSortOrderArray = this->lpsSortOrderArray;
  2183. struct sortOrder sSortHierarchy = { PR_EC_HIERARCHYID, EC_TABLE_SORT_DESCEND };
  2184. struct sortOrderArray sSortSimple = { &sSortHierarchy, 1 };
  2185. int n = 0;
  2186. assert(cValues <= static_cast<unsigned int>(lpsSortOrderArray->__size));
  2187. if(cValues == 0) {
  2188. // No sort columns. We use the object ID as the sorting
  2189. // key. This is fairly arbitrary as any sort order would be okay seen as no sort order was specified. However, sorting
  2190. // in this way makes sure that new items are sorted FIRST by default, which is a logical view when debugging. Also, if
  2191. // any algorithm does assumptions based on the first row, it is best to have the first row be the newest row; this is what
  2192. // happens when you export messages from OLK to a PST and it does a memory calculation of nItems * size_of_first_entryid
  2193. // for the memory requirements of all entryids. (which crashes if you don't do it properly)
  2194. sProp.ulPropTag = PR_EC_HIERARCHYID;
  2195. sProp.Value.ul = lpsRowKey->ulObjId;
  2196. sProp.__union = SOAP_UNION_propValData_ul;
  2197. cValues = 1;
  2198. lpProps = &sProp;
  2199. lpsSortOrderArray = &sSortSimple;
  2200. }
  2201. std::unique_ptr<struct propVal[]> lpOrderedProps(new struct propVal[cValues]);
  2202. std::unique_ptr<unsigned int[]> lpSortLen(new unsigned int[cValues]);
  2203. std::unique_ptr<unsigned char *[]> lppSortKeys(new unsigned char *[cValues]);
  2204. std::unique_ptr<unsigned char[]> lpSortFlags(new unsigned char[cValues]);
  2205. memset(lpOrderedProps.get(), 0, sizeof(struct propVal) * cValues);
  2206. memset(lpSortLen.get(), 0, sizeof(unsigned int) * cValues);
  2207. memset(lppSortKeys.get(), 0, sizeof(unsigned char *) * cValues);
  2208. memset(lpSortFlags.get(), 0, sizeof(unsigned char) * cValues);
  2209. for (unsigned int i = 0; i < cValues; ++i) {
  2210. if (ISMINMAX(lpsSortOrderArray->__ptr[i].ulOrder)) {
  2211. if (n == 0 || !lpCategory)
  2212. // Min/max ignored if the row is not in a category
  2213. continue;
  2214. // Swap around the current and the previous sorting order
  2215. lpOrderedProps[n] = lpOrderedProps[n-1];
  2216. // Get actual sort order from category
  2217. if(lpCategory->GetProp(NULL, lpsSortOrderArray->__ptr[n].ulPropTag, &lpOrderedProps[n-1]) != erSuccess) {
  2218. lpOrderedProps[n-1].ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(lpsSortOrderArray->__ptr[n].ulPropTag));
  2219. lpOrderedProps[n-1].Value.ul = KCERR_NOT_FOUND;
  2220. lpOrderedProps[n-1].__union = SOAP_UNION_propValData_ul;
  2221. }
  2222. } else {
  2223. er = CopyPropVal(&lpProps[n], &lpOrderedProps[n], NULL, false);
  2224. if(er != erSuccess)
  2225. goto exit;
  2226. }
  2227. ++n;
  2228. }
  2229. // Build binary sort keys from updated data
  2230. for (int i = 0; i < n; ++i) {
  2231. if(GetBinarySortKey(&lpOrderedProps[i], &lpSortLen[i], &lppSortKeys[i]) != erSuccess)
  2232. lppSortKeys[i] = NULL;
  2233. if(GetSortFlags(lpOrderedProps[i].ulPropTag, &lpSortFlags[i]) != erSuccess)
  2234. lpSortFlags[i] = 0;
  2235. if(lpsSortOrderArray->__ptr[i].ulOrder == EC_TABLE_SORT_DESCEND)
  2236. lpSortFlags[i] |= TABLEROW_FLAG_DESC;
  2237. }
  2238. // Update row
  2239. er = lpKeyTable->UpdateRow(ECKeyTable::TABLE_ROW_ADD, lpsRowKey,
  2240. cValues, lpSortLen.get(), lpSortFlags.get(), lppSortKeys.get(),
  2241. lpsPrevRow, fHidden, lpulAction);
  2242. exit:
  2243. if (lpOrderedProps != nullptr)
  2244. for (unsigned int i = 0; i < cValues; ++i)
  2245. FreePropVal(&lpOrderedProps[i], false);
  2246. if (lppSortKeys != nullptr)
  2247. for (unsigned int i = 0; i < cValues; ++i)
  2248. delete[] lppSortKeys[i];
  2249. return er;
  2250. }
  2251. /**
  2252. * Updates a category after a non-category row has been removed
  2253. *
  2254. * This function updates the category to contain the correct counters, and possibly removes the category if it is empty.
  2255. *
  2256. * Many row changes may be generated in a min/max category when the min/max row is removed from the category, which triggers
  2257. * a reorder of the category in the table.
  2258. *
  2259. * @param sOjbKey Object that was deleted
  2260. * @param ulFlags Notification flags
  2261. * @return result
  2262. */
  2263. ECRESULT ECGenericObjectTable::RemoveCategoryAfterRemoveRow(sObjectTableKey sObjKey, unsigned int ulFlags)
  2264. {
  2265. ECRESULT er = erSuccess;
  2266. sObjectTableKey sCatRow;
  2267. sObjectTableKey sPrevRow(0,0);
  2268. ECLeafMap::const_iterator iterLeafs;
  2269. ECKeyTable::UpdateType ulAction;
  2270. ECCategory *lpCategory = NULL;
  2271. ECCategory *lpParent = NULL;
  2272. bool fModified = false;
  2273. bool fHidden = false;
  2274. unsigned int ulDepth = 0;
  2275. unsigned int ulSortLen = 0;
  2276. unsigned char ulSortFlags = 0;
  2277. struct propVal sProp;
  2278. sProp.ulPropTag = PR_NULL;
  2279. // Find information for the deleted leaf
  2280. iterLeafs = m_mapLeafs.find(sObjKey);
  2281. if (iterLeafs == m_mapLeafs.cend()) {
  2282. er = KCERR_NOT_FOUND;
  2283. goto exit;
  2284. }
  2285. lpCategory = iterLeafs->second.lpCategory;
  2286. // Loop through this category and all its parents
  2287. while(lpCategory) {
  2288. ulDepth = lpCategory->m_ulDepth;
  2289. lpParent = lpCategory->m_lpParent;
  2290. // Build the row key for this category
  2291. sCatRow.ulObjId = 0;
  2292. sCatRow.ulOrderId = lpCategory->m_ulCategory;
  2293. // Decrease the number of leafs in the category
  2294. lpCategory->DecLeaf();
  2295. if(iterLeafs->second.fUnread)
  2296. lpCategory->DecUnread();
  2297. if(ulDepth+1 < lpsSortOrderArray->__size && ISMINMAX(lpsSortOrderArray->__ptr[ulDepth+1].ulOrder)) {
  2298. // Removing from a min/max category
  2299. er = lpCategory->UpdateMinMaxRemove(sObjKey, ulDepth+1, lpsSortOrderArray->__ptr[ulDepth+1].ulOrder == EC_TABLE_SORT_CATEG_MAX, &fModified);
  2300. if(er != erSuccess) {
  2301. assert(false);
  2302. goto exit;
  2303. }
  2304. if(fModified && lpCategory->GetCount() > 0) {
  2305. // We have removed the min or max value for the category, so reorder is needed (unless category is empty, since it will be removed)
  2306. ECObjectTableList lstObjects;
  2307. // Get the rows that are affected
  2308. er = lpKeyTable->GetRowsBySortPrefix(&sCatRow, &lstObjects);
  2309. if (er != erSuccess)
  2310. goto exit;
  2311. // Update the keytable to reflect the new change
  2312. for (auto &obj : lstObjects) {
  2313. // Update the keytable with the new effective sort data for this column
  2314. if(lpCategory->GetProp(NULL, lpsSortOrderArray->__ptr[ulDepth+1].ulPropTag, &sProp) != erSuccess) {
  2315. sProp.ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(lpsSortOrderArray->__ptr[ulDepth+1].ulPropTag));
  2316. sProp.Value.ul = KCERR_NOT_FOUND;
  2317. }
  2318. std::unique_ptr<unsigned char[]> lpSortKey;
  2319. if(GetBinarySortKey(&sProp, &ulSortLen, &unique_tie(lpSortKey)) != erSuccess)
  2320. lpSortKey = NULL;
  2321. if(GetSortFlags(sProp.ulPropTag, &ulSortFlags) != erSuccess)
  2322. ulSortFlags = 0;
  2323. ulSortFlags |= lpsSortOrderArray->__ptr[ulDepth].ulOrder == EC_TABLE_SORT_DESCEND ? TABLEROW_FLAG_DESC : 0;
  2324. er = lpKeyTable->UpdatePartialSortKey(&obj, ulDepth, lpSortKey.get(), ulSortLen, ulSortFlags, &sPrevRow, &fHidden, &ulAction);
  2325. if (er != erSuccess)
  2326. goto exit;
  2327. if ((ulFlags & OBJECTTABLE_NOTIFY) && !fHidden)
  2328. AddTableNotif(ulAction, obj, &sPrevRow);
  2329. FreePropVal(&sProp, false);
  2330. sProp.ulPropTag = PR_NULL;
  2331. }
  2332. }
  2333. }
  2334. if(lpCategory->GetCount() == 0) {
  2335. // The category row is empty and must be removed
  2336. ECTableRow *lpRow = NULL; // reference to the row in the keytable
  2337. er = lpKeyTable->GetRow(&sCatRow, &lpRow);
  2338. if(er != erSuccess) {
  2339. assert(false);
  2340. goto exit;
  2341. }
  2342. // Remove the category from the sorted categories map
  2343. m_mapSortedCategories.erase(lpCategory->iSortedCategory);
  2344. // Remove the category from the keytable
  2345. lpKeyTable->UpdateRow(ECKeyTable::TABLE_ROW_DELETE, &sCatRow, 0, NULL, NULL, NULL, NULL, false, &ulAction);
  2346. // Remove the category from the category map
  2347. assert(m_mapCategories.find(sCatRow) != m_mapCategories.end());
  2348. m_mapCategories.erase(sCatRow);
  2349. // Free the category itself
  2350. delete lpCategory;
  2351. // Send the notification
  2352. if (ulAction == ECKeyTable::TABLE_ROW_DELETE && (ulFlags & OBJECTTABLE_NOTIFY))
  2353. AddTableNotif(ulAction, sCatRow, NULL);
  2354. } else {
  2355. if(ulFlags & OBJECTTABLE_NOTIFY) {
  2356. // The category row has changed; the counts have updated, send a notification
  2357. if(lpKeyTable->GetPreviousRow(&sCatRow, &sPrevRow) != erSuccess) {
  2358. sPrevRow.ulOrderId = 0;
  2359. sPrevRow.ulObjId = 0;
  2360. }
  2361. AddTableNotif(ECKeyTable::TABLE_ROW_MODIFY, sCatRow, &sPrevRow);
  2362. }
  2363. }
  2364. lpCategory = lpParent;
  2365. }
  2366. // Remove the leaf from the leaf map
  2367. m_mapLeafs.erase(iterLeafs);
  2368. // All done
  2369. assert(m_mapCategories.size() == m_mapSortedCategories.size());
  2370. exit:
  2371. FreePropVal(&sProp, false);
  2372. sProp.ulPropTag = PR_NULL;
  2373. return er;
  2374. }
  2375. /**
  2376. * Get a table properties for a category
  2377. *
  2378. * @param soap SOAP object for memory allocation of data in lpPropVal
  2379. * @param ulPropTag Requested property tag
  2380. * @param sKey Key of the category to be retrieved
  2381. * @param lpPropVal Output location of the property
  2382. * @return result
  2383. */
  2384. ECRESULT ECGenericObjectTable::GetPropCategory(struct soap *soap, unsigned int ulPropTag, sObjectTableKey sKey, struct propVal *lpPropVal)
  2385. {
  2386. ECRESULT er = erSuccess;
  2387. unsigned int i = 0;
  2388. auto iterCategories = m_mapCategories.find(sKey);
  2389. if (iterCategories == m_mapCategories.cend())
  2390. return KCERR_NOT_FOUND;
  2391. switch(ulPropTag) {
  2392. case PR_INSTANCE_KEY:
  2393. lpPropVal->__union = SOAP_UNION_propValData_bin;
  2394. lpPropVal->Value.bin = s_alloc<struct xsd__base64Binary>(soap);
  2395. lpPropVal->Value.bin->__size = sizeof(unsigned int) * 2;
  2396. lpPropVal->Value.bin->__ptr = s_alloc<unsigned char>(soap, sizeof(unsigned int) * 2);
  2397. *((unsigned int *)lpPropVal->Value.bin->__ptr) = sKey.ulObjId;
  2398. *((unsigned int *)lpPropVal->Value.bin->__ptr+1) = sKey.ulOrderId;
  2399. lpPropVal->ulPropTag = PR_INSTANCE_KEY;
  2400. break;
  2401. case PR_ROW_TYPE:
  2402. lpPropVal->__union = SOAP_UNION_propValData_ul;
  2403. lpPropVal->Value.ul = iterCategories->second->m_fExpanded ? TBL_EXPANDED_CATEGORY : TBL_COLLAPSED_CATEGORY;
  2404. lpPropVal->ulPropTag = PR_ROW_TYPE;
  2405. break;
  2406. case PR_DEPTH:
  2407. lpPropVal->__union = SOAP_UNION_propValData_ul;
  2408. lpPropVal->Value.ul = iterCategories->second->m_ulDepth;
  2409. lpPropVal->ulPropTag = PR_DEPTH;
  2410. break;
  2411. case PR_CONTENT_COUNT:
  2412. lpPropVal->__union = SOAP_UNION_propValData_ul;
  2413. lpPropVal->Value.ul = iterCategories->second->m_ulLeafs;
  2414. lpPropVal->ulPropTag = PR_CONTENT_COUNT;
  2415. break;
  2416. case PR_CONTENT_UNREAD:
  2417. lpPropVal->__union = SOAP_UNION_propValData_ul;
  2418. lpPropVal->Value.ul = iterCategories->second->m_ulUnread;
  2419. lpPropVal->ulPropTag = PR_CONTENT_UNREAD;
  2420. break;
  2421. default:
  2422. for (i = 0; i < iterCategories->second->m_cProps; ++i)
  2423. // If MVI is set, search for the property as non-MV property, as this is how we will have
  2424. // received it when the category was added.
  2425. if (NormalizePropTag(iterCategories->second->m_lpProps[i].ulPropTag) == NormalizePropTag(ulPropTag & ~MVI_FLAG))
  2426. if(CopyPropVal(&iterCategories->second->m_lpProps[i], lpPropVal, soap) == erSuccess) {
  2427. lpPropVal->ulPropTag = (ulPropTag & ~MVI_FLAG);
  2428. break;
  2429. }
  2430. if(i == iterCategories->second->m_cProps)
  2431. er = KCERR_NOT_FOUND;
  2432. }
  2433. return er;
  2434. }
  2435. unsigned int ECGenericObjectTable::GetCategories()
  2436. {
  2437. return this->m_ulCategories;
  2438. }
  2439. // Normally overridden by subclasses
  2440. ECRESULT ECGenericObjectTable::CheckPermissions(unsigned int ulObjId)
  2441. {
  2442. return hrSuccess;
  2443. }
  2444. /**
  2445. * Get object size
  2446. *
  2447. * @return Object size in bytes
  2448. */
  2449. size_t ECGenericObjectTable::GetObjectSize(void)
  2450. {
  2451. size_t ulSize = sizeof(*this);
  2452. scoped_rlock biglock(m_hLock);
  2453. ulSize += SortOrderArraySize(lpsSortOrderArray);
  2454. ulSize += PropTagArraySize(lpsPropTagArray);
  2455. ulSize += RestrictTableSize(lpsRestrict);
  2456. ulSize += MEMORY_USAGE_LIST(m_listMVSortCols.size(), ECListInt);
  2457. ulSize += MEMORY_USAGE_MAP(mapObjects.size(), ECObjectTableMap);
  2458. ulSize += lpKeyTable->GetObjectSize();
  2459. ulSize += MEMORY_USAGE_MAP(m_mapCategories.size(), ECCategoryMap);
  2460. for (const auto &p : m_mapCategories)
  2461. ulSize += p.second->GetObjectSize();
  2462. ulSize += MEMORY_USAGE_MAP(m_mapLeafs.size(), ECLeafMap);
  2463. return ulSize;
  2464. }
  2465. ECCategory::ECCategory(unsigned int ulCategory, struct propVal *lpProps,
  2466. unsigned int cProps, unsigned int nProps, ECCategory *lpParent,
  2467. unsigned int ulDepth, bool fExpanded, const ECLocale &locale) :
  2468. m_cProps(nProps), m_lpParent(lpParent), m_ulDepth(ulDepth),
  2469. m_ulCategory(ulCategory), m_fExpanded(fExpanded), m_locale(locale)
  2470. {
  2471. unsigned int i;
  2472. m_lpProps = s_alloc<propVal>(nullptr, nProps);
  2473. for (i = 0; i < cProps; ++i)
  2474. CopyPropVal(&lpProps[i], &m_lpProps[i]);
  2475. for (; i < nProps; ++i) {
  2476. m_lpProps[i].ulPropTag = PR_NULL;
  2477. m_lpProps[i].Value.ul = 0;
  2478. m_lpProps[i].__union = SOAP_UNION_propValData_ul;
  2479. }
  2480. }
  2481. ECCategory::~ECCategory()
  2482. {
  2483. unsigned int i;
  2484. for (i = 0; i < m_cProps; ++i)
  2485. FreePropVal(&m_lpProps[i], false);
  2486. for (const auto &p : m_mapMinMax)
  2487. FreePropVal(p.second, true);
  2488. s_free(nullptr, m_lpProps);
  2489. }
  2490. void ECCategory::IncLeaf()
  2491. {
  2492. ++m_ulLeafs;
  2493. }
  2494. void ECCategory::DecLeaf()
  2495. {
  2496. --m_ulLeafs;
  2497. }
  2498. ECRESULT ECCategory::GetProp(struct soap *soap, unsigned int ulPropTag, struct propVal* lpPropVal)
  2499. {
  2500. ECRESULT er = erSuccess;
  2501. unsigned int i;
  2502. for (i = 0; i < m_cProps; ++i)
  2503. if (m_lpProps[i].ulPropTag == ulPropTag) {
  2504. er = CopyPropVal(&m_lpProps[i], lpPropVal, soap);
  2505. break;
  2506. }
  2507. if(i == m_cProps)
  2508. er = KCERR_NOT_FOUND;
  2509. return er;
  2510. }
  2511. ECRESULT ECCategory::SetProp(unsigned int i, struct propVal* lpPropVal)
  2512. {
  2513. ECRESULT er = erSuccess;
  2514. assert(i < m_cProps);
  2515. FreePropVal(&m_lpProps[i], false);
  2516. er = CopyPropVal(lpPropVal, &m_lpProps[i], NULL);
  2517. return er;
  2518. }
  2519. /**
  2520. * Updates a min/max value:
  2521. *
  2522. * Checks if the value passed is more or less than the current min/max value. Currently we treat
  2523. * an error value as a 'worse' value than ANY new value. This means that min(ERROR, 1) == 1, and max(ERROR, 1) == 1.
  2524. *
  2525. * The new value is also tracked in a list of min/max value so that UpdateMinMaxRemove() (see below) can update
  2526. * the min/max value when a row is removed.
  2527. *
  2528. * @param sKey Key of the new row
  2529. * @param i Column id of the min/max value
  2530. * @param lpNewValue New value for the column (may also be PT_ERROR)
  2531. * @param bool fMax TRUE if the column is a EC_TABLE_SORT_CATEG_MAX, FALSE if the column is EC_TABLE_SORT_CATEG_MIN
  2532. * @param lpfModified Returns TRUE if the new value updated the min/max value, false otherwise
  2533. * @return result
  2534. */
  2535. ECRESULT ECCategory::UpdateMinMax(const sObjectTableKey &sKey, unsigned int i, struct propVal *lpNewValue, bool fMax, bool *lpfModified)
  2536. {
  2537. ECRESULT er;
  2538. bool fModified = false;
  2539. int result = 0;
  2540. struct propVal *lpOldValue;
  2541. struct propVal *lpNew;
  2542. lpOldValue = &m_lpProps[i];
  2543. if(PROP_TYPE(lpOldValue->ulPropTag) != PT_ERROR && PROP_TYPE(lpOldValue->ulPropTag) != PT_NULL) {
  2544. // Compare old with new
  2545. er = CompareProp(lpOldValue, lpNewValue, m_locale, &result);
  2546. if (er != erSuccess)
  2547. return er;
  2548. }
  2549. // Copy the value so we can track it for later (in UpdateMinMaxRemove) if we didn't have it yet
  2550. er = CopyPropVal(lpNewValue, &lpNew);
  2551. if(er != erSuccess)
  2552. return er;
  2553. auto iterMinMax = m_mapMinMax.find(sKey);
  2554. if (iterMinMax == m_mapMinMax.cend()) {
  2555. m_mapMinMax.insert(std::make_pair(sKey, lpNew));
  2556. } else {
  2557. FreePropVal(iterMinMax->second, true); // NOTE this may free lpNewValue, so you can't use that anymore now
  2558. iterMinMax->second = lpNew;
  2559. }
  2560. if(PROP_TYPE(lpOldValue->ulPropTag) == PT_ERROR || PROP_TYPE(lpOldValue->ulPropTag) == PT_NULL || (!fMax && result > 0) || (fMax && result < 0)) {
  2561. // Either there was no old value, or the new value is larger or smaller than the old one
  2562. er = SetProp(i, lpNew);
  2563. if(er != erSuccess)
  2564. return er;
  2565. m_sCurMinMax = sKey;
  2566. fModified = true;
  2567. }
  2568. if(lpfModified)
  2569. *lpfModified = fModified;
  2570. return erSuccess;
  2571. }
  2572. /**
  2573. * Update the min/max value to a row removal
  2574. *
  2575. * This function removes the value from the internal list of values, and checks if the new min/max value
  2576. * differs from the last. It updates the category properties accordingly if needed.
  2577. *
  2578. * @param sKey Key of row that was removed
  2579. * @param i Column id of min/max value
  2580. * @param fMax TRUE if the column is a EC_TABLE_SORT_CATEG_MAX, FALSE if the column is EC_TABLE_SORT_CATEG_MIN
  2581. * @param lpfModified TRUE if a new min/max value came into play due to the deletion
  2582. * @return result
  2583. */
  2584. ECRESULT ECCategory::UpdateMinMaxRemove(const sObjectTableKey &sKey, unsigned int i, bool fMax, bool *lpfModified)
  2585. {
  2586. bool fModified = false;
  2587. auto iterMinMax = m_mapMinMax.find(sKey);
  2588. if (iterMinMax == m_mapMinMax.cend())
  2589. return KCERR_NOT_FOUND;
  2590. FreePropVal(iterMinMax->second, true);
  2591. m_mapMinMax.erase(iterMinMax);
  2592. if(m_sCurMinMax == sKey) {
  2593. fModified = true;
  2594. // Reset old value
  2595. FreePropVal(&this->m_lpProps[i], false);
  2596. this->m_lpProps[i].ulPropTag = PR_NULL;
  2597. // The min/max value until now was updated. Find the next min/max value.
  2598. for (iterMinMax = m_mapMinMax.begin();
  2599. iterMinMax != m_mapMinMax.end(); ++iterMinMax)
  2600. // Re-feed the values we had until now
  2601. UpdateMinMax(iterMinMax->first, i, iterMinMax->second, fMax, NULL); // FIXME this
  2602. }
  2603. if(lpfModified)
  2604. *lpfModified = fModified;
  2605. return erSuccess;
  2606. }
  2607. void ECCategory::DecUnread() {
  2608. --m_ulUnread;
  2609. }
  2610. void ECCategory::IncUnread() {
  2611. ++m_ulUnread;
  2612. }
  2613. /**
  2614. * Get object size
  2615. *
  2616. * @return Object size in bytes
  2617. */
  2618. size_t ECCategory::GetObjectSize(void) const
  2619. {
  2620. size_t ulSize = 0;
  2621. if (m_cProps > 0) {
  2622. ulSize += sizeof(struct propVal) * m_cProps;
  2623. for (unsigned int i = 0; i < m_cProps; ++i)
  2624. ulSize += PropSize(&m_lpProps[i]);
  2625. }
  2626. if (m_lpParent)
  2627. ulSize += m_lpParent->GetObjectSize();
  2628. return sizeof(*this) + ulSize;
  2629. }
  2630. /**
  2631. * Get PR_DEPTH for an object in the table
  2632. *
  2633. * @param lpThis Pointer to generic object table instance
  2634. * @param soap SOAP object for memory allocation
  2635. * @param lpSession Session assiociated with the table
  2636. * @param ulObjId Object ID of the object to get PR_DEPTH for
  2637. * @param lpProp PropVal to write to
  2638. * @return result
  2639. */
  2640. ECRESULT ECGenericObjectTable::GetComputedDepth(struct soap *soap, ECSession *lpSession, unsigned int ulObjId, struct propVal *lpProp)
  2641. {
  2642. lpProp->__union = SOAP_UNION_propValData_ul;
  2643. lpProp->ulPropTag = PR_DEPTH;
  2644. if (m_ulObjType == MAPI_MESSAGE)
  2645. // For contents tables, depth is equal to number of categories
  2646. lpProp->Value.ul = GetCategories();
  2647. else
  2648. // For hierarchy tables, depth is 1 (see ECConvenientDepthTable.cpp for exception)
  2649. lpProp->Value.ul = 1;
  2650. return erSuccess;
  2651. }
  2652. } /* namespace */