123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842 |
- /*
- * 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 <utility>
- #include <kopano/ECChannel.h>
- #include <kopano/MAPIErrors.h>
- #include <kopano/memory.hpp>
- #include <kopano/tie.hpp>
- #include "ECDatabaseUtils.h"
- #include "ECSessionManager.h"
- #include "ECPluginFactory.h"
- #include "ECDBDef.h"
- #include "ECDatabaseUpdate.h"
- #include "ECLicenseClient.h"
- #include <kopano/ECGuid.h>
- #include "ECLockManager.h"
- #include "soapH.h"
- #include <mutex>
- #include <unordered_map>
- #include <mapidefs.h>
- #include <mapitags.h>
- #include <sys/times.h>
- #include <ctime>
- #include <algorithm>
- #include <sstream>
- #include <set>
- #include <deque>
- #include <algorithm>
- #include <cstdio>
- #include <kopano/ECTags.h>
- #include <kopano/stringutil.h>
- #include "SOAPUtils.h"
- #include <kopano/kcodes.h>
- #include <kopano/Trace.h>
- #include "KCmd.nsmap"
- #include "ECFifoBuffer.h"
- #include "ECSerializer.h"
- #include "StreamUtil.h"
- #include <kopano/CommonUtil.h>
- #include "StorageUtil.h"
- #include "ics.h"
- #include "kcore.hpp"
- #include "pcutil.hpp"
- #include "ECAttachmentStorage.h"
- #include "ECGenProps.h"
- #include "ECUserManagement.h"
- #include "ECSecurity.h"
- #include "ECICS.h"
- #include "ECMultiStoreTable.h"
- #include "ECStatsCollector.h"
- #include "ECStringCompat.h"
- #include "ECTPropsPurge.h"
- #include "versions.h"
- #include "ECTestProtocol.h"
- #include <kopano/ECDefs.h>
- #include <kopano/EMSAbTag.h>
- #include <edkmdb.h>
- #include <kopano/ecversion.h>
- #include <kopano/mapiext.h>
- #include "../server/ECSoapServerConnection.h"
- #include "cmdutil.hpp"
- #include <kopano/ECThreadPool.h>
- #include "cmd.hpp"
- #include "logontime.hpp"
- #if defined(HAVE_GPERFTOOLS_MALLOC_EXTENSION_H)
- # include <gperftools/malloc_extension_c.h>
- # define HAVE_TCMALLOC 1
- #elif defined(HAVE_GOOGLE_MALLOC_EXTENSION_H)
- # include <google/malloc_extension_c.h>
- # define HAVE_TCMALLOC 1
- #endif
- #define STRIN_FIX(s) (bSupportUnicode ? (s) : ECStringCompat::WTF1252_to_UTF8(soap, (s)))
- #define STROUT_FIX(s) (bSupportUnicode ? (s) : ECStringCompat::UTF8_to_WTF1252(soap, (s)))
- #define STROUT_FIX_CPY(s) (bSupportUnicode ? s_strcpy(soap, (s)) : ECStringCompat::UTF8_to_WTF1252(soap, (s)))
- #define LOG_SOAP_DEBUG(_msg, ...) \
- ec_log(EC_LOGLEVEL_DEBUG | EC_LOGLEVEL_SOAP, "soap: " _msg, ##__VA_ARGS__)
- using namespace KCHL;
- namespace KC {
- class ECFifoSerializer _kc_final : public ECSerializer {
- public:
- enum eMode { serialize, deserialize };
- ECFifoSerializer(ECFifoBuffer *lpBuffer, eMode mode);
- virtual ~ECFifoSerializer(void);
- virtual ECRESULT SetBuffer(void *) _kc_override;
- virtual ECRESULT Write(const void *ptr, size_t size, size_t nmemb) _kc_override;
- virtual ECRESULT Read(void *ptr, size_t size, size_t nmemb) _kc_override;
- virtual ECRESULT Skip(size_t size, size_t nmemb) _kc_override;
- virtual ECRESULT Flush(void) _kc_override;
- virtual ECRESULT Stat(ULONG *have_read, ULONG *have_written) _kc_override;
- private:
- ECFifoBuffer *m_lpBuffer;
- eMode m_mode;
- ULONG m_ulRead = 0, m_ulWritten = 0;
- };
- extern ECSessionManager* g_lpSessionManager;
- extern ECStatsCollector* g_lpStatsCollector;
- // Hold the status of the softdelete purge system
- static bool g_bPurgeSoftDeleteStatus = FALSE;
- static ECRESULT CreateEntryId(GUID guidStore, unsigned int ulObjType,
- entryId **lppEntryId)
- {
- entryId* lpEntryId = NULL;
- EID eid;
- if (lppEntryId == NULL)
- return KCERR_INVALID_PARAMETER;
- if (CoCreateGuid(&eid.uniqueId) != hrSuccess)
- return KCERR_CALL_FAILED;
- eid.guid = guidStore;
- eid.usType = ulObjType;
- lpEntryId = s_alloc<entryId>(nullptr);
- lpEntryId->__size = sizeof(EID);
- lpEntryId->__ptr = s_alloc<unsigned char>(nullptr, lpEntryId->__size);
- memcpy(lpEntryId->__ptr, &eid, lpEntryId->__size);
- *lppEntryId = lpEntryId;
- return erSuccess;
- }
- /**
- * Get the local user id based on the entryid or the user id for old clients.
- *
- * When an entryid is provided, the extern id is extracted and the local user id
- * is resolved based on that. If no entryid is provided the provided legacy user id
- * is used as local user is and the extern id is resolved based on that. Old clients
- * that are not multi server aware provide the legacy user id in stead of the entryid.
- *
- * @param[in] sUserId The entryid of the user for which to obtain the local id
- * @param[in] ulLegacyUserId The legacy user id, which will be used as the entryid when.
- * no entryid is provided (old clients).
- * @param[out] lpulUserId The local user id.
- * @param[out] lpsExternId The extern id of the user. This can be NULL if the extern id
- * is not required by the caller.
- *
- * @retval KCERR_INVALID_PARAMATER One or more parameters are invalid.
- * @retval KCERR_NOT_FOUND The local is is not found.
- */
- static ECRESULT GetLocalId(entryId sUserId, unsigned int ulLegacyUserId,
- unsigned int *lpulUserId, objectid_t *lpsExternId)
- {
- ECRESULT er = erSuccess;
- unsigned int ulUserId = 0;
- objectid_t sExternId;
- objectdetails_t sDetails;
- if (lpulUserId == NULL)
- return KCERR_INVALID_PARAMETER;
- // If no entryid is present, use the 'current' user.
- if (ulLegacyUserId == 0 && sUserId.__size == 0) {
- // When lpsExternId is requested, the 'current' user will not be
- // requested in this way. However, to make sure a caller does expect a result in the future
- // we'll return an error in that case.
- if (lpsExternId != NULL)
- er = KCERR_INVALID_PARAMETER;
- else
- *lpulUserId = 0;
- // TODO: return value in lpulUserId ?
- return er;
- }
- if (sUserId.__ptr) {
- // Extract the information from the entryid.
- er = ABEntryIDToID(&sUserId, &ulUserId, &sExternId, NULL);
- if (er != erSuccess)
- return er;
- // If an extern id is present, we should get an object based on that.
- if (!sExternId.id.empty())
- er = g_lpSessionManager->GetCacheManager()->GetUserObject(sExternId, &ulUserId, NULL, NULL);
- } else {
- // use user id from 6.20 and older clients
- ulUserId = ulLegacyUserId;
- if (lpsExternId)
- er = g_lpSessionManager->GetCacheManager()->GetUserObject(ulLegacyUserId, &sExternId, NULL, NULL);
- }
- if (er != erSuccess)
- return er;
- *lpulUserId = ulUserId;
- if (lpsExternId)
- *lpsExternId = std::move(sExternId);
- return erSuccess;
- }
- /**
- * Check if a user has a store of a particular type on the local server.
- *
- * On a single server configuration this function will return true for
- * all ECSTORE_TYPE_PRIVATE and ECSTORE_TYPE_PUBLIC requests and false otherwise.
- *
- * In single tennant mode, requests for ECSTORE_TYPE_PUBLIC will always return true,
- * regardless of the server on which the public should exist. This is actually wrong
- * but is the same behaviour as before.
- *
- * @param[in] lpecSession The ECSession object for the current session.
- * @param[in] ulUserId The user id of the user for which to check if a
- * store is available.
- * @param[in] ulStoreType The store type to check for.
- * @param[out] lpbHasLocalStore The boolean that will contain the result on success.
- *
- * @retval KCERR_INVALID_PARAMETER One or more parameters are invalid.
- * @retval KCERR_NOT_FOUND The user specified by ulUserId was not found.
- */
- static ECRESULT CheckUserStore(ECSession *lpecSession, unsigned ulUserId,
- unsigned ulStoreType, bool *lpbHasLocalStore)
- {
- ECRESULT er;
- objectdetails_t sDetails;
- bool bPrivateOrPublic;
- if (lpecSession == NULL || lpbHasLocalStore == NULL || !ECSTORE_TYPE_ISVALID(ulStoreType))
- return KCERR_INVALID_PARAMETER;
- bPrivateOrPublic = (ulStoreType == ECSTORE_TYPE_PRIVATE || ulStoreType == ECSTORE_TYPE_PUBLIC);
- if (g_lpSessionManager->IsDistributedSupported()) {
- er = lpecSession->GetUserManagement()->GetObjectDetails(ulUserId, &sDetails);
- if (er != erSuccess)
- return er;
- if (bPrivateOrPublic) {
- // @todo: Check if there's a define or constant for everyone.
- if (ulUserId == 2) // Everyone, public in single tennant
- *lpbHasLocalStore = true;
- else
- *lpbHasLocalStore = (strcasecmp(sDetails.GetPropString(OB_PROP_S_SERVERNAME).c_str(), g_lpSessionManager->GetConfig()->GetSetting("server_name")) == 0);
- } else // Archive store
- *lpbHasLocalStore = sDetails.PropListStringContains((property_key_t)PR_EC_ARCHIVE_SERVERS_A, g_lpSessionManager->GetConfig()->GetSetting("server_name"), true);
- } else // Single tennant
- *lpbHasLocalStore = bPrivateOrPublic;
- return erSuccess;
- }
- static ECRESULT GetABEntryID(unsigned int ulUserId, soap *lpSoap,
- entryId *lpUserId)
- {
- ECRESULT er;
- entryId sUserId = {0};
- objectid_t sExternId;
- if (lpSoap == NULL)
- return KCERR_INVALID_PARAMETER;
- if (ulUserId == KOPANO_UID_SYSTEM) {
- sExternId.objclass = ACTIVE_USER;
- } else if (ulUserId == KOPANO_UID_EVERYONE) {
- sExternId.objclass = DISTLIST_SECURITY;
- } else {
- er = g_lpSessionManager->GetCacheManager()->GetUserObject(ulUserId, &sExternId, NULL, NULL);
- if (er != erSuccess)
- return er;
- }
- er = ABIDToEntryID(lpSoap, ulUserId, sExternId, &sUserId);
- if (er != erSuccess)
- return er;
- *lpUserId = std::move(sUserId); // pointer (__ptr) is copied, not data
- return erSuccess;
- }
- static ECRESULT PeerIsServer(struct soap *soap,
- const std::string &strServerName, const std::string &strHttpPath,
- const std::string &strSslPath, bool *lpbResult)
- {
- bool bResult = false;
- if (soap == NULL || lpbResult == NULL)
- return KCERR_INVALID_PARAMETER;
- // First check if we are connecting through Unix socket/named pipe and if the request URL matches this server
- if (SOAP_CONNECTION_TYPE_NAMED_PIPE(soap) &&
- strcasecmp(strServerName.c_str(), g_lpSessionManager->GetConfig()->GetSetting("server_name")) == 0)
- bResult = true;
- else
- {
- const std::string *lpstrPath = &strHttpPath;
- if (lpstrPath->empty())
- lpstrPath = &strSslPath;
- if (!lpstrPath->empty())
- {
- std::string strHost;
- std::string::size_type ulHostStart = 0;
- std::string::size_type ulHostEnd = 0;
- struct addrinfo sHint = {0};
- struct addrinfo *lpsAddrInfo = NULL;
- struct addrinfo *lpsAddrIter = NULL;
-
- ulHostStart = lpstrPath->find("://");
- if (ulHostStart == std::string::npos)
- return KCERR_INVALID_PARAMETER;
- ulHostStart += 3; // Skip the '://'
- ulHostEnd = lpstrPath->find(':', ulHostStart);
- if (ulHostEnd == std::string::npos)
- return KCERR_INVALID_PARAMETER;
- strHost = lpstrPath->substr(ulHostStart, ulHostEnd - ulHostStart);
- sHint.ai_family = AF_UNSPEC;
- sHint.ai_socktype = SOCK_STREAM;
- if (getaddrinfo(strHost.c_str(), NULL, &sHint, &lpsAddrInfo) != 0)
- return KCERR_NOT_FOUND;
- lpsAddrIter = lpsAddrInfo;
- while (lpsAddrIter && bResult == false) {
- if (soap->peerlen >= sizeof(sockaddr) && lpsAddrIter->ai_family == ((sockaddr*)&soap->peer)->sa_family) {
- switch (lpsAddrIter->ai_family) {
- case AF_INET:
- {
- auto lpsLeft = reinterpret_cast<struct sockaddr_in *>(lpsAddrIter->ai_addr);
- auto lpsRight = reinterpret_cast<struct sockaddr_in *>(&soap->peer);
- bResult = (memcmp(&lpsLeft->sin_addr, &lpsRight->sin_addr, sizeof(lpsLeft->sin_addr)) == 0);
- break;
- }
- case AF_INET6:
- {
- auto lpsLeft = reinterpret_cast<struct sockaddr_in6 *>(lpsAddrIter->ai_addr);
- auto lpsRight = reinterpret_cast<struct sockaddr_in6 *>(&soap->peer);
- bResult = (memcmp(&lpsLeft->sin6_addr, &lpsRight->sin6_addr, sizeof(lpsLeft->sin6_addr)) == 0);
- break;
- }
- default:
- break;
- }
- }
- lpsAddrIter = lpsAddrIter->ai_next;
- }
- freeaddrinfo(lpsAddrInfo);
- }
- }
- *lpbResult = bResult;
- return erSuccess;
- }
- ECFifoSerializer::ECFifoSerializer(ECFifoBuffer *lpBuffer, eMode mode) :
- m_mode(mode)
- {
- SetBuffer(lpBuffer);
- }
- ECFifoSerializer::~ECFifoSerializer(void)
- {
- if (m_lpBuffer == nullptr)
- return;
- ECFifoBuffer::close_flags flags = (m_mode == serialize ? ECFifoBuffer::cfWrite : ECFifoBuffer::cfRead);
- m_lpBuffer->Close(flags);
- }
- ECRESULT ECFifoSerializer::SetBuffer(void *lpBuffer)
- {
- m_lpBuffer = static_cast<ECFifoBuffer *>(lpBuffer);
- return erSuccess;
- }
- ECRESULT ECFifoSerializer::Write(const void *ptr, size_t size, size_t nmemb)
- {
- ECRESULT er = erSuccess;
- union {
- char c[8];
- short s;
- int i;
- long long ll;
- } tmp;
- if (m_mode != serialize)
- return KCERR_NO_SUPPORT;
- if (ptr == nullptr)
- return KCERR_INVALID_PARAMETER;
- switch (size) {
- case 1:
- er = m_lpBuffer->Write(ptr, nmemb, STR_DEF_TIMEOUT, NULL);
- break;
- case 2:
- for (size_t x = 0; x < nmemb && er == erSuccess; ++x) {
- tmp.s = htons(static_cast<const short *>(ptr)[x]);
- er = m_lpBuffer->Write(&tmp, size, STR_DEF_TIMEOUT, nullptr);
- }
- break;
- case 4:
- for (size_t x = 0; x < nmemb && er == erSuccess; ++x) {
- tmp.i = htonl(static_cast<const int *>(ptr)[x]);
- er = m_lpBuffer->Write(&tmp, size, STR_DEF_TIMEOUT, nullptr);
- }
- break;
- case 8:
- for (size_t x = 0; x < nmemb && er == erSuccess; ++x) {
- tmp.ll = htonll(static_cast<const long long *>(ptr)[x]);
- er = m_lpBuffer->Write(&tmp, size, STR_DEF_TIMEOUT, nullptr);
- }
- break;
- default:
- er = KCERR_INVALID_PARAMETER;
- break;
- }
- m_ulWritten += size * nmemb;
- return er;
- }
- ECRESULT ECFifoSerializer::Read(void *ptr, size_t size, size_t nmemb)
- {
- ECRESULT er;
- ECFifoBuffer::size_type cbRead = 0;
- if (m_mode != deserialize)
- return KCERR_NO_SUPPORT;
- if (ptr == nullptr)
- return KCERR_INVALID_PARAMETER;
- er = m_lpBuffer->Read(ptr, size * nmemb, STR_DEF_TIMEOUT, &cbRead);
- if (er != erSuccess)
- return er;
- m_ulRead += cbRead;
- if (cbRead != size * nmemb)
- return KCERR_CALL_FAILED;
- switch (size) {
- case 1: break;
- case 2:
- for (size_t x = 0; x < nmemb; ++x)
- static_cast<short *>(ptr)[x] = ntohs(static_cast<short *>(ptr)[x]);
- break;
- case 4:
- for (size_t x = 0; x < nmemb; ++x)
- static_cast<int *>(ptr)[x] = ntohl(static_cast<int *>(ptr)[x]);
- break;
- case 8:
- for (size_t x = 0; x < nmemb; ++x)
- static_cast<long long *>(ptr)[x] = ntohll(static_cast<long long *>(ptr)[x]);
- break;
- default:
- er = KCERR_INVALID_PARAMETER;
- break;
- }
- return er;
- }
- ECRESULT ECFifoSerializer::Skip(size_t size, size_t nmemb)
- {
- std::unique_ptr<char[]> buf(new(std::nothrow) char[size*nmemb]);
- if (buf == nullptr)
- return KCERR_NOT_ENOUGH_MEMORY;
- return Read(buf.get(), size, nmemb);
- }
- ECRESULT ECFifoSerializer::Flush()
- {
- ECRESULT er;
- size_t cbRead = 0;
- char buf[16384];
- while (true) {
- er = m_lpBuffer->Read(buf, sizeof(buf), STR_DEF_TIMEOUT, &cbRead);
- if (er != erSuccess)
- return er;
- m_ulRead += cbRead;
- if (cbRead < sizeof(buf))
- break;
- }
- return er;
- }
- ECRESULT ECFifoSerializer::Stat(ULONG *lpcbRead, ULONG *lpcbWrite)
- {
- if (lpcbRead != nullptr)
- *lpcbRead = m_ulRead;
- if (lpcbWrite != nullptr)
- *lpcbWrite = m_ulWritten;
- return erSuccess;
- }
- /**
- * Get the best server path for a server
- *
- * This function will return the 'best' server path to redirect the client to. This is
- * done by examining the existing incoming connection, and choosing an appropriate access
- * method to the given server. Rules are as follows:
- *
- * - If bProxy is TRUE and the destination server has a proxy address, return the proxy address
- * - If bProxy is FALSE:
- * - If existing connection is HTTP, return first available of: HTTP, HTTPS
- * - If existing connection is HTTPS, return HTTPS
- * - If existing connection is FILE, return first available of: HTTPS, HTTP
- *
- * @param[in] soap SOAP structure for incoming request
- * @param[in] lpecSession Session for the request
- * @param[in] strServerName Server name to get path for
- * @param[in] bProxy TRUE if we are requesting the proxy address for a server
- * @param[out] lpstrServerPath Output path of server (URL)
- * @return result
- */
- ECRESULT GetBestServerPath(struct soap *soap, ECSession *lpecSession, const std::string &strServerName, std::string *lpstrServerPath)
- {
- ECRESULT er;
- std::string strServerPath;
- bool bConnectPipe = false;
- serverdetails_t sServerDetails;
- std::string strFilePath;
- std::string strHttpPath;
- std::string strSslPath;
- std::string strProxyPath;
- const char *szProxyHeader = lpecSession->GetSessionManager()->GetConfig()->GetSetting("proxy_header");
- if (soap == NULL || soap->user == NULL || lpstrServerPath == NULL)
- return KCERR_INVALID_PARAMETER;
-
- auto lpInfo = soap_info(soap);
- er = lpecSession->GetUserManagement()->GetServerDetails(strServerName, &sServerDetails);
- if (er != erSuccess)
- return er;
- strProxyPath = sServerDetails.GetProxyPath();
- strFilePath = sServerDetails.GetFilePath();
- strHttpPath = sServerDetails.GetHttpPath();
- strSslPath = sServerDetails.GetSslPath();
- // Always redirect if proxy_header is "*"
- if (!strcmp(szProxyHeader, "*") || lpInfo->bProxy) {
- if(!strProxyPath.empty()) {
- *lpstrServerPath = std::move(strProxyPath);
- return erSuccess;
- } else {
- ec_log_warn("Proxy path not set for server \"%s\"! falling back to direct address.", strServerName.c_str());
- }
- }
- if (!strFilePath.empty())
- {
- er = PeerIsServer(soap, strServerName, strHttpPath, strSslPath, &bConnectPipe);
- if (er != erSuccess)
- return er;
- } else {
- // TODO: check if same server, and set strFilePath 'cause it's known
- bConnectPipe = false;
- }
- if (bConnectPipe)
- strServerPath = strFilePath;
- else
- switch (SOAP_CONNECTION_TYPE(soap))
- {
- case CONNECTION_TYPE_TCP:
- if (!strHttpPath.empty())
- strServerPath = strHttpPath;
- else if (!strSslPath.empty())
- strServerPath = strSslPath;
- break;
- case CONNECTION_TYPE_SSL:
- if (!strSslPath.empty())
- strServerPath = strSslPath;
- break;
- case CONNECTION_TYPE_NAMED_PIPE:
- case CONNECTION_TYPE_NAMED_PIPE_PRIORITY:
- if (!strSslPath.empty())
- strServerPath = strSslPath;
- else if (!strHttpPath.empty())
- strServerPath = strHttpPath;
- break;
- }
- if (strServerPath.empty())
- return KCERR_NOT_FOUND;
- *lpstrServerPath = std::move(strServerPath);
- return erSuccess;
- }
- } /* namespace */
- // exception: This function does internal Begin + Commit/Rollback
- static ECRESULT MoveObjects(ECSession *lpSession, ECDatabase *lpDatabase, ECListInt* lplObjectIds, unsigned int ulDestFolderId, unsigned int ulSyncId);
- // these functions don't do Begin + Commit/Rollback
- static ECRESULT WriteProps(struct soap *soap, ECSession *lpecSession, ECDatabase *lpDatabase, ECAttachmentStorage *lpAttachmentStorage, struct saveObject *lpsSaveObj, unsigned int ulObjId, bool fNewItem, unsigned int ulSyncId, struct saveObject *lpsReturnObj, bool *lpfHaveChangeKey, FILETIME *ftCreated, FILETIME *ftModified);
- static ECRESULT DoNotifySubscribe(ECSession *lpecSession, unsigned long long ulSessionId, struct notifySubscribe *notifySubscribe);
- /**
- * logon: log on and create a session with provided credentials
- */
- int ns__logon(struct soap *soap, char *user, char *pass, char *impersonate, char *clientVersion, unsigned int clientCaps, unsigned int logonFlags, struct xsd__base64Binary sLicenseRequest, ULONG64 ullSessionGroup, char *szClientApp, char *szClientAppVersion, char *szClientAppMisc, struct logonResponse *lpsResponse)
- {
- ECRESULT er = erSuccess;
- ECSession *lpecSession = NULL;
- ECSESSIONID sessionID = 0;
- GUID sServerGuid = {0};
- struct timespec startTimes = {0}, endTimes = {0};
- double dblStart = GetTimeOfDay();
- clock_gettime(CLOCK_THREAD_CPUTIME_ID, &startTimes);
- LOG_SOAP_DEBUG("%020llu: S logon", static_cast<unsigned long long>(sessionID));
- if ((clientCaps & KOPANO_CAP_UNICODE) == 0) {
- user = ECStringCompat::WTF1252_to_UTF8(soap, user);
- pass = ECStringCompat::WTF1252_to_UTF8(soap, pass);
- clientVersion = ECStringCompat::WTF1252_to_UTF8(soap, clientVersion);
- szClientApp = ECStringCompat::WTF1252_to_UTF8(soap, szClientApp);
- }
- lpsResponse->lpszVersion = const_cast<char *>("0," PROJECT_VERSION_SERVER_STR);
- lpsResponse->ulCapabilities = KOPANO_LATEST_CAPABILITIES;
- /*
- * Client desires compression, so turn it on, but only if remote.
- * Otherwise, clear the flag from clientCaps, because
- * Create(Auth)Session remembers them, re-evaluates CAP_COMPRESSION,
- * and would otherwise turn on compression again.
- */
- if (zcp_peerfd_is_local(soap->socket) <= 0 && (clientCaps & KOPANO_CAP_COMPRESSION)) {
- lpsResponse->ulCapabilities |= KOPANO_CAP_COMPRESSION;
- // (ECSessionManager::ValidateSession() will do this for all other functions)
- soap_set_imode(soap, SOAP_ENC_ZLIB); // also autodetected
- soap_set_omode(soap, SOAP_ENC_ZLIB | SOAP_IO_CHUNK);
- } else {
- clientCaps &= ~KOPANO_CAP_COMPRESSION;
- }
- // check username and password
- er = g_lpSessionManager->CreateSession(soap, user, pass, impersonate, clientVersion, szClientApp, szClientAppVersion, szClientAppMisc, clientCaps, ullSessionGroup, &sessionID, &lpecSession, true, (logonFlags & KOPANO_LOGON_NO_UID_AUTH) == 0);
- if(er != erSuccess){
- er = KCERR_LOGON_FAILED;
- goto exit;
- }
- // We allow Zarafa >=6 clients to connect to a Kopano server. However, anything below that will be
- // denied. We can't say what future clients may or may not be capable of. So we'll leave that to the
- // clients.
- if (KOPANO_COMPARE_VERSION_TO_GENERAL(lpecSession->ClientVersion(), MAKE_KOPANO_GENERAL(6)) < 0) {
- ec_log_warn("Rejected logon attempt from a %s version client.", clientVersion ? clientVersion : "<unknown>");
- er = KCERR_INVALID_VERSION;
- goto exit;
- }
- lpsResponse->ulSessionId = sessionID;
- if (clientCaps & KOPANO_CAP_MULTI_SERVER)
- lpsResponse->ulCapabilities |= KOPANO_CAP_MULTI_SERVER;
-
- if (clientCaps & KOPANO_CAP_ENHANCED_ICS && parseBool(g_lpSessionManager->GetConfig()->GetSetting("enable_enhanced_ics"))) {
- lpsResponse->ulCapabilities |= KOPANO_CAP_ENHANCED_ICS;
- soap_set_omode(soap, SOAP_ENC_MTOM | SOAP_IO_CHUNK);
- soap_set_imode(soap, SOAP_ENC_MTOM);
- soap_post_check_mime_attachments(soap);
- }
- if (clientCaps & KOPANO_CAP_UNICODE)
- lpsResponse->ulCapabilities |= KOPANO_CAP_UNICODE;
- if (clientCaps & KOPANO_CAP_MSGLOCK)
- lpsResponse->ulCapabilities |= KOPANO_CAP_MSGLOCK;
- er = g_lpSessionManager->GetServerGUID(&sServerGuid);
- if (er != erSuccess)
- goto exit;
- lpsResponse->sServerGuid.__ptr = (unsigned char*)s_memcpy(soap, (char*)&sServerGuid, sizeof(sServerGuid));
- lpsResponse->sServerGuid.__size = sizeof(sServerGuid);
- // Only save logon if credentials were supplied by the user; otherwise the logon is probably automated
- if(lpecSession->GetAuthMethod() == ECSession::METHOD_USERPASSWORD || lpecSession->GetAuthMethod() == ECSession::METHOD_SSO)
- record_logon_time(lpecSession, true);
-
- exit:
- if (lpecSession)
- lpecSession->Unlock();
- lpsResponse->er = er;
- clock_gettime(CLOCK_THREAD_CPUTIME_ID, &endTimes);
- LOG_SOAP_DEBUG("%020llu: E logon 0x%08x %f %f",
- static_cast<unsigned long long>(sessionID), er,
- timespec2dbl(endTimes) - timespec2dbl(startTimes),
- GetTimeOfDay() - dblStart);
- return SOAP_OK;
- }
- /**
- * logon: log on and create a session with provided credentials
- */
- int ns__ssoLogon(struct soap *soap, ULONG64 ulSessionId, char *szUsername, char *szImpersonateUser, struct xsd__base64Binary *lpInput, char *szClientVersion, unsigned int clientCaps, struct xsd__base64Binary sLicenseRequest, ULONG64 ullSessionGroup, char *szClientApp, char *szClientAppVersion, char *szClientAppMisc, struct ssoLogonResponse *lpsResponse)
- {
- ECRESULT er = KCERR_LOGON_FAILED;
- ECAuthSession *lpecAuthSession = NULL;
- ECSession *lpecSession = NULL;
- ECSESSIONID newSessionID = 0;
- GUID sServerGuid = {0};
- xsd__base64Binary *lpOutput = NULL;
- const char *lpszEnabled = NULL;
- struct timespec startTimes = {0}, endTimes = {0};
- double dblStart = GetTimeOfDay();
- clock_gettime(CLOCK_THREAD_CPUTIME_ID, &startTimes);
- LOG_SOAP_DEBUG("%020" PRIu64 ": S ssoLogon", ulSessionId);
- if (!lpInput || lpInput->__size == 0 || lpInput->__ptr == NULL || !szUsername || !szClientVersion)
- goto exit;
- lpszEnabled = g_lpSessionManager->GetConfig()->GetSetting("enable_sso");
- if (!(lpszEnabled && strcasecmp(lpszEnabled, "yes") == 0))
- goto nosso;
- lpsResponse->lpszVersion = const_cast<char *>("0," PROJECT_VERSION_SERVER_STR);
- lpsResponse->ulCapabilities = KOPANO_LATEST_CAPABILITIES;
- /* See ns__logon for comments. */
- if (zcp_peerfd_is_local(soap->socket) <= 0 && (clientCaps & KOPANO_CAP_COMPRESSION)) {
- lpsResponse->ulCapabilities |= KOPANO_CAP_COMPRESSION;
- // (ECSessionManager::ValidateSession() will do this for all other functions)
- soap_set_imode(soap, SOAP_ENC_ZLIB); // also autodetected
- soap_set_omode(soap, SOAP_ENC_ZLIB | SOAP_IO_CHUNK);
- } else {
- clientCaps &= ~KOPANO_CAP_COMPRESSION;
- }
- if (ulSessionId == 0) {
- // new auth session
- er = g_lpSessionManager->CreateAuthSession(soap, clientCaps, &newSessionID, &lpecAuthSession, true, true);
- if (er != erSuccess) {
- er = KCERR_LOGON_FAILED;
- goto exit;
- }
- // when the first validate fails, remove the correct sessionid
- ulSessionId = newSessionID;
- } else {
- er = g_lpSessionManager->ValidateSession(soap, ulSessionId, &lpecAuthSession, true);
- if (er != erSuccess)
- goto exit;
- }
- lpecAuthSession->SetClientMeta(szClientAppVersion, szClientAppMisc);
- if ((clientCaps & KOPANO_CAP_UNICODE) == 0) {
- szUsername = ECStringCompat::WTF1252_to_UTF8(soap, szUsername);
- szClientVersion = ECStringCompat::WTF1252_to_UTF8(soap, szClientVersion);
- szClientApp = ECStringCompat::WTF1252_to_UTF8(soap, szClientApp);
- }
- er = lpecAuthSession->ValidateSSOData(soap, szUsername, szImpersonateUser, szClientVersion, szClientApp, szClientAppVersion, szClientAppMisc, lpInput, &lpOutput);
- if (er == KCERR_SSO_CONTINUE) {
- // continue validation exchange
- lpsResponse->lpOutput = lpOutput;
- } else if (er == erSuccess) {
- // done and logged in
- // create ecsession from ecauthsession, and place in session map
- er = g_lpSessionManager->RegisterSession(lpecAuthSession, ullSessionGroup, szClientVersion, szClientApp, szClientAppVersion, szClientAppMisc, &newSessionID, &lpecSession, true);
- if (er != erSuccess) {
- ec_log_err("User authenticated, but failed to create session. Error 0x%08X", er);
- goto exit;
- }
- // We allow Zarafa >=6 clients to connect to a Kopano server. However, anything below that will be
- // denied. We can't say what future clients may or may not be capable of. So we'll leave that to the
- // clients.
- if (KOPANO_COMPARE_VERSION_TO_GENERAL(lpecSession->ClientVersion(), MAKE_KOPANO_GENERAL(6)) < 0) {
- ec_log_warn("Rejected logon attempt from a %s version client.", szClientVersion ? szClientVersion : "<unknown>");
- er = KCERR_INVALID_VERSION;
- goto exit;
- }
- // delete authsession
- lpecAuthSession->Unlock();
-
- g_lpSessionManager->RemoveSession(ulSessionId);
- lpecAuthSession = NULL;
- // return ecsession number
- lpsResponse->lpOutput = NULL;
- } else {
- // delete authsession
- lpecAuthSession->Unlock();
- g_lpSessionManager->RemoveSession(ulSessionId);
- lpecAuthSession = NULL;
- er = KCERR_LOGON_FAILED;
- goto exit;
- }
- lpsResponse->ulSessionId = newSessionID;
- if (clientCaps & KOPANO_CAP_MULTI_SERVER)
- lpsResponse->ulCapabilities |= KOPANO_CAP_MULTI_SERVER;
- if (clientCaps & KOPANO_CAP_ENHANCED_ICS && parseBool(g_lpSessionManager->GetConfig()->GetSetting("enable_enhanced_ics"))) {
- lpsResponse->ulCapabilities |= KOPANO_CAP_ENHANCED_ICS;
- soap_set_omode(soap, SOAP_ENC_MTOM | SOAP_IO_CHUNK);
- soap_set_imode(soap, SOAP_ENC_MTOM);
- soap_post_check_mime_attachments(soap);
- }
- if (clientCaps & KOPANO_CAP_UNICODE)
- lpsResponse->ulCapabilities |= KOPANO_CAP_UNICODE;
- if (clientCaps & KOPANO_CAP_MSGLOCK)
- lpsResponse->ulCapabilities |= KOPANO_CAP_MSGLOCK;
- if(er != KCERR_SSO_CONTINUE) {
- // Don't reset er to erSuccess on SSO_CONTINUE, we don't need the server guid yet
- er = g_lpSessionManager->GetServerGUID(&sServerGuid);
- if (er != erSuccess)
- goto exit;
-
- lpsResponse->sServerGuid.__ptr = (unsigned char*)s_memcpy(soap, (char*)&sServerGuid, sizeof(sServerGuid));
- lpsResponse->sServerGuid.__size = sizeof(sServerGuid);
- }
-
- if(lpecSession && (lpecSession->GetAuthMethod() == ECSession::METHOD_USERPASSWORD || lpecSession->GetAuthMethod() == ECSession::METHOD_SSO))
- record_logon_time(lpecSession, true);
- exit:
- if (lpecAuthSession != NULL)
- lpecAuthSession->Unlock();
- if (lpecSession)
- lpecSession->Unlock();
-
- if (er == erSuccess)
- g_lpStatsCollector->Increment(SCN_LOGIN_SSO);
- else if (er != KCERR_SSO_CONTINUE)
- g_lpStatsCollector->Increment(SCN_LOGIN_DENIED);
- nosso:
- lpsResponse->er = er;
- clock_gettime(CLOCK_THREAD_CPUTIME_ID, &endTimes);
- LOG_SOAP_DEBUG("%020" PRIu64 ": E ssoLogon 0x%08x %f %f", ulSessionId, er, timespec2dbl(endTimes) - timespec2dbl(startTimes), GetTimeOfDay() - dblStart);
- return SOAP_OK;
- }
- /**
- * logoff: invalidate the session and close all notifications and memory held by the session
- */
- int ns__logoff(struct soap *soap, ULONG64 ulSessionId, unsigned int *result)
- {
- ECRESULT er = erSuccess;
- ECSession *lpecSession = NULL;
- struct timespec startTimes = {0}, endTimes = {0};
- double dblStart = GetTimeOfDay();
- clock_gettime(CLOCK_THREAD_CPUTIME_ID, &startTimes);
- LOG_SOAP_DEBUG("%020" PRIu64 ": S logoff", ulSessionId);
- er = g_lpSessionManager->ValidateSession(soap, ulSessionId, &lpecSession, true);
- if(er != erSuccess)
- goto exit;
- if (lpecSession->GetAuthMethod() == ECSession::METHOD_USERPASSWORD ||
- lpecSession->GetAuthMethod() == ECSession::METHOD_SSO)
- record_logon_time(lpecSession, false);
- lpecSession->Unlock();
- // lpecSession is discarded. It is not locked, so we can do that. We only did the 'validatesession'
- // call to see if the session id existed in the first place, and the request is coming from the correct
- // IP address. Another logoff() call called at the same time may remove the session *here*, in which case the following call
- // will fail. This makes sure people can't terminate each others sessions unless they have the same source IP.
- er = g_lpSessionManager->RemoveSession(ulSessionId);
- exit:
- *result = er;
- clock_gettime(CLOCK_THREAD_CPUTIME_ID, &endTimes);
- LOG_SOAP_DEBUG("%020" PRIu64 ": E logoff 0x%08x %f %f", ulSessionId, 0, timespec2dbl(endTimes) - timespec2dbl(startTimes), GetTimeOfDay() - dblStart);
- return SOAP_OK;
- }
- #define SOAP_ENTRY_FUNCTION_HEADER(resultvar, fname) \
- ECRESULT er = erSuccess; \
- struct timespec startTimes = {0}, endTimes = {0}; \
- double dblStart = GetTimeOfDay(); \
- ECSession *lpecSession = NULL; \
- unsigned int *lpResultVar = &resultvar; \
- const char *szFname = #fname; \
- clock_gettime(CLOCK_THREAD_CPUTIME_ID, &startTimes); \
- LOG_SOAP_DEBUG("%020" PRIu64 ": S %s", ulSessionId, szFname); \
- er = g_lpSessionManager->ValidateSession(soap, ulSessionId, &lpecSession, true);\
- const bool bSupportUnicode = (er == erSuccess ? (lpecSession->GetCapabilities() & KOPANO_CAP_UNICODE) != 0 : false); \
- const ECStringCompat stringCompat(bSupportUnicode); \
- if(er != erSuccess) \
- goto __soapentry_exit; \
- soap_info(soap)->ulLastSessionId = ulSessionId; \
- soap_info(soap)->szFname = szFname; \
- lpecSession->AddBusyState(pthread_self(), #fname, soap_info(soap)->threadstart, soap_info(soap)->start);
- #define SOAP_ENTRY_FUNCTION_FOOTER \
- __soapentry_exit: \
- *lpResultVar = er; \
- clock_gettime(CLOCK_THREAD_CPUTIME_ID, &endTimes); \
- if(lpecSession) { \
- LOG_SOAP_DEBUG("%020" PRIu64 ": E %s 0x%08x %f %f", ulSessionId, szFname, er, timespec2dbl(endTimes) - timespec2dbl(startTimes), GetTimeOfDay() - dblStart); \
- lpecSession->UpdateBusyState(pthread_self(), SESSION_STATE_SENDING); \
- lpecSession->Unlock(); \
- } \
- return SOAP_OK;
- #define SOAP_ENTRY_START(fname,resultvar,...) \
- int ns__##fname(struct soap *soap, ULONG64 ulSessionId, ##__VA_ARGS__) \
- { \
- SOAP_ENTRY_FUNCTION_HEADER(resultvar, fname) \
- er = [&]() -> int {
- #define SOAP_ENTRY_END() \
- return er; \
- }(); \
- SOAP_ENTRY_FUNCTION_FOOTER \
- }
- #define ALLOC_DBRESULT() \
- DB_ROW UNUSED_VAR lpDBRow = NULL; \
- DB_LENGTHS UNUSED_VAR lpDBLen = NULL; \
- DB_RESULT UNUSED_VAR lpDBResult; \
- std::string UNUSED_VAR strQuery;
- #define USE_DATABASE() \
- ECDatabase* lpDatabase = NULL; \
- ALLOC_DBRESULT(); \
- \
- er = lpecSession->GetDatabase(&lpDatabase); \
- if (er != erSuccess) { \
- ec_log_err(" GetDatabase failed"); \
- return KCERR_DATABASE_ERROR; \
- }
- #define ROLLBACK_ON_ERROR() \
- if (lpDatabase && FAILED(er)) \
- lpDatabase->Rollback(); \
- static ECRESULT PurgeSoftDelete(ECSession *lpecSession,
- unsigned int ulLifetime, unsigned int *lpulMessages,
- unsigned int *lpulFolders, unsigned int *lpulStores, bool *lpbExit)
- {
- ECRESULT er = erSuccess;
- ECDatabase* lpDatabase = NULL;
- DB_RESULT lpDBResult;
- DB_ROW lpDBRow = NULL;
- std::string strQuery;
- FILETIME ft;
- unsigned int ulDeleteFlags = 0;
- time_t ulTime = 0;
- ECListInt lObjectIds;
- unsigned int ulFolders = 0, ulMessages = 0;
- unsigned int ulStores = 0;
- bool bExitDummy = false;
- if (g_bPurgeSoftDeleteStatus) {
- ec_log_err("Softdelete already running");
- er = KCERR_BUSY;
- goto exit;
- }
-
- g_bPurgeSoftDeleteStatus = TRUE;
- if (!lpbExit)
- lpbExit = &bExitDummy;
- er = lpecSession->GetDatabase(&lpDatabase);
- if (er != erSuccess)
- goto exit;
- // Although it doesn't make sense for the message deleter to include EC_DELETE_FOLDERS, it doesn't hurt either, since they shouldn't be there
- // and we really want to delete all the softdeleted items anyway.
- ulDeleteFlags = EC_DELETE_CONTAINER | EC_DELETE_FOLDERS | EC_DELETE_MESSAGES | EC_DELETE_RECIPIENTS | EC_DELETE_ATTACHMENTS | EC_DELETE_HARD_DELETE;
- GetSystemTimeAsFileTime(&ft);
- FileTimeToUnixTime(ft, &ulTime);
- ulTime -= ulLifetime;
- UnixTimeToFileTime(ulTime, &ft);
- // Select softdeleted stores (ignore softdelete_lifetime setting because a store can't be restored anyway)
- strQuery = "SELECT id FROM hierarchy WHERE parent IS NULL AND (flags&"+stringify(MSGFLAG_DELETED)+")="+stringify(MSGFLAG_DELETED)+" AND type="+stringify(MAPI_STORE);
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if(er != erSuccess)
- goto exit;
- ulStores = lpDatabase->GetNumRows(lpDBResult);
- if(ulStores > 0)
- {
- while( (lpDBRow = lpDatabase->FetchRow(lpDBResult)) )
- {
- if(lpDBRow == NULL || lpDBRow[0] == NULL)
- continue;
- lObjectIds.push_back(atoui(lpDBRow[0]));
- }
- // free before we call DeleteObjects()
- lpDBResult = DB_RESULT();
- if (*lpbExit) {
- er = KCERR_USER_CANCEL;
- goto exit;
- }
- ec_log_info("Start to purge %d stores", (int)lObjectIds.size());
- for (auto iterObjectId = lObjectIds.cbegin();
- iterObjectId != lObjectIds.cend() && !*lpbExit;
- ++iterObjectId)
- {
- ec_log_info(" purge store (%d)", *iterObjectId);
- er = DeleteObjects(lpecSession, lpDatabase, *iterObjectId, ulDeleteFlags|EC_DELETE_STORE, 0, false, false);
- if(er != erSuccess) {
- ec_log_err("Error while removing softdelete store objects, error code: 0x%x.", er);
- goto exit;
- }
- }
- ec_log_info("Store purge done");
- }
- if (*lpbExit) {
- er = KCERR_USER_CANCEL;
- goto exit;
- }
- // Select softdeleted folders
- strQuery = "SELECT h.id FROM hierarchy AS h JOIN properties AS p ON p.hierarchyid=h.id AND p.tag="+stringify(PROP_ID(PR_DELETED_ON))+" AND p.type="+stringify(PROP_TYPE(PR_DELETED_ON))+" WHERE (h.flags&"+stringify(MSGFLAG_DELETED)+")="+stringify(MSGFLAG_DELETED)+" AND p.val_hi<="+stringify(ft.dwHighDateTime)+" AND h.type="+stringify(MAPI_FOLDER);
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if(er != erSuccess)
- goto exit;
- ulFolders = lpDatabase->GetNumRows(lpDBResult);
- if(ulFolders > 0)
- {
- // Remove all items
- lObjectIds.clear();
- while( (lpDBRow = lpDatabase->FetchRow(lpDBResult)) )
- {
- if(lpDBRow == NULL || lpDBRow[0] == NULL)
- continue;
- lObjectIds.push_back(atoui(lpDBRow[0]));
- }
- // free before we call DeleteObjects()
- lpDBResult = DB_RESULT();
- if (*lpbExit) {
- er = KCERR_USER_CANCEL;
- goto exit;
- }
- ec_log_info("Start to purge %d folders", (int)lObjectIds.size());
- er = DeleteObjects(lpecSession, lpDatabase, &lObjectIds, ulDeleteFlags, 0, false, false);
- if(er != erSuccess) {
- ec_log_err("Error while removing softdelete folder objects, error code: 0x%x.", er);
- goto exit;
- }
- ec_log_info("Folder purge done");
- }
- if (*lpbExit) {
- er = KCERR_USER_CANCEL;
- goto exit;
- }
- // Select softdeleted messages
- strQuery = "SELECT h.id FROM hierarchy AS h JOIN properties AS p ON p.hierarchyid=h.id AND p.tag="+stringify(PROP_ID(PR_DELETED_ON))+" AND p.type="+stringify(PROP_TYPE(PR_DELETED_ON))+" WHERE (h.flags&"+stringify(MSGFLAG_DELETED)+")="+stringify(MSGFLAG_DELETED)+" AND h.type="+stringify(MAPI_MESSAGE)+" AND p.val_hi<="+stringify(ft.dwHighDateTime);
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if(er != erSuccess)
- goto exit;
- ulMessages = lpDatabase->GetNumRows(lpDBResult);
- if(ulMessages > 0)
- {
- // Remove all items
- lObjectIds.clear();
- while( (lpDBRow = lpDatabase->FetchRow(lpDBResult)) )
- {
- if(lpDBRow == NULL || lpDBRow[0] == NULL)
- continue;
- lObjectIds.push_back(atoui(lpDBRow[0]));
- }
- // free before we call DeleteObjects()
- lpDBResult = DB_RESULT();
- if (*lpbExit) {
- er = KCERR_USER_CANCEL;
- goto exit;
- }
- ec_log_info("Start to purge %d messages", (int)lObjectIds.size());
- er = DeleteObjects(lpecSession, lpDatabase, &lObjectIds, ulDeleteFlags, 0, false, false);
- if(er != erSuccess) {
- ec_log_err("Error while removing softdelete message objects, error code: 0x%x.", er);
- goto exit;
- }
- ec_log_info("Message purge done");
- }
- // these stats are only from toplevel objects
- if(lpulFolders)
- *lpulFolders = ulFolders;
- if(lpulMessages)
- *lpulMessages = ulMessages;
- if (lpulStores)
- *lpulStores = ulStores;
- exit:
- if (er != KCERR_BUSY)
- g_bPurgeSoftDeleteStatus = FALSE;
- return er;
- }
- /**
- * getPublicStore: get the root entryid, the store entryid and the store GUID for the public store.
- * FIXME, GUID is duplicate
- */
- SOAP_ENTRY_START(getPublicStore, lpsResponse->er, unsigned int ulFlags, struct getStoreResponse *lpsResponse)
- {
- unsigned int ulCompanyId = 0;
- std::string strStoreName;
- std::string strStoreServer;
- std::string strServerPath;
- std::string strCompanyName;
- const std::string strThisServer = g_lpSessionManager->GetConfig()->GetSetting("server_name", "", "Unknown");
- objectdetails_t details;
- USE_DATABASE();
- if (lpecSession->GetSessionManager()->IsHostedSupported()) {
- /* Hosted support, Public store owner is company */
- er = lpecSession->GetSecurity()->GetUserCompany(&ulCompanyId);
- if (er != erSuccess)
- return er;
- er = lpecSession->GetUserManagement()->GetObjectDetails(ulCompanyId, &details);
- if(er != erSuccess)
- return er;
- strStoreServer = details.GetPropString(OB_PROP_S_SERVERNAME);
- strCompanyName = details.GetPropString(OB_PROP_S_FULLNAME);
- } else {
- ulCompanyId = KOPANO_UID_EVERYONE; /* No hosted support, Public store owner is Everyone */
- if (lpecSession->GetSessionManager()->IsDistributedSupported()) {
- /*
- * GetObjectDetailsAndSync will return the group details for EVERYONE when called
- * with KOPANO_UID_EVERYONE. But we want the pseudo company for that contains the
- * public store.
- */
- er = lpecSession->GetUserManagement()->GetPublicStoreDetails(&details);
- if (er == KCERR_NO_SUPPORT) {
- /* Not supported: No MultiServer with this plugin, so we're good */
- strStoreServer = strThisServer;
- er = erSuccess;
- } else if (er == erSuccess)
- strStoreServer = details.GetPropString(OB_PROP_S_SERVERNAME);
- else
- return er;
- } else
- strStoreServer = strThisServer;
- }
- if (lpecSession->GetSessionManager()->IsDistributedSupported())
- {
- if (strStoreServer.empty()) {
- if (!strCompanyName.empty())
- ec_log_err("Company \"%s\" has no home server for its public store.", strCompanyName.c_str());
- else
- ec_log_err("Public store has no home server.");
- return KCERR_NOT_FOUND;
- }
-
- /* Do we own the store? */
- if (strcasecmp(strThisServer.c_str(), strStoreServer.c_str()) != 0)
- {
- if ((ulFlags & EC_OVERRIDE_HOMESERVER) == 0) {
- er = GetBestServerPath(soap, lpecSession, strStoreServer, &strServerPath);
- if (er != erSuccess)
- return er;
- lpsResponse->lpszServerPath = STROUT_FIX_CPY(strServerPath.c_str());
- ec_log_info("Redirecting request to \"%s\"", lpsResponse->lpszServerPath);
- g_lpStatsCollector->Increment(SCN_REDIRECT_COUNT, 1);
- return KCERR_UNABLE_TO_COMPLETE;
- }
- }
- }
- /*
- * The public store is stored in the database with the companyid as owner.
- */
- strQuery =
- "SELECT hierarchy.id, stores.guid, stores.hierarchy_id "
- "FROM stores "
- "JOIN hierarchy on stores.hierarchy_id=hierarchy.parent "
- "WHERE stores.user_id = " + stringify(ulCompanyId) + " LIMIT 1";
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if(er != erSuccess)
- return er;
- if (lpDatabase->GetNumRows(lpDBResult) == 0)
- return KCERR_NOT_FOUND;
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
- lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
- if( lpDBRow == NULL || lpDBRow[0] == NULL || lpDBRow[1] == NULL || lpDBRow[2] == NULL ||
- lpDBLen == NULL || lpDBLen[1] == 0)
- {
- ec_log_err("getPublicStore(): no rows from db");
- return KCERR_DATABASE_ERROR; // this should never happen
- }
- er = g_lpSessionManager->GetCacheManager()->GetEntryIdFromObject(atoui(lpDBRow[0]), soap, 0, &lpsResponse->sRootId);
- if(er != erSuccess)
- return er;
- if ((ulFlags & EC_OVERRIDE_HOMESERVER) == 0)
- lpsResponse->lpszServerPath = STROUT_FIX_CPY(string("pseudo://" + strStoreServer).c_str());
- er = g_lpSessionManager->GetCacheManager()->GetEntryIdFromObject(atoui(lpDBRow[2]), soap, ulFlags, &lpsResponse->sStoreId);
- if(er != erSuccess)
- return er;
- lpsResponse->guid.__size= lpDBLen[1];
- lpsResponse->guid.__ptr = s_alloc<unsigned char>(soap, lpDBLen[1]);
- memcpy(lpsResponse->guid.__ptr, lpDBRow[1], lpDBLen[1]);
- return erSuccess;
- }
- SOAP_ENTRY_END()
- /**
- * getStore: get the root entryid, the store entryid and the GUID of a store specified with lpsEntryId
- * FIXME: output store entryid equals input store entryid ?
- * FIXME: output GUID is also in entryid
- */
- SOAP_ENTRY_START(getStore, lpsResponse->er, entryId* lpsEntryId, struct getStoreResponse *lpsResponse)
- {
- unsigned int ulStoreId = 0;
- unsigned int ulUserId = 0;
- objectdetails_t sUserDetails;
- string strServerName;
- string strServerPath;
- USE_DATABASE();
- if (!lpsEntryId) {
- ulUserId = lpecSession->GetSecurity()->GetUserId();
- // Check if the store should be available on this server
- if (lpecSession->GetSessionManager()->IsDistributedSupported() &&
- !lpecSession->GetUserManagement()->IsInternalObject(ulUserId)) {
- er = lpecSession->GetUserManagement()->GetObjectDetails(ulUserId, &sUserDetails);
- if (er != erSuccess)
- return er;
- strServerName = sUserDetails.GetPropString(OB_PROP_S_SERVERNAME);
- if (strServerName.empty())
- return er;
- if (strcasecmp(strServerName.c_str(), g_lpSessionManager->GetConfig()->GetSetting("server_name")) != 0) {
- er = GetBestServerPath(soap, lpecSession, strServerName, &strServerPath);
- if (er != erSuccess)
- return er;
- lpsResponse->lpszServerPath = STROUT_FIX_CPY(strServerPath.c_str());
- ec_log_info("Redirecting request to \"%s\"", lpsResponse->lpszServerPath);
- g_lpStatsCollector->Increment(SCN_REDIRECT_COUNT, 1);
- return KCERR_UNABLE_TO_COMPLETE;
- }
- }
- }
-
- // If strServerName is empty, we're not running in distributed mode or we're dealing
- // with a local account. Just use the name from the configuration.
- if (strServerName.empty())
- strServerName = g_lpSessionManager->GetConfig()->GetSetting("server_name" ,"", "Unknown");
-
- // Always return a pseudo URL
- lpsResponse->lpszServerPath = STROUT_FIX_CPY(string("pseudo://" + strServerName).c_str());
- strQuery = "SELECT hierarchy.id, stores.guid, stores.hierarchy_id "
- "FROM stores join hierarchy on stores.hierarchy_id=hierarchy.parent ";
- if(lpsEntryId) {
- er = lpecSession->GetObjectFromEntryId(lpsEntryId, &ulStoreId);
- if(er != erSuccess)
- return er;
- strQuery += "WHERE stores.hierarchy_id=" + stringify(ulStoreId);// FIXME: mysql query
- }else {
- strQuery += "WHERE stores.user_id=" + stringify(ulUserId)
- + " AND stores.type=" + stringify(ECSTORE_TYPE_PRIVATE);
- }
- strQuery += " LIMIT 1";
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if(er != erSuccess)
- return er;
- if (lpDatabase->GetNumRows(lpDBResult) == 0)
- return KCERR_NOT_FOUND;
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
- if (lpDBRow == NULL) {
- ec_log_err("getStore(): no rows from db");
- return KCERR_DATABASE_ERROR; // this should never happen
- }
- lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
- /*
- * Avoid processing SQL NULL, or memory blocks that are not
- * NUL-terminated.
- * Ensure the GUID (lpDBRow[1]) is not empty. (Perhaps check for
- * !=16 instead of ==0?)
- */
- if (lpDBLen == NULL || lpDBRow[0] == NULL ||
- lpDBRow[1] == NULL || lpDBRow[2] == NULL ||
- memchr(lpDBRow[0], '\0', lpDBLen[0] + 1) == NULL ||
- memchr(lpDBRow[2], '\0', lpDBLen[2] + 1) == NULL ||
- lpDBLen[1] == 0) {
- ec_log_err("getStore(): received trash rows from db");
- return KCERR_DATABASE_ERROR;
- }
- er = g_lpSessionManager->GetCacheManager()->GetEntryIdFromObject(atoui(lpDBRow[0]), soap, 0, &lpsResponse->sRootId);
- if(er != erSuccess)
- return er;
- er = g_lpSessionManager->GetCacheManager()->GetEntryIdFromObject(atoui(lpDBRow[2]), soap, 0, &lpsResponse->sStoreId);
- if(er != erSuccess)
- return er;
- lpsResponse->guid.__size= lpDBLen[1];
- lpsResponse->guid.__ptr = s_alloc<unsigned char>(soap, lpDBLen[1]);
- memcpy(lpsResponse->guid.__ptr, lpDBRow[1], lpDBLen[1]);
- return erSuccess;
- }
- SOAP_ENTRY_END()
- /**
- * getStoreName: get the PR_DISPLAY_NAME of the store specified in sEntryId
- */
- SOAP_ENTRY_START(getStoreName, lpsResponse->er, entryId sEntryId, struct getStoreNameResponse *lpsResponse)
- {
- unsigned int ulObjId = 0;
- unsigned int ulStoreType = 0;
- er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulObjId);
- if(er != erSuccess)
- return er;
- er = lpecSession->GetSessionManager()->GetCacheManager()->GetStoreAndType(ulObjId, NULL, NULL, &ulStoreType);
- if (er != erSuccess)
- return er;
- er = ECGenProps::GetStoreName(soap, lpecSession, ulObjId, ulStoreType, &lpsResponse->lpszStoreName);
- if(er != erSuccess)
- return er;
- if (!bSupportUnicode)
- lpsResponse->lpszStoreName = STROUT_FIX(lpsResponse->lpszStoreName);
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(getStoreType, lpsResponse->er, entryId sEntryId, struct getStoreTypeResponse *lpsResponse)
- {
- unsigned int ulObjId = 0;
- er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulObjId);
- if (er != erSuccess)
- return er;
- return lpecSession->GetSessionManager()->GetCacheManager()->
- GetStoreAndType(ulObjId, nullptr, nullptr,
- &lpsResponse->ulStoreType);
- }
- SOAP_ENTRY_END()
- static ECRESULT ReadProps(struct soap *soap, ECSession *lpecSession,
- unsigned int ulObjId, unsigned ulObjType, unsigned int ulObjTypeParent,
- CHILDPROPS sChildProps, struct propTagArray *lpsPropTag,
- struct propValArray *lpsPropVal)
- {
- ECRESULT er = erSuccess;
- quotadetails_t sDetails;
- unsigned int ulCompanyId = 0;
- unsigned int ulStoreOwner = 0;
- object_ptr<ECAttachmentStorage> lpAttachmentStorage;
- struct propVal sPropVal;
- ECStringCompat stringCompat(lpecSession->GetCapabilities() & KOPANO_CAP_UNICODE);
- USE_DATABASE();
-
- if(ulObjType == MAPI_STORE) //fimxe: except public stores
- {
- if (ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_USER_NAME, ulObjId, 0, ulObjId, 0, ulObjType, &sPropVal) == erSuccess) {
- er = FixPropEncoding(soap, stringCompat, Out, &sPropVal);
- if (er != erSuccess)
- return er;
- sChildProps.lpPropTags->AddPropTag(sPropVal.ulPropTag);
- sChildProps.lpPropVals->AddPropVal(sPropVal);
- }
- if (ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_USER_ENTRYID, ulObjId, 0, ulObjId, 0, ulObjType, &sPropVal) == erSuccess) {
- sChildProps.lpPropTags->AddPropTag(PR_USER_ENTRYID);
- sChildProps.lpPropVals->AddPropVal(sPropVal);
- }
- er = lpecSession->GetSecurity()->GetStoreOwner(ulObjId, &ulStoreOwner);
- if (er != erSuccess)
- return er;
- // Quota information
- if (lpecSession->GetSecurity()->GetUserQuota(ulStoreOwner, false, &sDetails) == erSuccess)
- {
- // PR_QUOTA_WARNING_THRESHOLD
- sPropVal.ulPropTag = PR_QUOTA_WARNING_THRESHOLD;
- sPropVal.__union = SOAP_UNION_propValData_ul;
- sPropVal.Value.ul = (unsigned long)(sDetails.llWarnSize / 1024);
- sChildProps.lpPropTags->AddPropTag(PR_QUOTA_WARNING_THRESHOLD);
- sChildProps.lpPropVals->AddPropVal(sPropVal);
-
- // PR_QUOTA_SEND_THRESHOLD
- sPropVal.ulPropTag = PR_QUOTA_SEND_THRESHOLD;
- sPropVal.__union = SOAP_UNION_propValData_ul;
- sPropVal.Value.ul = (unsigned long)(sDetails.llSoftSize / 1024);
- sChildProps.lpPropTags->AddPropTag(PR_QUOTA_SEND_THRESHOLD);
- sChildProps.lpPropVals->AddPropVal(sPropVal);
- // PR_QUOTA_RECEIVE_THRESHOLD
- sPropVal.ulPropTag = PR_QUOTA_RECEIVE_THRESHOLD;
- sPropVal.__union = SOAP_UNION_propValData_ul;
- sPropVal.Value.ul = (unsigned long)(sDetails.llHardSize / 1024);
- sChildProps.lpPropTags->AddPropTag(PR_QUOTA_RECEIVE_THRESHOLD);
- sChildProps.lpPropVals->AddPropVal(sPropVal);
- }
- if (lpecSession->GetCapabilities() & KOPANO_CAP_MAILBOX_OWNER) {
- // get the companyid to which the logged in user belongs to.
- er = lpecSession->GetSecurity()->GetUserCompany(&ulCompanyId);
- if (er != erSuccess)
- return er;
- // 5.0 client knows how to handle the PR_MAILBOX_OWNER_* properties
- if(ulStoreOwner != KOPANO_UID_EVERYONE && ulStoreOwner != ulCompanyId ) {
- if (ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_MAILBOX_OWNER_NAME, ulObjId, 0, ulObjId, 0, ulObjType, &sPropVal) == erSuccess) {
- er = FixPropEncoding(soap, stringCompat, Out, &sPropVal);
- if (er != erSuccess)
- return er;
- sChildProps.lpPropTags->AddPropTag(sPropVal.ulPropTag);
- sChildProps.lpPropVals->AddPropVal(sPropVal);
- }
- // Add PR_MAILBOX_OWNER_ENTRYID
- if (ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_MAILBOX_OWNER_ENTRYID, ulObjId, 0, ulObjId, 0, ulObjType, &sPropVal) == erSuccess) {
- sChildProps.lpPropTags->AddPropTag(PR_MAILBOX_OWNER_ENTRYID);
- sChildProps.lpPropVals->AddPropVal(sPropVal);
- }
- }
- }
- // Add PR_DISPLAY_NAME
- if (ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_DISPLAY_NAME, ulObjId, 0, ulObjId, 0, ulObjType, &sPropVal) == erSuccess) {
- er = FixPropEncoding(soap, stringCompat, Out, &sPropVal);
- if (er != erSuccess)
- return er;
- sChildProps.lpPropTags->AddPropTag(sPropVal.ulPropTag);
- sChildProps.lpPropVals->AddPropVal(sPropVal);
- }
- if(ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_MAPPING_SIGNATURE, ulObjId, 0, 0, 0, ulObjType, &sPropVal) == erSuccess) {
- sChildProps.lpPropTags->AddPropTag(PR_MAPPING_SIGNATURE);
- sChildProps.lpPropVals->AddPropVal(sPropVal);
- }
- if (!sChildProps.lpPropTags->HasPropTag(PR_SORT_LOCALE_ID)) {
- sPropVal.__union = SOAP_UNION_propValData_ul;
- sPropVal.ulPropTag = PR_SORT_LOCALE_ID;
- sPropVal.Value.ul = lpecSession->GetSessionManager()->GetSortLCID(ulObjId);
- sChildProps.lpPropTags->AddPropTag(PR_SORT_LOCALE_ID);
- sChildProps.lpPropVals->AddPropVal(sPropVal);
- }
- }
- //PR_PARENT_SOURCE_KEY for folders and messages
- if(ulObjType == MAPI_FOLDER || (ulObjType == MAPI_MESSAGE && ulObjTypeParent == MAPI_FOLDER))
- {
- if(ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_PARENT_SOURCE_KEY, ulObjId, 0, 0, 0, ulObjType, &sPropVal) == erSuccess)
- {
- sChildProps.lpPropTags->AddPropTag(sPropVal.ulPropTag);
- sChildProps.lpPropVals->AddPropVal(sPropVal);
- }
- if(ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_SOURCE_KEY, ulObjId, 0, 0, 0, ulObjType, &sPropVal) == erSuccess)
- {
- sChildProps.lpPropTags->AddPropTag(sPropVal.ulPropTag);
- sChildProps.lpPropVals->AddPropVal(sPropVal);
- }
- }
- if (ulObjType == MAPI_MESSAGE || ulObjType == MAPI_ATTACH) {
- ULONG ulPropTag = (ulObjType == MAPI_MESSAGE ? PR_EC_IMAP_EMAIL : PR_ATTACH_DATA_BIN);
- er = CreateAttachmentStorage(lpDatabase, &~lpAttachmentStorage);
- if (er != erSuccess)
- return er;
- if (lpAttachmentStorage->ExistAttachment(ulObjId, PROP_ID(ulPropTag)))
- sChildProps.lpPropTags->AddPropTag(ulPropTag);
- }
- if ((ulObjType == MAPI_MAILUSER || ulObjType == MAPI_DISTLIST) &&
- ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_INSTANCE_KEY, ulObjId, 0, 0, 0, ulObjType, &sPropVal) == erSuccess) {
- sChildProps.lpPropTags->AddPropTag(sPropVal.ulPropTag);
- sChildProps.lpPropVals->AddPropVal(sPropVal);
- }
- // Set the PR_RECORD_KEY
- if ((ulObjType != MAPI_ATTACH || !sChildProps.lpPropTags->HasPropTag(PR_RECORD_KEY)) &&
- ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_RECORD_KEY, ulObjId, 0, 0, 0, ulObjType, &sPropVal) == erSuccess) {
- sChildProps.lpPropTags->AddPropTag(sPropVal.ulPropTag);
- sChildProps.lpPropVals->AddPropVal(sPropVal);
- }
- if (ulObjType == MAPI_FOLDER || ulObjType == MAPI_STORE || ulObjType == MAPI_MESSAGE) {
- // Get PARENT_ENTRYID
- strQuery = "SELECT hierarchy.parent,hierarchy.flags,hierarchy.type FROM hierarchy WHERE hierarchy.id="+stringify(ulObjId) + " LIMIT 1";
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if(er != erSuccess)
- return er;
- if(lpDatabase->GetNumRows(lpDBResult) > 0)
- {
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
- if(lpDBRow && lpDBRow[0]) {
- if(ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_PARENT_ENTRYID, ulObjId, 0, 0, 0, ulObjType, &sPropVal) == erSuccess)
- {
- sChildProps.lpPropTags->AddPropTag(sPropVal.ulPropTag);
- sChildProps.lpPropVals->AddPropVal(sPropVal);
- }
- }
- if(lpDBRow && lpDBRow[1] && lpDBRow[2] && atoi(lpDBRow[2]) == 3) {
- // PR_RIGHTS
- if (ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_RIGHTS, ulObjId, 0, 0, 0, ulObjType, &sPropVal) == erSuccess) {
- sChildProps.lpPropTags->AddPropTag(sPropVal.ulPropTag);
- sChildProps.lpPropVals->AddPropVal(sPropVal);
- }
- }
- // Set the flags PR_ACCESS and PR_ACCESS_LEVEL
- if(lpDBRow && lpDBRow[0] && lpDBRow[2] && (atoi(lpDBRow[2]) == 3 || atoi(lpDBRow[2]) == 5) && lpDBRow[1])
- {
- if (ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_ACCESS, ulObjId, 0, 0, 0, ulObjType, &sPropVal) == erSuccess) {
- sChildProps.lpPropTags->AddPropTag(sPropVal.ulPropTag);
- sChildProps.lpPropVals->AddPropVal(sPropVal);
- }
- }
- if(lpDBRow && lpDBRow[2] && lpDBRow[1])
- {
- if (ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_ACCESS_LEVEL, ulObjId, 0, 0, 0, ulObjType, &sPropVal) == erSuccess) {
- sChildProps.lpPropTags->AddPropTag(sPropVal.ulPropTag);
- sChildProps.lpPropVals->AddPropVal(sPropVal);
- }
- }
- }
- }
-
- er = sChildProps.lpPropTags->GetPropTagArray(lpsPropTag);
- if(er != erSuccess)
- return er;
- return sChildProps.lpPropVals->GetPropValArray(lpsPropVal);
- }
- /**
- * loadProp: Reads a single, large property from the database. No size limit.
- * this can also be a complete attachment
- */
- SOAP_ENTRY_START(loadProp, lpsResponse->er, entryId sEntryId, unsigned int ulObjId, unsigned int ulPropTag, struct loadPropResponse *lpsResponse)
- {
- object_ptr<ECAttachmentStorage> lpAttachmentStorage;
- USE_DATABASE();
- if(ulObjId == 0) {
- er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulObjId);
- if (er != erSuccess)
- goto exit;
-
- }
-
- // Check permission
- er = lpecSession->GetSecurity()->CheckPermission(ulObjId, ecSecurityRead);
- if(er != erSuccess)
- goto exit;
- er = lpDatabase->Begin();
- if(er != erSuccess)
- goto exit;
- if (ulPropTag != PR_ATTACH_DATA_BIN && ulPropTag != PR_EC_IMAP_EMAIL) {
- if (ulPropTag & MV_FLAG)
- strQuery = "SELECT " + (std::string)MVPROPCOLORDER + " FROM mvproperties WHERE hierarchyid="+stringify(ulObjId)+ " AND tag = " + stringify(PROP_ID(ulPropTag))+" GROUP BY hierarchyid, tag";
- else
- strQuery = "SELECT " PROPCOLORDER " FROM properties WHERE hierarchyid = " + stringify(ulObjId) + " AND tag = " + stringify(PROP_ID(ulPropTag));
- strQuery += " LIMIT 2";
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if(er != erSuccess)
- goto exit;
- if(lpDatabase->GetNumRows(lpDBResult) != 1) {
- er = KCERR_NOT_FOUND;
- goto exit;
- }
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
- lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
- if (lpDBRow == NULL || lpDBLen == NULL)
- {
- er = KCERR_DATABASE_ERROR;
- ec_log_err("loadProp(): no rows from db");
- goto exit;
- }
- lpsResponse->lpPropVal = s_alloc<propVal>(soap);
- memset(lpsResponse->lpPropVal,0,sizeof(struct propVal));
- er = CopyDatabasePropValToSOAPPropVal(soap, lpDBRow, lpDBLen, lpsResponse->lpPropVal);
- if (er != erSuccess)
- goto exit;
- } else {
- lpsResponse->lpPropVal = s_alloc<propVal>(soap);
- memset(lpsResponse->lpPropVal,0,sizeof(struct propVal));
- lpsResponse->lpPropVal->ulPropTag = ulPropTag;
- lpsResponse->lpPropVal->__union = SOAP_UNION_propValData_bin;
- lpsResponse->lpPropVal->Value.bin = s_alloc<struct xsd__base64Binary>(soap);
- memset(lpsResponse->lpPropVal->Value.bin, 0, sizeof(struct xsd__base64Binary));
- er = CreateAttachmentStorage(lpDatabase, &~lpAttachmentStorage);
- if (er != erSuccess)
- goto exit;
- size_t atsize = 0;
- er = lpAttachmentStorage->LoadAttachment(soap, ulObjId, PROP_ID(ulPropTag), &atsize, &lpsResponse->lpPropVal->Value.bin->__ptr);
- lpsResponse->lpPropVal->Value.bin->__size = atsize;
- if (er != erSuccess)
- goto exit;
- }
- er = lpDatabase->Commit();
- if(er != erSuccess)
- goto exit;
- if (!bSupportUnicode) {
- er = FixPropEncoding(soap, stringCompat, Out, lpsResponse->lpPropVal);
- if (er != erSuccess)
- goto exit;
- }
- exit:
- ROLLBACK_ON_ERROR();
- }
- SOAP_ENTRY_END()
- //TODO: flag to get size of normal folder or deleted folders
- static ECRESULT GetFolderSize(ECDatabase *lpDatabase, unsigned int ulFolderId,
- long long *lpllFolderSize)
- {
- ECRESULT er = erSuccess;
- DB_RESULT lpDBResult;
- DB_ROW lpDBRow = NULL;
- long long llSize = 0;
- long long llSubSize = 0;
- std::string strQuery;
- // sum size of all messages in a folder
- strQuery = "SELECT SUM(p.val_ulong) FROM hierarchy AS h JOIN properties AS p ON p.hierarchyid=h.id AND p.tag="+stringify(PROP_ID(PR_MESSAGE_SIZE))+" AND p.type="+stringify(PROP_TYPE(PR_MESSAGE_SIZE))+" WHERE h.parent="+stringify(ulFolderId)+" AND h.type="+stringify(MAPI_MESSAGE);
- // except the deleted items!
- strQuery += " AND h.flags & " + stringify(MSGFLAG_DELETED)+ "=0";
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if(er != erSuccess)
- return er;
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
- if(lpDBRow == NULL || lpDBRow[0] == NULL)
- llSize = 0;
- else
- llSize = atoll(lpDBRow[0]);
- // Get the subfolders
- strQuery = "SELECT id FROM hierarchy WHERE parent=" + stringify(ulFolderId) + " AND type="+stringify(MAPI_FOLDER);
- // except the deleted items!
- strQuery += " AND flags & " + stringify(MSGFLAG_DELETED)+ "=0";
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if(er != erSuccess)
- return er;
- if(lpDatabase->GetNumRows(lpDBResult) > 0)
- {
- // Walk through the folder list
- while( (lpDBRow = lpDatabase->FetchRow(lpDBResult)) )
- {
- if(lpDBRow[0] == NULL)
- continue; //Skip item
- er = GetFolderSize(lpDatabase, atoi(lpDBRow[0]), &llSubSize);
- if(er != erSuccess)
- return er;
- llSize += llSubSize;
- }
- }
- *lpllFolderSize = llSize;
- return erSuccess;
- }
- /**
- * Write properties to an object
- *
- * @param[in] soap
- * @param[in] lpecSession Pointer to the session of the caller; Cannot be NULL.
- * @param[in] lpDatabase Pointer to the database handler of the caller; Cannot be NULL.
- * @param[in] lpAttachmentStorage Pointer to an attachment storage object; Cannot be NULL.
- * @param[in] lpsSaveObj Data object which include the new propery information; Cannot be NULL.
- * @param[in] ulObjId Identify the database object to write the property data to the database.
- * @param[in] fNewItem false for an existing object, true for a new object.
- * @param[in] ulSyncId Client sync identifier.
- * @param[out] lpsReturnObj
- * @param[out] lpfHaveChangeKey
- * @param[out] lpftCreated Receives create time if new object (can be now or passed value in lpsSaveObj)
- * @param[out] lpftModified Receives modification time of written object (can be now or passed value in lpsSaveObj)
- *
- * \remarks
- * Check the permissions and quota before you call this function.
- *
- * @todo unclear comment -> sync id only to saveObject !
- */
- static ECRESULT WriteProps(struct soap *soap, ECSession *lpecSession,
- ECDatabase *lpDatabase, ECAttachmentStorage *lpAttachmentStorage,
- struct saveObject *lpsSaveObj, unsigned int ulObjId, bool fNewItem,
- unsigned int ulSyncId, struct saveObject *lpsReturnObj,
- bool *lpfHaveChangeKey, FILETIME *lpftCreated, FILETIME *lpftModified)
- {
- ECRESULT er;
- std::string strInsertQuery;
- std::string strColName;
- std::string strQuery;
- struct propValArray *lpPropValArray = &lpsSaveObj->modProps;
- unsigned int ulParent = 0;
- unsigned int ulGrandParent = 0;
- unsigned int ulParentType = 0;
- unsigned int ulObjType = 0;
- unsigned int ulOwner = 0;
- unsigned int ulFlags = 0;
- unsigned int ulAffected = 0;
- unsigned int ulPropInserts = 0;
- gsoap_size_t nMVItems;
- unsigned long long ullIMAP = 0;
- std::set<unsigned int> setInserted;
- GUID sGuidServer;
- std::list<ULONG> lstObjIds;
- ULONG ulInstanceId = 0;
- ULONG ulInstanceTag = 0;
- bool bAttachmentStored = false;
- entryId sUserId;
- std::string strUsername;
- ECStringCompat stringCompat(lpecSession->GetCapabilities() & KOPANO_CAP_UNICODE);
- std::string strColData;
-
- std::string strInsert;
- std::string strInsertTProp;
-
- SOURCEKEY sSourceKey;
- SOURCEKEY sParentSourceKey;
- DB_RESULT lpDBResult;
- if (lpAttachmentStorage == nullptr)
- return KCERR_INVALID_PARAMETER;
- if (lpfHaveChangeKey)
- *lpfHaveChangeKey = false;
- er = g_lpSessionManager->GetServerGUID(&sGuidServer);
- if (er != erSuccess)
- return er;
- er = g_lpSessionManager->GetCacheManager()->GetObject(ulObjId, &ulParent, &ulOwner, &ulFlags, &ulObjType);
- if (er != erSuccess)
- return er;
- if(ulObjType != MAPI_STORE){
- er = g_lpSessionManager->GetCacheManager()->GetObject(ulParent, &ulGrandParent, NULL, NULL, &ulParentType);
- if(er != erSuccess)
- return er;
- }
- if(ulObjType == MAPI_FOLDER && !ulSyncId) {
- for (gsoap_size_t i = 0; i < lpPropValArray->__size; ++i) {
- // Check whether the requested folder name already exists
- if(lpPropValArray->__ptr[i].ulPropTag == PR_DISPLAY_NAME ) {
- if(lpPropValArray->__ptr[i].Value.lpszA == NULL)
- break; // Name property found, but name isn't present. This is broken, so skip this.
- strQuery = "SELECT hierarchy.id FROM hierarchy JOIN properties ON hierarchy.id = properties.hierarchyid WHERE hierarchy.parent=" + stringify(ulParent) + " AND hierarchy.type="+stringify(MAPI_FOLDER)+" AND hierarchy.flags & " + stringify(MSGFLAG_DELETED)+ "=0 AND properties.tag=" + stringify(KOPANO_TAG_DISPLAY_NAME) + " AND properties.val_string = '" + lpDatabase->Escape(lpPropValArray->__ptr[i].Value.lpszA) + "' AND properties.type="+stringify(PT_STRING8)+" AND hierarchy.id!=" + stringify(ulObjId) + " LIMIT 1";
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if(er != erSuccess) {
- ec_log_err("WriteProps(): DoSelect failed %x", er);
- return er;
- }
- if(lpDatabase->GetNumRows(lpDBResult) > 0) {
- ec_log_err("WriteProps(): Folder already exists while putting folder");
- return KCERR_COLLISION;
- }
- break;
- }
- }// for(...)
- }
- // If we have a recipient, remove the old one. The client does not send a diff of props because ECMemTable cannot do this
- if (!fNewItem && (ulObjType == MAPI_MAILUSER || ulObjType == MAPI_DISTLIST)) {
- strQuery = "DELETE FROM properties WHERE hierarchyid=" + stringify(ulObjId);
- er = lpDatabase->DoDelete(strQuery);
- if(er != erSuccess)
- return er;
- strQuery = "DELETE FROM mvproperties WHERE hierarchyid=" + stringify (ulObjId);
- er = lpDatabase->DoDelete(strQuery);
- if(er != erSuccess)
- return er;
- if (ulObjType != lpsSaveObj->ulObjType) {
- strQuery = "UPDATE hierarchy SET type=" +stringify(lpsSaveObj->ulObjType) +" WHERE id=" + stringify (ulObjId);
- er = lpDatabase->DoUpdate(strQuery);
- if(er != erSuccess)
- return er;
- // Switch the type so the cache will be updated at the end.
- ulObjType = lpsSaveObj->ulObjType;
- }
- }
- /* FIXME: Support multiple InstanceIds */
- if (lpsSaveObj->lpInstanceIds && lpsSaveObj->lpInstanceIds->__size) {
- GUID sGuidTmp;
- if (lpsSaveObj->lpInstanceIds->__size > 1)
- return KCERR_UNKNOWN_INSTANCE_ID;
- er = SIEntryIDToID(&lpsSaveObj->lpInstanceIds->__ptr[0], &sGuidTmp, (unsigned int*)&ulInstanceId, (unsigned int*)&ulInstanceTag);
- if (er != erSuccess)
- return er;
- /* Server GUID must always match */
- if (memcmp(&sGuidTmp, &sGuidServer, sizeof(sGuidTmp)) != 0)
- return KCERR_UNKNOWN_INSTANCE_ID;
- /* The attachment should at least exist */
- if (!lpAttachmentStorage->ExistAttachmentInstance(ulInstanceId))
- return KCERR_UNKNOWN_INSTANCE_ID;
- /*
- * Check if we have access to the instance which is being referenced,
- * a user has access to an instance when he is administrator or owns at
- * least one reference to the instance.
- * For security we won't announce the difference between not finding the instance
- * or not having access to it.
- */
- if (lpecSession->GetSecurity()->GetAdminLevel() != ADMIN_LEVEL_SYSADMIN) {
- er = lpAttachmentStorage->GetSingleInstanceParents(ulInstanceId, &lstObjIds);
- if (er != erSuccess)
- return er;
- er = KCERR_UNKNOWN_INSTANCE_ID;
- for (const auto i : lstObjIds)
- if (lpecSession->GetSecurity()->CheckPermission(i, ecSecurityRead) == erSuccess) {
- er = erSuccess;
- break;
- }
- if (er != erSuccess)
- return er;
- }
- er = lpAttachmentStorage->SaveAttachment(ulObjId, ulInstanceTag, !fNewItem, ulInstanceId, &ulInstanceId);
- if (er != erSuccess)
- return er;
- lpsReturnObj->lpInstanceIds = s_alloc<entryList>(soap);
- lpsReturnObj->lpInstanceIds->__size = 1;
- lpsReturnObj->lpInstanceIds->__ptr = s_alloc<entryId>(soap, lpsReturnObj->lpInstanceIds->__size);
- er = SIIDToEntryID(soap, &sGuidServer, ulInstanceId, ulInstanceTag, &lpsReturnObj->lpInstanceIds->__ptr[0]);
- if (er != erSuccess) {
- lpsReturnObj->lpInstanceIds = NULL;
- return er;
- }
- /* Either by instanceid or attachment data, we have stored the attachment */
- bAttachmentStored = true;
- }
- // Write the properties
- for (gsoap_size_t i = 0; i < lpPropValArray->__size; ++i) {
- // Check if we already inserted this property tag. We only accept the first.
- auto iterInserted = setInserted.find(lpPropValArray->__ptr[i].ulPropTag);
- if (iterInserted != setInserted.cend())
- continue;
- // Check if we actually need to write this property. The client may send us properties
- // that we generate, so we don't need to save them
- if(ECGenProps::IsPropRedundant(lpPropValArray->__ptr[i].ulPropTag, ulObjType) == erSuccess)
- continue;
- // Some properties may only be saved on the first save (fNewItem == TRUE) for folders, stores and messages
- if(!fNewItem && (ulObjType == MAPI_MESSAGE || ulObjType == MAPI_FOLDER || ulObjType == MAPI_STORE)) {
- switch(lpPropValArray->__ptr[i].ulPropTag) {
- case PR_LAST_MODIFICATION_TIME:
- case PR_MESSAGE_FLAGS:
- case PR_SEARCH_KEY:
- case PR_LAST_MODIFIER_NAME:
- case PR_LAST_MODIFIER_ENTRYID:
- if (ulSyncId == 0)
- // Only on first write, unless sent by ICS
- continue;
- break;
- case PR_SOURCE_KEY:
- if (ulParentType == MAPI_FOLDER)
- // Only on first write, unless message-in-message
- continue;
- break;
- }
- }
- if (lpPropValArray->__ptr[i].ulPropTag == PR_LAST_MODIFIER_NAME_W ||
- lpPropValArray->__ptr[i].ulPropTag == PR_LAST_MODIFIER_ENTRYID)
- if(!fNewItem && ulSyncId == 0)
- continue;
- // Same goes for flags in PR_MESSAGE_FLAGS
- if(lpPropValArray->__ptr[i].ulPropTag == PR_MESSAGE_FLAGS) {
- if (ulSyncId == 0)
- // Normalize PR_MESSAGE_FLAGS so that the user cannot set things like MSGFLAG_ASSOCIATED
- lpPropValArray->__ptr[i].Value.ul = (lpPropValArray->__ptr[i].Value.ul & (MSGFLAG_SETTABLE_BY_USER | MSGFLAG_SETTABLE_BY_SPOOLER)) | ulFlags;
- else
- // Normalize PR_MESSAGE_FLAGS so that the user cannot change flags that are also
- // stored in the hierarchy table.
- lpPropValArray->__ptr[i].Value.ul = (lpPropValArray->__ptr[i].Value.ul & ~MSGFLAG_UNSETTABLE) | ulFlags;
- }
- // Make sure we dont have a colliding PR_SOURCE_KEY. This can happen if a user imports an exported message for example.
- if(lpPropValArray->__ptr[i].ulPropTag == PR_SOURCE_KEY)
- {
- // Remove any old (deleted) indexed property if it's there
- er = RemoveStaleIndexedProp(lpDatabase, PR_SOURCE_KEY, lpPropValArray->__ptr[i].Value.bin->__ptr, lpPropValArray->__ptr[i].Value.bin->__size);
- if(er != erSuccess) {
- // Unable to remove the (old) sourcekey in use. This means that it is in use by some other object. We just skip
- // the property so that it is generated later as a new random sourcekey
- er = erSuccess;
- continue;
- }
-
- // Insert sourcekey, use REPLACE because createfolder already created a sourcekey.
- // Because there is a non-primary unique key on the
- // val_binary part of the table, it will fail if the source key is duplicate.
- strQuery = "REPLACE INTO indexedproperties(hierarchyid,tag,val_binary) VALUES ("+stringify(ulObjId)+","+stringify(PROP_ID(PR_SOURCE_KEY))+","+lpDatabase->EscapeBinary(lpPropValArray->__ptr[i].Value.bin->__ptr, lpPropValArray->__ptr[i].Value.bin->__size)+")";
- er = lpDatabase->DoInsert(strQuery);
- if(er != erSuccess)
- return er;
- setInserted.insert(lpPropValArray->__ptr[i].ulPropTag);
- // Remember the source key in the cache
- g_lpSessionManager->GetCacheManager()->SetObjectProp(PROP_ID(PR_SOURCE_KEY), lpPropValArray->__ptr[i].Value.bin->__size, lpPropValArray->__ptr[i].Value.bin->__ptr, ulObjId);
- continue;
- }
- // attachments are in the blob too
- if (lpPropValArray->__ptr[i].ulPropTag == PR_ATTACH_DATA_BIN || lpPropValArray->__ptr[i].ulPropTag == PR_EC_IMAP_EMAIL) {
- /*
- * bAttachmentStored indicates we already processed the attachment.
- * this could happen when the user provided the instance ID but as
- * backup also send the PR_ATTACH_DATA_BIN.
- */
- if (bAttachmentStored)
- continue;
- er = lpAttachmentStorage->SaveAttachment(ulObjId, PROP_ID(lpPropValArray->__ptr[i].ulPropTag), !fNewItem,
- lpPropValArray->__ptr[i].Value.bin->__size,
- lpPropValArray->__ptr[i].Value.bin->__ptr,
- &ulInstanceId);
- if (er != erSuccess)
- return er;
- lpsReturnObj->lpInstanceIds = s_alloc<entryList>(soap);
- lpsReturnObj->lpInstanceIds->__size = 1;
- lpsReturnObj->lpInstanceIds->__ptr = s_alloc<entryId>(soap, lpsReturnObj->lpInstanceIds->__size);
- er = SIIDToEntryID(soap, &sGuidServer, ulInstanceId, PROP_ID(lpPropValArray->__ptr[i].ulPropTag), &lpsReturnObj->lpInstanceIds->__ptr[0]);
- if (er != erSuccess) {
- lpsReturnObj->lpInstanceIds = NULL;
- return er;
- }
- continue;
- }
-
- // We have to return the values for PR_LAST_MODIFICATION_TIME and PR_CREATION_TIME
- if (lpPropValArray->__ptr[i].ulPropTag == PR_LAST_MODIFICATION_TIME) {
- lpftModified->dwHighDateTime = lpPropValArray->__ptr[i].Value.hilo->hi;
- lpftModified->dwLowDateTime = lpPropValArray->__ptr[i].Value.hilo->lo;
- }
-
- if (lpPropValArray->__ptr[i].ulPropTag == PR_CREATION_TIME) {
- lpftCreated->dwHighDateTime = lpPropValArray->__ptr[i].Value.hilo->hi;
- lpftCreated->dwLowDateTime = lpPropValArray->__ptr[i].Value.hilo->lo;
- }
-
- if (lpfHaveChangeKey && lpPropValArray->__ptr[i].ulPropTag == PR_CHANGE_KEY)
- *lpfHaveChangeKey = true;
- if (PROP_TYPE(lpPropValArray->__ptr[i].ulPropTag) & MV_FLAG) {
- // Make sure string prop_types become PT_MV_STRING8
- if (PROP_TYPE(lpPropValArray->__ptr[i].ulPropTag) == PT_MV_UNICODE)
- lpPropValArray->__ptr[i].ulPropTag = CHANGE_PROP_TYPE(lpPropValArray->__ptr[i].ulPropTag, PT_MV_STRING8);
- //Write mv properties
- nMVItems = GetMVItemCount(&lpPropValArray->__ptr[i]);
- for (gsoap_size_t j = 0; j < nMVItems; ++j) {
- assert(PROP_TYPE(lpPropValArray->__ptr[i].ulPropTag) != PT_MV_UNICODE);
- // Make sure string propvals are in UTF8
- if (PROP_TYPE(lpPropValArray->__ptr[i].ulPropTag) == PT_MV_STRING8)
- lpPropValArray->__ptr[i].Value.mvszA.__ptr[j] = stringCompat.to_UTF8(soap, lpPropValArray->__ptr[i].Value.mvszA.__ptr[j]);
- er = CopySOAPPropValToDatabaseMVPropVal(&lpPropValArray->__ptr[i], j, strColName, strColData, lpDatabase);
- if(er != erSuccess)
- continue;
- strQuery = "REPLACE INTO mvproperties(hierarchyid,orderid,tag,type," + strColName + ") VALUES(" + stringify(ulObjId) + "," + stringify(j) + "," + stringify(PROP_ID(lpPropValArray->__ptr[i].ulPropTag)) + "," + stringify(PROP_TYPE(lpPropValArray->__ptr[i].ulPropTag)) + "," + strColData + ")";
- er = lpDatabase->DoInsert(strQuery, NULL, &ulAffected);
- if(er != erSuccess)
- return er;
- // According to the MySQL documentation (http://dev.mysql.com/doc/refman/5.0/en/mysql-affected-rows.html) ulAffected rows
- // will be 2 if a row was replaced.
- // Interestingly, I (MSw) have observer in a consecutive call to the above replace query, where in both cases an old value
- // was replaced with a new value, that it returned 1 the first time and 2 the second time.
- // We'll allow both though.
- if(ulAffected != 1 && ulAffected != 2) {
- ec_log_err("Unable to update MVProperties during save: %d, object id: %d", ulAffected, ulObjId);
- return KCERR_DATABASE_ERROR;
- }
- }
- if(!fNewItem) {
- strQuery = "DELETE FROM mvproperties WHERE hierarchyid=" + stringify (ulObjId) +
- " AND tag=" + stringify(PROP_ID(lpPropValArray->__ptr[i].ulPropTag)) +
- " AND type=" + stringify(PROP_TYPE(lpPropValArray->__ptr[i].ulPropTag)) +
- " AND orderid >= " + stringify(nMVItems);
- er = lpDatabase->DoDelete(strQuery);
- if(er != erSuccess)
- return er;
- }
- } else {
- // Make sure string propvals are in UTF8 with tag PT_STRING8
- if (PROP_TYPE(lpPropValArray->__ptr[i].ulPropTag) == PT_STRING8 || PROP_TYPE(lpPropValArray->__ptr[i].ulPropTag) == PT_UNICODE) {
- lpPropValArray->__ptr[i].Value.lpszA = stringCompat.to_UTF8(soap, lpPropValArray->__ptr[i].Value.lpszA);
- lpPropValArray->__ptr[i].ulPropTag = CHANGE_PROP_TYPE(lpPropValArray->__ptr[i].ulPropTag, PT_STRING8);
- }
-
- // Write the property to the database
- er = WriteSingleProp(lpDatabase, ulObjId, ulParent, &lpPropValArray->__ptr[i], false, lpDatabase->GetMaxAllowedPacket(), strInsert);
- if (er == KCERR_TOO_BIG) {
- er = lpDatabase->DoInsert(strInsert);
- if (er == erSuccess) {
- strInsert.clear();
- er = WriteSingleProp(lpDatabase, ulObjId, ulParent, &lpPropValArray->__ptr[i], false, lpDatabase->GetMaxAllowedPacket(), strInsert);
- }
- }
- if(er != erSuccess)
- return er;
-
- // Write the property to the table properties if needed (only on objects in folders (folders, messages), and if the property is being tracked here.
- if(ulParentType == MAPI_FOLDER) {
- // Cache the written value
- sObjectTableKey key(ulObjId,0);
- g_lpSessionManager->GetCacheManager()->SetCell(&key, lpPropValArray->__ptr[i].ulPropTag, &lpPropValArray->__ptr[i]);
-
- if (0) {
- // FIXME do we need this code? Currently we get always a deferredupdate!
- // Please also update streamutil.cpp:DeserializeProps
- er = WriteSingleProp(lpDatabase, ulObjId, ulParent, &lpPropValArray->__ptr[i], true, lpDatabase->GetMaxAllowedPacket(), strInsertTProp);
- if (er == KCERR_TOO_BIG) {
- er = lpDatabase->DoInsert(strInsertTProp);
- if (er == erSuccess) {
- strInsertTProp.clear();
- er = WriteSingleProp(lpDatabase, ulObjId, ulParent, &lpPropValArray->__ptr[i], true, lpDatabase->GetMaxAllowedPacket(), strInsertTProp);
- }
- }
- if(er != erSuccess)
- return er;
- }
- }
- }
- setInserted.insert(lpPropValArray->__ptr[i].ulPropTag);
- } // for (i = 0; i < lpPropValArray->__size; ++i)
- if(!strInsert.empty()) {
- er = lpDatabase->DoInsert(strInsert);
- if(er != erSuccess)
- return er;
- }
-
- if(ulParentType == MAPI_FOLDER) {
- if(0) {
- /* Modification, just directly write the tproperties
- * The idea behind this is that we'd need some serious random-access reads to properties later when flushing
- * tproperties, and we have the properties in memory now anyway. Also, modifications usually are just a few properties, causing
- * only minor random I/O on tproperties, and a tproperties flush reads all the properties, not just the modified ones.
- */
- if(!strInsertTProp.empty()) {
- er = lpDatabase->DoInsert(strInsertTProp);
- if(er != erSuccess)
- return er;
- }
- } else {
- // Instead of writing directly to tproperties, save a delayed write request.
- if(ulParent != CACHE_NO_PARENT) {
- er = ECTPropsPurge::AddDeferredUpdateNoPurge(lpDatabase, ulParent, 0, ulObjId);
- if(er != erSuccess)
- return er;
- }
- }
- }
-
- // Insert the properties
- if(ulPropInserts > 0) {
- er = lpDatabase->DoInsert(strInsertQuery, NULL, NULL);
- if(er != erSuccess)
- return er;
- }
-
- if(ulObjType == MAPI_MESSAGE) {
- auto iterInserted = setInserted.find(PR_LAST_MODIFIER_NAME_W);
- // update the PR_LAST_MODIFIER_NAME and PR_LAST_MODIFIER_ENTRYID
- if (iterInserted == setInserted.cend()) {
- er = GetABEntryID(lpecSession->GetSecurity()->GetUserId(), soap, &sUserId);
- if(er != erSuccess)
- return er;
-
- lpecSession->GetSecurity()->GetUsername(&strUsername);
- strQuery = "REPLACE INTO properties(hierarchyid, tag, type, val_string, val_binary) VALUES(" +
- stringify(ulObjId) + "," +
- stringify(PROP_ID(PR_LAST_MODIFIER_NAME_A)) + "," +
- stringify(PROP_TYPE(PR_LAST_MODIFIER_NAME_A)) + ",\"" +
- lpDatabase->Escape(strUsername) +
- "\", NULL), (" +
- stringify(ulObjId) + "," +
- stringify(PROP_ID(PR_LAST_MODIFIER_ENTRYID)) + "," +
- stringify(PROP_TYPE(PR_LAST_MODIFIER_ENTRYID)) +
- ", NULL, " +
- lpDatabase->EscapeBinary(sUserId.__ptr, sUserId.__size) + ")";
- er = lpDatabase->DoInsert(strQuery);
- if(er != erSuccess)
- return er;
- sObjectTableKey key(ulObjId,0);
- struct propVal sPropVal;
- sPropVal.ulPropTag = PR_LAST_MODIFIER_NAME_A;
- sPropVal.Value.lpszA = (char*)strUsername.c_str();
- sPropVal.__union = SOAP_UNION_propValData_lpszA;
- g_lpSessionManager->GetCacheManager()->SetCell(&key, PR_LAST_MODIFIER_NAME_A, &sPropVal);
- sPropVal.ulPropTag = PR_LAST_MODIFIER_ENTRYID;
- sPropVal.Value.bin = &sUserId;
- sPropVal.__union = SOAP_UNION_propValData_bin;
- g_lpSessionManager->GetCacheManager()->SetCell(&key, PR_LAST_MODIFIER_ENTRYID, &sPropVal);
- }
- }
- if(!fNewItem) {
- // Update, so write the modtime and clear UNMODIFIED flag
- if (setInserted.find(PR_LAST_MODIFICATION_TIME) == setInserted.cend()) {
- struct propVal sProp;
- struct hiloLong sHilo;
- struct sObjectTableKey key;
-
- FILETIME ft;
-
- sProp.ulPropTag = PR_LAST_MODIFICATION_TIME;
- sProp.Value.hilo = &sHilo;
- sProp.__union = SOAP_UNION_propValData_hilo;
- UnixTimeToFileTime(time(NULL), &sProp.Value.hilo->hi, &sProp.Value.hilo->lo);
-
- ft.dwHighDateTime = sProp.Value.hilo->hi;
- ft.dwLowDateTime = sProp.Value.hilo->lo;
-
- er = WriteProp(lpDatabase, ulObjId, ulParent, &sProp);
- if (er != erSuccess)
- return er;
-
- *lpftModified = ft;
-
- // Add to cache
- key.ulObjId = ulObjId;
- key.ulOrderId = 0;
- if(ulParentType == MAPI_FOLDER)
- g_lpSessionManager->GetCacheManager()->SetCell(&key, PR_LAST_MODIFICATION_TIME, &sProp);
-
- }
-
- if(ulObjType == MAPI_MESSAGE) {
- // Unset MSGFLAG_UNMODIFIED
- strQuery = "UPDATE properties SET val_ulong=val_ulong&"+stringify(~MSGFLAG_UNMODIFIED) + " WHERE hierarchyid=" + stringify(ulObjId)+ " AND tag=" + stringify(PROP_ID(PR_MESSAGE_FLAGS)) + " AND type=" + stringify(PROP_TYPE(PR_MESSAGE_FLAGS));
- er = lpDatabase->DoUpdate(strQuery);
- if(er != erSuccess)
- return er;
- // Update cache
- if(ulParentType == MAPI_FOLDER)
- g_lpSessionManager->GetCacheManager()->UpdateCell(ulObjId, PR_MESSAGE_FLAGS, (unsigned int)MSGFLAG_UNMODIFIED, 0);
- }
- } else {
- // New item, make sure PR_CREATION_TIME and PR_LAST_MODIFICATION_TIME are available
- if (setInserted.find(PR_LAST_MODIFICATION_TIME) == setInserted.cend() ||
- setInserted.find(PR_CREATION_TIME) == setInserted.cend()) {
- struct propVal sPropTime;
- FILETIME ft;
- unsigned int tags[] = { PR_LAST_MODIFICATION_TIME, PR_CREATION_TIME };
-
- // Get current time
- UnixTimeToFileTime(time(NULL), &ft);
-
- sPropTime.Value.hilo = s_alloc<struct hiloLong>(soap);
- sPropTime.Value.hilo->hi = ft.dwHighDateTime;
- sPropTime.Value.hilo->lo = ft.dwLowDateTime;
- sPropTime.__union = SOAP_UNION_propValData_hilo;
- // Same thing for both PR_LAST_MODIFICATION_TIME and PR_CREATION_TIME
- for (size_t i = 0; i < ARRAY_SIZE(tags); ++i) {
- if(setInserted.find(tags[i]) == setInserted.end()) {
- sObjectTableKey key;
- sPropTime.ulPropTag = tags[i];
-
- strQuery.clear();
- WriteSingleProp(lpDatabase, ulObjId, ulParent, &sPropTime, false, 0, strQuery);
- er = lpDatabase->DoInsert(strQuery);
- if(er != erSuccess)
- return er;
-
- strQuery.clear();
- WriteSingleProp(lpDatabase, ulObjId, ulParent, &sPropTime, true, 0, strQuery);
- er = lpDatabase->DoInsert(strQuery);
- if(er != erSuccess)
- return er;
- if(tags[i] == PR_LAST_MODIFICATION_TIME)
- *lpftModified = ft;
- if(tags[i] == PR_CREATION_TIME)
- *lpftCreated = ft;
-
- // Add to cache
- key.ulObjId = ulObjId;
- key.ulOrderId = 0;
- if(ulParentType == MAPI_FOLDER)
- g_lpSessionManager->GetCacheManager()->SetCell(&key, tags[i], &sPropTime);
- }
- }
- }
- }
- if(fNewItem && ulObjType == MAPI_MESSAGE) {
- // Add PR_SOURCE_KEY to new messages without a given PR_SOURCE_KEY
- // This isn't for folders, this done in the createfolder function
- auto iterInserted = setInserted.find(PR_SOURCE_KEY);
- if (iterInserted == setInserted.cend()) {
- er = lpecSession->GetNewSourceKey(&sSourceKey);
- if (er != erSuccess)
- return er;
- strQuery = "INSERT INTO indexedproperties(hierarchyid,tag,val_binary) VALUES(" + stringify(ulObjId) + "," + stringify(PROP_ID(PR_SOURCE_KEY)) + "," + lpDatabase->EscapeBinary(sSourceKey, sSourceKey.size()) + ")";
- er = lpDatabase->DoInsert(strQuery);
- if(er != erSuccess)
- return er;
- g_lpSessionManager->GetCacheManager()->SetObjectProp(PROP_ID(PR_SOURCE_KEY), sSourceKey.size(), sSourceKey, ulObjId);
- }
- if(ulParentType == MAPI_FOLDER) {
- // Add a PR_EC_IMAP_ID to the newly created message
- sObjectTableKey key(ulObjId, 0);
- struct propVal sProp;
- er = g_lpSessionManager->GetNewSequence(ECSessionManager::SEQ_IMAP, &ullIMAP);
- if(er != erSuccess)
- return er;
-
- strQuery = "INSERT INTO properties(hierarchyid, tag, type, val_ulong) VALUES(" +
- stringify(ulObjId) + "," +
- stringify(PROP_ID(PR_EC_IMAP_ID)) + "," +
- stringify(PROP_TYPE(PR_EC_IMAP_ID)) + "," +
- stringify(ullIMAP) +
- ")";
- er = lpDatabase->DoInsert(strQuery);
- if(er != erSuccess)
- return er;
- sProp.ulPropTag = PR_EC_IMAP_ID;
- sProp.Value.ul = ullIMAP;
- sProp.__union = SOAP_UNION_propValData_ul;
- er = g_lpSessionManager->GetCacheManager()->SetCell(&key, PR_EC_IMAP_ID, &sProp);
- if (er != erSuccess)
- return er;
- }
- }
- if(fNewItem && ulParentType == MAPI_FOLDER)
- // Since we have written a new item, we know that the cache contains *all* properties for this object
- g_lpSessionManager->GetCacheManager()->SetComplete(ulObjId);
-
- // We know the values for the object cache, so add them here
- if(ulObjType == MAPI_MESSAGE || ulObjType == MAPI_FOLDER)
- g_lpSessionManager->GetCacheManager()->SetObject(ulObjId, ulParent, ulOwner, ulFlags, ulObjType);
- return erSuccess;
- }
- // You need to check the permissions before you call this function
- static ECRESULT DeleteProps(ECSession *lpecSession, ECDatabase *lpDatabase,
- ULONG ulObjId, struct propTagArray *lpsPropTags,
- ECAttachmentStorage *at_storage)
- {
- ECRESULT er;
- std::string strQuery;
- sObjectTableKey key;
- struct propVal sPropVal;
- // block removal of certain properties (per object type?), properties handled in WriteProps
- unsigned int ulPropTags[] = {PR_MESSAGE_FLAGS, PR_CREATION_TIME, PR_LAST_MODIFICATION_TIME, PR_LAST_MODIFIER_ENTRYID, PR_LAST_MODIFIER_NAME_W, PR_SOURCE_KEY};
- std::set<unsigned int> setNotDeletable(ulPropTags, ulPropTags + ARRAY_SIZE(ulPropTags));
- // Delete one or more properties of an object
- for (gsoap_size_t i = 0; i < lpsPropTags->__size; ++i) {
- if (setNotDeletable.find(lpsPropTags->__ptr[i]) != setNotDeletable.cend())
- continue;
- if((lpsPropTags->__ptr[i]&MV_FLAG) == 0)
- strQuery = "DELETE FROM properties WHERE hierarchyid="+stringify(ulObjId)+" AND tag="+stringify(PROP_ID(lpsPropTags->__ptr[i]));
- else // mvprops
- strQuery = "DELETE FROM mvproperties WHERE hierarchyid="+stringify(ulObjId)+" AND tag="+stringify(PROP_ID(lpsPropTags->__ptr[i]) );
- er = lpDatabase->DoDelete(strQuery);
- if(er != erSuccess)
- return er;
-
- // Remove from tproperties
- if((lpsPropTags->__ptr[i]&MV_FLAG) == 0) {
- strQuery = "DELETE FROM tproperties WHERE hierarchyid="+stringify(ulObjId)+" AND tag="+stringify(PROP_ID(lpsPropTags->__ptr[i]));
- er = lpDatabase->DoDelete(strQuery);
- if(er != erSuccess)
- return er;
- }
- // Remove eml attachment
- if (lpsPropTags->__ptr[i] == PR_EC_IMAP_EMAIL) {
- std::list<ULONG> at_list;
- at_list.push_back(ulObjId);
- at_storage->DeleteAttachments(at_list);
- }
- // Update cache with NOT_FOUND for this property
- key.ulObjId = ulObjId;
- key.ulOrderId = 0;
- sPropVal.ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(lpsPropTags->__ptr[i]));
- sPropVal.Value.ul = KCERR_NOT_FOUND;
- sPropVal.__union = SOAP_UNION_propValData_ul;
- g_lpSessionManager->GetCacheManager()->SetCell(&key, lpsPropTags->__ptr[i], &sPropVal);
- }
- return erSuccess;
- }
- static unsigned int SaveObject(struct soap *soap, ECSession *lpecSession,
- ECDatabase *lpDatabase, ECAttachmentStorage *lpAttachmentStorage,
- unsigned int ulStoreId, unsigned int ulParentObjId,
- unsigned int ulParentType, unsigned int ulFlags, unsigned int ulSyncId,
- struct saveObject *lpsSaveObj, struct saveObject *lpsReturnObj,
- unsigned int ulLevel, bool *lpfHaveChangeKey = NULL)
- {
- ECRESULT er = erSuccess;
- ALLOC_DBRESULT();
- int n;
- unsigned int ulParentObjType = 0;
- unsigned int ulSize = 0;
- unsigned int ulObjId = 0;
- bool fNewItem = false;
- bool fHasAttach = false;
- bool fGenHasAttach = false;
- ECListDeleteItems lstDeleteItems;
- ECListDeleteItems lstDeleted;
- FILETIME ftCreated = { 0,0 };
- FILETIME ftModified = { 0,0 };
- if (ulLevel <= 0) {
- er = KCERR_TOO_COMPLEX;
- goto exit;
- }
- // reset return object
- lpsReturnObj->__size = 0;
- lpsReturnObj->__ptr = NULL;
- lpsReturnObj->delProps.__size = 0;
- lpsReturnObj->delProps.__ptr = NULL;
- lpsReturnObj->modProps.__size = 0;
- lpsReturnObj->modProps.__ptr = NULL;
- lpsReturnObj->bDelete = false;
- lpsReturnObj->ulClientId = lpsSaveObj->ulClientId;
- lpsReturnObj->ulServerId = lpsSaveObj->ulServerId;
- lpsReturnObj->ulObjType = lpsSaveObj->ulObjType;
- lpsReturnObj->lpInstanceIds = NULL;
- if (lpsSaveObj->ulServerId == 0) {
- if (ulParentObjId == 0) {
- er = KCERR_INVALID_PARAMETER;
- goto exit;
- }
- er = CreateObject(lpecSession, lpDatabase, ulParentObjId, ulParentType, lpsSaveObj->ulObjType, ulFlags, &lpsReturnObj->ulServerId);
- if (er != erSuccess)
- goto exit;
- fNewItem = true;
- ulObjId = lpsReturnObj->ulServerId;
- } else {
- ulObjId = lpsSaveObj->ulServerId;
- }
- if (lpsSaveObj->bDelete) {
- // make list of all children object IDs in std::list<int> ?
- ECListInt lstDel;
- lstDel.push_back(lpsSaveObj->ulServerId);
- // we always hard delete, because we can only delete submessages here
- // make sure we also delete message-in-message attachments, so all message related flags are on
- ULONG ulDelFlags = EC_DELETE_CONTAINER | EC_DELETE_MESSAGES | EC_DELETE_RECIPIENTS | EC_DELETE_ATTACHMENTS | EC_DELETE_HARD_DELETE;
- // Collect recursive parent objects, validate item and check the permissions
- er = ExpandDeletedItems(lpecSession, lpDatabase, &lstDel, ulDelFlags, false, &lstDeleteItems);
- if (er != erSuccess) {
- assert(false);
- goto exit;
- }
- er = DeleteObjectHard(lpecSession, lpDatabase, lpAttachmentStorage, ulDelFlags, lstDeleteItems, true, lstDeleted);
- if (er != erSuccess) {
- assert(false);
- goto exit;
- }
- er = DeleteObjectStoreSize(lpecSession, lpDatabase, ulDelFlags, lstDeleted);
- if (er != erSuccess) {
- assert(false);
- goto exit;
- }
- er = DeleteObjectCacheUpdate(lpecSession, ulDelFlags, lstDeleted);
- if (er != erSuccess) {
- assert(false);
- goto exit;
- }
- // object deleted, so we're done
- goto exit;
- }
- // ------
- // the following code is only for objects that still exist
- // ------
- {
- // Do not delete properties if this is a new object: this avoids any delete queries that cause unnecessary locks on the tables
- if (lpsSaveObj->delProps.__size > 0 && !fNewItem) {
- er = DeleteProps(lpecSession, lpDatabase, lpsReturnObj->ulServerId, &lpsSaveObj->delProps, lpAttachmentStorage);
- if (er != erSuccess)
- goto exit;
- }
- if (lpsSaveObj->modProps.__size > 0) {
- er = WriteProps(soap, lpecSession, lpDatabase, lpAttachmentStorage, lpsSaveObj, lpsReturnObj->ulServerId, fNewItem, ulSyncId, lpsReturnObj, lpfHaveChangeKey, &ftCreated, &ftModified);
- if (er != erSuccess)
- goto exit;
- }
- // check children
- if (lpsSaveObj->__size > 0) {
- lpsReturnObj->__size = lpsSaveObj->__size;
- lpsReturnObj->__ptr = s_alloc<struct saveObject>(soap, lpsReturnObj->__size);
- for (gsoap_size_t i = 0; i < lpsSaveObj->__size; ++i) {
- er = SaveObject(soap, lpecSession, lpDatabase, lpAttachmentStorage, ulStoreId, /*myself as parent*/lpsReturnObj->ulServerId, lpsReturnObj->ulObjType, 0, ulSyncId, &lpsSaveObj->__ptr[i], &lpsReturnObj->__ptr[i], lpsReturnObj->ulObjType == MAPI_MESSAGE ? ulLevel-1 : ulLevel);
- if (er != erSuccess)
- goto exit;
- }
- }
-
- if (lpsReturnObj->ulObjType == MAPI_MESSAGE) {
- // Generate properties that we need to generate (PR_HASATTACH, PR_LAST_MODIFICATION_TIME, PR_CREATION_TIME)
- if (fNewItem) {
- // We have to write PR_HASTTACH since it is a new object
- fGenHasAttach = true;
- // We can generate PR_HASATTACH from the passed object data
- for (gsoap_size_t i = 0; i < lpsSaveObj->__size; ++i)
- if(lpsSaveObj->__ptr[i].ulObjType == MAPI_ATTACH) {
- fHasAttach = true;
- break;
- }
- } else {
- // Modified object. Only change PR_HASATTACH if something has changed
- for (gsoap_size_t i = 0; i < lpsSaveObj->__size; ++i)
- if(lpsSaveObj->__ptr[i].ulObjType == MAPI_ATTACH && (lpsSaveObj->__ptr[i].bDelete || lpsSaveObj->__ptr[i].ulServerId == 0)) {
- // An attachment was deleted or added in this call
- fGenHasAttach = true;
- break;
- }
- if(fGenHasAttach) {
- // An attachment was added or deleted, check the database to see if any attachments are left.
- strQuery = "SELECT id FROM hierarchy WHERE parent=" + stringify(lpsReturnObj->ulServerId) + " AND type=" + stringify(MAPI_ATTACH) + " LIMIT 1";
-
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if(er != erSuccess)
- goto exit;
-
- fHasAttach = lpDatabase->GetNumRows(lpDBResult) > 0;
- }
- }
-
- if(fGenHasAttach) {
- // We have to generate/update PR_HASATTACH
- unsigned int ulParentTmp, ulOwnerTmp, ulFlagsTmp, ulTypeTmp;
-
- sObjectTableKey key(lpsReturnObj->ulServerId, 0);
- struct propVal sPropHasAttach;
- sPropHasAttach.ulPropTag = PR_HASATTACH;
- sPropHasAttach.Value.b = fHasAttach;
- sPropHasAttach.__union = SOAP_UNION_propValData_b;
-
- // Write in properties
- strQuery.clear();
- WriteSingleProp(lpDatabase, lpsReturnObj->ulServerId, ulParentObjId, &sPropHasAttach, false, 0, strQuery);
- er = lpDatabase->DoInsert(strQuery);
- if(er != erSuccess)
- goto exit;
-
- // Write in tproperties
- strQuery.clear();
- WriteSingleProp(lpDatabase, lpsReturnObj->ulServerId, ulParentObjId, &sPropHasAttach, true, 0, strQuery);
- er = lpDatabase->DoInsert(strQuery);
- if(er != erSuccess)
- goto exit;
-
- // Update cache, since it may have been written before by WriteProps with a possibly wrong value
- g_lpSessionManager->GetCacheManager()->SetCell(&key, PR_HASATTACH, &sPropHasAttach);
-
- // Update MSGFLAG_HASATTACH in the same way. We can assume PR_MESSAGE_FLAGS is already available, so we
- // just do an update (instead of REPLACE INTO)
- strQuery = (std::string)"UPDATE properties SET val_ulong = val_ulong " + (fHasAttach ? " | 0x10 " : " & ~0x10") + " WHERE hierarchyid = " + stringify(lpsReturnObj->ulServerId) + " AND tag = " + stringify(PROP_ID(PR_MESSAGE_FLAGS)) + " AND type = " + stringify(PROP_TYPE(PR_MESSAGE_FLAGS));
- er = lpDatabase->DoUpdate(strQuery);
- if(er != erSuccess)
- goto exit;
-
- // Update cache if it's actually in the cache
- if(g_lpSessionManager->GetCacheManager()->GetCell(&key, PR_MESSAGE_FLAGS, &sPropHasAttach, soap, false) == erSuccess) {
- sPropHasAttach.Value.ul &= ~MSGFLAG_HASATTACH;
- sPropHasAttach.Value.ul |= fHasAttach ? MSGFLAG_HASATTACH : 0;
- g_lpSessionManager->GetCacheManager()->SetCell(&key, PR_MESSAGE_FLAGS, &sPropHasAttach);
- }
- // More cache
- if (g_lpSessionManager->GetCacheManager()->GetObject(lpsReturnObj->ulServerId, &ulParentTmp, &ulOwnerTmp, &ulFlagsTmp, &ulTypeTmp) == erSuccess) {
- ulFlags &= ~MSGFLAG_HASATTACH;
- ulFlags |= fHasAttach ? MSGFLAG_HASATTACH : 0;
- g_lpSessionManager->GetCacheManager()->SetObject(lpsReturnObj->ulServerId, ulParentTmp, ulOwnerTmp, ulFlagsTmp, ulTypeTmp);
- }
- }
- }
- // 1. calc size of object, now that all children are saved.
- if (lpsReturnObj->ulObjType == MAPI_MESSAGE || lpsReturnObj->ulObjType == MAPI_ATTACH) {
- // Remove old size
- if (fNewItem != true && lpsReturnObj->ulObjType == MAPI_MESSAGE && ulParentType == MAPI_FOLDER) {
- if (GetObjectSize(lpDatabase, lpsReturnObj->ulServerId, &ulSize) == erSuccess)
- er = UpdateObjectSize(lpDatabase, ulStoreId, MAPI_STORE, UPDATE_SUB, ulSize);
- if (er != erSuccess)
- goto exit;
- }
- // Add new size
- if (CalculateObjectSize(lpDatabase, lpsReturnObj->ulServerId, lpsReturnObj->ulObjType, &ulSize) == erSuccess) {
- er = UpdateObjectSize(lpDatabase, lpsReturnObj->ulServerId, lpsReturnObj->ulObjType, UPDATE_SET, ulSize);
- if (er != erSuccess)
- goto exit;
- if (lpsReturnObj->ulObjType == MAPI_MESSAGE && ulParentType == MAPI_FOLDER) {
- er = UpdateObjectSize(lpDatabase, ulStoreId, MAPI_STORE, UPDATE_ADD, ulSize);
- if (er != erSuccess)
- goto exit;
- }
- } else {
- assert(false);
- }
- }
- // 2. find props to return
- // the server returns the following 4 properties, when the item is new:
- // PR_CREATION_TIME, (PR_PARENT_SOURCE_KEY, PR_SOURCE_KEY /type==5|3) (PR_RECORD_KEY /type==7|5|3)
- // TODO: recipients: PR_INSTANCE_KEY
- // it always sends the following property:
- // PR_LAST_MODIFICATION_TIME
- // currently, it always sends them all
- // we also can't send the PR_MESSAGE_SIZE and PR_MESSAGE_FLAGS, since the recursion is the wrong way around: attachments come later than the actual message
- // we can skip PR_ACCESS and PR_ACCESS_LEVEL because the client already inherited those from the parent
- // we need to alloc 2 properties for PR_CHANGE_KEY and PR_PREDECESSOR_CHANGE_LIST
- lpsReturnObj->delProps.__size = 8;
- lpsReturnObj->delProps.__ptr = s_alloc<unsigned int>(soap, lpsReturnObj->delProps.__size);
- lpsReturnObj->modProps.__size = 8;
- lpsReturnObj->modProps.__ptr = s_alloc<struct propVal>(soap, lpsReturnObj->modProps.__size);
- n = 0;
- // set the PR_RECORD_KEY
- // New clients generate the instance key, old clients don't. See if one was provided.
- if (lpsSaveObj->ulObjType == MAPI_ATTACH || lpsSaveObj->ulObjType == MAPI_MESSAGE || lpsSaveObj->ulObjType == MAPI_FOLDER) {
- bool bSkip = false;
- if (lpsSaveObj->ulObjType == MAPI_ATTACH) {
- for (gsoap_size_t i = 0; !bSkip && i < lpsSaveObj->modProps.__size; ++i)
- bSkip = lpsSaveObj->modProps.__ptr[i].ulPropTag == PR_RECORD_KEY;
- // @todo if we don't have a pr_record_key for an attachment, generate a guid for it like the client does
- }
- if (!bSkip && ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_RECORD_KEY, lpsSaveObj->ulServerId, 0, 0, ulParentObjId, lpsSaveObj->ulObjType, &lpsReturnObj->modProps.__ptr[n]) == erSuccess)
- lpsReturnObj->delProps.__ptr[n++] = PR_RECORD_KEY;
- }
- if (lpsSaveObj->ulObjType != MAPI_STORE) {
- er = g_lpSessionManager->GetCacheManager()->GetObject(ulParentObjId, NULL, NULL, NULL, &ulParentObjType);
- if(er != erSuccess)
- goto exit;
- }
- //PR_PARENT_SOURCE_KEY for folders and messages
- if (lpsSaveObj->ulObjType == MAPI_FOLDER || (lpsSaveObj->ulObjType == MAPI_MESSAGE && ulParentObjType == MAPI_FOLDER))
- {
- if(ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_PARENT_SOURCE_KEY, ulObjId, 0, 0, ulParentObjId, lpsSaveObj->ulObjType, &lpsReturnObj->modProps.__ptr[n]) == erSuccess)
- lpsReturnObj->delProps.__ptr[n++] = PR_PARENT_SOURCE_KEY;
- if(ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_SOURCE_KEY, ulObjId, 0, 0, ulParentObjId, lpsSaveObj->ulObjType, &lpsReturnObj->modProps.__ptr[n]) == erSuccess)
- lpsReturnObj->delProps.__ptr[n++] = PR_SOURCE_KEY;
- }
- // PR_LAST_MODIFICATION_TIME
- lpsReturnObj->delProps.__ptr[n] = PR_LAST_MODIFICATION_TIME;
- lpsReturnObj->modProps.__ptr[n].__union = SOAP_UNION_propValData_hilo;
- lpsReturnObj->modProps.__ptr[n].ulPropTag = PR_LAST_MODIFICATION_TIME;
- lpsReturnObj->modProps.__ptr[n].Value.hilo = s_alloc<hiloLong>(soap);
- lpsReturnObj->modProps.__ptr[n].Value.hilo->hi = ftModified.dwHighDateTime;
- lpsReturnObj->modProps.__ptr[n].Value.hilo->lo = ftModified.dwLowDateTime;
- ++n;
- if (fNewItem)
- {
- lpsReturnObj->delProps.__ptr[n] = PR_CREATION_TIME;
- lpsReturnObj->modProps.__ptr[n].__union = SOAP_UNION_propValData_hilo;
- lpsReturnObj->modProps.__ptr[n].ulPropTag = PR_CREATION_TIME;
- lpsReturnObj->modProps.__ptr[n].Value.hilo = s_alloc<hiloLong>(soap);
- lpsReturnObj->modProps.__ptr[n].Value.hilo->hi = ftCreated.dwHighDateTime;
- lpsReturnObj->modProps.__ptr[n].Value.hilo->lo = ftCreated.dwLowDateTime;
- ++n;
- }
- // set actual array size
- lpsReturnObj->delProps.__size = n;
- lpsReturnObj->modProps.__size = n;
- }
- exit:
- FreeDeletedItems(&lstDeleteItems);
- return er;
- }
- SOAP_ENTRY_START(saveObject, lpsLoadObjectResponse->er, entryId sParentEntryId, entryId sEntryId, struct saveObject *lpsSaveObj, unsigned int ulFlags, unsigned int ulSyncId, struct loadObjectResponse *lpsLoadObjectResponse)
- {
- USE_DATABASE();
- unsigned int ulStoreId = 0;
- unsigned int ulGrandParent = 0;
- unsigned int ulGrandParentType = 0;
- unsigned int ulParentObjId = 0;
- unsigned int ulParentObjType = 0;
- unsigned int ulObjType = lpsSaveObj->ulObjType;
- unsigned int ulObjFlags = 0;
- unsigned int ulPrevReadState = 0;
- unsigned int ulNewReadState = 0;
- SOURCEKEY sSourceKey;
- SOURCEKEY sParentSourceKey;
- struct saveObject sReturnObject;
- std::string strChangeKey;
- std::string strChangeList;
- BOOL fNewItem = false;
- bool fHaveChangeKey = false;
- unsigned int ulObjId = 0;
- struct propVal *pvCommitTime = NULL;
- object_ptr<ECAttachmentStorage> lpAttachmentStorage;
- er = CreateAttachmentStorage(lpDatabase, &~lpAttachmentStorage);
- if (er != erSuccess)
- goto exit;
- er = lpAttachmentStorage->Begin();
- if (er != erSuccess)
- goto exit;
-
- er = lpDatabase->Begin();
- if (er != erSuccess)
- goto exit;
-
- if (!sParentEntryId.__ptr) {
- // saveObject is called on the store itself (doesn't have a parent)
- ulParentObjType = MAPI_STORE;
- ulStoreId = lpsSaveObj->ulServerId;
- } else {
- if (lpsSaveObj->ulServerId == 0) {
- // new object, parent entry id given by client
- er = lpecSession->GetObjectFromEntryId(&sParentEntryId, &ulParentObjId);
- if (er != erSuccess)
- goto exit;
- fNewItem = true;
- // Lock folder counters now
- strQuery = "SELECT val_ulong FROM properties WHERE hierarchyid = " + stringify(ulParentObjId) + " FOR UPDATE";
- er = lpDatabase->DoSelect(strQuery, NULL);
- if (er != erSuccess)
- goto exit;
- } else {
- // existing item, search parent ourselves because the client just sent its store entryid (see ECMsgStore::OpenEntry())
- er = g_lpSessionManager->GetCacheManager()->GetObject(lpsSaveObj->ulServerId, &ulParentObjId, NULL, &ulObjFlags, &ulObjType);
- if (er != erSuccess)
- goto exit;
-
- if (ulObjFlags & MSGFLAG_DELETED) {
- er = KCERR_OBJECT_DELETED;
- goto exit;
- }
- fNewItem = false;
-
- // Lock folder counters now
- strQuery = "SELECT val_ulong FROM properties WHERE hierarchyid = " + stringify(ulParentObjId) + " FOR UPDATE";
- er = lpDatabase->DoSelect(strQuery, NULL);
- if (er != erSuccess)
- goto exit;
-
- // We also need the old read flags so we can compare the new read flags to see if we need to update the unread counter. Note
- // that the read flags can only be modified through saveObject() when using ICS.
-
- strQuery = "SELECT val_ulong FROM properties WHERE hierarchyid = " + stringify(lpsSaveObj->ulServerId) + " AND tag = " + stringify(PROP_ID(PR_MESSAGE_FLAGS)) + " AND type = " + stringify(PROP_TYPE(PR_MESSAGE_FLAGS)) + " LIMIT 1";
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if (er != erSuccess)
- goto exit;
-
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
-
- if (lpDBRow == nullptr || lpDBRow[0] == nullptr)
- ulPrevReadState = 0;
- else
- ulPrevReadState = atoui(lpDBRow[0]) & MSGFLAG_READ;
-
- ulNewReadState = ulPrevReadState; // Copy read state, may be updated by SaveObject() later
- }
- if (ulParentObjId != CACHE_NO_PARENT) {
- er = g_lpSessionManager->GetCacheManager()->GetObject(ulParentObjId, NULL, NULL, NULL, &ulParentObjType);
- if(er != erSuccess)
- goto exit;
- er = lpecSession->GetSessionManager()->GetCacheManager()->GetStore(ulParentObjId, &ulStoreId, NULL);
- if(er != erSuccess)
- goto exit;
- } else {
- // we get here on create store, but I'm not exactly sure why :|
- ulParentObjType = MAPI_STORE;
- ulStoreId = lpsSaveObj->ulServerId;
- }
- }
- if (ulParentObjId != 0 && ulParentObjType != MAPI_STORE) {
- er = g_lpSessionManager->GetCacheManager()->GetObject(ulParentObjId, &ulGrandParent, NULL, NULL, &ulGrandParentType);
- if(er != erSuccess)
- goto exit;
- }
- // Check permissions
- if (!fNewItem && ulObjType == MAPI_FOLDER)
- er = lpecSession->GetSecurity()->CheckPermission(lpsSaveObj->ulServerId, ecSecurityFolderAccess);
- else if(fNewItem)
- er = lpecSession->GetSecurity()->CheckPermission(ulParentObjId, ecSecurityCreate);
- else
- er = lpecSession->GetSecurity()->CheckPermission(lpsSaveObj->ulServerId, ecSecurityEdit);
- if(er != erSuccess)
- goto exit;
-
- // Quota check
- if(ulObjType == MAPI_MESSAGE) {
- er = CheckQuota(lpecSession, ulStoreId);
- if(er != erSuccess)
- goto exit;
-
- // Update folder counts
- if(fNewItem) {
- er = UpdateFolderCounts(lpDatabase, ulParentObjId, ulFlags, &lpsSaveObj->modProps);
- if (er != erSuccess)
- goto exit;
- }
- else if(ulSyncId != 0) {
- // On modified appointments, unread flags may have changed (only possible during ICS import)
- for (gsoap_size_t i = 0; i < lpsSaveObj->modProps.__size; ++i)
- if(lpsSaveObj->modProps.__ptr[i].ulPropTag == PR_MESSAGE_FLAGS) {
- ulNewReadState = lpsSaveObj->modProps.__ptr[i].Value.ul & MSGFLAG_READ;
- break;
- }
- if (ulPrevReadState != ulNewReadState) {
- er = UpdateFolderCount(lpDatabase, ulParentObjId, PR_CONTENT_UNREAD, ulNewReadState == MSGFLAG_READ ? -1 : 1);
- if (er != erSuccess)
- goto exit;
- }
- }
- }
- er = SaveObject(soap, lpecSession, lpDatabase, lpAttachmentStorage, ulStoreId, ulParentObjId, ulParentObjType, ulFlags, ulSyncId, lpsSaveObj, &sReturnObject, atoui(g_lpSessionManager->GetConfig()->GetSetting("embedded_attachment_limit")), &fHaveChangeKey);
- if (er != erSuccess)
- goto exit;
- // update PR_LOCAL_COMMIT_TIME_MAX for disconnected clients who want to know if the folder contents changed
- if (ulObjType == MAPI_MESSAGE && ulParentObjType == MAPI_FOLDER) {
- er = WriteLocalCommitTimeMax(soap, lpDatabase, ulParentObjId, &pvCommitTime);
- if(er != erSuccess)
- goto exit;
- }
- if (lpsSaveObj->ulServerId == 0) {
- gsoap_size_t rki;
- er = MapEntryIdToObjectId(lpecSession, lpDatabase, sReturnObject.ulServerId, sEntryId);
- if (er != erSuccess)
- goto exit;
- ulObjId = sReturnObject.ulServerId;
- // now that we have an entry id, find the generated PR_RECORD_KEY from SaveObject and override it with the PR_ENTRYID value (fixme, ZCP-6706)
- for (rki = 0; rki < sReturnObject.modProps.__size; ++rki)
- if (sReturnObject.modProps.__ptr[rki].ulPropTag == PR_RECORD_KEY)
- break;
- // @note static alloc of 8 props in SaveObject. we did not find the record key: make it now
- if (rki == sReturnObject.modProps.__size && rki < 8) {
- ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_RECORD_KEY, sReturnObject.ulServerId, 0, 0, ulParentObjId,
- lpsSaveObj->ulObjType, &sReturnObject.modProps.__ptr[rki]);
- ++sReturnObject.modProps.__size;
- sReturnObject.delProps.__ptr[rki] = PR_RECORD_KEY;
- ++sReturnObject.delProps.__size;
- }
- } else {
- ulObjId = lpsSaveObj->ulServerId;
- }
- // 3. pr_source_key magic
- if ((sReturnObject.ulObjType == MAPI_MESSAGE && ulParentObjType == MAPI_FOLDER) ||
- (sReturnObject.ulObjType == MAPI_FOLDER && !(ulFlags & FOLDER_SEARCH)))
- {
- GetSourceKey(sReturnObject.ulServerId, &sSourceKey);
- GetSourceKey(ulParentObjId, &sParentSourceKey);
- if (sReturnObject.ulObjType == MAPI_MESSAGE && ulParentObjType == MAPI_FOLDER) {
- if (lpsSaveObj->ulServerId == 0)
- AddChange(lpecSession, ulSyncId, sSourceKey, sParentSourceKey, ICS_MESSAGE_NEW, 0, !fHaveChangeKey, &strChangeKey, &strChangeList);
- else
- AddChange(lpecSession, ulSyncId, sSourceKey, sParentSourceKey, ICS_MESSAGE_CHANGE, 0, !fHaveChangeKey, &strChangeKey, &strChangeList);
- } else if (lpsSaveObj->ulObjType == MAPI_FOLDER && !(ulFlags & FOLDER_SEARCH)) {
- AddChange(lpecSession, ulSyncId, sSourceKey, sParentSourceKey, ICS_FOLDER_CHANGE, 0, !fHaveChangeKey, &strChangeKey, &strChangeList);
- }
- if(!strChangeKey.empty()){
- sReturnObject.delProps.__ptr[sReturnObject.delProps.__size] = PR_CHANGE_KEY;
- ++sReturnObject.delProps.__size;
- sReturnObject.modProps.__ptr[sReturnObject.modProps.__size].ulPropTag = PR_CHANGE_KEY;
- sReturnObject.modProps.__ptr[sReturnObject.modProps.__size].__union = SOAP_UNION_propValData_bin;
- sReturnObject.modProps.__ptr[sReturnObject.modProps.__size].Value.bin = s_alloc<struct xsd__base64Binary>(soap);
- sReturnObject.modProps.__ptr[sReturnObject.modProps.__size].Value.bin->__size = strChangeKey.size();
- sReturnObject.modProps.__ptr[sReturnObject.modProps.__size].Value.bin->__ptr = s_alloc<unsigned char>(soap, strChangeKey.size());
- memcpy(sReturnObject.modProps.__ptr[sReturnObject.modProps.__size].Value.bin->__ptr, strChangeKey.c_str(), strChangeKey.size());
- ++sReturnObject.modProps.__size;
- }
- if(!strChangeList.empty()){
- sReturnObject.delProps.__ptr[sReturnObject.delProps.__size] = PR_PREDECESSOR_CHANGE_LIST;
- ++sReturnObject.delProps.__size;
- sReturnObject.modProps.__ptr[sReturnObject.modProps.__size].ulPropTag = PR_PREDECESSOR_CHANGE_LIST;
- sReturnObject.modProps.__ptr[sReturnObject.modProps.__size].__union = SOAP_UNION_propValData_bin;
- sReturnObject.modProps.__ptr[sReturnObject.modProps.__size].Value.bin = s_alloc<struct xsd__base64Binary>(soap);
- sReturnObject.modProps.__ptr[sReturnObject.modProps.__size].Value.bin->__size = strChangeList.size();
- sReturnObject.modProps.__ptr[sReturnObject.modProps.__size].Value.bin->__ptr = s_alloc<unsigned char>(soap, strChangeList.size());
- memcpy(sReturnObject.modProps.__ptr[sReturnObject.modProps.__size].Value.bin->__ptr, strChangeList.c_str(), strChangeList.size());
- ++sReturnObject.modProps.__size;
- }
- }
- // 5. TODO: ulSyncId updates sync tables
- // 6. process MSGFLAG_SUBMIT if needed
- er = ProcessSubmitFlag(lpDatabase, ulSyncId, ulStoreId, ulObjId, fNewItem, &lpsSaveObj->modProps);
- if (er != erSuccess)
- goto exit;
- if (ulParentObjType == MAPI_FOLDER) {
- er = ECTPropsPurge::NormalizeDeferredUpdates(lpecSession, lpDatabase, ulParentObjId);
- if (er != erSuccess)
- goto exit;
- }
- er = lpAttachmentStorage->Commit();
- if (er != erSuccess)
- goto exit;
- er = lpDatabase->Commit();
- if (er != erSuccess)
- goto exit;
- // 7. notification
- // Only Notify on MAPI_MESSAGE, MAPI_FOLDER and MAPI_STORE
- // but don't nofity if parent object is a store and object type is attachment or message
- CreateNotifications(ulObjId, ulObjType, ulParentObjId, ulGrandParent, fNewItem, &lpsSaveObj->modProps, pvCommitTime);
- lpsLoadObjectResponse->sSaveObject = sReturnObject;
- g_lpStatsCollector->Increment(SCN_DATABASE_MWOPS);
-
- exit:
- if (er != erSuccess && lpAttachmentStorage)
- lpAttachmentStorage->Rollback();
- ROLLBACK_ON_ERROR();
- }
- SOAP_ENTRY_END()
- static ECRESULT LoadObject(struct soap *soap, ECSession *lpecSession,
- unsigned int ulObjId, unsigned int ulObjType, unsigned int ulParentObjType,
- struct saveObject *lpsSaveObj,
- std::map<unsigned int, CHILDPROPS> *lpChildProps)
- {
- ECRESULT er = erSuccess;
- object_ptr<ECAttachmentStorage> lpAttachmentStorage;
- ULONG ulInstanceId = 0;
- ULONG ulInstanceTag = 0;
- struct saveObject sSavedObject;
- GUID sGuidServer;
- ChildPropsMap mapChildProps;
- ChildPropsMap::const_iterator iterProps;
- USE_DATABASE();
- CHILDPROPS sEmptyProps;
-
- sEmptyProps.lpPropVals = new DynamicPropValArray(soap);
- sEmptyProps.lpPropTags = new DynamicPropTagArray(soap);
- memset(&sSavedObject, 0, sizeof(saveObject));
- // Check permission
- if (ulObjType == MAPI_STORE || (ulObjType == MAPI_FOLDER && ulParentObjType == MAPI_STORE))
- // Always read rights on the store and the root folder
- er = erSuccess;
- else if (ulObjType == MAPI_FOLDER)
- er = lpecSession->GetSecurity()->CheckPermission(ulObjId, ecSecurityFolderVisible);
- else if (ulObjType == MAPI_MESSAGE)
- er = lpecSession->GetSecurity()->CheckPermission(ulObjId, ecSecurityRead);
-
- // Allow reading MAPI_MAILUSER and MAPI_ATTACH since that is only called internally
- if (er != erSuccess)
- goto exit;
- sSavedObject.ulClientId = 0;
- sSavedObject.ulServerId = ulObjId;
- sSavedObject.ulObjType = ulObjType;
-
- if(lpChildProps == NULL) {
- // We were not provided with a property list for this object, get our own now.
- er = PrepareReadProps(soap, lpDatabase, true, lpecSession->GetCapabilities() & KOPANO_CAP_UNICODE, ulObjId, 0, MAX_PROP_SIZE, &mapChildProps, NULL);
- if(er != erSuccess)
- goto exit;
-
- lpChildProps = &mapChildProps;
- }
-
- iterProps = lpChildProps->find(ulObjId);
- if (iterProps == lpChildProps->cend())
- er = ReadProps(soap, lpecSession, ulObjId, ulObjType, ulParentObjType, sEmptyProps, &sSavedObject.delProps, &sSavedObject.modProps);
- else
- er = ReadProps(soap, lpecSession, ulObjId, ulObjType, ulParentObjType, iterProps->second, &sSavedObject.delProps, &sSavedObject.modProps);
- if (er != erSuccess)
- goto exit;
-
- FreeChildProps(&mapChildProps);
-
- if (ulObjType == MAPI_MESSAGE || ulObjType == MAPI_ATTACH) {
- // Pre-load *all* properties of *all* subobjects for fast accessibility
- er = PrepareReadProps(soap, lpDatabase, true, lpecSession->GetCapabilities() & KOPANO_CAP_UNICODE, 0, ulObjId, MAX_PROP_SIZE, &mapChildProps, NULL);
- if (er != erSuccess)
- goto exit;
- // find subobjects
- strQuery = "SELECT id, type FROM hierarchy WHERE parent="+stringify(ulObjId);
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if(er != erSuccess)
- goto exit;
- sSavedObject.__size = lpDatabase->GetNumRows(lpDBResult);
- sSavedObject.__ptr = s_alloc<saveObject>(soap, sSavedObject.__size);
- memset(sSavedObject.__ptr, 0, sizeof(saveObject) * sSavedObject.__size);
- for (gsoap_size_t i = 0; i < sSavedObject.__size; ++i) {
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
- lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
- if(lpDBRow == NULL || lpDBLen == NULL) {
- er = KCERR_DATABASE_ERROR; // this should never happen
- ec_log_err("LoadObject(): no rows from db");
- goto exit;
- }
-
- LoadObject(soap, lpecSession, atoi(lpDBRow[0]), atoi(lpDBRow[1]), ulObjType, &sSavedObject.__ptr[i], &mapChildProps);
- }
- FreeChildProps(&mapChildProps);
- }
- if (ulObjType == MAPI_MESSAGE || ulObjType == MAPI_ATTACH) {
- er = CreateAttachmentStorage(lpDatabase, &~lpAttachmentStorage);
- if (er != erSuccess)
- goto exit;
- er = g_lpSessionManager->GetServerGUID(&sGuidServer);
- if (er != erSuccess)
- goto exit;
- /* Not having a single instance ID is not critical, we might create it at a later time */
- // @todo this should be GetSingleInstanceIdsWithTags(ulObjId, map<tag, instanceid>);
- if (ulObjType == MAPI_MESSAGE)
- ulInstanceTag = PROP_ID(PR_EC_IMAP_EMAIL);
- else
- ulInstanceTag = PROP_ID(PR_ATTACH_DATA_BIN);
- if (lpAttachmentStorage->GetSingleInstanceId(ulObjId, ulInstanceTag, &ulInstanceId) == erSuccess) {
- sSavedObject.lpInstanceIds = s_alloc<entryList>(soap);
- sSavedObject.lpInstanceIds->__size = 1;
- sSavedObject.lpInstanceIds->__ptr = s_alloc<entryId>(soap, sSavedObject.lpInstanceIds->__size);
- er = SIIDToEntryID(soap, &sGuidServer, ulInstanceId, ulInstanceTag, &sSavedObject.lpInstanceIds->__ptr[0]);
- if (er != erSuccess) {
- sSavedObject.lpInstanceIds->__size = 0;
- goto exit;
- }
- }
- }
- if (ulObjType == MAPI_MESSAGE) {
- // @todo: Check if we can do this on the fly to avoid the additional lookup.
- for (gsoap_size_t i = 0; i < sSavedObject.modProps.__size; ++i) {
- if (sSavedObject.modProps.__ptr[i].ulPropTag != PR_SUBMIT_FLAGS)
- continue;
- if (g_lpSessionManager->GetLockManager()->IsLocked(ulObjId, NULL))
- sSavedObject.modProps.__ptr[i].Value.ul |= SUBMITFLAG_LOCKED;
- else
- sSavedObject.modProps.__ptr[i].Value.ul &= ~SUBMITFLAG_LOCKED;
- }
- }
- *lpsSaveObj = std::move(sSavedObject);
- exit:
- delete sEmptyProps.lpPropVals;
- delete sEmptyProps.lpPropTags;
- return er;
- }
- SOAP_ENTRY_START(loadObject, lpsLoadObjectResponse->er, entryId sEntryId, struct notifySubscribe *lpsNotSubscribe, unsigned int ulFlags, struct loadObjectResponse *lpsLoadObjectResponse)
- {
- unsigned int ulObjId = 0;
- unsigned int ulObjFlags = 0;
- unsigned int ulObjType = 0;
- unsigned int ulParentId = 0;
- unsigned int ulParentObjType = 0;
- unsigned int ulOwnerId = 0;
- unsigned int ulEidFlags = 0;
- USE_DATABASE();
- struct saveObject sSavedObject;
- er = BeginLockFolders(lpDatabase, EntryId(sEntryId), LOCK_SHARED);
- if(er != erSuccess)
- goto exit;
-
- /*
- * 2 Reasons to send KCERR_UNABLE_TO_COMPLETE (and have the client try to open the store elsewhere):
- * 1. We can't find the object based on the entryid.
- * 2. The owner of the store is not supposed to have a store on this server.
- */
- er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulObjId, &ulEidFlags);
- if ((ulEidFlags & OPENSTORE_OVERRIDE_HOME_MDB) == 0) {
- if (er == KCERR_NOT_FOUND && sEntryId.__size >= (int)min(sizeof(EID), sizeof(EID_V0)) && ((EID*)sEntryId.__ptr)->usType == MAPI_STORE)
- er = KCERR_UNABLE_TO_COMPLETE; // Reason 1
- }
- if (er != erSuccess)
- goto exit;
-
- er = g_lpSessionManager->GetCacheManager()->GetObject(ulObjId, &ulParentId, &ulOwnerId, &ulObjFlags, &ulObjType);
- if (er != erSuccess)
- goto exit;
-
- if(ulObjType == MAPI_STORE) {
- if ((ulEidFlags & OPENSTORE_OVERRIDE_HOME_MDB) == 0) {
- if (lpecSession->GetSessionManager()->IsDistributedSupported() && !lpecSession->GetUserManagement()->IsInternalObject(ulOwnerId)) {
- objectdetails_t sUserDetails;
- if (lpecSession->GetUserManagement()->GetObjectDetails(ulOwnerId, &sUserDetails) == erSuccess) {
- unsigned int ulStoreType;
- er = lpecSession->GetSessionManager()->GetCacheManager()->GetStoreAndType(ulObjId, NULL, NULL, &ulStoreType);
- if (er != erSuccess)
- goto exit;
- if (ulStoreType == ECSTORE_TYPE_PRIVATE || ulStoreType == ECSTORE_TYPE_PUBLIC) {
- std::string strServerName = sUserDetails.GetPropString(OB_PROP_S_SERVERNAME);
- if (strServerName.empty()) {
- er = KCERR_NOT_FOUND;
- goto exit;
- }
- if (strcasecmp(strServerName.c_str(), g_lpSessionManager->GetConfig()->GetSetting("server_name")) != 0) {
- er = KCERR_UNABLE_TO_COMPLETE; // Reason 2
- goto exit;
- }
- } else if (ulStoreType == ECSTORE_TYPE_ARCHIVE) {
- // We allow an archive store to be opened by sysadmins even if it's not supposed
- // to exist on this server for a particular user.
- if (lpecSession->GetSecurity()->GetAdminLevel() < ADMIN_LEVEL_SYSADMIN &&
- !sUserDetails.PropListStringContains((property_key_t)PR_EC_ARCHIVE_SERVERS_A, g_lpSessionManager->GetConfig()->GetSetting("server_name"), true))
- {
- er = KCERR_NOT_FOUND;
- goto exit;
- }
- } else {
- er = KCERR_NOT_FOUND;
- goto exit;
- }
- } else {
- // unhooked store of a deleted user
- if(lpecSession->GetSecurity()->GetAdminLevel() < ADMIN_LEVEL_SYSADMIN) {
- er = KCERR_NO_ACCESS;
- goto exit;
- }
- }
- }
- }
- ulParentObjType = 0;
- } else if(ulObjType == MAPI_MESSAGE) {
- // If the object is locked on another session, access should be denied
- ECSESSIONID ulLockedSessionId;
- if (g_lpSessionManager->GetLockManager()->IsLocked(ulObjId, &ulLockedSessionId) && ulLockedSessionId != ulSessionId) {
- er = KCERR_NO_ACCESS;
- goto exit;
- }
-
- ulParentObjType = MAPI_FOLDER;
- } else if(ulObjType == MAPI_FOLDER) {
- er = g_lpSessionManager->GetCacheManager()->GetObject(ulParentId, NULL, NULL, NULL, &ulParentObjType);
- if (er != erSuccess)
- goto exit;
-
- // Reset folder counts now (Note: runs in a DB transaction!). Note: we only update counts when lpNotSubscribe is not NULL; this
- // makes sure that we only reset folder counts on the first open of a folder, and not when the folder properties are updated (eg
- // due to counter changes)
- if(lpsNotSubscribe && ulObjFlags != FOLDER_SEARCH && parseBool(g_lpSessionManager->GetConfig()->GetSetting("counter_reset")))
- ResetFolderCount(lpecSession, ulObjId);
- }
- // check if flags were passed, older clients call checkExistObject
- if(ulFlags & 0x80000000) {
- // Flags passed by client, check object flags
- ulFlags = ulFlags & ~0x80000000;
-
- if((ulObjFlags & MSGFLAG_DELETED) != ulFlags) {
- if (ulObjType == MAPI_STORE)
- er = KCERR_UNABLE_TO_COMPLETE;
- else
- er = KCERR_NOT_FOUND;
- goto exit;
- }
- }
- // Subscribe for notification
- if (lpsNotSubscribe) {
- er = DoNotifySubscribe(lpecSession, ulSessionId, lpsNotSubscribe);
- if (er != erSuccess)
- goto exit;
- }
- er = LoadObject(soap, lpecSession, ulObjId, ulObjType, ulParentObjType, &sSavedObject, NULL);
- if (er != erSuccess)
- goto exit;
- lpsLoadObjectResponse->sSaveObject = sSavedObject;
- g_lpStatsCollector->Increment(SCN_DATABASE_MROPS);
- exit:
- lpDatabase->Commit();
- }
- SOAP_ENTRY_END()
- // if lpsNewEntryId is NULL this function create a new entryid
- // if lpsOrigSourceKey is NULL this function creates a new sourcekey
- static ECRESULT CreateFolder(ECSession *lpecSession, ECDatabase *lpDatabase,
- unsigned int ulParentId, entryId *lpsNewEntryId, unsigned int type,
- const char *name, char *comment, bool openifexists, bool bNotify,
- unsigned int ulSyncId, const struct xsd__base64Binary *lpsOrigSourceKey,
- unsigned int *lpFolderId, bool *lpbExist)
- {
- ECRESULT er = erSuccess;
- ALLOC_DBRESULT();
- unsigned int ulFolderId = 0;
- unsigned int ulLastId = 0;
- unsigned int ulStoreId = 0;
- unsigned int ulGrandParent = 0;
- bool bExist = false;
- bool bFreeNewEntryId = false;
- GUID guid;
- SOURCEKEY sSourceKey;
- unsigned int tags [] = { PR_CONTENT_COUNT, PR_CONTENT_UNREAD, PR_ASSOC_CONTENT_COUNT, PR_DELETED_MSG_COUNT, PR_DELETED_FOLDER_COUNT, PR_DELETED_ASSOC_MSG_COUNT, PR_FOLDER_CHILD_COUNT };
- unsigned int timeTags [] = { PR_LAST_MODIFICATION_TIME, PR_CREATION_TIME };
- time_t now = 0;
- struct propVal sProp;
- struct hiloLong sHilo;
- er = lpecSession->GetSessionManager()->GetCacheManager()->GetStore(ulParentId, &ulStoreId, &guid);
- if(er != erSuccess)
- goto exit;
- er = g_lpSessionManager->GetCacheManager()->GetParent(ulParentId, &ulGrandParent);
- if(er != erSuccess)
- goto exit;
- // Check whether the requested name already exists
- strQuery = "SELECT hierarchy.id, properties.val_string FROM hierarchy JOIN properties ON hierarchy.id = properties.hierarchyid WHERE hierarchy.parent=" + stringify(ulParentId) + " AND hierarchy.type="+stringify(MAPI_FOLDER)+" AND hierarchy.flags & " + stringify(MSGFLAG_DELETED)+ "=0 AND properties.tag=" + stringify(KOPANO_TAG_DISPLAY_NAME) + " AND properties.val_string = '" + lpDatabase->Escape(name) + "' AND properties.type="+stringify(PT_STRING8);
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if(er != erSuccess)
- goto exit;
- while (!bExist && (lpDBRow = lpDatabase->FetchRow(lpDBResult)) != NULL) {
- if (lpDBRow[0] == NULL || lpDBRow[1] == NULL) {
- er = KCERR_DATABASE_ERROR;
- ec_log_err("CreateFolder(): columns null");
- goto exit;
- }
- if (strcasecmp(lpDBRow[1], name) == 0)
- bExist = true;
- }
- if(bExist && !ulSyncId) {
- // Check folder read access
- er = lpecSession->GetSecurity()->CheckPermission(ulParentId, ecSecurityFolderVisible);
- if(er != erSuccess)
- goto exit;
- // Object exists
- if (!openifexists) {
- er = KCERR_COLLISION;
- ec_log_err("CreateFolder(): folder already exists");
- goto exit;
- }
-
- ulFolderId = atoi(lpDBRow[0]);
- }
- else {
- // Check write permission of the folder destination
- er = lpecSession->GetSecurity()->CheckPermission(ulParentId, ecSecurityCreateFolder);
- if(er != erSuccess)
- goto exit;
- // Create folder
- strQuery = "INSERT INTO hierarchy (parent, type, flags, owner) values(" + stringify(ulParentId) + "," + stringify(KOPANO_OBJTYPE_FOLDER) + ", " + stringify(type) + ", "+stringify(lpecSession->GetSecurity()->GetUserId(ulParentId))+")";
- er = lpDatabase->DoInsert(strQuery, &ulLastId);
- if(er != erSuccess)
- goto exit;
- if(lpsNewEntryId == NULL) {
- er = CreateEntryId(guid, MAPI_FOLDER, &lpsNewEntryId);
- if(er != erSuccess)
- goto exit;
- bFreeNewEntryId = true;
- }
- //Create entryid, 0x0FFF = PR_ENTRYID
- er = RemoveStaleIndexedProp(lpDatabase, PR_ENTRYID, lpsNewEntryId->__ptr, lpsNewEntryId->__size);
- if(er != erSuccess)
- goto exit;
- strQuery = "INSERT INTO indexedproperties (hierarchyid,tag,val_binary) VALUES("+stringify(ulLastId)+", 0x0FFF, "+lpDatabase->EscapeBinary(lpsNewEntryId->__ptr, lpsNewEntryId->__size)+")";
- er = lpDatabase->DoInsert(strQuery);
- if(er != erSuccess)
- goto exit;
- // Create Displayname
- strQuery = "INSERT INTO properties (hierarchyid, tag, type, val_string) values(" + stringify(ulLastId) + "," + stringify(KOPANO_TAG_DISPLAY_NAME) + "," + stringify(PT_STRING8) + ",'" + lpDatabase->Escape(name) + "')";
- er = lpDatabase->DoInsert(strQuery);
- if(er != erSuccess)
- goto exit;
- // Create Displayname
- strQuery = "INSERT INTO tproperties (hierarchyid, tag, type, folderid, val_string) values(" + stringify(ulLastId) + "," + stringify(KOPANO_TAG_DISPLAY_NAME) + "," + stringify(PT_STRING8) + "," + stringify(ulParentId) + ",'" + lpDatabase->Escape(name) + "')";
- er = lpDatabase->DoInsert(strQuery);
- if(er != erSuccess)
- goto exit;
-
- // Create counters
- for (size_t i = 0; i < ARRAY_SIZE(tags); ++i) {
- sProp.ulPropTag = tags[i];
- sProp.__union = SOAP_UNION_propValData_ul;
- sProp.Value.ul = 0;
-
- er = WriteProp(lpDatabase, ulLastId, ulParentId, &sProp);
- if(er != erSuccess)
- goto exit;
- }
-
- // Create PR_SUBFOLDERS
- sProp.ulPropTag = PR_SUBFOLDERS;
- sProp.__union = SOAP_UNION_propValData_b;
- sProp.Value.b = false;
-
- er = WriteProp(lpDatabase, ulLastId, ulParentId, &sProp);
- if(er != erSuccess)
- goto exit;
-
- // Create PR_FOLDERTYPE
- sProp.ulPropTag = PR_FOLDER_TYPE;
- sProp.__union = SOAP_UNION_propValData_ul;
- sProp.Value.ul = type;
- er = WriteProp(lpDatabase, ulLastId, ulParentId, &sProp);
- if(er != erSuccess)
- goto exit;
- // Create PR_COMMENT
- if (comment) {
- sProp.ulPropTag = PR_COMMENT_A;
- sProp.__union = SOAP_UNION_propValData_lpszA;
- sProp.Value.lpszA = comment;
-
- er = WriteProp(lpDatabase, ulLastId, ulParentId, &sProp);
- if(er != erSuccess)
- goto exit;
- }
-
- // Create PR_LAST_MODIFICATION_TIME and PR_CREATION_TIME
- now = time(NULL);
- for (size_t i = 0; i < ARRAY_SIZE(timeTags); ++i) {
- sProp.ulPropTag = timeTags[i];
- sProp.__union = SOAP_UNION_propValData_hilo;
- sProp.Value.hilo = &sHilo;
- UnixTimeToFileTime(now, &sProp.Value.hilo->hi, &sProp.Value.hilo->lo);
-
- er = WriteProp(lpDatabase, ulLastId, ulParentId, &sProp);
- if(er != erSuccess)
- goto exit;
- }
- // Create SourceKey
- if (lpsOrigSourceKey && lpsOrigSourceKey->__size > (int)sizeof(GUID) && lpsOrigSourceKey->__ptr){
- sSourceKey = SOURCEKEY(lpsOrigSourceKey->__size, (char*)lpsOrigSourceKey->__ptr);
- }else{
- er = lpecSession->GetNewSourceKey(&sSourceKey);
- if(er != erSuccess)
- goto exit;
- }
- er = RemoveStaleIndexedProp(lpDatabase, PR_SOURCE_KEY, sSourceKey, sSourceKey.size());
- if(er != erSuccess)
- goto exit;
-
- strQuery = "INSERT INTO indexedproperties(hierarchyid,tag,val_binary) VALUES(" + stringify(ulLastId) + "," + stringify(PROP_ID(PR_SOURCE_KEY)) + "," + lpDatabase->EscapeBinary(sSourceKey, sSourceKey.size()) + ")";
- er = lpDatabase->DoInsert(strQuery);
- if(er != erSuccess)
- goto exit;
- ulFolderId = ulLastId;
-
- er = UpdateFolderCount(lpDatabase, ulParentId, PR_SUBFOLDERS, 1);
- if(er != erSuccess)
- goto exit;
- er = UpdateFolderCount(lpDatabase, ulParentId, PR_FOLDER_CHILD_COUNT, 1);
- if(er != erSuccess)
- goto exit;
- }
- if(bExist == false && !(type & FOLDER_SEARCH)){
- SOURCEKEY sParentSourceKey;
-
- GetSourceKey(ulParentId, &sParentSourceKey);
- AddChange(lpecSession, ulSyncId, sSourceKey, sParentSourceKey, ICS_FOLDER_NEW);
- }
- // Notify that the folder has been created
- if(bExist == false && bNotify == true) {
- g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, ulParentId);
- g_lpSessionManager->NotificationCreated(MAPI_FOLDER, ulFolderId, ulParentId);
- g_lpSessionManager->NotificationModified(MAPI_FOLDER, ulParentId);
- // Update all tables viewing this folder
- g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_ADD, 0, ulParentId, ulFolderId, MAPI_FOLDER);
- // Update notification, grandparent of the mainfolder
- g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY, 0, ulGrandParent, ulParentId, MAPI_FOLDER);
- }
- if(lpFolderId)
- *lpFolderId = ulFolderId;
- if(lpbExist)
- *lpbExist = bExist;
- exit:
- if(bFreeNewEntryId == true && lpsNewEntryId)
- FreeEntryId(lpsNewEntryId, true);
- return er;
- }
- /**
- * createFolder: Create a folder object in the hierarchy table, and add a 'PR_DISPLAY_NAME' property.
- *
- * The data model actually supports multiple folders having the same PR_DISPLAY_NAME, however, MAPI does not,
- * so we have to give the engine the knowledge of the PR_DISPLAY_NAME property here, one of the few properties
- * that the backend engine actually knows about.
- *
- * Of course, the frontend could also enforce this constraint, but that would require 2 server accesses (1. check
- * existing, 2. create folder), and we're trying to keep the amount of server accesses as low as possible.
- *
- */
- SOAP_ENTRY_START(createFolder, lpsResponse->er, entryId sParentId, entryId* lpsNewEntryId, unsigned int ulType, char *szName, char *szComment, bool fOpenIfExists, unsigned int ulSyncId, struct xsd__base64Binary sOrigSourceKey, struct createFolderResponse *lpsResponse)
- {
- unsigned int ulParentId = 0;
- unsigned int ulFolderId = 0;
- USE_DATABASE();
- if(szName == NULL) {
- er = KCERR_INVALID_PARAMETER;
- goto exit;
- }
- // Fixup the input strings
- szName = STRIN_FIX(szName);
- szComment = STRIN_FIX(szComment);
- er = lpDatabase->Begin();
- if (er != erSuccess)
- goto exit;
- er = lpecSession->GetObjectFromEntryId(&sParentId, &ulParentId);
- if (er != erSuccess)
- goto exit;
- er = CreateFolder(lpecSession, lpDatabase, ulParentId, lpsNewEntryId, ulType, szName, szComment, fOpenIfExists, true, ulSyncId, &sOrigSourceKey, &ulFolderId, NULL);
- if (er != erSuccess)
- goto exit;
- er = lpDatabase->Commit();
- if (er != erSuccess)
- goto exit;
- er = g_lpSessionManager->GetCacheManager()->GetEntryIdFromObject(ulFolderId, soap, 0, &lpsResponse->sEntryId);
- if (er != erSuccess)
- goto exit;
- exit:
- ROLLBACK_ON_ERROR();
- }
- SOAP_ENTRY_END()
- /**
- * tableOpen: Open a mapi table
- *
- * @param[in] lpecSession server session object, cannot be NULL
- * @param[in] sEntryId entryid data
- * @param[in] ulTableType the type of table to open:
- * TABLETYPE_MS
- * For all tables of a messagestore.
- * TABLETYPE_AB
- * For the addressbook tables.
- * TABLETYPE_SPOOLER
- * For the spooler tables, the sEntryId must always the store entryid.
- * ulType and ulFlags are ignored, reserved for future use.
- * TABLE_TYPE_MULTISTORE
- * Special Kopano-only table to have given objects from different stores in one table view.
- * TABLE_TYPE_USERSTORES
- * Special Kopano-only table. Lists all combinations of users and stores on this server. (Used for the orphan management in kopano-admin).
- * TABLE_TYPE_STATS_*
- * Special Kopano-only tables. Used for various statistics and other uses.
- * @param[in] ulType the type of the object you want to open.
- * @param[in] ulFlags ulFlags from the client
- * MAPI_ASSOCIATED
- * List associated messages/folders instead of normal messages/folders.
- * MAPI_UNICODE
- * Default and all columns will contain _W string properties, otherwise _A strings are used.
- * CONVENIENT_DEPTH
- * Returns a convenient depth (flat list) of all folders.
- * SHOW_SOFT_DELETES
- * List deleted items in this table, rewritten to MSGFLAG_DELETED.
- * EC_TABLE_NOCAP
- * Do not cap string entries to 255 bytes, an extension of ours.
- *
- * @param[out] lpulTableId Server table id for this new table.
- */
- static ECRESULT OpenTable(ECSession *lpecSession, entryId sEntryId,
- unsigned int ulTableType, unsigned int ulType, unsigned int ulFlags,
- unsigned int *lpulTableId)
- {
- ECRESULT er;
- objectid_t sExternId;
- unsigned int ulTableId = 0;
- unsigned int ulId = 0;
- unsigned int ulTypeId = 0;
- switch (ulTableType) {
- case TABLETYPE_MS:
- if (ulFlags & SHOW_SOFT_DELETES)
- {
- ulFlags &=~SHOW_SOFT_DELETES;
- ulFlags |= MSGFLAG_DELETED;
- }
- er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulId);
- if (er != erSuccess)
- return er;
- er = lpecSession->GetTableManager()->OpenGenericTable(ulId, ulType, ulFlags, &ulTableId);
- if (er != erSuccess)
- return er;
- break;
- case TABLETYPE_AB:
- er = ABEntryIDToID(&sEntryId, &ulId, &sExternId, &ulTypeId);
- if (er != erSuccess)
- return er;
- // If an extern id is present, we should get an object based on that.
- if (!sExternId.id.empty()) {
- er = g_lpSessionManager->GetCacheManager()->GetUserObject(sExternId, &ulId, NULL, NULL);
- if (er != erSuccess)
- return er;
- }
- er = lpecSession->GetTableManager()->OpenABTable(ulId, ulTypeId, ulType, ulFlags, &ulTableId);
- if (er != erSuccess)
- return er;
- break;
- case TABLETYPE_SPOOLER:
- // sEntryId must be a store entryid or zero for all stores
- if (sEntryId.__size > 0) {
- er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulId);
- if (er != erSuccess)
- return er;
- } else
- ulId = 0; //All stores
- er = lpecSession->GetTableManager()->OpenOutgoingQueueTable(ulId, &ulTableId);
- if (er != erSuccess)
- return er;
- break;
- case TABLETYPE_MULTISTORE:
- er = lpecSession->GetTableManager()->OpenMultiStoreTable(ulType, ulFlags, &ulTableId);
- if (er != erSuccess)
- return er;
- break;
- case TABLETYPE_USERSTORES:
- er = lpecSession->GetTableManager()->OpenUserStoresTable(ulFlags, &ulTableId);
- if (er != erSuccess)
- return er;
- break;
- case TABLETYPE_STATS_SYSTEM:
- case TABLETYPE_STATS_SESSIONS:
- case TABLETYPE_STATS_USERS:
- case TABLETYPE_STATS_COMPANY:
- case TABLETYPE_STATS_SERVERS:
- er = lpecSession->GetTableManager()->OpenStatsTable(ulTableType, ulFlags, &ulTableId);
- if (er != erSuccess)
- return er;
- break;
- case TABLETYPE_MAILBOX:
- er = lpecSession->GetTableManager()->OpenMailBoxTable(ulFlags, &ulTableId);
- if (er != erSuccess)
- return er;
- break;
- default:
- return KCERR_BAD_VALUE;
- break; //Happy compiler
- } // switch (ulTableType)
- *lpulTableId = ulTableId;
- return erSuccess;
- }
- SOAP_ENTRY_START(tableOpen, lpsTableOpenResponse->er, entryId sEntryId, unsigned int ulTableType, unsigned ulType, unsigned int ulFlags, struct tableOpenResponse *lpsTableOpenResponse)
- {
- unsigned int ulTableId = 0;
-
- er = OpenTable(lpecSession, sEntryId, ulTableType, ulType, ulFlags, &ulTableId);
- if (er != erSuccess)
- return er;
- lpsTableOpenResponse->ulTableId = ulTableId;
- return erSuccess;
- }
- SOAP_ENTRY_END()
- /**
- * tableClose: close the table with the specified table ID
- */
- SOAP_ENTRY_START(tableClose, *result, unsigned int ulTableId, unsigned int *result)
- {
- return lpecSession->GetTableManager()->CloseTable(ulTableId);
- }
- SOAP_ENTRY_END()
- /**
- * tableSetSearchCritieria: set search criteria for a searchfolder
- */
- SOAP_ENTRY_START(tableSetSearchCriteria, *result, entryId sEntryId, struct restrictTable *lpRestrict, struct entryList *lpFolders, unsigned int ulFlags, unsigned int *result)
- {
- unsigned int ulStoreId = 0;
- unsigned int ulParent = 0;
- if (!(ulFlags & STOP_SEARCH) &&
- (lpRestrict == nullptr || lpFolders == nullptr))
- return KCERR_INVALID_PARAMETER;
- er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulParent);
- if(er != erSuccess)
- return er;
- er = lpecSession->GetSessionManager()->GetCacheManager()->GetStore(ulParent, &ulStoreId, NULL);
- if(er != erSuccess)
- return er;
- // Check permission
- er = lpecSession->GetSecurity()->CheckPermission(ulParent, ecSecurityEdit);
- if(er != erSuccess)
- return er;
- // If a STOP was requested, then that's all we need to do
- if(ulFlags & STOP_SEARCH) {
- return lpecSession->GetSessionManager()->GetSearchFolders()->SetSearchCriteria(ulStoreId, ulParent, nullptr);
- } else {
- struct searchCriteria sSearchCriteria;
- if (!bSupportUnicode) {
- er = FixRestrictionEncoding(soap, stringCompat, In, lpRestrict);
- if (er != erSuccess)
- return er;
- }
- sSearchCriteria.lpRestrict = lpRestrict;
- sSearchCriteria.lpFolders = lpFolders;
- sSearchCriteria.ulFlags = ulFlags;
- return lpecSession->GetSessionManager()->GetSearchFolders()->SetSearchCriteria(ulStoreId, ulParent, &sSearchCriteria);
- }
- }
- SOAP_ENTRY_END()
- /**
- * tableGetSearchCriteria: get the search criteria for a searchfolder previously called with tableSetSearchCriteria
- */
- SOAP_ENTRY_START(tableGetSearchCriteria, lpsResponse->er, entryId sEntryId, struct tableGetSearchCriteriaResponse *lpsResponse)
- {
- unsigned int ulFlags = 0;
- unsigned int ulStoreId = 0;
- unsigned int ulId = 0;
- struct searchCriteria *lpSearchCriteria = NULL;
- er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulId);
- if(er != erSuccess)
- goto exit;
- er = g_lpSessionManager->GetCacheManager()->GetStore(ulId, &ulStoreId, NULL);
- if(er != erSuccess)
- goto exit;
- // Check permission
- er = lpecSession->GetSecurity()->CheckPermission(ulId, ecSecurityRead);
- if(er != erSuccess)
- goto exit;
- er = lpecSession->GetSessionManager()->GetSearchFolders()->GetSearchCriteria(ulStoreId, ulId, &lpSearchCriteria, &ulFlags);
- if(er != erSuccess)
- goto exit;
- er = CopyRestrictTable(soap, lpSearchCriteria->lpRestrict, &lpsResponse->lpRestrict);
- if(er != erSuccess)
- goto exit;
- if (!bSupportUnicode) {
- er = FixRestrictionEncoding(soap, stringCompat, Out, lpsResponse->lpRestrict);
- if (er != erSuccess)
- goto exit;
- }
- er = CopyEntryList(soap, lpSearchCriteria->lpFolders, &lpsResponse->lpFolderIDs);
- if(er != erSuccess)
- goto exit;
- lpsResponse->ulFlags = ulFlags;
- exit:
- if(lpSearchCriteria)
- FreeSearchCriteria(lpSearchCriteria);
- }
- SOAP_ENTRY_END()
- /**
- * tableSetColumns: called from IMAPITable::SetColumns()
- */
- SOAP_ENTRY_START(tableSetColumns, *result, unsigned int ulTableId, struct propTagArray *aPropTag, unsigned int *result)
- {
- object_ptr<ECGenericObjectTable> lpTable;
- er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
- if(er != erSuccess)
- return er;
- return lpTable->SetColumns(aPropTag, false);
- }
- SOAP_ENTRY_END()
- /**
- * tableQueryColumns: called from IMAPITable::GetColumns()
- */
- SOAP_ENTRY_START(tableQueryColumns, lpsResponse->er, unsigned int ulTableId, unsigned int ulFlags, struct tableQueryColumnsResponse *lpsResponse)
- {
- object_ptr<ECGenericObjectTable> lpTable;
- struct propTagArray *lpPropTags = NULL;
- // Init
- lpsResponse->sPropTagArray.__size = 0;
- lpsResponse->sPropTagArray.__ptr = NULL;
- er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
- if(er != erSuccess)
- return er;
- er = lpTable->GetColumns(soap, ulFlags, &lpPropTags);
- if(er != erSuccess)
- return er;
- lpsResponse->sPropTagArray.__size = lpPropTags->__size;
- lpsResponse->sPropTagArray.__ptr = lpPropTags->__ptr;
- return erSuccess;
- }
- SOAP_ENTRY_END()
- /**
- * tableRestrict: called from IMAPITable::Restrict()
- */
- SOAP_ENTRY_START(tableRestrict, *result, unsigned int ulTableId, struct restrictTable *lpsRestrict, unsigned int *result)
- {
- object_ptr<ECGenericObjectTable> lpTable;
- er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
- if(er != erSuccess)
- return er;
- if (!bSupportUnicode) {
- er = FixRestrictionEncoding(soap, stringCompat, In, lpsRestrict);
- if (er != erSuccess)
- return er;
- }
- return lpTable->Restrict(lpsRestrict);
- }
- SOAP_ENTRY_END()
- /**
- * tableSort: called from IMAPITable::Sort()
- */
- SOAP_ENTRY_START(tableSort, *result, unsigned int ulTableId, struct sortOrderArray *lpSortOrder, unsigned int ulCategories, unsigned int ulExpanded, unsigned int *result)
- {
- object_ptr<ECGenericObjectTable> lpTable;
- if (lpSortOrder == nullptr)
- return KCERR_INVALID_PARAMETER;
- er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
- if(er != erSuccess)
- return er;
- return lpTable->SetSortOrder(lpSortOrder, ulCategories, ulExpanded);
- }
- SOAP_ENTRY_END()
- /**
- * tableQueryRows: called from IMAPITable::QueryRows()
- */
- SOAP_ENTRY_START(tableQueryRows, lpsResponse->er, unsigned int ulTableId, unsigned int ulRowCount, unsigned int ulFlags, struct tableQueryRowsResponse *lpsResponse)
- {
- object_ptr<ECGenericObjectTable> lpTable;
- struct rowSet *lpRowSet = NULL;
- lpsResponse->sRowSet.__ptr = NULL;
- lpsResponse->sRowSet.__size = 0;
- // Get the table
- er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
- if(er != erSuccess)
- return er;
- // FIXME: Check permission
- er = lpTable->QueryRows(soap, ulRowCount, ulFlags, &lpRowSet);
- if(er != erSuccess)
- return er;
- if (!bSupportUnicode) {
- er = FixRowSetEncoding(soap, stringCompat, Out, lpRowSet);
- if (er != erSuccess)
- return er;
- }
- lpsResponse->sRowSet.__ptr = lpRowSet->__ptr;
- lpsResponse->sRowSet.__size = lpRowSet->__size;
- return erSuccess;
- }
- SOAP_ENTRY_END()
- /**
- * tableGetRowCount: called from IMAPITable::GetRowCount()
- */
- SOAP_ENTRY_START(tableGetRowCount, lpsResponse->er, unsigned int ulTableId, struct tableGetRowCountResponse *lpsResponse)
- {
- object_ptr<ECGenericObjectTable> lpTable;
- //FIXME: security? give rowcount 0 is failed ?
- er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
- if(er != erSuccess)
- return er;
- return lpTable->GetRowCount(&lpsResponse->ulCount, &lpsResponse->ulRow);
- }
- SOAP_ENTRY_END()
- /**
- * tableSeekRow: called from IMAPITable::SeekRow()
- */
- SOAP_ENTRY_START(tableSeekRow, lpsResponse->er, unsigned int ulTableId , unsigned int ulBookmark, int lRows, struct tableSeekRowResponse *lpsResponse)
- {
- object_ptr<ECGenericObjectTable> lpTable;
- er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
- if(er != erSuccess)
- return er;
- return lpTable->SeekRow(ulBookmark, lRows, &lpsResponse->lRowsSought);
- }
- SOAP_ENTRY_END()
- /**
- * tableFindRow: called from IMAPITable::FindRow()
- */
- SOAP_ENTRY_START(tableFindRow, *result, unsigned int ulTableId ,unsigned int ulBookmark, unsigned int ulFlags, struct restrictTable *lpsRestrict, unsigned int *result)
- {
- object_ptr<ECGenericObjectTable> lpTable;
- er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
- if(er != erSuccess)
- return er;
- if (!bSupportUnicode) {
- er = FixRestrictionEncoding(soap, stringCompat, In, lpsRestrict);
- if (er != erSuccess)
- return er;
- }
- return lpTable->FindRow(lpsRestrict, ulBookmark, ulFlags);
- }
- SOAP_ENTRY_END()
- /**
- * tableCreateBookmark: called from IMAPITable::CreateBookmark()
- */
- SOAP_ENTRY_START(tableCreateBookmark, lpsResponse->er, unsigned int ulTableId, struct tableBookmarkResponse *lpsResponse)
- {
- object_ptr<ECGenericObjectTable> lpTable;
- unsigned int ulbkPosition = 0;
- er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
- if(er != erSuccess)
- return er;
- er = lpTable->CreateBookmark(&ulbkPosition);
- if(er != erSuccess)
- return er;
- lpsResponse->ulbkPosition = ulbkPosition;
- return erSuccess;
- }
- SOAP_ENTRY_END()
- /**
- * tableCreateBookmark: called from IMAPITable::FreeBookmark()
- */
- SOAP_ENTRY_START(tableFreeBookmark, *result, unsigned int ulTableId, unsigned int ulbkPosition, unsigned int *result)
- {
- object_ptr<ECGenericObjectTable> lpTable;
- er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
- if(er != erSuccess)
- return er;
- return lpTable->FreeBookmark(ulbkPosition);
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(tableExpandRow, lpsResponse->er, unsigned int ulTableId, xsd__base64Binary sInstanceKey, unsigned int ulRowCount, unsigned int ulFlags, tableExpandRowResponse* lpsResponse)
- {
- object_ptr<ECGenericObjectTable> lpTable;
- struct rowSet *lpRowSet = NULL;
- unsigned int ulMoreRows = 0;
- er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
- if(er != erSuccess)
- return er;
- er = lpTable->ExpandRow(soap, sInstanceKey, ulRowCount, ulFlags, &lpRowSet, &ulMoreRows);
- if(er != erSuccess)
- return er;
- if (!bSupportUnicode) {
- er = FixRowSetEncoding(soap, stringCompat, Out, lpRowSet);
- if (er != erSuccess)
- return er;
- }
- lpsResponse->ulMoreRows = ulMoreRows;
- lpsResponse->rowSet = *lpRowSet;
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(tableCollapseRow, lpsResponse->er, unsigned int ulTableId, xsd__base64Binary sInstanceKey, unsigned int ulFlags, tableCollapseRowResponse* lpsResponse)
- {
- object_ptr<ECGenericObjectTable> lpTable;
- unsigned int ulRows = 0;
- er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
- if(er != erSuccess)
- return er;
- er = lpTable->CollapseRow(sInstanceKey, ulFlags, &ulRows);
- if(er != erSuccess)
- return er;
- lpsResponse->ulRows = ulRows;
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(tableGetCollapseState, lpsResponse->er, unsigned int ulTableId, struct xsd__base64Binary sBookmark, tableGetCollapseStateResponse *lpsResponse)
- {
- object_ptr<ECGenericObjectTable> lpTable;
- er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
- if(er != erSuccess)
- return er;
- return lpTable->GetCollapseState(soap, sBookmark, &lpsResponse->sCollapseState);
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(tableSetCollapseState, lpsResponse->er, unsigned int ulTableId, struct xsd__base64Binary sCollapseState, struct tableSetCollapseStateResponse *lpsResponse);
- {
- object_ptr<ECGenericObjectTable> lpTable;
- er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
- if(er != erSuccess)
- return er;
- return lpTable->SetCollapseState(sCollapseState, &lpsResponse->ulBookmark);
- }
- SOAP_ENTRY_END()
- /**
- * tableSetMultiStoreEntryIDs: client sets content for MultiStoreTable
- */
- SOAP_ENTRY_START(tableSetMultiStoreEntryIDs, *result, unsigned int ulTableId, struct entryList *lpEntryList, unsigned int *result)
- {
- object_ptr<ECGenericObjectTable> lpTable;
- ECMultiStoreTable *lpMultiStoreTable = NULL;
- ECListInt lObjectList;
- if (lpEntryList == nullptr)
- return KCERR_INVALID_PARAMETER;
- er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
- if (er != erSuccess)
- return er;
- // ignore errors
- g_lpSessionManager->GetCacheManager()->GetEntryListToObjectList(lpEntryList, &lObjectList);
- lpMultiStoreTable = dynamic_cast<ECMultiStoreTable *>(lpTable.get());
- if (lpMultiStoreTable == nullptr)
- return KCERR_INVALID_PARAMETER;
- return lpMultiStoreTable->SetEntryIDs(&lObjectList);
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(tableMulti, lpsResponse->er, struct tableMultiRequest sRequest, struct tableMultiResponse *lpsResponse)
- {
- unsigned int ulTableId = sRequest.ulTableId;
- object_ptr<ECGenericObjectTable> lpTable;
- struct rowSet *lpRowSet = NULL;
-
- if(sRequest.lpOpen) {
- er = OpenTable(lpecSession, sRequest.lpOpen->sEntryId, sRequest.lpOpen->ulTableType, sRequest.lpOpen->ulType, sRequest.lpOpen->ulFlags, &lpsResponse->ulTableId);
- if(er != erSuccess)
- return er;
- ulTableId = lpsResponse->ulTableId;
- }
-
- er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
- if(er != erSuccess)
- return er;
- if(sRequest.lpSort) {
- er = lpTable->SetSortOrder(&sRequest.lpSort->sSortOrder, sRequest.lpSort->ulCategories, sRequest.lpSort->ulExpanded);
- if(er != erSuccess)
- return er;
- }
- if(sRequest.lpSetColumns) {
- er = lpTable->SetColumns(sRequest.lpSetColumns, false);
- if(er != erSuccess)
- return er;
- }
-
- if(sRequest.lpRestrict || (sRequest.ulFlags&TABLE_MULTI_CLEAR_RESTRICTION)) {
- if (!bSupportUnicode && sRequest.lpRestrict) {
- er = FixRestrictionEncoding(soap, stringCompat, In, sRequest.lpRestrict);
- if (er != erSuccess)
- return er;
- }
- er = lpTable->Restrict(sRequest.lpRestrict);
- if(er != erSuccess)
- return er;
- }
-
- if(sRequest.lpQueryRows) {
- er = lpTable->QueryRows(soap, sRequest.lpQueryRows->ulCount, sRequest.lpQueryRows->ulFlags, &lpRowSet);
- if(er != erSuccess)
- return er;
- if (!bSupportUnicode) {
- er = FixRowSetEncoding(soap, stringCompat, Out, lpRowSet);
- if (er != erSuccess)
- return er;
- }
-
- lpsResponse->sRowSet.__ptr = lpRowSet->__ptr;
- lpsResponse->sRowSet.__size = lpRowSet->__size;
- }
- return erSuccess;
- }
- SOAP_ENTRY_END()
- // Delete a set of messages, recipients, or attachments
- SOAP_ENTRY_START(deleteObjects, *result, unsigned int ulFlags, struct entryList *lpEntryList, unsigned int ulSyncId, unsigned int *result)
- {
- ECListInt lObjectList;
- unsigned int ulDeleteFlags = EC_DELETE_ATTACHMENTS | EC_DELETE_RECIPIENTS | EC_DELETE_CONTAINER | EC_DELETE_MESSAGES;
- USE_DATABASE();
- if (lpEntryList == nullptr)
- return KCERR_INVALID_PARAMETER;
- if(ulFlags & DELETE_HARD_DELETE)
- ulDeleteFlags |= EC_DELETE_HARD_DELETE;
- // ignore errors
- g_lpSessionManager->GetCacheManager()->GetEntryListToObjectList(lpEntryList, &lObjectList);
- return DeleteObjects(lpecSession, lpDatabase, &lObjectList, ulDeleteFlags, ulSyncId, false, true);
- }
- SOAP_ENTRY_END()
- // Delete everything in a folder, but not the folder itself
- // Quirk: this works with messages also, deleting attachments and recipients, but not the message itself.
- //FIXME: michel? what with associated messages ?
- SOAP_ENTRY_START(emptyFolder, *result, entryId sEntryId, unsigned int ulFlags, unsigned int ulSyncId, unsigned int *result)
- {
- unsigned int ulDeleteFlags = 0;
- unsigned int ulId = 0;
- ECListInt lObjectIds;
- USE_DATABASE();
- er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulId);
- if(er != erSuccess)
- goto exit;
- // Check Rights set permission
- er = lpecSession->GetSecurity()->CheckPermission(ulId, ecSecurityDelete);
- if(er != erSuccess)
- goto exit;
- // Add object into the list
- lObjectIds.push_back(ulId);
- ulDeleteFlags = EC_DELETE_MESSAGES | EC_DELETE_FOLDERS | EC_DELETE_RECIPIENTS | EC_DELETE_ATTACHMENTS;
- if (ulFlags & DELETE_HARD_DELETE)
- ulDeleteFlags |= EC_DELETE_HARD_DELETE;
- if((ulFlags&DEL_ASSOCIATED) == 0)
- ulDeleteFlags |= EC_DELETE_NOT_ASSOCIATED_MSG;
- er = DeleteObjects(lpecSession, lpDatabase, &lObjectIds, ulDeleteFlags, ulSyncId, false, true);
- if (er != erSuccess)
- goto exit;
- exit:
- ROLLBACK_ON_ERROR();
- }
- SOAP_ENTRY_END()
- /* FIXME
- *
- * Currently, when deleteFolders is called with DEL_FOLDERS but without DEL_MESSAGES, it will return an error
- * when a subfolder of the specified folder contains messages. I don't think this is up to spec. DeleteObjects
- * should therefore be changed so that the check is only done against messages and folders directly under the
- * top-level object.
- */
- // Deletes a complete folder, with optional recursive subfolder and submessage deletion
- SOAP_ENTRY_START(deleteFolder, *result, entryId sEntryId, unsigned int ulFlags, unsigned int ulSyncId, unsigned int *result)
- {
- unsigned int ulDeleteFlags = 0;
- unsigned int ulId = 0;
- unsigned int ulFolderFlags = 0;
- ECListInt lObjectIds;
- USE_DATABASE();
- er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulId);
- if(er != erSuccess)
- goto exit;
- // Check permission
- er = lpecSession->GetSecurity()->CheckPermission(ulId, ecSecurityFolderAccess);
- if(er != erSuccess)
- goto exit;
- er = g_lpSessionManager->GetCacheManager()->GetObjectFlags(ulId, &ulFolderFlags);
- if(er != erSuccess)
- goto exit;
- // insert objectid into the delete list
- lObjectIds.push_back(ulId);
- ulDeleteFlags = EC_DELETE_CONTAINER;
- if(ulFlags & DEL_FOLDERS)
- ulDeleteFlags |= EC_DELETE_FOLDERS;
- if(ulFlags & DEL_MESSAGES)
- ulDeleteFlags |= EC_DELETE_MESSAGES | EC_DELETE_RECIPIENTS | EC_DELETE_ATTACHMENTS;
- if( (ulFlags & DELETE_HARD_DELETE) || ulFolderFlags == FOLDER_SEARCH)
- ulDeleteFlags |= EC_DELETE_HARD_DELETE;
- er = DeleteObjects(lpecSession, lpDatabase, &lObjectIds, ulDeleteFlags, ulSyncId, false, true);
- if (er != erSuccess)
- goto exit;
- exit:
- ROLLBACK_ON_ERROR();
- }
- SOAP_ENTRY_END()
- static ECRESULT DoNotifySubscribe(ECSession *lpecSession,
- unsigned long long ulSessionId, struct notifySubscribe *notifySubscribe)
- {
- ECRESULT er = erSuccess;
- unsigned int ulKey = 0;
- object_ptr<ECGenericObjectTable> lpTable;
- //NOTE: An sKey with size 4 is a table notification id
- if(notifySubscribe->sKey.__size == 4) {
- memcpy(&ulKey, notifySubscribe->sKey.__ptr, 4);
- }else {
- er = lpecSession->GetObjectFromEntryId(¬ifySubscribe->sKey, &ulKey);
- if(er != erSuccess)
- return er;
- // Check permissions
- er = lpecSession->GetSecurity()->CheckPermission(ulKey, ecSecurityFolderVisible);
- if(er != erSuccess)
- return er;
- }
-
- if(notifySubscribe->ulEventMask & fnevTableModified) {
- // An advise has been done on a table. The table ID is in 'ulKey' in this case. When this is done
- // we have to populate the table first since row modifications would otherwise be wrong until the
- // table is populated; if the table is unpopulated and a row changes, the row will be added into the table
- // whenever it is modified, producing a TABLE_ROW_ADDED for that row instead of the correct TABLE_ROW_MODIFIED.
- er = lpecSession->GetTableManager()->GetTable(ulKey, &~lpTable);
- if(er != erSuccess)
- return er;
- er = lpTable->Populate();
- if(er != erSuccess)
- return er;
- }
- er = lpecSession->AddAdvise(notifySubscribe->ulConnection, ulKey, notifySubscribe->ulEventMask);
- if (er == erSuccess)
- TRACE_SOAP(TRACE_INFO, "ns__notifySubscribe", "connectionId: %d SessionId: %d Mask: %d",notifySubscribe->ulConnection, ulSessionId, notifySubscribe->ulEventMask);
- return er;
- }
- SOAP_ENTRY_START(notifySubscribe, *result, struct notifySubscribe *notifySubscribe, unsigned int *result)
- {
- if (notifySubscribe == nullptr)
- return KCERR_INVALID_PARAMETER;
- if (notifySubscribe->ulEventMask == fnevKopanoIcsChange)
- return lpecSession->AddChangeAdvise(notifySubscribe->ulConnection, ¬ifySubscribe->sSyncState);
- else
- return DoNotifySubscribe(lpecSession, ulSessionId, notifySubscribe);
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(notifySubscribeMulti, *result, struct notifySubscribeArray *notifySubscribeArray, unsigned int *result)
- {
- if (notifySubscribeArray == nullptr)
- return KCERR_INVALID_PARAMETER;
- for (gsoap_size_t i = 0; i < notifySubscribeArray->__size; ++i) {
- if (notifySubscribeArray->__ptr[i].ulEventMask == fnevKopanoIcsChange)
- er = lpecSession->AddChangeAdvise(notifySubscribeArray->__ptr[i].ulConnection, ¬ifySubscribeArray->__ptr[i].sSyncState);
- else
- er = DoNotifySubscribe(lpecSession, ulSessionId, ¬ifySubscribeArray->__ptr[i]);
- if (er != erSuccess) {
- for (gsoap_size_t j = 0; j < i; ++j)
- lpecSession->DelAdvise(notifySubscribeArray->__ptr[j].ulConnection);
- break;
- }
- }
- return er;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(notifyUnSubscribe, *result, unsigned int ulConnection, unsigned int *result)
- {
- return lpecSession->DelAdvise(ulConnection);
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(notifyUnSubscribeMulti, *result, struct mv_long *ulConnectionArray, unsigned int *result)
- {
- unsigned int erTmp = erSuccess;
- unsigned int erFirst = erSuccess;
- if (ulConnectionArray == nullptr)
- return KCERR_INVALID_PARAMETER;
- for (gsoap_size_t i = 0; i < ulConnectionArray->__size; ++i) {
- erTmp = lpecSession->DelAdvise(ulConnectionArray->__ptr[i]);
- if (erTmp != erSuccess && erFirst == erSuccess)
- erFirst = erTmp;
- }
- // return first seen error (if any).
- return erFirst;
- }
- SOAP_ENTRY_END()
- /*
- * Gets notifications queued for the session group that the specified session is attached to; you can access
- * all notifications of a session group via any session on that group. The request itself is handled by the
- * ECNotificationManager class since you don't want to block the calling thread while waiting for notifications.
- */
- int ns__notifyGetItems(struct soap *soap, ULONG64 ulSessionId, struct notifyResponse *notifications)
- {
- ECRESULT er = erSuccess;
- ECSession *lpSession = NULL;
-
- // Check if the session exists, and discard result
- er = g_lpSessionManager->ValidateSession(soap, ulSessionId, &lpSession, true);
- if(er != erSuccess) {
- // Directly return with error in er
- notifications->er = er;
- // SOAP call itself succeeded
- return SOAP_OK;
- }
-
- // discard lpSession
- lpSession->Unlock();
- lpSession = NULL;
-
- g_lpSessionManager->DeferNotificationProcessing(ulSessionId, soap);
- // Return SOAP_NULL so that the caller does *nothing* with the soap struct since we have passed it to the session
- // manager for deferred processing
- throw SOAP_NULL;
- }
- SOAP_ENTRY_START(getRights, lpsRightResponse->er, entryId sEntryId, int ulType, struct rightsResponse *lpsRightResponse)
- {
- unsigned int ulobjid = 0;
- struct rightsArray *lpsRightArray = NULL;
- er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulobjid);
- if(er != erSuccess)
- goto exit;
- lpsRightArray = s_alloc<rightsArray>(nullptr);
- memset(lpsRightArray, 0, sizeof(struct rightsArray));
- er = lpecSession->GetSecurity()->GetRights(ulobjid, ulType, lpsRightArray);
- if(er != erSuccess)
- goto exit;
- er = CopyRightsArrayToSoap(soap, lpsRightArray, &lpsRightResponse->pRightsArray);
- if (er != erSuccess)
- goto exit;
- exit:
- if (lpsRightArray) {
- for (gsoap_size_t i = 0; i < lpsRightArray->__size; ++i)
- s_free(nullptr, lpsRightArray->__ptr[i].sUserId.__ptr);
- s_free(nullptr, lpsRightArray->__ptr);
- s_free(nullptr, lpsRightArray);
- }
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(setRights, *result, entryId sEntryId, struct rightsArray *lpsRightsArray, unsigned int *result)
- {
- unsigned int ulObjId = 0;
- if (lpsRightsArray == nullptr)
- return KCERR_INVALID_PARAMETER;
- er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulObjId);
- if(er != erSuccess)
- return er;
- // Check Rights set permission
- er = lpecSession->GetSecurity()->CheckPermission(ulObjId, ecSecurityFolderAccess);
- if(er != erSuccess)
- return er;
- return lpecSession->GetSecurity()->SetRights(ulObjId, lpsRightsArray);
- }
- SOAP_ENTRY_END()
- /* DEPRICATED:
- * this is only accessable through IECSecurity, and nobody calls this anymore!
- */
- SOAP_ENTRY_START(getUserObjectList, lpsUserObjectResponse->er, unsigned int ulCompanyId, entryId sCompanyId, int ulType, struct userobjectResponse *lpsUserObjectResponse)
- {
- // TODO: if we just throw the soap call away, it has the same result?
- er = KCERR_NO_SUPPORT;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(getOwner, lpsResponse->er, entryId sEntryId, struct getOwnerResponse *lpsResponse)
- {
- unsigned int ulobjid = 0;
- er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulobjid);
- if(er != erSuccess)
- return er;
- er = lpecSession->GetSecurity()->GetOwner(ulobjid, &lpsResponse->ulOwner);
- if(er != erSuccess)
- return er;
- return GetABEntryID(lpsResponse->ulOwner, soap, &lpsResponse->sOwner);
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(getIDsFromNames, lpsResponse->er, struct namedPropArray *lpsNamedProps, unsigned int ulFlags, struct getIDsFromNamesResponse *lpsResponse)
- {
- std::string strEscapedString;
- std::string strEscapedGUID;
- unsigned int ulLastId = 0;
- USE_DATABASE();
- if(lpsNamedProps == NULL) {
- er = KCERR_INVALID_PARAMETER;
- goto exit;
- }
- er = lpDatabase->Begin();
- if(er != erSuccess)
- goto exit;
- lpsResponse->lpsPropTags.__ptr = s_alloc<unsigned int>(soap, lpsNamedProps->__size);
- lpsResponse->lpsPropTags.__size = 0;
- // One query per named property (too slow ?) FIXME could be faster if brought down to less SQL queries
- for (gsoap_size_t i = 0; i < lpsNamedProps->__size; ++i) {
- strQuery = "SELECT id FROM names WHERE ";
- // ID, then add ID where clause
- if(lpsNamedProps->__ptr[i].lpId != NULL) {
- strQuery += "nameid=" + stringify(*lpsNamedProps->__ptr[i].lpId) + " ";
- }
- // String, then add STRING where clause
- else if(lpsNamedProps->__ptr[i].lpString != NULL) {
- strEscapedString = lpDatabase->Escape(lpsNamedProps->__ptr[i].lpString);
- strQuery += "namestring='" + strEscapedString + "' ";
- }
- // Add a GUID specifier if there
- if(lpsNamedProps->__ptr[i].lpguid != NULL) {
- strEscapedGUID = lpDatabase->EscapeBinary(lpsNamedProps->__ptr[i].lpguid->__ptr, lpsNamedProps->__ptr[i].lpguid->__size);
- strQuery += "AND guid=" + strEscapedGUID;
- }
- strQuery += " LIMIT 1";
- // Run the query
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if(er != erSuccess)
- goto exit;
- if(lpDatabase->GetNumRows(lpDBResult) == 0) {
- // No rows found, so the named property has not been registered yet
- if(ulFlags & MAPI_CREATE) {
- // Create requested ? then add a new named property
- // GUID must be specified with create
- if(lpsNamedProps->__ptr[i].lpguid == NULL) {
- er = KCERR_NO_ACCESS;
- goto exit;
- }
- strQuery = "INSERT INTO names (nameid, namestring, guid) VALUES(";
- if (lpsNamedProps->__ptr[i].lpId)
- strQuery += stringify(*lpsNamedProps->__ptr[i].lpId);
- else
- strQuery += "null";
- strQuery += ",";
- if (lpsNamedProps->__ptr[i].lpString)
- strQuery += "'" + strEscapedString + "'";
- else
- strQuery += "null";
- strQuery += ",";
- if (lpsNamedProps->__ptr[i].lpguid)
- strQuery += strEscapedGUID;
- else
- strQuery += "null";
- strQuery += ")";
- er = lpDatabase->DoInsert(strQuery, &ulLastId);
- if(er != erSuccess)
- goto exit;
- lpsResponse->lpsPropTags.__ptr[i] = ulLastId+1; // offset one because 0 is 'not found'
- } else {
- // No create ? Then not found
- lpsResponse->lpsPropTags.__ptr[i] = 0;
- }
- } else {
- // found it
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
- if(lpDBRow!= NULL && lpDBRow[0] != NULL)
- lpsResponse->lpsPropTags.__ptr[i] = atoi(lpDBRow[0])+1;
- else
- lpsResponse->lpsPropTags.__ptr[i] = 0;
- }
- }
- // Everything is done, now set the size
- lpsResponse->lpsPropTags.__size = lpsNamedProps->__size;
- er = lpDatabase->Commit();
- if(er != erSuccess)
- goto exit;
- exit:
- ROLLBACK_ON_ERROR();
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(getNamesFromIDs, lpsResponse->er, struct propTagArray *lpPropTags, struct getNamesFromIDsResponse *lpsResponse)
- {
- struct namedPropArray lpsNames;
- USE_DATABASE();
- if (lpPropTags == nullptr)
- return KCERR_INVALID_PARAMETER;
- er = GetNamesFromIDs(soap, lpDatabase, lpPropTags, &lpsNames);
- if (er != erSuccess)
- return er;
- lpsResponse->lpsNames = lpsNames;
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(getReceiveFolder, lpsReceiveFolder->er, entryId sStoreId, char* msg_class, struct receiveFolderResponse *lpsReceiveFolder)
- {
- const char *lpszMessageClass = msg_class;
- unsigned int ulStoreid = 0;
- const char *lpDest;
- USE_DATABASE();
- lpszMessageClass = STRIN_FIX(lpszMessageClass);
- er = lpecSession->GetObjectFromEntryId(&sStoreId, &ulStoreid);
- if(er != erSuccess)
- return er;
- // Check for default store
- if(lpszMessageClass == NULL)
- lpszMessageClass = "";
- strQuery = "SELECT objid, messageclass FROM receivefolder WHERE storeid="+stringify(ulStoreid)+" AND (";
- strQuery += "messageclass='"+lpDatabase->Escape(lpszMessageClass)+"'";
- lpDest = lpszMessageClass;
- do {
- lpDest = strchr(lpDest, '.');
- if(lpDest){
- strQuery += " OR messageclass='"+lpDatabase->Escape(string(lpszMessageClass, lpDest-lpszMessageClass))+"'";
- ++lpDest;
- }
- }while(lpDest);
- if(strlen(lpszMessageClass) != 0)
- strQuery += " OR messageclass=''";
- strQuery += ") ORDER BY length(messageclass) DESC LIMIT 1";
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if(er != erSuccess)
- return er;
- if(lpDatabase->GetNumRows(lpDBResult) == 1) {
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
- if(lpDBRow == NULL || lpDBRow[0] == NULL || lpDBRow[1] == NULL){
- ec_log_err("getReceiveFolder(): row or columns null");
- return KCERR_DATABASE_ERROR;
- }
- er = g_lpSessionManager->GetCacheManager()->GetEntryIdFromObject(atoui(lpDBRow[0]), soap, 0, &lpsReceiveFolder->sReceiveFolder.sEntryId);
- if(er != erSuccess)
- return er;
- lpsReceiveFolder->sReceiveFolder.lpszAExplicitClass = STROUT_FIX_CPY(lpDBRow[1]);
- }else{
- //items not found
- return KCERR_NOT_FOUND;
- }
- return erSuccess;
- }
- SOAP_ENTRY_END()
- // FIXME: should be able to delete an entry too
- SOAP_ENTRY_START(setReceiveFolder, *result, entryId sStoreId, entryId* lpsEntryId, char* msg_class, unsigned int *result)
- {
- const char *lpszMessageClass = msg_class;
- bool bIsUpdate = false;
- unsigned int ulCheckStoreId = 0;
- unsigned int ulStoreid = 0;
- unsigned int ulId = 0;
- USE_DATABASE();
- lpszMessageClass = STRIN_FIX(lpszMessageClass);
- er = lpDatabase->Begin();
- if (er != erSuccess)
- goto exit;
- // Check, lpsEntryId and lpszMessageClass can't both be empty or 0
- if(lpsEntryId == NULL && (lpszMessageClass == NULL || *lpszMessageClass == '\0')){
- er = KCERR_INVALID_TYPE;
- goto exit;
- }
- er = lpecSession->GetObjectFromEntryId(&sStoreId, &ulStoreid);
- if(er != erSuccess)
- goto exit;
- // an empty lpszMessageClass is the default folder
- if(lpszMessageClass == NULL)
- lpszMessageClass = "";
- // If the lpsEntryId parameter is set to NULL then replace the current receive folder with the message store's default.
- if(lpsEntryId)
- {
- // Check if object really exist and the relation between storeid and ulId
- er = lpecSession->GetObjectFromEntryId(lpsEntryId, &ulId);
- if(er != erSuccess)
- goto exit;
- // Check if storeid and ulId have a relation
- er = lpecSession->GetSessionManager()->GetCacheManager()->GetStore(ulId, &ulCheckStoreId, NULL);
- if(er != erSuccess){
- er = KCERR_INVALID_ENTRYID;
- goto exit;
- }
- if(ulStoreid != ulCheckStoreId) {
- er = KCERR_INVALID_ENTRYID;
- goto exit;
- }
- er = lpecSession->GetSecurity()->CheckDeletedParent(ulId);
- if (er != erSuccess)
- goto exit;
- } else {
- // Set MessageClass with the default of the store (that's the empty MessageClass)
- strQuery = "SELECT objid FROM receivefolder WHERE storeid="+stringify(ulStoreid)+" AND messageclass='' LIMIT 2";
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if(er != erSuccess)
- goto exit;
- if(lpDatabase->GetNumRows(lpDBResult) == 1)
- {
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
- if(lpDBRow == NULL || lpDBRow[0] == NULL){
- er = KCERR_DATABASE_ERROR;
- ec_log_err("setReceiveFolder(): row or columns null");
- goto exit;
- }
- //Set the default folder
- ulId = atoi(lpDBRow[0]);
- }else{
- er = KCERR_DATABASE_ERROR; //FIXME: no default error ?
- ec_log_err("setReceiveFolder(): unexpected row count");
- goto exit;
- }
- }
- strQuery = "SELECT objid, id FROM receivefolder WHERE storeid="+stringify(ulStoreid)+" AND messageclass='"+lpDatabase->Escape(lpszMessageClass)+"' LIMIT 1";
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if(er != erSuccess)
- goto exit;
- bIsUpdate = false;
- // If ok, item already exists, return ok
- if(lpDatabase->GetNumRows(lpDBResult) == 1){
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
- if(lpDBRow == NULL || lpDBRow[0] == NULL || lpDBRow[1] == NULL){
- er = KCERR_DATABASE_ERROR;
- ec_log_err("setReceiveFolder(): row or columns null");
- goto exit;
- }
- // Item exists
- if(ulId == atoui(lpDBRow[0])){
- er = lpDatabase->Rollback(); // Nothing changed, so Commit() would also do.
- goto exit;
- }
- bIsUpdate = true;
- }
- // Check permission
- //FIXME: also on delete?
- if(bIsUpdate)
- er = lpecSession->GetSecurity()->CheckPermission(ulStoreid, ecSecurityEdit);
- else
- er = lpecSession->GetSecurity()->CheckPermission(ulStoreid, ecSecurityCreate);
- if(er != erSuccess)
- goto exit;
- if(bIsUpdate) {
- strQuery = "UPDATE receivefolder SET objid="+stringify(ulId);
- strQuery+= " WHERE storeid="+stringify(ulStoreid)+" AND messageclass='"+lpDatabase->Escape(lpszMessageClass)+"'";
- er = lpDatabase->DoUpdate(strQuery);
- }else{
- strQuery = "INSERT INTO receivefolder (storeid, objid, messageclass) VALUES (";
- strQuery += stringify(ulStoreid)+", "+stringify(ulId)+", '"+lpDatabase->Escape(lpszMessageClass)+"')";
- er = lpDatabase->DoInsert(strQuery);
- }
- if(er != erSuccess)
- goto exit;
- er = lpDatabase->Commit();
- if (er != erSuccess)
- goto exit;
- exit:
- ROLLBACK_ON_ERROR();
- }
- SOAP_ENTRY_END()
- /*
- * WARNING
- *
- * lpsEntryID != NULL && lpMessageList != NULL: messages in lpMessageList must be set, lpsEntryId MUST BE IGNORED (may be entryid of search folder)
- * lpsEntryID == NULL && lpMessageList != NULL: called from IMessage::SetReadFlag, lpMessageList->__size == 1
- * lpsEntryID != NULL && lpMessageList == NULL: 'mark all messages as (un)read'
- *
- * Items are assumed to all be in the same store.
- *
- */
- SOAP_ENTRY_START(setReadFlags, *result, unsigned int ulFlags, entryId* lpsEntryId, struct entryList *lpMessageList, unsigned int ulSyncId, unsigned int *result)
- {
- std::list<unsigned int> lHierarchyIDs;
- std::list<std::pair<unsigned int, unsigned int> > lObjectIds;
- std::string strQueryCache;
- USE_DATABASE();
- unsigned int i = 0;
- unsigned int ulParent = 0;
- unsigned int ulGrandParent = 0;
- unsigned int ulFlagsNotify = 0;
- unsigned int ulFlagsRemove = 0;
- unsigned int ulFlagsAdd = 0;
- unsigned int ulFolderId = 0;
- size_t cObjectSize = 0;
-
- // List of unique parents
- std::map<unsigned int, int> mapParents;
- std::set<unsigned int> setParents;
- //NOTE: either lpMessageList may be NULL or lpsEntryId may be NULL
- if(ulFlags & GENERATE_RECEIPT_ONLY)
- goto exit;
- if(lpMessageList == NULL && lpsEntryId == NULL) {
- // Bad input
- er = KCERR_INVALID_PARAMETER;
- goto exit;
- }
- if (lpMessageList != nullptr)
- // Ignore errors
- g_lpSessionManager->GetCacheManager()->GetEntryListToObjectList(lpMessageList, &lHierarchyIDs);
- strQuery = "UPDATE properties SET ";
- if ((ulFlags & CLEAR_NRN_PENDING) || (ulFlags & SUPPRESS_RECEIPT) || (ulFlags & GENERATE_RECEIPT_ONLY) )
- ulFlagsRemove |= MSGFLAG_NRN_PENDING;
- if ((ulFlags & CLEAR_RN_PENDING) || (ulFlags & SUPPRESS_RECEIPT) || (ulFlags & GENERATE_RECEIPT_ONLY) )
- ulFlagsRemove |= MSGFLAG_RN_PENDING;
- if (!(ulFlags & GENERATE_RECEIPT_ONLY) && (ulFlags & CLEAR_READ_FLAG))
- ulFlagsRemove |= MSGFLAG_READ;
- else if( !(ulFlags & GENERATE_RECEIPT_ONLY) )
- ulFlagsAdd |= MSGFLAG_READ;
- if(ulFlagsRemove != 0)
- strQuery += "val_ulong=val_ulong & ~" + stringify(ulFlagsRemove);
- if(ulFlagsAdd != 0) {
- strQuery += (ulFlagsRemove!=0)?",":"";
- strQuery += "val_ulong=val_ulong | " + stringify(ulFlagsAdd);
- }
- if (ulFlagsRemove == 0 && ulFlagsAdd == 0)
- // Nothing to update
- goto exit;
- er = lpDatabase->Begin();
- if(er != erSuccess)
- goto exit;
- if(lpMessageList == NULL) {
- // No message list passed, so 'mark all items (un)read'
- er = lpecSession->GetObjectFromEntryId(lpsEntryId, &ulFolderId);
- if(er != erSuccess)
- goto exit;
-
- er = lpDatabase->DoSelect("SELECT val_ulong FROM properties WHERE hierarchyid=" + stringify(ulFolderId) + " FOR UPDATE", NULL);
- if(er != erSuccess)
- goto exit;
- // Check permission
- er = lpecSession->GetSecurity()->CheckPermission(ulFolderId, ecSecurityRead);
- if(er != erSuccess)
- goto exit;
- // Purge changes
- ECTPropsPurge::PurgeDeferredTableUpdates(lpDatabase, ulFolderId);
- // Get all items MAPI_MESSAGE exclude items with flags MSGFLAG_DELETED AND MSGFLAG_ASSOCIATED of which we will be changing flags
-
- // Note we use FOR UPDATE which locks the records in the hierarchy (and in tproperties as a sideeffect), which serializes access to the rows, avoiding deadlocks
- strQueryCache = "SELECT id, tproperties.val_ulong FROM hierarchy FORCE INDEX(parenttypeflags) JOIN tproperties FORCE INDEX(PRIMARY) ON tproperties.hierarchyid=hierarchy.id AND tproperties.tag = " + stringify(PROP_ID(PR_MESSAGE_FLAGS)) + " AND tproperties.type = " + stringify(PROP_TYPE(PR_MESSAGE_FLAGS)) + " WHERE parent="+ stringify(ulFolderId) + " AND hierarchy.type=5 AND flags = 0 AND (tproperties.val_ulong & " + stringify(ulFlagsRemove) + " OR tproperties.val_ulong & " + stringify(ulFlagsAdd) + " != " + stringify(ulFlagsAdd) + ") AND tproperties.folderid = " + stringify(ulFolderId) + " FOR UPDATE";
- er = lpDatabase->DoSelect(strQueryCache, &lpDBResult);
- if(er != erSuccess)
- goto exit;
- while( (lpDBRow = lpDatabase->FetchRow(lpDBResult)) )
- {
- if(lpDBRow[0] == NULL || lpDBRow[1] == NULL){
- er = KCERR_DATABASE_ERROR;
- ec_log_err("setReadFlags(): columns null");
- goto exit;
- }
- lObjectIds.push_back( std::pair<unsigned int, unsigned int>( atoui(lpDBRow[0]), atoui(lpDBRow[1]) ));
- ++i;
- }
- ulParent = ulFolderId;
- } else {
- if(lHierarchyIDs.empty()) {
- // Nothing to do
- lpDatabase->Commit();
- goto exit;
- }
-
- // Because the messagelist can contain messages from all over the place, we have to check permissions for all the parent folders of the items
- // we are setting 'read' or 'unread'
- for (auto hier_id : lHierarchyIDs) {
- // Get the parent object. Note that the cache will hold this information so the loop below with GetObject() will
- // be done directly from the cache (assuming it's not too large)
- if (g_lpSessionManager->GetCacheManager()->GetObject(hier_id, &ulParent, NULL, NULL) != erSuccess)
- continue;
- setParents.insert(ulParent);
- }
- // Lock parent folders
- for (auto parent_id : setParents) {
- er = lpDatabase->DoSelect("SELECT val_ulong FROM properties WHERE hierarchyid=" + stringify(parent_id) + " FOR UPDATE", NULL);
- if(er != erSuccess)
- goto exit;
- }
- // Check permission
- for (auto parent_id : setParents) {
- er = lpecSession->GetSecurity()->CheckPermission(parent_id, ecSecurityRead);
- if(er != erSuccess)
- goto exit;
- }
-
- // Now find all messages that will actually change
- strQueryCache = "SELECT id, properties.val_ulong FROM hierarchy JOIN properties ON hierarchy.id=properties.hierarchyid AND properties.tag = " + stringify(PROP_ID(PR_MESSAGE_FLAGS)) + " AND properties.type = " + stringify(PROP_TYPE(PR_MESSAGE_FLAGS)) + " WHERE hierarchy.type=5 AND flags = 0 AND (properties.val_ulong & " + stringify(ulFlagsRemove) + " OR properties.val_ulong & " + stringify(ulFlagsAdd) + " != " + stringify(ulFlagsAdd) + ") AND hierarchyid IN (";
- for (auto hier = lHierarchyIDs.cbegin();
- hier != lHierarchyIDs.cend(); ++hier) {
- if (hier != lHierarchyIDs.cbegin())
- strQueryCache += ",";
- strQueryCache += stringify(*hier);
- }
- strQueryCache += ") FOR UPDATE"; // See comment above about FOR UPDATE
- er = lpDatabase->DoSelect(strQueryCache, &lpDBResult);
- if(er != erSuccess)
- goto exit;
- while( (lpDBRow = lpDatabase->FetchRow(lpDBResult)) )
- {
- if(lpDBRow[0] == NULL || lpDBRow[1] == NULL){
- er = KCERR_DATABASE_ERROR;
- ec_log_err("setReadFlags(): columns null(2)");
- goto exit;
- }
- lObjectIds.push_back( std::pair<unsigned int, unsigned int>( atoui(lpDBRow[0]), atoui(lpDBRow[1]) ) );
- ++i;
- }
- }
- // Security passed, and we have a list of all the items that must be changed, and the records are locked
-
- // Check if there is anything to do
- if(lObjectIds.empty()) {
- lpDatabase->Commit();
- goto exit;
- }
- strQuery += " WHERE properties.hierarchyid IN(";
- lHierarchyIDs.clear();
- for (auto iObjectid = lObjectIds.cbegin(); iObjectid != lObjectIds.cend(); ++iObjectid) {
- if (iObjectid != lObjectIds.cbegin())
- strQuery += ",";
- strQuery += stringify(iObjectid->first);
- lHierarchyIDs.push_back(iObjectid->first);
- }
- strQuery += ")";
- strQuery += " AND properties.tag = " + stringify(PROP_ID(PR_MESSAGE_FLAGS)) + " AND properties.type = " + stringify(PROP_TYPE(PR_MESSAGE_FLAGS));
- // Update the database
- er = lpDatabase->DoUpdate(strQuery);
- if(er != erSuccess)
- goto exit;
- er = UpdateTProp(lpDatabase, PR_MESSAGE_FLAGS, ulParent, &lHierarchyIDs); // FIXME ulParent is not constant for all lHierarchyIDs
- if(er != erSuccess)
- goto exit;
-
- // Add changes to ICS
- for (const auto &op : lObjectIds) {
- bool read = (ulFlagsRemove & MSGFLAG_READ) ||
- (ulFlagsAdd & MSGFLAG_READ);
- if (!read)
- continue;
- // Only save ICS change when the actual readflag has changed
- SOURCEKEY sSourceKey;
- SOURCEKEY sParentSourceKey;
- if (g_lpSessionManager->GetCacheManager()->GetObject(op.first, &ulParent, NULL, NULL) != erSuccess)
- continue;
- GetSourceKey(op.first, &sSourceKey);
- GetSourceKey(ulParent, &sParentSourceKey);
- // Because we know that ulFlagsRemove && MSGFLAG_READ || ulFlagsAdd & MSGFLAG_READ and we assume
- // that they are never both TRUE, we can ignore ulFlagsRemove and just look at ulFlagsAdd for the new
- // readflag state
- AddChange(lpecSession, ulSyncId, sSourceKey, sParentSourceKey, ICS_MESSAGE_FLAG, ulFlagsAdd & MSGFLAG_READ);
- }
- // Update counters, by counting the number of changes per folder
- for (const auto &op : lObjectIds) {
- er = g_lpSessionManager->GetCacheManager()->GetObject(op.first, &ulParent, NULL, NULL);
- if (er != erSuccess)
- goto exit;
-
- mapParents.insert(std::pair<unsigned int, unsigned int>(ulParent, 0));
-
- if (ulFlagsAdd & MSGFLAG_READ &&
- (op.second & MSGFLAG_READ) == 0)
- --mapParents[ulParent]; // Decrease unread count
- if (ulFlagsRemove & MSGFLAG_READ && op.second & MSGFLAG_READ)
- ++mapParents[ulParent]; // Increase unread count
- }
-
- for (const auto &p : mapParents) {
- if (p.second == 0)
- continue;
- er = g_lpSessionManager->GetCacheManager()->GetParent(p.first, &ulGrandParent);
- if(er != erSuccess)
- goto exit;
- er = UpdateFolderCount(lpDatabase, p.first, PR_CONTENT_UNREAD, p.second);
- if (er != erSuccess)
- goto exit;
- }
-
- er = lpDatabase->Commit();
- if(er != erSuccess)
- goto exit;
- // Now, update cache and send the notifications
- cObjectSize = lObjectIds.size();
- // Loop through the messages, updating each
- for (const auto &op : lObjectIds) {
- // Remove the item from the cache
- g_lpSessionManager->GetCacheManager()->UpdateCell(op.first,
- PR_MESSAGE_FLAGS,
- (ulFlagsAdd | ulFlagsRemove) & MSGFLAG_READ,
- ulFlagsAdd & MSGFLAG_READ);
- if (g_lpSessionManager->GetCacheManager()->GetObject(op.first,
- &ulParent, NULL, &ulFlagsNotify) != erSuccess) {
- ulParent = 0;
- ulFlagsNotify = 0;
- }
- // Update the message itself in tables and object notification
- g_lpSessionManager->NotificationModified(MAPI_MESSAGE,
- op.first, ulParent);
- if(ulParent && cObjectSize < EC_TABLE_CHANGE_THRESHOLD)
- g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY,
- ulFlagsNotify & MSGFLAG_NOTIFY_FLAGS, ulParent,
- op.first, MAPI_MESSAGE);
- }
- // Loop through all the parent folders of the objects, sending notifications for them
- for (const auto &p : mapParents) {
- // The parent has changed its PR_CONTENT_UNREAD
- g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, p.first);
- g_lpSessionManager->NotificationModified(MAPI_FOLDER, p.first);
- // The grand parent's table view of the parent has changed
- if (g_lpSessionManager->GetCacheManager()->GetObject(p.first,
- &ulGrandParent, NULL, &ulFlagsNotify) == erSuccess)
- g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY,
- ulFlagsNotify & MSGFLAG_NOTIFY_FLAGS,
- ulGrandParent, p.first, MAPI_FOLDER);
- if(cObjectSize >= EC_TABLE_CHANGE_THRESHOLD)
- g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_CHANGE,
- ulFlagsNotify & MSGFLAG_NOTIFY_FLAGS,
- p.first, 0, MAPI_MESSAGE);
- }
- exit:
- ROLLBACK_ON_ERROR();
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(createUser, lpsUserSetResponse->er, struct user *lpsUser, struct setUserResponse *lpsUserSetResponse)
- {
- unsigned int ulUserId = 0;
- objectdetails_t details(ACTIVE_USER); // should this function also be able to createContact?
- if (lpsUser == NULL || lpsUser->lpszUsername == NULL || lpsUser->lpszFullName == NULL || lpsUser->lpszMailAddress == NULL ||
- (lpsUser->lpszPassword == nullptr && lpsUser->ulIsNonActive == 0))
- return KCERR_INVALID_PARAMETER;
- if (!bSupportUnicode) {
- er = FixUserEncoding(soap, stringCompat, In, lpsUser);
- if (er != erSuccess)
- return er;
- }
- er = CopyUserDetailsFromSoap(lpsUser, NULL, &details, soap);
- if (er != erSuccess)
- return er;
- er = lpecSession->GetUserManagement()->UpdateUserDetailsFromClient(&details);
- if (er != erSuccess)
- return er;
- // Check permission
- er = lpecSession->GetSecurity()->IsAdminOverUserObject(details.GetPropInt(OB_PROP_I_COMPANYID));
- if(er != erSuccess)
- return er;
- // Create user and sync
- er = lpecSession->GetUserManagement()->CreateObjectAndSync(details, &ulUserId);
- if(er != erSuccess)
- return er;
- er = GetABEntryID(ulUserId, soap, &lpsUserSetResponse->sUserId);
- if (er != erSuccess)
- return er;
- lpsUserSetResponse->ulUserId = ulUserId;
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(setUser, *result, struct user *lpsUser, unsigned int *result)
- {
- objectdetails_t details;
- objectdetails_t oldDetails;
- unsigned int ulUserId = 0;
- objectid_t sExternId;
- if (lpsUser == nullptr)
- return KCERR_INVALID_PARAMETER;
- if (!bSupportUnicode) {
- er = FixUserEncoding(soap, stringCompat, In, lpsUser);
- if (er != erSuccess)
- return er;
- }
- if (lpsUser->sUserId.__size > 0 && lpsUser->sUserId.__ptr != NULL)
- {
- er = GetLocalId(lpsUser->sUserId, lpsUser->ulUserId, &ulUserId, &sExternId);
- if (er != erSuccess)
- return er;
- }
- else
- ulUserId = lpsUser->ulUserId;
- if(ulUserId) {
- er = lpecSession->GetUserManagement()->GetObjectDetails(ulUserId, &oldDetails);
- if (er != erSuccess)
- return er;
- }
- // Check security
- // @todo add check on anonymous (mv)properties
- if (lpecSession->GetSecurity()->IsAdminOverUserObject(ulUserId) == erSuccess) {
- // admins can update anything of a user
- // FIXME: prevent the user from removing admin rights from itself?
- er = erSuccess;
- } else if (lpecSession->GetSecurity()->GetUserId() == ulUserId) {
- // you're only allowed to set your password, force the lpsUser struct to only contain that update
- if (lpsUser->lpszUsername && oldDetails.GetPropString(OB_PROP_S_LOGIN) != lpsUser->lpszUsername) {
- ec_log_warn("Disallowing user \"%s\" to update their username to \"%s\"",
- oldDetails.GetPropString(OB_PROP_S_LOGIN).c_str(), lpsUser->lpszUsername);
- lpsUser->lpszUsername = NULL;
- }
- // leave lpszPassword
- if (lpsUser->lpszMailAddress && oldDetails.GetPropString(OB_PROP_S_EMAIL) != lpsUser->lpszMailAddress) {
- ec_log_warn("Disallowing user \"%s\" to update their mail address to \"%s\"",
- oldDetails.GetPropString(OB_PROP_S_LOGIN).c_str(), lpsUser->lpszMailAddress);
- lpsUser->lpszMailAddress = NULL;
- }
- if (lpsUser->lpszFullName && oldDetails.GetPropString(OB_PROP_S_FULLNAME) != lpsUser->lpszFullName) {
- ec_log_warn("Disallowing user \"%s\" to update their fullname to \"%s\"",
- oldDetails.GetPropString(OB_PROP_S_LOGIN).c_str(), lpsUser->lpszFullName);
- lpsUser->lpszFullName = NULL;
- }
- if (lpsUser->lpszServername && oldDetails.GetPropString(OB_PROP_S_SERVERNAME) != lpsUser->lpszServername) {
- ec_log_warn("Disallowing user \"%s\" to update their home server to \"%s\"",
- oldDetails.GetPropString(OB_PROP_S_LOGIN).c_str(), lpsUser->lpszServername);
- lpsUser->lpszServername = NULL;
- }
- // FIXME: check OB_PROP_B_NONACTIVE too? NOTE: ulIsNonActive is now ignored.
- if (lpsUser->ulObjClass != (ULONG)-1 && oldDetails.GetClass() != (objectclass_t)lpsUser->ulObjClass) {
- ec_log_warn("Disallowing user \"%s\" to update their active flag to %d",
- oldDetails.GetPropString(OB_PROP_S_LOGIN).c_str(), lpsUser->ulObjClass);
- lpsUser->ulObjClass = (ULONG)-1;
- }
- if (lpsUser->ulIsAdmin != (ULONG)-1 && oldDetails.GetPropInt(OB_PROP_I_ADMINLEVEL) != lpsUser->ulIsAdmin) {
- ec_log_warn("Disallowing user \"%s\" to update their admin flag to %d",
- oldDetails.GetPropString(OB_PROP_S_LOGIN).c_str(), lpsUser->ulIsAdmin);
- lpsUser->ulIsAdmin = (ULONG)-1;
- }
- } else {
- // you cannot set any details if you're not an admin or not yourself
- ec_log_warn(
- "Disallowing user \"%s\" to update details of user \"%s\"",
- oldDetails.GetPropString(OB_PROP_S_LOGIN).c_str(),
- lpsUser->lpszUsername);
- return KCERR_NO_ACCESS;
- }
- details = objectdetails_t(ACTIVE_USER);
- // construct new details
- er = CopyUserDetailsFromSoap(lpsUser, &sExternId.id, &details, soap);
- if (er != erSuccess)
- return er;
- er = lpecSession->GetUserManagement()->UpdateUserDetailsFromClient(&details);
- if (er != erSuccess)
- return er;
- return lpecSession->GetUserManagement()->SetObjectDetailsAndSync(ulUserId, details, nullptr);
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(getUser, lpsGetUserResponse->er, unsigned int ulUserId, entryId sUserId, struct getUserResponse *lpsGetUserResponse)
- {
- objectdetails_t details;
- entryId sTmpUserId = {0};
- er = GetLocalId(sUserId, ulUserId, &ulUserId, NULL);
- if (er != erSuccess)
- return er;
- /* Check if we are able to view the returned userobject */
- er = lpecSession->GetSecurity()->IsUserObjectVisible(ulUserId);
- if (er != erSuccess)
- return er;
- lpsGetUserResponse->lpsUser = s_alloc<user>(soap);
- memset(lpsGetUserResponse->lpsUser, 0, sizeof(struct user));
- if (ulUserId == 0)
- ulUserId = lpecSession->GetSecurity()->GetUserId();
- er = lpecSession->GetUserManagement()->GetObjectDetails(ulUserId, &details);
- if (er != erSuccess)
- return er;
- if (OBJECTCLASS_TYPE(details.GetClass()) != OBJECTTYPE_MAILUSER)
- return KCERR_NOT_FOUND;
- er = GetABEntryID(ulUserId, soap, &sTmpUserId);
- if (er == erSuccess)
- er = CopyUserDetailsToSoap(ulUserId, &sTmpUserId, details, lpecSession->GetCapabilities() & KOPANO_CAP_EXTENDED_ANON, soap, lpsGetUserResponse->lpsUser);
- if (er != erSuccess)
- return er;
- // 6.40.0 stores the object class in the IsNonActive field
- if (lpecSession->ClientVersion() == ZARAFA_VERSION_6_40_0)
- lpsGetUserResponse->lpsUser->ulIsNonActive = lpsGetUserResponse->lpsUser->ulObjClass;
- if (!bSupportUnicode) {
- er = FixUserEncoding(soap, stringCompat, Out, lpsGetUserResponse->lpsUser);
- if (er != erSuccess)
- return er;
- }
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(getUserList, lpsUserList->er, unsigned int ulCompanyId, entryId sCompanyId, struct userListResponse *lpsUserList)
- {
- std::unique_ptr<std::list<localobjectdetails_t> > lpUsers;
- entryId sUserEid = {0};
- er = GetLocalId(sCompanyId, ulCompanyId, &ulCompanyId, NULL);
- if (er != erSuccess)
- return er;
- /* Input check, if ulCompanyId is 0, we want the user's company,
- * otherwise we must check if the requested company is visible for the user. */
- if (ulCompanyId == 0)
- er = lpecSession->GetSecurity()->GetUserCompany(&ulCompanyId);
- else
- er = lpecSession->GetSecurity()->IsUserObjectVisible(ulCompanyId);
- if (er != erSuccess)
- return er;
- er = lpecSession->GetUserManagement()->GetCompanyObjectListAndSync(OBJECTCLASS_USER, ulCompanyId, &unique_tie(lpUsers), 0);
- if(er != erSuccess)
- return er;
- lpsUserList->sUserArray.__size = 0;
- lpsUserList->sUserArray.__ptr = s_alloc<user>(soap, lpUsers->size());
- for (const auto &user : *lpUsers) {
- if (OBJECTCLASS_TYPE(user.GetClass()) != OBJECTTYPE_MAILUSER ||
- user.GetClass() == NONACTIVE_CONTACT)
- continue;
- er = GetABEntryID(user.ulId, soap, &sUserEid);
- if (er != erSuccess)
- return er;
- er = CopyUserDetailsToSoap(user.ulId, &sUserEid, user,
- lpecSession->GetCapabilities() & KOPANO_CAP_EXTENDED_ANON,
- soap, &lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size]);
- if (er != erSuccess)
- return er;
- // 6.40.0 stores the object class in the IsNonActive field
- if (lpecSession->ClientVersion() == ZARAFA_VERSION_6_40_0)
- lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size].ulIsNonActive = lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size].ulObjClass;
- if (!bSupportUnicode) {
- er = FixUserEncoding(soap, stringCompat, Out, lpsUserList->sUserArray.__ptr + lpsUserList->sUserArray.__size);
- if (er != erSuccess)
- return er;
- }
- ++lpsUserList->sUserArray.__size;
- }
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(getSendAsList, lpsUserList->er, unsigned int ulUserId, entryId sUserId, struct userListResponse *lpsUserList)
- {
- objectdetails_t userDetails, senderDetails;
- list<unsigned int> userIds;
- entryId sSenderEid = {0};
- er = GetLocalId(sUserId, ulUserId, &ulUserId, NULL);
- if (er != erSuccess)
- return er;
- er = lpecSession->GetSecurity()->IsUserObjectVisible(ulUserId);
- if (er != erSuccess)
- return er;
- er = lpecSession->GetUserManagement()->GetObjectDetails(ulUserId, &userDetails);
- if (er != erSuccess)
- return er;
- userIds = userDetails.GetPropListInt(OB_PROP_LI_SENDAS);
- lpsUserList->sUserArray.__size = 0;
- lpsUserList->sUserArray.__ptr = s_alloc<user>(soap, userIds.size());
- for (auto user_id : userIds) {
- if (lpecSession->GetSecurity()->IsUserObjectVisible(user_id) != erSuccess)
- continue;
- er = lpecSession->GetUserManagement()->GetObjectDetails(user_id, &senderDetails);
- if (er == KCERR_NOT_FOUND)
- continue;
- if (er != erSuccess)
- return er;
- er = GetABEntryID(user_id, soap, &sSenderEid);
- if (er != erSuccess)
- return er;
- er = CopyUserDetailsToSoap(user_id, &sSenderEid, senderDetails,
- lpecSession->GetCapabilities() & KOPANO_CAP_EXTENDED_ANON,
- soap, &lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size]);
- if (er != erSuccess)
- return er;
- // 6.40.0 stores the object class in the IsNonActive field
- if (lpecSession->ClientVersion() == ZARAFA_VERSION_6_40_0)
- lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size].ulIsNonActive = lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size].ulObjClass;
- if (!bSupportUnicode) {
- er = FixUserEncoding(soap, stringCompat, Out, lpsUserList->sUserArray.__ptr + lpsUserList->sUserArray.__size);
- if (er != erSuccess)
- return er;
- }
- ++lpsUserList->sUserArray.__size;
- }
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(addSendAsUser, *result, unsigned int ulUserId, entryId sUserId, unsigned int ulSenderId, entryId sSenderId, unsigned int *result)
- {
- er = GetLocalId(sUserId, ulUserId, &ulUserId, NULL);
- if (er != erSuccess) {
- ec_log_err("addSendAsUser(): GetLocalId(ulUserId) failed %x", er);
- return er;
- }
- er = GetLocalId(sSenderId, ulSenderId, &ulSenderId, NULL);
- if (er != erSuccess) {
- ec_log_err("addSendAsUser(): GetLocalId(ulSenderId) failed %x", er);
- return er;
- }
- if (ulUserId == ulSenderId) {
- ec_log_err("addSendAsUser(): ulUserId == ulSenderId");
- return KCERR_COLLISION;
- }
- // Check security, only admins can set sendas users, not the user itself
- if(lpecSession->GetSecurity()->IsAdminOverUserObject(ulUserId) != erSuccess) {
- ec_log_err("addSendAsUser(): IsAdminOverUserObject failed %x", er);
- return KCERR_NO_ACCESS;
- }
- // needed?
- er = lpecSession->GetSecurity()->IsUserObjectVisible(ulUserId);
- if (er != erSuccess) {
- ec_log_err("addSendAsUser(): IsUserObjectVisible failed %x", er);
- return er;
- }
- er = lpecSession->GetUserManagement()->AddSubObjectToObjectAndSync(OBJECTRELATION_USER_SENDAS, ulUserId, ulSenderId);
- if (er != erSuccess)
- ec_log_err("addSendAsUser(): AddSubObjectToObjectAndSync failed %x", er);
- return er;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(delSendAsUser, *result, unsigned int ulUserId, entryId sUserId, unsigned int ulSenderId, entryId sSenderId, unsigned int *result)
- {
- er = GetLocalId(sUserId, ulUserId, &ulUserId, NULL);
- if (er != erSuccess)
- return er;
- er = GetLocalId(sSenderId, ulSenderId, &ulSenderId, NULL);
- if (er != erSuccess)
- return er;
- if (ulUserId == ulSenderId) {
- ec_log_err("delSendAsUser(): ulUserId == ulSenderId");
- return KCERR_COLLISION;
- }
- // Check security, only admins can set sendas users, not the user itself
- if (lpecSession->GetSecurity()->IsAdminOverUserObject(ulUserId) != erSuccess)
- return KCERR_NO_ACCESS;
- // needed ?
- er = lpecSession->GetSecurity()->IsUserObjectVisible(ulUserId);
- if (er != erSuccess)
- return er;
- return lpecSession->GetUserManagement()->DeleteSubObjectFromObjectAndSync(OBJECTRELATION_USER_SENDAS, ulUserId, ulSenderId);
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(purgeSoftDelete, *result, unsigned int ulDays, unsigned int *result)
- {
- unsigned int ulFolders = 0;
- unsigned int ulMessages = 0;
- unsigned int ulStores = 0;
- // Only system-admins may run this
- if (lpecSession->GetSecurity()->GetAdminLevel() < ADMIN_LEVEL_SYSADMIN)
- return KCERR_NO_ACCESS;
- ec_log_info("Start forced softdelete clean up");
- er = PurgeSoftDelete(lpecSession, ulDays * 24 * 60 * 60, &ulMessages, &ulFolders, &ulStores, NULL);
- if (er == erSuccess)
- ec_log_info("Softdelete done: removed %d stores, %d folders, and %d messages", ulStores, ulFolders, ulMessages);
- else if (er == KCERR_BUSY)
- ec_log_info("Softdelete already running");
- else
- ec_log_info("Softdelete failed: removed %d stores, %d folders, and %d messages", ulStores, ulFolders, ulMessages);
- return er;
- }
- SOAP_ENTRY_END()
- static inline void kc_purge_cache_tcmalloc(void)
- {
- #ifdef HAVE_TCMALLOC
- auto rfm = reinterpret_cast<decltype(MallocExtension_ReleaseFreeMemory) *>
- (dlsym(NULL, "MallocExtension_ReleaseFreeMemory"));
- if (rfm != NULL)
- rfm();
- #endif
- }
- SOAP_ENTRY_START(purgeCache, *result, unsigned int ulFlags, unsigned int *result)
- {
- if (lpecSession->GetSecurity()->GetAdminLevel() < ADMIN_LEVEL_SYSADMIN)
- return KCERR_NO_ACCESS;
- er = g_lpSessionManager->GetCacheManager()->PurgeCache(ulFlags);
- kc_purge_cache_tcmalloc();
- g_lpStatsCollector->SetTime(SCN_SERVER_LAST_CACHECLEARED, time(NULL));
- return er;
- }
- SOAP_ENTRY_END()
- //Create a store
- // Info: Userid can also be a group id ('everyone' for public store)
- SOAP_ENTRY_START(createStore, *result, unsigned int ulStoreType, unsigned int ulUserId, entryId sUserId, entryId sStoreId, entryId sRootId, unsigned int ulFlags, unsigned int *result)
- {
- unsigned int ulStoreId = 0;
- unsigned int ulRootMapId = 0;
- objectdetails_t userDetails;
- unsigned int ulCompanyId = 0;
- bool bHasLocalStore = false;
- SOURCEKEY sSourceKey;
- GUID guidStore;
- time_t now;
- unsigned int timeProps[] = { PR_LAST_MODIFICATION_TIME, PR_CREATION_TIME };
- struct propVal sProp;
- struct hiloLong sHilo;
- struct rightsArray srightsArray;
- USE_DATABASE();
- memset(&srightsArray, 0 , sizeof(srightsArray));
- if((unsigned int)sStoreId.__size < sizeof(EID_V0)) {
- er = KCERR_INVALID_PARAMETER;
- goto exit;
- }
-
- // Normalize flags
- ((EID_V0 *)sStoreId.__ptr)->usFlags = 0;
- er = GetLocalId(sUserId, ulUserId, &ulUserId, NULL);
- if (er != erSuccess)
- goto exit;
- er = CheckUserStore(lpecSession, ulUserId, ulStoreType, &bHasLocalStore);
- if (er != erSuccess)
- goto exit;
- if (!bHasLocalStore && (ulFlags & EC_OVERRIDE_HOMESERVER) == 0) {
- ec_log_err("Create store requested, but store is not on this server, or server property not set for object %d", ulUserId);
- er = KCERR_NOT_FOUND;
- goto exit;
- }
- ec_log_info("Started to create store (userid=%d, type=%d)", ulUserId, ulStoreType);
- er = lpDatabase->Begin();
- if (er != erSuccess)
- goto exit;
- // Check permission
- er = lpecSession->GetSecurity()->IsAdminOverUserObject(ulUserId);
- if (er != erSuccess)
- goto exit;
- // Get object details, and resolve company
- er = lpecSession->GetUserManagement()->GetObjectDetails(ulUserId, &userDetails);
- if (er != erSuccess)
- goto exit;
- if (lpecSession->GetSessionManager()->IsHostedSupported())
- ulCompanyId = userDetails.GetPropInt(OB_PROP_I_COMPANYID);
- // Validate store entryid
- if(ValidateZEntryId(sStoreId.__size, sStoreId.__ptr, MAPI_STORE) == false) {
- er = KCERR_INVALID_ENTRYID;
- goto exit;
- }
- // Validate root entryid
- if(ValidateZEntryId(sRootId.__size, sRootId.__ptr, MAPI_FOLDER) == false) {
- er = KCERR_INVALID_ENTRYID;
- goto exit;
- }
- er = GetStoreGuidFromEntryId(sStoreId.__size, sStoreId.__ptr, &guidStore);
- if(er != erSuccess)
- goto exit;
- // Check if there's already a store for the user or group
- strQuery = "SELECT 0 FROM stores WHERE (type="+stringify(ulStoreType)+" AND user_id="+stringify(ulUserId)+") OR guid="+lpDatabase->EscapeBinary((unsigned char*)&guidStore , sizeof(GUID)) + " LIMIT 1";
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if(er != erSuccess)
- goto exit;
- if(lpDatabase->GetNumRows(lpDBResult) > 0) {
- er = KCERR_COLLISION;
- ec_log_err("createStore(): already exists");
- goto exit;
- }
- // Create Toplevel of the store
- strQuery = "INSERT INTO hierarchy(parent, type, owner) VALUES(NULL, "+stringify(MAPI_STORE)+", "+ stringify(ulUserId)+")";
- er = lpDatabase->DoInsert(strQuery, &ulStoreId);
- if(er != erSuccess)
- goto exit;
- // Create the rootfolder of a store
- strQuery = "INSERT INTO hierarchy(parent, type, owner) VALUES("+stringify(ulStoreId)+", "+stringify(MAPI_FOLDER)+ ", "+ stringify(ulUserId)+")";
- er = lpDatabase->DoInsert(strQuery, &ulRootMapId);
- if(er != erSuccess)
- goto exit;
- //Init storesize
- er = UpdateObjectSize(lpDatabase, ulStoreId, MAPI_STORE, UPDATE_SET, 0);
- if (er != erSuccess)
- goto exit;
- // Add SourceKey
- er = lpecSession->GetNewSourceKey(&sSourceKey);
- if(er != erSuccess)
- goto exit;
-
- er = RemoveStaleIndexedProp(lpDatabase, PR_SOURCE_KEY, sSourceKey, sSourceKey.size());
- if (er != erSuccess)
- goto exit;
-
- strQuery = "INSERT INTO indexedproperties(hierarchyid,tag,val_binary) VALUES(" + stringify(ulRootMapId) + "," + stringify(PROP_ID(PR_SOURCE_KEY)) + "," + lpDatabase->EscapeBinary(sSourceKey, sSourceKey.size()) + ")";
- er = lpDatabase->DoInsert(strQuery);
- if(er != erSuccess)
- goto exit;
- // Add store entryid: 0x0FFF = PR_ENTRYID
- er = RemoveStaleIndexedProp(lpDatabase, PR_ENTRYID, sStoreId.__ptr, sStoreId.__size);
- if (er != erSuccess)
- goto exit;
-
- strQuery = "INSERT INTO indexedproperties (hierarchyid,tag,val_binary) VALUES("+stringify(ulStoreId)+", 0x0FFF, "+lpDatabase->EscapeBinary(sStoreId.__ptr, sStoreId.__size)+")";
- er = lpDatabase->DoInsert(strQuery);
- if(er != erSuccess)
- goto exit;
- // Add rootfolder entryid: 0x0FFF = PR_ENTRYID
- er = RemoveStaleIndexedProp(lpDatabase, PR_ENTRYID, sRootId.__ptr, sRootId.__size);
- if (er != erSuccess)
- goto exit;
-
- strQuery = "INSERT INTO indexedproperties (hierarchyid,tag,val_binary) VALUES("+stringify(ulRootMapId)+", 0x0FFF, "+lpDatabase->EscapeBinary(sRootId.__ptr, sRootId.__size)+")";
- er = lpDatabase->DoInsert(strQuery);
- if(er != erSuccess)
- goto exit;
- // Add rootfolder type: 0x3601 = FOLDER_ROOT (= 0)
- strQuery = "INSERT INTO properties (tag,type,hierarchyid,val_ulong) VALUES(0x3601, 0x0003, "+stringify(ulRootMapId)+", 0)";
- er = lpDatabase->DoInsert(strQuery);
- if(er != erSuccess)
- goto exit;
-
- now = time(NULL);
- for (size_t i = 0; i < ARRAY_SIZE(timeProps); ++i) {
- sProp.ulPropTag = timeProps[i];
- sProp.__union = SOAP_UNION_propValData_hilo;
- sProp.Value.hilo = &sHilo;
-
- UnixTimeToFileTime(now, &sProp.Value.hilo->hi, &sProp.Value.hilo->lo);
-
- WriteProp(lpDatabase, ulStoreId, 0, &sProp);
- if(er != erSuccess)
- goto exit;
-
- WriteProp(lpDatabase, ulRootMapId, 0, &sProp);
- if(er != erSuccess)
- goto exit;
- }
- // Couple store with user
- strQuery = "INSERT INTO stores(hierarchy_id, user_id, type, user_name, company, guid) VALUES(" +
- stringify(ulStoreId) + ", " +
- stringify(ulUserId) + ", " +
- stringify(ulStoreType) + ", " +
- "'" + lpDatabase->Escape(userDetails.GetPropString(OB_PROP_S_LOGIN)) + "', " +
- stringify(ulCompanyId) + ", " +
- lpDatabase->EscapeBinary((unsigned char*)&guidStore , sizeof(GUID))+")";
- er = lpDatabase->DoInsert(strQuery);
- if(er != erSuccess)
- goto exit;
- // Set ACL's on public store
- if(ulStoreType == ECSTORE_TYPE_PUBLIC) {
- // ulUserId == a group
- // ulUserId 1 = group everyone
- srightsArray.__ptr = s_alloc<rights>(nullptr, 1);
- srightsArray.__ptr[0].ulRights = ecRightsDefaultPublic;
- srightsArray.__ptr[0].ulUserid = ulUserId;
- srightsArray.__ptr[0].ulState = RIGHT_NEW|RIGHT_AUTOUPDATE_DENIED;
- srightsArray.__ptr[0].ulType = ACCESS_TYPE_GRANT;
- memset(&srightsArray.__ptr[0].sUserId, 0, sizeof(entryId));
- srightsArray.__size = 1;
- er = lpecSession->GetSecurity()->SetRights(ulStoreId, &srightsArray);
- if(er != erSuccess)
- goto exit;
- }
- er = lpDatabase->Commit();
- if (er != erSuccess)
- goto exit;
- ec_log_info("Finished create store (userid=%d, storeid=%d, type=%d)", ulUserId, ulStoreId, ulStoreType);
- exit:
- if(er == KCERR_NO_ACCESS)
- ec_log_err("Failed to create store access denied");
- else if(er != erSuccess)
- ec_log_err("Failed to create store (id=%d), errorcode=0x%08X", ulUserId, er);
- s_free(nullptr, srightsArray.__ptr);
- ROLLBACK_ON_ERROR();
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(createGroup, lpsSetGroupResponse->er, struct group *lpsGroup, struct setGroupResponse *lpsSetGroupResponse)
- {
- unsigned int ulGroupId = 0;
- objectdetails_t details(DISTLIST_SECURITY); // DB plugin wants to be able to set permissions on groups
- if (lpsGroup == nullptr || lpsGroup->lpszGroupname == nullptr ||
- lpsGroup->lpszFullname == nullptr)
- return KCERR_INVALID_PARAMETER;
- if (!bSupportUnicode) {
- er = FixGroupEncoding(soap, stringCompat, In, lpsGroup);
- if (er != erSuccess)
- return er;
- }
- er = CopyGroupDetailsFromSoap(lpsGroup, NULL, &details, soap);
- if (er != erSuccess)
- return er;
- er = lpecSession->GetUserManagement()->UpdateUserDetailsFromClient(&details);
- if (er != erSuccess)
- return er;
- // Check permission
- er = lpecSession->GetSecurity()->IsAdminOverUserObject(details.GetPropInt(OB_PROP_I_COMPANYID));
- if(er != erSuccess)
- return er;
- er = lpecSession->GetUserManagement()->CreateObjectAndSync(details, &ulGroupId);
- if (er != erSuccess)
- return er;
- er = GetABEntryID(ulGroupId, soap, &lpsSetGroupResponse->sGroupId);
- if (er != erSuccess)
- return er;
- lpsSetGroupResponse->ulGroupId = ulGroupId;
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(setGroup, *result, struct group *lpsGroup, unsigned int *result)
- {
- objectdetails_t details;
- unsigned int ulGroupId = 0;
- objectid_t sExternId;
-
- if (lpsGroup == nullptr)
- return KCERR_INVALID_PARAMETER;
- if (!bSupportUnicode) {
- er = FixGroupEncoding(soap, stringCompat, In, lpsGroup);
- if (er != erSuccess)
- return er;
- }
- if (lpsGroup->sGroupId.__size > 0 && lpsGroup->sGroupId.__ptr != NULL)
- {
- er = GetLocalId(lpsGroup->sGroupId, lpsGroup->ulGroupId, &ulGroupId, &sExternId);
- if (er != erSuccess)
- return er;
- }
- else
- ulGroupId = lpsGroup->ulGroupId;
- // Check permission
- er = lpecSession->GetSecurity()->IsAdminOverUserObject(ulGroupId);
- if(er != erSuccess)
- return er;
- details = objectdetails_t(DISTLIST_GROUP);
- er = CopyGroupDetailsFromSoap(lpsGroup, &sExternId.id, &details, soap);
- if (er != erSuccess)
- return er;
- er = lpecSession->GetUserManagement()->UpdateUserDetailsFromClient(&details);
- if (er != erSuccess)
- return er;
- return lpecSession->GetUserManagement()->SetObjectDetailsAndSync(ulGroupId, details, nullptr);
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(getGroup, lpsResponse->er, unsigned int ulGroupId, entryId sGroupId, struct getGroupResponse *lpsResponse)
- {
- objectdetails_t details;
- entryId sTmpGroupId = {0};
- er = GetLocalId(sGroupId, ulGroupId, &ulGroupId, NULL);
- if (er != erSuccess)
- return er;
- /* Check if we are able to view the returned userobject */
- er = lpecSession->GetSecurity()->IsUserObjectVisible(ulGroupId);
- if (er != erSuccess)
- return er;
- er = lpecSession->GetUserManagement()->GetObjectDetails(ulGroupId, &details);
- if (er != erSuccess)
- return er;
- if (OBJECTCLASS_TYPE(details.GetClass()) != OBJECTTYPE_DISTLIST)
- return KCERR_NOT_FOUND;
- lpsResponse->lpsGroup = s_alloc<group>(soap);
- er = GetABEntryID(ulGroupId, soap, &sTmpGroupId);
- if (er == erSuccess)
- er = CopyGroupDetailsToSoap(ulGroupId, &sTmpGroupId, details, lpecSession->GetCapabilities() & KOPANO_CAP_EXTENDED_ANON, soap, lpsResponse->lpsGroup);
- if (er != erSuccess)
- return er;
- if (!bSupportUnicode) {
- er = FixGroupEncoding(soap, stringCompat, Out, lpsResponse->lpsGroup);
- if (er != erSuccess)
- return er;
- }
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(getGroupList, lpsGroupList->er, unsigned int ulCompanyId, entryId sCompanyId, struct groupListResponse *lpsGroupList)
- {
- std::unique_ptr<std::list<localobjectdetails_t> > lpGroups;
- entryId sGroupEid = {0};
- er = GetLocalId(sCompanyId, ulCompanyId, &ulCompanyId, NULL);
- if (er != erSuccess)
- return er;
- /* Input check, if ulCompanyId is 0, we want the user's company,
- * otherwise we must check if the requested company is visible for the user. */
- if (ulCompanyId == 0)
- er = lpecSession->GetSecurity()->GetUserCompany(&ulCompanyId);
- else
- er = lpecSession->GetSecurity()->IsUserObjectVisible(ulCompanyId);
- if (er != erSuccess)
- return er;
- er = lpecSession->GetUserManagement()->GetCompanyObjectListAndSync(OBJECTCLASS_DISTLIST, ulCompanyId, &unique_tie(lpGroups), 0);
- if (er != erSuccess)
- return er;
- lpsGroupList->sGroupArray.__size = 0;
- lpsGroupList->sGroupArray.__ptr = s_alloc<group>(soap, lpGroups->size());
- for (const auto &grp : *lpGroups) {
- if (OBJECTCLASS_TYPE(grp.GetClass()) != OBJECTTYPE_DISTLIST)
- continue;
- er = GetABEntryID(grp.ulId, soap, &sGroupEid);
- if (er != erSuccess)
- return er;
- er = CopyGroupDetailsToSoap(grp.ulId, &sGroupEid, grp,
- lpecSession->GetCapabilities() & KOPANO_CAP_EXTENDED_ANON,
- soap, &lpsGroupList->sGroupArray.__ptr[lpsGroupList->sGroupArray.__size]);
- if (er != erSuccess)
- return er;
- if (!bSupportUnicode) {
- er = FixGroupEncoding(soap, stringCompat, Out, lpsGroupList->sGroupArray.__ptr + lpsGroupList->sGroupArray.__size);
- if (er != erSuccess)
- return er;
- }
- ++lpsGroupList->sGroupArray.__size;
- }
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(groupDelete, *result, unsigned int ulGroupId, entryId sGroupId, unsigned int *result)
- {
- er = GetLocalId(sGroupId, ulGroupId, &ulGroupId, NULL);
- if (er != erSuccess)
- return er;
- // Check permission
- er = lpecSession->GetSecurity()->IsAdminOverUserObject(ulGroupId);
- if(er != erSuccess)
- return er;
- return lpecSession->GetUserManagement()->DeleteObjectAndSync(ulGroupId);
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(resolveUsername, lpsResponse->er, char *lpszUsername, struct resolveUserResponse *lpsResponse)
- {
- unsigned int ulUserId = 0;
- if (lpszUsername == nullptr)
- return KCERR_INVALID_PARAMETER;
- er = lpecSession->GetUserManagement()->ResolveObjectAndSync(OBJECTCLASS_USER, STRIN_FIX(lpszUsername), &ulUserId);
- if (er != erSuccess)
- return er;
- /* Check if we are able to view the returned userobject */
- er = lpecSession->GetSecurity()->IsUserObjectVisible(ulUserId);
- if (er != erSuccess)
- return er;
- er = GetABEntryID(ulUserId, soap, &lpsResponse->sUserId);
- if (er != erSuccess)
- return er;
- lpsResponse->ulUserId = ulUserId;
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(resolveGroupname, lpsResponse->er, char *lpszGroupname, struct resolveGroupResponse *lpsResponse)
- {
- unsigned int ulGroupId = 0;
- if (lpszGroupname == nullptr)
- return KCERR_INVALID_PARAMETER;
- er = lpecSession->GetUserManagement()->ResolveObjectAndSync(OBJECTCLASS_DISTLIST, STRIN_FIX(lpszGroupname), &ulGroupId);
- if (er != erSuccess)
- return er;
- /* Check if we are able to view the returned userobject */
- er = lpecSession->GetSecurity()->IsUserObjectVisible(ulGroupId);
- if (er != erSuccess)
- return er;
- er = GetABEntryID(ulGroupId, soap, &lpsResponse->sGroupId);
- if (er != erSuccess)
- return er;
- lpsResponse->ulGroupId = ulGroupId;
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(deleteGroupUser, *result, unsigned int ulGroupId, entryId sGroupId, unsigned int ulUserId, entryId sUserId, unsigned int *result)
- {
- er = GetLocalId(sGroupId, ulGroupId, &ulGroupId, NULL);
- if (er != erSuccess)
- return er;
- // Check permission
- er = lpecSession->GetSecurity()->IsAdminOverUserObject(ulGroupId);
- if(er != erSuccess)
- return er;
- er = GetLocalId(sUserId, ulUserId, &ulUserId, NULL);
- if (er != erSuccess)
- return er;
- return lpecSession->GetUserManagement()->DeleteSubObjectFromObjectAndSync(OBJECTRELATION_GROUP_MEMBER, ulGroupId, ulUserId);
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(addGroupUser, *result, unsigned int ulGroupId, entryId sGroupId, unsigned int ulUserId, entryId sUserId, unsigned int *result)
- {
- er = GetLocalId(sGroupId, ulGroupId, &ulGroupId, NULL);
- if (er != erSuccess)
- return er;
- // Check permission
- er = lpecSession->GetSecurity()->IsAdminOverUserObject(ulGroupId);
- if(er != erSuccess)
- return er;
- er = GetLocalId(sUserId, ulUserId, &ulUserId, NULL);
- if (er != erSuccess)
- return er;
- return lpecSession->GetUserManagement()->AddSubObjectToObjectAndSync(OBJECTRELATION_GROUP_MEMBER, ulGroupId, ulUserId);
- }
- SOAP_ENTRY_END()
- // not only returns users of a group anymore
- // TODO resolve group in group here on the fly?
- SOAP_ENTRY_START(getUserListOfGroup, lpsUserList->er, unsigned int ulGroupId, entryId sGroupId, struct userListResponse *lpsUserList)
- {
- std::unique_ptr<std::list<localobjectdetails_t> > lpUsers;
- entryId sUserEid = {0};
- er = GetLocalId(sGroupId, ulGroupId, &ulGroupId, NULL);
- if (er != erSuccess)
- return er;
- er = lpecSession->GetSecurity()->IsUserObjectVisible(ulGroupId);
- if (er != erSuccess)
- return er;
- er = lpecSession->GetUserManagement()->GetSubObjectsOfObjectAndSync(OBJECTRELATION_GROUP_MEMBER, ulGroupId, &unique_tie(lpUsers));
- if(er != erSuccess)
- return er;
- lpsUserList->sUserArray.__size = 0;
- lpsUserList->sUserArray.__ptr = s_alloc<user>(soap, lpUsers->size());
- for (const auto &user : *lpUsers) {
- if (lpecSession->GetSecurity()->IsUserObjectVisible(user.ulId) != erSuccess)
- continue;
- er = GetABEntryID(user.ulId, soap, &sUserEid);
- if (er != erSuccess)
- return er;
- // @todo Whoops, we can have group-in-groups. But since details of a group are almost identical to user details (eg. name, fullname, email)
- // this copy will succeed without any problems ... but it's definitly not correct.
- er = CopyUserDetailsToSoap(user.ulId, &sUserEid, user,
- lpecSession->GetCapabilities() & KOPANO_CAP_EXTENDED_ANON,
- soap, &lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size]);
- if (er != erSuccess)
- return er;
- // 6.40.0 stores the object class in the IsNonActive field
- if (lpecSession->ClientVersion() == ZARAFA_VERSION_6_40_0)
- lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size].ulIsNonActive = lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size].ulObjClass;
- if (!bSupportUnicode) {
- er = FixUserEncoding(soap, stringCompat, Out, lpsUserList->sUserArray.__ptr + lpsUserList->sUserArray.__size);
- if (er != erSuccess)
- return er;
- }
- ++lpsUserList->sUserArray.__size;
- }
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(getGroupListOfUser, lpsGroupList->er, unsigned int ulUserId, entryId sUserId, struct groupListResponse *lpsGroupList)
- {
- std::unique_ptr<std::list<localobjectdetails_t> > lpGroups;
- entryId sGroupEid = {0};
- er = GetLocalId(sUserId, ulUserId, &ulUserId, NULL);
- if (er != erSuccess)
- return er;
- er = lpecSession->GetSecurity()->IsUserObjectVisible(ulUserId);
- if (er != erSuccess)
- return er;
- er = lpecSession->GetUserManagement()->GetParentObjectsOfObjectAndSync(OBJECTRELATION_GROUP_MEMBER, ulUserId, &unique_tie(lpGroups));
- if(er != erSuccess)
- return er;
- lpsGroupList->sGroupArray.__size = 0;
- lpsGroupList->sGroupArray.__ptr = s_alloc<group>(soap, lpGroups->size());
- for (const auto &grp : *lpGroups) {
- if (lpecSession->GetSecurity()->IsUserObjectVisible(grp.ulId) != erSuccess)
- continue;
- er = GetABEntryID(grp.ulId, soap, &sGroupEid);
- if (er != erSuccess)
- return er;
- er = CopyGroupDetailsToSoap(grp.ulId, &sGroupEid, grp,
- lpecSession->GetCapabilities() & KOPANO_CAP_EXTENDED_ANON,
- soap, &lpsGroupList->sGroupArray.__ptr[lpsGroupList->sGroupArray.__size]);
- if (er != erSuccess)
- return er;
- if (!bSupportUnicode) {
- er = FixGroupEncoding(soap, stringCompat, Out, lpsGroupList->sGroupArray.__ptr + lpsGroupList->sGroupArray.__size);
- if (er != erSuccess)
- return er;
- }
- ++lpsGroupList->sGroupArray.__size;
- }
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(createCompany, lpsResponse->er, struct company *lpsCompany, struct setCompanyResponse *lpsResponse)
- {
- unsigned int ulCompanyId = 0;
- objectdetails_t details(CONTAINER_COMPANY);
- if (lpsCompany == nullptr)
- return KCERR_INVALID_PARAMETER;
- if (!g_lpSessionManager->IsHostedSupported())
- return KCERR_NO_SUPPORT;
- // Check permission, only the system user is allowed to create or delete a company
- er = lpecSession->GetSecurity()->IsAdminOverUserObject(KOPANO_UID_SYSTEM);
- if(er != erSuccess)
- return er;
- if (!bSupportUnicode) {
- er = FixCompanyEncoding(soap, stringCompat, In, lpsCompany);
- if (er != erSuccess)
- return er;
- }
- er = CopyCompanyDetailsFromSoap(lpsCompany, NULL, KOPANO_UID_SYSTEM, &details, soap);
- if (er != erSuccess)
- return er;
- er = lpecSession->GetUserManagement()->UpdateUserDetailsFromClient(&details);
- if (er != erSuccess)
- return er;
- er = lpecSession->GetUserManagement()->CreateObjectAndSync(details, &ulCompanyId);
- if(er != erSuccess)
- return er;
- er = GetABEntryID(ulCompanyId, soap, &lpsResponse->sCompanyId);
- if (er != erSuccess)
- return er;
- lpsResponse->ulCompanyId = ulCompanyId;
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(deleteCompany, *result, unsigned int ulCompanyId, entryId sCompanyId, unsigned int *result)
- {
- if (!g_lpSessionManager->IsHostedSupported())
- return KCERR_NO_SUPPORT;
- // Check permission, only the system user is allowed to create or delete a company
- er = lpecSession->GetSecurity()->IsAdminOverUserObject(KOPANO_UID_SYSTEM);
- if(er != erSuccess)
- return er;
- er = GetLocalId(sCompanyId, ulCompanyId, &ulCompanyId, NULL);
- if (er != erSuccess)
- return er;
- return lpecSession->GetUserManagement()->DeleteObjectAndSync(ulCompanyId);
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(setCompany, *result, struct company *lpsCompany, unsigned int *result)
- {
- objectdetails_t details;
- unsigned int ulCompanyId = 0;
- unsigned int ulAdministrator = 0;
- objectid_t sExternId;
-
- if (lpsCompany == nullptr)
- return KCERR_INVALID_PARAMETER;
- if (!g_lpSessionManager->IsHostedSupported())
- return KCERR_NO_SUPPORT;
- if (!bSupportUnicode) {
- er = FixCompanyEncoding(soap, stringCompat, In, lpsCompany);
- if (er != erSuccess)
- return er;
- }
- if (lpsCompany->sCompanyId.__size > 0 && lpsCompany->sCompanyId.__ptr != NULL)
- {
- er = GetLocalId(lpsCompany->sCompanyId, lpsCompany->ulCompanyId, &ulCompanyId, &sExternId);
- if (er != erSuccess)
- return er;
- }
- else
- ulCompanyId = lpsCompany->ulCompanyId;
- er = GetLocalId(lpsCompany->sAdministrator, lpsCompany->ulAdministrator, &ulAdministrator, NULL);
- if (er != erSuccess)
- return er;
- // Check permission
- er = lpecSession->GetSecurity()->IsAdminOverUserObject(ulCompanyId);
- if(er != erSuccess)
- return er;
- details = objectdetails_t(CONTAINER_COMPANY);
- er = CopyCompanyDetailsFromSoap(lpsCompany, &sExternId.id, ulAdministrator, &details, soap);
- if (er != erSuccess)
- return er;
- er = lpecSession->GetUserManagement()->UpdateUserDetailsFromClient(&details);
- if (er != erSuccess)
- return er;
- return lpecSession->GetUserManagement()->SetObjectDetailsAndSync(ulCompanyId, details, nullptr);
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(getCompany, lpsResponse->er, unsigned int ulCompanyId, entryId sCompanyId, struct getCompanyResponse *lpsResponse)
- {
- objectdetails_t details;
- unsigned int ulAdmin = 0;
- entryId sAdminEid = {0};
- entryId sTmpCompanyId = {0};
- if (!g_lpSessionManager->IsHostedSupported())
- return KCERR_NO_SUPPORT;
- er = GetLocalId(sCompanyId, ulCompanyId, &ulCompanyId, NULL);
- if (er != erSuccess)
- return er;
- /* Input check, if ulCompanyId is 0, we want the user's company,
- * otherwise we must check if the requested company is visible for the user. */
- if (ulCompanyId == 0)
- er = lpecSession->GetSecurity()->GetUserCompany(&ulCompanyId);
- else
- er = lpecSession->GetSecurity()->IsUserObjectVisible(ulCompanyId);
- if (er != erSuccess)
- return er;
- er = lpecSession->GetUserManagement()->GetObjectDetails(ulCompanyId, &details);
- if(er != erSuccess)
- return er;
- if (details.GetClass() != CONTAINER_COMPANY)
- return KCERR_NOT_FOUND;
- ulAdmin = details.GetPropInt(OB_PROP_I_SYSADMIN);
- er = lpecSession->GetSecurity()->IsUserObjectVisible(ulAdmin);
- if (er != erSuccess)
- return er;
- er = GetABEntryID(ulAdmin, soap, &sAdminEid);
- if (er != erSuccess)
- return er;
- er = GetABEntryID(ulCompanyId, soap, &sTmpCompanyId);
- if (er != erSuccess)
- return er;
- lpsResponse->lpsCompany = s_alloc<company>(soap);
- memset(lpsResponse->lpsCompany, 0, sizeof(company));
- er = CopyCompanyDetailsToSoap(ulCompanyId, &sTmpCompanyId, ulAdmin, &sAdminEid, details, lpecSession->GetCapabilities() & KOPANO_CAP_EXTENDED_ANON, soap, lpsResponse->lpsCompany);
- if (er != erSuccess)
- return er;
- if (!bSupportUnicode) {
- er = FixCompanyEncoding(soap, stringCompat, Out, lpsResponse->lpsCompany);
- if (er != erSuccess)
- return er;
- }
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(resolveCompanyname, lpsResponse->er, char *lpszCompanyname, struct resolveCompanyResponse *lpsResponse)
- {
- unsigned int ulCompanyId = 0;
- if (!g_lpSessionManager->IsHostedSupported())
- return KCERR_NO_SUPPORT;
- if (lpszCompanyname == nullptr)
- return KCERR_INVALID_PARAMETER;
- er = lpecSession->GetUserManagement()->ResolveObjectAndSync(CONTAINER_COMPANY, STRIN_FIX(lpszCompanyname), &ulCompanyId);
- if(er != erSuccess)
- return er;
- /* Check if we are able to view the returned userobject */
- er = lpecSession->GetSecurity()->IsUserObjectVisible(ulCompanyId);
- if (er != erSuccess)
- return er;
- er = GetABEntryID(ulCompanyId, soap, &lpsResponse->sCompanyId);
- if (er != erSuccess)
- return er;
- lpsResponse->ulCompanyId = ulCompanyId;
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(getCompanyList, lpsCompanyList->er, struct companyListResponse *lpsCompanyList)
- {
- unsigned int ulAdmin = 0;
- entryId sCompanyEid = {0};
- entryId sAdminEid = {0};
- std::unique_ptr<std::list<localobjectdetails_t> > lpCompanies;
- if (!g_lpSessionManager->IsHostedSupported())
- return KCERR_NO_SUPPORT;
- er = lpecSession->GetSecurity()->GetViewableCompanyIds(0, &unique_tie(lpCompanies));
- if(er != erSuccess)
- return er;
- lpsCompanyList->sCompanyArray.__size = 0;
- lpsCompanyList->sCompanyArray.__ptr = s_alloc<company>(soap, lpCompanies->size());
- for (const auto &com : *lpCompanies) {
- ulAdmin = com.GetPropInt(OB_PROP_I_SYSADMIN);
- er = lpecSession->GetSecurity()->IsUserObjectVisible(ulAdmin);
- if (er != erSuccess)
- return er;
- er = GetABEntryID(com.ulId, soap, &sCompanyEid);
- if (er != erSuccess)
- return er;
- er = GetABEntryID(ulAdmin, soap, &sAdminEid);
- if (er != erSuccess)
- return er;
- er = CopyCompanyDetailsToSoap(com.ulId, &sCompanyEid,
- ulAdmin, &sAdminEid, com,
- lpecSession->GetCapabilities() & KOPANO_CAP_EXTENDED_ANON,
- soap, &lpsCompanyList->sCompanyArray.__ptr[lpsCompanyList->sCompanyArray.__size]);
- if (er != erSuccess)
- return er;
- if (!bSupportUnicode) {
- er = FixCompanyEncoding(soap, stringCompat, Out, lpsCompanyList->sCompanyArray.__ptr + lpsCompanyList->sCompanyArray.__size);
- if (er != erSuccess)
- return er;
- }
- ++lpsCompanyList->sCompanyArray.__size;
- }
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(addCompanyToRemoteViewList, *result, unsigned int ulSetCompanyId, entryId sSetCompanyId, unsigned int ulCompanyId, entryId sCompanyId, unsigned int *result)
- {
- if (!g_lpSessionManager->IsHostedSupported())
- return KCERR_NO_SUPPORT;
- er = GetLocalId(sCompanyId, ulCompanyId, &ulCompanyId, NULL);
- if (er != erSuccess)
- return er;
- // Check permission
- er = lpecSession->GetSecurity()->IsAdminOverUserObject(ulCompanyId);
- if(er != erSuccess)
- return er;
- er = GetLocalId(sSetCompanyId, ulSetCompanyId, &ulSetCompanyId, NULL);
- if (er != erSuccess)
- return er;
- return lpecSession->GetUserManagement()->AddSubObjectToObjectAndSync(OBJECTRELATION_COMPANY_VIEW, ulCompanyId, ulSetCompanyId);
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(delCompanyFromRemoteViewList, *result, unsigned int ulSetCompanyId, entryId sSetCompanyId, unsigned int ulCompanyId, entryId sCompanyId, unsigned int *result)
- {
- if (!g_lpSessionManager->IsHostedSupported())
- return KCERR_NO_SUPPORT;
- er = GetLocalId(sCompanyId, ulCompanyId, &ulCompanyId, NULL);
- if (er != erSuccess)
- return er;
- // Check permission
- er = lpecSession->GetSecurity()->IsAdminOverUserObject(ulCompanyId);
- if(er != erSuccess)
- return er;
- er = GetLocalId(sSetCompanyId, ulSetCompanyId, &ulSetCompanyId, NULL);
- if (er != erSuccess)
- return er;
- return lpecSession->GetUserManagement()->DeleteSubObjectFromObjectAndSync(OBJECTRELATION_COMPANY_VIEW, ulCompanyId, ulSetCompanyId);
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(getRemoteViewList, lpsCompanyList->er, unsigned int ulCompanyId, entryId sCompanyId, struct companyListResponse *lpsCompanyList)
- {
- unsigned int ulAdmin = 0;
- entryId sCompanyEid = {0};
- entryId sAdminEid = {0};
- std::unique_ptr<std::list<localobjectdetails_t> > lpCompanies;
- if (!g_lpSessionManager->IsHostedSupported())
- return KCERR_NO_SUPPORT;
- er = GetLocalId(sCompanyId, ulCompanyId, &ulCompanyId, NULL);
- if (er != erSuccess)
- return er;
- /* Input check, if ulCompanyId is 0, we want the user's company,
- * otherwise we must check if the requested company is visible for the user. */
- if (ulCompanyId == 0)
- er = lpecSession->GetSecurity()->GetUserCompany(&ulCompanyId);
- else
- er = lpecSession->GetSecurity()->IsUserObjectVisible(ulCompanyId);
- if (er != erSuccess)
- return er;
- er = lpecSession->GetUserManagement()->GetSubObjectsOfObjectAndSync(OBJECTRELATION_COMPANY_VIEW, ulCompanyId, &unique_tie(lpCompanies));
- if(er != erSuccess)
- return er;
- lpsCompanyList->sCompanyArray.__size = 0;
- lpsCompanyList->sCompanyArray.__ptr = s_alloc<company>(soap, lpCompanies->size());
- for (const auto &com : *lpCompanies) {
- if (lpecSession->GetSecurity()->IsUserObjectVisible(com.ulId) != erSuccess)
- continue;
- ulAdmin = com.GetPropInt(OB_PROP_I_SYSADMIN);
- er = lpecSession->GetSecurity()->IsUserObjectVisible(ulAdmin);
- if (er != erSuccess)
- return er;
- er = GetABEntryID(com.ulId, soap, &sCompanyEid);
- if (er != erSuccess)
- return er;
- er = GetABEntryID(ulAdmin, soap, &sAdminEid);
- if (er != erSuccess)
- return er;
- er = CopyCompanyDetailsToSoap(com.ulId, &sCompanyEid,
- ulAdmin, &sAdminEid, com,
- lpecSession->GetCapabilities() & KOPANO_CAP_EXTENDED_ANON,
- soap, &lpsCompanyList->sCompanyArray.__ptr[lpsCompanyList->sCompanyArray.__size]);
- if (er != erSuccess)
- return er;
- if (!bSupportUnicode) {
- er = FixCompanyEncoding(soap, stringCompat, Out, lpsCompanyList->sCompanyArray.__ptr + lpsCompanyList->sCompanyArray.__size);
- if (er != erSuccess)
- return er;
- }
- ++lpsCompanyList->sCompanyArray.__size;
- }
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(addUserToRemoteAdminList, *result, unsigned int ulUserId, entryId sUserId, unsigned int ulCompanyId, entryId sCompanyId, unsigned int *result)
- {
- if (!g_lpSessionManager->IsHostedSupported())
- return KCERR_NO_SUPPORT;
- er = GetLocalId(sCompanyId, ulCompanyId, &ulCompanyId, NULL);
- if (er != erSuccess)
- return er;
- // Check permission
- er = lpecSession->GetSecurity()->IsAdminOverUserObject(ulCompanyId);
- if(er != erSuccess)
- return er;
- er = GetLocalId(sUserId, ulUserId, &ulUserId, NULL);
- if (er != erSuccess)
- return er;
- return lpecSession->GetUserManagement()->AddSubObjectToObjectAndSync(OBJECTRELATION_COMPANY_ADMIN, ulCompanyId, ulUserId);
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(delUserFromRemoteAdminList, *result, unsigned int ulUserId, entryId sUserId, unsigned int ulCompanyId, entryId sCompanyId, unsigned int *result)
- {
- if (!g_lpSessionManager->IsHostedSupported())
- return KCERR_NO_SUPPORT;
- er = GetLocalId(sCompanyId, ulCompanyId, &ulCompanyId, NULL);
- if (er != erSuccess)
- return er;
- // Check permission
- er = lpecSession->GetSecurity()->IsAdminOverUserObject(ulCompanyId);
- if(er != erSuccess)
- return er;
- er = GetLocalId(sUserId, ulUserId, &ulUserId, NULL);
- if (er != erSuccess)
- return er;
- return lpecSession->GetUserManagement()->DeleteSubObjectFromObjectAndSync(OBJECTRELATION_COMPANY_ADMIN, ulCompanyId, ulUserId);
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(getRemoteAdminList, lpsUserList->er, unsigned int ulCompanyId, entryId sCompanyId, struct userListResponse *lpsUserList)
- {
- std::unique_ptr<std::list<localobjectdetails_t> > lpUsers;
- entryId sUserEid = {0};
- if (!g_lpSessionManager->IsHostedSupported())
- return KCERR_NO_SUPPORT;
- er = GetLocalId(sCompanyId, ulCompanyId, &ulCompanyId, NULL);
- if (er != erSuccess)
- return er;
- /* Input check, if ulCompanyId is 0, we want the user's company,
- * otherwise we must check if the requested company is visible for the user. */
- if (ulCompanyId == 0)
- er = lpecSession->GetSecurity()->GetUserCompany(&ulCompanyId);
- else
- er = lpecSession->GetSecurity()->IsUserObjectVisible(ulCompanyId);
- if (er != erSuccess)
- return er;
- // only users can be admins, nonactive users make no sense.
- er = lpecSession->GetUserManagement()->GetSubObjectsOfObjectAndSync(OBJECTRELATION_COMPANY_ADMIN, ulCompanyId, &unique_tie(lpUsers));
- if(er != erSuccess)
- return er;
- lpsUserList->sUserArray.__size = 0;
- lpsUserList->sUserArray.__ptr = s_alloc<user>(soap, lpUsers->size());
- for (const auto &user : *lpUsers) {
- if (lpecSession->GetSecurity()->IsUserObjectVisible(user.ulId) != erSuccess)
- continue;
- er = GetABEntryID(user.ulId, soap, &sUserEid);
- if (er != erSuccess)
- return er;
- er = CopyUserDetailsToSoap(user.ulId, &sUserEid, user,
- lpecSession->GetCapabilities() & KOPANO_CAP_EXTENDED_ANON,
- soap, &lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size]);
- if (er != erSuccess)
- return er;
- // 6.40.0 stores the object class in the IsNonActive field
- if (lpecSession->ClientVersion() == ZARAFA_VERSION_6_40_0)
- lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size].ulIsNonActive = lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size].ulObjClass;
- if (!bSupportUnicode) {
- er = FixUserEncoding(soap, stringCompat, Out, lpsUserList->sUserArray.__ptr + lpsUserList->sUserArray.__size);
- if (er != erSuccess)
- return er;
- }
- ++lpsUserList->sUserArray.__size;
- if (sUserEid.__ptr)
- {
- // sUserEid is placed in userdetails, no need to free
- sUserEid.__ptr = NULL;
- sUserEid.__size = 0;
- }
- }
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(submitMessage, *result, entryId sEntryId, unsigned int ulFlags, unsigned int *result)
- {
- unsigned int ulParentId = 0;
- unsigned int ulObjId = 0;
- unsigned int ulMsgFlags = 0;
- unsigned int ulStoreId = 0;
- unsigned int ulStoreOwner = 0;
- SOURCEKEY sSourceKey;
- SOURCEKEY sParentSourceKey;
- bool bMessageChanged = false;
- eQuotaStatus QuotaStatus;
- long long llStoreSize = 0;
- objectdetails_t details;
-
- USE_DATABASE();
- er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulObjId);
- if(er != erSuccess)
- goto exit;
- er = lpecSession->GetSessionManager()->GetCacheManager()->GetStore(ulObjId, &ulStoreId, NULL);
- if(er != erSuccess)
- goto exit;
- er = lpecSession->GetSessionManager()->GetCacheManager()->GetObject(ulObjId, &ulParentId, NULL, &ulMsgFlags, NULL);
- if(er != erSuccess)
- goto exit;
-
- er = lpecSession->GetSessionManager()->GetCacheManager()->GetObject(ulStoreId, NULL, &ulStoreOwner, NULL, NULL);
- if(er != erSuccess)
- goto exit;
-
- er = lpecSession->GetUserManagement()->GetObjectDetails(ulStoreOwner, &details);
- if(er != erSuccess)
- goto exit;
-
- // Cannot submit a message in a public store
- if(OBJECTCLASS_TYPE(details.GetClass()) != OBJECTTYPE_MAILUSER) {
- er = KCERR_NO_ACCESS;
- goto exit;
- }
- // Check permission
- er = lpecSession->GetSecurity()->CheckPermission(ulStoreId, ecSecurityOwner);
- if(er != erSuccess)
- goto exit;
- // Quota check
- er = lpecSession->GetSecurity()->GetStoreSize(ulStoreId, &llStoreSize);
- if(er != erSuccess)
- goto exit;
- er = lpecSession->GetSecurity()->CheckQuota(ulStoreId, llStoreSize, &QuotaStatus);
- if(er != erSuccess)
- goto exit;
- if(QuotaStatus == QUOTA_SOFTLIMIT || QuotaStatus == QUOTA_HARDLIMIT) {
- er = KCERR_STORE_FULL;
- goto exit;
- }
- er = lpDatabase->Begin();
- if(er != erSuccess)
- goto exit;
- // Set PR_MESSAGE_FLAGS to MSGFLAG_SUBMIT|MSGFLAG_UNSENT
- if(!(ulFlags & EC_SUBMIT_MASTER)) {
- // Set the submit flag (because it has just been submitted), and set it to UNSENT, as it has definitely
- // not been sent if the user has just submitted it.
- strQuery = "UPDATE properties SET val_ulong=val_ulong|"+stringify(MSGFLAG_SUBMIT|MSGFLAG_UNSENT)+" where hierarchyid="+stringify(ulObjId) + " and tag=" + stringify(PROP_ID(PR_MESSAGE_FLAGS)) + " and type=" + stringify(PROP_TYPE(PR_MESSAGE_FLAGS));
- er = lpDatabase->DoUpdate(strQuery);
- if(er != erSuccess)
- goto exit;
- // Add change to ICS
- GetSourceKey(ulObjId, &sSourceKey);
- GetSourceKey(ulParentId, &sParentSourceKey);
- AddChange(lpecSession, 0, sSourceKey, sParentSourceKey, ICS_MESSAGE_CHANGE);
- // Mask for notification
- bMessageChanged = true;
- }
-
- er = UpdateTProp(lpDatabase, PR_MESSAGE_FLAGS, ulParentId, ulObjId);
- if(er != erSuccess)
- goto exit;
- // Insert the message into the outgoing queue
- strQuery = "INSERT IGNORE INTO outgoingqueue (store_id, hierarchy_id, flags) VALUES("+stringify(ulStoreId)+", "+stringify(ulObjId)+","+stringify(ulFlags)+")";
- er = lpDatabase->DoInsert(strQuery);
- if(er != erSuccess)
- goto exit;
- er = lpDatabase->Commit();
- if(er != erSuccess)
- goto exit;
- if (bMessageChanged) {
- // Update cache
- g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, ulObjId);
- g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, ulParentId);
- // Notify
- g_lpSessionManager->NotificationModified(MAPI_MESSAGE, ulObjId, ulParentId);
- g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY, 0, ulParentId, ulObjId, MAPI_MESSAGE);
- }
-
- g_lpSessionManager->UpdateOutgoingTables(ECKeyTable::TABLE_ROW_ADD, ulStoreId, ulObjId, ulFlags, MAPI_MESSAGE);
- exit:
- ROLLBACK_ON_ERROR();
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(finishedMessage, *result, entryId sEntryId, unsigned int ulFlags, unsigned int *result)
- {
- unsigned int ulParentId = 0;
- unsigned int ulGrandParentId = 0;
- unsigned int ulAffectedRows = 0;
- unsigned int ulStoreId = (unsigned int)-1; // not 0 security issue
- unsigned int ulObjId = 0;
- unsigned int ulPrevFlags = 0;
- bool bMessageChanged = false;
- SOURCEKEY sSourceKey;
- SOURCEKEY sParentSourceKey;
- USE_DATABASE();
- er = lpDatabase->Begin();
- if(er != erSuccess)
- goto exit;
- er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulObjId);
- if(er != erSuccess)
- goto exit;
- er = g_lpSessionManager->GetCacheManager()->GetParent(ulObjId, &ulParentId);
- if(er != erSuccess)
- goto exit;
- //Get storeid
- er = lpecSession->GetSessionManager()->GetCacheManager()->GetStore(ulObjId, &ulStoreId, NULL);
- switch (er) {
- case erSuccess:
- break;
- case KCERR_NOT_FOUND:
- // ulObjId should be in outgoingtable, but the ulStoreId cannot be retrieved
- // because ulObjId does not exist in the hierarchy table, so we remove the message
- // fix table and notify and pass error to caller
- ec_log_warn("Unable to find store for hierarchy id %d", ulObjId);
- ulStoreId = 0;
- goto table;
- default:
- goto exit; // database error
- }
- // Check permission
- er = lpecSession->GetSecurity()->CheckPermission(ulStoreId, ecSecurityOwner);
- if(er != erSuccess)
- goto exit;
-
- strQuery = "SELECT val_ulong FROM properties WHERE hierarchyid="+stringify(ulObjId) + " AND tag=" + stringify(PROP_ID(PR_MESSAGE_FLAGS)) + " AND type=" + stringify(PROP_TYPE(PR_MESSAGE_FLAGS)) + " FOR UPDATE";
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if(er != erSuccess)
- goto exit;
-
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
- if(lpDBRow == NULL || lpDBRow[0] == NULL) {
- er = KCERR_DATABASE_ERROR;
- ec_log_err("finishedMessages(): row/col null");
- goto exit;
- }
-
- ulPrevFlags = atoui(lpDBRow[0]);
- strQuery = "UPDATE properties ";
- if (!(ulFlags & EC_SUBMIT_MASTER))
- // Removing from local queue; remove submit flag and unsent flag
- strQuery += " SET val_ulong=val_ulong&~"+stringify(MSGFLAG_SUBMIT|MSGFLAG_UNSENT);
- else if (ulFlags & EC_SUBMIT_DOSENTMAIL)
- // Removing from master queue
- // Spooler sent message and moved, remove submit flag and unsent flag
- strQuery += " SET val_ulong=val_ulong&~" +stringify(MSGFLAG_SUBMIT|MSGFLAG_UNSENT);
- else
- // Spooler only sent message
- strQuery += " SET val_ulong=val_ulong&~" +stringify(MSGFLAG_UNSENT);
- // Always set message read
- strQuery += ", val_ulong=val_ulong|" + stringify(MSGFLAG_READ) + " WHERE hierarchyid="+stringify(ulObjId) + " AND tag=" + stringify(PROP_ID(PR_MESSAGE_FLAGS)) + " AND type=" + stringify(PROP_TYPE(PR_MESSAGE_FLAGS));
- er = lpDatabase->DoUpdate(strQuery);
- if(er != erSuccess)
- goto exit;
-
- er = UpdateTProp(lpDatabase, PR_MESSAGE_FLAGS, ulParentId, ulObjId);
- if(er != erSuccess)
- goto exit;
- if(!(ulPrevFlags & MSGFLAG_READ)) {
- // The item has been set read, decrease the unread counter for the folder
- er = UpdateFolderCount(lpDatabase, ulParentId, PR_CONTENT_UNREAD, -1);
- if(er != erSuccess)
- goto exit;
- }
- GetSourceKey(ulObjId, &sSourceKey);
- GetSourceKey(ulParentId, &sParentSourceKey);
- AddChange(lpecSession, 0, sSourceKey, sParentSourceKey, ICS_MESSAGE_CHANGE);
-
- // NOTE: Unlock message is done in client
- // Mark for notification
- bMessageChanged = true;
- table:
- // delete the message from the outgoing queue
- strQuery = "DELETE FROM outgoingqueue WHERE hierarchy_id="+stringify(ulObjId) + " AND flags & 1=" + stringify(ulFlags & 1);
- er = lpDatabase->DoDelete(strQuery, &ulAffectedRows);
- if(er != erSuccess)
- goto exit;
- er = lpDatabase->Commit();
- if(er != erSuccess)
- goto exit;
- // Remove messge from the outgoing queue
- g_lpSessionManager->UpdateOutgoingTables(ECKeyTable::TABLE_ROW_DELETE, ulStoreId, ulObjId, ulFlags, MAPI_MESSAGE);
- // The flags have changed, so we have to send a modified
- if (bMessageChanged) {
- lpecSession->GetSessionManager()->GetCacheManager()->Update(fnevObjectModified, ulObjId);
- g_lpSessionManager->NotificationModified(MAPI_MESSAGE, ulObjId, ulParentId);
- if(g_lpSessionManager->GetCacheManager()->GetParent(ulObjId, &ulParentId) == erSuccess) {
- g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, ulParentId);
- g_lpSessionManager->NotificationModified(MAPI_FOLDER, ulParentId);
-
- g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY, 0, ulParentId, ulObjId, MAPI_MESSAGE);
- if (g_lpSessionManager->GetCacheManager()->GetParent(ulParentId, &ulGrandParentId) == erSuccess)
- g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY, 0, ulGrandParentId, ulParentId, MAPI_FOLDER);
- }
- }
- exit:
- ROLLBACK_ON_ERROR();
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(abortSubmit, *result, entryId sEntryId, unsigned int *result)
- {
- unsigned int ulParentId = 0;
- unsigned int ulStoreId = 0;
- unsigned int ulSubmitFlags = 0;
- unsigned int ulGrandParentId = 0;
- unsigned int ulObjId = 0;
- SOURCEKEY sSourceKey;
- SOURCEKEY sParentSourceKey;
- USE_DATABASE();
- er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulObjId);
- if(er != erSuccess)
- goto exit;
- er = g_lpSessionManager->GetCacheManager()->GetParent(ulObjId, &ulParentId);
- if(er != erSuccess)
- goto exit;
- // Check permission
- er = lpecSession->GetSecurity()->CheckPermission(ulObjId, ecSecurityOwner);
- if(er != erSuccess)
- goto exit;
- //Get storeid
- er = lpecSession->GetSessionManager()->GetCacheManager()->GetStore(ulObjId, &ulStoreId, NULL);
- if(er != erSuccess)
- goto exit;
-
- er = lpDatabase->Begin();
- if(er != erSuccess)
- goto exit;
- // Get storeid and check if the message into the queue
- strQuery = "SELECT store_id, flags FROM outgoingqueue WHERE hierarchy_id="+stringify(ulObjId) + " LIMIT 2";
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if(er != erSuccess)
- goto exit;
- // FIXME: can be also more than 2??
- if(lpDatabase->GetNumRows(lpDBResult) != 1){
- er = KCERR_NOT_IN_QUEUE;
- goto exit;
- }
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
- if(lpDBRow == NULL || lpDBRow[0] == NULL || lpDBRow[1] == NULL) {
- er = KCERR_DATABASE_ERROR;
- ec_log_err("abortSubmit(): row/col null");
- goto exit;
- }
- ulStoreId = atoui(lpDBRow[0]);
- ulSubmitFlags = atoi(lpDBRow[1]);
- // delete the message from the outgoing queue
- strQuery = "DELETE FROM outgoingqueue WHERE hierarchy_id="+stringify(ulObjId);
- er = lpDatabase->DoDelete(strQuery);
- if(er != erSuccess)
- goto exit;
- // remove in property PR_MESSAGE_FLAGS the MSGFLAG_SUBMIT flag
- strQuery = "UPDATE properties SET val_ulong=val_ulong& ~"+stringify(MSGFLAG_SUBMIT)+" WHERE hierarchyid="+stringify(ulObjId)+ " AND tag=" + stringify(PROP_ID(PR_MESSAGE_FLAGS)) + " AND type=" + stringify(PROP_TYPE(PR_MESSAGE_FLAGS));
- er = lpDatabase->DoUpdate(strQuery);
- if(er != erSuccess)
- goto exit;
-
- er = UpdateTProp(lpDatabase, PR_MESSAGE_FLAGS, ulParentId, ulObjId);
- if(er != erSuccess)
- goto exit;
-
- // Update ICS system
- GetSourceKey(ulObjId, &sSourceKey);
- GetSourceKey(ulParentId, &sParentSourceKey);
- AddChange(lpecSession, 0, sSourceKey, sParentSourceKey, ICS_MESSAGE_CHANGE);
-
- er = lpDatabase->Commit();
- if(er != erSuccess)
- goto exit;
- g_lpSessionManager->UpdateOutgoingTables(ECKeyTable::TABLE_ROW_DELETE, ulStoreId, ulObjId, ulSubmitFlags, MAPI_MESSAGE);
- if(g_lpSessionManager->GetCacheManager()->GetParent(ulObjId, &ulParentId) == erSuccess) {
- g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, ulObjId);
- g_lpSessionManager->NotificationModified(MAPI_MESSAGE, ulObjId, ulParentId);
- g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, ulParentId);
- g_lpSessionManager->NotificationModified(MAPI_FOLDER, ulParentId);
- g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY, 0, ulParentId, ulObjId, MAPI_MESSAGE);
- if (g_lpSessionManager->GetCacheManager()->GetParent(ulParentId, &ulGrandParentId) == erSuccess)
- g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY, 0, ulGrandParentId, ulParentId, MAPI_FOLDER);
- }
- exit:
- ROLLBACK_ON_ERROR();
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(isMessageInQueue, *result, entryId sEntryId, unsigned int *result)
- {
- unsigned int ulObjId = 0;
- USE_DATABASE();
- er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulObjId);
- if(er != erSuccess)
- return er;
- // Checks if message is unsent
- strQuery = "SELECT hierarchy_id FROM outgoingqueue WHERE hierarchy_id=" + stringify(ulObjId) + " AND flags & " + stringify(EC_SUBMIT_MASTER) + " LIMIT 1";
- if(lpDatabase->DoSelect(strQuery, &lpDBResult) != erSuccess) {
- ec_log_err("isMessageInQueue(): select failed");
- return KCERR_DATABASE_ERROR;
- }
- if (lpDatabase->GetNumRows(lpDBResult) == 0)
- return KCERR_NOT_FOUND;
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(resolveStore, lpsResponse->er, struct xsd__base64Binary sStoreGuid, struct resolveUserStoreResponse *lpsResponse)
- {
- USE_DATABASE();
- string strStoreGuid;
- if (sStoreGuid.__ptr == nullptr || sStoreGuid.__size == 0)
- return KCERR_INVALID_PARAMETER;
- strStoreGuid = lpDatabase->EscapeBinary(sStoreGuid.__ptr, sStoreGuid.__size);
- // @todo: Check if this is supposed to work with public stores.
- strQuery =
- "SELECT u.id, s.hierarchy_id, s.guid, s.company "
- "FROM stores AS s "
- "LEFT JOIN users AS u "
- "ON s.user_id = u.id "
- "WHERE s.guid=" + strStoreGuid + " LIMIT 2";
- if(lpDatabase->DoSelect(strQuery, &lpDBResult) != erSuccess) {
- ec_log_err("resolveStore(): select failed %x", er);
- return KCERR_DATABASE_ERROR;
- }
- if (lpDatabase->GetNumRows(lpDBResult) != 1)
- return KCERR_NOT_FOUND;
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
- lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
- if (lpDBRow == nullptr || lpDBRow[1] == nullptr ||
- lpDBRow[2] == nullptr || lpDBRow[3] == nullptr ||
- lpDBLen == nullptr)
- return KCERR_NOT_FOUND;
- if (lpDBRow[0] == NULL) {
- // check if we're admin over the store object
- er = lpecSession->GetSecurity()->IsAdminOverUserObject(atoi(lpDBRow[3]));
- if (er != erSuccess)
- return er;
- lpsResponse->ulUserId = 0;
- lpsResponse->sUserId.__size = 0;
- lpsResponse->sUserId.__ptr = NULL;
- } else {
- lpsResponse->ulUserId = atoi(lpDBRow[0]);
- er = GetABEntryID(lpsResponse->ulUserId, soap, &lpsResponse->sUserId);
- if (er != erSuccess)
- return er;
- }
- er = g_lpSessionManager->GetCacheManager()->GetEntryIdFromObject(atoui(lpDBRow[1]), soap, OPENSTORE_OVERRIDE_HOME_MDB, &lpsResponse->sStoreId);
- if(er != erSuccess)
- return er;
- lpsResponse->guid.__size = lpDBLen[2];
- lpsResponse->guid.__ptr = s_alloc<unsigned char>(soap, lpDBLen[2]);
- memcpy(lpsResponse->guid.__ptr, lpDBRow[2], lpDBLen[2]);
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(resolveUserStore, lpsResponse->er, char *szUserName, unsigned int ulStoreTypeMask, unsigned int ulFlags, struct resolveUserStoreResponse *lpsResponse)
- {
- unsigned int ulObjectId = 0;
- objectdetails_t sUserDetails;
- string strServerName;
- USE_DATABASE();
- if (szUserName == nullptr)
- return KCERR_INVALID_PARAMETER;
- szUserName = STRIN_FIX(szUserName);
- if (ulStoreTypeMask == 0)
- ulStoreTypeMask = ECSTORE_TYPE_MASK_PRIVATE | ECSTORE_TYPE_MASK_PUBLIC;
- er = lpecSession->GetUserManagement()->ResolveObjectAndSync(OBJECTCLASS_USER, szUserName, &ulObjectId);
- if ((er == KCERR_NOT_FOUND || er == KCERR_INVALID_PARAMETER) && lpecSession->GetSessionManager()->IsHostedSupported())
- // FIXME: this function is being misused, szUserName can also be a company name
- er = lpecSession->GetUserManagement()->ResolveObjectAndSync(CONTAINER_COMPANY, szUserName, &ulObjectId);
- if (er != erSuccess)
- return er;
- /* If we are allowed to view the user, we are allowed to know the store exists */
- er = lpecSession->GetSecurity()->IsUserObjectVisible(ulObjectId);
- if (er != erSuccess)
- return er;
- er = lpecSession->GetUserManagement()->GetObjectDetails(ulObjectId, &sUserDetails);
- if (er != erSuccess)
- return er;
- /* Only users and companies have a store */
- if ((OBJECTCLASS_TYPE(sUserDetails.GetClass()) == OBJECTTYPE_MAILUSER && sUserDetails.GetClass() == NONACTIVE_CONTACT) ||
- (OBJECTCLASS_TYPE(sUserDetails.GetClass()) != OBJECTTYPE_MAILUSER && sUserDetails.GetClass() != CONTAINER_COMPANY))
- return KCERR_NOT_FOUND;
- if (lpecSession->GetSessionManager()->IsDistributedSupported() &&
- !lpecSession->GetUserManagement()->IsInternalObject(ulObjectId))
- {
- if (ulStoreTypeMask & (ECSTORE_TYPE_MASK_PRIVATE | ECSTORE_TYPE_MASK_PUBLIC)) {
- /* Check if this is the correct server for its store */
- string strServerName = sUserDetails.GetPropString(OB_PROP_S_SERVERNAME);
- if (strServerName.empty())
- return KCERR_NOT_FOUND;
- if (strcasecmp(strServerName.c_str(), g_lpSessionManager->GetConfig()->GetSetting("server_name")) != 0) {
- if ((ulFlags & OPENSTORE_OVERRIDE_HOME_MDB) == 0) {
- string strServerPath;
- er = GetBestServerPath(soap, lpecSession, strServerName, &strServerPath);
- if (er != erSuccess)
- return er;
- lpsResponse->lpszServerPath = STROUT_FIX_CPY(strServerPath.c_str());
- ec_log_info("Redirecting request to \"%s\'", lpsResponse->lpszServerPath);
- g_lpStatsCollector->Increment(SCN_REDIRECT_COUNT, 1);
- return KCERR_UNABLE_TO_COMPLETE;
- }
- }
- }
- else if (ulStoreTypeMask & ECSTORE_TYPE_MASK_ARCHIVE) {
- // We allow an archive store to be resolved by sysadmins even if it's not supposed
- // to exist on this server for a particular user.
- if (lpecSession->GetSecurity()->GetAdminLevel() < ADMIN_LEVEL_SYSADMIN &&
- !sUserDetails.PropListStringContains((property_key_t)PR_EC_ARCHIVE_SERVERS_A, g_lpSessionManager->GetConfig()->GetSetting("server_name"), true))
- // No redirect with archive stores because there can be multiple archive stores.
- return KCERR_NOT_FOUND;
- }
- else {
- assert(false);
- return KCERR_NOT_FOUND;
- }
- }
- strQuery = "SELECT hierarchy_id, guid FROM stores WHERE user_id = " + stringify(ulObjectId) + " AND (1 << type) & " + stringify(ulStoreTypeMask) + " LIMIT 1";
- if ((er = lpDatabase->DoSelect(strQuery, &lpDBResult)) != erSuccess) {
- ec_log_err("resolveUserStore(): select failed %x", er);
- return KCERR_DATABASE_ERROR;
- }
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
- lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
- if (lpDBRow == nullptr)
- return KCERR_NOT_FOUND;
- if (lpDBRow[0] == NULL || lpDBRow[1] == NULL || lpDBLen == NULL || lpDBLen[1] == 0) {
- ec_log_err("resolveUserStore(): row/col null");
- return KCERR_DATABASE_ERROR;
- }
- /* We found the store, so we don't need to check if this is the correct server. */
- strServerName = g_lpSessionManager->GetConfig()->GetSetting("server_name", "", "Unknown");
- // Always return the pseudo URL.
- lpsResponse->lpszServerPath = STROUT_FIX_CPY(string("pseudo://" + strServerName).c_str());
- er = g_lpSessionManager->GetCacheManager()->GetEntryIdFromObject(atoui(lpDBRow[0]), soap, ulFlags & OPENSTORE_OVERRIDE_HOME_MDB, &lpsResponse->sStoreId);
- if(er != erSuccess)
- return er;
- er = GetABEntryID(ulObjectId, soap, &lpsResponse->sUserId);
- if (er != erSuccess)
- return er;
- lpsResponse->ulUserId = ulObjectId;
- lpsResponse->guid.__size = lpDBLen[1];
- lpsResponse->guid.__ptr = s_alloc<unsigned char>(soap, lpDBLen[1]);
- memcpy(lpsResponse->guid.__ptr, lpDBRow[1], lpDBLen[1]);
- return erSuccess;
- }
- SOAP_ENTRY_END()
- struct COPYITEM {
- unsigned int ulId;
- unsigned int ulType;
- unsigned int ulParent;
- unsigned int ulNewId;
- unsigned int ulFlags;
- unsigned int ulMessageFlags;
- unsigned int ulOwner;
- SOURCEKEY sSourceKey;
- SOURCEKEY sParentSourceKey;
- SOURCEKEY sNewSourceKey;
- EntryId sOldEntryId;
- EntryId sNewEntryId;
- bool bMoved;
- };
- // Move one or more messages and/or moved a softdeleted message to a normal message
- static ECRESULT MoveObjects(ECSession *lpSession, ECDatabase *lpDatabase,
- ECListInt* lplObjectIds, unsigned int ulDestFolderId,
- unsigned int ulSyncId)
- {
- ECRESULT er = erSuccess;
- bool bPartialCompletion = false;
- unsigned int ulGrandParent = 0;
- COPYITEM sItem;
- unsigned int ulItemSize = 0;
- unsigned int ulSourceStoreId = 0;
- unsigned int ulDestStoreId = 0;
- long long llStoreSize;
- eQuotaStatus QuotaStatus;
- bool bUpdateDeletedSize = false;
- ECListIntIterator iObjectId;
- size_t cCopyItems = 0;
- FILETIME ft;
- unsigned long long ullIMAP = 0;
- std::list<unsigned int> lstParent;
- std::list<unsigned int> lstGrandParent;
- std::list<COPYITEM> lstCopyItems;
- SOURCEKEY sDestFolderSourceKey;
- std::map<unsigned int, PARENTINFO> mapFolderCounts;
- entryId* lpsNewEntryId = NULL;
- entryId* lpsOldEntryId = NULL;
- GUID guidStore;
- ALLOC_DBRESULT();
- if(lplObjectIds == NULL) {
- ec_log_err("MoveObjects: no list of objects given");
- er = KCERR_INVALID_PARAMETER;
- goto exit;
- }
- if(lplObjectIds->empty())
- goto exit; // Nothing to do
- GetSystemTimeAsFileTime(&ft);
- // Check permission, Destination folder
- er = lpSession->GetSecurity()->CheckPermission(ulDestFolderId, ecSecurityCreate);
- if (er != erSuccess) {
- ec_log_err("MoveObjects: failed checking permissions on %u: %s (%x)", ulDestFolderId, GetMAPIErrorMessage(er), er);
- goto exit;
- }
- er = lpSession->GetSessionManager()->GetCacheManager()->GetStore(ulDestFolderId, &ulDestStoreId, &guidStore);
- if (er != erSuccess) {
- ec_log_err("MoveObjects: failed retrieving store of %u: %s (%x)", ulDestFolderId, GetMAPIErrorMessage(er), er);
- goto exit;
- }
- GetSourceKey(ulDestFolderId, &sDestFolderSourceKey);
- // Get all items for the object list
- strQuery = "SELECT h.id, h.parent, h.type, h.flags, h.owner, p.val_ulong, p2.val_ulong FROM hierarchy AS h LEFT JOIN properties AS p ON p.hierarchyid=h.id AND p.tag="+stringify(PROP_ID(PR_MESSAGE_SIZE))+" AND p.type="+stringify(PROP_TYPE(PR_MESSAGE_SIZE)) +
- " LEFT JOIN properties AS p2 ON p2.hierarchyid=h.id AND p2.tag = " + stringify(PROP_ID(PR_MESSAGE_FLAGS)) + " AND p2.type = " + stringify(PROP_TYPE(PR_MESSAGE_FLAGS)) + " WHERE h.id IN(";
- for (iObjectId = lplObjectIds->begin();
- iObjectId != lplObjectIds->end(); ++iObjectId) {
- if(iObjectId != lplObjectIds->begin())
- strQuery += ",";
- strQuery += stringify(*iObjectId);
- }
- strQuery += ")";
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if (er != erSuccess) {
- ec_log_err("MoveObjects: failed retrieving list objects from database: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- // First, put all the root objects in the list
- while( (lpDBRow = lpDatabase->FetchRow(lpDBResult)) != NULL)
- {
- if(lpDBRow[0] == NULL || lpDBRow[1] == NULL || lpDBRow[2] == NULL || lpDBRow[3] == NULL || lpDBRow[4] == NULL) // no id, type or parent folder?
- continue;
- sItem.bMoved = false;
- sItem.ulId = atoi(lpDBRow[0]);
- sItem.ulParent = atoi(lpDBRow[1]);
- sItem.ulType = atoi(lpDBRow[2]);
- sItem.ulFlags = atoi(lpDBRow[3]);
- sItem.ulOwner = atoi(lpDBRow[4]);
- sItem.ulMessageFlags = lpDBRow[6] ? atoi(lpDBRow[6]) : 0;
- if (sItem.ulType != MAPI_MESSAGE) {
- bPartialCompletion = true;
- continue;
- }
-
- GetSourceKey(sItem.ulId, &sItem.sSourceKey);
- GetSourceKey(sItem.ulParent, &sItem.sParentSourceKey);
- // Check permission, source messages
- er = lpSession->GetSecurity()->CheckPermission(sItem.ulId, ecSecurityDelete);
- if(er == erSuccess) {
- // Check if the source and dest the same store
- er = lpSession->GetSessionManager()->GetCacheManager()->GetStore(sItem.ulId, &ulSourceStoreId, NULL);
- if(er != erSuccess || ulSourceStoreId != ulDestStoreId) {
- bPartialCompletion = true;
- er = erSuccess;
- continue;
- }
- lstCopyItems.push_back(sItem);
- ulItemSize += (lpDBRow[5] != NULL)? atoi(lpDBRow[5]) : 0;
- // check if it a deleted item
- if (lpDBRow[3] != NULL && atoi(lpDBRow[3]) & MSGFLAG_DELETED)
- bUpdateDeletedSize = true;
- }else {
- bPartialCompletion = true;
- er = erSuccess;
- }
- }
-
- // Check the quota size when the item is a softdelete item
- if(bUpdateDeletedSize == true)
- {
- // Quota check
- er = lpSession->GetSecurity()->GetStoreSize(ulDestFolderId, &llStoreSize);
- if (er != erSuccess) {
- ec_log_err("MoveObjects: GetStoreSize(%u) failed: %s (%x)", ulDestFolderId, GetMAPIErrorMessage(er), er);
- goto exit;
- }
- // substract itemsize and check
- llStoreSize -= (llStoreSize >= (long long)ulItemSize)?(long long)ulItemSize:0;
- er = lpSession->GetSecurity()->CheckQuota(ulDestFolderId, llStoreSize, &QuotaStatus);
- if (er != erSuccess) {
- ec_log_err("MoveObjects: CheckQuota(%u) failed: %s (%x)", ulDestFolderId, GetMAPIErrorMessage(er), er);
- goto exit;
- }
- if(QuotaStatus == QUOTA_HARDLIMIT) {
- er = KCERR_STORE_FULL;
- goto exit;
- }
- }
- cCopyItems = lstCopyItems.size();
- // Move the messages to another folder
- for (auto &cop : lstCopyItems) {
- sObjectTableKey key(cop.ulId, 0);
- struct propVal sPropIMAPId;
- // Check whether it is a move to the same parent, and if so, skip them.
- if (cop.ulParent == ulDestFolderId &&
- (cop.ulFlags & MSGFLAG_DELETED) == 0)
- continue;
- er = g_lpSessionManager->GetCacheManager()->GetEntryIdFromObject(cop.ulId, NULL, 0, &lpsOldEntryId);
- if(er != erSuccess) {
- // FIXME isn't this an error?
- ec_log_err("MoveObjects: problem retrieving entry id of object %u: %s (%x)",
- cop.ulId, GetMAPIErrorMessage(er), er);
- bPartialCompletion = true;
- er = erSuccess;
- // FIXME: Delete from list: cop
- continue;
- }
- cop.sOldEntryId = EntryId(lpsOldEntryId);
- FreeEntryId(lpsOldEntryId, true);
- lpsOldEntryId = NULL;
- er = CreateEntryId(guidStore, MAPI_MESSAGE, &lpsNewEntryId);
- if (er != erSuccess) {
- ec_log_err("MoveObjects: CreateEntryID for type MAPI_MESSAGE failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- cop.sNewEntryId = EntryId(lpsNewEntryId);
- FreeEntryId(lpsNewEntryId, true);
- lpsNewEntryId = NULL;
- // Update entryid (changes on move)
- strQuery = "REPLACE INTO indexedproperties(hierarchyid,tag,val_binary) VALUES (" +
- stringify(cop.ulId) + ", 0x0FFF," +
- lpDatabase->EscapeBinary(cop.sNewEntryId, cop.sNewEntryId.size()) + ")";
- er = lpDatabase->DoUpdate(strQuery);
- if (er != erSuccess) {
- ec_log_err("MoveObjects: problem setting new entry id: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- er = lpSession->GetNewSourceKey(&cop.sNewSourceKey);
- if (er != erSuccess) {
- ec_log_err("MoveObjects: GetNewSourceKey failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
-
- // Update source key (changes on move)
- strQuery = "REPLACE INTO indexedproperties(hierarchyid,tag,val_binary) VALUES (" +
- stringify(cop.ulId) + "," +
- stringify(PROP_ID(PR_SOURCE_KEY)) + "," +
- lpDatabase->EscapeBinary(cop.sNewSourceKey, cop.sNewSourceKey.size()) + ")";
- er = lpDatabase->DoUpdate(strQuery);
- if (er != erSuccess) {
- ec_log_err("MoveObjects: Update source key for %u failed: %s (%x)",
- cop.ulId, GetMAPIErrorMessage(er), er);
- goto exit;
- }
- // Update IMAP ID (changes on move)
- er = g_lpSessionManager->GetNewSequence(ECSessionManager::SEQ_IMAP, &ullIMAP);
- if (er != erSuccess) {
- ec_log_err("MoveObjects: problem retrieving new IMAP ID: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- strQuery = "INSERT INTO properties(hierarchyid, tag, type, val_ulong) VALUES(" +
- stringify(cop.ulId) + "," +
- stringify(PROP_ID(PR_EC_IMAP_ID)) + "," +
- stringify(PROP_TYPE(PR_EC_IMAP_ID)) + "," +
- stringify(ullIMAP) +
- ") ON DUPLICATE KEY UPDATE val_ulong=" +
- stringify(ullIMAP);
- er = lpDatabase->DoInsert(strQuery);
- if (er != erSuccess) {
- ec_log_err("MoveObjects: problem updating new IMAP ID for %u to %llu: %s (%x)",
- cop.ulId, ullIMAP, GetMAPIErrorMessage(er), er);
- goto exit;
- }
- sPropIMAPId.ulPropTag = PR_EC_IMAP_ID;
- sPropIMAPId.Value.ul = ullIMAP;
- sPropIMAPId.__union = SOAP_UNION_propValData_ul;
- er = g_lpSessionManager->GetCacheManager()->SetCell(&key, PR_EC_IMAP_ID, &sPropIMAPId);
- if (er != erSuccess) {
- ec_log_err("MoveObjects: problem cache sell for IMAP ID %llu: %s (%x)", ullIMAP, GetMAPIErrorMessage(er), er);
- goto exit;
- }
- strQuery = "UPDATE hierarchy SET parent=" +
- stringify(ulDestFolderId) + ", flags=flags&" +
- stringify(~MSGFLAG_DELETED) + " WHERE id=" +
- stringify(cop.ulId);
- er = lpDatabase->DoUpdate(strQuery);
- if (er != erSuccess) {
- // FIXME isn't this an error?
- ec_log_debug("MoveObjects: problem updating hierarchy id for %u in %u: %s (%x)",
- cop.ulId, ulDestFolderId,
- GetMAPIErrorMessage(er), er);
- bPartialCompletion = true;
- er = erSuccess;
- // FIXME: Delete from list: cop
- continue;
- }
- // update last modification time
- // PR_LAST_MODIFICATION_TIME (ZCP-11897)
- strQuery = "INSERT INTO properties(hierarchyid, tag, type, val_lo, val_hi) VALUES(" +
- stringify(cop.ulId) + "," +
- stringify(PROP_ID(PR_LAST_MODIFICATION_TIME)) + "," +
- stringify(PROP_TYPE(PR_LAST_MODIFICATION_TIME)) + "," +
- stringify(ft.dwLowDateTime) + "," +
- stringify(ft.dwHighDateTime) +
- ") ON DUPLICATE KEY UPDATE val_lo=" +
- stringify(ft.dwLowDateTime) + ", val_hi=" +
- stringify(ft.dwHighDateTime);
- er = lpDatabase->DoUpdate(strQuery);
- if (er != erSuccess)
- goto exit;
- g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, cop.ulId);
- // remove PR_DELETED_ON, This is on a softdeleted message
- strQuery = "DELETE FROM properties WHERE hierarchyid=" +
- stringify(cop.ulId) + " AND tag=" +
- stringify(PROP_ID(PR_DELETED_ON)) + " AND type=" +
- stringify(PROP_TYPE(PR_DELETED_ON));
- er = lpDatabase->DoDelete(strQuery);
- if(er != erSuccess) {
- ec_log_debug("MoveObjects: problem removing PR_DELETED_ON for %u: %s (%x)",
- cop.ulId, GetMAPIErrorMessage(er), er);
- bPartialCompletion = true;
- er = erSuccess; //ignore error // FIXME WHY?!
- }
- // a move is a delete in the originating folder and a new in the destination folder except for softdelete that is a change
- if (cop.ulParent != ulDestFolderId) {
- AddChange(lpSession, ulSyncId, cop.sSourceKey, cop.sParentSourceKey, ICS_MESSAGE_HARD_DELETE);
- AddChange(lpSession, ulSyncId, cop.sNewSourceKey, sDestFolderSourceKey, ICS_MESSAGE_NEW);
- } else if (cop.ulFlags & MSGFLAG_DELETED) {
- // Restore a softdeleted message
- AddChange(lpSession, ulSyncId, cop.sNewSourceKey, sDestFolderSourceKey, ICS_MESSAGE_NEW);
- }
- er = ECTPropsPurge::AddDeferredUpdate(lpSession, lpDatabase,
- ulDestFolderId, cop.ulParent, cop.ulId);
- if (er != erSuccess) {
- ec_log_debug("MoveObjects: ECTPropsPurge::AddDeferredUpdate failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- // Track folder count changes
- if (cop.ulType == MAPI_MESSAGE) {
- if (cop.ulFlags & MSGFLAG_DELETED) {
- // Undelete
- if (cop.ulFlags & MAPI_ASSOCIATED) {
- // Associated message undeleted
- --mapFolderCounts[cop.ulParent].lDeletedAssoc;
- ++mapFolderCounts[ulDestFolderId].lAssoc;
- } else {
- // Message undeleted
- --mapFolderCounts[cop.ulParent].lDeleted;
- ++mapFolderCounts[ulDestFolderId].lItems;
- if ((cop.ulMessageFlags & MSGFLAG_READ) == 0)
- // Undeleted message was unread
- ++mapFolderCounts[ulDestFolderId].lUnread;
- }
- } else {
- // Move
- --mapFolderCounts[cop.ulParent].lItems;
- ++mapFolderCounts[ulDestFolderId].lItems;
- if ((cop.ulMessageFlags & MSGFLAG_READ) == 0) {
- --mapFolderCounts[cop.ulParent].lUnread;
- ++mapFolderCounts[ulDestFolderId].lUnread;
- }
- }
- }
-
- cop.bMoved = true;
- }
-
- er = ApplyFolderCounts(lpDatabase, mapFolderCounts);
- if (er != erSuccess) {
- ec_log_debug("MoveObjects: ApplyFolderCounts failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- // change the size if it is a soft delete item
- if(bUpdateDeletedSize == true) {
- er = UpdateObjectSize(lpDatabase, ulDestStoreId, MAPI_STORE, UPDATE_ADD, ulItemSize);
- if (er != erSuccess) {
- ec_log_debug("MoveObjects: UpdateObjectSize(store %u) failed: %s (%x)", ulDestStoreId, GetMAPIErrorMessage(er), er);
- goto exit;
- }
- }
- for (const auto &cop : lstCopyItems) {
- if (!cop.bMoved)
- continue;
- // Cache update for object
- g_lpSessionManager->GetCacheManager()->SetObject(cop.ulId,
- ulDestFolderId, cop.ulOwner,
- cop.ulFlags & ~MSGFLAG_DELETED /* possible undelete */,
- cop.ulType);
- // Remove old sourcekey and entryid and add them
- g_lpSessionManager->GetCacheManager()->RemoveIndexData(cop.ulId);
- g_lpSessionManager->GetCacheManager()->SetObjectProp(PROP_ID(PR_SOURCE_KEY),
- cop.sNewSourceKey.size(), cop.sNewSourceKey, cop.ulId);
- g_lpSessionManager->GetCacheManager()->SetObjectProp(PROP_ID(PR_ENTRYID),
- cop.sNewEntryId.size(), cop.sNewEntryId, cop.ulId);
- }
-
- er = lpDatabase->Commit();
- if (er != erSuccess) {
- ec_log_debug("MoveObjects: database commit failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- for (auto &cop : lstCopyItems) {
- if (!cop.bMoved)
- continue;
- // update destenation folder after PR_ENTRYID update
- if (cCopyItems < EC_TABLE_CHANGE_THRESHOLD) {
- // Update messages
- g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_DELETE,
- 0, cop.ulParent, cop.ulId, cop.ulType);
- // Update destenation folder
- g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_ADD,
- 0, ulDestFolderId, cop.ulId, cop.ulType);
- }
- // Update Store object
- g_lpSessionManager->NotificationMoved(cop.ulType, cop.ulId,
- ulDestFolderId, cop.ulParent, cop.sOldEntryId);
- lstParent.push_back(cop.ulParent);
- }
- lstParent.sort();
- lstParent.unique();
- //Update message folders
- for (auto pa_id : lstParent) {
- if (cCopyItems >= EC_TABLE_CHANGE_THRESHOLD)
- g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_CHANGE,
- 0, pa_id, 0, MAPI_MESSAGE);
- // update the source parent folder for disconnected clients
- WriteLocalCommitTimeMax(NULL, lpDatabase, pa_id, NULL);
- // ignore error, no need to set partial even.
- // Get the grandparent
- g_lpSessionManager->GetCacheManager()->GetParent(pa_id, &ulGrandParent);
- g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, pa_id);
- g_lpSessionManager->NotificationModified(MAPI_FOLDER, pa_id);
- g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY,
- 0, ulGrandParent, pa_id, MAPI_FOLDER);
- }
- // update the destination folder for disconnected clients
- WriteLocalCommitTimeMax(NULL, lpDatabase, ulDestFolderId, NULL);
- // ignore error, no need to set partial even.
- if(cCopyItems >= EC_TABLE_CHANGE_THRESHOLD)
- g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_CHANGE, 0, ulDestFolderId, 0, MAPI_MESSAGE);
- //Update destination folder
- g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, ulDestFolderId);
- g_lpSessionManager->NotificationModified(MAPI_FOLDER, ulDestFolderId);
- // Update the grandfolder of dest. folder
- g_lpSessionManager->GetCacheManager()->GetParent(ulDestFolderId, &ulGrandParent);
- g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY, 0, ulGrandParent, ulDestFolderId, MAPI_FOLDER);
- if(bPartialCompletion && er == erSuccess)
- er = KCWARN_PARTIAL_COMPLETION;
- exit:
- if(lpDatabase && er != erSuccess && er != KCWARN_PARTIAL_COMPLETION)
- lpDatabase->Rollback();
- if(lpsNewEntryId)
- FreeEntryId(lpsNewEntryId, true);
- if(lpsOldEntryId)
- FreeEntryId(lpsOldEntryId, true);
- return er;
- }
- /**
- * Copy one message with his parent data like attachments and recipient
- *
- * @param[in] lpecSession Pointer to a session object; cannot be NULL.
- * @param[in] lpAttachmentStorage Pointer to an attachment storage object. If NULL is passed in lpAttachmentStorage,
- * a default storage object with transaction enabled will be used.
- * @param[in] ulObjId Source object that identify the message, recipient or attachment to copy.
- * @param[in] ulDestFolderId Destenation object to received the copied message, recipient or attachment.
- * @param[in] bIsRoot Identify the root object; For callers this should be true;
- * @param[in] bDoNotification true if you want to send object notifications.
- * @param[in] bDoTableNotification true if you want to send table notifications.
- * @param[in] ulSyncId Client sync identify.
- *
- * @FIXME It is possible to send notifications before a commit, this can give issues with the cache!
- * This function should be refactored
- */
- static ECRESULT CopyObject(ECSession *lpecSession,
- ECAttachmentStorage *lpAttachmentStorage, unsigned int ulObjId,
- unsigned int ulDestFolderId, bool bIsRoot, bool bDoNotification,
- bool bDoTableNotification, unsigned int ulSyncId)
- {
- ECRESULT er = erSuccess;
- ECDatabase *lpDatabase = NULL;
- std::string strQuery;
- DB_RESULT lpDBResult;
- DB_ROW lpDBRow;
- unsigned int ulNewObjectId = 0;
- std::string strExclude;
- long long llStoreSize;
- unsigned int ulStoreId = 0;
- unsigned int ulSize;
- unsigned int ulObjType;
- unsigned int ulParent = 0;
- unsigned int ulFlags = 0;
- GUID guidStore;
- eQuotaStatus QuotaStatus;
- SOURCEKEY sSourceKey;
- SOURCEKEY sParentSourceKey;
- entryId* lpsNewEntryId = NULL;
- unsigned long long ullIMAP = 0;
- object_ptr<ECAttachmentStorage> lpInternalAttachmentStorage;
- er = lpecSession->GetDatabase(&lpDatabase);
- if (er != erSuccess) {
- ec_log_err("CopyObject: cannot retrieve database: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- if (!lpAttachmentStorage) {
- if (!bIsRoot) {
- ec_log_err("CopyObject: \"!attachmentstore && !isroot\" clause failed: %s (%x)", GetMAPIErrorMessage(er), er);
- er = KCERR_INVALID_PARAMETER;
- goto exit;
- }
- er = CreateAttachmentStorage(lpDatabase, &~lpInternalAttachmentStorage);
- if (er != erSuccess) {
- ec_log_err("CopyObject: CreateAttachmentStorage failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- lpAttachmentStorage = lpInternalAttachmentStorage;
- // Hack, when lpInternalAttachmentStorage exist your are in a transaction!
- }
- er = lpecSession->GetSessionManager()->GetCacheManager()->GetStore(ulDestFolderId, &ulStoreId, &guidStore);
- if (er != erSuccess) {
- ec_log_err("CopyObject: GetStore(destination folder %u) failed: %s (%x)", ulDestFolderId, GetMAPIErrorMessage(er), er);
- goto exit;
- }
- // Check permission
- if(bIsRoot == true)
- {
- er = lpecSession->GetSecurity()->CheckPermission(ulObjId, ecSecurityRead);
- if (er != erSuccess) {
- ec_log_err("CopyObject: check permissions of %u failed: %s (%x)", ulObjId, GetMAPIErrorMessage(er), er);
- goto exit;
- }
- // Quota check
- er = lpecSession->GetSecurity()->GetStoreSize(ulDestFolderId, &llStoreSize);
- if (er != erSuccess) {
- ec_log_err("CopyObject: store size of dest folder %u failed: %s (%x)", ulDestFolderId, GetMAPIErrorMessage(er), er);
- goto exit;
- }
- er = lpecSession->GetSecurity()->CheckQuota(ulDestFolderId, llStoreSize, &QuotaStatus);
- if (er != erSuccess) {
- ec_log_err("CopyObject: check quota of dest folder %u failed: %s (%x)", ulDestFolderId, GetMAPIErrorMessage(er), er);
- goto exit;
- }
- if(QuotaStatus == QUOTA_HARDLIMIT) {
- er = KCERR_STORE_FULL;
- goto exit;
- }
- // Start tranaction
- if (lpInternalAttachmentStorage) {
- er = lpInternalAttachmentStorage->Begin();
- if (er != erSuccess) {
- ec_log_err("CopyObject: starting transaction in attachment storage failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- er = lpDatabase->Begin();
- if (er != erSuccess) {
- ec_log_err("CopyObject: starting transaction in database failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- }
- }
- // Get the hierarchy messageroot but not the deleted items
- strQuery = "SELECT h.parent, h.type, p.val_ulong FROM hierarchy AS h LEFT JOIN properties AS p ON h.id = p.hierarchyid AND p.tag = " + stringify(PROP_ID(PR_MESSAGE_FLAGS)) + " AND p.type = " + stringify(PROP_TYPE(PR_MESSAGE_FLAGS)) + " WHERE h.flags & " + stringify(MSGFLAG_DELETED) + " = 0 AND id="+stringify(ulObjId) + " LIMIT 1";
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if (er != erSuccess) {
- ec_log_err("CopyObject: failed retrieving hierarchy message root: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- if(lpDatabase->GetNumRows(lpDBResult) < 1) {
- er = KCERR_NOT_FOUND;// FIXME: right error?
- goto exit;
- }
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
- if( lpDBRow == NULL || lpDBRow[0] == NULL || lpDBRow[1] == NULL) {
- er = KCERR_NOT_FOUND;
- goto exit;
- }
- ulObjType = atoui(lpDBRow[1]);
- ulParent = atoui(lpDBRow[0]);
- if (lpDBRow[2])
- ulFlags = atoui(lpDBRow[2]);
- if (bIsRoot == true && ulObjType != MAPI_MESSAGE) {
- ec_log_err("CopyObject: \"isRoot && != MAPI_MESSAGE\" fail");
- er = KCERR_INVALID_ENTRYID;
- goto exit;
- }
- //FIXME: Why do we always use the mod and create time of the old object? Create time can always be NOW
- //Create new message (Only valid flag in hierarchy is MSGFLAG_ASSOCIATED)
- strQuery = "INSERT INTO hierarchy(parent, type, flags, owner) VALUES(" +
- stringify(ulDestFolderId) + ", " +
- (string)lpDBRow[1] + ", " +
- stringify(ulFlags) + "&" + stringify(MSGFLAG_ASSOCIATED) + "," +
- stringify(lpecSession->GetSecurity()->GetUserId()) + ") ";
- er = lpDatabase->DoInsert(strQuery, &ulNewObjectId);
- if (er != erSuccess) {
- ec_log_err("CopyObject: failed inserting entry in hierarchy table: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- if(bIsRoot == true) {
- sObjectTableKey key(ulNewObjectId, 0);
- propVal sProp;
- // Create message entry
- er = CreateEntryId(guidStore, MAPI_MESSAGE, &lpsNewEntryId);
- if (er != erSuccess) {
- ec_log_err("CopyObject: CreateEntryId failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- //0x0FFF = PR_ENTRYID
- strQuery = "INSERT INTO indexedproperties (hierarchyid,tag,val_binary) VALUES("+stringify(ulNewObjectId)+", 0x0FFF, "+lpDatabase->EscapeBinary(lpsNewEntryId->__ptr, lpsNewEntryId->__size)+")";
- er = lpDatabase->DoInsert(strQuery);
- if (er != erSuccess) {
- ec_log_err("CopyObject: PR_ENTRYID property insert failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- // Add a PR_EC_IMAP_ID
- er = g_lpSessionManager->GetNewSequence(ECSessionManager::SEQ_IMAP, &ullIMAP);
- if (er != erSuccess) {
- ec_log_err("CopyObject: retrieving new seqnr for PR_EC_IMAP_ID failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- strQuery = "INSERT INTO properties(hierarchyid, tag, type, val_ulong) VALUES(" +
- stringify(ulNewObjectId) + "," +
- stringify(PROP_ID(PR_EC_IMAP_ID)) + "," +
- stringify(PROP_TYPE(PR_EC_IMAP_ID)) + "," +
- stringify(ullIMAP) +
- ")";
- er = lpDatabase->DoInsert(strQuery);
- if (er != erSuccess) {
- ec_log_err("CopyObject: PR_EC_IMAP_ID property insert failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- sProp.ulPropTag = PR_EC_IMAP_ID;
- sProp.Value.ul = ullIMAP;
- sProp.__union = SOAP_UNION_propValData_ul;
- er = g_lpSessionManager->GetCacheManager()->SetCell(&key, PR_EC_IMAP_ID, &sProp);
- if (er != erSuccess) {
- ec_log_err("CopyObject: updating PR_EC_IMAP_ID sell in cache failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- }
- if (lpsNewEntryId != NULL) {
- FreeEntryId(lpsNewEntryId, true);
- lpsNewEntryId = NULL;
- }
- // Get child items of the message like , attachment, recipient...
- strQuery = "SELECT id FROM hierarchy WHERE parent="+stringify(ulObjId);
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if (er != erSuccess) {
- ec_log_err("CopyObject: failed retrieving child items of message: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- if(lpDatabase->GetNumRows(lpDBResult) > 0) {
- while( (lpDBRow = lpDatabase->FetchRow(lpDBResult)) )
- {
- if(lpDBRow[0] == NULL)
- continue; // FIXME: Skip, give an error/warning ?
- er = CopyObject(lpecSession, lpAttachmentStorage, atoui(lpDBRow[0]), ulNewObjectId, false, false, false, ulSyncId);
- if (er != erSuccess && er != KCERR_NOT_FOUND) {
- ec_log_err("CopyObject: CopyObject(%s) failed: %s (%x)", lpDBRow[0], GetMAPIErrorMessage(er), er);
- goto exit;
- } else {
- er = erSuccess;
- }
- }
- }
- // Exclude properties
- // PR_DELETED_ON
- strExclude = " AND NOT (tag="+stringify(PROP_ID(PR_DELETED_ON))+" AND type="+stringify(PROP_TYPE(PR_DELETED_ON))+")";
- //Exclude PR_SOURCE_KEY, PR_CHANGE_KEY, PR_PREDECESSOR_CHANGE_LIST
- strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_SOURCE_KEY))+" AND type="+stringify(PROP_TYPE(PR_SOURCE_KEY))+")";
- strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_CHANGE_KEY))+" AND type="+stringify(PROP_TYPE(PR_CHANGE_KEY))+")";
- strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_PREDECESSOR_CHANGE_LIST))+" AND type="+stringify(PROP_TYPE(PR_PREDECESSOR_CHANGE_LIST))+")";
- strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_EC_IMAP_ID))+" AND type="+stringify(PROP_TYPE(PR_EC_IMAP_ID))+")";
- // because of #7699, messages contain PR_LOCAL_COMMIT_TIME_MAX
- strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_LOCAL_COMMIT_TIME_MAX))+" AND type="+stringify(PROP_TYPE(PR_LOCAL_COMMIT_TIME_MAX))+")";
- // Copy properties...
- strQuery = "INSERT INTO properties (hierarchyid, tag, type, val_ulong, val_string, val_binary,val_double,val_longint,val_hi,val_lo) SELECT "+stringify(ulNewObjectId)+", tag,type,val_ulong,val_string,val_binary,val_double,val_longint,val_hi,val_lo FROM properties WHERE hierarchyid ="+stringify(ulObjId)+strExclude;
- er = lpDatabase->DoInsert(strQuery);
- if (er != erSuccess) {
- ec_log_err("CopyObject: copy properties failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
-
- // Copy MVproperties...
- strQuery = "INSERT INTO mvproperties (hierarchyid, orderid, tag, type, val_ulong, val_string, val_binary,val_double,val_longint,val_hi,val_lo) SELECT "+stringify(ulNewObjectId)+", orderid, tag,type,val_ulong,val_string,val_binary,val_double,val_longint,val_hi,val_lo FROM mvproperties WHERE hierarchyid ="+stringify(ulObjId);
- er = lpDatabase->DoInsert(strQuery);
- if (er != erSuccess) {
- ec_log_err("CopyObject: copy MVproperties failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- // Copy large objects... if present
- er = lpAttachmentStorage->CopyAttachment(ulObjId, ulNewObjectId);
- if (er != erSuccess && er != KCERR_NOT_FOUND) {
- ec_log_err("CopyObject: CopyAttachment(%u -> %u) failed: %s (%x)", ulObjId, ulNewObjectId, GetMAPIErrorMessage(er), er);
- goto exit;
- }
- er = erSuccess;
- if(bIsRoot == true)
- {
- // Create indexedproperties, Add new PR_SOURCE_KEY
- er = lpecSession->GetNewSourceKey(&sSourceKey);
- if (er != erSuccess) {
- ec_log_err("CopyObject: GetNewSourceKey failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- strQuery = "INSERT INTO indexedproperties(hierarchyid,tag,val_binary) VALUES(" + stringify(ulNewObjectId) + "," + stringify(PROP_ID(PR_SOURCE_KEY)) + "," + lpDatabase->EscapeBinary(sSourceKey, sSourceKey.size()) + ")";
- er = lpDatabase->DoInsert(strQuery);
- if (er != erSuccess) {
- ec_log_err("CopyObject: insert %u in indexedproperties failed: %s (%x)", ulNewObjectId, GetMAPIErrorMessage(er), er);
- goto exit;
- }
- // Track folder count changes
- // Can we copy deleted items?
- if(ulFlags & MAPI_ASSOCIATED) {
- // Associated message undeleted
- er = UpdateFolderCount(lpDatabase, ulDestFolderId, PR_ASSOC_CONTENT_COUNT, 1);
- } else {
- // Message undeleted
- er = UpdateFolderCount(lpDatabase, ulDestFolderId, PR_CONTENT_COUNT, 1);
- if (er == erSuccess && (ulFlags & MSGFLAG_READ) == 0)
- // Undeleted message was unread
- er = UpdateFolderCount(lpDatabase, ulDestFolderId, PR_CONTENT_UNREAD, 1);
- }
- if (er != erSuccess) {
- ec_log_err("CopyObject: UpdateFolderCount (%u) failed: %s (%x)", ulDestFolderId, GetMAPIErrorMessage(er), er);
- goto exit;
- }
- // Update ICS system
- GetSourceKey(ulDestFolderId, &sParentSourceKey);
- AddChange(lpecSession, ulSyncId, sSourceKey, sParentSourceKey, ICS_MESSAGE_NEW);
- // Hack, when lpInternalAttachmentStorage exist your are in a transaction!
- if (lpInternalAttachmentStorage) {
- // Deferred tproperties
- er = ECTPropsPurge::AddDeferredUpdate(lpecSession, lpDatabase, ulDestFolderId, 0, ulNewObjectId);
- if (er != erSuccess) {
- ec_log_err("CopyObject: ECTPropsPurge::AddDeferredUpdate(%u): %s (%x)", ulDestFolderId, GetMAPIErrorMessage(er), er);
- goto exit;
- }
- er = lpInternalAttachmentStorage->Commit();
- if (er != erSuccess) {
- ec_log_err("CopyObject: attachmentstorage commit failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- er = lpDatabase->Commit();
- if (er != erSuccess) {
- ec_log_err("CopyObject: database commit failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- } else {
- // Deferred tproperties, let the caller handle the purge so we won't purge every 20 messages on a copy
- // of a complete folder.
- er = ECTPropsPurge::AddDeferredUpdateNoPurge(lpDatabase, ulDestFolderId, 0, ulNewObjectId);
- if(er != erSuccess) {
- ec_log_err("CopyObject: ECTPropsPurge::AddDeferredUpdateNoPurge(%u, %u) failed: %s (%x)", ulDestFolderId, ulNewObjectId, GetMAPIErrorMessage(er), er);
- goto exit;
- }
- }
- g_lpSessionManager->GetCacheManager()->SetObjectProp(PROP_ID(PR_SOURCE_KEY), sSourceKey.size(), sSourceKey, ulNewObjectId);
- // Update Size
- if(GetObjectSize(lpDatabase, ulNewObjectId, &ulSize) == erSuccess)
- {
- if(lpecSession->GetSessionManager()->GetCacheManager()->GetStore(ulNewObjectId, &ulStoreId, NULL) == erSuccess) {
- er = UpdateObjectSize(lpDatabase, ulStoreId, MAPI_STORE, UPDATE_ADD, ulSize);
- if (er != erSuccess) {
- ec_log_err("CopyObject: UpdateObjectSize(store %u) failed: %s (%x)", ulStoreId, GetMAPIErrorMessage(er), er);
- goto exit;
- }
- }
- }
- }
- g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, ulDestFolderId);
- if(bDoNotification){
- // Update destenation folder
- if (bDoTableNotification)
- g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_ADD, 0, ulDestFolderId, ulNewObjectId, MAPI_MESSAGE);
- g_lpSessionManager->NotificationModified(MAPI_FOLDER, ulDestFolderId);
- // Notify object is copied
- g_lpSessionManager->NotificationCopied(MAPI_MESSAGE, ulNewObjectId, ulDestFolderId, ulObjId, ulParent);
- }
- exit:
- if(er != erSuccess && lpInternalAttachmentStorage) {
- // Rollback attachments and database!
- lpInternalAttachmentStorage->Rollback();
- lpDatabase->Rollback();
- }
- if(lpsNewEntryId)
- FreeEntryId(lpsNewEntryId, true);
- return er;
- }
- /**
- * Copy folder and his childs
- *
- * @note please check the object type before you call this function, the type should be MAPI_FOLDER
- */
- static ECRESULT CopyFolderObjects(struct soap *soap, ECSession *lpecSession,
- unsigned int ulFolderFrom, unsigned int ulDestFolderId,
- char *lpszNewFolderName, bool bCopySubFolder, unsigned int ulSyncId)
- {
- ECRESULT er = erSuccess;
- ECDatabase *lpDatabase = NULL;
- std::string strQuery, strSubQuery, strExclude;
- DB_RESULT lpDBResult;
- DB_ROW lpDBRow;
- unsigned int ulNewDestFolderId = 0;
- unsigned int ulItems = 0;
- unsigned int ulGrandParent = 0;
- unsigned int ulDestStoreId = 0;
- unsigned int ulSourceStoreId = 0;
- bool bPartialCompletion = false;
- long long llStoreSize = 0;
- eQuotaStatus QuotaStatus;
- SOURCEKEY sSourceKey;
- SOURCEKEY sParentSourceKey;
- object_ptr<ECAttachmentStorage> lpAttachmentStorage;
- if(lpszNewFolderName == NULL) {
- ec_log_err("CopyFolderObjects: \"new folder name\" missing");
- er = KCERR_INVALID_PARAMETER;
- goto exit;
- }
- er = lpecSession->GetDatabase(&lpDatabase);
- if (er != erSuccess) {
- ec_log_err("CopyFolderObjects: cannot retrieve database: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- er = CreateAttachmentStorage(lpDatabase, &~lpAttachmentStorage);
- if (er != erSuccess) {
- ec_log_err("CopyFolderObjects: CreateAttachmentStorage failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- er = lpecSession->GetSessionManager()->GetCacheManager()->GetStore(ulDestFolderId, &ulDestStoreId, NULL);
- if (er != erSuccess) {
- ec_log_err("CopyFolderObjects: GetStore for %u (from cache) failed: %s (%x)", ulDestFolderId, GetMAPIErrorMessage(er), er);
- goto exit;
- }
- er = lpecSession->GetSessionManager()->GetCacheManager()->GetStore(ulFolderFrom, &ulSourceStoreId, NULL);
- if (er != erSuccess) {
- ec_log_err("CopyFolderObjects: GetStore for %u (from cache) failed: %s (%x)", ulFolderFrom, GetMAPIErrorMessage(er), er);
- goto exit;
- }
- er = lpAttachmentStorage->Begin();
- if (er != erSuccess) {
- ec_log_err("CopyFolderObjects: Begin() on attachment storage failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- er = lpDatabase->Begin();
- if (er != erSuccess) {
- ec_log_err("CopyFolderObjects: Begin() on database failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- // Quota check
- er = lpecSession->GetSecurity()->GetStoreSize(ulDestFolderId, &llStoreSize);
- if (er != erSuccess) {
- ec_log_err("CopyFolderObjects: GetStoreSize failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- er = lpecSession->GetSecurity()->CheckQuota(ulDestFolderId, llStoreSize, &QuotaStatus);
- if (er != erSuccess) {
- ec_log_err("CopyFolderObjects: CheckQuota failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- if(QuotaStatus == QUOTA_HARDLIMIT) {
- er = KCERR_STORE_FULL;
- goto exit;
- }
- // Create folder (with a sourcekey)
- er = CreateFolder(lpecSession, lpDatabase, ulDestFolderId, NULL, FOLDER_GENERIC, lpszNewFolderName, NULL, false, true, ulSyncId, NULL, &ulNewDestFolderId, NULL);
- if (er != erSuccess) {
- ec_log_err("CopyFolderObjects: CreateFolder \"%s\" in %u failed: %s (%x)", lpszNewFolderName, ulDestFolderId, GetMAPIErrorMessage(er), er);
- goto exit;
- }
- // Always use the string version if you want to exclude properties
- strExclude = " AND NOT (tag="+stringify(PROP_ID(PR_DELETED_ON))+" AND type="+stringify(PROP_TYPE(PR_DELETED_ON))+")";
- strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_DISPLAY_NAME_A))+" AND type="+stringify(PROP_TYPE(PR_DISPLAY_NAME_A))+")";
- //Exclude PR_SOURCE_KEY, PR_CHANGE_KEY, PR_PREDECESSOR_CHANGE_LIST
- strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_SOURCE_KEY))+" AND type="+stringify(PROP_TYPE(PR_SOURCE_KEY))+")";
- strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_CHANGE_KEY))+" AND type="+stringify(PROP_TYPE(PR_CHANGE_KEY))+")";
- strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_PREDECESSOR_CHANGE_LIST))+" AND type="+stringify(PROP_TYPE(PR_PREDECESSOR_CHANGE_LIST))+")";
- // Exclude the counters
- strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_CONTENT_COUNT))+" AND type="+stringify(PROP_TYPE(PR_CONTENT_COUNT))+")";
- strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_CONTENT_UNREAD))+" AND type="+stringify(PROP_TYPE(PR_CONTENT_UNREAD))+")";
- strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_FOLDER_CHILD_COUNT))+" AND type="+stringify(PROP_TYPE(PR_FOLDER_CHILD_COUNT))+")";
- strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_ASSOC_CONTENT_COUNT))+" AND type="+stringify(PROP_TYPE(PR_ASSOC_CONTENT_COUNT))+")";
- strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_DELETED_MSG_COUNT))+" AND type="+stringify(PROP_TYPE(PR_DELETED_MSG_COUNT))+")";
- strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_DELETED_FOLDER_COUNT))+" AND type="+stringify(PROP_TYPE(PR_DELETED_FOLDER_COUNT))+")";
- strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_DELETED_ASSOC_MSG_COUNT))+" AND type="+stringify(PROP_TYPE(PR_DELETED_ASSOC_MSG_COUNT))+")";
- strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_SUBFOLDERS))+" AND type="+stringify(PROP_TYPE(PR_SUBFOLDER))+")";
- // Copy properties...
- strQuery = "REPLACE INTO properties (hierarchyid, tag, type, val_ulong, val_string, val_binary,val_double,val_longint,val_hi,val_lo) SELECT "+stringify(ulNewDestFolderId)+", tag,type,val_ulong,val_string,val_binary,val_double,val_longint,val_hi,val_lo FROM properties WHERE hierarchyid ="+stringify(ulFolderFrom)+strExclude;
- er = lpDatabase->DoInsert(strQuery);
- if (er != erSuccess) {
- ec_log_err("CopyFolderObjects: copy properties step failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- // Copy MVproperties...
- strQuery = "REPLACE INTO mvproperties (hierarchyid, orderid, tag, type, val_ulong, val_string, val_binary,val_double,val_longint,val_hi,val_lo) SELECT "+stringify(ulNewDestFolderId)+", orderid, tag,type,val_ulong,val_string,val_binary,val_double,val_longint,val_hi,val_lo FROM mvproperties WHERE hierarchyid ="+stringify(ulFolderFrom);
- er = lpDatabase->DoInsert(strQuery);
- if (er != erSuccess) {
- ec_log_err("CopyFolderObjects: copy mvproperties step failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- // Copy large objects... if present .. probably not, on a folder
- er = lpAttachmentStorage->CopyAttachment(ulFolderFrom, ulNewDestFolderId);
- if (er != erSuccess && er != KCERR_NOT_FOUND) {
- ec_log_err("CopyFolderObjects: copy attachment step failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- er = erSuccess;
- // update ICS sytem with a change
- GetSourceKey(ulDestFolderId, &sParentSourceKey);
- GetSourceKey(ulNewDestFolderId, &sSourceKey);
- AddChange(lpecSession, ulSyncId, sSourceKey, sParentSourceKey, ICS_FOLDER_CHANGE);
- //Select all Messages of the home folder
- // Skip deleted and associated items
- strQuery = "SELECT id FROM hierarchy WHERE parent="+stringify(ulFolderFrom)+ " AND type="+stringify(MAPI_MESSAGE)+" AND flags & " + stringify(MSGFLAG_DELETED|MSGFLAG_ASSOCIATED) + " = 0";
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if (er != erSuccess) {
- ec_log_err("CopyFolderObjects: retrieving list of messages from home folder failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- ulItems = lpDatabase->GetNumRows(lpDBResult);
- // Walk through the messages list
- while( (lpDBRow = lpDatabase->FetchRow(lpDBResult)) )
- {
- if(lpDBRow[0] == NULL)
- continue; //FIXME: error show ???
- er = CopyObject(lpecSession, lpAttachmentStorage, atoui(lpDBRow[0]), ulNewDestFolderId, true, false, false, ulSyncId);
- // FIXME: handle KCERR_STORE_FULL
- if(er == KCERR_NOT_FOUND) {
- bPartialCompletion = true;
- } else if (er != erSuccess) {
- ec_log_err("CopyFolderObjects: CopyObject %s failed failed: %s (%x)", lpDBRow[0], GetMAPIErrorMessage(er), er);
- goto exit;
- }
- }
- // update the destination folder for disconnected clients
- er = WriteLocalCommitTimeMax(NULL, lpDatabase, ulNewDestFolderId, NULL);
- if (er != erSuccess) {
- ec_log_err("CopyFolderObjects: WriteLocalCommitTimeMax failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- er = ECTPropsPurge::AddDeferredUpdate(lpecSession, lpDatabase, ulDestFolderId, 0, ulNewDestFolderId);
- if (er != erSuccess) {
- ec_log_err("CopyFolderObjects: ECTPropsPurge::AddDeferredUpdate failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- er = lpDatabase->Commit();
- if (er != erSuccess) {
- ec_log_err("CopyFolderObjects: database commit failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- er = lpAttachmentStorage->Commit();
- if (er != erSuccess) {
- ec_log_err("CopyFolderObjects: attachment storage commit failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- // Notifications
- if(ulItems > 0)
- {
- //Update destenation folder
- g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_CHANGE, 0, ulNewDestFolderId, 0, MAPI_MESSAGE);
- // Update the grandfolder of dest. folder
- g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY, 0, ulDestFolderId, ulNewDestFolderId, MAPI_FOLDER);
- //Update destination folder
- g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, ulNewDestFolderId);
- g_lpSessionManager->NotificationModified(MAPI_FOLDER, ulNewDestFolderId);
- }
- g_lpSessionManager->GetCacheManager()->GetParent(ulFolderFrom ,&ulGrandParent);
- g_lpSessionManager->NotificationCopied(MAPI_FOLDER, ulNewDestFolderId, ulDestFolderId, ulFolderFrom, ulGrandParent);
- if(bCopySubFolder) {
- //Select all folders of the home folder
- // Skip deleted folders
- strQuery = "SELECT hierarchy.id, properties.val_string FROM hierarchy JOIN properties ON hierarchy.id = properties.hierarchyid WHERE hierarchy.parent=" + stringify(ulFolderFrom) +" AND hierarchy.type="+stringify(MAPI_FOLDER)+" AND (flags & " + stringify(MSGFLAG_DELETED) + ") = 0 AND properties.tag=" + stringify(KOPANO_TAG_DISPLAY_NAME) + " AND properties.type="+stringify(PT_STRING8);
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if (er != erSuccess) {
- ec_log_err("CopyFolderObjects: retrieving list of folders from home folder failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- if(lpDatabase->GetNumRows(lpDBResult) > 0) {
- // Walk through the folder list
- while( (lpDBRow = lpDatabase->FetchRow(lpDBResult)) )
- {
- if(lpDBRow[0] == NULL || lpDBRow[1] == NULL)
- continue; // ignore
- // Create SubFolder with messages. This object type checking is done in the where of the query
- er = CopyFolderObjects(soap, lpecSession, atoui(lpDBRow[0]), ulNewDestFolderId, lpDBRow[1], true, ulSyncId);
- if (er == KCWARN_PARTIAL_COMPLETION) {
- bPartialCompletion = true;
- } else if (er != erSuccess) {
- ec_log_err("CopyFolderObjects: CopyFolderObjects %s failed failed: %s (%x)", lpDBRow[0], GetMAPIErrorMessage(er), er);
- goto exit;
- }
- }
- }
- }
- if(bPartialCompletion && er == erSuccess)
- er = KCWARN_PARTIAL_COMPLETION;
- exit:
- if(lpDatabase && er != erSuccess && er != KCWARN_PARTIAL_COMPLETION) {
- lpDatabase->Rollback();
- if (lpAttachmentStorage)
- lpAttachmentStorage->Rollback();
- }
- return er;
- }
- /**
- * Copy one or more messages to a destenation
- */
- SOAP_ENTRY_START(copyObjects, *result, struct entryList *aMessages, entryId sDestFolderId, unsigned int ulFlags, unsigned int ulSyncId, unsigned int *result)
- {
- bool bPartialCompletion = false;
- unsigned int ulGrandParent=0;
- unsigned int ulDestFolderId = 0;
- ECListInt lObjectIds;
- ECListIntIterator iObjectId;
- size_t cObjectItems = 0;
- std::set<EntryId> setEntryIds;
- USE_DATABASE();
- const EntryId dstEntryId(&sDestFolderId);
- if(aMessages == NULL) {
- ec_log_err("SOAP::copyObjects: list of messages (entryList) missing");
- er = KCERR_INVALID_PARAMETER;
- goto exit;
- }
-
- for (unsigned int i = 0; i < aMessages->__size; ++i)
- setEntryIds.insert(EntryId(aMessages->__ptr[i]));
- setEntryIds.insert(EntryId(sDestFolderId));
-
- er = BeginLockFolders(lpDatabase, setEntryIds, LOCK_EXCLUSIVE);
- if (er != erSuccess) {
- ec_log_err("SOAP::copyObjects: failed locking folders: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- er = lpecSession->GetObjectFromEntryId(&sDestFolderId, &ulDestFolderId);
- if (er != erSuccess) {
- std::string dstEntryIdStr = dstEntryId;
- ec_log_err("SOAP::copyObjects: failed obtaining object by entry id (%s): %s (%x)", dstEntryIdStr.c_str(), GetMAPIErrorMessage(er), er);
- goto exit;
- }
- // Check permission, Destination folder
- er = lpecSession->GetSecurity()->CheckPermission(ulDestFolderId, ecSecurityCreate);
- if (er != erSuccess) {
- ec_log_err("SOAP::copyObjects: failed checking permissions for folder id %u: %s (%x)", ulDestFolderId, GetMAPIErrorMessage(er), er);
- goto exit;
- }
- if(g_lpSessionManager->GetCacheManager()->GetEntryListToObjectList(aMessages, &lObjectIds) != erSuccess)
- bPartialCompletion = true;
- // @note The object type checking wille be done in MoveObjects or CopyObject
- //check copy or a move
- if(ulFlags & FOLDER_MOVE ) { // A move
- er = MoveObjects(lpecSession, lpDatabase, &lObjectIds, ulDestFolderId, ulSyncId);
- if (er != erSuccess) {
- ec_log_err("SOAP::copyObjects: MoveObjects failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- }else { // A copy
- cObjectItems = lObjectIds.size();
- for (iObjectId = lObjectIds.begin();
- iObjectId != lObjectIds.end(); ++iObjectId) {
- er = CopyObject(lpecSession, NULL, *iObjectId, ulDestFolderId, true, true, cObjectItems < EC_TABLE_CHANGE_THRESHOLD, ulSyncId);
- if(er != erSuccess) {
- ec_log_err("SOAP::copyObjects: failed copying object %u: %s (%x)", *iObjectId, GetMAPIErrorMessage(er), er);
- bPartialCompletion = true;
- er = erSuccess;
- }
- }
- // update the destination folder for disconnected clients
- er = WriteLocalCommitTimeMax(NULL, lpDatabase, ulDestFolderId, NULL);
- if (er != erSuccess) {
- ec_log_err("SOAP::copyObjects: WriteLocalCommitTimeMax failed: %s (%x)", GetMAPIErrorMessage(er), er);
- goto exit;
- }
- if(cObjectItems >= EC_TABLE_CHANGE_THRESHOLD)
- g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_CHANGE, 0, ulDestFolderId, 0, MAPI_MESSAGE);
- // Update the grandfolder of dest. folder
- g_lpSessionManager->GetCacheManager()->GetParent(ulDestFolderId, &ulGrandParent);
- g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY, 0, ulGrandParent, ulDestFolderId, MAPI_FOLDER);
- //Update destination folder
- g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, ulDestFolderId);
- g_lpSessionManager->NotificationModified(MAPI_FOLDER, ulDestFolderId);
- }
- if(bPartialCompletion && er == erSuccess)
- er = KCWARN_PARTIAL_COMPLETION;
- exit:
- lpDatabase->Commit();
-
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(copyFolder, *result, entryId sEntryId, entryId sDestFolderId, char *lpszNewFolderName, unsigned int ulFlags, unsigned int ulSyncId, unsigned int *result)
- {
- unsigned int ulAffRows = 0;
- unsigned int ulOldParent = 0;
- unsigned int ulGrandParent = 0;
- unsigned int ulParentCycle = 0;
- unsigned int ulDestStoreId = 0;
- unsigned int ulSourceStoreId = 0;
- unsigned int ulObjFlags = 0;
- unsigned int ulOldGrandParent = 0;
- unsigned int ulFolderId = 0;
- unsigned int ulDestFolderId = 0;
- unsigned int ulSourceType = 0;
- unsigned int ulDestType = 0;
- long long llFolderSize = 0;
- SOURCEKEY sSourceKey;
- SOURCEKEY sParentSourceKey; // Old parent
- SOURCEKEY sDestSourceKey; // New parent
- std::string strSubQuery;
- USE_DATABASE();
- const EntryId srcEntryId(&sEntryId);
- const EntryId dstEntryId(&sDestFolderId);
- std::string name;
- // NOTE: lpszNewFolderName can be NULL
- if (lpszNewFolderName)
- lpszNewFolderName = STRIN_FIX(lpszNewFolderName);
- er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulFolderId);
- if (er != erSuccess) {
- const std::string srcEntryIdStr = srcEntryId;
- ec_log_err("SOAP::copyFolder GetObjectFromEntryId failed for %s: %s (%x)", srcEntryIdStr.c_str(), GetMAPIErrorMessage(er), er);
- return er;
- }
- // Get source store
- er = g_lpSessionManager->GetCacheManager()->GetStore(ulFolderId, &ulSourceStoreId, NULL);
- if (er != erSuccess) {
- ec_log_err("SOAP::copyFolder GetStore failed for folder id %ul: %s (%x)", ulFolderId, GetMAPIErrorMessage(er), er);
- return er;
- }
- er = lpecSession->GetObjectFromEntryId(&sDestFolderId, &ulDestFolderId);
- if (er != erSuccess) {
- const std::string dstEntryIdStr = dstEntryId;
- ec_log_err("SOAP::copyFolder GetObjectFromEntryId failed for %s: %s (%x)", dstEntryIdStr.c_str(), GetMAPIErrorMessage(er), er);
- return er;
- }
- // Get dest store
- er = g_lpSessionManager->GetCacheManager()->GetStore(ulDestFolderId, &ulDestStoreId, NULL);
- if (er != erSuccess) {
- ec_log_err("SOAP::copyFolder GetStore for folder %d failed: %s (%x)", ulDestFolderId, GetMAPIErrorMessage(er), er);
- return er;
- }
- if(ulDestStoreId != ulSourceStoreId) {
- ec_log_err("SOAP::copyFolder copy from/to different stores (from %u to %u) is not supported", ulSourceStoreId, ulDestStoreId);
- assert(false);
- return KCERR_NO_SUPPORT;
- }
- // Check permission
- er = lpecSession->GetSecurity()->CheckPermission(ulDestFolderId, ecSecurityCreateFolder);
- if (er != erSuccess) {
- ec_log_debug("SOAP::copyFolder copy folder (to %u) is not allowed: %s (%x)", ulDestFolderId, GetMAPIErrorMessage(er), er);
- return er;
- }
- if(ulFlags & FOLDER_MOVE ) // is the folder editable?
- er = lpecSession->GetSecurity()->CheckPermission(ulFolderId, ecSecurityFolderAccess);
- else // is the folder readable
- er = lpecSession->GetSecurity()->CheckPermission(ulFolderId, ecSecurityRead);
- if (er != erSuccess) {
- ec_log_debug("SOAP::copyFolder folder (%u) is not editable: %s (%x)", ulFolderId, GetMAPIErrorMessage(er), er);
- return er;
- }
- // Check MAPI_E_FOLDER_CYCLE
- if(ulFolderId == ulDestFolderId) {
- ec_log_err("SOAP::copyFolder target folder (%u) cannot be the same as source folder", ulDestFolderId);
- return KCERR_FOLDER_CYCLE;
- }
- // Get the parent id, for notification and copy
- er = g_lpSessionManager->GetCacheManager()->GetObject(ulFolderId, &ulOldParent, NULL, &ulObjFlags, &ulSourceType);
- if (er != erSuccess) {
- ec_log_err("SOAP::copyFolder cannot get parent folder id for %u: %s (%x)", ulFolderId, GetMAPIErrorMessage(er), er);
- return er;
- }
- er = g_lpSessionManager->GetCacheManager()->GetObject(ulDestFolderId, NULL, NULL, NULL, &ulDestType);
- if (er != erSuccess) {
- ec_log_err("SOAP::copyFolder cannot get type of destination folder (%u): %s (%x)", ulDestFolderId, GetMAPIErrorMessage(er), er);
- return er;
- }
- if (ulSourceType != MAPI_FOLDER || ulDestType != MAPI_FOLDER) {
- const std::string srcEntryIdStr = srcEntryId;
- const std::string dstEntryIdStr = dstEntryId;
- ec_log_err("SOAP::copyFolder source (%u) or destination (%u) is not a folder, invalid entry id (%s / %s)", ulSourceType, ulDestType, srcEntryIdStr.c_str(), dstEntryIdStr.c_str());
- return KCERR_INVALID_ENTRYID;
- }
- // Check folder and dest folder are the same
- if (!(ulObjFlags & MSGFLAG_DELETED) && (ulFlags & FOLDER_MOVE) &&
- ulDestFolderId == ulOldParent) {
- ec_log_debug("SOAP::copyFolder destination (%u) == source", ulDestFolderId);
- return erSuccess; // Do nothing... folder already on the right place
- }
- ulParentCycle = ulDestFolderId;
- while (g_lpSessionManager->GetCacheManager()->GetParent(ulParentCycle, &ulParentCycle) == erSuccess) {
- if(ulFolderId == ulParentCycle)
- {
- ec_log_debug("SOAP::copyFolder infinite loop detected for %u", ulDestFolderId);
- return KCERR_FOLDER_CYCLE;
- }
- }
- // Check whether the requested name already exists
- strQuery = "SELECT hierarchy.id FROM hierarchy JOIN properties ON hierarchy.id = properties.hierarchyid WHERE parent=" + stringify(ulDestFolderId) + " AND (hierarchy.flags & " + stringify(MSGFLAG_DELETED) + ") = 0 AND hierarchy.type="+stringify(MAPI_FOLDER)+" AND properties.tag=" + stringify(KOPANO_TAG_DISPLAY_NAME) + " AND properties.type="+stringify(PT_STRING8);
- if(lpszNewFolderName) {
- name = lpszNewFolderName;
- strQuery+= " AND properties.val_string = '" + lpDatabase->Escape(lpszNewFolderName) + "'";
- } else {
- name = format("%u", ulFolderId);
- strSubQuery = "SELECT properties.val_string FROM hierarchy JOIN properties ON hierarchy.id = properties.hierarchyid WHERE hierarchy.id=" + stringify(ulFolderId) + " AND properties.tag=" + stringify(KOPANO_TAG_DISPLAY_NAME) + " AND properties.type=" + stringify(PT_STRING8);
- strQuery+= " AND properties.val_string = ("+strSubQuery+")";
- }
- strQuery += " LIMIT 1";
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if (er != erSuccess) {
- ec_log_debug("SOAP::copyFolder check for existing name (%s) failed: %s (%x)", name.c_str(), GetMAPIErrorMessage(er), er);
- return er;
- }
- if(lpDatabase->GetNumRows(lpDBResult) > 0 && !ulSyncId) {
- ec_log_err("SOAP::copyFolder(): target name (%s) already exists", name.c_str());
- return KCERR_COLLISION;
- }
- if(lpszNewFolderName == NULL)
- {
- strQuery = "SELECT properties.val_string FROM hierarchy JOIN properties ON hierarchy.id = properties.hierarchyid WHERE hierarchy.id=" + stringify(ulFolderId) + " AND properties.tag=" + stringify(KOPANO_TAG_DISPLAY_NAME) + " AND properties.type=" + stringify(PT_STRING8) + " LIMIT 1";
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if (er != erSuccess) {
- ec_log_err("SOAP::copyFolder(): problem retrieving source name for %u: %s (%x)", ulFolderId, GetMAPIErrorMessage(er), er);
- return er;
- }
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
- if( lpDBRow == NULL || lpDBRow[0] == NULL) {
- ec_log_err("SOAP::copyFolder(): source name (%s) not known", name.c_str());
- return KCERR_NOT_FOUND;
- }
- lpszNewFolderName = s_alloc<char>(soap, strlen(lpDBRow[0])+1);
- memcpy(lpszNewFolderName, lpDBRow[0], strlen(lpDBRow[0])+1);
- }
- //check copy or a move
- if(ulFlags & FOLDER_MOVE ) {
- if (ulObjFlags & MSGFLAG_DELETED) {
- /*
- * The folder we are moving used to be deleted. This
- * effictively makes this call an un-delete. We need to
- * get the folder size for quota management.
- */
- er = GetFolderSize(lpDatabase, ulFolderId, &llFolderSize);
- if (er != erSuccess) {
- ec_log_err("SOAP::copyFolder(): cannot find size of folder %u: %s (%x)", ulFolderId, GetMAPIErrorMessage(er), er);
- return er;
- }
- }
- // Get grandParent of the old folder
- g_lpSessionManager->GetCacheManager()->GetParent(ulOldParent, &ulOldGrandParent);
- er = lpDatabase->Begin();
- if (er != erSuccess) {
- ec_log_err("SOAP::copyFolder(): cannot start transaction: %s (%x)", GetMAPIErrorMessage(er), er);
- return er;
- }
- // Move the folder to the dest. folder
-
- // FIXME update modtime
- strQuery = "UPDATE hierarchy SET parent="+stringify(ulDestFolderId)+", flags=flags&"+stringify(~MSGFLAG_DELETED)+" WHERE id="+stringify(ulFolderId);
- if ((er = lpDatabase->DoUpdate(strQuery, &ulAffRows)) != erSuccess) {
- lpDatabase->Rollback();
- ec_log_err("SOAP::copyFolder(): update of modification time failed: %s (%x)", GetMAPIErrorMessage(er), er);
- return KCERR_DATABASE_ERROR;
- }
- if(ulAffRows != 1) {
- lpDatabase->Rollback();
- ec_log_err("SOAP::copyFolder(): unexpected number of affected rows (expected: 1, got: %u)", ulAffRows);
- return KCERR_DATABASE_ERROR;
- }
- // Update the folder to the destination folder
- //Info: Always an update, It's not faster first check and than update/or not
- strQuery = "UPDATE properties SET val_string = '" + lpDatabase->Escape(lpszNewFolderName) + "' WHERE tag=" + stringify(KOPANO_TAG_DISPLAY_NAME) + " AND hierarchyid="+stringify(ulFolderId) + " AND type=" + stringify(PT_STRING8);
- if ((er = lpDatabase->DoUpdate(strQuery, &ulAffRows)) != erSuccess) {
- ec_log_err("SOAP::copyFolder(): actual move of folder %s failed: %s (%x)", lpszNewFolderName, GetMAPIErrorMessage(er), er);
- lpDatabase->Rollback();
- return KCERR_DATABASE_ERROR;
- }
- // remove PR_DELETED_ON, as the folder is a softdelete folder
- strQuery = "DELETE FROM properties WHERE hierarchyid="+stringify(ulFolderId)+" AND tag="+stringify(PROP_ID(PR_DELETED_ON))+" AND type="+stringify(PROP_TYPE(PR_DELETED_ON));
- er = lpDatabase->DoDelete(strQuery);
- if(er != erSuccess) {
- ec_log_err("SOAP::copyFolder(): cannot remove PR_DELETED_ON property for %u: %s (%x)", ulFolderId, GetMAPIErrorMessage(er), er);
- lpDatabase->Rollback();
- return KCERR_DATABASE_ERROR;
- }
- // Update the store size if we did an undelete. Note ulSourceStoreId == ulDestStoreId.
- if (llFolderSize > 0) {
- er = UpdateObjectSize(lpDatabase, ulSourceStoreId, MAPI_STORE, UPDATE_ADD, llFolderSize);
- if (er != erSuccess) {
- ec_log_err("SOAP::copyFolder(): problem updating store (%u) size: %s (%x)", ulSourceStoreId, GetMAPIErrorMessage(er), er);
- return er;
- }
- }
- // ICS
- GetSourceKey(ulFolderId, &sSourceKey);
- GetSourceKey(ulDestFolderId, &sDestSourceKey);
- GetSourceKey(ulOldParent, &sParentSourceKey);
-
- AddChange(lpecSession, ulSyncId, sSourceKey, sParentSourceKey, ICS_FOLDER_CHANGE);
- AddChange(lpecSession, ulSyncId, sSourceKey, sDestSourceKey, ICS_FOLDER_CHANGE);
- // Update folder counters
- if (ulObjFlags & MSGFLAG_DELETED) {
- // Undelete
- er = UpdateFolderCount(lpDatabase, ulOldParent, PR_DELETED_FOLDER_COUNT, -1);
- if (er == erSuccess)
- er = UpdateFolderCount(lpDatabase, ulDestFolderId, PR_SUBFOLDERS, 1);
- if (er == erSuccess)
- er = UpdateFolderCount(lpDatabase, ulDestFolderId, PR_FOLDER_CHILD_COUNT, 1);
- } else {
- // Move
- er = UpdateFolderCount(lpDatabase, ulOldParent, PR_SUBFOLDERS, -1);
- if (er == erSuccess)
- er = UpdateFolderCount(lpDatabase, ulOldParent, PR_FOLDER_CHILD_COUNT, -1);
- if (er == erSuccess)
- er = UpdateFolderCount(lpDatabase, ulDestFolderId, PR_SUBFOLDERS, 1);
- if (er == erSuccess)
- er = UpdateFolderCount(lpDatabase, ulDestFolderId, PR_FOLDER_CHILD_COUNT, 1);
- }
- if (er != erSuccess) {
- ec_log_err("SOAP::copyFolder(): updating folder counts failed: %s (%x)", GetMAPIErrorMessage(er), er);
- return er;
- }
- er = ECTPropsPurge::AddDeferredUpdate(lpecSession, lpDatabase, ulDestFolderId, ulOldParent, ulFolderId);
- if (er != erSuccess) {
- ec_log_err("SOAP::copyFolder(): ECTPropsPurge::AddDeferredUpdate failed: %s (%x)", GetMAPIErrorMessage(er), er);
- return er;
- }
- er = lpDatabase->Commit();
- if(er != erSuccess) {
- ec_log_err("SOAP::copyFolder(): database commit failed: %s (%x)", GetMAPIErrorMessage(er), er);
- lpDatabase->Rollback();
- return er;
- }
- // Cache update for objects
- g_lpSessionManager->GetCacheManager()->Update(fnevObjectMoved, ulFolderId);
- // Notify that the folder has moved
- g_lpSessionManager->NotificationMoved(MAPI_FOLDER, ulFolderId, ulDestFolderId, ulOldParent);
- // Update the old folder
- g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, ulOldParent);
- g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_DELETE, 0, ulOldParent, ulFolderId, MAPI_FOLDER);
- g_lpSessionManager->NotificationModified(MAPI_FOLDER, ulOldParent);
- // Update the old folder's parent
- g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY, 0, ulOldGrandParent, ulOldParent, MAPI_FOLDER);
- // Update the destination folder
- g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, ulDestFolderId);
- g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_ADD, 0, ulDestFolderId, ulFolderId, MAPI_FOLDER);
- g_lpSessionManager->NotificationModified(MAPI_FOLDER, ulDestFolderId);
- // Update the destination's parent
- g_lpSessionManager->GetCacheManager()->GetParent(ulDestFolderId, &ulGrandParent);
- g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY, 0, ulGrandParent, ulDestFolderId, MAPI_FOLDER);
- }else {// a copy
- er = CopyFolderObjects(soap, lpecSession, ulFolderId, ulDestFolderId, lpszNewFolderName, !!(ulFlags©_SUBFOLDERS), ulSyncId);
- if (er != erSuccess) {
- ec_log_err("SOAP::copyFolder(): CopyFolderObjects (src folder: %u, dest folder: %u, new name: \"%s\") failed: %s (%x)", ulFolderId, ulDestFolderId, lpszNewFolderName, GetMAPIErrorMessage(er), er);
- return er;
- }
- }
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(notify, *result, struct notification sNotification, unsigned int *result)
- {
- unsigned int ulKey = 0;
- USE_DATABASE();
- // You are only allowed to send newmail notifications at the moment. This could currently
- // only be misused to send other users new mail popup notification for e-mails that aren't
- // new at all ...
- if (sNotification.ulEventType != fnevNewMail)
- return KCERR_NO_ACCESS;
- if (sNotification.newmail == nullptr ||
- sNotification.newmail->pParentId == nullptr ||
- sNotification.newmail->pEntryId == nullptr)
- return KCERR_INVALID_PARAMETER;
- if (!bSupportUnicode && sNotification.newmail->lpszMessageClass != NULL)
- sNotification.newmail->lpszMessageClass = STRIN_FIX(sNotification.newmail->lpszMessageClass);
- er = lpecSession->GetObjectFromEntryId(sNotification.newmail->pParentId, &ulKey);
- if(er != erSuccess)
- return er;
- sNotification.ulConnection = ulKey;
- return g_lpSessionManager->AddNotification(&sNotification, ulKey);
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(getReceiveFolderTable, lpsReceiveFolderTable->er, entryId sStoreId, struct receiveFolderTableResponse *lpsReceiveFolderTable)
- {
- int ulRows = 0;
- int i;
- unsigned int ulStoreid = 0;
- USE_DATABASE();
- er = lpecSession->GetObjectFromEntryId(&sStoreId, &ulStoreid);
- if(er != erSuccess)
- return er;
- // Check permission
- er = lpecSession->GetSecurity()->CheckPermission(ulStoreid, ecSecurityRead);
- if(er != erSuccess)
- return er;
- strQuery = "SELECT objid, messageclass FROM receivefolder WHERE storeid="+stringify(ulStoreid);
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if(er != erSuccess)
- return er;
- ulRows = lpDatabase->GetNumRows(lpDBResult);
- lpsReceiveFolderTable->sFolderArray.__ptr = s_alloc<receiveFolder>(soap, ulRows);
- lpsReceiveFolderTable->sFolderArray.__size = 0;
- i = 0;
- while((lpDBRow = lpDatabase->FetchRow(lpDBResult)) )
- {
- if(lpDBRow == NULL || lpDBRow[0] == NULL || lpDBRow[1] == NULL){
- ec_log_err("getReceiveFolderTable(): row or col null");
- return KCERR_DATABASE_ERROR;
- }
- er = g_lpSessionManager->GetCacheManager()->GetEntryIdFromObject(atoui(lpDBRow[0]), soap, 0, &lpsReceiveFolderTable->sFolderArray.__ptr[i].sEntryId);
- if(er != erSuccess){
- er = erSuccess;
- continue;
- }
- lpsReceiveFolderTable->sFolderArray.__ptr[i].lpszAExplicitClass = STROUT_FIX_CPY(lpDBRow[1]);
- ++i;
- }
- lpsReceiveFolderTable->sFolderArray.__size = i;
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(deleteUser, *result, unsigned int ulUserId, entryId sUserId, unsigned int *result)
- {
- er = GetLocalId(sUserId, ulUserId, &ulUserId, NULL);
- if (er != erSuccess)
- return er;
- // Check permission
- er = lpecSession->GetSecurity()->IsAdminOverUserObject(ulUserId);
- if(er != erSuccess)
- return er;
- return lpecSession->GetUserManagement()->DeleteObjectAndSync(ulUserId);
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(unhookStore, *result, unsigned int ulStoreType, entryId sUserId, unsigned int ulSyncId, unsigned int *result)
- {
- unsigned int ulUserId = 0;
- unsigned int ulAffected = 0;
- std::string strGUID = "Unknown";
- USE_DATABASE();
- // do not use GetLocalId since the user may exist on a different server,
- // but will be migrated here and we need to remove the previous store with different guid.
- er = ABEntryIDToID(&sUserId, &ulUserId, NULL, NULL);
- if(er != erSuccess)
- goto exit;
- if (ulUserId == 0 || ulUserId == KOPANO_UID_SYSTEM || !ECSTORE_TYPE_ISVALID(ulStoreType))
- {
- er = KCERR_INVALID_PARAMETER;
- goto exit;
- }
- er = lpDatabase->Begin();
- if (er != erSuccess)
- goto exit;
- strQuery = "SELECT guid FROM stores WHERE user_id=" + stringify(ulUserId) + " AND type=" + stringify(ulStoreType) + " LIMIT 1";
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if (er != erSuccess)
- goto exit;
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
- lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
- if (lpDBRow == NULL || lpDBRow[0] == NULL) {
- // store not on this server
- er = KCERR_NOT_FOUND;
- goto exit;
- }
- strGUID = bin2hex(lpDBLen[0], (unsigned char*)lpDBRow[0]);
- strQuery = "UPDATE stores SET user_id=0 WHERE user_id=" + stringify(ulUserId) + " AND type=" + stringify(ulStoreType);
- er = lpDatabase->DoUpdate(strQuery, &ulAffected);
- if (er != erSuccess)
- goto exit;
- // ulAffected == 0: The user was already orphaned
- // ulAffected == 1: correctly disowned owner of store
- if (ulAffected > 1) {
- er = KCERR_COLLISION;
- ec_log_err("unhookStore(): more than expected");
- goto exit;
- }
- er = lpDatabase->Commit();
- if (er != erSuccess)
- goto exit;
- exit:
- if (er != erSuccess)
- ec_log_err("Unhook of store (type %d) with userid %d and GUID %s failed with error code 0x%x", ulStoreType, ulUserId, strGUID.c_str(), er);
- else
- ec_log_err("Unhook of store (type %d) with userid %d and GUID %s succeeded", ulStoreType, ulUserId, strGUID.c_str());
-
- ROLLBACK_ON_ERROR();
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(hookStore, *result, unsigned int ulStoreType, entryId sUserId, struct xsd__base64Binary sStoreGuid, unsigned int ulSyncId, unsigned int *result)
- {
- unsigned int ulUserId = 0;
- unsigned int ulAffected = 0;
- objectdetails_t sUserDetails;
- USE_DATABASE();
- // do not use GetLocalId since the user may exist on a different server,
- // but will be migrated here and we need to hook an old store with specified guid.
- er = ABEntryIDToID(&sUserId, &ulUserId, NULL, NULL);
- if(er != erSuccess)
- goto exit;
- if (ulUserId == 0 || ulUserId == KOPANO_UID_SYSTEM || !ECSTORE_TYPE_ISVALID(ulStoreType))
- {
- er = KCERR_INVALID_PARAMETER;
- goto exit;
- }
- // get user details, see if this is the correct server
- er = lpecSession->GetUserManagement()->GetObjectDetails(ulUserId, &sUserDetails);
- if (er != erSuccess)
- goto exit;
- // check if store currently is owned and the correct type
- strQuery = "SELECT users.id, stores.id, stores.user_id, stores.hierarchy_id, stores.type FROM stores LEFT JOIN users ON stores.user_id = users.id WHERE guid = ";
- strQuery += lpDatabase->EscapeBinary(sStoreGuid.__ptr, sStoreGuid.__size);
- strQuery += " LIMIT 1";
-
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if (er != erSuccess)
- goto exit;
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
- lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
- if(lpDBRow == NULL || lpDBLen == NULL) {
- er = KCERR_NOT_FOUND;
- goto exit;
- }
- if (lpDBRow[4] == NULL) {
- er = KCERR_DATABASE_ERROR;
- ec_log_err("hookStore(): col null");
- goto exit;
- }
- if (lpDBRow[0]) {
- // this store already belongs to a user
- er = KCERR_COLLISION;
- ec_log_err("hookStore(): store already belongs to a user");
- goto exit;
- }
- if (atoui(lpDBRow[4]) != ulStoreType) {
- ec_log_err("Requested store type is %u, actual store type is %s", ulStoreType, lpDBRow[4]);
- er = KCERR_INVALID_TYPE;
- goto exit;
- }
- ec_log_info("Hooking store \"%s\" to user %d", lpDBRow[1], ulUserId);
- // lpDBRow[2] is the old user id, which is now orphaned. We'll use this id to make the other store orphaned, so we "trade" user IDs.
- // update user with new store id
- er = lpDatabase->Begin();
- if (er != erSuccess)
- goto exit;
- // remove previous user of store
- strQuery = "UPDATE stores SET user_id = " + string(lpDBRow[2]) + " WHERE user_id = " + stringify(ulUserId) + " AND type = " + stringify(ulStoreType);
- er = lpDatabase->DoUpdate(strQuery, &ulAffected);
- if (er != erSuccess)
- goto exit;
- // ulAffected == 0: The user was already orphaned
- // ulAffected == 1: correctly disowned previous owner of store
- if (ulAffected > 1) {
- er = KCERR_COLLISION;
- ec_log_err("hookStore(): owned by multiple users");
- goto exit;
- }
- // set new store
- strQuery = "UPDATE stores SET user_id = " + stringify(ulUserId) + " WHERE guid = ";
- strQuery += lpDatabase->EscapeBinary(sStoreGuid.__ptr, sStoreGuid.__size);
- er = lpDatabase->DoUpdate(strQuery, &ulAffected);
- if (er != erSuccess)
- goto exit;
- // we can't have one store being owned by multiple users
- if (ulAffected != 1) {
- er = KCERR_COLLISION;
- ec_log_err("hookStore(): owned by multiple users (2)");
- goto exit;
- }
- // update owner of store
- strQuery = "UPDATE hierarchy SET owner = " + stringify(ulUserId) + " WHERE id = " + string(lpDBRow[3]);
- er = lpDatabase->DoUpdate(strQuery, &ulAffected);
- if (er != erSuccess)
- goto exit;
- // one store has only one entry point in the hierarchy
- // (may be zero, when the user returns to its original store, so the owner field stays the same)
- if (ulAffected > 1) {
- er = KCERR_COLLISION;
- ec_log_err("hookStore(): owned by multiple users (3)");
- goto exit;
- }
- er = lpDatabase->Commit();
- if (er != erSuccess)
- goto exit;
- // remove store cache item
- g_lpSessionManager->GetCacheManager()->Update(fnevObjectMoved, atoi(lpDBRow[3]));
- exit:
- if (er != erSuccess)
- ec_log_err("Hook of store failed: 0x%x", er);
- ROLLBACK_ON_ERROR();
- }
- SOAP_ENTRY_END()
- // Used to move the store to the public, but this isn't possible due to multi-server setups.
- // And is was super slow because of the storeid column in the properties table.
- SOAP_ENTRY_START(deleteStore, *result, unsigned int ulStoreId, unsigned int ulSyncId, unsigned int *result)
- {
- er = KCERR_NOT_IMPLEMENTED;
- }
- SOAP_ENTRY_END()
- // softdelete the store from the database, so this function returns quickly
- SOAP_ENTRY_START(removeStore, *result, struct xsd__base64Binary sStoreGuid, unsigned int ulSyncId, unsigned int *result)
- {
- unsigned int ulCompanyId = 0;
- unsigned int ulStoreHierarchyId = 0;
- objectdetails_t sObjectDetails;
- std::string strUsername;
- USE_DATABASE();
- // find store id and company of guid
- strQuery = "SELECT users.id, stores.guid, stores.hierarchy_id, stores.company, stores.user_name FROM stores LEFT JOIN users ON stores.user_id = users.id WHERE stores.guid = ";
- strQuery += lpDatabase->EscapeBinary(sStoreGuid.__ptr, sStoreGuid.__size);
- strQuery += " LIMIT 1";
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if (er != erSuccess)
- goto exit;
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
- lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
- if(lpDBRow == NULL || lpDBLen == NULL) {
- er = KCERR_NOT_FOUND;
- goto exit;
- }
- // if users.id != NULL, user still present .. log a warning admin is doing something that might not have been the action it wanted to do.
- if (lpDBRow[0] != NULL) {
- // trying to remove store from existing user
- strUsername = lpDBRow[4];
- if (lpecSession->GetUserManagement()->GetObjectDetails(atoi(lpDBRow[0]), &sObjectDetails) == erSuccess)
- strUsername = sObjectDetails.GetPropString(OB_PROP_S_LOGIN); // fullname?
- ec_log_err("Unable to remove store: store is in use by user \"%s\"", strUsername.c_str());
- er = KCERR_COLLISION;
- goto exit;
- }
- // these are all 'not null' columns
- ulStoreHierarchyId = atoi(lpDBRow[2]);
- ulCompanyId = atoi(lpDBRow[3]);
- // Must be administrator over the company to be able to remove the store
- er = lpecSession->GetSecurity()->IsAdminOverUserObject(ulCompanyId);
- if (er != erSuccess)
- goto exit;
- ec_log_info("Started to remove store (%s) with storename \"%s\"", bin2hex(lpDBLen[1], reinterpret_cast<const unsigned char *>(lpDBRow[1])).c_str(), lpDBRow[4]);
- er = lpDatabase->Begin();
- if(er != hrSuccess)
- goto exit;
- // Soft delete store
- er = MarkStoreAsDeleted(lpecSession, lpDatabase, ulStoreHierarchyId, ulSyncId);
- if(er != erSuccess)
- goto exit;
- // Remove the store entry
- strQuery = "DELETE FROM stores WHERE guid=" + lpDatabase->EscapeBinary((unsigned char*)lpDBRow[1], lpDBLen[1]);
- er = lpDatabase->DoDelete(strQuery);
- if(er != erSuccess)
- goto exit;
- // Remove receivefolder entries
- strQuery = "DELETE FROM receivefolder WHERE storeid="+stringify(ulStoreHierarchyId);
- er = lpDatabase->DoDelete(strQuery);
- if(er != erSuccess)
- goto exit;
- // Remove the acls
- strQuery = "DELETE FROM acl WHERE hierarchy_id="+stringify(ulStoreHierarchyId);
- er = lpDatabase->DoDelete(strQuery);
- if(er != erSuccess)
- goto exit;
- // TODO: acl cache!
- er = lpDatabase->Commit();
- if(er != erSuccess)
- goto exit;
- ec_log_info("Finished remove store (%s)", bin2hex(lpDBLen[1], reinterpret_cast<const unsigned char *>(lpDBRow[1])).c_str());
- exit:
- if(er == KCERR_NO_ACCESS)
- ec_log_err("Failed to remove store access denied");
- else if(er != erSuccess) {
- lpDatabase->Rollback();
- ec_log_err("Failed to remove store, errorcode=0x%08X", er);
- }
- }
- SOAP_ENTRY_END()
- namespace KC {
- void *SoftDeleteRemover(void *lpTmpMain)
- {
- ECRESULT er = erSuccess;
- ECRESULT* lper = NULL;
- const char *lpszSetting = NULL;
- unsigned int ulDeleteTime = 0;
- unsigned int ulFolders = 0;
- unsigned int ulStores = 0;
- unsigned int ulMessages = 0;
- ECSession *lpecSession = NULL;
- lpszSetting = g_lpSessionManager->GetConfig()->GetSetting("softdelete_lifetime");
- if(lpszSetting)
- ulDeleteTime = atoi(lpszSetting) * 24 * 60 * 60;
- if(ulDeleteTime == 0)
- goto exit;
- er = g_lpSessionManager->CreateSessionInternal(&lpecSession);
- if(er != erSuccess)
- goto exit;
- // Lock the session
- lpecSession->Lock();
- ec_log_info("Start scheduled softdelete clean up");
- er = PurgeSoftDelete(lpecSession, ulDeleteTime, &ulMessages, &ulFolders, &ulStores, (bool*)lpTmpMain);
- exit:
- if(ulDeleteTime > 0) {
- if (er == erSuccess)
- ec_log_info("Softdelete done: removed %d stores, %d folders, and %d messages", ulStores, ulFolders, ulMessages);
- else if (er == KCERR_BUSY)
- ec_log_info("Softdelete already running");
- else
- ec_log_info("Softdelete failed: removed %d stores, %d folders, and %d messages", ulStores, ulFolders, ulMessages);
- }
- if(lpecSession) {
- lpecSession->Unlock();
- g_lpSessionManager->RemoveSessionInternal(lpecSession);
- }
- // Exit with the error result
- lper = new ECRESULT;
- *lper = er;
- return (void *)lper;
- }
- } /* namespace */
- SOAP_ENTRY_START(checkExistObject, *result, entryId sEntryId, unsigned int ulFlags, unsigned int *result)
- {
- unsigned int ulObjId = 0;
- unsigned int ulObjType = 0;
- unsigned int ulDBFlags = 0;
-
- USE_DATABASE();
- er = BeginLockFolders(lpDatabase, EntryId(sEntryId), LOCK_SHARED);
- if(er != erSuccess)
- goto exit;
-
- er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulObjId);
- if(er != erSuccess)
- goto exit;
- er = g_lpSessionManager->GetCacheManager()->GetObject(ulObjId, NULL, NULL, &ulDBFlags, &ulObjType);
- if(er != erSuccess)
- goto exit;
- if(ulFlags & SHOW_SOFT_DELETES) {
- if(!(ulDBFlags & MSGFLAG_DELETED)) {
- er = KCERR_NOT_FOUND;
- goto exit;
- }
- } else {
- if(ulDBFlags & MSGFLAG_DELETED) {
- er = KCERR_NOT_FOUND;
- goto exit;
- }
- }
- exit:
- lpDatabase->Commit();
-
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(readABProps, readPropsResponse->er, entryId sEntryId, struct readPropsResponse *readPropsResponse)
- {
- // FIXME: when props are PT_ERROR, they shouldn't be send to the client
- // now we have properties in the client which are MAPI_E_NOT_ENOUGH_MEMORY,
- // while they shouldn't be present (or atleast MAPI_E_NOT_FOUND)
- // These properties must be of type PT_UNICODE for string properties
- unsigned int sProps[] = {
- /* Don't touch the order of the first 7 elements!!! */
- PR_ENTRYID, PR_CONTAINER_FLAGS, PR_DEPTH, PR_EMS_AB_CONTAINERID, PR_DISPLAY_NAME, PR_EMS_AB_IS_MASTER, PR_EMS_AB_PARENT_ENTRYID,
- PR_EMAIL_ADDRESS, PR_OBJECT_TYPE, PR_DISPLAY_TYPE, PR_SEARCH_KEY, PR_PARENT_ENTRYID, PR_ADDRTYPE, PR_RECORD_KEY, PR_ACCOUNT,
- PR_SMTP_ADDRESS, PR_TRANSMITABLE_DISPLAY_NAME, PR_EMS_AB_HOME_MDB, PR_EMS_AB_HOME_MTA, PR_EMS_AB_PROXY_ADDRESSES,
- PR_EC_ADMINISTRATOR, PR_EC_NONACTIVE, PR_EC_COMPANY_NAME, PR_EMS_AB_X509_CERT, PR_AB_PROVIDER_ID, PR_EMS_AB_HIERARCHY_PATH,
- PR_EC_SENDAS_USER_ENTRYIDS, PR_EC_HOMESERVER_NAME, PR_DISPLAY_TYPE_EX, CHANGE_PROP_TYPE(PR_EMS_AB_IS_MEMBER_OF_DL, PT_MV_BINARY),
- PR_EC_ENABLED_FEATURES, PR_EC_DISABLED_FEATURES, PR_EC_ARCHIVE_SERVERS, PR_EC_ARCHIVE_COUPLINGS, PR_EMS_AB_ROOM_CAPACITY, PR_EMS_AB_ROOM_DESCRIPTION,
- PR_ASSISTANT
- };
- unsigned int sPropsContainerRoot[] = {
- /* Don't touch the order of the first 7 elements!!! */
- PR_ENTRYID, PR_CONTAINER_FLAGS, PR_DEPTH, PR_EMS_AB_CONTAINERID, PR_DISPLAY_NAME, PR_EMS_AB_IS_MASTER, PR_EMS_AB_PARENT_ENTRYID,
- PR_OBJECT_TYPE, PR_DISPLAY_TYPE, PR_SEARCH_KEY, PR_RECORD_KEY, PR_PARENT_ENTRYID, PR_AB_PROVIDER_ID, PR_EMS_AB_HIERARCHY_PATH, PR_ACCOUNT,
- PR_EC_HOMESERVER_NAME, PR_EC_COMPANY_NAME
- };
- struct propTagArray ptaProps;
- unsigned int ulId = 0;
- unsigned int ulTypeId = 0;
- ECDatabase* lpDatabase = NULL;
- objectid_t sExternId;
- std::unique_ptr<abprops_t> lExtraProps;
- unsigned int *lpProps = NULL;
- unsigned int ulProps = 0;
- int i = 0;
- er = lpecSession->GetDatabase(&lpDatabase);
- if (er != erSuccess)
- return er;
- er = ABEntryIDToID(&sEntryId, &ulId, &sExternId, &ulTypeId);
- if(er != erSuccess)
- return er;
- // A v1 EntryID would return a non-empty extern id string.
- if (!sExternId.id.empty())
- {
- er = lpecSession->GetSessionManager()->GetCacheManager()->GetUserObject(sExternId, &ulId, NULL, NULL);
- if (er != erSuccess)
- return er;
- }
- er = lpecSession->GetSecurity()->IsUserObjectVisible(ulId);
- if (er != erSuccess)
- return er;
- if (ulTypeId == MAPI_ABCONT) {
- lpProps = sPropsContainerRoot;
- ulProps = ARRAY_SIZE(sPropsContainerRoot);
- } else if (ulTypeId == MAPI_MAILUSER || ulTypeId == MAPI_DISTLIST) {
- lpProps = sProps;
- ulProps = ARRAY_SIZE(sProps);
- } else {
- return KCERR_INVALID_PARAMETER;
- }
- /* Load the additional addressbook properties */
- try {
- UserPlugin *lpPlugin = NULL;
- if (GetThreadLocalPlugin(g_lpSessionManager->GetPluginFactory(), &lpPlugin) == erSuccess)
- lExtraProps = lpPlugin->getExtraAddressbookProperties();
- } catch (...) { }
- ptaProps.__size = ulProps;
- if (lExtraProps.get())
- ptaProps.__size += lExtraProps->size();
- ptaProps.__ptr = s_alloc<unsigned int>(soap, ptaProps.__size);
- /* Copy fixed properties */
- memcpy(ptaProps.__ptr, lpProps, ulProps * sizeof(unsigned int));
- i = ulProps;
- /* Copy extra properties */
- if (lExtraProps.get()) {
- for (const auto &prop : *lExtraProps) {
- ptaProps.__ptr[i] = prop;
- /* The client requires some properties with non-standard types */
- switch ( PROP_ID(ptaProps.__ptr[i]) ) {
- case PROP_ID(PR_MANAGER_NAME):
- case PROP_ID(PR_EMS_AB_MANAGER):
- /* Rename PR_MANAGER_NAME to PR_EMS_AB_MANAGER and provide the PT_BINARY version with the entryid */
- ptaProps.__ptr[i] = CHANGE_PROP_TYPE(PR_EMS_AB_MANAGER, PT_BINARY);
- break;
- case PROP_ID(PR_EMS_AB_REPORTS):
- ptaProps.__ptr[i] = CHANGE_PROP_TYPE(PR_EMS_AB_REPORTS, PT_MV_BINARY);
- break;
- case PROP_ID(PR_EMS_AB_OWNER):
- /* Also provide the PT_BINARY version with the entryid */
- ptaProps.__ptr[i] = CHANGE_PROP_TYPE(PR_EMS_AB_OWNER, PT_BINARY);
- break;
- default:
- // @note plugin most likely returns PT_STRING8 and PT_MV_STRING8 types
- // Since CopyDatabasePropValToSOAPPropVal() always returns PT_UNICODE types, we will convert these here too
- // Therefore, the sProps / sPropsContainerRoot must contain PT_UNICODE types only!
- if (PROP_TYPE(ptaProps.__ptr[i]) == PT_MV_STRING8)
- ptaProps.__ptr[i] = CHANGE_PROP_TYPE(ptaProps.__ptr[i], PT_MV_UNICODE);
- else if (PROP_TYPE(ptaProps.__ptr[i]) == PT_STRING8)
- ptaProps.__ptr[i] = CHANGE_PROP_TYPE(ptaProps.__ptr[i], PT_UNICODE);
- break;
- }
- ++i;
- }
- }
- /* Update the total size, the previously set value might not be accurate */
- ptaProps.__size = i;
- /* Read properties */
- if (ulTypeId == MAPI_ABCONT) {
- er = lpecSession->GetUserManagement()->GetContainerProps(soap, ulId, &ptaProps, &readPropsResponse->aPropVal);
- if (er != erSuccess)
- return er;
- } else {
- er = lpecSession->GetUserManagement()->GetProps(soap, ulId, &ptaProps, &readPropsResponse->aPropVal);
- if (er != erSuccess)
- return er;
- }
- /* Copy properties which have been correctly read to tag array */
- readPropsResponse->aPropTag.__size = 0;
- readPropsResponse->aPropTag.__ptr = s_alloc<unsigned int>(soap, ptaProps.__size);
- for (gsoap_size_t i = 0; i < readPropsResponse->aPropVal.__size; ++i) {
- if (!bSupportUnicode) {
- er = FixPropEncoding(soap, stringCompat, Out, readPropsResponse->aPropVal.__ptr + i);
- if (er != erSuccess)
- return er;
- }
- if(PROP_TYPE(readPropsResponse->aPropVal.__ptr[i].ulPropTag) != PT_ERROR)
- readPropsResponse->aPropTag.__ptr[readPropsResponse->aPropTag.__size++] = readPropsResponse->aPropVal.__ptr[i].ulPropTag;
- }
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(writeABProps, *result, entryId sEntryId, struct propValArray *aPropVal, unsigned int *result)
- {
- er = KCERR_NOT_FOUND;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(deleteABProps, *result, entryId sEntryId, struct propTagArray *lpsPropTags, unsigned int *result)
- {
- er = KCERR_NOT_FOUND;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(loadABProp, lpsResponse->er, entryId sEntryId, unsigned int ulPropTag, struct loadPropResponse *lpsResponse)
- {
- return SOAP_OK;
- }
- SOAP_ENTRY_END()
- /**
- * ns__abResolveNames
- *
- * @param[in] lpaPropTag SOAP proptag array containing requested properties.
- * @param[in] lpsRowSet Rows with possible search request, if matching flag in lpaFlags is MAPI_UNRESOLVED.
- * @param[in] lpaFlags Status of row.
- * @param[in] ulFlags Client ulFlags for IABContainer::ResolveNames()
- * @param[out] lpsABResolveNames copies of new rows and flags.
- */
- SOAP_ENTRY_START(abResolveNames, lpsABResolveNames->er, struct propTagArray* lpaPropTag, struct rowSet* lpsRowSet, struct flagArray* lpaFlags, unsigned int ulFlags, struct abResolveNamesResponse* lpsABResolveNames)
- {
- unsigned int ulFlag = 0;
- unsigned int ulObjectId = 0;
- char* search = NULL;
- struct propValArray sPropValArrayDst;
- struct propVal *lpDisplayName = NULL;
- lpsABResolveNames->aFlags.__size = lpaFlags->__size;
- lpsABResolveNames->aFlags.__ptr = s_alloc<unsigned int>(soap, lpaFlags->__size);
- memset(lpsABResolveNames->aFlags.__ptr, 0, sizeof(unsigned int) * lpaFlags->__size);
- lpsABResolveNames->sRowSet.__size = lpsRowSet->__size;
- lpsABResolveNames->sRowSet.__ptr = s_alloc<propValArray>(soap, lpsRowSet->__size);
- memset(lpsABResolveNames->sRowSet.__ptr, 0, sizeof(struct propValArray) * lpsRowSet->__size);
- if (!bSupportUnicode) {
- er = FixRowSetEncoding(soap, stringCompat, In, lpsRowSet);
- if (er != erSuccess)
- return er;
- }
- for (gsoap_size_t i = 0; i < lpsRowSet->__size; ++i) {
- lpsABResolveNames->aFlags.__ptr[i] = lpaFlags->__ptr[i];
- if(lpaFlags->__ptr[i] == MAPI_RESOLVED)
- continue; // Client knows the information
- lpDisplayName = FindProp(&lpsRowSet->__ptr[i], CHANGE_PROP_TYPE(PR_DISPLAY_NAME, PT_UNSPECIFIED));
- if(lpDisplayName == NULL || (PROP_TYPE(lpDisplayName->ulPropTag) != PT_STRING8 && PROP_TYPE(lpDisplayName->ulPropTag) != PT_UNICODE))
- continue; // No display name
- /* Blackberry likes it to put a '=' in front of the username */
- search = strrchr(lpDisplayName->Value.lpszA, '=');
- if (search) {
- ++search;
- ulFlags |= EMS_AB_ADDRESS_LOOKUP;
- } else {
- search = lpDisplayName->Value.lpszA;
- }
- /* NOTE: ECUserManagement is responsible for calling ECSecurity::IsUserObjectVisible(ulObjectId) for found objects */
- switch (lpecSession->GetUserManagement()->SearchObjectAndSync(search, ulFlags, &ulObjectId)) {
- case KCERR_COLLISION:
- ulFlag = MAPI_AMBIGUOUS;
- break;
- case erSuccess:
- ulFlag = MAPI_RESOLVED;
- break;
- case KCERR_NOT_FOUND:
- default:
- ulFlag = MAPI_UNRESOLVED;
- break;
- }
- lpsABResolveNames->aFlags.__ptr[i] = ulFlag;
- if(lpsABResolveNames->aFlags.__ptr[i] == MAPI_RESOLVED) {
- er = lpecSession->GetUserManagement()->GetProps(soap, ulObjectId, lpaPropTag, &sPropValArrayDst);
- if(er != erSuccess)
- return er;
- er = MergePropValArray(soap, &lpsRowSet->__ptr[i], &sPropValArrayDst, &lpsABResolveNames->sRowSet.__ptr[i]);
- if(er != erSuccess)
- return er;
- }
- }
- if (!bSupportUnicode) {
- er = FixRowSetEncoding(soap, stringCompat, Out, &lpsABResolveNames->sRowSet);
- if (er != erSuccess)
- return er;
- }
- return erSuccess;
- }
- SOAP_ENTRY_END()
- /**
- * Syncs a new list of companies, and for each company syncs the users.
- *
- * @param[in] ulCompanyId unused, id of company to sync
- * @param[in] sCompanyId unused, entryid of company to sync
- * @param[out] result kopano error code
- *
- * @return soap error code
- */
- SOAP_ENTRY_START(syncUsers, *result, unsigned int ulCompanyId, entryId sCompanyId, unsigned int *result)
- {
- er = lpecSession->GetUserManagement()->SyncAllObjects();
- }
- SOAP_ENTRY_END()
- // Quota
- SOAP_ENTRY_START(GetQuota, lpsQuota->er, unsigned int ulUserid, entryId sUserId, bool bGetUserDefault, struct quotaResponse* lpsQuota)
- {
- quotadetails_t quotadetails;
- er = GetLocalId(sUserId, ulUserid, &ulUserid, NULL);
- if (er != erSuccess)
- return er;
- // Check permission
- if(lpecSession->GetSecurity()->IsAdminOverUserObject(ulUserid) != erSuccess &&
- (lpecSession->GetSecurity()->GetUserId() != ulUserid))
- return KCERR_NO_ACCESS;
- er = lpecSession->GetSecurity()->GetUserQuota(ulUserid, bGetUserDefault, "adetails);
- if(er != erSuccess)
- return er;
- lpsQuota->sQuota.bUseDefaultQuota = quotadetails.bUseDefaultQuota;
- lpsQuota->sQuota.bIsUserDefaultQuota = quotadetails.bIsUserDefaultQuota;
- lpsQuota->sQuota.llHardSize = quotadetails.llHardSize;
- lpsQuota->sQuota.llSoftSize = quotadetails.llSoftSize;
- lpsQuota->sQuota.llWarnSize = quotadetails.llWarnSize;
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(SetQuota, *result, unsigned int ulUserid, entryId sUserId, struct quota* lpsQuota, unsigned int *result)
- {
- quotadetails_t quotadetails;
- er = GetLocalId(sUserId, ulUserid, &ulUserid, NULL);
- if (er != erSuccess)
- return er;
- // Check permission
- if(lpecSession->GetSecurity()->IsAdminOverUserObject(ulUserid) != erSuccess &&
- (lpecSession->GetSecurity()->GetUserId() != ulUserid))
- return KCERR_NO_ACCESS;
- quotadetails.bUseDefaultQuota = lpsQuota->bUseDefaultQuota;
- quotadetails.bIsUserDefaultQuota = lpsQuota->bIsUserDefaultQuota;
- quotadetails.llHardSize = lpsQuota->llHardSize;
- quotadetails.llSoftSize = lpsQuota->llSoftSize;
- quotadetails.llWarnSize = lpsQuota->llWarnSize;
- return lpecSession->GetUserManagement()->SetQuotaDetailsAndSync(ulUserid, quotadetails);
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(AddQuotaRecipient, *result, unsigned int ulCompanyid, entryId sCompanyId, unsigned int ulRecipientId, entryId sRecipientId, unsigned int ulType, unsigned int *result);
- {
- er = GetLocalId(sCompanyId, ulCompanyid, &ulCompanyid, NULL);
- if (er != erSuccess)
- return er;
- if (lpecSession->GetSecurity()->IsAdminOverUserObject(ulCompanyid) != erSuccess)
- return KCERR_NO_ACCESS;
- er = GetLocalId(sRecipientId, ulRecipientId, &ulRecipientId, NULL);
- if (er != erSuccess)
- return er;
- if (OBJECTCLASS_TYPE(ulType) == OBJECTTYPE_MAILUSER)
- er = lpecSession->GetUserManagement()->AddSubObjectToObjectAndSync(OBJECTRELATION_QUOTA_USERRECIPIENT, ulCompanyid, ulRecipientId);
- else if (ulType == CONTAINER_COMPANY)
- er = lpecSession->GetUserManagement()->AddSubObjectToObjectAndSync(OBJECTRELATION_QUOTA_COMPANYRECIPIENT, ulCompanyid, ulRecipientId);
- else
- er = KCERR_INVALID_TYPE;
- return er;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(DeleteQuotaRecipient, *result, unsigned int ulCompanyid, entryId sCompanyId, unsigned int ulRecipientId, entryId sRecipientId, unsigned int ulType, unsigned int *result);
- {
- er = GetLocalId(sCompanyId, ulCompanyid, &ulCompanyid, NULL);
- if (er != erSuccess)
- return er;
- if (lpecSession->GetSecurity()->IsAdminOverUserObject(ulCompanyid) != erSuccess)
- return KCERR_NO_ACCESS;
- er = GetLocalId(sRecipientId, ulRecipientId, &ulRecipientId, NULL);
- if (er != erSuccess)
- return er;
- if (OBJECTCLASS_TYPE(ulType) == OBJECTTYPE_MAILUSER)
- er = lpecSession->GetUserManagement()->DeleteSubObjectFromObjectAndSync(OBJECTRELATION_QUOTA_USERRECIPIENT, ulCompanyid, ulRecipientId);
- else if (ulType == CONTAINER_COMPANY)
- er = lpecSession->GetUserManagement()->DeleteSubObjectFromObjectAndSync(OBJECTRELATION_QUOTA_COMPANYRECIPIENT, ulCompanyid, ulRecipientId);
- else
- er = KCERR_INVALID_TYPE;
- return er;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(GetQuotaRecipients, lpsUserList->er, unsigned int ulUserid, entryId sUserId, struct userListResponse *lpsUserList)
- {
- std::unique_ptr<std::list<localobjectdetails_t> > lpUsers;
- objectid_t sExternId;
- objectdetails_t details;
- userobject_relation_t relation;
- unsigned int ulCompanyId;
- bool bHasLocalStore = false;
- entryId sUserEid = {0};
- // does not return full class in sExternId.objclass
- er = GetLocalId(sUserId, ulUserid, &ulUserid, &sExternId);
- if (er != erSuccess)
- return er;
- // re-evaluate userid to externid to get the full class (mapi clients only know MAPI_ABCONT for companies)
- er = g_lpSessionManager->GetCacheManager()->GetUserObject(ulUserid, &sExternId, NULL, NULL);
- if (er != erSuccess)
- return er;
- er = CheckUserStore(lpecSession, ulUserid, ECSTORE_TYPE_PRIVATE, &bHasLocalStore);
- if (er != erSuccess)
- return er;
- if (!bHasLocalStore)
- return KCERR_NOT_FOUND;
- //Check permission
- if (lpecSession->GetSecurity()->IsAdminOverUserObject(ulUserid) != erSuccess)
- return KCERR_NO_ACCESS;
- /* Not all objectclasses support quota */
- if ((sExternId.objclass == NONACTIVE_CONTACT) ||
- (OBJECTCLASS_TYPE(sExternId.objclass) == OBJECTTYPE_DISTLIST) ||
- (sExternId.objclass == CONTAINER_ADDRESSLIST))
- return KCERR_INVALID_TYPE;
- er = lpecSession->GetUserManagement()->GetObjectDetails(ulUserid, &details);
- if (er != erSuccess)
- return er;
- if (OBJECTCLASS_TYPE(details.GetClass())== OBJECTTYPE_MAILUSER) {
- ulCompanyId = details.GetPropInt(OB_PROP_I_COMPANYID);
- relation = OBJECTRELATION_QUOTA_USERRECIPIENT;
- } else if (details.GetClass() == CONTAINER_COMPANY) {
- ulCompanyId = ulUserid;
- relation = OBJECTRELATION_QUOTA_COMPANYRECIPIENT;
- } else {
- return KCERR_INVALID_TYPE;
- }
- /* When uLCompanyId is 0 then there are no recipient relations we could request,
- * in that case we should manually allocate the list so it is safe to add the user
- * to the list. */
- if (ulCompanyId != 0) {
- er = lpecSession->GetUserManagement()->GetSubObjectsOfObjectAndSync(relation, ulCompanyId, &unique_tie(lpUsers));
- if (er != erSuccess)
- return er;
- } else
- lpUsers.reset(new std::list<localobjectdetails_t>);
- if (OBJECTCLASS_TYPE(details.GetClass())== OBJECTTYPE_MAILUSER) {
- /* The main recipient (the user over quota) must be the first entry */
- lpUsers->push_front(localobjectdetails_t(ulUserid, details));
- } else if (details.GetClass() == CONTAINER_COMPANY) {
- /* Append the system administrator for the company */
- unsigned int ulSystem;
- objectdetails_t systemdetails;
- ulSystem = details.GetPropInt(OB_PROP_I_SYSADMIN);
- er = lpecSession->GetSecurity()->IsUserObjectVisible(ulSystem);
- if (er != erSuccess)
- return er;
- er = lpecSession->GetUserManagement()->GetObjectDetails(ulSystem, &systemdetails);
- if (er != erSuccess)
- return er;
- lpUsers->push_front(localobjectdetails_t(ulSystem, systemdetails));
- /* The main recipient (the company's public store) must be the first entry */
- lpUsers->push_front(localobjectdetails_t(ulUserid, details));
- }
- lpsUserList->sUserArray.__size = 0;
- lpsUserList->sUserArray.__ptr = s_alloc<user>(soap, lpUsers->size());
- for (const auto &user : *lpUsers) {
- if ((OBJECTCLASS_TYPE(user.GetClass()) != OBJECTTYPE_MAILUSER) ||
- (details.GetClass() == NONACTIVE_CONTACT))
- continue;
- if (lpecSession->GetSecurity()->IsUserObjectVisible(user.ulId) != erSuccess)
- continue;
- er = GetABEntryID(user.ulId, soap, &sUserEid);
- if (er != erSuccess)
- return er;
- er = CopyUserDetailsToSoap(user.ulId, &sUserEid, user,
- lpecSession->GetCapabilities() & KOPANO_CAP_EXTENDED_ANON,
- soap, &lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size]);
- if (er != erSuccess)
- return er;
- // 6.40.0 stores the object class in the IsNonActive field
- if (lpecSession->ClientVersion() == ZARAFA_VERSION_6_40_0)
- lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size].ulIsNonActive = lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size].ulObjClass;
- if (!bSupportUnicode) {
- er = FixUserEncoding(soap, stringCompat, Out, lpsUserList->sUserArray.__ptr + lpsUserList->sUserArray.__size);
- if (er != erSuccess)
- return er;
- }
- ++lpsUserList->sUserArray.__size;
- if (sUserEid.__ptr)
- {
- // sUserEid is placed in userdetails, no need to free
- sUserEid.__ptr = NULL;
- sUserEid.__size = 0;
- }
- }
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(GetQuotaStatus, lpsQuotaStatus->er, unsigned int ulUserid, entryId sUserId, struct quotaStatus* lpsQuotaStatus)
- {
- quotadetails_t quotadetails;
- long long llStoreSize = 0;
- objectid_t sExternId;
- bool bHasLocalStore = false;
- eQuotaStatus QuotaStatus;
- //Set defaults
- lpsQuotaStatus->llStoreSize = 0;
- // does not return full class in sExternId.objclass
- er = GetLocalId(sUserId, ulUserid, &ulUserid, &sExternId);
- if (er != erSuccess)
- return er;
- // re-evaluate userid to externid to get the full class (mapi clients only know MAPI_ABCONT for companies)
- er = g_lpSessionManager->GetCacheManager()->GetUserObject(ulUserid, &sExternId, NULL, NULL);
- if (er != erSuccess)
- return er;
- er = CheckUserStore(lpecSession, ulUserid, ECSTORE_TYPE_PRIVATE, &bHasLocalStore);
- if (er != erSuccess)
- return er;
- if (!bHasLocalStore)
- return KCERR_NOT_FOUND;
- // Check permission
- if(lpecSession->GetSecurity()->IsAdminOverUserObject(ulUserid) != erSuccess &&
- (lpecSession->GetSecurity()->GetUserId() != ulUserid))
- return KCERR_NO_ACCESS;
- /* Not all objectclasses support quota */
- if ((sExternId.objclass == NONACTIVE_CONTACT) ||
- (OBJECTCLASS_TYPE(sExternId.objclass) == OBJECTTYPE_DISTLIST) ||
- (sExternId.objclass == CONTAINER_ADDRESSLIST))
- return KCERR_INVALID_TYPE;
- if (OBJECTCLASS_TYPE(sExternId.objclass) == OBJECTTYPE_MAILUSER || sExternId.objclass == CONTAINER_COMPANY) {
- er = lpecSession->GetSecurity()->GetUserSize(ulUserid, &llStoreSize);
- if(er != erSuccess)
- return er;
- } else {
- return KCERR_INVALID_PARAMETER;
- }
- // check the store quota status
- er = lpecSession->GetSecurity()->CheckUserQuota(ulUserid, llStoreSize, &QuotaStatus);
- if(er != erSuccess)
- return er;
- lpsQuotaStatus->llStoreSize = llStoreSize;
- lpsQuotaStatus->ulQuotaStatus = (unsigned int)QuotaStatus;
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(getMessageStatus, lpsStatus->er, entryId sEntryId, unsigned int ulFlags, struct messageStatus* lpsStatus)
- {
- unsigned int ulMsgStatus = 0;
- unsigned int ulId = 0;
- USE_DATABASE();
- er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulId);
- if(er != erSuccess)
- return er;
- //Check security
- er = lpecSession->GetSecurity()->CheckPermission(ulId, ecSecurityRead);
- if(er != erSuccess)
- return er;
- // Get the old flags
- strQuery = "SELECT val_ulong FROM properties WHERE hierarchyid="+stringify(ulId)+" AND tag=3607 AND type=3 LIMIT 2";
- if ((er = lpDatabase->DoSelect(strQuery, &lpDBResult)) != erSuccess) {
- ec_log_err("getMessageStatus(): select failed %x", er);
- return KCERR_DATABASE_ERROR;
- }
- if (lpDatabase->GetNumRows(lpDBResult) == 1) {
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
- if(lpDBRow == NULL || lpDBRow[0] == NULL) {
- ec_log_err("getMessageStatus(): row or col null");
- return KCERR_DATABASE_ERROR;
- }
- ulMsgStatus = atoui(lpDBRow[0]);
- }
- lpsStatus->ulMessageStatus = ulMsgStatus;
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(setMessageStatus, lpsOldStatus->er, entryId sEntryId, unsigned int ulNewStatus, unsigned int ulNewStatusMask, unsigned int ulSyncId, struct messageStatus* lpsOldStatus)
- {
- unsigned int ulOldMsgStatus = 0;
- unsigned int ulNewMsgStatus = 0;
- unsigned int ulRows = 0;
- unsigned int ulId = 0;
- unsigned int ulParent = 0;
- unsigned int ulObjFlags = 0;
-
- SOURCEKEY sSourceKey;
- SOURCEKEY sParentSourceKey;
- USE_DATABASE();
- er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulId);
- if(er != erSuccess)
- goto exit;
- //Check security
- er = lpecSession->GetSecurity()->CheckPermission(ulId, ecSecurityEdit);
- if(er != erSuccess)
- goto exit;
- er = lpDatabase->Begin();
- if(er != erSuccess)
- goto exit;
- er = g_lpSessionManager->GetCacheManager()->GetObject(ulId, &ulParent, NULL, &ulObjFlags);
- if(er != erSuccess)
- goto exit;
- // Get the old flags (PR_MSG_STATUS)
- strQuery = "SELECT val_ulong FROM properties WHERE hierarchyid="+stringify(ulId)+" AND tag=3607 AND type=3 LIMIT 2";
- if((er = lpDatabase->DoSelect(strQuery, &lpDBResult)) != erSuccess) {
- ec_log_err("setMessageStatus(): select failed %x", er);
- er = KCERR_DATABASE_ERROR;
- goto exit;
- }
- ulRows = lpDatabase->GetNumRows(lpDBResult);
- if (ulRows == 1) {
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
- if(lpDBRow == NULL || lpDBRow[0] == NULL) {
- er = KCERR_DATABASE_ERROR;
- ec_log_err("setMessageStatus(): row or col null");
- goto exit;
- }
- ulOldMsgStatus = atoui(lpDBRow[0]);
- }
- // Set the new flags
- ulNewMsgStatus = (ulOldMsgStatus &~ulNewStatusMask) | (ulNewStatusMask & ulNewStatus);
- if(ulRows > 0){
- strQuery = "UPDATE properties SET val_ulong="+stringify(ulNewMsgStatus)+" WHERE hierarchyid="+stringify(ulId)+" AND tag=3607 AND type=3";
- er = lpDatabase->DoUpdate(strQuery);
- }else {
- strQuery = "INSERT INTO properties(hierarchyid, tag, type, val_ulong) VALUES("+stringify(ulId)+", 3607, 3,"+stringify(ulNewMsgStatus)+")";
- er = lpDatabase->DoInsert(strQuery);
- }
- if(er != erSuccess) {
- ec_log_err("setMessageStatus(): query failed");
- er = KCERR_DATABASE_ERROR;
- goto exit;
- }
- lpsOldStatus->ulMessageStatus = ulOldMsgStatus;
- GetSourceKey(ulId, &sSourceKey);
- GetSourceKey(ulParent, &sParentSourceKey);
- AddChange(lpecSession, ulSyncId, sSourceKey, sParentSourceKey, ICS_MESSAGE_CHANGE);
- er = lpDatabase->Commit();
- if(er != erSuccess)
- goto exit;
- // Now, send the notifications
- g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, ulId);
- g_lpSessionManager->NotificationModified(MAPI_MESSAGE, ulId, ulParent);
- g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY, ulObjFlags&MSGFLAG_NOTIFY_FLAGS, ulParent, ulId, MAPI_MESSAGE);
- exit:
- ROLLBACK_ON_ERROR();
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(getChanges, lpsChangesResponse->er, struct xsd__base64Binary sSourceKeyFolder, unsigned int ulSyncId, unsigned int ulChangeId, unsigned int ulChangeType, unsigned int ulFlags, struct restrictTable *lpsRestrict, struct icsChangeResponse* lpsChangesResponse)
- {
- icsChangesArray *lpChanges = NULL;
- SOURCEKEY sSourceKey(sSourceKeyFolder.__size, (char *)sSourceKeyFolder.__ptr);
- er = GetChanges(soap, lpecSession, sSourceKey, ulSyncId, ulChangeId, ulChangeType, ulFlags, lpsRestrict, &lpsChangesResponse->ulMaxChangeId, &lpChanges);
- if(er != erSuccess)
- return er;
- lpsChangesResponse->sChangesArray = *lpChanges;
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(setSyncStatus, lpsResponse->er, struct xsd__base64Binary sSourceKeyFolder, unsigned int ulSyncId, unsigned int ulChangeId, unsigned int ulChangeType, unsigned int ulFlags, struct setSyncStatusResponse *lpsResponse)
- {
- SOURCEKEY sSourceKey(sSourceKeyFolder.__size, (char *)sSourceKeyFolder.__ptr);
- unsigned int ulFolderId = 0, dummy = 0;
- USE_DATABASE();
- er = lpDatabase->Begin();
- if (er != erSuccess)
- goto exit;
- if(sSourceKey.size()) {
- er = g_lpSessionManager->GetCacheManager()->GetObjectFromProp(PROP_ID(PR_SOURCE_KEY), sSourceKey.size(), sSourceKey, &ulFolderId);
- if(er != erSuccess)
- goto exit;
- } else {
- ulFolderId = 0;
- }
-
- if(ulFolderId == 0) {
- if(lpecSession->GetSecurity()->GetAdminLevel() != ADMIN_LEVEL_SYSADMIN)
- er = KCERR_NO_ACCESS;
- }
- // Check security
- else if (ulChangeType == ICS_SYNC_CONTENTS)
- er = lpecSession->GetSecurity()->CheckPermission(ulFolderId, ecSecurityRead);
- else if (ulChangeType == ICS_SYNC_HIERARCHY)
- er = lpecSession->GetSecurity()->CheckPermission(ulFolderId, ecSecurityFolderVisible);
- else
- er = KCERR_INVALID_TYPE;
- if(er != erSuccess)
- goto exit;
- if(ulSyncId == 0){
- // SyncID is 0, which means the client will be requesting an initial sync from this new sync. The change_id will
- // be updated in another call done when the synchronization is complete.
- strQuery = "INSERT INTO syncs (change_id, sourcekey, sync_type, sync_time) VALUES (1, "+lpDatabase->EscapeBinary(sSourceKey, sSourceKey.size())+", '" + stringify(ulChangeType) + "', FROM_UNIXTIME("+stringify(time(NULL))+"))";
- er = lpDatabase->DoInsert(strQuery, &ulSyncId);
- if (er == erSuccess) {
- er = lpDatabase->Commit();
- lpsResponse->ulSyncId = ulSyncId;
- }
- goto exit;
- }
- strQuery = "SELECT sourcekey, change_id, sync_type FROM syncs WHERE id ="+stringify(ulSyncId)+" FOR UPDATE";
- //TODO check existing sync
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if(er != erSuccess)
- goto exit;
- if(lpDatabase->GetNumRows(lpDBResult) == 0){
- er = KCERR_NOT_FOUND;
- goto exit;
- }
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
- lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
- if( lpDBRow == NULL || lpDBRow[0] == NULL || lpDBRow[1] == NULL || lpDBRow[2] == NULL){
- er = KCERR_DATABASE_ERROR; // this should never happen
- ec_log_err("setSyncStatus(): row/col NULL");
- goto exit;
- }
- if(lpDBLen[0] != sSourceKey.size() || memcmp(lpDBRow[0], sSourceKey, sSourceKey.size()) != 0){
- er = KCERR_COLLISION;
- ec_log_err("setSyncStatus(): collision");
- goto exit;
- }
- if((dummy = atoui(lpDBRow[2])) != ulChangeType){
- er = KCERR_COLLISION;
- ec_log_err("SetSyncStatus(): unexpected change type %u/%u", dummy, ulChangeType);
- goto exit;
- }
- strQuery = "UPDATE syncs SET change_id = "+stringify(ulChangeId)+", sync_time = FROM_UNIXTIME("+stringify(time(NULL))+") WHERE id = "+stringify(ulSyncId);
- er = lpDatabase->DoUpdate(strQuery);
- if(er != erSuccess)
- goto exit;
- er = lpDatabase->Commit();
- if (er != erSuccess)
- goto exit;
- lpsResponse->ulSyncId = ulSyncId;
- exit:
- ROLLBACK_ON_ERROR();
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(getEntryIDFromSourceKey, lpsResponse->er, entryId sStoreId, struct xsd__base64Binary folderSourceKey, struct xsd__base64Binary messageSourceKey, struct getEntryIDFromSourceKeyResponse *lpsResponse)
- {
- unsigned int ulObjType = 0;
- unsigned int ulObjId = 0;
- unsigned int ulMessageId = 0;
- unsigned int ulFolderId = 0;
- unsigned int ulParent = 0;
- unsigned int ulStoreId = 0;
- unsigned int ulStoreFound = 0;
- EID eid;
-
- USE_DATABASE();
- er = BeginLockFolders(lpDatabase, SOURCEKEY(folderSourceKey), LOCK_SHARED);
- if(er != erSuccess)
- goto exit;
-
- er = lpecSession->GetObjectFromEntryId(&sStoreId, &ulStoreId);
- if(er != erSuccess)
- goto exit;
- er = g_lpSessionManager->GetCacheManager()->GetObjectFromProp(PROP_ID(PR_SOURCE_KEY), folderSourceKey.__size, folderSourceKey.__ptr, &ulFolderId);
- if(er != erSuccess)
- goto exit;
-
- if(messageSourceKey.__size != 0) {
- er = g_lpSessionManager->GetCacheManager()->GetObjectFromProp(PROP_ID(PR_SOURCE_KEY), messageSourceKey.__size, messageSourceKey.__ptr, &ulMessageId);
- if(er != erSuccess)
- goto exit;
- // Check if given sourcekey is in the given parent sourcekey
- er = g_lpSessionManager->GetCacheManager()->GetParent(ulMessageId, &ulParent);
- if(er != erSuccess || ulFolderId != ulParent) {
- er = KCERR_NOT_FOUND;
- goto exit;
- }
-
-
- ulObjId = ulMessageId;
- ulObjType = MAPI_MESSAGE;
- } else {
- ulObjId = ulFolderId;
- ulObjType = MAPI_FOLDER;
- }
- // Check if the folder given is actually in the store we're working on (may not be so if cache
- // is out-of-date during a re-import of a store that has been deleted and re-imported). In this case
- // we return NOT FOUND, which really is true since we cannot found the given sourcekey in this store.
- er = g_lpSessionManager->GetCacheManager()->GetStore(ulFolderId, &ulStoreFound, NULL);
- if(er != erSuccess || ulStoreFound != ulStoreId) {
- er = KCERR_NOT_FOUND;
- goto exit;
- }
- // Check security
- if (ulObjType == MAPI_FOLDER)
- er = lpecSession->GetSecurity()->CheckPermission(ulObjId, ecSecurityFolderVisible);
- else
- er = lpecSession->GetSecurity()->CheckPermission(ulObjId, ecSecurityRead);
- if(er != erSuccess)
- goto exit;
- er = g_lpSessionManager->GetCacheManager()->GetEntryIdFromObject(ulObjId, soap, 0, &lpsResponse->sEntryId);
- if(er != erSuccess)
- goto exit;
- exit:
- lpDatabase->Commit();
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(getSyncStates, lpsResponse->er, struct mv_long ulaSyncId, struct getSyncStatesReponse *lpsResponse)
- {
- er = GetSyncStates(soap, lpecSession, ulaSyncId, &lpsResponse->sSyncStates);
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(getLicenseAuth, lpsResponse->er, struct xsd__base64Binary sAuthData, struct getLicenseAuthResponse *lpsResponse)
- {
- void *data = NULL;
- er = ECLicenseClient().Auth(sAuthData.__ptr, sAuthData.__size, &data, reinterpret_cast<unsigned int *>(&lpsResponse->sAuthResponse.__size));
- if (er != erSuccess)
- goto exit;
- lpsResponse->sAuthResponse.__ptr = s_alloc<unsigned char>(soap, lpsResponse->sAuthResponse.__size);
- memcpy(lpsResponse->sAuthResponse.__ptr, data, lpsResponse->sAuthResponse.__size);
- exit:
- free(data);
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(getLicenseCapa, lpsResponse->er, unsigned int ulServiceType, struct getLicenseCapaResponse *lpsResponse)
- {
- std::vector<std::string> lstCapabilities;
- er = ECLicenseClient().GetCapabilities(ulServiceType, lstCapabilities);
- if(er != erSuccess)
- return er;
-
- lpsResponse->sCapabilities.__size = lstCapabilities.size();
- lpsResponse->sCapabilities.__ptr = s_alloc<char *>(soap, lstCapabilities.size());
-
- for (unsigned int i = 0; i < lstCapabilities.size(); ++i)
- lpsResponse->sCapabilities.__ptr[i] = s_strcpy(soap, lstCapabilities[i].c_str());
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(getLicenseUsers, lpsResponse->er, unsigned int ulServiceType, struct getLicenseUsersResponse *lpsResponse)
- {
- unsigned int ulUsers = 0;
- std::vector<std::string> lstCapabilities;
- er = ECLicenseClient().GetInfo(ulServiceType, &ulUsers);
- if(er != erSuccess)
- return er;
- lpsResponse->ulUsers = ulUsers;
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(resolvePseudoUrl, lpsResponse->er, char *lpszPseudoUrl, struct resolvePseudoUrlResponse* lpsResponse)
- {
- std::string strServerPath;
- if (!lpecSession->GetSessionManager()->IsDistributedSupported())
- {
- /**
- * Non distributed environments do issue pseudo url's, but merely to
- * make upgrading later possible. We'll just return the passed pseudo url
- * and say that we're the peer. That would cause the client to keep on
- * using the current connection.
- **/
- lpsResponse->lpszServerPath = lpszPseudoUrl;
- lpsResponse->bIsPeer = true;
- return erSuccess;
- }
- lpszPseudoUrl = STRIN_FIX(lpszPseudoUrl);
- if (strncmp(lpszPseudoUrl, "pseudo://", 9))
- return KCERR_INVALID_PARAMETER;
- er = GetBestServerPath(soap, lpecSession, lpszPseudoUrl + 9, &strServerPath);
- if (er != erSuccess)
- return er;
- lpsResponse->lpszServerPath = STROUT_FIX_CPY(strServerPath.c_str());
- lpsResponse->bIsPeer = strcasecmp(g_lpSessionManager->GetConfig()->GetSetting("server_name"), lpszPseudoUrl + 9) == 0;
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(getServerDetails, lpsResponse->er, struct mv_string8 szaSvrNameList, unsigned int ulFlags, struct getServerDetailsResponse* lpsResponse)
- {
- serverdetails_t sDetails;
- std::string strServerPath;
- std::string strPublicServer;
- objectdetails_t details;
- if (!lpecSession->GetSessionManager()->IsDistributedSupported())
- /**
- * We want to pretend the method doesn't exist. The best way to do that is to return
- * SOAP_NO_METHOD. But that doesn't fit well in the macro famework. And since the
- * client would translate that to a KCERR_NETWORK_ERROR anyway, we'll just return
- * that instead.
- **/
- return KCERR_NETWORK_ERROR;
- // lookup server which contains the public
- if (lpecSession->GetUserManagement()->GetPublicStoreDetails(&details) == erSuccess)
- strPublicServer = details.GetPropString(OB_PROP_S_SERVERNAME);
- if (ulFlags & ~(EC_SERVERDETAIL_NO_NAME | EC_SERVERDETAIL_FILEPATH |
- EC_SERVERDETAIL_HTTPPATH | EC_SERVERDETAIL_SSLPATH |
- EC_SERVERDETAIL_PREFEREDPATH))
- return KCERR_UNKNOWN_FLAGS;
- if (ulFlags == 0)
- ulFlags = EC_SERVERDETAIL_FILEPATH|EC_SERVERDETAIL_HTTPPATH|EC_SERVERDETAIL_SSLPATH |EC_SERVERDETAIL_PREFEREDPATH;
-
- if (szaSvrNameList.__size > 0 && szaSvrNameList.__ptr != NULL) {
- lpsResponse->sServerList.__size = szaSvrNameList.__size;
- lpsResponse->sServerList.__ptr = s_alloc<struct server>(soap, szaSvrNameList.__size);
- memset(lpsResponse->sServerList.__ptr, 0, szaSvrNameList.__size * sizeof *lpsResponse->sServerList.__ptr);
-
- for (gsoap_size_t i = 0; i < szaSvrNameList.__size; ++i) {
- er = lpecSession->GetUserManagement()->GetServerDetails(STRIN_FIX(szaSvrNameList.__ptr[i]), &sDetails);
- if (er != erSuccess)
- return er;
- if (strcasecmp(sDetails.GetServerName().c_str(), g_lpSessionManager->GetConfig()->GetSetting("server_name")) == 0)
- lpsResponse->sServerList.__ptr[i].ulFlags |= EC_SDFLAG_IS_PEER;
- // note: "contains a public of a company" is also a possibility
- if (!strPublicServer.empty() && strcasecmp(sDetails.GetServerName().c_str(), strPublicServer.c_str()) == 0)
- lpsResponse->sServerList.__ptr[i].ulFlags |= EC_SDFLAG_HAS_PUBLIC;
- if (!(ulFlags & EC_SERVERDETAIL_NO_NAME))
- lpsResponse->sServerList.__ptr[i].lpszName = STROUT_FIX_CPY(sDetails.GetServerName().c_str());
- if (ulFlags & EC_SERVERDETAIL_FILEPATH)
- lpsResponse->sServerList.__ptr[i].lpszFilePath = STROUT_FIX_CPY(sDetails.GetFilePath().c_str());
- if (ulFlags & EC_SERVERDETAIL_HTTPPATH)
- lpsResponse->sServerList.__ptr[i].lpszHttpPath = STROUT_FIX_CPY(sDetails.GetHttpPath().c_str());
- if (ulFlags & EC_SERVERDETAIL_SSLPATH)
- lpsResponse->sServerList.__ptr[i].lpszSslPath = STROUT_FIX_CPY(sDetails.GetSslPath().c_str());
- if (ulFlags & EC_SERVERDETAIL_PREFEREDPATH &&
- GetBestServerPath(soap, lpecSession, sDetails.GetServerName(), &strServerPath) == erSuccess)
- lpsResponse->sServerList.__ptr[i].lpszPreferedPath = STROUT_FIX_CPY(strServerPath.c_str());
- }
- }
- return erSuccess;
- }
- SOAP_ENTRY_END()
- // legacy calls required for 6.30 clients
- SOAP_ENTRY_START(getServerBehavior, lpsResponse->er, struct getServerBehaviorResponse* lpsResponse)
- {
- lpsResponse->ulBehavior = 1;
- }
- SOAP_ENTRY_END()
-
- SOAP_ENTRY_START(setServerBehavior, *result, unsigned int ulBehavior, unsigned int *result)
- {
- er = KCERR_NO_SUPPORT;
- }
- SOAP_ENTRY_END()
- typedef ECDeferredFunc<ECRESULT, ECRESULT(*)(void*), void*> task_type;
- struct MTOMStreamInfo;
- struct MTOMSessionInfo {
- ECSession *lpecSession;
- ECDatabase *lpSharedDatabase;
- ECDatabase *lpDatabase;
- ECAttachmentStorage *lpAttachmentStorage;
- ECRESULT er;
- ECThreadPool *lpThreadPool;
- MTOMStreamInfo *lpCurrentWriteStream; /* This is only tracked for cleanup at session exit */
- MTOMStreamInfo *lpCurrentReadStream; /* This is only tracked for cleanup at session exit */
- };
- struct MTOMStreamInfo {
- ECFifoBuffer *lpFifoBuffer;
- unsigned int ulObjectId;
- unsigned int ulStoreId;
- bool bNewItem;
- unsigned long long ullIMAP;
- GUID sGuid;
- ULONG ulFlags;
- task_type *lpTask;
- struct propValArray *lpPropValArray;
- MTOMSessionInfo *lpSessionInfo;
- };
- typedef MTOMStreamInfo * LPMTOMStreamInfo;
- static ECRESULT SerializeObject(void *arg)
- {
- ECRESULT er = erSuccess;
- LPMTOMStreamInfo lpStreamInfo = NULL;
- ECSerializer *lpSink = NULL;
- lpStreamInfo = (LPMTOMStreamInfo)arg;
- assert(lpStreamInfo != NULL);
- lpStreamInfo->lpSessionInfo->lpSharedDatabase->ThreadInit();
- lpSink = new ECFifoSerializer(lpStreamInfo->lpFifoBuffer, ECFifoSerializer::serialize);
- er = SerializeMessage(lpStreamInfo->lpSessionInfo->lpecSession, lpStreamInfo->lpSessionInfo->lpSharedDatabase, lpStreamInfo->lpSessionInfo->lpAttachmentStorage, NULL, lpStreamInfo->ulObjectId, MAPI_MESSAGE, lpStreamInfo->ulStoreId, &lpStreamInfo->sGuid, lpStreamInfo->ulFlags, lpSink, true);
- delete lpSink;
- lpStreamInfo->lpSessionInfo->lpSharedDatabase->ThreadEnd();
- lpStreamInfo->lpSessionInfo->er = er;
- return er;
- }
- static void *MTOMReadOpen(struct soap *soap, void *handle, const char *id,
- const char* /*type*/, const char* /*options*/)
- {
- LPMTOMStreamInfo lpStreamInfo = NULL;
- lpStreamInfo = (LPMTOMStreamInfo)handle;
- assert(lpStreamInfo != NULL);
- if (lpStreamInfo->lpSessionInfo->er != erSuccess) {
- soap->error = SOAP_FATAL_ERROR;
- return NULL;
- }
- lpStreamInfo->lpFifoBuffer = new ECFifoBuffer();
- if (strncmp(id, "emcas-", 6) == 0) {
- std::unique_ptr<task_type> ptrTask(new task_type(SerializeObject, lpStreamInfo));
- if (ptrTask->dispatchOn(lpStreamInfo->lpSessionInfo->lpThreadPool) == false) {
- ec_log_err("Failed to dispatch serialization task for \"%s\"", id);
- soap->error = SOAP_FATAL_ERROR;
- delete lpStreamInfo->lpFifoBuffer;
- lpStreamInfo->lpFifoBuffer = NULL;
- return NULL;
- }
- lpStreamInfo->lpTask = ptrTask.release();
- } else {
- ec_log_err("Got stream request for unknown ID \"%s\"", id);
- soap->error = SOAP_FATAL_ERROR;
- delete lpStreamInfo->lpFifoBuffer;
- lpStreamInfo->lpFifoBuffer = NULL;
- return NULL;
- }
-
- lpStreamInfo->lpSessionInfo->lpCurrentReadStream = lpStreamInfo; // Track currently opened stream info
-
- return (void *)lpStreamInfo;
- }
- static size_t MTOMRead(struct soap * /*soap*/, void *handle,
- char *buf, size_t len)
- {
- ECRESULT er = erSuccess;
- LPMTOMStreamInfo lpStreamInfo = (LPMTOMStreamInfo)handle;
- ECFifoBuffer::size_type cbRead = 0;
-
- assert(lpStreamInfo->lpFifoBuffer != NULL);
- er = lpStreamInfo->lpFifoBuffer->Read(buf, len, STR_DEF_TIMEOUT, &cbRead);
- if (er != erSuccess)
- ec_log_err("Failed to read data. er=%s", stringify(er).c_str());
- return cbRead;
- }
- static void MTOMReadClose(struct soap *soap, void *handle)
- {
- LPMTOMStreamInfo lpStreamInfo = (LPMTOMStreamInfo)handle;
-
- assert(lpStreamInfo->lpFifoBuffer != NULL);
- lpStreamInfo->lpSessionInfo->lpCurrentReadStream = NULL; // Cleanup done
- // We get here when the last call to MTOMRead returned 0 OR when
- // an error occurred within gSOAP's bowels. In the latter case, we need
- // to close the FIFO to make sure the writing thread will not lock up.
- // Since gSOAP will not be reading from the FIFO in any case once we
- // read this point, it is safe to just close the FIFO.
- lpStreamInfo->lpFifoBuffer->Close(ECFifoBuffer::cfRead);
- if (lpStreamInfo->lpTask) {
- lpStreamInfo->lpTask->wait(); // Todo: use result() to wait and get result
- delete lpStreamInfo->lpTask;
- }
- delete lpStreamInfo->lpFifoBuffer;
- lpStreamInfo->lpFifoBuffer = NULL;
- }
- static void MTOMWriteClose(struct soap *soap, void *handle);
- static void MTOMSessionDone(struct soap *soap, void *param)
- {
- auto lpInfo = static_cast<MTOMSessionInfo *>(param);
- if (lpInfo->lpCurrentWriteStream != NULL)
- // Apparently a write stream was opened but not closed by gSOAP by calling MTOMWriteClose. Do it now.
- MTOMWriteClose(soap, lpInfo->lpCurrentWriteStream);
- else if (lpInfo->lpCurrentReadStream != NULL)
- // Same but for MTOMReadClose()
- MTOMReadClose(soap, lpInfo->lpCurrentReadStream);
- // We can now safely remove sessions, etc since nobody is using them.
- lpInfo->lpAttachmentStorage->Release();
- lpInfo->lpecSession->Unlock();
-
- delete lpInfo->lpSharedDatabase;
- delete lpInfo->lpThreadPool;
- delete lpInfo;
- }
- SOAP_ENTRY_START(exportMessageChangesAsStream, lpsResponse->er, unsigned int ulFlags, struct propTagArray sPropTags, struct sourceKeyPairArray sSourceKeyPairs, unsigned int ulPropTag, exportMessageChangesAsStreamResponse *lpsResponse)
- {
- LPMTOMStreamInfo lpStreamInfo = NULL;
- object_ptr<ECAttachmentStorage> lpAttachmentStorage;
- unsigned int ulObjectId = 0;
- unsigned int ulParentId = 0;
- unsigned int ulParentCheck = 0;
- unsigned int ulObjFlags = 0;
- unsigned int ulStoreId = 0;
- unsigned long ulObjCnt = 0;
- GUID sGuid;
- ECObjectTableList rows;
- struct rowSet *lpRowSet = NULL; // Do not free, used in response data
- ECODStore ecODStore;
- ECDatabase *lpBatchDB;
- unsigned int ulDepth = 20;
- unsigned int ulMode = 0;
- MTOMSessionInfo *lpMTOMSessionInfo = NULL;
- bool bUseSQLMulti = parseBool(g_lpSessionManager->GetConfig()->GetSetting("enable_sql_procedures"));
- // Backward compat, old clients do not send ulPropTag
- if(!ulPropTag)
- ulPropTag = PR_SOURCE_KEY;
-
- if(ulFlags & SYNC_BEST_BODY)
- ulMode = 1;
- else if(ulFlags & SYNC_LIMITED_IMESSAGE)
- ulMode = 2;
-
- USE_DATABASE();
- if(ulPropTag == PR_ENTRYID) {
- std::set<EntryId> setEntryIDs;
- for (gsoap_size_t i = 0; i < sSourceKeyPairs.__size; ++i)
- setEntryIDs.insert(EntryId(sSourceKeyPairs.__ptr[i].sObjectKey));
- er = BeginLockFolders(lpDatabase, setEntryIDs, LOCK_SHARED);
- } else if (ulPropTag == PR_SOURCE_KEY) {
- std::set<SOURCEKEY> setParentSourcekeys;
- for (gsoap_size_t i = 0; i < sSourceKeyPairs.__size; ++i)
- setParentSourcekeys.insert(SOURCEKEY(sSourceKeyPairs.__ptr[i].sParentKey));
- er = BeginLockFolders(lpDatabase, setParentSourcekeys, LOCK_SHARED);
- } else
- er = KCERR_INVALID_PARAMETER;
-
- if (er == KCERR_NOT_FOUND) {
- // BeginLockFolders returns KCERR_NOT_FOUND when there's no
- // folder to lock, which can only happen if none of the passed
- // objects exist.
- // This is not an error as that's perfectly valid when performing
- // a selective export. So we'll just return an empty batch, which
- // will be interpreted by the caller as 'all message are deleted'.
- er = erSuccess;
- goto exit;
- } else if (er != erSuccess)
- goto exit;
- ulDepth = atoui(lpecSession->GetSessionManager()->GetConfig()->GetSetting("embedded_attachment_limit"));
-
- er = lpecSession->GetAdditionalDatabase(&lpBatchDB);
- if (er != erSuccess)
- goto exit;
-
- if ((lpecSession->GetCapabilities() & KOPANO_CAP_ENHANCED_ICS) == 0) {
- er = KCERR_NO_SUPPORT;
- goto exit;
- }
- er = CreateAttachmentStorage(lpDatabase, &~lpAttachmentStorage);
- if (er != erSuccess)
- goto exit;
- lpMTOMSessionInfo = new MTOMSessionInfo;
- lpMTOMSessionInfo->lpCurrentWriteStream = NULL;
- lpMTOMSessionInfo->lpCurrentReadStream = NULL;
- lpMTOMSessionInfo->lpAttachmentStorage = lpAttachmentStorage;
- lpMTOMSessionInfo->lpAttachmentStorage->AddRef();
- lpMTOMSessionInfo->lpecSession = lpecSession; // Should be unlocked after MTOM is done
- lpMTOMSessionInfo->lpecSession->Lock();
- lpMTOMSessionInfo->lpSharedDatabase = lpBatchDB;
- lpMTOMSessionInfo->er = erSuccess;
- lpMTOMSessionInfo->lpThreadPool = new ECThreadPool(1);
- soap_info(soap)->fdone = MTOMSessionDone;
- soap_info(soap)->fdoneparam = lpMTOMSessionInfo;
- lpsResponse->sMsgStreams.__ptr = s_alloc<messageStream>(soap, sSourceKeyPairs.__size);
- for (gsoap_size_t i = 0; i < sSourceKeyPairs.__size; ++i) {
- // Progress information
- lpsResponse->sMsgStreams.__ptr[ulObjCnt].ulStep = i;
- // Find the correct object
- er = g_lpSessionManager->GetCacheManager()->GetObjectFromProp(PROP_ID(ulPropTag),
- sSourceKeyPairs.__ptr[i].sObjectKey.__size,
- sSourceKeyPairs.__ptr[i].sObjectKey.__ptr, &ulObjectId);
- if(er != erSuccess) {
- er = erSuccess;
- goto next_object;
- }
-
- if(sSourceKeyPairs.__ptr[i].sParentKey.__size) {
- er = g_lpSessionManager->GetCacheManager()->GetObjectFromProp(PROP_ID(ulPropTag),
- sSourceKeyPairs.__ptr[i].sParentKey.__size,
- sSourceKeyPairs.__ptr[i].sParentKey.__ptr, &ulParentId);
- if(er != erSuccess) {
- er = erSuccess;
- goto next_object;
- }
- er = g_lpSessionManager->GetCacheManager()->GetObject(ulObjectId, &ulParentCheck, NULL, &ulObjFlags, NULL);
- if (er != erSuccess) {
- er = erSuccess;
- goto next_object;
- }
-
- if (ulParentId != ulParentCheck) {
- assert(false);
- goto next_object;
- }
- }
-
- if ((ulObjFlags & MSGFLAG_DELETED) != (ulFlags & MSGFLAG_DELETED))
- goto next_object;
-
- // Check security
- er = lpecSession->GetSecurity()->CheckPermission(ulObjectId, ecSecurityRead);
- if (er != erSuccess) {
- er = erSuccess;
- goto next_object;
- }
-
- // Get store
- er = g_lpSessionManager->GetCacheManager()->GetStore(ulObjectId, &ulStoreId, &sGuid);
- if(er != erSuccess) {
- er = erSuccess;
- goto next_object;
- }
-
- lpStreamInfo = (MTOMStreamInfo *)soap_malloc(soap, sizeof(MTOMStreamInfo));
- lpStreamInfo->ulObjectId = ulObjectId;
- lpStreamInfo->ulStoreId = ulStoreId;
- lpStreamInfo->bNewItem = false;
- lpStreamInfo->ullIMAP = 0;
- lpStreamInfo->sGuid = sGuid;
- lpStreamInfo->ulFlags = ulFlags;
- lpStreamInfo->lpPropValArray = NULL;
- lpStreamInfo->lpTask = NULL;
- lpStreamInfo->lpSessionInfo = lpMTOMSessionInfo;
- if(bUseSQLMulti)
- strQuery += "call StreamObj(" + stringify(ulObjectId) + "," + stringify(ulDepth) + ", " + stringify(ulMode) + ");";
-
- // Setup the MTOM Attachments
- memset(&lpsResponse->sMsgStreams.__ptr[ulObjCnt].sStreamData, 0, sizeof(lpsResponse->sMsgStreams.__ptr[ulObjCnt].sStreamData));
- lpsResponse->sMsgStreams.__ptr[ulObjCnt].sStreamData.xop__Include.__ptr = (unsigned char*)lpStreamInfo;
- lpsResponse->sMsgStreams.__ptr[ulObjCnt].sStreamData.xop__Include.type = s_strcpy(soap, "application/binary");
- lpsResponse->sMsgStreams.__ptr[ulObjCnt].sStreamData.xop__Include.id = s_strcpy(soap, string("emcas-" + stringify(ulObjCnt, false)).c_str());
- ++ulObjCnt;
- // Remember the object ID since we need it later
- rows.push_back(sObjectTableKey(ulObjectId, 0));
- next_object:
- ;
- }
- lpsResponse->sMsgStreams.__size = ulObjCnt;
-
- // The results of this query will be consumed by the MTOMRead function
- if(!strQuery.empty()) {
- er = lpBatchDB->DoSelectMulti(strQuery);
- if(er != erSuccess)
- goto exit;
- }
-
- memset(&ecODStore, 0, sizeof(ECODStore));
- ecODStore.ulObjType = MAPI_MESSAGE;
-
- // Get requested properties for all rows
- er = ECStoreObjectTable::QueryRowData(NULL, soap, lpecSession, &rows, &sPropTags, &ecODStore, &lpRowSet, true, true);
- if (er != erSuccess)
- goto exit;
-
- assert(lpRowSet->__size == static_cast<gsoap_size_t>(ulObjCnt));
- for (gsoap_size_t i = 0; i < lpRowSet->__size; ++i)
- lpsResponse->sMsgStreams.__ptr[i].sPropVals = lpRowSet->__ptr[i];
- soap->fmimereadopen = &MTOMReadOpen;
- soap->fmimeread = &MTOMRead;
- soap->fmimereadclose = &MTOMReadClose;
- g_lpStatsCollector->Increment(SCN_DATABASE_MROPS, (int)ulObjCnt);
-
- exit:
- lpDatabase->Commit();
- if (er != erSuccess)
- // Do not output any streams
- lpsResponse->sMsgStreams.__size = 0;
- soap->mode &= ~SOAP_XML_TREE;
- soap->omode &= ~SOAP_XML_TREE;
- }
- SOAP_ENTRY_END()
- static ECRESULT DeserializeObject(void *arg)
- {
- LPMTOMStreamInfo lpStreamInfo = NULL;
- ECSerializer *lpSource = NULL;
- ECRESULT er = erSuccess;
- lpStreamInfo = (LPMTOMStreamInfo)arg;
- assert(lpStreamInfo != NULL);
- lpSource = new ECFifoSerializer(lpStreamInfo->lpFifoBuffer, ECFifoSerializer::deserialize);
- er = DeserializeObject(lpStreamInfo->lpSessionInfo->lpecSession, lpStreamInfo->lpSessionInfo->lpDatabase, lpStreamInfo->lpSessionInfo->lpAttachmentStorage, NULL, lpStreamInfo->ulObjectId, lpStreamInfo->ulStoreId, &lpStreamInfo->sGuid, lpStreamInfo->bNewItem, lpStreamInfo->ullIMAP, lpSource, &lpStreamInfo->lpPropValArray);
- delete lpSource;
- return er;
- }
- static void *MTOMWriteOpen(struct soap *soap, void *handle,
- const char * /*id*/, const char * /*type*/, const char * /*description*/,
- enum soap_mime_encoding /*encoding*/)
- {
- LPMTOMStreamInfo lpStreamInfo = NULL;
- lpStreamInfo = (LPMTOMStreamInfo)handle;
- // Just return the handle (needed for gsoap to operate properly
- lpStreamInfo->lpFifoBuffer = new ECFifoBuffer();
- std::unique_ptr<task_type> ptrTask(new task_type(DeserializeObject, lpStreamInfo));
- if (ptrTask->dispatchOn(lpStreamInfo->lpSessionInfo->lpThreadPool) == false) {
- ec_log_err("Failed to dispatch deserialization task");
- lpStreamInfo->lpSessionInfo->er = KCERR_UNABLE_TO_COMPLETE;
- soap->error = SOAP_FATAL_ERROR;
- return NULL;
- }
- lpStreamInfo->lpTask = ptrTask.release();
- lpStreamInfo->lpSessionInfo->lpCurrentWriteStream = lpStreamInfo; // Remember that MTOMWriteOpen was called, and that a cleanup is needed
- return handle;
- }
- static int MTOMWrite(struct soap *soap, void *handle,
- const char *buf, size_t len)
- {
- ECRESULT er = erSuccess;
- LPMTOMStreamInfo lpStreamInfo = NULL;
-
- lpStreamInfo = (LPMTOMStreamInfo)handle;
- assert(lpStreamInfo != NULL);
- // Only write data if a reader thread is available
- if (lpStreamInfo->lpTask) {
- er = lpStreamInfo->lpFifoBuffer->Write(buf, len, STR_DEF_TIMEOUT, NULL);
- if (er != erSuccess) {
- lpStreamInfo->lpSessionInfo->er = er;
- return SOAP_EOF;
- }
- }
- return SOAP_OK;
- }
- static void MTOMWriteClose(struct soap *soap, void *handle)
- {
- ECRESULT er = erSuccess;
- LPMTOMStreamInfo lpStreamInfo = NULL;
- lpStreamInfo = (LPMTOMStreamInfo)handle;
- assert(lpStreamInfo != NULL);
- lpStreamInfo->lpSessionInfo->lpCurrentWriteStream = NULL; // Since we are cleaning up ourselves, another cleanup is not necessary
- lpStreamInfo->lpFifoBuffer->Close(ECFifoBuffer::cfWrite);
- if (lpStreamInfo->lpTask) {
- lpStreamInfo->lpTask->wait(); // Todo: use result() to wait and get result
- er = lpStreamInfo->lpTask->result();
- delete lpStreamInfo->lpTask;
- }
- delete lpStreamInfo->lpFifoBuffer;
- lpStreamInfo->lpFifoBuffer = NULL;
- // Signal error to caller
- if(er != erSuccess) {
- lpStreamInfo->lpSessionInfo->er = er;
- soap->error = SOAP_FATAL_ERROR;
- }
- }
- SOAP_ENTRY_START(importMessageFromStream, *result, unsigned int ulFlags, unsigned int ulSyncId, entryId sFolderEntryId, entryId sEntryId, bool bIsNew, struct propVal *lpsConflictItems, struct xsd__Binary sStreamData, unsigned int *result)
- {
- MTOMStreamInfo *lpsStreamInfo = NULL;
- unsigned int ulObjectId = 0;
- unsigned int ulParentId = 0;
- unsigned int ulParentType = 0;
- unsigned int ulGrandParentId = 0;
- unsigned int ulStoreId = 0;
- unsigned int ulOwner = 0;
- unsigned long long ullIMAP = 0;
- GUID sGuid = {0};
- SOURCEKEY sSourceKey;
- SOURCEKEY sParentSourceKey;
- ECListInt lObjectList;
- gsoap_size_t nMVItems = 0;
- std::string strColName;
- std::string strColData;
- unsigned int ulAffected = 0;
- unsigned int ulDeleteFlags = EC_DELETE_ATTACHMENTS | EC_DELETE_RECIPIENTS | EC_DELETE_CONTAINER | EC_DELETE_MESSAGES | EC_DELETE_HARD_DELETE;
- ECListDeleteItems lstDeleteItems;
- ECListDeleteItems lstDeleted;
- object_ptr<ECAttachmentStorage> lpAttachmentStorage;
- MTOMSessionInfo *lpMTOMSessionInfo = NULL;
- USE_DATABASE();
- er = CreateAttachmentStorage(lpDatabase, &~lpAttachmentStorage);
- if (er != erSuccess)
- goto exit;
- lpMTOMSessionInfo = new MTOMSessionInfo;
- lpMTOMSessionInfo->lpCurrentWriteStream = NULL;
- lpMTOMSessionInfo->lpCurrentReadStream = NULL;
- lpMTOMSessionInfo->lpAttachmentStorage = lpAttachmentStorage;
- lpMTOMSessionInfo->lpAttachmentStorage->AddRef();
- lpMTOMSessionInfo->lpecSession = lpecSession; // Should be unlocked after MTOM is done
- lpMTOMSessionInfo->lpecSession->Lock();
- lpMTOMSessionInfo->lpDatabase = lpDatabase;
- lpMTOMSessionInfo->lpSharedDatabase = NULL;
- lpMTOMSessionInfo->er = erSuccess;
- lpMTOMSessionInfo->lpThreadPool = new ECThreadPool(1);
- soap_info(soap)->fdone = MTOMSessionDone;
- soap_info(soap)->fdoneparam = lpMTOMSessionInfo;
- er = lpAttachmentStorage->Begin();
- if (er != erSuccess)
- goto exit;
- er = lpDatabase->Begin();
- if (er != erSuccess)
- goto exit;
- // Get the parent object id.
- er = lpecSession->GetObjectFromEntryId(&sFolderEntryId, &ulParentId);
- if (er != erSuccess)
- goto exit;
-
- // Lock the parent folder
- strQuery = "SELECT val_ulong FROM properties WHERE hierarchyid = " + stringify(ulParentId) + " FOR UPDATE";
- er = lpDatabase->DoSelect(strQuery, NULL);
- if (er != erSuccess)
- goto exit;
- if (!bIsNew) {
- // Delete the existing message and recreate it
- er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulObjectId);
- if (er != erSuccess)
- goto exit;
- // When a message is update the flags are not passed. So obtain the old flags before
- // deleting so we can pass them to CreateObject.
- er = g_lpSessionManager->GetCacheManager()->GetObjectFlags(ulObjectId, &ulFlags);
- if (er != erSuccess)
- goto exit;
- // Get the original IMAP ID
- strQuery = "SELECT val_ulong FROM properties WHERE"
- " hierarchyid=" + stringify(ulObjectId) +
- " and tag=" + stringify(PROP_ID(PR_EC_IMAP_ID)) +
- " and type=" + stringify(PROP_TYPE(PR_EC_IMAP_ID)) +
- " LIMIT 1";
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if (er != erSuccess)
- goto exit;
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
- if (lpDBRow == NULL || lpDBRow[0] == NULL)
- // Items created in previous versions of Kopano will not have a PR_EC_IMAP_ID. The rule
- // is that an item has a PR_EC_IMAP_ID that is equal to the hierarchyid in that case.
- ullIMAP = ulObjectId;
- else
- // atoui return a unsigned int at best, but since PR_EC_IMAP_ID is a PT_LONG, the same conversion
- // will be done when getting the property through MAPI.
- ullIMAP = atoui(lpDBRow[0]);
-
- lObjectList.push_back(ulObjectId);
- // Collect recursive parent objects, validate item and check the permissions
- er = ExpandDeletedItems(lpecSession, lpDatabase, &lObjectList, ulDeleteFlags, true, &lstDeleteItems);
- if (er != erSuccess) {
- assert(false);
- goto exit;
- }
- // Delete the items hard
- er = DeleteObjectHard(lpecSession, lpDatabase, lpAttachmentStorage, ulDeleteFlags, lstDeleteItems, true, lstDeleted);
- if (er != erSuccess) {
- assert(false);
- goto exit;
- }
- // Update storesize
- er = DeleteObjectStoreSize(lpecSession, lpDatabase, ulDeleteFlags, lstDeleted);
- if (er != erSuccess) {
- assert(false);
- goto exit;
- }
- // Update cache
- er = DeleteObjectCacheUpdate(lpecSession, ulDeleteFlags, lstDeleted);
- if (er != erSuccess) {
- assert(false);
- goto exit;
- }
- }
- // Create the message
- if (bIsNew) {
- er = CreateObject(lpecSession, lpDatabase, ulParentId, MAPI_FOLDER, MAPI_MESSAGE, ulFlags, &ulObjectId);
- if (er != erSuccess)
- goto exit;
- } else {
- ulOwner = lpecSession->GetSecurity()->GetUserId(ulParentId); // Owner of object is either the current user or the owner of the folder
- // Reinsert the entry in the hierarchy table with the same id so the change notification later doesn't
- // become a add notification because the id is different.
- strQuery = "INSERT INTO hierarchy (id, parent, type, flags, owner) values("+stringify(ulObjectId)+", "+stringify(ulParentId)+", "+stringify(MAPI_MESSAGE)+", "+stringify(ulFlags)+", "+stringify(ulOwner)+")";
- er = lpDatabase->DoInsert(strQuery);
- if(er != erSuccess)
- goto exit;
- }
- // Get store
- er = g_lpSessionManager->GetCacheManager()->GetStore(ulObjectId, &ulStoreId, &sGuid);
- if(er != erSuccess)
- goto exit;
- // Quota check
- er = CheckQuota(lpecSession, ulStoreId);
- if (er != erSuccess)
- goto exit;
- // Map entryId <-> ulObjectId
- er = MapEntryIdToObjectId(lpecSession, lpDatabase, ulObjectId, sEntryId);
- if (er != erSuccess)
- goto exit;
- // Deserialize the streamed message
- soap->fmimewriteopen = &MTOMWriteOpen;
- soap->fmimewrite = &MTOMWrite;
- soap->fmimewriteclose= &MTOMWriteClose;
- // We usually do not pass database objects to other threads. However, since
- // we want to be able to perform a complete rollback we need to pass it
- // to thread that processes the data and puts it in the database.
- lpsStreamInfo = (MTOMStreamInfo *)soap_malloc(soap, sizeof(MTOMStreamInfo));
- lpsStreamInfo->ulObjectId = ulObjectId;
- lpsStreamInfo->ulStoreId = ulStoreId;
- lpsStreamInfo->bNewItem = bIsNew;
- lpsStreamInfo->ullIMAP = ullIMAP;
- lpsStreamInfo->sGuid = sGuid;
- lpsStreamInfo->ulFlags = ulFlags;
- lpsStreamInfo->lpPropValArray = NULL;
- lpsStreamInfo->lpTask = NULL;
- lpsStreamInfo->lpSessionInfo = lpMTOMSessionInfo;
- if (soap_check_mime_attachments(soap)) {
- struct soap_multipart *content;
-
- content = soap_get_mime_attachment(soap, (void*)lpsStreamInfo);
- if (!content) {
- er = (lpMTOMSessionInfo->er ? lpMTOMSessionInfo->er : KCERR_CALL_FAILED);
- goto exit;
- }
-
- // Flush remaining attachments (that shouldn't even be there)
- while (true) {
- content = soap_get_mime_attachment(soap, (void*)lpsStreamInfo);
- if (!content)
- break;
- };
- }
- er = g_lpSessionManager->GetCacheManager()->GetObject(ulParentId, &ulGrandParentId, NULL, NULL, &ulParentType);
- if (er != erSuccess)
- goto exit;
- // pr_source_key magic
- if (ulParentType == MAPI_FOLDER) {
- GetSourceKey(ulObjectId, &sSourceKey);
- GetSourceKey(ulParentId, &sParentSourceKey);
- AddChange(lpecSession, ulSyncId, sSourceKey, sParentSourceKey, bIsNew ? ICS_MESSAGE_NEW : ICS_MESSAGE_CHANGE);
- }
- // Update the folder counts
- er = UpdateFolderCounts(lpDatabase, ulParentId, ulFlags, lpsStreamInfo->lpPropValArray);
- if (er != erSuccess)
- goto exit;
- // Set PR_CONFLICT_ITEMS if available
- if (lpsConflictItems != NULL && lpsConflictItems->ulPropTag == PR_CONFLICT_ITEMS) {
- // Delete to be sure
- strQuery = "DELETE FROM mvproperties WHERE hierarchyid=" + stringify(ulObjectId) + " AND tag=" + stringify(PROP_ID(PR_CONFLICT_ITEMS)) + " AND type=" + stringify(PROP_TYPE(PR_CONFLICT_ITEMS));
- er = lpDatabase->DoDelete(strQuery);
- if (er != erSuccess)
- goto exit;
- nMVItems = GetMVItemCount(lpsConflictItems);
- for (gsoap_size_t i = 0; i < nMVItems; ++i) {
- er = CopySOAPPropValToDatabaseMVPropVal(lpsConflictItems, i, strColName, strColData, lpDatabase);
- if (er != erSuccess)
- goto exit;
- strQuery = "INSERT INTO mvproperties(hierarchyid,orderid,tag,type," + strColName + ") VALUES(" + stringify(ulObjectId) + "," + stringify(i) + "," + stringify(PROP_ID(PR_CONFLICT_ITEMS)) + "," + stringify(PROP_TYPE(PR_CONFLICT_ITEMS)) + "," + strColData + ")";
- er = lpDatabase->DoInsert(strQuery, NULL, &ulAffected);
- if (er != erSuccess)
- goto exit;
- if (ulAffected != 1) {
- er = KCERR_DATABASE_ERROR;
- ec_log_err("importMessageFromStream(): affected row count != 1");
- goto exit;
- }
- }
- }
- // Process MSGFLAG_SUBMIT
- // If the messages was saved by an ICS syncer, then we need to sync the PR_MESSAGE_FLAGS for MSGFLAG_SUBMIT if it
- // was included in the save.
- er = ProcessSubmitFlag(lpDatabase, ulSyncId, ulStoreId, ulObjectId, bIsNew, lpsStreamInfo->lpPropValArray);
- if (er != erSuccess)
- goto exit;
- if (ulParentType == MAPI_FOLDER) {
- er = ECTPropsPurge::NormalizeDeferredUpdates(lpecSession, lpDatabase, ulParentId);
- if (er != erSuccess)
- goto exit;
- }
- er = lpAttachmentStorage->Commit();
- if (er != erSuccess)
- goto exit;
- er = lpDatabase->Commit();
- if (er != erSuccess)
- goto exit;
- // Notification
- CreateNotifications(ulObjectId, MAPI_MESSAGE, ulParentId, ulGrandParentId, bIsNew, lpsStreamInfo->lpPropValArray, NULL);
- g_lpStatsCollector->Increment(SCN_DATABASE_MWOPS);
- exit:
- if (lpsStreamInfo != NULL && lpsStreamInfo->lpPropValArray != NULL)
- FreePropValArray(lpsStreamInfo->lpPropValArray, true);
- FreeDeletedItems(&lstDeleteItems);
- if(lpAttachmentStorage && er != erSuccess)
- lpAttachmentStorage->Rollback();
- ROLLBACK_ON_ERROR();
- if (er != erSuccess) {
- // remove from cache, else we can get sync issue, with missing messages offline
- lpecSession->GetSessionManager()->GetCacheManager()->RemoveIndexData(ulObjectId);
- lpecSession->GetSessionManager()->GetCacheManager()->Update(fnevObjectDeleted, ulObjectId);
- }
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(getChangeInfo, lpsResponse->er, entryId sEntryId, struct getChangeInfoResponse *lpsResponse)
- {
- unsigned int ulObjId = 0;
- USE_DATABASE();
- // Get object
- er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulObjId);
- if (er != erSuccess)
- return er;
-
- // Check security
- er = lpecSession->GetSecurity()->CheckPermission(ulObjId, ecSecurityRead);
- if (er != erSuccess)
- return er;
- // Get the Change Key
- strQuery = "SELECT val_binary FROM properties "
- "WHERE tag = " + stringify(PROP_ID(PR_CHANGE_KEY)) +
- " AND type = " + stringify(PROP_TYPE(PR_CHANGE_KEY)) +
- " AND hierarchyid = " + stringify(ulObjId) + " LIMIT 1";
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if(er != erSuccess)
- return er;
- if (lpDatabase->GetNumRows(lpDBResult) > 0) {
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
- lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
- lpsResponse->sPropCK.ulPropTag = PR_CHANGE_KEY;
- lpsResponse->sPropCK.__union = SOAP_UNION_propValData_bin;
- lpsResponse->sPropCK.Value.bin = s_alloc<xsd__base64Binary>(soap, 1);
- lpsResponse->sPropCK.Value.bin->__size = lpDBLen[0];
- lpsResponse->sPropCK.Value.bin->__ptr = s_alloc<unsigned char>(soap, lpDBLen[0]);
- memcpy(lpsResponse->sPropCK.Value.bin->__ptr, lpDBRow[0], lpDBLen[0]);
- } else {
- return KCERR_NOT_FOUND;
- }
- // Get the Predecessor Change List
- strQuery = "SELECT val_binary FROM properties "
- "WHERE tag = " + stringify(PROP_ID(PR_PREDECESSOR_CHANGE_LIST)) +
- " AND type = " + stringify(PROP_TYPE(PR_PREDECESSOR_CHANGE_LIST)) +
- " AND hierarchyid = " + stringify(ulObjId) + " LIMIT 1";
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if(er != erSuccess)
- return er;
- if (lpDatabase->GetNumRows(lpDBResult) > 0) {
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
- lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
- lpsResponse->sPropPCL.ulPropTag = PR_PREDECESSOR_CHANGE_LIST;
- lpsResponse->sPropPCL.__union = SOAP_UNION_propValData_bin;
- lpsResponse->sPropPCL.Value.bin = s_alloc<xsd__base64Binary>(soap, 1);
- lpsResponse->sPropPCL.Value.bin->__size = lpDBLen[0];
- lpsResponse->sPropPCL.Value.bin->__ptr = s_alloc<unsigned char>(soap, lpDBLen[0]);
- memcpy(lpsResponse->sPropPCL.Value.bin->__ptr, lpDBRow[0], lpDBLen[0]);
- } else {
- return KCERR_NOT_FOUND;
- }
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(purgeDeferredUpdates, lpsResponse->er, struct purgeDeferredUpdatesResponse *lpsResponse)
- {
- unsigned int ulFolderId = 0;
- USE_DATABASE();
-
- // Only system-admins may run this
- if (lpecSession->GetSecurity()->GetAdminLevel() < ADMIN_LEVEL_SYSADMIN)
- return KCERR_NO_ACCESS;
- er = ECTPropsPurge::GetLargestFolderId(lpDatabase, &ulFolderId);
- if(er == KCERR_NOT_FOUND) {
- // Nothing to purge
- lpsResponse->ulDeferredRemaining = 0;
- return er;
- }
-
- if(er != erSuccess)
- return er;
- er = ECTPropsPurge::PurgeDeferredTableUpdates(lpDatabase, ulFolderId);
- if(er != erSuccess)
- return er;
- return ECTPropsPurge::GetDeferredCount(lpDatabase, &lpsResponse->ulDeferredRemaining);
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(testPerform, *result, const char *szCommand,
- struct testPerformArgs sPerform, unsigned int *result)
- {
- if(parseBool(g_lpSessionManager->GetConfig()->GetSetting("enable_test_protocol")))
- er = TestPerform(soap, lpecSession, szCommand, sPerform.__size, sPerform.__ptr);
- else
- er = KCERR_NO_ACCESS;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(testSet, *result, const char *szVarName, const char *szValue,
- unsigned int *result)
- {
- if(parseBool(g_lpSessionManager->GetConfig()->GetSetting("enable_test_protocol")))
- er = TestSet(soap, lpecSession, szVarName, szValue);
- else
- er = KCERR_NO_ACCESS;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(testGet, lpsResponse->er, const char *szVarName,
- struct testGetResponse *lpsResponse)
- {
- if(parseBool(g_lpSessionManager->GetConfig()->GetSetting("enable_test_protocol")))
- er = TestGet(soap, lpecSession, szVarName, &lpsResponse->szValue);
- else
- er = KCERR_NO_ACCESS;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(setLockState, *result, entryId sEntryId, bool bLocked, unsigned int *result)
- {
- unsigned int ulObjId = 0;
- unsigned int ulOwner = 0;
- unsigned int ulObjType = 0;
- er = g_lpSessionManager->GetCacheManager()->GetObjectFromEntryId(&sEntryId, &ulObjId);
- if (er != erSuccess)
- return er;
- er = g_lpSessionManager->GetCacheManager()->GetObject(ulObjId, NULL, &ulOwner, NULL, &ulObjType);
- if (er != erSuccess)
- return er;
- if (ulObjType != MAPI_MESSAGE)
- return KCERR_NO_SUPPORT;
- // Do we need to be owner?
- er = lpecSession->GetSecurity()->CheckPermission(ulObjId, ecSecurityOwner);
- if (er != erSuccess)
- return er;
- if (bLocked) {
- er = lpecSession->LockObject(ulObjId);
- if (er == KCERR_NO_ACCESS)
- er = KCERR_SUBMITTED;
- } else {
- er = lpecSession->UnlockObject(ulObjId);
- }
- return er;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(getUserClientUpdateStatus, lpsResponse->er, entryId sUserId, struct userClientUpdateStatusResponse *lpsResponse)
- {
- USE_DATABASE();
- objectid_t sExternId;
- unsigned int ulUserId = 0;
- bool bHasLocalStore = false;
- if (!parseBool(g_lpSessionManager->GetConfig()->GetSetting("client_update_enabled")))
- return KCERR_NO_SUPPORT;
- er = GetLocalId(sUserId, 0, &ulUserId, &sExternId);
- if (er != erSuccess)
- return er;
- er = CheckUserStore(lpecSession, ulUserId, ECSTORE_TYPE_PRIVATE, &bHasLocalStore);
- if (er != erSuccess)
- return er;
- if (!bHasLocalStore)
- return KCERR_NOT_FOUND;
- strQuery = "SELECT trackid, UNIX_TIMESTAMP(updatetime), currentversion, latestversion, computername, status FROM clientupdatestatus WHERE userid="+stringify(ulUserId) + " LIMIT 1";
- er = lpDatabase->DoSelect(strQuery, &lpDBResult);
- if(er != erSuccess)
- return er;
- lpDBRow = lpDatabase->FetchRow(lpDBResult);
- if (lpDBRow == nullptr)
- return MAPI_E_NOT_FOUND;
- if(lpDBRow[0]) lpsResponse->ulTrackId = atoui(lpDBRow[0]);
- if(lpDBRow[1]) lpsResponse->tUpdatetime = atoui(lpDBRow[1]);
- if(lpDBRow[2]) lpsResponse->lpszCurrentversion = s_strcpy(soap, lpDBRow[2]);
- if(lpDBRow[3]) lpsResponse->lpszLatestversion = s_strcpy(soap, lpDBRow[3]);
- if(lpDBRow[4]) lpsResponse->lpszComputername = s_strcpy(soap, lpDBRow[4]);
- if(lpDBRow[5]) lpsResponse->ulStatus = atoui(lpDBRow[5]);
- return erSuccess;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(removeAllObjects, *result, entryId sExceptUserId, unsigned int *result)
- {
- er = KCERR_NO_SUPPORT;
- }
- SOAP_ENTRY_END()
- SOAP_ENTRY_START(resetFolderCount, lpsResponse->er, entryId sEntryId, struct resetFolderCountResponse *lpsResponse)
- {
- unsigned int ulObjId = 0;
- unsigned int ulOwner = 0;
- unsigned int ulObjType = 0;
- er = g_lpSessionManager->GetCacheManager()->GetObjectFromEntryId(&sEntryId, &ulObjId);
- if (er != erSuccess)
- return er;
- er = g_lpSessionManager->GetCacheManager()->GetObject(ulObjId, NULL, &ulOwner, NULL, &ulObjType);
- if (er != erSuccess)
- return er;
- if (ulObjType != MAPI_FOLDER)
- return KCERR_INVALID_TYPE;
- er = lpecSession->GetSecurity()->CheckPermission(ulObjId, ecSecurityOwner);
- if (er != erSuccess)
- return er;
- return ResetFolderCount(lpecSession, ulObjId, &lpsResponse->ulUpdates);
- }
- SOAP_ENTRY_END()
|