1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154 |
- /*
- * Copyright 2005 - 2016 Zarafa and its licensors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
- #include <kopano/platform.h>
- #include <memory>
- #include <kopano/lockhelper.hpp>
- #include <kopano/tie.hpp>
- /* Returns the rows for a contents- or hierarchytable
- *
- * objtype == MAPI_MESSAGE, then contents table
- * objtype == MAPI_MESSAGE, flags == MAPI_ASSOCIATED, then associated contents table
- * objtype == MAPI_FOLDER, then hierarchy table
- *
- * Tables are generated from SQL in the following way:
- *
- * Tables are constructed by joining the hierarchy table with the property table multiple
- * times, once for each requested property (column). Because each column of each row can always have
- * only one or zero records, a unique index is created on the property table, indexing (hierarchyid, type, tag).
- *
- * This means that for each cell that we request, the index needs to be accessed by the SQL
- * engine only once, which makes the actual query extremely fast.
- *
- * In tests, this has shown to required around 60ms for 30 rows and 10 columns from a table of 10000
- * rows. Also, this is a O(n log n) operation and therefore not prone to large scaling problems. (Yay!)
- * (with respect to the amount of columns, it is O(n), but that's quite constant, and so is the
- * actual amount of rows requested per query (also O(n)).
- *
- */
- #include "soapH.h"
- #include <kopano/kcodes.h>
- #include <mapidefs.h>
- #include <mapitags.h>
- #include <kopano/mapiext.h>
- #include <sys/types.h>
- #if 1 /* change to HAVE_REGEX_H */
- #include <regex.h>
- #endif
- #include <iostream>
- #include "kcore.hpp"
- #include "pcutil.hpp"
- #include "ECSecurity.h"
- #include "ECDatabaseUtils.h"
- #include <kopano/ECKeyTable.h>
- #include "ECGenProps.h"
- #include "ECGenericObjectTable.h"
- #include "SOAPUtils.h"
- #include <kopano/stringutil.h>
- #include <kopano/Trace.h>
- #include "ECSessionManager.h"
-
- #include "ECSession.h"
- using namespace KCHL;
- namespace KC {
- static struct sortOrderArray sDefaultSortOrder{__gszeroinit};
- static const ULONG sANRProps[] = {
- PR_DISPLAY_NAME, PR_SMTP_ADDRESS, PR_ACCOUNT, PR_DEPARTMENT_NAME,
- PR_OFFICE_TELEPHONE_NUMBER, PR_OFFICE_LOCATION, PR_PRIMARY_FAX_NUMBER,
- PR_SURNAME
- };
- #define ISMINMAX(x) ((x) == EC_TABLE_SORT_CATEG_MIN || (x) == EC_TABLE_SORT_CATEG_MAX)
- /**
- * Apply RELOP_* rules to equality value from CompareProp
- *
- * 'equality' is a value from CompareProp which can be -1, 0 or 1. This function
- * returns TRUE when the passed relop matches the equality value. (Eg equality=0
- * and RELOP_EQ, then returns TRUE)
- * @param relop RELOP
- * @param equality Equality value from CompareProp
- * @return TRUE if the relop matches
- */
- static inline bool match(unsigned int relop, int equality)
- {
- bool fMatch = false;
-
- switch(relop) {
- case RELOP_GE:
- fMatch = equality >= 0;
- break;
- case RELOP_GT:
- fMatch = equality > 0;
- break;
- case RELOP_LE:
- fMatch = equality <= 0;
- break;
- case RELOP_LT:
- fMatch = equality < 0;
- break;
- case RELOP_NE:
- fMatch = equality != 0;
- break;
- case RELOP_RE:
- fMatch = false; // FIXME ?? how should this work ??
- break;
- case RELOP_EQ:
- fMatch = equality == 0;
- break;
- }
-
- return fMatch;
- }
- /**
- * Constructor of the Generic Object Table
- *
- * @param[in] lpSession
- * Reference to a session object; cannot be NULL.
- * @param[in] ulObjType
- * The Object type of the objects in the table
- */
- ECGenericObjectTable::ECGenericObjectTable(ECSession *lpSession,
- unsigned int ulObjType, unsigned int ulFlags, const ECLocale &locale) :
- m_ulObjType(ulObjType), m_ulFlags(ulFlags), m_locale(locale)
- {
- this->lpSession = lpSession;
- this->lpKeyTable = new ECKeyTable;
- // No columns by default
- this->lpsPropTagArray = s_alloc<propTagArray>(nullptr);
- this->lpsPropTagArray->__size = 0;
- this->lpsPropTagArray->__ptr = NULL;
- }
- /**
- * Destructor of the Generic Object Table
- */
- ECGenericObjectTable::~ECGenericObjectTable()
- {
- delete lpKeyTable;
- if(this->lpsPropTagArray)
- FreePropTagArray(this->lpsPropTagArray);
- if(this->lpsSortOrderArray)
- FreeSortOrderArray(this->lpsSortOrderArray);
- if(this->lpsRestrict)
- FreeRestrictTable(this->lpsRestrict);
-
- for (const auto &p : m_mapCategories)
- delete p.second;
- }
- /**
- * Moves the cursor to a specific row in the table.
- *
- * @param[in] ulBookmark
- * Identifying the starting position for the seek action. A bookmark can be created with
- * ECGenericObjectTable::CreateBookmark call, or use one of the following bookmark predefines:
- * \arg BOOKMARK_BEGINNING Start seeking from the beginning of the table.
- * \arg BOOKMARK_CURRENT Start seeking from the current position of the table.
- * \arg BOOKMARK_END Start seeking from the end of the table.
- * @param[in] lSeekTo
- * Positive or negative number of rows moved starting from the bookmark.
- * @param[in] lplRowsSought
- * Pointer to the number or rows that were processed in the seek action. If lplRowsSought is NULL,
- * the caller iss not interested in the returned output.
- *
- * @return Kopano error code
- */
- ECRESULT ECGenericObjectTable::SeekRow(unsigned int ulBookmark, int lSeekTo, int *lplRowsSought)
- {
- scoped_rlock biglock(m_hLock);
- ECRESULT er = Populate();
- if(er != erSuccess)
- return er;
- if(lpsSortOrderArray == NULL) {
- er = SetSortOrder(&sDefaultSortOrder, 0, 0);
- if(er != erSuccess)
- return er;
- }
- return lpKeyTable->SeekRow(ulBookmark, lSeekTo, lplRowsSought);
- }
- /**
- * Finds the next row in the table that matches specific search criteria.
- *
- *
- * @param[in] lpsRestrict
- * @param[in] ulBookmark
- * @param[in] ulFlags
- *
- * @return Kopano error code
- */
- ECRESULT ECGenericObjectTable::FindRow(struct restrictTable *lpsRestrict, unsigned int ulBookmark, unsigned int ulFlags)
- {
- bool fMatch = false;
- int ulSeeked = 0;
- unsigned int ulRow = 0;
- unsigned int ulCount = 0;
- int ulTraversed = 0;
- SUBRESTRICTIONRESULTS *lpSubResults = NULL;
-
- struct propTagArray *lpPropTags = NULL;
- struct rowSet *lpRowSet = NULL;
- ECObjectTableList ecRowList;
- sObjectTableKey sRowItem;
- entryId sEntryId;
- ulock_rec biglock(m_hLock);
- ECRESULT er = Populate();
- if(er != erSuccess)
- goto exit;
-
- /* We may need the table position later (ulCount is not used) */
- er = lpKeyTable->GetRowCount(&ulCount, &ulRow);
- if (er != erSuccess)
- goto exit;
- // Start searching at the right place
- if (ulBookmark == BOOKMARK_END && ulFlags & DIR_BACKWARD)
- er = SeekRow(ulBookmark, -1, NULL);
- else
- er = SeekRow(ulBookmark, 0, NULL);
- if (er != erSuccess)
- goto exit;
- // Special optimisation case: if you're searching the PR_INSTANCE_KEY, we can
- // look this up directly!
- if( ulBookmark == BOOKMARK_BEGINNING &&
- lpsRestrict->ulType == RES_PROPERTY && lpsRestrict->lpProp->ulType == RELOP_EQ &&
- lpsRestrict->lpProp->lpProp && lpsRestrict->lpProp->ulPropTag == PR_INSTANCE_KEY &&
- lpsRestrict->lpProp->lpProp->ulPropTag == PR_INSTANCE_KEY &&
- lpsRestrict->lpProp->lpProp->Value.bin && lpsRestrict->lpProp->lpProp->Value.bin->__size == sizeof(unsigned int)*2)
- {
- sRowItem.ulObjId = *(unsigned int *)lpsRestrict->lpProp->lpProp->Value.bin->__ptr;
- sRowItem.ulOrderId = *(unsigned int *)(lpsRestrict->lpProp->lpProp->Value.bin->__ptr+sizeof(LONG));
- er = this->lpKeyTable->SeekId(&sRowItem);
- goto exit;
- }
- // We can do the same with PR_ENTRYID
- if( ulBookmark == BOOKMARK_BEGINNING &&
- lpsRestrict->ulType == RES_PROPERTY && lpsRestrict->lpProp->ulType == RELOP_EQ &&
- lpsRestrict->lpProp->lpProp && lpsRestrict->lpProp->ulPropTag == PR_ENTRYID &&
- lpsRestrict->lpProp->lpProp->ulPropTag == PR_ENTRYID &&
- lpsRestrict->lpProp->lpProp->Value.bin && IsKopanoEntryId(lpsRestrict->lpProp->lpProp->Value.bin->__size, lpsRestrict->lpProp->lpProp->Value.bin->__ptr))
- {
- sEntryId.__ptr = lpsRestrict->lpProp->lpProp->Value.bin->__ptr;
- sEntryId.__size = lpsRestrict->lpProp->lpProp->Value.bin->__size;
- er = lpSession->GetSessionManager()->GetCacheManager()->GetObjectFromEntryId(&sEntryId, &sRowItem.ulObjId);
- if(er != erSuccess)
- goto exit;
- sRowItem.ulOrderId = 0; // FIXME: this is incorrect when MV_INSTANCE is specified on a column, but this won't happen often.
- er = this->lpKeyTable->SeekId(&sRowItem);
- goto exit;
- }
- // Get the columns we will be needing for this search
- er = GetRestrictPropTags(lpsRestrict, NULL, &lpPropTags);
- if(er != erSuccess)
- goto exit;
- // Loop through the rows, matching it with the search criteria
- while(1) {
- ecRowList.clear();
- // Get the row ID of the next row
- er = lpKeyTable->QueryRows(20, &ecRowList, (ulFlags & DIR_BACKWARD)?true:false, TBL_NOADVANCE);
- if(er != erSuccess)
- goto exit;
- if(ecRowList.empty())
- break;
- // Get the rowdata from the QueryRowData function
- er = m_lpfnQueryRowData(this, NULL, lpSession, &ecRowList, lpPropTags, m_lpObjectData, &lpRowSet, true, false);
- if(er != erSuccess)
- goto exit;
-
- er = RunSubRestrictions(lpSession, m_lpObjectData, lpsRestrict, &ecRowList, m_locale, &lpSubResults);
- if(er != erSuccess)
- goto exit;
- assert(lpRowSet->__size == static_cast<gsoap_size_t>(ecRowList.size()));
- for (gsoap_size_t i = 0; i < lpRowSet->__size; ++i) {
- // Match the row
- er = MatchRowRestrict(lpSession->GetSessionManager()->GetCacheManager(), &lpRowSet->__ptr[i], lpsRestrict, lpSubResults, m_locale, &fMatch);
- if(er != erSuccess)
- goto exit;
- if(fMatch)
- {
- // A Match, seek the cursor
- lpKeyTable->SeekRow(BOOKMARK_CURRENT, ulFlags & DIR_BACKWARD ? -i : i, &ulSeeked);
- break;
- }
- }
- if(fMatch)
- break;
- // No match, then advance the cursor
- lpKeyTable->SeekRow(BOOKMARK_CURRENT, ulFlags & DIR_BACKWARD ? -(int)ecRowList.size() : ecRowList.size(), &ulSeeked);
- // No advance possible, break the loop
- if(ulSeeked == 0)
- break;
- // Free memory
- FreeRowSet(lpRowSet, true);
- lpRowSet = NULL;
-
- if(lpSubResults)
- FreeSubRestrictionResults(lpSubResults);
- lpSubResults = NULL;
- }
- if(!fMatch) {
- er = KCERR_NOT_FOUND;
- lpKeyTable->SeekRow(ECKeyTable::EC_SEEK_SET, ulRow, &ulTraversed);
- }
- exit:
- biglock.unlock();
- if(lpSubResults)
- FreeSubRestrictionResults(lpSubResults);
-
- if(lpRowSet)
- FreeRowSet(lpRowSet, true);
- if(lpPropTags)
- FreePropTagArray(lpPropTags);
-
- return er;
- }
- /**
- * Returns the total number of rows in the table.
- *
- * @param[out] lpulRowCount
- * Pointer to the number of rows in the table.
- * @param[out] lpulCurrentRow
- * Pointer to the current row id in the table.
- *
- * @return Kopano error code
- */
- ECRESULT ECGenericObjectTable::GetRowCount(unsigned int *lpulRowCount, unsigned int *lpulCurrentRow)
- {
- scoped_rlock biglock(m_hLock);
- ECRESULT er = Populate();
- if(er != erSuccess)
- return er;
-
- if(lpsSortOrderArray == NULL) {
- er = SetSortOrder(&sDefaultSortOrder, 0, 0);
- if(er != erSuccess)
- return er;
- }
- return lpKeyTable->GetRowCount(lpulRowCount, lpulCurrentRow);
- }
- ECRESULT ECGenericObjectTable::ReloadTableMVData(ECObjectTableList* lplistRows, ECListInt* lplistMVPropTag)
- {
- // default ignore MV-view, show as an normal view
- return erSuccess;
- }
- /**
- * Returns a list of columns for the table.
- *
- * If the function is not overridden, it returns always an empty column set.
- *
- * @param[in,out] lplstProps
- * a list of columns for the table
- *
- * @return Kopano error code
- */
- ECRESULT ECGenericObjectTable::GetColumnsAll(ECListInt* lplstProps)
- {
- return erSuccess;
- }
- /**
- * Reload the table objects.
- *
- * Rebuild the whole table with the current restriction and sort order. If the sort order
- * includes a multi-valued property, a single row appearing in multiple rows. ReloadTable
- * may expand or contract expanded MVI rows if the sort order or column set have changed. If there
- * is no change in MVI-related expansion, it will call ReloadKeyTable which only does a
- * resort/refilter of the existing rows.
- *
- * @param[in] eType
- * The reload type determines how it should reload.
- *
- * @return Kopano error code
- */
- ECRESULT ECGenericObjectTable::ReloadTable(enumReloadType eType)
- {
- ECRESULT er = erSuccess;
- bool bMVColsNew = false;
- bool bMVSortNew = false;
- ECObjectTableList listRows;
- ECListInt listMVPropTag;
- scoped_rlock biglock(m_hLock);
- //Scan for MVI columns
- for (gsoap_size_t i = 0; lpsPropTagArray != NULL && i < lpsPropTagArray->__size; ++i) {
- if ((PROP_TYPE(lpsPropTagArray->__ptr[i]) &MVI_FLAG) != MVI_FLAG)
- continue;
- if (bMVColsNew == true)
- assert(false); //FIXME: error 1 mv prop set!!!
- bMVColsNew = true;
- listMVPropTag.push_back(lpsPropTagArray->__ptr[i]);
- }
-
- //Check for mvi props
- for (gsoap_size_t i = 0; lpsSortOrderArray != NULL && i < lpsSortOrderArray->__size; ++i) {
- if ((PROP_TYPE(lpsSortOrderArray->__ptr[i].ulPropTag) & MVI_FLAG) != MVI_FLAG)
- continue;
- if (bMVSortNew == true)
- assert(false);
- bMVSortNew = true;
- listMVPropTag.push_back(lpsSortOrderArray->__ptr[i].ulPropTag);
- }
- listMVPropTag.sort();
- listMVPropTag.unique();
- if((m_bMVCols == false && m_bMVSort == false && bMVColsNew == false && bMVSortNew == false) ||
- (listMVPropTag == m_listMVSortCols && (m_bMVCols == bMVColsNew || m_bMVSort == bMVSortNew)) )
- {
- if(eType == RELOAD_TYPE_SORTORDER)
- er = ReloadKeyTable();
- /* No MVprops or already sorted, skip MV sorts. */
- return er;
- }
- m_listMVSortCols = listMVPropTag;
- // Get all the Single Row IDs from the ID map
- for (const auto &p : mapObjects)
- if (p.first.ulOrderId == 0)
- listRows.push_back(p.first);
- if(mapObjects.empty())
- goto skip;
- if(bMVColsNew == true || bMVSortNew == true)
- {
- // Expand rows to contain all MVI expansions (listRows is appended to)
- er = ReloadTableMVData(&listRows, &listMVPropTag);
- if(er != erSuccess)
- return er;
- }
- // Clear row data
- Clear();
- //Add items
- for (const auto &row : listRows)
- mapObjects[row] = 1;
- // Load the keys with sort data from the table
- er = AddRowKey(&listRows, NULL, 0, true, false, NULL);
- skip:
- m_bMVCols = bMVColsNew;
- m_bMVSort = bMVSortNew;
- return er;
- }
- /**
- * Returns the total number of multi value rows of a specific object.
- *
- * This methode should be overridden and should return the total number of multi value rows of a specific object.
- *
- * @param[in] ulObjId
- * Object id to receive the number of multi value rows
- * @param[out] lpulCount
- * Pointer to the number of multi value rows of the object ulObjId
- *
- * @return Kopano error code
- */
- ECRESULT ECGenericObjectTable::GetMVRowCount(unsigned int ulObjId, unsigned int *lpulCount)
- {
- return KCERR_NO_SUPPORT;
- }
- /**
- * Defines the properties and order of properties to appear as columns in the table.
- *
- * @param[in] lpsPropTags
- * Pointer to an array of property tags with a specific order.
- * The lpsPropTags parameter cannot be set to NULL; table must have at least one column.
- *
- * @return Kopano error code
- */
- ECRESULT ECGenericObjectTable::SetColumns(const struct propTagArray *lpsPropTags,
- bool bDefaultSet)
- {
- //FIXME: check the lpsPropTags array, 0x????xxxx -> xxxx must be checked
- // Remember the columns for later use (in QueryRows)
- // This is a very very quick operation, as we only save the information.
- scoped_rlock biglock(m_hLock);
- // Delete the old column set
- if(this->lpsPropTagArray)
- FreePropTagArray(this->lpsPropTagArray);
- lpsPropTagArray = s_alloc<propTagArray>(nullptr);
- lpsPropTagArray->__size = lpsPropTags->__size;
- lpsPropTagArray->__ptr = s_alloc<unsigned int>(nullptr, lpsPropTags->__size);
- if (bDefaultSet) {
- for (gsoap_size_t n = 0; n < lpsPropTags->__size; ++n) {
- if (PROP_TYPE(lpsPropTags->__ptr[n]) == PT_STRING8 || PROP_TYPE(lpsPropTags->__ptr[n]) == PT_UNICODE)
- lpsPropTagArray->__ptr[n] = CHANGE_PROP_TYPE(lpsPropTags->__ptr[n], ((m_ulFlags & MAPI_UNICODE) ? PT_UNICODE : PT_STRING8));
- else if (PROP_TYPE(lpsPropTags->__ptr[n]) == PT_MV_STRING8 || PROP_TYPE(lpsPropTags->__ptr[n]) == PT_MV_UNICODE)
- lpsPropTagArray->__ptr[n] = CHANGE_PROP_TYPE(lpsPropTags->__ptr[n], ((m_ulFlags & MAPI_UNICODE) ? PT_MV_UNICODE : PT_MV_STRING8));
- else
- lpsPropTagArray->__ptr[n] = lpsPropTags->__ptr[n];
- }
- } else
- memcpy(lpsPropTagArray->__ptr, lpsPropTags->__ptr, sizeof(unsigned int) * lpsPropTags->__size);
-
- return ReloadTable(RELOAD_TYPE_SETCOLUMNS);
- }
- ECRESULT ECGenericObjectTable::GetColumns(struct soap *soap, ULONG ulFlags, struct propTagArray **lppsPropTags)
- {
- ECRESULT er = erSuccess;
- int n = 0;
- ECListInt lstProps;
- struct propTagArray *lpsPropTags;
- scoped_rlock biglock(m_hLock);
- if(ulFlags & TBL_ALL_COLUMNS) {
- // All columns were requested. Simply get a unique list of all the proptags used in all the objects in this table
- er = Populate();
- if(er != erSuccess)
- return er;
- er = GetColumnsAll(&lstProps);
- if(er != erSuccess)
- return er;
-
- // Make sure we have a unique list
- lstProps.sort();
- lstProps.unique();
- // Convert them all over to a struct propTagArray
- lpsPropTags = s_alloc<propTagArray>(soap);
- lpsPropTags->__size = lstProps.size();
- lpsPropTags->__ptr = s_alloc<unsigned int>(soap, lstProps.size());
- n = 0;
- for (auto prop_int : lstProps) {
- lpsPropTags->__ptr[n] = prop_int;
- if (PROP_TYPE(lpsPropTags->__ptr[n]) == PT_STRING8 || PROP_TYPE(lpsPropTags->__ptr[n]) == PT_UNICODE)
- lpsPropTags->__ptr[n] = CHANGE_PROP_TYPE(lpsPropTags->__ptr[n], ((m_ulFlags & MAPI_UNICODE) ? PT_UNICODE : PT_STRING8));
- else if (PROP_TYPE(lpsPropTags->__ptr[n]) == PT_MV_STRING8 || PROP_TYPE(lpsPropTags->__ptr[n]) == PT_MV_UNICODE)
- lpsPropTags->__ptr[n] = CHANGE_PROP_TYPE(lpsPropTags->__ptr[n], ((m_ulFlags & MAPI_UNICODE) ? PT_MV_UNICODE : PT_MV_STRING8));
- ++n;
- }
- } else {
- lpsPropTags = s_alloc<propTagArray>(soap);
- if(lpsPropTagArray) {
- lpsPropTags->__size = lpsPropTagArray->__size;
- lpsPropTags->__ptr = s_alloc<unsigned int>(soap, lpsPropTagArray->__size);
- memcpy(lpsPropTags->__ptr, lpsPropTagArray->__ptr, sizeof(unsigned int) * lpsPropTagArray->__size);
- } else {
- lpsPropTags->__size = 0;
- lpsPropTags->__ptr = NULL;
- }
- }
- *lppsPropTags = lpsPropTags;
- return er;
- }
- ECRESULT ECGenericObjectTable::ReloadKeyTable()
- {
- ECObjectTableList listRows;
- scoped_rlock biglock(m_hLock);
- // Get all the Row IDs from the ID map
- for (const auto &p : mapObjects)
- listRows.push_back(p.first);
- // Reset the key table
- lpKeyTable->Clear();
- m_mapLeafs.clear();
- for (const auto &p : m_mapCategories)
- delete p.second;
- m_mapCategories.clear();
- m_mapSortedCategories.clear();
- // Load the keys with sort data from the table
- return AddRowKey(&listRows, NULL, 0, true, false, NULL);
- }
- ECRESULT ECGenericObjectTable::SetSortOrder(struct sortOrderArray *lpsSortOrder, unsigned int ulCategories, unsigned int ulExpanded)
- {
- ECRESULT er = erSuccess;
-
- // Set the sort order, re-read the data from the database, and reset the current row
- // The current row is reset to point to the row it was pointing to in the first place.
- // This is pretty easy as it is pointing at the same object ID as it was before we
- // reloaded.
- scoped_rlock biglock(m_hLock);
- if(m_ulCategories == ulCategories && m_ulExpanded == ulExpanded && this->lpsSortOrderArray && CompareSortOrderArray(this->lpsSortOrderArray, lpsSortOrder) == 0) {
- // Sort requested was already set, return OK
- this->SeekRow(BOOKMARK_BEGINNING, 0, NULL);
- return er;
- }
-
- // Check validity of tags
- for (gsoap_size_t i = 0; i < lpsSortOrder->__size; ++i)
- if ((PROP_TYPE(lpsSortOrder->__ptr[i].ulPropTag) & MVI_FLAG) == MV_FLAG)
- return KCERR_TOO_COMPLEX;
-
- m_ulCategories = ulCategories;
- m_ulExpanded = ulExpanded;
- // Save the sort order requested
- if(this->lpsSortOrderArray)
- FreeSortOrderArray(this->lpsSortOrderArray);
- this->lpsSortOrderArray = s_alloc<sortOrderArray>(nullptr);
- this->lpsSortOrderArray->__size = lpsSortOrder->__size;
- if(lpsSortOrder->__size == 0 ) {
- this->lpsSortOrderArray->__ptr = NULL;
- } else {
- this->lpsSortOrderArray->__ptr = s_alloc<sortOrder>(nullptr, lpsSortOrder->__size);
- memcpy(this->lpsSortOrderArray->__ptr, lpsSortOrder->__ptr, sizeof(struct sortOrder) * lpsSortOrder->__size);
- }
- er = ReloadTable(RELOAD_TYPE_SORTORDER);
-
- if(er != erSuccess)
- return er;
- // FIXME When you change the sort order, current row should be equal to previous row ID
- return lpKeyTable->SeekRow(0, 0, NULL);
- }
- ECRESULT ECGenericObjectTable::GetBinarySortKey(struct propVal *lpsPropVal, unsigned int *lpSortLen, unsigned char **lppSortData)
- {
- ECRESULT er = erSuccess;
- unsigned char *lpSortData = NULL;
- unsigned int ulSortLen = 0;
- switch(PROP_TYPE(lpsPropVal->ulPropTag)) {
- case PT_BOOLEAN:
- case PT_I2:
- ulSortLen = 2;
- lpSortData = new unsigned char[2];
- *(unsigned short *)lpSortData = htons(lpsPropVal->Value.b);
- break;
- case PT_LONG:
- ulSortLen = 4;
- lpSortData = new unsigned char[4];
- *(unsigned int *)lpSortData = htonl(lpsPropVal->Value.ul);
- break;
- case PT_R4:
- ulSortLen = sizeof(double);
- lpSortData = new unsigned char[sizeof(double)];
- *(double *)lpSortData = lpsPropVal->Value.flt;
- break;
- case PT_APPTIME:
- case PT_DOUBLE:
- ulSortLen = sizeof(double);
- lpSortData = new unsigned char[sizeof(double)];
- *(double *)lpSortData = lpsPropVal->Value.dbl;
- break;
- case PT_CURRENCY:
- ulSortLen = 0;
- lpSortData = NULL;
- break;
- case PT_SYSTIME:
- ulSortLen = 8;
- lpSortData = new unsigned char[8];
- *(unsigned int *)lpSortData = htonl(lpsPropVal->Value.hilo->hi);
- *(unsigned int *)(lpSortData+4) = htonl(lpsPropVal->Value.hilo->lo);
- break;
- case PT_I8:
- ulSortLen = 8;
- lpSortData = new unsigned char[8];
- *(unsigned int *)lpSortData = htonl((unsigned int)(lpsPropVal->Value.li >> 32));
- *(unsigned int *)(lpSortData+4) = htonl((unsigned int)lpsPropVal->Value.li);
- break;
- case PT_STRING8:
- case PT_UNICODE: {
- // is this check needed here, or is it already checked 50 times along the way?
- if (!lpsPropVal->Value.lpszA) {
- ulSortLen = 0;
- lpSortData = NULL;
- break;
- }
-
- createSortKeyDataFromUTF8(lpsPropVal->Value.lpszA, 255, m_locale, &ulSortLen, &lpSortData);
- }
- break;
- case PT_CLSID:
- case PT_BINARY:
- ulSortLen = lpsPropVal->Value.bin->__size;
- lpSortData = new unsigned char [ulSortLen];
- memcpy(lpSortData, lpsPropVal->Value.bin->__ptr, ulSortLen); // could be optimized to one func
- break;
- case PT_ERROR:
- ulSortLen = 0;
- lpSortData = NULL;
- break;
- default:
- er = KCERR_INVALID_TYPE;
- break;
- }
- if(er != erSuccess)
- return er;
- *lpSortLen = ulSortLen;
- *lppSortData = lpSortData;
- return erSuccess;
- }
- /**
- * The ECGenericObjectTable::GetSortFlags method gets tablerow flags for a property.
- *
- * This flag alters the comparison behaviour of the ECKeyTable. This behaviour only needs
- * to be altered for float/double values and strings.
- *
- * @param[in] ulPropTag The PropTag for which to get the flags.
- * @param[out] lpFlags The flags needed to properly compare properties for the provided PropTag.
- *
- * @return Kopano error code
- */
- ECRESULT ECGenericObjectTable::GetSortFlags(unsigned int ulPropTag, unsigned char *lpFlags)
- {
- ECRESULT er = erSuccess;
- unsigned int ulFlags = 0;
-
- switch(PROP_TYPE(ulPropTag)) {
- case PT_DOUBLE:
- case PT_APPTIME:
- case PT_R4:
- ulFlags = TABLEROW_FLAG_FLOAT;
- break;
- case PT_STRING8:
- case PT_UNICODE:
- ulFlags = TABLEROW_FLAG_STRING;
- break;
- default:
- break;
- }
-
- *lpFlags = ulFlags;
-
- return er;
- }
- /**
- * The ECGenericObjectTable::Restrict methode applies a filter to a table
- *
- * The ECGenericObjectTable::Restrict methode applies a filter to a table, reducing
- * the row set to only those rows matching the specified criteria.
- *
- * @param[in] lpsRestrict
- * Pointer to a restrictTable structure defining the conditions of the filter.
- * Passing NULL in the lpsRestrict parameter removes the current filter.
- *
- * @return Kopano error code
- */
- ECRESULT ECGenericObjectTable::Restrict(struct restrictTable *lpsRestrict)
- {
- ECRESULT er = erSuccess;
- scoped_rlock biglock(m_hLock);
- if(lpsSortOrderArray == NULL) {
- er = SetSortOrder(&sDefaultSortOrder, 0, 0);
- if(er != erSuccess)
- return er;
- }
- // No point turning off a restriction that's already off
- if (this->lpsRestrict == NULL && lpsRestrict == NULL)
- return er;
- // Copy the restriction so we can remember it
- if(this->lpsRestrict)
- FreeRestrictTable(this->lpsRestrict);
- this->lpsRestrict = NULL; // turn off restriction
- if(lpsRestrict) {
- er = CopyRestrictTable(NULL, lpsRestrict, &this->lpsRestrict);
- if(er != erSuccess)
- return er;
- }
- er = ReloadKeyTable();
- if(er != erSuccess)
- return er;
- // Seek to row 0 (according to spec)
- this->SeekRow(BOOKMARK_BEGINNING, 0, NULL);
- return er;
- }
- /*
- * Adds a set of rows to the key table, with the correct sort keys
- *
- * This function attempts to add the set of rows passed in lpRows to the table. The rows are added according
- * to sorting currently set and are tested against the current restriction. If bFilter is FALSE, then rows are not
- * tested against the current restriction and always added. A row may also not be added if data for the row is no
- * longer available, in which case the row is silently ignored.
- *
- * The bLoad parameter is not used here, but may be used be subclasses to determine if the rows are being added
- * due to an initial load of the table, or due to a later update.
- *
- * @param[in] lpRows Candidate rows to add to the table
- * @param[out] lpulLoaded Number of rows added to the table
- * @param[in] ulFlags Type of rows being added (May be 0, MSGFLAG_ASSOCIATED, MSGFLAG_DELETED or combination)
- * @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
- * @param[in] bOverride TRUE if the restriction set by Restrict() must be ignored, and the rows in lpRows must be filtered with lpOverrideRestrict. lpOverrideRestrict
- * MAY be NULL indicating that all rows in lpRows are to be added without filtering.
- * @param[in] lpOverrideRestrict Overrides the set restriction, using this one instead; the rows passed in lpRows are filtered with this restriction
- */
- ECRESULT ECGenericObjectTable::AddRowKey(ECObjectTableList* lpRows, unsigned int *lpulLoaded, unsigned int ulFlags, bool bLoad, bool bOverride, struct restrictTable *lpOverrideRestrict)
- {
- TRACE_INTERNAL(TRACE_ENTRY, "Table call:", "ECGenericObjectTable::AddRowKey", "");
- ECRESULT er = erSuccess;
- bool fMatch = true;
- gsoap_size_t ulFirstCol = 0, n = 0;
- unsigned int ulLoaded = 0;
- bool bExist;
- bool fHidden = false;
- SUBRESTRICTIONRESULTS *lpSubResults = NULL;
- ECObjectTableList sQueryRows;
- struct propTagArray sPropTagArray = {0, 0};
- struct rowSet *lpRowSet = NULL;
- struct propTagArray *lpsRestrictPropTagArray = NULL;
- struct restrictTable *lpsRestrict = NULL;
- sObjectTableKey sRowItem;
-
- ECCategory *lpCategory = NULL;
- ulock_rec biglock(m_hLock);
- if (lpRows->empty()) {
- // nothing todo
- if(lpulLoaded)
- *lpulLoaded = 0;
- goto exit;
- }
- lpsRestrict = bOverride ? lpOverrideRestrict : this->lpsRestrict;
- // We want all columns of the sort data, plus all the columns needed for restriction, plus the ID of the row
- if(this->lpsSortOrderArray)
- sPropTagArray.__size = this->lpsSortOrderArray->__size; // sort columns
- else
- sPropTagArray.__size = 0;
- if(lpsRestrict) {
- er = GetRestrictPropTags(lpsRestrict, NULL, &lpsRestrictPropTagArray);
- if(er != erSuccess)
- goto exit;
- sPropTagArray.__size += lpsRestrictPropTagArray->__size; // restrict columns
- }
-
- ++sPropTagArray.__size; // for PR_INSTANCE_KEY
- ++sPropTagArray.__size; // for PR_MESSAGE_FLAGS
- sPropTagArray.__ptr = s_alloc<unsigned int>(nullptr, sPropTagArray.__size);
- sPropTagArray.__ptr[n++]= PR_INSTANCE_KEY;
- if(m_ulCategories > 0)
- sPropTagArray.__ptr[n++]= PR_MESSAGE_FLAGS;
- ulFirstCol = n;
-
- // Put all the proptags of the sort columns in a proptag array
- if(lpsSortOrderArray)
- for (gsoap_size_t i = 0; i < this->lpsSortOrderArray->__size; ++i)
- sPropTagArray.__ptr[n++] = this->lpsSortOrderArray->__ptr[i].ulPropTag;
- // Same for restrict columns
- // Check if an item already exist
- if(lpsRestrictPropTagArray) {
- for (gsoap_size_t i = 0; i < lpsRestrictPropTagArray->__size; ++i) {
- bExist = false;
- for (gsoap_size_t j = 0; j < n; ++j)
- if(sPropTagArray.__ptr[j] == lpsRestrictPropTagArray->__ptr[i])
- bExist = true;
- if(bExist == false)
- sPropTagArray.__ptr[n++] = lpsRestrictPropTagArray->__ptr[i];
- }
- }
- sPropTagArray.__size = n;
- for (auto iterRows = lpRows->cbegin(); iterRows != lpRows->cend(); ) {
- sQueryRows.clear();
- // if we use a restriction, memory usage goes up, so only fetch 20 rows at a time
- for (size_t i = 0; i < (lpsRestrictPropTagArray ? 20 : 256) && iterRows != lpRows->cend(); ++i) {
- sQueryRows.push_back(*iterRows);
- ++iterRows;
- }
- // Now, query the database for the actual data
- er = m_lpfnQueryRowData(this, NULL, lpSession, &sQueryRows, &sPropTagArray, m_lpObjectData, &lpRowSet, true, lpsRestrictPropTagArray ? false : true /* FIXME */);
- if(er != erSuccess)
- goto exit;
-
- if(lpsRestrict) {
- er = RunSubRestrictions(lpSession, m_lpObjectData, lpsRestrict, &sQueryRows, m_locale, &lpSubResults);
- if(er != erSuccess)
- goto exit;
- }
- // Send all this data to the internal key table
- for (gsoap_size_t i = 0; i < lpRowSet->__size; ++i) {
- lpCategory = NULL;
- if (lpRowSet->__ptr[i].__ptr[0].ulPropTag != PR_INSTANCE_KEY) // Row completely not found
- continue;
- // is PR_INSTANCE_KEY
- memcpy(&sRowItem.ulObjId, lpRowSet->__ptr[i].__ptr[0].Value.bin->__ptr, sizeof(ULONG));
- memcpy(&sRowItem.ulOrderId, lpRowSet->__ptr[i].__ptr[0].Value.bin->__ptr+sizeof(ULONG), sizeof(ULONG));
- // Match the row with the restriction, if any
- if(lpsRestrict) {
- MatchRowRestrict(lpSession->GetSessionManager()->GetCacheManager(), &lpRowSet->__ptr[i], lpsRestrict, lpSubResults, m_locale, &fMatch);
- if(fMatch == false) {
- // this row isn't in the table, as it does not match the restrict criteria. Remove it as if it had
- // been deleted if it was already in the table.
- DeleteRow(sRowItem, ulFlags);
-
- RemoveCategoryAfterRemoveRow(sRowItem, ulFlags);
- continue;
- }
- }
- if(m_ulCategories > 0) {
- bool bUnread = false;
-
- if((lpRowSet->__ptr[i].__ptr[1].Value.ul & MSGFLAG_READ) == 0)
- bUnread = true;
- // Update category for this row if required, and send notification if required
- AddCategoryBeforeAddRow(sRowItem, lpRowSet->__ptr[i].__ptr+ulFirstCol, lpsSortOrderArray->__size, ulFlags, bUnread, &fHidden, &lpCategory);
- }
- // Put the row into the key table and send notification if required
- AddRow(sRowItem, lpRowSet->__ptr[i].__ptr+ulFirstCol, lpsSortOrderArray->__size, ulFlags, fHidden, lpCategory);
- // Loaded one row
- ++ulLoaded;
- }
- if(lpSubResults) {
- FreeSubRestrictionResults(lpSubResults);
- lpSubResults = NULL;
- }
- FreeRowSet(lpRowSet, true);
- lpRowSet = NULL;
- }
- if(lpulLoaded)
- *lpulLoaded = ulLoaded;
- exit:
- biglock.unlock();
- if(lpSubResults)
- FreeSubRestrictionResults(lpSubResults);
- if(lpRowSet)
- FreeRowSet(lpRowSet, true);
- if(lpsRestrictPropTagArray != NULL)
- s_free(nullptr, lpsRestrictPropTagArray->__ptr);
- s_free(nullptr, lpsRestrictPropTagArray);
- s_free(nullptr, sPropTagArray.__ptr);
- return er;
- }
- // Actually add a row to the table
- ECRESULT ECGenericObjectTable::AddRow(sObjectTableKey sRowItem, struct propVal *lpProps, unsigned int cProps, unsigned int ulFlags, bool fHidden, ECCategory *lpCategory)
- {
- ECRESULT er;
- ECKeyTable::UpdateType ulAction;
- sObjectTableKey sPrevRow;
- UpdateKeyTableRow(lpCategory, &sRowItem, lpProps, cProps, fHidden, &sPrevRow, &ulAction);
- // Send notification if required
- if(ulAction && !fHidden && (ulFlags & OBJECTTABLE_NOTIFY)) {
- er = AddTableNotif(ulAction, sRowItem, &sPrevRow);
- if(er != erSuccess)
- return er;
- }
- return erSuccess;
- }
- // Actually remove a row from the table
- ECRESULT ECGenericObjectTable::DeleteRow(sObjectTableKey sRow, unsigned int ulFlags)
- {
- ECRESULT er;
- ECKeyTable::UpdateType ulAction;
- // Delete the row from the key table
- er = lpKeyTable->UpdateRow(ECKeyTable::TABLE_ROW_DELETE, &sRow, 0, NULL, NULL, NULL, NULL, false, &ulAction);
- if(er != erSuccess)
- return er;
-
- // Send notification if required
- if ((ulFlags & OBJECTTABLE_NOTIFY) && ulAction == ECKeyTable::TABLE_ROW_DELETE)
- AddTableNotif(ulAction, sRow, NULL);
- return erSuccess;
- }
- // Add a table notification by getting row data and sending it
- ECRESULT ECGenericObjectTable::AddTableNotif(ECKeyTable::UpdateType ulAction, sObjectTableKey sRowItem, sObjectTableKey *lpsPrevRow)
- {
- ECRESULT er = erSuccess;
- std::list<sObjectTableKey> lstItems;
- struct rowSet *lpRowSetNotif = NULL;
-
- if(ulAction == ECKeyTable::TABLE_ROW_ADD || ulAction == ECKeyTable::TABLE_ROW_MODIFY) {
- lstItems.push_back(sRowItem);
-
- er = m_lpfnQueryRowData(this, NULL, lpSession, &lstItems, this->lpsPropTagArray, m_lpObjectData, &lpRowSetNotif, true, true);
- if(er != erSuccess)
- goto exit;
-
- if(lpRowSetNotif->__size != 1) {
- er = KCERR_NOT_FOUND;
- goto exit;
- }
- lpSession->AddNotificationTable(ulAction, m_ulObjType, m_ulTableId, &sRowItem, lpsPrevRow, &lpRowSetNotif->__ptr[0]);
- } else if(ulAction == ECKeyTable::TABLE_ROW_DELETE) {
- lpSession->AddNotificationTable(ulAction, m_ulObjType, m_ulTableId, &sRowItem, NULL, NULL);
- } else {
- er = KCERR_NOT_FOUND;
- goto exit;
- }
-
- exit:
- if(lpRowSetNotif)
- FreeRowSet(lpRowSetNotif, true);
- return er;
- }
- ECRESULT ECGenericObjectTable::QueryRows(struct soap *soap, unsigned int ulRowCount, unsigned int ulFlags, struct rowSet **lppRowSet)
- {
- // Retrieve the keyset from our KeyTable, and use that to retrieve the other columns
- // specified by SetColumns
- struct rowSet *lpRowSet = NULL;
- ECObjectTableList ecRowList;
- scoped_rlock biglock(m_hLock);
- ECRESULT er = Populate();
- if (er != erSuccess)
- return er;
- if(lpsSortOrderArray == NULL) {
- er = SetSortOrder(&sDefaultSortOrder, 0, 0);
- if(er != erSuccess)
- return er;
- }
- // Get the keys per row
- er = lpKeyTable->QueryRows(ulRowCount, &ecRowList, false, ulFlags);
- if(er != erSuccess)
- return er;
- assert(ecRowList.size() <= this->mapObjects.size() + this->m_mapCategories.size());
- if(ecRowList.empty()) {
- lpRowSet = s_alloc<rowSet>(soap);
- lpRowSet->__size = 0;
- lpRowSet->__ptr = NULL;
- } else {
-
- // We now have the ordering of the rows, all we have to do now is get the data.
- er = m_lpfnQueryRowData(this, soap, lpSession, &ecRowList, this->lpsPropTagArray, m_lpObjectData, &lpRowSet, true, true);
- }
- if(er != erSuccess)
- return er;
- *lppRowSet = lpRowSet;
- return er;
- }
- ECRESULT ECGenericObjectTable::CreateBookmark(unsigned int* lpulbkPosition)
- {
- scoped_rlock biglock(m_hLock);
- return lpKeyTable->CreateBookmark(lpulbkPosition);
- }
- ECRESULT ECGenericObjectTable::FreeBookmark(unsigned int ulbkPosition)
- {
- scoped_rlock biglock(m_hLock);
- return lpKeyTable->FreeBookmark(ulbkPosition);
- }
- // Expand the category identified by sInstanceKey
- ECRESULT ECGenericObjectTable::ExpandRow(struct soap *soap, xsd__base64Binary sInstanceKey, unsigned int ulRowCount, unsigned int ulFlags, struct rowSet **lppRowSet, unsigned int *lpulRowsLeft)
- {
- sObjectTableKey sKey;
- sObjectTableKey sPrevRow;
- ECCategoryMap::const_iterator iterCategory;
- ECCategory *lpCategory = NULL;
- ECObjectTableList lstUnhidden;
- unsigned int ulRowsLeft = 0;
- struct rowSet *lpRowSet = NULL;
- scoped_rlock biglock(m_hLock);
-
- ECRESULT er = Populate();
- if(er != erSuccess)
- return er;
- if (sInstanceKey.__size != sizeof(sObjectTableKey))
- return KCERR_INVALID_PARAMETER;
- sKey.ulObjId = *((unsigned int *)sInstanceKey.__ptr);
- sKey.ulOrderId = *((unsigned int *)sInstanceKey.__ptr+1);
-
- iterCategory = m_mapCategories.find(sKey);
- if (iterCategory == m_mapCategories.cend())
- return KCERR_NOT_FOUND;
- lpCategory = iterCategory->second;
- // Unhide all rows under this category
- er = lpKeyTable->UnhideRows(&sKey, &lstUnhidden);
- if(er != erSuccess)
- return er;
- // Only return a maximum of ulRowCount rows
- if(ulRowCount < lstUnhidden.size()) {
- ulRowsLeft = lstUnhidden.size() - ulRowCount;
- lstUnhidden.resize(ulRowCount);
-
- // Put the keytable cursor just after the rows we will be returning, so the next queryrows() would return the remaining rows
- lpKeyTable->SeekRow(1, -ulRowsLeft, NULL);
- }
-
- // Get the row data to return, if required
- if(lppRowSet) {
- if(lstUnhidden.empty()){
- lpRowSet = s_alloc<rowSet>(soap);
- lpRowSet->__size = 0;
- lpRowSet->__ptr = NULL;
- } else {
- // Get data for unhidden rows
- er = m_lpfnQueryRowData(this, soap, lpSession, &lstUnhidden, this->lpsPropTagArray, m_lpObjectData, &lpRowSet, true, true);
- }
- if(er != erSuccess)
- return er;
- }
- lpCategory->m_fExpanded = true;
- if(lppRowSet)
- *lppRowSet = lpRowSet;
- if(lpulRowsLeft)
- *lpulRowsLeft = ulRowsLeft;
- return er;
- }
- // Collapse the category row identified by sInstanceKey
- ECRESULT ECGenericObjectTable::CollapseRow(xsd__base64Binary sInstanceKey, unsigned int ulFlags, unsigned int *lpulRows)
- {
- sObjectTableKey sKey;
- sObjectTableKey sPrevRow;
- ECCategoryMap::const_iterator iterCategory;
- ECCategory *lpCategory = NULL;
- ECObjectTableList lstHidden;
- scoped_rlock biglock(m_hLock);
-
- if (sInstanceKey.__size != sizeof(sObjectTableKey))
- return KCERR_INVALID_PARAMETER;
-
- ECRESULT er = Populate();
- if(er != erSuccess)
- return er;
- sKey.ulObjId = *((unsigned int *)sInstanceKey.__ptr);
- sKey.ulOrderId = *((unsigned int *)sInstanceKey.__ptr+1);
-
- iterCategory = m_mapCategories.find(sKey);
- if (iterCategory == m_mapCategories.cend())
- return KCERR_NOT_FOUND;
- lpCategory = iterCategory->second;
- // Hide the rows under this category
- er = lpKeyTable->HideRows(&sKey, &lstHidden);
- if(er != erSuccess)
- return er;
-
- // Mark the category as collapsed
- lpCategory->m_fExpanded = false;
-
- // Loop through the hidden rows to see if we have hidden any categories. If so, mark them as
- // collapsed
- for (auto iterHidden = lstHidden.cbegin(); iterHidden != lstHidden.cend(); ++iterHidden) {
- iterCategory = m_mapCategories.find(*iterHidden);
-
- if (iterCategory != m_mapCategories.cend())
- iterCategory->second->m_fExpanded = false;
- }
- if(lpulRows)
- *lpulRows = lstHidden.size();
- return er;
- }
- ECRESULT ECGenericObjectTable::GetCollapseState(struct soap *soap, struct xsd__base64Binary sBookmark, struct xsd__base64Binary *lpsCollapseState)
- {
- ECRESULT er = erSuccess;
- struct collapseState sCollapseState;
- int n = 0;
- std::ostringstream os;
- sObjectTableKey sKey;
- struct rowSet *lpsRowSet = NULL;
-
- struct soap xmlsoap; // static, so c++ inits struct, no need for soap init
- ulock_rec biglock(m_hLock);
-
- er = Populate();
- if(er != erSuccess)
- goto exit;
- memset(&sCollapseState, 0, sizeof(sCollapseState));
- // Generate a binary collapsestate which is simply an XML stream of all categories with their collapse state
- sCollapseState.sCategoryStates.__size = m_mapCategories.size();
- sCollapseState.sCategoryStates.__ptr = s_alloc<struct categoryState>(soap, sCollapseState.sCategoryStates.__size);
- memset(sCollapseState.sCategoryStates.__ptr, 0, sizeof(struct categoryState) * sCollapseState.sCategoryStates.__size);
- for (const auto &p : m_mapCategories) {
- sCollapseState.sCategoryStates.__ptr[n].fExpanded = p.second->m_fExpanded;
- sCollapseState.sCategoryStates.__ptr[n].sProps.__ptr = s_alloc<struct propVal>(soap, p.second->m_cProps);
- memset(sCollapseState.sCategoryStates.__ptr[n].sProps.__ptr, 0, sizeof(struct propVal) * p.second->m_cProps);
- for (unsigned int i = 0; i < p.second->m_cProps; ++i) {
- er = CopyPropVal(&p.second->m_lpProps[i], &sCollapseState.sCategoryStates.__ptr[n].sProps.__ptr[i], soap);
- if (er != erSuccess)
- goto exit;
- }
- sCollapseState.sCategoryStates.__ptr[n].sProps.__size = p.second->m_cProps;
- ++n;
- }
- // We also need to save the sort keys for the given bookmark, so that we can return a bookmark when SetCollapseState is called
- if(sBookmark.__size == 8) {
- sKey.ulObjId = *((unsigned int *)sBookmark.__ptr);
- sKey.ulOrderId = *((unsigned int *)sBookmark.__ptr+1);
-
- // Go the the row requested
- if(lpKeyTable->SeekId(&sKey) == erSuccess) {
- // If the row exists, we simply get the data from the properties of this row, including all properties used
- // in the current sort.
- ECObjectTableList list;
-
- list.push_back(sKey);
-
- er = m_lpfnQueryRowData(this, &xmlsoap, lpSession, &list, lpsPropTagArray, m_lpObjectData, &lpsRowSet, false, true);
- if(er != erSuccess)
- goto exit;
-
- // Copy row 1 from rowset into our bookmark props.
- sCollapseState.sBookMarkProps = lpsRowSet->__ptr[0];
-
- // Free of lpsRowSet coupled to xmlsoap so not explicitly needed
- }
- }
-
- soap_set_mode(&xmlsoap, SOAP_XML_TREE | SOAP_C_UTFSTRING);
- xmlsoap.os = &os;
-
- soap_serialize_collapseState(&xmlsoap, &sCollapseState);
- soap_begin_send(&xmlsoap);
- soap_put_collapseState(&xmlsoap, &sCollapseState, "CollapseState", NULL);
- soap_end_send(&xmlsoap);
-
- // os.str() now contains serialized objects, copy into return structure
- lpsCollapseState->__size = os.str().size();
- lpsCollapseState->__ptr = s_alloc<unsigned char>(soap, os.str().size());
- memcpy(lpsCollapseState->__ptr, os.str().c_str(), os.str().size());
- exit:
- soap_destroy(&xmlsoap);
- soap_end(&xmlsoap);
- // static struct, so c++ destructor frees memory
- biglock.unlock();
- return er;
- }
- ECRESULT ECGenericObjectTable::SetCollapseState(struct xsd__base64Binary sCollapseState, unsigned int *lpulBookmark)
- {
- ECRESULT er = erSuccess;
- struct soap xmlsoap;
- struct collapseState cst;
- std::istringstream is(std::string((const char *)sCollapseState.__ptr, sCollapseState.__size));
- sObjectTableKey sKey;
- struct xsd__base64Binary sInstanceKey;
- ulock_rec giblock(m_hLock);
-
- er = Populate();
- if(er != erSuccess)
- goto exit;
- // The collapse state is the serialized collapse state as returned by GetCollapseState(), which we need to parse here
- soap_set_mode(&xmlsoap, SOAP_XML_TREE | SOAP_C_UTFSTRING);
- xmlsoap.is = &is;
-
- soap_default_collapseState(&xmlsoap, &cst);
- if (soap_begin_recv(&xmlsoap) != 0) {
- er = KCERR_NETWORK_ERROR;
- goto exit;
- }
- soap_get_collapseState(&xmlsoap, &cst, "CollapseState", NULL);
- if(xmlsoap.error) {
- er = KCERR_DATABASE_ERROR;
- ec_log_crit("ECGenericObjectTable::SetCollapseState(): xmlsoap error %d", xmlsoap.error);
- goto exit;
- }
-
- /* @cst now contains the collapse state for all categories, apply them now. */
- for (gsoap_size_t i = 0; i < cst.sCategoryStates.__size; ++i) {
- std::unique_ptr<unsigned int[]> lpSortLen(new unsigned int[cst.sCategoryStates.__ptr[i].sProps.__size]);
- std::unique_ptr<unsigned char *[]> lpSortData(new unsigned char *[cst.sCategoryStates.__ptr[i].sProps.__size]);
- std::unique_ptr<unsigned char[]> lpSortFlags(new unsigned char[cst.sCategoryStates.__ptr[i].sProps.__size]);
-
- memset(lpSortData.get(), 0, cst.sCategoryStates.__ptr[i].sProps.__size * sizeof(unsigned char *));
-
- // Get the binary sortkeys for all properties
- for (gsoap_size_t n = 0; n < cst.sCategoryStates.__ptr[i].sProps.__size; ++n) {
- if (GetBinarySortKey(&cst.sCategoryStates.__ptr[i].sProps.__ptr[n], &lpSortLen[n], &lpSortData[n]) != erSuccess)
- goto next;
- if (GetSortFlags(cst.sCategoryStates.__ptr[i].sProps.__ptr[n].ulPropTag, &lpSortFlags[n]) != erSuccess)
- goto next;
- }
- // Find the category and expand or collapse it. If it's not there anymore, just ignore it.
- if (lpKeyTable->Find(cst.sCategoryStates.__ptr[i].sProps.__size,
- reinterpret_cast<int *>(lpSortLen.get()),
- lpSortData.get(), lpSortFlags.get(), &sKey) == erSuccess) {
- sInstanceKey.__size = 8;
- sInstanceKey.__ptr = (unsigned char *)&sKey;
-
- if (cst.sCategoryStates.__ptr[i].fExpanded)
- ExpandRow(NULL, sInstanceKey, 0, 0, NULL, NULL);
- else
- CollapseRow(sInstanceKey, 0, NULL);
- }
- next:
- for (gsoap_size_t j = 0; j < cst.sCategoryStates.__ptr[i].sProps.__size; ++j)
- delete[] lpSortData[j];
- }
-
- // 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,
- // we return a bookmark to the nearest next row.
- if (cst.sBookMarkProps.__size > 0) {
- std::unique_ptr<unsigned int[]> lpSortLen(new unsigned int[cst.sBookMarkProps.__size]);
- std::unique_ptr<unsigned char *[]> lpSortData(new unsigned char *[cst.sBookMarkProps.__size]);
- std::unique_ptr<unsigned char[]> lpSortFlags(new unsigned char[cst.sBookMarkProps.__size]);
-
- memset(lpSortData.get(), 0, cst.sBookMarkProps.__size * sizeof(unsigned char *));
- gsoap_size_t n;
- for (n = 0; n < cst.sBookMarkProps.__size; ++n) {
- if (GetBinarySortKey(&cst.sBookMarkProps.__ptr[n], &lpSortLen[n], &lpSortData[n]) != erSuccess)
- break;
- if (GetSortFlags(cst.sBookMarkProps.__ptr[n].ulPropTag, &lpSortFlags[n]) != erSuccess)
- break;
- }
-
- // If an error occurred in the previous loop, just ignore the whole bookmark thing, just return bookmark 0 (BOOKMARK_BEGINNING)
- if (n == cst.sBookMarkProps.__size) {
- lpKeyTable->LowerBound(cst.sBookMarkProps.__size,
- reinterpret_cast<int *>(lpSortLen.get()),
- lpSortData.get(), lpSortFlags.get());
- lpKeyTable->CreateBookmark(lpulBookmark);
- }
- for (gsoap_size_t j = 0; j < cst.sBookMarkProps.__size; ++j)
- delete[] lpSortData[j];
- }
-
- /*
- * We do not generate notifications for this event, just like
- * ExpandRow and CollapseRow. You just need to reload the table
- * yourself.
- */
- if (soap_end_recv(&xmlsoap) != 0)
- er = KCERR_NETWORK_ERROR;
-
- exit:
- soap_destroy(&xmlsoap);
- soap_end(&xmlsoap);
- giblock.unlock();
- return er;
- }
- ECRESULT ECGenericObjectTable::UpdateRow(unsigned int ulType, unsigned int ulObjId, unsigned int ulFlags)
- {
- ECRESULT er = erSuccess;
- std::list<unsigned int> lstObjId;
-
- lstObjId.push_back(ulObjId);
-
- er = UpdateRows(ulType, &lstObjId, ulFlags, false);
-
- return er;
- }
- /**
- * Load a set of rows into the table
- *
- * This is called to populate a table initially, it is functionally equivalent to calling UpdateRow() repeatedly
- * for each item in lstObjId with ulType set to ECKeyTable::TABLE_ROW_ADD.
- *
- * @param lstObjId List of hierarchy IDs for the objects to load
- * @param ulFlags 0, MSGFLAG_DELETED, MAPI_ASSOCIATED or combination
- */
- ECRESULT ECGenericObjectTable::LoadRows(std::list<unsigned int> *lstObjId, unsigned int ulFlags)
- {
- return UpdateRows(ECKeyTable::TABLE_ROW_ADD, lstObjId, ulFlags, true);
- }
- /**
- * Update one or more rows in a table
- *
- * This function adds, modifies or removes objects from a table. The normal function of this is that normally
- * either multiple objects are added, or a single object is removed or updated. The result of such an update can
- * be complex, for example adding an item to a table may cause multiple rows to be added when using categorization
- * or multi-valued properties. In the same way, an update may generate multiple notifications if category headers are
- * involved or when the update modifies the sorting position of the row.
- *
- * Rows are also checked for read permissions here before being added to the table.
- *
- * The bLoad parameter is not used in the ECGenericObjectTable implementation, but simply passed to AddRowKey to indicate
- * whether the rows are being updated due to a change or due to initial loading. The parameter is also not used in AddRowKey
- * but can be used by subclasses to generate different behaviour on the initial load compared to later updates.
- *
- * @param ulType ECKeyTable::TABLE_ROW_ADD, TABLE_ROW_DELETE or TABLE_ROW_MODIFY
- * @param lstObjId List of objects to add, modify or delete
- * @param ulFlags Flags for the objects in lstObjId (0, MSGFLAG_DELETED, MAPI_ASSOCIATED)
- * @param bLoad Indicates that this is the initial load or reload of the table, and not an update
- */
- ECRESULT ECGenericObjectTable::UpdateRows(unsigned int ulType, std::list<unsigned int> *lstObjId, unsigned int ulFlags, bool bLoad)
- {
- ECRESULT er = erSuccess;
- unsigned int ulRead = 0;
- unsigned int cMVOld = 0,
- cMVNew = 1;
- unsigned int i;
- std::list<unsigned int> lstFilteredIds;
-
- ECObjectTableList ecRowsItem;
- ECObjectTableList ecRowsDeleted;
- sObjectTableKey sRow;
- scoped_rlock biglock(m_hLock);
- // Perform security checks for this object
- switch(ulType) {
- case ECKeyTable::TABLE_CHANGE:
- // Accept table change in all cases
- break;
- case ECKeyTable::TABLE_ROW_MODIFY:
- case ECKeyTable::TABLE_ROW_ADD:
- // Filter out any item we cannot access (for example, in search-results tables)
- for (const auto &obj_id : *lstObjId)
- if (CheckPermissions(obj_id) == erSuccess)
- lstFilteredIds.push_back(obj_id);
- // Use our filtered list now
- lstObjId = &lstFilteredIds;
- break;
- case ECKeyTable::TABLE_ROW_DELETE:
- // You may always delete a row
- break;
- }
- if(lpsSortOrderArray == NULL) {
- er = SetSortOrder(&sDefaultSortOrder, 0, 0);
- if(er != erSuccess)
- return er;
- }
- // Update a row in the keyset as having changed. Get the data from the DB and send it to the KeyTable.
- switch(ulType) {
- case ECKeyTable::TABLE_ROW_DELETE:
- // Delete the object ID from our object list, and all items with that object ID (including various order IDs)
- for (const auto &obj_id : *lstObjId) {
- auto iterMapObject = this->mapObjects.find(sObjectTableKey(obj_id, 0));
- while (iterMapObject != this->mapObjects.cend()) {
- if (iterMapObject->first.ulObjId == obj_id)
- ecRowsItem.push_back(iterMapObject->first);
- else if (iterMapObject->first.ulObjId != obj_id)
- break;
- ++iterMapObject;
- }
-
- for (const auto &row : ecRowsItem) {
- this->mapObjects.erase(row);
- /* Delete the object from the active keyset */
- DeleteRow(row, ulFlags);
- RemoveCategoryAfterRemoveRow(row, ulFlags);
- }
- }
- break;
- case ECKeyTable::TABLE_ROW_MODIFY:
- case ECKeyTable::TABLE_ROW_ADD:
- for (const auto &obj_id : *lstObjId) {
- /* Add the object to our list of objects */
- ecRowsItem.push_back(sObjectTableKey(obj_id, 0));
- if(IsMVSet() == true) {
- // get new mvprop count
- er = GetMVRowCount(obj_id, &cMVNew);
- if (er != erSuccess)
- assert(false);// What now???
- // get old mvprops count
- cMVOld = 0;
- auto iterMapObject = this->mapObjects.find(sObjectTableKey(obj_id, 0));
- while (iterMapObject != this->mapObjects.cend()) {
- if (iterMapObject->first.ulObjId == obj_id) {
- ++cMVOld;
- if(cMVOld > cMVNew && (ulFlags&OBJECTTABLE_NOTIFY) == OBJECTTABLE_NOTIFY) {
- auto iterToDelete = iterMapObject;
- --iterMapObject;
- sRow = iterToDelete->first;
- //Delete of map
- this->mapObjects.erase(iterToDelete->first);
-
- DeleteRow(sRow, ulFlags);
-
- RemoveCategoryAfterRemoveRow(sRow, ulFlags);
- }//if(cMVOld > cMVNew)
- } else if (iterMapObject->first.ulObjId != obj_id)
- break;
- ++iterMapObject;
- }
-
- sRow = sObjectTableKey(obj_id, 0);
- for (i = 1; i < cMVNew; ++i) { // 0 already added
- sRow.ulOrderId = i;
- ecRowsItem.push_back(sRow);
- }
- }
- }
-
- // Remember that the specified row is available
- for (const auto &row : ecRowsItem)
- this->mapObjects[row] = 1;
-
- // Add/modify the key in the keytable
- er = AddRowKey(&ecRowsItem, &ulRead, ulFlags, bLoad, false, NULL);
- if(er != erSuccess)
- return er;
- break;
- case ECKeyTable::TABLE_CHANGE:
- // The whole table needs to be reread
- this->Clear();
- er = this->Load();
- lpSession->AddNotificationTable(ulType, m_ulObjType, m_ulTableId, NULL, NULL, NULL);
- break;
- }
- return er;
- }
- ECRESULT ECGenericObjectTable::GetRestrictPropTagsRecursive(struct restrictTable *lpsRestrict, list<ULONG> *lpPropTags, ULONG ulLevel)
- {
- ECRESULT er = erSuccess;
- if (ulLevel > RESTRICT_MAX_DEPTH)
- return KCERR_TOO_COMPLEX;
- switch(lpsRestrict->ulType) {
- case RES_COMMENT:
- er = GetRestrictPropTagsRecursive(lpsRestrict->lpComment->lpResTable, lpPropTags, ulLevel+1);
- if(er != erSuccess)
- return er;
- break;
-
- case RES_OR:
- for (gsoap_size_t i = 0; i < lpsRestrict->lpOr->__size; ++i) {
- er = GetRestrictPropTagsRecursive(lpsRestrict->lpOr->__ptr[i], lpPropTags, ulLevel+1);
- if(er != erSuccess)
- return er;
- }
- break;
-
- case RES_AND:
- for (gsoap_size_t i = 0; i < lpsRestrict->lpAnd->__size; ++i) {
- er = GetRestrictPropTagsRecursive(lpsRestrict->lpAnd->__ptr[i], lpPropTags, ulLevel+1);
- if(er != erSuccess)
- return er;
- }
- break;
- case RES_NOT:
- er = GetRestrictPropTagsRecursive(lpsRestrict->lpNot->lpNot, lpPropTags, ulLevel+1);
- if(er != erSuccess)
- return er;
- break;
- case RES_CONTENT:
- lpPropTags->push_back(lpsRestrict->lpContent->ulPropTag);
- break;
- case RES_PROPERTY:
- if(PROP_ID(lpsRestrict->lpProp->ulPropTag) == PROP_ID(PR_ANR))
- lpPropTags->insert(lpPropTags->end(), sANRProps, sANRProps + ARRAY_SIZE(sANRProps));
-
- else {
- lpPropTags->push_back(lpsRestrict->lpProp->lpProp->ulPropTag);
- lpPropTags->push_back(lpsRestrict->lpProp->ulPropTag);
- }
- break;
- case RES_COMPAREPROPS:
- lpPropTags->push_back(lpsRestrict->lpCompare->ulPropTag1);
- lpPropTags->push_back(lpsRestrict->lpCompare->ulPropTag2);
- break;
- case RES_BITMASK:
- lpPropTags->push_back(lpsRestrict->lpBitmask->ulPropTag);
- break;
- case RES_SIZE:
- lpPropTags->push_back(lpsRestrict->lpSize->ulPropTag);
- break;
- case RES_EXIST:
- lpPropTags->push_back(lpsRestrict->lpExist->ulPropTag);
- break;
- case RES_SUBRESTRICTION:
- lpPropTags->push_back(PR_ENTRYID); // we need the entryid in subrestriction searches, because we need to know which object to subsearch
- break;
- }
- return erSuccess;
- }
- /**
- * Generate a list of all properties required to evaluate a restriction
- *
- * The list of properties returned are the maximum set of properties required to evaluate the given restriction. Additionally
- * a list of properties can be added to the front of the property set. If the property is required both through the prefix list
- * and through the restriction, it is included only once in the property list.
- *
- * The order of the first N properties in the returned proptag array are guaranteed to be equal to the N items in lstPrefix
- *
- * @param[in] lpsRestrict Restriction tree to evaluate
- * @param[in] lstPrefix NULL or list of property tags to prefix
- * @param[out] lppPropTags PropTagArray with proptags from lpsRestrict and lstPrefix
- * @return ECRESULT
- */
- ECRESULT ECGenericObjectTable::GetRestrictPropTags(struct restrictTable *lpsRestrict, std::list<ULONG> *lstPrefix, struct propTagArray **lppPropTags)
- {
- ECRESULT er;
- struct propTagArray *lpPropTagArray;
- std::list<ULONG> lstPropTags;
- // Just go through all the properties, adding the properties one-by-one
- er = GetRestrictPropTagsRecursive(lpsRestrict, &lstPropTags, 0);
- if (er != erSuccess)
- return er;
- // Sort and unique-ize the properties (order is not important in the returned array)
- lstPropTags.sort();
- lstPropTags.unique();
-
- // Prefix if needed
- if(lstPrefix)
- lstPropTags.insert(lstPropTags.begin(), lstPrefix->begin(), lstPrefix->end());
- lpPropTagArray = s_alloc<propTagArray>(nullptr);
- // Put the data into an array
- lpPropTagArray->__size = lstPropTags.size();
- lpPropTagArray->__ptr = s_alloc<unsigned int>(nullptr, lpPropTagArray->__size);
- copy(lstPropTags.begin(), lstPropTags.end(), lpPropTagArray->__ptr);
- *lppPropTags = lpPropTagArray;
- return erSuccess;
- }
- // Simply matches the restriction with the given data. Make sure you pass all the data
- // needed for the restriction in lpPropVals. (missing columns do not match, ever.)
- ECRESULT ECGenericObjectTable::MatchRowRestrict(ECCacheManager* lpCacheManager, propValArray *lpPropVals, restrictTable *lpsRestrict, SUBRESTRICTIONRESULTS *lpSubResults, const ECLocale &locale, bool *lpfMatch, unsigned int *lpulSubRestriction)
- {
- ECRESULT er = erSuccess;
- bool fMatch = false;
- int lCompare = 0;
- unsigned int ulSize = 0;
- struct propVal *lpProp = NULL;
- struct propVal *lpProp2 = NULL;
- char* lpSearchString;
- char* lpSearchData;
- unsigned int ulSearchDataSize;
- unsigned int ulSearchStringSize;
- ULONG ulPropType;
- ULONG ulFuzzyLevel;
- unsigned int ulSubRestrict = 0;
- entryId sEntryId;
- unsigned int ulResId = 0;
- unsigned int ulPropTagRestrict;
- unsigned int ulPropTagValue;
-
- if(lpulSubRestriction == NULL) // called externally
- lpulSubRestriction = &ulSubRestrict;
-
- switch(lpsRestrict->ulType) {
- case RES_COMMENT:
- if (lpsRestrict->lpComment == NULL)
- return KCERR_INVALID_TYPE;
- er = MatchRowRestrict(lpCacheManager, lpPropVals, lpsRestrict->lpComment->lpResTable, lpSubResults, locale, &fMatch, lpulSubRestriction);
- break;
-
- case RES_OR:
- if (lpsRestrict->lpOr == NULL)
- return KCERR_INVALID_TYPE;
- fMatch = false;
- for (gsoap_size_t i = 0; i < lpsRestrict->lpOr->__size; ++i) {
- er = MatchRowRestrict(lpCacheManager, lpPropVals, lpsRestrict->lpOr->__ptr[i], lpSubResults, locale, &fMatch, lpulSubRestriction);
- if(er != erSuccess)
- return er;
- if(fMatch) // found a restriction in an OR which matches, ignore the rest of the query
- break;
- }
- break;
- case RES_AND:
- if (lpsRestrict->lpAnd == NULL)
- return KCERR_INVALID_TYPE;
- fMatch = true;
- for (gsoap_size_t i = 0; i < lpsRestrict->lpAnd->__size; ++i) {
- er = MatchRowRestrict(lpCacheManager, lpPropVals, lpsRestrict->lpAnd->__ptr[i], lpSubResults, locale, &fMatch, lpulSubRestriction);
- if(er != erSuccess)
- return er;
- if(!fMatch) // found a restriction in an AND which doesn't match, ignore the rest of the query
- break;
- }
- break;
- case RES_NOT:
- if (lpsRestrict->lpNot == NULL)
- return KCERR_INVALID_TYPE;
- er = MatchRowRestrict(lpCacheManager, lpPropVals, lpsRestrict->lpNot->lpNot, lpSubResults, locale, &fMatch, lpulSubRestriction);
- if(er != erSuccess)
- return er;
- fMatch = !fMatch;
- break;
- case RES_CONTENT:
- if (lpsRestrict->lpContent == NULL ||
- lpsRestrict->lpContent->lpProp == NULL)
- return KCERR_INVALID_TYPE;
- // FIXME: FL_IGNORENONSPACE and FL_LOOSE are ignored
- ulPropTagRestrict = lpsRestrict->lpContent->ulPropTag;
- ulPropTagValue = lpsRestrict->lpContent->lpProp->ulPropTag;
- // use the same string type in compares
- if ((PROP_TYPE(ulPropTagRestrict) & PT_MV_STRING8) == PT_STRING8)
- ulPropTagRestrict = CHANGE_PROP_TYPE(ulPropTagRestrict, PT_TSTRING);
- else if ((PROP_TYPE(ulPropTagRestrict) & PT_MV_STRING8) == PT_MV_STRING8)
- ulPropTagRestrict = CHANGE_PROP_TYPE(ulPropTagRestrict, PT_MV_TSTRING);
- // @todo are MV properties in the compare prop allowed?
- if ((PROP_TYPE(ulPropTagValue) & PT_MV_STRING8) == PT_STRING8)
- ulPropTagValue = CHANGE_PROP_TYPE(ulPropTagValue, PT_TSTRING);
- else if ((PROP_TYPE(ulPropTagValue) & PT_MV_STRING8) == PT_MV_STRING8)
- ulPropTagValue = CHANGE_PROP_TYPE(ulPropTagValue, PT_MV_TSTRING);
- if( PROP_TYPE(ulPropTagRestrict) != PT_TSTRING &&
- PROP_TYPE(ulPropTagRestrict) != PT_BINARY &&
- PROP_TYPE(ulPropTagRestrict) != PT_MV_TSTRING &&
- PROP_TYPE(ulPropTagRestrict) != PT_MV_BINARY &&
- lpsRestrict->lpContent->lpProp != NULL)
- {
- assert(false);
- fMatch = false;
- break;
- }
- // find using original proptag from restriction
- lpProp = FindProp(lpPropVals, lpsRestrict->lpContent->ulPropTag);
- if(lpProp == NULL) {
- fMatch = false;
- break;
- } else {
- unsigned int ulScan = 1;
- if(ulPropTagRestrict & MV_FLAG)
- {
- if(PROP_TYPE(ulPropTagRestrict) == PT_MV_TSTRING)
- ulScan = lpProp->Value.mvszA.__size;
- else
- ulScan = lpProp->Value.mvbin.__size;
- }
- ulPropType = PROP_TYPE(ulPropTagRestrict)&~MVI_FLAG;
-
- if(PROP_TYPE(ulPropTagValue) == PT_TSTRING) {
- lpSearchString = lpsRestrict->lpContent->lpProp->Value.lpszA;
- ulSearchStringSize = (lpSearchString)?strlen(lpSearchString):0;
- }else {
- lpSearchString = (char*)lpsRestrict->lpContent->lpProp->Value.bin->__ptr;
- ulSearchStringSize = lpsRestrict->lpContent->lpProp->Value.bin->__size;
- }
-
- // Default match is false
- fMatch = false;
- for (unsigned int ulPos = 0; ulPos < ulScan; ++ulPos) {
- if(ulPropTagRestrict & MV_FLAG)
- {
- if(PROP_TYPE(ulPropTagRestrict) == PT_MV_TSTRING) {
- lpSearchData = lpProp->Value.mvszA.__ptr[ulPos];
- ulSearchDataSize = (lpSearchData)?strlen(lpSearchData):0;
- }else {
- lpSearchData = (char*)lpProp->Value.mvbin.__ptr[ulPos].__ptr;
- ulSearchDataSize = lpProp->Value.mvbin.__ptr[ulPos].__size;
- }
- }else {
- if(PROP_TYPE(ulPropTagRestrict) == PT_TSTRING) {
- lpSearchData = lpProp->Value.lpszA;
- ulSearchDataSize = (lpSearchData)?strlen(lpSearchData):0;
- }else {
- lpSearchData = (char*)lpProp->Value.bin->__ptr;
- ulSearchDataSize = lpProp->Value.bin->__size;
- }
- }
- ulFuzzyLevel = lpsRestrict->lpContent->ulFuzzyLevel;
- switch(ulFuzzyLevel & 0xFFFF) {
- case FL_FULLSTRING:
- if(ulSearchDataSize == ulSearchStringSize)
- if ((ulPropType == PT_TSTRING && (ulFuzzyLevel & FL_IGNORECASE) && u8_iequals(lpSearchData, lpSearchString, locale)) ||
- (ulPropType == PT_TSTRING && ((ulFuzzyLevel & FL_IGNORECASE) == 0) && u8_equals(lpSearchData, lpSearchString, locale)) ||
- (ulPropType != PT_TSTRING && memcmp(lpSearchData, lpSearchString, ulSearchDataSize) == 0))
- fMatch = true;
- break;
- case FL_PREFIX:
- if(ulSearchDataSize >= ulSearchStringSize)
- if ((ulPropType == PT_TSTRING && (ulFuzzyLevel & FL_IGNORECASE) && u8_istartswith(lpSearchData, lpSearchString, locale)) ||
- (ulPropType == PT_TSTRING && ((ulFuzzyLevel & FL_IGNORECASE) == 0) && u8_startswith(lpSearchData, lpSearchString, locale)) ||
- (ulPropType != PT_TSTRING && memcmp(lpSearchData, lpSearchString, ulSearchStringSize) == 0))
- fMatch = true;
- break;
- case FL_SUBSTRING:
- if ((ulPropType == PT_TSTRING && (ulFuzzyLevel & FL_IGNORECASE) && u8_icontains(lpSearchData, lpSearchString, locale)) ||
- (ulPropType == PT_TSTRING && ((ulFuzzyLevel & FL_IGNORECASE) == 0) && u8_contains(lpSearchData, lpSearchString, locale)) ||
- (ulPropType != PT_TSTRING && memsubstr(lpSearchData, ulSearchDataSize, lpSearchString, ulSearchStringSize) == 0))
- fMatch = true;
- break;
- }
- if(fMatch)
- break;
- }
- }
- break;
- case RES_PROPERTY:
- if (lpsRestrict->lpProp == NULL ||
- lpsRestrict->lpProp->lpProp == NULL)
- return KCERR_INVALID_TYPE;
- ulPropTagRestrict = lpsRestrict->lpProp->ulPropTag;
- ulPropTagValue = lpsRestrict->lpProp->lpProp->ulPropTag;
- // use the same string type in compares
- if ((PROP_TYPE(ulPropTagRestrict) & PT_MV_STRING8) == PT_STRING8)
- ulPropTagRestrict = CHANGE_PROP_TYPE(ulPropTagRestrict, PT_TSTRING);
- else if ((PROP_TYPE(ulPropTagRestrict) & PT_MV_STRING8) == PT_MV_STRING8)
- ulPropTagRestrict = CHANGE_PROP_TYPE(ulPropTagRestrict, PT_MV_TSTRING);
- if (PROP_TYPE(ulPropTagValue) == PT_STRING8)
- ulPropTagValue = CHANGE_PROP_TYPE(ulPropTagValue, PT_TSTRING);
- if((PROP_TYPE(ulPropTagRestrict) & ~MV_FLAG) != PROP_TYPE(ulPropTagValue))
- // cannot compare two different types, except mvprop -> prop
- return KCERR_INVALID_TYPE;
- #if 1 /* HAVE_REGEX_H */
- if(lpsRestrict->lpProp->ulType == RELOP_RE) {
- regex_t reg;
- // find using original restriction proptag
- lpProp = FindProp(lpPropVals, lpsRestrict->lpProp->ulPropTag);
- if(lpProp == NULL) {
- fMatch = false;
- break;
- }
- // @todo add support for ulPropTagRestrict PT_MV_TSTRING
- if (PROP_TYPE(ulPropTagValue) != PT_TSTRING ||
- PROP_TYPE(ulPropTagRestrict) != PT_TSTRING)
- return KCERR_INVALID_TYPE;
-
- if(regcomp(®, lpsRestrict->lpProp->lpProp->Value.lpszA, REG_NOSUB | REG_NEWLINE | REG_ICASE) != 0) {
- fMatch = false;
- break;
- }
-
- if(regexec(®, lpProp->Value.lpszA, 0, NULL, 0) == 0)
- fMatch = true;
-
- regfree(®);
-
- // Finished for this restriction
- break;
- }
- #endif
- if(PROP_ID(ulPropTagRestrict) == PROP_ID(PR_ANR))
- {
- for (size_t j = 0; j < ARRAY_SIZE(sANRProps); ++j) {
- lpProp = FindProp(lpPropVals, sANRProps[j]);
- // We need this because CompareProp will fail if the types are not the same
- if(lpProp) {
- lpProp->ulPropTag = lpsRestrict->lpProp->lpProp->ulPropTag;
- CompareProp(lpProp, lpsRestrict->lpProp->lpProp, locale, &lCompare); //IGNORE error
- } else
- continue;
-
- // PR_ANR has special semantics, lCompare is 1 if the substring is found, 0 if not
-
- // Note that RELOP_EQ will work as expected, but RELOP_GT and RELOP_LT will
- // not work. Use of these is undefined anyway. RELOP_NE is useless since one of the
- // strings will definitely not match, so RELOP_NE will almost match.
- lCompare = lCompare ? 0 : -1;
-
- fMatch = match(lpsRestrict->lpProp->ulType, lCompare);
-
- if(fMatch)
- break;
- }
-
- // Finished for this restriction
- break;
- }else {
- // find using original restriction proptag
- lpProp = FindProp(lpPropVals, lpsRestrict->lpProp->ulPropTag);
- if(lpProp == NULL) {
- if(lpsRestrict->lpProp->ulType == RELOP_NE)
- fMatch = true;
- else
- fMatch = false;
- break;
- }
-
- if((ulPropTagRestrict&MV_FLAG)) {
- er = CompareMVPropWithProp(lpProp, lpsRestrict->lpProp->lpProp, lpsRestrict->lpProp->ulType, locale, &fMatch);
- if(er != erSuccess)
- {
- assert(false);
- er = erSuccess;
- fMatch = false;
- break;
- }
- } else {
- er = CompareProp(lpProp, lpsRestrict->lpProp->lpProp, locale, &lCompare);
- if(er != erSuccess)
- {
- assert(false);
- er = erSuccess;
- fMatch = false;
- break;
- }
-
- fMatch = match(lpsRestrict->lpProp->ulType, lCompare);
- }
- }// if(ulPropTagRestrict == PR_ANR)
- break;
-
- case RES_COMPAREPROPS:
- if (lpsRestrict->lpCompare == NULL)
- return KCERR_INVALID_TYPE;
- unsigned int ulPropTag1;
- unsigned int ulPropTag2;
- ulPropTag1 = lpsRestrict->lpCompare->ulPropTag1;
- ulPropTag2 = lpsRestrict->lpCompare->ulPropTag2;
- // use the same string type in compares
- if ((PROP_TYPE(ulPropTag1) & PT_MV_STRING8) == PT_STRING8)
- ulPropTag1 = CHANGE_PROP_TYPE(ulPropTag1, PT_TSTRING);
- else if ((PROP_TYPE(ulPropTag1) & PT_MV_STRING8) == PT_MV_STRING8)
- ulPropTag1 = CHANGE_PROP_TYPE(ulPropTag1, PT_MV_TSTRING);
- // use the same string type in compares
- if ((PROP_TYPE(ulPropTag2) & PT_MV_STRING8) == PT_STRING8)
- ulPropTag2 = CHANGE_PROP_TYPE(ulPropTag2, PT_TSTRING);
- else if ((PROP_TYPE(ulPropTag2) & PT_MV_STRING8) == PT_MV_STRING8)
- ulPropTag2 = CHANGE_PROP_TYPE(ulPropTag2, PT_MV_TSTRING);
- // FIXME: Is this check correct, PT_STRING8 vs PT_ERROR == false and not a error? (RELOP_NE == true)
- if (PROP_TYPE(ulPropTag1) != PROP_TYPE(ulPropTag2))
- // cannot compare two different types
- return KCERR_INVALID_TYPE;
- // find using original restriction proptag
- lpProp = FindProp(lpPropVals, lpsRestrict->lpCompare->ulPropTag1);
- lpProp2 = FindProp(lpPropVals, lpsRestrict->lpCompare->ulPropTag2);
- if(lpProp == NULL || lpProp2 == NULL) {
- fMatch = false;
- break;
- }
- er = CompareProp(lpProp, lpProp2, locale, &lCompare);
- if(er != erSuccess)
- {
- assert(false);
- er = erSuccess;
- fMatch = false;
- break;
- }
- switch(lpsRestrict->lpCompare->ulType) {
- case RELOP_GE:
- fMatch = lCompare >= 0;
- break;
- case RELOP_GT:
- fMatch = lCompare > 0;
- break;
- case RELOP_LE:
- fMatch = lCompare <= 0;
- break;
- case RELOP_LT:
- fMatch = lCompare < 0;
- break;
- case RELOP_NE:
- fMatch = lCompare != 0;
- break;
- case RELOP_RE:
- fMatch = false; // FIXME ?? how should this work ??
- break;
- case RELOP_EQ:
- fMatch = lCompare == 0;
- break;
- }
- break;
- case RES_BITMASK:
- if (lpsRestrict->lpBitmask == NULL)
- return KCERR_INVALID_TYPE;
- // We can only bitmask 32-bit LONG values (aka ULONG)
- if (PROP_TYPE(lpsRestrict->lpBitmask->ulPropTag) != PT_LONG)
- return KCERR_INVALID_TYPE;
- lpProp = FindProp(lpPropVals, lpsRestrict->lpBitmask->ulPropTag);
- if(lpProp == NULL) {
- fMatch = false;
- break;
- }
- fMatch = (lpProp->Value.ul & lpsRestrict->lpBitmask->ulMask) > 0;
- if(lpsRestrict->lpBitmask->ulType == BMR_EQZ)
- fMatch = !fMatch;
- break;
-
- case RES_SIZE:
- if (lpsRestrict->lpSize == NULL)
- return KCERR_INVALID_TYPE;
- lpProp = FindProp(lpPropVals, lpsRestrict->lpSize->ulPropTag);
- if (lpProp == NULL)
- return KCERR_INVALID_TYPE;
- ulSize = PropSize(lpProp);
- lCompare = ulSize - lpsRestrict->lpSize->cb;
- switch(lpsRestrict->lpSize->ulType) {
- case RELOP_GE:
- fMatch = lCompare >= 0;
- break;
- case RELOP_GT:
- fMatch = lCompare > 0;
- break;
- case RELOP_LE:
- fMatch = lCompare <= 0;
- break;
- case RELOP_LT:
- fMatch = lCompare < 0;
- break;
- case RELOP_NE:
- fMatch = lCompare != 0;
- break;
- case RELOP_RE:
- fMatch = false; // FIXME ?? how should this work ??
- break;
- case RELOP_EQ:
- fMatch = lCompare == 0;
- break;
- }
- break;
- case RES_EXIST:
- if (lpsRestrict->lpExist == NULL)
- return KCERR_INVALID_TYPE;
- lpProp = FindProp(lpPropVals, lpsRestrict->lpExist->ulPropTag);
- fMatch = (lpProp != NULL);
- break;
- case RES_SUBRESTRICTION:
- lpProp = FindProp(lpPropVals, PR_ENTRYID);
- if (lpProp == NULL)
- return KCERR_INVALID_TYPE;
- if(lpSubResults == NULL) {
- fMatch = false;
- } else {
- // Find out if this object matches this subrestriction with the passed
- // subrestriction results.
-
- if(lpSubResults->size() <= ulSubRestrict) {
- fMatch = false; // No results in the results list for this subquery ??
- } else {
- fMatch = false;
- sEntryId.__ptr = lpProp->Value.bin->__ptr;
- sEntryId.__size = lpProp->Value.bin->__size;
- if(lpCacheManager->GetObjectFromEntryId(&sEntryId, &ulResId) == erSuccess)
- {
- auto r = (*lpSubResults)[ulSubRestrict]->find(ulResId); // If the item is in the set, it matched
- if (r != (*lpSubResults)[ulSubRestrict]->cend())
- fMatch = true;
- }
- }
- }
- break;
- default:
- return KCERR_INVALID_TYPE;
- }
- *lpfMatch = fMatch;
- return er;
- }
- bool ECGenericObjectTable::IsMVSet()
- {
- return (m_bMVSort | m_bMVCols);
- }
- void ECGenericObjectTable::SetTableId(unsigned int ulTableId)
- {
- m_ulTableId = ulTableId;
- }
- ECRESULT ECGenericObjectTable::Clear()
- {
- scoped_rlock biglock(m_hLock);
- // Clear old entries
- mapObjects.clear();
- lpKeyTable->Clear();
- m_mapLeafs.clear();
- for (const auto &p : m_mapCategories)
- delete p.second;
- m_mapCategories.clear();
- m_mapSortedCategories.clear();
- return hrSuccess;
- }
- ECRESULT ECGenericObjectTable::Load()
- {
- return hrSuccess;
- }
- ECRESULT ECGenericObjectTable::Populate()
- {
- scoped_rlock biglock(m_hLock);
- if(m_bPopulated)
- return erSuccess;
- m_bPopulated = true;
- return Load();
- }
- // Sort functions, overide this functions as you used a caching system
- ECRESULT ECGenericObjectTable::IsSortKeyExist(const sObjectTableKey* lpsRowItem, unsigned int ulPropTag)
- {
- return KCERR_NOT_FOUND;
- }
- ECRESULT ECGenericObjectTable::GetSortKey(sObjectTableKey* lpsRowItem, unsigned int ulPropTag, unsigned int *lpSortLen, unsigned char **lppSortData)
- {
- assert(false);
- return KCERR_NO_SUPPORT;
- }
- ECRESULT ECGenericObjectTable::SetSortKey(sObjectTableKey* lpsRowItem, unsigned int ulPropTag, unsigned int ulSortLen, unsigned char *lpSortData)
- {
- return KCERR_NO_SUPPORT;
- }
- // Category handling functions
- /*
- * GENERAL workings of categorization
- *
- * Due to min/max categories, the order of rows in the key table (m_lpKeyTable) in not predictable by looking
- * only at a new row, since we don't know what the min/max value for the category is. We therefore have a second
- * sorted list of categories which is purely for looking up if a category exists, and what its min/max values are.
- * This list is m_mapSortedCategories.
- *
- * The order of rows in m_lpKeyTable is the actual order of rows that will be seen by the MAPI Client.
- *
- * quick overview:
- *
- * When a new row is added, we do the following:
- * - Look in m_mapSortedCategories with the categorized properties to see if we already have the category
- * -on new:
- * - Add category to both mapSortedCategories and mapCategories
- * - Initialize counters and possibly min/max value
- * -on existing:
- * - Update counters (unread,count)
- * - Update min/max value
- * -on change of min/max value:
- * - reorder *all* rows of the category
- * - Track the row in m_mapLeafs
- *
- * When a row is removed, we do the following
- * - Find the row in m_mapLeafs
- * - Get the category of the row
- * - Update counters of the category
- * - Update min/max value of the category
- * -on change of min/max value and non-empty category:
- * - reorder *all* rows of the category
- * - If count == 0, remove category
- *
- * We currently support only one min/max category in the table. This is actually only enforced in ECCategory which
- * tracks only one sCurMinMax, the rest of the code should be pretty much able to handle multiple levels of min/max
- * categories.
- */
- /**
- * Add or modify a category row
- *
- * Called just before the actual message row is added to the table.
- *
- * Due to min/max handling, this function may modify MANY rows in the table because the entire category needed to be relocated.
- *
- * @param sObjKey Object key of new (non-category) row
- * @param lpProps Properties of the new or modified row
- * @param cProps Number of properties in lpProps
- * @param ulFlags Notification flags
- * @param fUnread TRUE if the new state of the object in sObjKey is UNREAD
- * @param lpfHidden Returns if the new row should be hidden because the category is collapsed
- * @param lppCategory Returns a reference to the new or existing category that the item sObjKey should be added to
- */
- ECRESULT ECGenericObjectTable::AddCategoryBeforeAddRow(sObjectTableKey sObjKey, struct propVal *lpProps, unsigned int cProps, unsigned int ulFlags, bool fUnread, bool *lpfHidden, ECCategory **lppCategory)
- {
- ECRESULT er = erSuccess;
- bool fPrevUnread = false;
- bool fNewLeaf = false;
- unsigned int i = 0;
- std::unique_ptr<unsigned int[]> lpSortLen;
- std::unique_ptr<unsigned char *[]> lppSortKeys;
- std::unique_ptr<unsigned char[]> lpSortFlags;
- sObjectTableKey sPrevRow(0,0);
- ECCategory *lpCategory = NULL;
- LEAFINFO sLeafInfo;
- ECCategory *lpParent = NULL;
- ECKeyTable::UpdateType ulAction;
- sObjectTableKey sCatRow;
- ECLeafMap::const_iterator iterLeafs;
- int fResult = 0;
- bool fCollapsed = false;
- bool fHidden = false;
-
- if(m_ulCategories == 0)
- goto exit;
-
- lpSortLen.reset(new unsigned int[cProps]);
- lppSortKeys.reset(new unsigned char *[cProps]);
- lpSortFlags.reset(new unsigned char[cProps]);
- // Build binary sort keys
-
- // +1 because we may have a trailing category followed by a MINMAX column
- for (i = 0; i < m_ulCategories + 1 && i < cProps; ++i) {
- if(GetBinarySortKey(&lpProps[i], &lpSortLen[i], &lppSortKeys[i]) != erSuccess)
- lppSortKeys[i] = NULL;
- if(GetSortFlags(lpProps[i].ulPropTag, &lpSortFlags[i]) != erSuccess)
- lpSortFlags[i] = 0;
- }
- // See if we're dealing with a changed row, not a new row
- iterLeafs = m_mapLeafs.find(sObjKey);
- if (iterLeafs != m_mapLeafs.cend()) {
- fPrevUnread = iterLeafs->second.fUnread;
- // The leaf was already in the table, compare new properties of the leaf with those of the category it
- // was in.
- for (i = 0; i < iterLeafs->second.lpCategory->m_cProps && i < cProps; ++i) {
- // If the type is different (ie PT_ERROR first, PT_STRING8 now, then it has definitely changed ..)
- if(PROP_TYPE(lpProps[i].ulPropTag) != PROP_TYPE(iterLeafs->second.lpCategory->m_lpProps[i].ulPropTag))
- break;
-
- // Otherwise, compare the properties
- er = CompareProp(&iterLeafs->second.lpCategory->m_lpProps[i], &lpProps[i], m_locale, &fResult);
- if (er != erSuccess)
- goto exit;
- if(fResult != 0)
- break;
- }
-
- if(iterLeafs->second.lpCategory->m_cProps && i < cProps) {
- // One of the category properties has changed, remove the row from the old category
- RemoveCategoryAfterRemoveRow(sObjKey, ulFlags);
- fNewLeaf = true; // We're re-adding the leaf
- } else if (fUnread == iterLeafs->second.fUnread) {
- // Nothing to do, the leaf was already in the correct category, and the readstate has not changed
- goto exit;
- }
- } else {
- fNewLeaf = true;
- }
-
- // For each level, check if category already exists in key table (LowerBound), gives sObjectTableKey
- for (i = 0; i < m_ulCategories && i < cProps; ++i) {
- unsigned int ulDepth = i;
- bool fCategoryMoved = false; // TRUE if the entire category has moved somewhere (due to CATEG_MIN / CATEG_MAX change)
- ECTableRow row(sObjectTableKey(0, 0), i + 1, lpSortLen.get(), lpSortFlags.get(), lppSortKeys.get(), false);
- // Find the actual category in our sorted category map
- auto iterCategoriesSorted = m_mapSortedCategories.find(row);
- // Include the next sort order if it s CATEG_MIN or CATEG_MAX
- if(lpsSortOrderArray->__size > (int)i+1 && ISMINMAX(lpsSortOrderArray->__ptr[i+1].ulOrder))
- ++i;
-
- if (iterCategoriesSorted == m_mapSortedCategories.cend()) {
- assert(fNewLeaf); // The leaf must be new if the category is new
- // Category not available yet, add it now
- sCatRow.ulObjId = 0;
- sCatRow.ulOrderId = m_ulCategory;
-
- // We are hidden if our parent was collapsed
- fHidden = fCollapsed;
-
- // This category is itself collapsed if our parent was collapsed, or if we should be collapsed due to m_ulExpanded
- fCollapsed = fCollapsed || (ulDepth >= m_ulExpanded);
-
- lpCategory = new ECCategory(m_ulCategory, lpProps, ulDepth+1, i+1, lpParent, ulDepth, !fCollapsed, m_locale);
- ++m_ulCategory;
- lpCategory->IncLeaf(); // New category has 1 leaf
-
- // Make sure the category has the current row as min/max value
- er = UpdateCategoryMinMax(sObjKey, lpCategory, i, lpProps, cProps, NULL);
- if(er != erSuccess)
- goto exit;
-
- if(fUnread)
- lpCategory->IncUnread();
- // Add the category into our sorted-category list and numbered-category list
- assert(m_mapSortedCategories.find(row) == m_mapSortedCategories.end());
- m_mapCategories[sCatRow] = lpCategory;
- lpCategory->iSortedCategory = m_mapSortedCategories.insert(std::make_pair(row, sCatRow)).first;
- // Update the keytable with the effective sort columns
- er = UpdateKeyTableRow(lpCategory, &sCatRow, lpProps, i+1, fHidden, &sPrevRow, &ulAction);
- if (er != erSuccess)
- goto exit;
- lpParent = lpCategory;
- } else {
- // Category already available
- sCatRow = iterCategoriesSorted->second;
- // Get prev row for notification purposes
- if(lpKeyTable->GetPreviousRow(&sCatRow, &sPrevRow) != erSuccess) {
- sPrevRow.ulObjId = 0;
- sPrevRow.ulOrderId = 0;
- }
-
- auto iterCategory = m_mapCategories.find(sCatRow);
- if (iterCategory == m_mapCategories.cend()) {
- assert(false);
- er = KCERR_NOT_FOUND;
- goto exit;
- }
- lpCategory = iterCategory->second;
- // Increase leaf count of category (each level must be increased by one) for a new leaf
- if(fNewLeaf) {
- lpCategory->IncLeaf();
- if(fUnread)
- lpCategory->IncUnread();
- } else {
- // Increase or decrease unread counter depending on change of the leaf's unread state
- if(fUnread && !fPrevUnread)
- lpCategory->IncUnread();
-
- if(!fUnread && fPrevUnread)
- lpCategory->DecUnread();
- }
-
- // This category was hidden if the parent was collapsed
- fHidden = fCollapsed;
- // Remember if this category was collapsed
- fCollapsed = !lpCategory->m_fExpanded;
-
- // Update category min/max values
- er = UpdateCategoryMinMax(sObjKey, lpCategory, i, lpProps, cProps, &fCategoryMoved);
- if(er != erSuccess)
- goto exit;
-
- ulAction = ECKeyTable::TABLE_ROW_MODIFY;
- }
- if (fCategoryMoved) {
- ECObjectTableList lstObjects;
- // The min/max value of this category has changed. We have to move all the rows in the category
- // somewhere else in the table.
-
- // Get the rows that are affected
- er = lpKeyTable->GetRowsBySortPrefix(&sCatRow, &lstObjects);
- if (er != erSuccess)
- goto exit;
-
- // Update the keytable to reflect the new change
- for (auto &obj : lstObjects) {
- // Update the keytable with the new effective sort data for this column
-
- bool bDescend = lpsSortOrderArray->__ptr[ulDepth].ulOrder == EC_TABLE_SORT_DESCEND; // Column we're updating is descending
-
- er = lpKeyTable->UpdatePartialSortKey(&obj,
- ulDepth, lppSortKeys[i], lpSortLen[i],
- lpSortFlags[i] | (bDescend ? TABLEROW_FLAG_DESC : 0),
- &sPrevRow, &fHidden, &ulAction);
- if (er != erSuccess)
- goto exit;
- if ((ulFlags & OBJECTTABLE_NOTIFY) && !fHidden)
- AddTableNotif(ulAction, obj, &sPrevRow);
- }
- }
- // Send notification if required (only the category header has changed)
- else if((ulFlags & OBJECTTABLE_NOTIFY) && !fHidden) {
- AddTableNotif(ulAction, sCatRow, &sPrevRow);
- }
- }
-
- // lpCategory is now the deepest category, and therefore the category we're adding the leaf to
- // Add sObjKey to leaf list via LEAFINFO
- sLeafInfo.lpCategory = lpCategory;
- sLeafInfo.fUnread = fUnread;
-
- m_mapLeafs[sObjKey] = sLeafInfo;
-
- // The item that the request was for is hidden if the deepest category was collapsed
- if (lpfHidden != NULL)
- *lpfHidden = fCollapsed;
-
- if(lppCategory)
- *lppCategory = lpCategory;
- assert(m_mapCategories.size() == m_mapSortedCategories.size());
- exit:
- if (lppSortKeys != nullptr)
- for (i = 0; i < m_ulCategories + 1 && i < cProps; ++i)
- delete[] lppSortKeys[i];
- return er;
- }
- /**
- * Update a category min/max value if needed
- *
- * This function updates the min/max value if the category is part of a min/max sorting scheme.
- *
- * @param sKey Key of the category
- * @param lpCategory Category to update
- * @param i Column id of the possible min/max sort
- * @param lpProps Properties for the category
- * @param cProps Number of properties in lpProps
- * @param lpfModified Returns whether the category min/max value was changed
- * @return result
- */
- ECRESULT ECGenericObjectTable::UpdateCategoryMinMax(sObjectTableKey &sKey,
- ECCategory *lpCategory, size_t i, struct propVal *lpProps, size_t cProps,
- bool *lpfModified)
- {
- if (lpsSortOrderArray->__size < 0 ||
- static_cast<size_t>(lpsSortOrderArray->__size) <= i ||
- !ISMINMAX(lpsSortOrderArray->__ptr[i].ulOrder))
- return erSuccess;
- lpCategory->UpdateMinMax(sKey, i, &lpProps[i], lpsSortOrderArray->__ptr[i].ulOrder == EC_TABLE_SORT_CATEG_MAX, lpfModified);
- return erSuccess;
- }
- /**
- * Creates a row in the key table
- *
- * The only complexity of this function is when doing min/max categorization; consider the sort order
- *
- * CONVERSATION_TOPIC ASC, DATE CATEG_MAX, DATE DESC
- * with ulCategories = 1
- *
- * This effectively generates the following category row in the key table:
- *
- * MAX_DATE, CONVERSATION_TOPIC for the category and
- * MAX_DATE, CONVERSATION_TOPIC, DATE for the data row
- *
- * This means we have to get the 'max date' part, generate a sortkey, and switch the order for the first
- * two columns, and add that to the key table.
- *
- * @param lpCategory For a normal row, the category to which it belongs
- * @param lpsRowKey The row key of the row
- * @param ulDepth Number properties in lpProps/cValues to process. For normal rows, ulDepth == cValues
- * @param lpProps Properties from the database of the row
- * @param cValues Number of properties in lpProps
- * @param fHidden TRUE if the row is to be hidden
- * @param sPrevRow Previous row ID
- * @param lpulAction Action performed
- * @return result
- */
- ECRESULT ECGenericObjectTable::UpdateKeyTableRow(ECCategory *lpCategory, sObjectTableKey *lpsRowKey, struct propVal *lpProps, unsigned int cValues, bool fHidden, sObjectTableKey *lpsPrevRow, ECKeyTable::UpdateType *lpulAction)
- {
- ECRESULT er = erSuccess;
- struct propVal sProp;
- struct sortOrderArray *lpsSortOrderArray = this->lpsSortOrderArray;
- struct sortOrder sSortHierarchy = { PR_EC_HIERARCHYID, EC_TABLE_SORT_DESCEND };
- struct sortOrderArray sSortSimple = { &sSortHierarchy, 1 };
- int n = 0;
-
- assert(cValues <= static_cast<unsigned int>(lpsSortOrderArray->__size));
- if(cValues == 0) {
- // No sort columns. We use the object ID as the sorting
- // key. This is fairly arbitrary as any sort order would be okay seen as no sort order was specified. However, sorting
- // in this way makes sure that new items are sorted FIRST by default, which is a logical view when debugging. Also, if
- // any algorithm does assumptions based on the first row, it is best to have the first row be the newest row; this is what
- // happens when you export messages from OLK to a PST and it does a memory calculation of nItems * size_of_first_entryid
- // for the memory requirements of all entryids. (which crashes if you don't do it properly)
- sProp.ulPropTag = PR_EC_HIERARCHYID;
- sProp.Value.ul = lpsRowKey->ulObjId;
- sProp.__union = SOAP_UNION_propValData_ul;
-
- cValues = 1;
- lpProps = &sProp;
- lpsSortOrderArray = &sSortSimple;
- }
-
- std::unique_ptr<struct propVal[]> lpOrderedProps(new struct propVal[cValues]);
- std::unique_ptr<unsigned int[]> lpSortLen(new unsigned int[cValues]);
- std::unique_ptr<unsigned char *[]> lppSortKeys(new unsigned char *[cValues]);
- std::unique_ptr<unsigned char[]> lpSortFlags(new unsigned char[cValues]);
- memset(lpOrderedProps.get(), 0, sizeof(struct propVal) * cValues);
- memset(lpSortLen.get(), 0, sizeof(unsigned int) * cValues);
- memset(lppSortKeys.get(), 0, sizeof(unsigned char *) * cValues);
- memset(lpSortFlags.get(), 0, sizeof(unsigned char) * cValues);
- for (unsigned int i = 0; i < cValues; ++i) {
- if (ISMINMAX(lpsSortOrderArray->__ptr[i].ulOrder)) {
- if (n == 0 || !lpCategory)
- // Min/max ignored if the row is not in a category
- continue;
-
- // Swap around the current and the previous sorting order
- lpOrderedProps[n] = lpOrderedProps[n-1];
- // Get actual sort order from category
- if(lpCategory->GetProp(NULL, lpsSortOrderArray->__ptr[n].ulPropTag, &lpOrderedProps[n-1]) != erSuccess) {
- lpOrderedProps[n-1].ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(lpsSortOrderArray->__ptr[n].ulPropTag));
- lpOrderedProps[n-1].Value.ul = KCERR_NOT_FOUND;
- lpOrderedProps[n-1].__union = SOAP_UNION_propValData_ul;
- }
- } else {
- er = CopyPropVal(&lpProps[n], &lpOrderedProps[n], NULL, false);
- if(er != erSuccess)
- goto exit;
- }
- ++n;
- }
-
- // Build binary sort keys from updated data
- for (int i = 0; i < n; ++i) {
- if(GetBinarySortKey(&lpOrderedProps[i], &lpSortLen[i], &lppSortKeys[i]) != erSuccess)
- lppSortKeys[i] = NULL;
- if(GetSortFlags(lpOrderedProps[i].ulPropTag, &lpSortFlags[i]) != erSuccess)
- lpSortFlags[i] = 0;
- if(lpsSortOrderArray->__ptr[i].ulOrder == EC_TABLE_SORT_DESCEND)
- lpSortFlags[i] |= TABLEROW_FLAG_DESC;
- }
- // Update row
- er = lpKeyTable->UpdateRow(ECKeyTable::TABLE_ROW_ADD, lpsRowKey,
- cValues, lpSortLen.get(), lpSortFlags.get(), lppSortKeys.get(),
- lpsPrevRow, fHidden, lpulAction);
- exit:
- if (lpOrderedProps != nullptr)
- for (unsigned int i = 0; i < cValues; ++i)
- FreePropVal(&lpOrderedProps[i], false);
- if (lppSortKeys != nullptr)
- for (unsigned int i = 0; i < cValues; ++i)
- delete[] lppSortKeys[i];
- return er;
- }
- /**
- * Updates a category after a non-category row has been removed
- *
- * This function updates the category to contain the correct counters, and possibly removes the category if it is empty.
- *
- * Many row changes may be generated in a min/max category when the min/max row is removed from the category, which triggers
- * a reorder of the category in the table.
- *
- * @param sOjbKey Object that was deleted
- * @param ulFlags Notification flags
- * @return result
- */
- ECRESULT ECGenericObjectTable::RemoveCategoryAfterRemoveRow(sObjectTableKey sObjKey, unsigned int ulFlags)
- {
- ECRESULT er = erSuccess;
- sObjectTableKey sCatRow;
- sObjectTableKey sPrevRow(0,0);
- ECLeafMap::const_iterator iterLeafs;
- ECKeyTable::UpdateType ulAction;
- ECCategory *lpCategory = NULL;
- ECCategory *lpParent = NULL;
- bool fModified = false;
- bool fHidden = false;
- unsigned int ulDepth = 0;
- unsigned int ulSortLen = 0;
- unsigned char ulSortFlags = 0;
- struct propVal sProp;
-
- sProp.ulPropTag = PR_NULL;
-
- // Find information for the deleted leaf
- iterLeafs = m_mapLeafs.find(sObjKey);
- if (iterLeafs == m_mapLeafs.cend()) {
- er = KCERR_NOT_FOUND;
- goto exit;
- }
-
- lpCategory = iterLeafs->second.lpCategory;
- // Loop through this category and all its parents
- while(lpCategory) {
- ulDepth = lpCategory->m_ulDepth;
-
- lpParent = lpCategory->m_lpParent;
-
- // Build the row key for this category
- sCatRow.ulObjId = 0;
- sCatRow.ulOrderId = lpCategory->m_ulCategory;
-
- // Decrease the number of leafs in the category
- lpCategory->DecLeaf();
- if(iterLeafs->second.fUnread)
- lpCategory->DecUnread();
-
- if(ulDepth+1 < lpsSortOrderArray->__size && ISMINMAX(lpsSortOrderArray->__ptr[ulDepth+1].ulOrder)) {
- // Removing from a min/max category
- er = lpCategory->UpdateMinMaxRemove(sObjKey, ulDepth+1, lpsSortOrderArray->__ptr[ulDepth+1].ulOrder == EC_TABLE_SORT_CATEG_MAX, &fModified);
- if(er != erSuccess) {
- assert(false);
- goto exit;
- }
-
- if(fModified && lpCategory->GetCount() > 0) {
- // We have removed the min or max value for the category, so reorder is needed (unless category is empty, since it will be removed)
- ECObjectTableList lstObjects;
-
- // Get the rows that are affected
- er = lpKeyTable->GetRowsBySortPrefix(&sCatRow, &lstObjects);
- if (er != erSuccess)
- goto exit;
-
- // Update the keytable to reflect the new change
- for (auto &obj : lstObjects) {
- // Update the keytable with the new effective sort data for this column
-
- if(lpCategory->GetProp(NULL, lpsSortOrderArray->__ptr[ulDepth+1].ulPropTag, &sProp) != erSuccess) {
- sProp.ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(lpsSortOrderArray->__ptr[ulDepth+1].ulPropTag));
- sProp.Value.ul = KCERR_NOT_FOUND;
- }
- std::unique_ptr<unsigned char[]> lpSortKey;
- if(GetBinarySortKey(&sProp, &ulSortLen, &unique_tie(lpSortKey)) != erSuccess)
- lpSortKey = NULL;
- if(GetSortFlags(sProp.ulPropTag, &ulSortFlags) != erSuccess)
- ulSortFlags = 0;
-
- ulSortFlags |= lpsSortOrderArray->__ptr[ulDepth].ulOrder == EC_TABLE_SORT_DESCEND ? TABLEROW_FLAG_DESC : 0;
- er = lpKeyTable->UpdatePartialSortKey(&obj, ulDepth, lpSortKey.get(), ulSortLen, ulSortFlags, &sPrevRow, &fHidden, &ulAction);
- if (er != erSuccess)
- goto exit;
- if ((ulFlags & OBJECTTABLE_NOTIFY) && !fHidden)
- AddTableNotif(ulAction, obj, &sPrevRow);
- FreePropVal(&sProp, false);
- sProp.ulPropTag = PR_NULL;
- }
- }
- }
-
- if(lpCategory->GetCount() == 0) {
- // The category row is empty and must be removed
- ECTableRow *lpRow = NULL; // reference to the row in the keytable
-
- er = lpKeyTable->GetRow(&sCatRow, &lpRow);
- if(er != erSuccess) {
- assert(false);
- goto exit;
- }
-
- // Remove the category from the sorted categories map
- m_mapSortedCategories.erase(lpCategory->iSortedCategory);
-
- // Remove the category from the keytable
- lpKeyTable->UpdateRow(ECKeyTable::TABLE_ROW_DELETE, &sCatRow, 0, NULL, NULL, NULL, NULL, false, &ulAction);
- // Remove the category from the category map
- assert(m_mapCategories.find(sCatRow) != m_mapCategories.end());
- m_mapCategories.erase(sCatRow);
-
- // Free the category itself
- delete lpCategory;
-
- // Send the notification
- if (ulAction == ECKeyTable::TABLE_ROW_DELETE && (ulFlags & OBJECTTABLE_NOTIFY))
- AddTableNotif(ulAction, sCatRow, NULL);
- } else {
- if(ulFlags & OBJECTTABLE_NOTIFY) {
- // The category row has changed; the counts have updated, send a notification
-
- if(lpKeyTable->GetPreviousRow(&sCatRow, &sPrevRow) != erSuccess) {
- sPrevRow.ulOrderId = 0;
- sPrevRow.ulObjId = 0;
- }
-
- AddTableNotif(ECKeyTable::TABLE_ROW_MODIFY, sCatRow, &sPrevRow);
- }
- }
-
- lpCategory = lpParent;
- }
- // Remove the leaf from the leaf map
- m_mapLeafs.erase(iterLeafs);
-
- // All done
- assert(m_mapCategories.size() == m_mapSortedCategories.size());
- exit:
- FreePropVal(&sProp, false);
- sProp.ulPropTag = PR_NULL;
- return er;
- }
- /**
- * Get a table properties for a category
- *
- * @param soap SOAP object for memory allocation of data in lpPropVal
- * @param ulPropTag Requested property tag
- * @param sKey Key of the category to be retrieved
- * @param lpPropVal Output location of the property
- * @return result
- */
- ECRESULT ECGenericObjectTable::GetPropCategory(struct soap *soap, unsigned int ulPropTag, sObjectTableKey sKey, struct propVal *lpPropVal)
- {
- ECRESULT er = erSuccess;
- unsigned int i = 0;
-
- auto iterCategories = m_mapCategories.find(sKey);
- if (iterCategories == m_mapCategories.cend())
- return KCERR_NOT_FOUND;
-
- switch(ulPropTag) {
- case PR_INSTANCE_KEY:
- lpPropVal->__union = SOAP_UNION_propValData_bin;
- lpPropVal->Value.bin = s_alloc<struct xsd__base64Binary>(soap);
- lpPropVal->Value.bin->__size = sizeof(unsigned int) * 2;
- lpPropVal->Value.bin->__ptr = s_alloc<unsigned char>(soap, sizeof(unsigned int) * 2);
- *((unsigned int *)lpPropVal->Value.bin->__ptr) = sKey.ulObjId;
- *((unsigned int *)lpPropVal->Value.bin->__ptr+1) = sKey.ulOrderId;
- lpPropVal->ulPropTag = PR_INSTANCE_KEY;
- break;
- case PR_ROW_TYPE:
- lpPropVal->__union = SOAP_UNION_propValData_ul;
- lpPropVal->Value.ul = iterCategories->second->m_fExpanded ? TBL_EXPANDED_CATEGORY : TBL_COLLAPSED_CATEGORY;
- lpPropVal->ulPropTag = PR_ROW_TYPE;
- break;
- case PR_DEPTH:
- lpPropVal->__union = SOAP_UNION_propValData_ul;
- lpPropVal->Value.ul = iterCategories->second->m_ulDepth;
- lpPropVal->ulPropTag = PR_DEPTH;
- break;
- case PR_CONTENT_COUNT:
- lpPropVal->__union = SOAP_UNION_propValData_ul;
- lpPropVal->Value.ul = iterCategories->second->m_ulLeafs;
- lpPropVal->ulPropTag = PR_CONTENT_COUNT;
- break;
- case PR_CONTENT_UNREAD:
- lpPropVal->__union = SOAP_UNION_propValData_ul;
- lpPropVal->Value.ul = iterCategories->second->m_ulUnread;
- lpPropVal->ulPropTag = PR_CONTENT_UNREAD;
- break;
- default:
- for (i = 0; i < iterCategories->second->m_cProps; ++i)
- // If MVI is set, search for the property as non-MV property, as this is how we will have
- // received it when the category was added.
- if (NormalizePropTag(iterCategories->second->m_lpProps[i].ulPropTag) == NormalizePropTag(ulPropTag & ~MVI_FLAG))
- if(CopyPropVal(&iterCategories->second->m_lpProps[i], lpPropVal, soap) == erSuccess) {
- lpPropVal->ulPropTag = (ulPropTag & ~MVI_FLAG);
- break;
- }
-
- if(i == iterCategories->second->m_cProps)
- er = KCERR_NOT_FOUND;
- }
- return er;
- }
- unsigned int ECGenericObjectTable::GetCategories()
- {
- return this->m_ulCategories;
- }
- // Normally overridden by subclasses
- ECRESULT ECGenericObjectTable::CheckPermissions(unsigned int ulObjId)
- {
- return hrSuccess;
- }
- /**
- * Get object size
- *
- * @return Object size in bytes
- */
- size_t ECGenericObjectTable::GetObjectSize(void)
- {
- size_t ulSize = sizeof(*this);
- scoped_rlock biglock(m_hLock);
- ulSize += SortOrderArraySize(lpsSortOrderArray);
- ulSize += PropTagArraySize(lpsPropTagArray);
- ulSize += RestrictTableSize(lpsRestrict);
- ulSize += MEMORY_USAGE_LIST(m_listMVSortCols.size(), ECListInt);
- ulSize += MEMORY_USAGE_MAP(mapObjects.size(), ECObjectTableMap);
- ulSize += lpKeyTable->GetObjectSize();
- ulSize += MEMORY_USAGE_MAP(m_mapCategories.size(), ECCategoryMap);
- for (const auto &p : m_mapCategories)
- ulSize += p.second->GetObjectSize();
-
- ulSize += MEMORY_USAGE_MAP(m_mapLeafs.size(), ECLeafMap);
- return ulSize;
- }
- ECCategory::ECCategory(unsigned int ulCategory, struct propVal *lpProps,
- unsigned int cProps, unsigned int nProps, ECCategory *lpParent,
- unsigned int ulDepth, bool fExpanded, const ECLocale &locale) :
- m_cProps(nProps), m_lpParent(lpParent), m_ulDepth(ulDepth),
- m_ulCategory(ulCategory), m_fExpanded(fExpanded), m_locale(locale)
- {
- unsigned int i;
- m_lpProps = s_alloc<propVal>(nullptr, nProps);
- for (i = 0; i < cProps; ++i)
- CopyPropVal(&lpProps[i], &m_lpProps[i]);
- for (; i < nProps; ++i) {
- m_lpProps[i].ulPropTag = PR_NULL;
- m_lpProps[i].Value.ul = 0;
- m_lpProps[i].__union = SOAP_UNION_propValData_ul;
- }
- }
- ECCategory::~ECCategory()
- {
- unsigned int i;
-
- for (i = 0; i < m_cProps; ++i)
- FreePropVal(&m_lpProps[i], false);
- for (const auto &p : m_mapMinMax)
- FreePropVal(p.second, true);
- s_free(nullptr, m_lpProps);
- }
- void ECCategory::IncLeaf()
- {
- ++m_ulLeafs;
- }
- void ECCategory::DecLeaf()
- {
- --m_ulLeafs;
- }
- ECRESULT ECCategory::GetProp(struct soap *soap, unsigned int ulPropTag, struct propVal* lpPropVal)
- {
- ECRESULT er = erSuccess;
- unsigned int i;
-
- for (i = 0; i < m_cProps; ++i)
- if (m_lpProps[i].ulPropTag == ulPropTag) {
- er = CopyPropVal(&m_lpProps[i], lpPropVal, soap);
- break;
- }
-
- if(i == m_cProps)
- er = KCERR_NOT_FOUND;
-
- return er;
- }
- ECRESULT ECCategory::SetProp(unsigned int i, struct propVal* lpPropVal)
- {
- ECRESULT er = erSuccess;
- assert(i < m_cProps);
- FreePropVal(&m_lpProps[i], false);
-
- er = CopyPropVal(lpPropVal, &m_lpProps[i], NULL);
-
- return er;
- }
- /**
- * Updates a min/max value:
- *
- * Checks if the value passed is more or less than the current min/max value. Currently we treat
- * an error value as a 'worse' value than ANY new value. This means that min(ERROR, 1) == 1, and max(ERROR, 1) == 1.
- *
- * The new value is also tracked in a list of min/max value so that UpdateMinMaxRemove() (see below) can update
- * the min/max value when a row is removed.
- *
- * @param sKey Key of the new row
- * @param i Column id of the min/max value
- * @param lpNewValue New value for the column (may also be PT_ERROR)
- * @param bool fMax TRUE if the column is a EC_TABLE_SORT_CATEG_MAX, FALSE if the column is EC_TABLE_SORT_CATEG_MIN
- * @param lpfModified Returns TRUE if the new value updated the min/max value, false otherwise
- * @return result
- */
- ECRESULT ECCategory::UpdateMinMax(const sObjectTableKey &sKey, unsigned int i, struct propVal *lpNewValue, bool fMax, bool *lpfModified)
- {
- ECRESULT er;
- bool fModified = false;
- int result = 0;
- struct propVal *lpOldValue;
- struct propVal *lpNew;
-
- lpOldValue = &m_lpProps[i];
-
- if(PROP_TYPE(lpOldValue->ulPropTag) != PT_ERROR && PROP_TYPE(lpOldValue->ulPropTag) != PT_NULL) {
- // Compare old with new
- er = CompareProp(lpOldValue, lpNewValue, m_locale, &result);
- if (er != erSuccess)
- return er;
- }
-
- // Copy the value so we can track it for later (in UpdateMinMaxRemove) if we didn't have it yet
- er = CopyPropVal(lpNewValue, &lpNew);
- if(er != erSuccess)
- return er;
-
- auto iterMinMax = m_mapMinMax.find(sKey);
- if (iterMinMax == m_mapMinMax.cend()) {
- m_mapMinMax.insert(std::make_pair(sKey, lpNew));
- } else {
- FreePropVal(iterMinMax->second, true); // NOTE this may free lpNewValue, so you can't use that anymore now
- iterMinMax->second = lpNew;
- }
-
- if(PROP_TYPE(lpOldValue->ulPropTag) == PT_ERROR || PROP_TYPE(lpOldValue->ulPropTag) == PT_NULL || (!fMax && result > 0) || (fMax && result < 0)) {
- // Either there was no old value, or the new value is larger or smaller than the old one
- er = SetProp(i, lpNew);
- if(er != erSuccess)
- return er;
- m_sCurMinMax = sKey;
-
- fModified = true;
- }
-
- if(lpfModified)
- *lpfModified = fModified;
- return erSuccess;
- }
- /**
- * Update the min/max value to a row removal
- *
- * This function removes the value from the internal list of values, and checks if the new min/max value
- * differs from the last. It updates the category properties accordingly if needed.
- *
- * @param sKey Key of row that was removed
- * @param i Column id of min/max value
- * @param fMax TRUE if the column is a EC_TABLE_SORT_CATEG_MAX, FALSE if the column is EC_TABLE_SORT_CATEG_MIN
- * @param lpfModified TRUE if a new min/max value came into play due to the deletion
- * @return result
- */
- ECRESULT ECCategory::UpdateMinMaxRemove(const sObjectTableKey &sKey, unsigned int i, bool fMax, bool *lpfModified)
- {
- bool fModified = false;
- auto iterMinMax = m_mapMinMax.find(sKey);
- if (iterMinMax == m_mapMinMax.cend())
- return KCERR_NOT_FOUND;
-
- FreePropVal(iterMinMax->second, true);
- m_mapMinMax.erase(iterMinMax);
-
- if(m_sCurMinMax == sKey) {
- fModified = true;
-
- // Reset old value
- FreePropVal(&this->m_lpProps[i], false);
- this->m_lpProps[i].ulPropTag = PR_NULL;
-
- // The min/max value until now was updated. Find the next min/max value.
- for (iterMinMax = m_mapMinMax.begin();
- iterMinMax != m_mapMinMax.end(); ++iterMinMax)
- // Re-feed the values we had until now
- UpdateMinMax(iterMinMax->first, i, iterMinMax->second, fMax, NULL); // FIXME this
- }
-
- if(lpfModified)
- *lpfModified = fModified;
- return erSuccess;
- }
- void ECCategory::DecUnread() {
- --m_ulUnread;
- }
- void ECCategory::IncUnread() {
- ++m_ulUnread;
- }
- /**
- * Get object size
- *
- * @return Object size in bytes
- */
- size_t ECCategory::GetObjectSize(void) const
- {
- size_t ulSize = 0;
-
- if (m_cProps > 0) {
- ulSize += sizeof(struct propVal) * m_cProps;
- for (unsigned int i = 0; i < m_cProps; ++i)
- ulSize += PropSize(&m_lpProps[i]);
- }
- if (m_lpParent)
- ulSize += m_lpParent->GetObjectSize();
- return sizeof(*this) + ulSize;
- }
- /**
- * Get PR_DEPTH for an object in the table
- *
- * @param lpThis Pointer to generic object table instance
- * @param soap SOAP object for memory allocation
- * @param lpSession Session assiociated with the table
- * @param ulObjId Object ID of the object to get PR_DEPTH for
- * @param lpProp PropVal to write to
- * @return result
- */
- ECRESULT ECGenericObjectTable::GetComputedDepth(struct soap *soap, ECSession *lpSession, unsigned int ulObjId, struct propVal *lpProp)
- {
- lpProp->__union = SOAP_UNION_propValData_ul;
- lpProp->ulPropTag = PR_DEPTH;
- if (m_ulObjType == MAPI_MESSAGE)
- // For contents tables, depth is equal to number of categories
- lpProp->Value.ul = GetCategories();
- else
- // For hierarchy tables, depth is 1 (see ECConvenientDepthTable.cpp for exception)
- lpProp->Value.ul = 1;
-
- return erSuccess;
- }
- } /* namespace */
|