cmd.cpp 379 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842
  1. /*
  2. * Copyright 2005 - 2016 Zarafa and its licensors
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU Affero General Public License, version 3,
  6. * as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. *
  16. */
  17. #include <kopano/platform.h>
  18. #include <memory>
  19. #include <utility>
  20. #include <kopano/ECChannel.h>
  21. #include <kopano/MAPIErrors.h>
  22. #include <kopano/memory.hpp>
  23. #include <kopano/tie.hpp>
  24. #include "ECDatabaseUtils.h"
  25. #include "ECSessionManager.h"
  26. #include "ECPluginFactory.h"
  27. #include "ECDBDef.h"
  28. #include "ECDatabaseUpdate.h"
  29. #include "ECLicenseClient.h"
  30. #include <kopano/ECGuid.h>
  31. #include "ECLockManager.h"
  32. #include "soapH.h"
  33. #include <mutex>
  34. #include <unordered_map>
  35. #include <mapidefs.h>
  36. #include <mapitags.h>
  37. #include <sys/times.h>
  38. #include <ctime>
  39. #include <algorithm>
  40. #include <sstream>
  41. #include <set>
  42. #include <deque>
  43. #include <algorithm>
  44. #include <cstdio>
  45. #include <kopano/ECTags.h>
  46. #include <kopano/stringutil.h>
  47. #include "SOAPUtils.h"
  48. #include <kopano/kcodes.h>
  49. #include <kopano/Trace.h>
  50. #include "KCmd.nsmap"
  51. #include "ECFifoBuffer.h"
  52. #include "ECSerializer.h"
  53. #include "StreamUtil.h"
  54. #include <kopano/CommonUtil.h>
  55. #include "StorageUtil.h"
  56. #include "ics.h"
  57. #include "kcore.hpp"
  58. #include "pcutil.hpp"
  59. #include "ECAttachmentStorage.h"
  60. #include "ECGenProps.h"
  61. #include "ECUserManagement.h"
  62. #include "ECSecurity.h"
  63. #include "ECICS.h"
  64. #include "ECMultiStoreTable.h"
  65. #include "ECStatsCollector.h"
  66. #include "ECStringCompat.h"
  67. #include "ECTPropsPurge.h"
  68. #include "versions.h"
  69. #include "ECTestProtocol.h"
  70. #include <kopano/ECDefs.h>
  71. #include <kopano/EMSAbTag.h>
  72. #include <edkmdb.h>
  73. #include <kopano/ecversion.h>
  74. #include <kopano/mapiext.h>
  75. #include "../server/ECSoapServerConnection.h"
  76. #include "cmdutil.hpp"
  77. #include <kopano/ECThreadPool.h>
  78. #include "cmd.hpp"
  79. #include "logontime.hpp"
  80. #if defined(HAVE_GPERFTOOLS_MALLOC_EXTENSION_H)
  81. # include <gperftools/malloc_extension_c.h>
  82. # define HAVE_TCMALLOC 1
  83. #elif defined(HAVE_GOOGLE_MALLOC_EXTENSION_H)
  84. # include <google/malloc_extension_c.h>
  85. # define HAVE_TCMALLOC 1
  86. #endif
  87. #define STRIN_FIX(s) (bSupportUnicode ? (s) : ECStringCompat::WTF1252_to_UTF8(soap, (s)))
  88. #define STROUT_FIX(s) (bSupportUnicode ? (s) : ECStringCompat::UTF8_to_WTF1252(soap, (s)))
  89. #define STROUT_FIX_CPY(s) (bSupportUnicode ? s_strcpy(soap, (s)) : ECStringCompat::UTF8_to_WTF1252(soap, (s)))
  90. #define LOG_SOAP_DEBUG(_msg, ...) \
  91. ec_log(EC_LOGLEVEL_DEBUG | EC_LOGLEVEL_SOAP, "soap: " _msg, ##__VA_ARGS__)
  92. using namespace KCHL;
  93. namespace KC {
  94. class ECFifoSerializer _kc_final : public ECSerializer {
  95. public:
  96. enum eMode { serialize, deserialize };
  97. ECFifoSerializer(ECFifoBuffer *lpBuffer, eMode mode);
  98. virtual ~ECFifoSerializer(void);
  99. virtual ECRESULT SetBuffer(void *) _kc_override;
  100. virtual ECRESULT Write(const void *ptr, size_t size, size_t nmemb) _kc_override;
  101. virtual ECRESULT Read(void *ptr, size_t size, size_t nmemb) _kc_override;
  102. virtual ECRESULT Skip(size_t size, size_t nmemb) _kc_override;
  103. virtual ECRESULT Flush(void) _kc_override;
  104. virtual ECRESULT Stat(ULONG *have_read, ULONG *have_written) _kc_override;
  105. private:
  106. ECFifoBuffer *m_lpBuffer;
  107. eMode m_mode;
  108. ULONG m_ulRead = 0, m_ulWritten = 0;
  109. };
  110. extern ECSessionManager* g_lpSessionManager;
  111. extern ECStatsCollector* g_lpStatsCollector;
  112. // Hold the status of the softdelete purge system
  113. static bool g_bPurgeSoftDeleteStatus = FALSE;
  114. static ECRESULT CreateEntryId(GUID guidStore, unsigned int ulObjType,
  115. entryId **lppEntryId)
  116. {
  117. entryId* lpEntryId = NULL;
  118. EID eid;
  119. if (lppEntryId == NULL)
  120. return KCERR_INVALID_PARAMETER;
  121. if (CoCreateGuid(&eid.uniqueId) != hrSuccess)
  122. return KCERR_CALL_FAILED;
  123. eid.guid = guidStore;
  124. eid.usType = ulObjType;
  125. lpEntryId = s_alloc<entryId>(nullptr);
  126. lpEntryId->__size = sizeof(EID);
  127. lpEntryId->__ptr = s_alloc<unsigned char>(nullptr, lpEntryId->__size);
  128. memcpy(lpEntryId->__ptr, &eid, lpEntryId->__size);
  129. *lppEntryId = lpEntryId;
  130. return erSuccess;
  131. }
  132. /**
  133. * Get the local user id based on the entryid or the user id for old clients.
  134. *
  135. * When an entryid is provided, the extern id is extracted and the local user id
  136. * is resolved based on that. If no entryid is provided the provided legacy user id
  137. * is used as local user is and the extern id is resolved based on that. Old clients
  138. * that are not multi server aware provide the legacy user id in stead of the entryid.
  139. *
  140. * @param[in] sUserId The entryid of the user for which to obtain the local id
  141. * @param[in] ulLegacyUserId The legacy user id, which will be used as the entryid when.
  142. * no entryid is provided (old clients).
  143. * @param[out] lpulUserId The local user id.
  144. * @param[out] lpsExternId The extern id of the user. This can be NULL if the extern id
  145. * is not required by the caller.
  146. *
  147. * @retval KCERR_INVALID_PARAMATER One or more parameters are invalid.
  148. * @retval KCERR_NOT_FOUND The local is is not found.
  149. */
  150. static ECRESULT GetLocalId(entryId sUserId, unsigned int ulLegacyUserId,
  151. unsigned int *lpulUserId, objectid_t *lpsExternId)
  152. {
  153. ECRESULT er = erSuccess;
  154. unsigned int ulUserId = 0;
  155. objectid_t sExternId;
  156. objectdetails_t sDetails;
  157. if (lpulUserId == NULL)
  158. return KCERR_INVALID_PARAMETER;
  159. // If no entryid is present, use the 'current' user.
  160. if (ulLegacyUserId == 0 && sUserId.__size == 0) {
  161. // When lpsExternId is requested, the 'current' user will not be
  162. // requested in this way. However, to make sure a caller does expect a result in the future
  163. // we'll return an error in that case.
  164. if (lpsExternId != NULL)
  165. er = KCERR_INVALID_PARAMETER;
  166. else
  167. *lpulUserId = 0;
  168. // TODO: return value in lpulUserId ?
  169. return er;
  170. }
  171. if (sUserId.__ptr) {
  172. // Extract the information from the entryid.
  173. er = ABEntryIDToID(&sUserId, &ulUserId, &sExternId, NULL);
  174. if (er != erSuccess)
  175. return er;
  176. // If an extern id is present, we should get an object based on that.
  177. if (!sExternId.id.empty())
  178. er = g_lpSessionManager->GetCacheManager()->GetUserObject(sExternId, &ulUserId, NULL, NULL);
  179. } else {
  180. // use user id from 6.20 and older clients
  181. ulUserId = ulLegacyUserId;
  182. if (lpsExternId)
  183. er = g_lpSessionManager->GetCacheManager()->GetUserObject(ulLegacyUserId, &sExternId, NULL, NULL);
  184. }
  185. if (er != erSuccess)
  186. return er;
  187. *lpulUserId = ulUserId;
  188. if (lpsExternId)
  189. *lpsExternId = std::move(sExternId);
  190. return erSuccess;
  191. }
  192. /**
  193. * Check if a user has a store of a particular type on the local server.
  194. *
  195. * On a single server configuration this function will return true for
  196. * all ECSTORE_TYPE_PRIVATE and ECSTORE_TYPE_PUBLIC requests and false otherwise.
  197. *
  198. * In single tennant mode, requests for ECSTORE_TYPE_PUBLIC will always return true,
  199. * regardless of the server on which the public should exist. This is actually wrong
  200. * but is the same behaviour as before.
  201. *
  202. * @param[in] lpecSession The ECSession object for the current session.
  203. * @param[in] ulUserId The user id of the user for which to check if a
  204. * store is available.
  205. * @param[in] ulStoreType The store type to check for.
  206. * @param[out] lpbHasLocalStore The boolean that will contain the result on success.
  207. *
  208. * @retval KCERR_INVALID_PARAMETER One or more parameters are invalid.
  209. * @retval KCERR_NOT_FOUND The user specified by ulUserId was not found.
  210. */
  211. static ECRESULT CheckUserStore(ECSession *lpecSession, unsigned ulUserId,
  212. unsigned ulStoreType, bool *lpbHasLocalStore)
  213. {
  214. ECRESULT er;
  215. objectdetails_t sDetails;
  216. bool bPrivateOrPublic;
  217. if (lpecSession == NULL || lpbHasLocalStore == NULL || !ECSTORE_TYPE_ISVALID(ulStoreType))
  218. return KCERR_INVALID_PARAMETER;
  219. bPrivateOrPublic = (ulStoreType == ECSTORE_TYPE_PRIVATE || ulStoreType == ECSTORE_TYPE_PUBLIC);
  220. if (g_lpSessionManager->IsDistributedSupported()) {
  221. er = lpecSession->GetUserManagement()->GetObjectDetails(ulUserId, &sDetails);
  222. if (er != erSuccess)
  223. return er;
  224. if (bPrivateOrPublic) {
  225. // @todo: Check if there's a define or constant for everyone.
  226. if (ulUserId == 2) // Everyone, public in single tennant
  227. *lpbHasLocalStore = true;
  228. else
  229. *lpbHasLocalStore = (strcasecmp(sDetails.GetPropString(OB_PROP_S_SERVERNAME).c_str(), g_lpSessionManager->GetConfig()->GetSetting("server_name")) == 0);
  230. } else // Archive store
  231. *lpbHasLocalStore = sDetails.PropListStringContains((property_key_t)PR_EC_ARCHIVE_SERVERS_A, g_lpSessionManager->GetConfig()->GetSetting("server_name"), true);
  232. } else // Single tennant
  233. *lpbHasLocalStore = bPrivateOrPublic;
  234. return erSuccess;
  235. }
  236. static ECRESULT GetABEntryID(unsigned int ulUserId, soap *lpSoap,
  237. entryId *lpUserId)
  238. {
  239. ECRESULT er;
  240. entryId sUserId = {0};
  241. objectid_t sExternId;
  242. if (lpSoap == NULL)
  243. return KCERR_INVALID_PARAMETER;
  244. if (ulUserId == KOPANO_UID_SYSTEM) {
  245. sExternId.objclass = ACTIVE_USER;
  246. } else if (ulUserId == KOPANO_UID_EVERYONE) {
  247. sExternId.objclass = DISTLIST_SECURITY;
  248. } else {
  249. er = g_lpSessionManager->GetCacheManager()->GetUserObject(ulUserId, &sExternId, NULL, NULL);
  250. if (er != erSuccess)
  251. return er;
  252. }
  253. er = ABIDToEntryID(lpSoap, ulUserId, sExternId, &sUserId);
  254. if (er != erSuccess)
  255. return er;
  256. *lpUserId = std::move(sUserId); // pointer (__ptr) is copied, not data
  257. return erSuccess;
  258. }
  259. static ECRESULT PeerIsServer(struct soap *soap,
  260. const std::string &strServerName, const std::string &strHttpPath,
  261. const std::string &strSslPath, bool *lpbResult)
  262. {
  263. bool bResult = false;
  264. if (soap == NULL || lpbResult == NULL)
  265. return KCERR_INVALID_PARAMETER;
  266. // First check if we are connecting through Unix socket/named pipe and if the request URL matches this server
  267. if (SOAP_CONNECTION_TYPE_NAMED_PIPE(soap) &&
  268. strcasecmp(strServerName.c_str(), g_lpSessionManager->GetConfig()->GetSetting("server_name")) == 0)
  269. bResult = true;
  270. else
  271. {
  272. const std::string *lpstrPath = &strHttpPath;
  273. if (lpstrPath->empty())
  274. lpstrPath = &strSslPath;
  275. if (!lpstrPath->empty())
  276. {
  277. std::string strHost;
  278. std::string::size_type ulHostStart = 0;
  279. std::string::size_type ulHostEnd = 0;
  280. struct addrinfo sHint = {0};
  281. struct addrinfo *lpsAddrInfo = NULL;
  282. struct addrinfo *lpsAddrIter = NULL;
  283. ulHostStart = lpstrPath->find("://");
  284. if (ulHostStart == std::string::npos)
  285. return KCERR_INVALID_PARAMETER;
  286. ulHostStart += 3; // Skip the '://'
  287. ulHostEnd = lpstrPath->find(':', ulHostStart);
  288. if (ulHostEnd == std::string::npos)
  289. return KCERR_INVALID_PARAMETER;
  290. strHost = lpstrPath->substr(ulHostStart, ulHostEnd - ulHostStart);
  291. sHint.ai_family = AF_UNSPEC;
  292. sHint.ai_socktype = SOCK_STREAM;
  293. if (getaddrinfo(strHost.c_str(), NULL, &sHint, &lpsAddrInfo) != 0)
  294. return KCERR_NOT_FOUND;
  295. lpsAddrIter = lpsAddrInfo;
  296. while (lpsAddrIter && bResult == false) {
  297. if (soap->peerlen >= sizeof(sockaddr) && lpsAddrIter->ai_family == ((sockaddr*)&soap->peer)->sa_family) {
  298. switch (lpsAddrIter->ai_family) {
  299. case AF_INET:
  300. {
  301. auto lpsLeft = reinterpret_cast<struct sockaddr_in *>(lpsAddrIter->ai_addr);
  302. auto lpsRight = reinterpret_cast<struct sockaddr_in *>(&soap->peer);
  303. bResult = (memcmp(&lpsLeft->sin_addr, &lpsRight->sin_addr, sizeof(lpsLeft->sin_addr)) == 0);
  304. break;
  305. }
  306. case AF_INET6:
  307. {
  308. auto lpsLeft = reinterpret_cast<struct sockaddr_in6 *>(lpsAddrIter->ai_addr);
  309. auto lpsRight = reinterpret_cast<struct sockaddr_in6 *>(&soap->peer);
  310. bResult = (memcmp(&lpsLeft->sin6_addr, &lpsRight->sin6_addr, sizeof(lpsLeft->sin6_addr)) == 0);
  311. break;
  312. }
  313. default:
  314. break;
  315. }
  316. }
  317. lpsAddrIter = lpsAddrIter->ai_next;
  318. }
  319. freeaddrinfo(lpsAddrInfo);
  320. }
  321. }
  322. *lpbResult = bResult;
  323. return erSuccess;
  324. }
  325. ECFifoSerializer::ECFifoSerializer(ECFifoBuffer *lpBuffer, eMode mode) :
  326. m_mode(mode)
  327. {
  328. SetBuffer(lpBuffer);
  329. }
  330. ECFifoSerializer::~ECFifoSerializer(void)
  331. {
  332. if (m_lpBuffer == nullptr)
  333. return;
  334. ECFifoBuffer::close_flags flags = (m_mode == serialize ? ECFifoBuffer::cfWrite : ECFifoBuffer::cfRead);
  335. m_lpBuffer->Close(flags);
  336. }
  337. ECRESULT ECFifoSerializer::SetBuffer(void *lpBuffer)
  338. {
  339. m_lpBuffer = static_cast<ECFifoBuffer *>(lpBuffer);
  340. return erSuccess;
  341. }
  342. ECRESULT ECFifoSerializer::Write(const void *ptr, size_t size, size_t nmemb)
  343. {
  344. ECRESULT er = erSuccess;
  345. union {
  346. char c[8];
  347. short s;
  348. int i;
  349. long long ll;
  350. } tmp;
  351. if (m_mode != serialize)
  352. return KCERR_NO_SUPPORT;
  353. if (ptr == nullptr)
  354. return KCERR_INVALID_PARAMETER;
  355. switch (size) {
  356. case 1:
  357. er = m_lpBuffer->Write(ptr, nmemb, STR_DEF_TIMEOUT, NULL);
  358. break;
  359. case 2:
  360. for (size_t x = 0; x < nmemb && er == erSuccess; ++x) {
  361. tmp.s = htons(static_cast<const short *>(ptr)[x]);
  362. er = m_lpBuffer->Write(&tmp, size, STR_DEF_TIMEOUT, nullptr);
  363. }
  364. break;
  365. case 4:
  366. for (size_t x = 0; x < nmemb && er == erSuccess; ++x) {
  367. tmp.i = htonl(static_cast<const int *>(ptr)[x]);
  368. er = m_lpBuffer->Write(&tmp, size, STR_DEF_TIMEOUT, nullptr);
  369. }
  370. break;
  371. case 8:
  372. for (size_t x = 0; x < nmemb && er == erSuccess; ++x) {
  373. tmp.ll = htonll(static_cast<const long long *>(ptr)[x]);
  374. er = m_lpBuffer->Write(&tmp, size, STR_DEF_TIMEOUT, nullptr);
  375. }
  376. break;
  377. default:
  378. er = KCERR_INVALID_PARAMETER;
  379. break;
  380. }
  381. m_ulWritten += size * nmemb;
  382. return er;
  383. }
  384. ECRESULT ECFifoSerializer::Read(void *ptr, size_t size, size_t nmemb)
  385. {
  386. ECRESULT er;
  387. ECFifoBuffer::size_type cbRead = 0;
  388. if (m_mode != deserialize)
  389. return KCERR_NO_SUPPORT;
  390. if (ptr == nullptr)
  391. return KCERR_INVALID_PARAMETER;
  392. er = m_lpBuffer->Read(ptr, size * nmemb, STR_DEF_TIMEOUT, &cbRead);
  393. if (er != erSuccess)
  394. return er;
  395. m_ulRead += cbRead;
  396. if (cbRead != size * nmemb)
  397. return KCERR_CALL_FAILED;
  398. switch (size) {
  399. case 1: break;
  400. case 2:
  401. for (size_t x = 0; x < nmemb; ++x)
  402. static_cast<short *>(ptr)[x] = ntohs(static_cast<short *>(ptr)[x]);
  403. break;
  404. case 4:
  405. for (size_t x = 0; x < nmemb; ++x)
  406. static_cast<int *>(ptr)[x] = ntohl(static_cast<int *>(ptr)[x]);
  407. break;
  408. case 8:
  409. for (size_t x = 0; x < nmemb; ++x)
  410. static_cast<long long *>(ptr)[x] = ntohll(static_cast<long long *>(ptr)[x]);
  411. break;
  412. default:
  413. er = KCERR_INVALID_PARAMETER;
  414. break;
  415. }
  416. return er;
  417. }
  418. ECRESULT ECFifoSerializer::Skip(size_t size, size_t nmemb)
  419. {
  420. std::unique_ptr<char[]> buf(new(std::nothrow) char[size*nmemb]);
  421. if (buf == nullptr)
  422. return KCERR_NOT_ENOUGH_MEMORY;
  423. return Read(buf.get(), size, nmemb);
  424. }
  425. ECRESULT ECFifoSerializer::Flush()
  426. {
  427. ECRESULT er;
  428. size_t cbRead = 0;
  429. char buf[16384];
  430. while (true) {
  431. er = m_lpBuffer->Read(buf, sizeof(buf), STR_DEF_TIMEOUT, &cbRead);
  432. if (er != erSuccess)
  433. return er;
  434. m_ulRead += cbRead;
  435. if (cbRead < sizeof(buf))
  436. break;
  437. }
  438. return er;
  439. }
  440. ECRESULT ECFifoSerializer::Stat(ULONG *lpcbRead, ULONG *lpcbWrite)
  441. {
  442. if (lpcbRead != nullptr)
  443. *lpcbRead = m_ulRead;
  444. if (lpcbWrite != nullptr)
  445. *lpcbWrite = m_ulWritten;
  446. return erSuccess;
  447. }
  448. /**
  449. * Get the best server path for a server
  450. *
  451. * This function will return the 'best' server path to redirect the client to. This is
  452. * done by examining the existing incoming connection, and choosing an appropriate access
  453. * method to the given server. Rules are as follows:
  454. *
  455. * - If bProxy is TRUE and the destination server has a proxy address, return the proxy address
  456. * - If bProxy is FALSE:
  457. * - If existing connection is HTTP, return first available of: HTTP, HTTPS
  458. * - If existing connection is HTTPS, return HTTPS
  459. * - If existing connection is FILE, return first available of: HTTPS, HTTP
  460. *
  461. * @param[in] soap SOAP structure for incoming request
  462. * @param[in] lpecSession Session for the request
  463. * @param[in] strServerName Server name to get path for
  464. * @param[in] bProxy TRUE if we are requesting the proxy address for a server
  465. * @param[out] lpstrServerPath Output path of server (URL)
  466. * @return result
  467. */
  468. ECRESULT GetBestServerPath(struct soap *soap, ECSession *lpecSession, const std::string &strServerName, std::string *lpstrServerPath)
  469. {
  470. ECRESULT er;
  471. std::string strServerPath;
  472. bool bConnectPipe = false;
  473. serverdetails_t sServerDetails;
  474. std::string strFilePath;
  475. std::string strHttpPath;
  476. std::string strSslPath;
  477. std::string strProxyPath;
  478. const char *szProxyHeader = lpecSession->GetSessionManager()->GetConfig()->GetSetting("proxy_header");
  479. if (soap == NULL || soap->user == NULL || lpstrServerPath == NULL)
  480. return KCERR_INVALID_PARAMETER;
  481. auto lpInfo = soap_info(soap);
  482. er = lpecSession->GetUserManagement()->GetServerDetails(strServerName, &sServerDetails);
  483. if (er != erSuccess)
  484. return er;
  485. strProxyPath = sServerDetails.GetProxyPath();
  486. strFilePath = sServerDetails.GetFilePath();
  487. strHttpPath = sServerDetails.GetHttpPath();
  488. strSslPath = sServerDetails.GetSslPath();
  489. // Always redirect if proxy_header is "*"
  490. if (!strcmp(szProxyHeader, "*") || lpInfo->bProxy) {
  491. if(!strProxyPath.empty()) {
  492. *lpstrServerPath = std::move(strProxyPath);
  493. return erSuccess;
  494. } else {
  495. ec_log_warn("Proxy path not set for server \"%s\"! falling back to direct address.", strServerName.c_str());
  496. }
  497. }
  498. if (!strFilePath.empty())
  499. {
  500. er = PeerIsServer(soap, strServerName, strHttpPath, strSslPath, &bConnectPipe);
  501. if (er != erSuccess)
  502. return er;
  503. } else {
  504. // TODO: check if same server, and set strFilePath 'cause it's known
  505. bConnectPipe = false;
  506. }
  507. if (bConnectPipe)
  508. strServerPath = strFilePath;
  509. else
  510. switch (SOAP_CONNECTION_TYPE(soap))
  511. {
  512. case CONNECTION_TYPE_TCP:
  513. if (!strHttpPath.empty())
  514. strServerPath = strHttpPath;
  515. else if (!strSslPath.empty())
  516. strServerPath = strSslPath;
  517. break;
  518. case CONNECTION_TYPE_SSL:
  519. if (!strSslPath.empty())
  520. strServerPath = strSslPath;
  521. break;
  522. case CONNECTION_TYPE_NAMED_PIPE:
  523. case CONNECTION_TYPE_NAMED_PIPE_PRIORITY:
  524. if (!strSslPath.empty())
  525. strServerPath = strSslPath;
  526. else if (!strHttpPath.empty())
  527. strServerPath = strHttpPath;
  528. break;
  529. }
  530. if (strServerPath.empty())
  531. return KCERR_NOT_FOUND;
  532. *lpstrServerPath = std::move(strServerPath);
  533. return erSuccess;
  534. }
  535. } /* namespace */
  536. // exception: This function does internal Begin + Commit/Rollback
  537. static ECRESULT MoveObjects(ECSession *lpSession, ECDatabase *lpDatabase, ECListInt* lplObjectIds, unsigned int ulDestFolderId, unsigned int ulSyncId);
  538. // these functions don't do Begin + Commit/Rollback
  539. 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);
  540. static ECRESULT DoNotifySubscribe(ECSession *lpecSession, unsigned long long ulSessionId, struct notifySubscribe *notifySubscribe);
  541. /**
  542. * logon: log on and create a session with provided credentials
  543. */
  544. 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)
  545. {
  546. ECRESULT er = erSuccess;
  547. ECSession *lpecSession = NULL;
  548. ECSESSIONID sessionID = 0;
  549. GUID sServerGuid = {0};
  550. struct timespec startTimes = {0}, endTimes = {0};
  551. double dblStart = GetTimeOfDay();
  552. clock_gettime(CLOCK_THREAD_CPUTIME_ID, &startTimes);
  553. LOG_SOAP_DEBUG("%020llu: S logon", static_cast<unsigned long long>(sessionID));
  554. if ((clientCaps & KOPANO_CAP_UNICODE) == 0) {
  555. user = ECStringCompat::WTF1252_to_UTF8(soap, user);
  556. pass = ECStringCompat::WTF1252_to_UTF8(soap, pass);
  557. clientVersion = ECStringCompat::WTF1252_to_UTF8(soap, clientVersion);
  558. szClientApp = ECStringCompat::WTF1252_to_UTF8(soap, szClientApp);
  559. }
  560. lpsResponse->lpszVersion = const_cast<char *>("0," PROJECT_VERSION_SERVER_STR);
  561. lpsResponse->ulCapabilities = KOPANO_LATEST_CAPABILITIES;
  562. /*
  563. * Client desires compression, so turn it on, but only if remote.
  564. * Otherwise, clear the flag from clientCaps, because
  565. * Create(Auth)Session remembers them, re-evaluates CAP_COMPRESSION,
  566. * and would otherwise turn on compression again.
  567. */
  568. if (zcp_peerfd_is_local(soap->socket) <= 0 && (clientCaps & KOPANO_CAP_COMPRESSION)) {
  569. lpsResponse->ulCapabilities |= KOPANO_CAP_COMPRESSION;
  570. // (ECSessionManager::ValidateSession() will do this for all other functions)
  571. soap_set_imode(soap, SOAP_ENC_ZLIB); // also autodetected
  572. soap_set_omode(soap, SOAP_ENC_ZLIB | SOAP_IO_CHUNK);
  573. } else {
  574. clientCaps &= ~KOPANO_CAP_COMPRESSION;
  575. }
  576. // check username and password
  577. er = g_lpSessionManager->CreateSession(soap, user, pass, impersonate, clientVersion, szClientApp, szClientAppVersion, szClientAppMisc, clientCaps, ullSessionGroup, &sessionID, &lpecSession, true, (logonFlags & KOPANO_LOGON_NO_UID_AUTH) == 0);
  578. if(er != erSuccess){
  579. er = KCERR_LOGON_FAILED;
  580. goto exit;
  581. }
  582. // We allow Zarafa >=6 clients to connect to a Kopano server. However, anything below that will be
  583. // denied. We can't say what future clients may or may not be capable of. So we'll leave that to the
  584. // clients.
  585. if (KOPANO_COMPARE_VERSION_TO_GENERAL(lpecSession->ClientVersion(), MAKE_KOPANO_GENERAL(6)) < 0) {
  586. ec_log_warn("Rejected logon attempt from a %s version client.", clientVersion ? clientVersion : "<unknown>");
  587. er = KCERR_INVALID_VERSION;
  588. goto exit;
  589. }
  590. lpsResponse->ulSessionId = sessionID;
  591. if (clientCaps & KOPANO_CAP_MULTI_SERVER)
  592. lpsResponse->ulCapabilities |= KOPANO_CAP_MULTI_SERVER;
  593. if (clientCaps & KOPANO_CAP_ENHANCED_ICS && parseBool(g_lpSessionManager->GetConfig()->GetSetting("enable_enhanced_ics"))) {
  594. lpsResponse->ulCapabilities |= KOPANO_CAP_ENHANCED_ICS;
  595. soap_set_omode(soap, SOAP_ENC_MTOM | SOAP_IO_CHUNK);
  596. soap_set_imode(soap, SOAP_ENC_MTOM);
  597. soap_post_check_mime_attachments(soap);
  598. }
  599. if (clientCaps & KOPANO_CAP_UNICODE)
  600. lpsResponse->ulCapabilities |= KOPANO_CAP_UNICODE;
  601. if (clientCaps & KOPANO_CAP_MSGLOCK)
  602. lpsResponse->ulCapabilities |= KOPANO_CAP_MSGLOCK;
  603. er = g_lpSessionManager->GetServerGUID(&sServerGuid);
  604. if (er != erSuccess)
  605. goto exit;
  606. lpsResponse->sServerGuid.__ptr = (unsigned char*)s_memcpy(soap, (char*)&sServerGuid, sizeof(sServerGuid));
  607. lpsResponse->sServerGuid.__size = sizeof(sServerGuid);
  608. // Only save logon if credentials were supplied by the user; otherwise the logon is probably automated
  609. if(lpecSession->GetAuthMethod() == ECSession::METHOD_USERPASSWORD || lpecSession->GetAuthMethod() == ECSession::METHOD_SSO)
  610. record_logon_time(lpecSession, true);
  611. exit:
  612. if (lpecSession)
  613. lpecSession->Unlock();
  614. lpsResponse->er = er;
  615. clock_gettime(CLOCK_THREAD_CPUTIME_ID, &endTimes);
  616. LOG_SOAP_DEBUG("%020llu: E logon 0x%08x %f %f",
  617. static_cast<unsigned long long>(sessionID), er,
  618. timespec2dbl(endTimes) - timespec2dbl(startTimes),
  619. GetTimeOfDay() - dblStart);
  620. return SOAP_OK;
  621. }
  622. /**
  623. * logon: log on and create a session with provided credentials
  624. */
  625. 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)
  626. {
  627. ECRESULT er = KCERR_LOGON_FAILED;
  628. ECAuthSession *lpecAuthSession = NULL;
  629. ECSession *lpecSession = NULL;
  630. ECSESSIONID newSessionID = 0;
  631. GUID sServerGuid = {0};
  632. xsd__base64Binary *lpOutput = NULL;
  633. const char *lpszEnabled = NULL;
  634. struct timespec startTimes = {0}, endTimes = {0};
  635. double dblStart = GetTimeOfDay();
  636. clock_gettime(CLOCK_THREAD_CPUTIME_ID, &startTimes);
  637. LOG_SOAP_DEBUG("%020" PRIu64 ": S ssoLogon", ulSessionId);
  638. if (!lpInput || lpInput->__size == 0 || lpInput->__ptr == NULL || !szUsername || !szClientVersion)
  639. goto exit;
  640. lpszEnabled = g_lpSessionManager->GetConfig()->GetSetting("enable_sso");
  641. if (!(lpszEnabled && strcasecmp(lpszEnabled, "yes") == 0))
  642. goto nosso;
  643. lpsResponse->lpszVersion = const_cast<char *>("0," PROJECT_VERSION_SERVER_STR);
  644. lpsResponse->ulCapabilities = KOPANO_LATEST_CAPABILITIES;
  645. /* See ns__logon for comments. */
  646. if (zcp_peerfd_is_local(soap->socket) <= 0 && (clientCaps & KOPANO_CAP_COMPRESSION)) {
  647. lpsResponse->ulCapabilities |= KOPANO_CAP_COMPRESSION;
  648. // (ECSessionManager::ValidateSession() will do this for all other functions)
  649. soap_set_imode(soap, SOAP_ENC_ZLIB); // also autodetected
  650. soap_set_omode(soap, SOAP_ENC_ZLIB | SOAP_IO_CHUNK);
  651. } else {
  652. clientCaps &= ~KOPANO_CAP_COMPRESSION;
  653. }
  654. if (ulSessionId == 0) {
  655. // new auth session
  656. er = g_lpSessionManager->CreateAuthSession(soap, clientCaps, &newSessionID, &lpecAuthSession, true, true);
  657. if (er != erSuccess) {
  658. er = KCERR_LOGON_FAILED;
  659. goto exit;
  660. }
  661. // when the first validate fails, remove the correct sessionid
  662. ulSessionId = newSessionID;
  663. } else {
  664. er = g_lpSessionManager->ValidateSession(soap, ulSessionId, &lpecAuthSession, true);
  665. if (er != erSuccess)
  666. goto exit;
  667. }
  668. lpecAuthSession->SetClientMeta(szClientAppVersion, szClientAppMisc);
  669. if ((clientCaps & KOPANO_CAP_UNICODE) == 0) {
  670. szUsername = ECStringCompat::WTF1252_to_UTF8(soap, szUsername);
  671. szClientVersion = ECStringCompat::WTF1252_to_UTF8(soap, szClientVersion);
  672. szClientApp = ECStringCompat::WTF1252_to_UTF8(soap, szClientApp);
  673. }
  674. er = lpecAuthSession->ValidateSSOData(soap, szUsername, szImpersonateUser, szClientVersion, szClientApp, szClientAppVersion, szClientAppMisc, lpInput, &lpOutput);
  675. if (er == KCERR_SSO_CONTINUE) {
  676. // continue validation exchange
  677. lpsResponse->lpOutput = lpOutput;
  678. } else if (er == erSuccess) {
  679. // done and logged in
  680. // create ecsession from ecauthsession, and place in session map
  681. er = g_lpSessionManager->RegisterSession(lpecAuthSession, ullSessionGroup, szClientVersion, szClientApp, szClientAppVersion, szClientAppMisc, &newSessionID, &lpecSession, true);
  682. if (er != erSuccess) {
  683. ec_log_err("User authenticated, but failed to create session. Error 0x%08X", er);
  684. goto exit;
  685. }
  686. // We allow Zarafa >=6 clients to connect to a Kopano server. However, anything below that will be
  687. // denied. We can't say what future clients may or may not be capable of. So we'll leave that to the
  688. // clients.
  689. if (KOPANO_COMPARE_VERSION_TO_GENERAL(lpecSession->ClientVersion(), MAKE_KOPANO_GENERAL(6)) < 0) {
  690. ec_log_warn("Rejected logon attempt from a %s version client.", szClientVersion ? szClientVersion : "<unknown>");
  691. er = KCERR_INVALID_VERSION;
  692. goto exit;
  693. }
  694. // delete authsession
  695. lpecAuthSession->Unlock();
  696. g_lpSessionManager->RemoveSession(ulSessionId);
  697. lpecAuthSession = NULL;
  698. // return ecsession number
  699. lpsResponse->lpOutput = NULL;
  700. } else {
  701. // delete authsession
  702. lpecAuthSession->Unlock();
  703. g_lpSessionManager->RemoveSession(ulSessionId);
  704. lpecAuthSession = NULL;
  705. er = KCERR_LOGON_FAILED;
  706. goto exit;
  707. }
  708. lpsResponse->ulSessionId = newSessionID;
  709. if (clientCaps & KOPANO_CAP_MULTI_SERVER)
  710. lpsResponse->ulCapabilities |= KOPANO_CAP_MULTI_SERVER;
  711. if (clientCaps & KOPANO_CAP_ENHANCED_ICS && parseBool(g_lpSessionManager->GetConfig()->GetSetting("enable_enhanced_ics"))) {
  712. lpsResponse->ulCapabilities |= KOPANO_CAP_ENHANCED_ICS;
  713. soap_set_omode(soap, SOAP_ENC_MTOM | SOAP_IO_CHUNK);
  714. soap_set_imode(soap, SOAP_ENC_MTOM);
  715. soap_post_check_mime_attachments(soap);
  716. }
  717. if (clientCaps & KOPANO_CAP_UNICODE)
  718. lpsResponse->ulCapabilities |= KOPANO_CAP_UNICODE;
  719. if (clientCaps & KOPANO_CAP_MSGLOCK)
  720. lpsResponse->ulCapabilities |= KOPANO_CAP_MSGLOCK;
  721. if(er != KCERR_SSO_CONTINUE) {
  722. // Don't reset er to erSuccess on SSO_CONTINUE, we don't need the server guid yet
  723. er = g_lpSessionManager->GetServerGUID(&sServerGuid);
  724. if (er != erSuccess)
  725. goto exit;
  726. lpsResponse->sServerGuid.__ptr = (unsigned char*)s_memcpy(soap, (char*)&sServerGuid, sizeof(sServerGuid));
  727. lpsResponse->sServerGuid.__size = sizeof(sServerGuid);
  728. }
  729. if(lpecSession && (lpecSession->GetAuthMethod() == ECSession::METHOD_USERPASSWORD || lpecSession->GetAuthMethod() == ECSession::METHOD_SSO))
  730. record_logon_time(lpecSession, true);
  731. exit:
  732. if (lpecAuthSession != NULL)
  733. lpecAuthSession->Unlock();
  734. if (lpecSession)
  735. lpecSession->Unlock();
  736. if (er == erSuccess)
  737. g_lpStatsCollector->Increment(SCN_LOGIN_SSO);
  738. else if (er != KCERR_SSO_CONTINUE)
  739. g_lpStatsCollector->Increment(SCN_LOGIN_DENIED);
  740. nosso:
  741. lpsResponse->er = er;
  742. clock_gettime(CLOCK_THREAD_CPUTIME_ID, &endTimes);
  743. LOG_SOAP_DEBUG("%020" PRIu64 ": E ssoLogon 0x%08x %f %f", ulSessionId, er, timespec2dbl(endTimes) - timespec2dbl(startTimes), GetTimeOfDay() - dblStart);
  744. return SOAP_OK;
  745. }
  746. /**
  747. * logoff: invalidate the session and close all notifications and memory held by the session
  748. */
  749. int ns__logoff(struct soap *soap, ULONG64 ulSessionId, unsigned int *result)
  750. {
  751. ECRESULT er = erSuccess;
  752. ECSession *lpecSession = NULL;
  753. struct timespec startTimes = {0}, endTimes = {0};
  754. double dblStart = GetTimeOfDay();
  755. clock_gettime(CLOCK_THREAD_CPUTIME_ID, &startTimes);
  756. LOG_SOAP_DEBUG("%020" PRIu64 ": S logoff", ulSessionId);
  757. er = g_lpSessionManager->ValidateSession(soap, ulSessionId, &lpecSession, true);
  758. if(er != erSuccess)
  759. goto exit;
  760. if (lpecSession->GetAuthMethod() == ECSession::METHOD_USERPASSWORD ||
  761. lpecSession->GetAuthMethod() == ECSession::METHOD_SSO)
  762. record_logon_time(lpecSession, false);
  763. lpecSession->Unlock();
  764. // lpecSession is discarded. It is not locked, so we can do that. We only did the 'validatesession'
  765. // call to see if the session id existed in the first place, and the request is coming from the correct
  766. // IP address. Another logoff() call called at the same time may remove the session *here*, in which case the following call
  767. // will fail. This makes sure people can't terminate each others sessions unless they have the same source IP.
  768. er = g_lpSessionManager->RemoveSession(ulSessionId);
  769. exit:
  770. *result = er;
  771. clock_gettime(CLOCK_THREAD_CPUTIME_ID, &endTimes);
  772. LOG_SOAP_DEBUG("%020" PRIu64 ": E logoff 0x%08x %f %f", ulSessionId, 0, timespec2dbl(endTimes) - timespec2dbl(startTimes), GetTimeOfDay() - dblStart);
  773. return SOAP_OK;
  774. }
  775. #define SOAP_ENTRY_FUNCTION_HEADER(resultvar, fname) \
  776. ECRESULT er = erSuccess; \
  777. struct timespec startTimes = {0}, endTimes = {0}; \
  778. double dblStart = GetTimeOfDay(); \
  779. ECSession *lpecSession = NULL; \
  780. unsigned int *lpResultVar = &resultvar; \
  781. const char *szFname = #fname; \
  782. clock_gettime(CLOCK_THREAD_CPUTIME_ID, &startTimes); \
  783. LOG_SOAP_DEBUG("%020" PRIu64 ": S %s", ulSessionId, szFname); \
  784. er = g_lpSessionManager->ValidateSession(soap, ulSessionId, &lpecSession, true);\
  785. const bool bSupportUnicode = (er == erSuccess ? (lpecSession->GetCapabilities() & KOPANO_CAP_UNICODE) != 0 : false); \
  786. const ECStringCompat stringCompat(bSupportUnicode); \
  787. if(er != erSuccess) \
  788. goto __soapentry_exit; \
  789. soap_info(soap)->ulLastSessionId = ulSessionId; \
  790. soap_info(soap)->szFname = szFname; \
  791. lpecSession->AddBusyState(pthread_self(), #fname, soap_info(soap)->threadstart, soap_info(soap)->start);
  792. #define SOAP_ENTRY_FUNCTION_FOOTER \
  793. __soapentry_exit: \
  794. *lpResultVar = er; \
  795. clock_gettime(CLOCK_THREAD_CPUTIME_ID, &endTimes); \
  796. if(lpecSession) { \
  797. LOG_SOAP_DEBUG("%020" PRIu64 ": E %s 0x%08x %f %f", ulSessionId, szFname, er, timespec2dbl(endTimes) - timespec2dbl(startTimes), GetTimeOfDay() - dblStart); \
  798. lpecSession->UpdateBusyState(pthread_self(), SESSION_STATE_SENDING); \
  799. lpecSession->Unlock(); \
  800. } \
  801. return SOAP_OK;
  802. #define SOAP_ENTRY_START(fname,resultvar,...) \
  803. int ns__##fname(struct soap *soap, ULONG64 ulSessionId, ##__VA_ARGS__) \
  804. { \
  805. SOAP_ENTRY_FUNCTION_HEADER(resultvar, fname) \
  806. er = [&]() -> int {
  807. #define SOAP_ENTRY_END() \
  808. return er; \
  809. }(); \
  810. SOAP_ENTRY_FUNCTION_FOOTER \
  811. }
  812. #define ALLOC_DBRESULT() \
  813. DB_ROW UNUSED_VAR lpDBRow = NULL; \
  814. DB_LENGTHS UNUSED_VAR lpDBLen = NULL; \
  815. DB_RESULT UNUSED_VAR lpDBResult; \
  816. std::string UNUSED_VAR strQuery;
  817. #define USE_DATABASE() \
  818. ECDatabase* lpDatabase = NULL; \
  819. ALLOC_DBRESULT(); \
  820. \
  821. er = lpecSession->GetDatabase(&lpDatabase); \
  822. if (er != erSuccess) { \
  823. ec_log_err(" GetDatabase failed"); \
  824. return KCERR_DATABASE_ERROR; \
  825. }
  826. #define ROLLBACK_ON_ERROR() \
  827. if (lpDatabase && FAILED(er)) \
  828. lpDatabase->Rollback(); \
  829. static ECRESULT PurgeSoftDelete(ECSession *lpecSession,
  830. unsigned int ulLifetime, unsigned int *lpulMessages,
  831. unsigned int *lpulFolders, unsigned int *lpulStores, bool *lpbExit)
  832. {
  833. ECRESULT er = erSuccess;
  834. ECDatabase* lpDatabase = NULL;
  835. DB_RESULT lpDBResult;
  836. DB_ROW lpDBRow = NULL;
  837. std::string strQuery;
  838. FILETIME ft;
  839. unsigned int ulDeleteFlags = 0;
  840. time_t ulTime = 0;
  841. ECListInt lObjectIds;
  842. unsigned int ulFolders = 0, ulMessages = 0;
  843. unsigned int ulStores = 0;
  844. bool bExitDummy = false;
  845. if (g_bPurgeSoftDeleteStatus) {
  846. ec_log_err("Softdelete already running");
  847. er = KCERR_BUSY;
  848. goto exit;
  849. }
  850. g_bPurgeSoftDeleteStatus = TRUE;
  851. if (!lpbExit)
  852. lpbExit = &bExitDummy;
  853. er = lpecSession->GetDatabase(&lpDatabase);
  854. if (er != erSuccess)
  855. goto exit;
  856. // 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
  857. // and we really want to delete all the softdeleted items anyway.
  858. ulDeleteFlags = EC_DELETE_CONTAINER | EC_DELETE_FOLDERS | EC_DELETE_MESSAGES | EC_DELETE_RECIPIENTS | EC_DELETE_ATTACHMENTS | EC_DELETE_HARD_DELETE;
  859. GetSystemTimeAsFileTime(&ft);
  860. FileTimeToUnixTime(ft, &ulTime);
  861. ulTime -= ulLifetime;
  862. UnixTimeToFileTime(ulTime, &ft);
  863. // Select softdeleted stores (ignore softdelete_lifetime setting because a store can't be restored anyway)
  864. strQuery = "SELECT id FROM hierarchy WHERE parent IS NULL AND (flags&"+stringify(MSGFLAG_DELETED)+")="+stringify(MSGFLAG_DELETED)+" AND type="+stringify(MAPI_STORE);
  865. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  866. if(er != erSuccess)
  867. goto exit;
  868. ulStores = lpDatabase->GetNumRows(lpDBResult);
  869. if(ulStores > 0)
  870. {
  871. while( (lpDBRow = lpDatabase->FetchRow(lpDBResult)) )
  872. {
  873. if(lpDBRow == NULL || lpDBRow[0] == NULL)
  874. continue;
  875. lObjectIds.push_back(atoui(lpDBRow[0]));
  876. }
  877. // free before we call DeleteObjects()
  878. lpDBResult = DB_RESULT();
  879. if (*lpbExit) {
  880. er = KCERR_USER_CANCEL;
  881. goto exit;
  882. }
  883. ec_log_info("Start to purge %d stores", (int)lObjectIds.size());
  884. for (auto iterObjectId = lObjectIds.cbegin();
  885. iterObjectId != lObjectIds.cend() && !*lpbExit;
  886. ++iterObjectId)
  887. {
  888. ec_log_info(" purge store (%d)", *iterObjectId);
  889. er = DeleteObjects(lpecSession, lpDatabase, *iterObjectId, ulDeleteFlags|EC_DELETE_STORE, 0, false, false);
  890. if(er != erSuccess) {
  891. ec_log_err("Error while removing softdelete store objects, error code: 0x%x.", er);
  892. goto exit;
  893. }
  894. }
  895. ec_log_info("Store purge done");
  896. }
  897. if (*lpbExit) {
  898. er = KCERR_USER_CANCEL;
  899. goto exit;
  900. }
  901. // Select softdeleted folders
  902. 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);
  903. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  904. if(er != erSuccess)
  905. goto exit;
  906. ulFolders = lpDatabase->GetNumRows(lpDBResult);
  907. if(ulFolders > 0)
  908. {
  909. // Remove all items
  910. lObjectIds.clear();
  911. while( (lpDBRow = lpDatabase->FetchRow(lpDBResult)) )
  912. {
  913. if(lpDBRow == NULL || lpDBRow[0] == NULL)
  914. continue;
  915. lObjectIds.push_back(atoui(lpDBRow[0]));
  916. }
  917. // free before we call DeleteObjects()
  918. lpDBResult = DB_RESULT();
  919. if (*lpbExit) {
  920. er = KCERR_USER_CANCEL;
  921. goto exit;
  922. }
  923. ec_log_info("Start to purge %d folders", (int)lObjectIds.size());
  924. er = DeleteObjects(lpecSession, lpDatabase, &lObjectIds, ulDeleteFlags, 0, false, false);
  925. if(er != erSuccess) {
  926. ec_log_err("Error while removing softdelete folder objects, error code: 0x%x.", er);
  927. goto exit;
  928. }
  929. ec_log_info("Folder purge done");
  930. }
  931. if (*lpbExit) {
  932. er = KCERR_USER_CANCEL;
  933. goto exit;
  934. }
  935. // Select softdeleted messages
  936. 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);
  937. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  938. if(er != erSuccess)
  939. goto exit;
  940. ulMessages = lpDatabase->GetNumRows(lpDBResult);
  941. if(ulMessages > 0)
  942. {
  943. // Remove all items
  944. lObjectIds.clear();
  945. while( (lpDBRow = lpDatabase->FetchRow(lpDBResult)) )
  946. {
  947. if(lpDBRow == NULL || lpDBRow[0] == NULL)
  948. continue;
  949. lObjectIds.push_back(atoui(lpDBRow[0]));
  950. }
  951. // free before we call DeleteObjects()
  952. lpDBResult = DB_RESULT();
  953. if (*lpbExit) {
  954. er = KCERR_USER_CANCEL;
  955. goto exit;
  956. }
  957. ec_log_info("Start to purge %d messages", (int)lObjectIds.size());
  958. er = DeleteObjects(lpecSession, lpDatabase, &lObjectIds, ulDeleteFlags, 0, false, false);
  959. if(er != erSuccess) {
  960. ec_log_err("Error while removing softdelete message objects, error code: 0x%x.", er);
  961. goto exit;
  962. }
  963. ec_log_info("Message purge done");
  964. }
  965. // these stats are only from toplevel objects
  966. if(lpulFolders)
  967. *lpulFolders = ulFolders;
  968. if(lpulMessages)
  969. *lpulMessages = ulMessages;
  970. if (lpulStores)
  971. *lpulStores = ulStores;
  972. exit:
  973. if (er != KCERR_BUSY)
  974. g_bPurgeSoftDeleteStatus = FALSE;
  975. return er;
  976. }
  977. /**
  978. * getPublicStore: get the root entryid, the store entryid and the store GUID for the public store.
  979. * FIXME, GUID is duplicate
  980. */
  981. SOAP_ENTRY_START(getPublicStore, lpsResponse->er, unsigned int ulFlags, struct getStoreResponse *lpsResponse)
  982. {
  983. unsigned int ulCompanyId = 0;
  984. std::string strStoreName;
  985. std::string strStoreServer;
  986. std::string strServerPath;
  987. std::string strCompanyName;
  988. const std::string strThisServer = g_lpSessionManager->GetConfig()->GetSetting("server_name", "", "Unknown");
  989. objectdetails_t details;
  990. USE_DATABASE();
  991. if (lpecSession->GetSessionManager()->IsHostedSupported()) {
  992. /* Hosted support, Public store owner is company */
  993. er = lpecSession->GetSecurity()->GetUserCompany(&ulCompanyId);
  994. if (er != erSuccess)
  995. return er;
  996. er = lpecSession->GetUserManagement()->GetObjectDetails(ulCompanyId, &details);
  997. if(er != erSuccess)
  998. return er;
  999. strStoreServer = details.GetPropString(OB_PROP_S_SERVERNAME);
  1000. strCompanyName = details.GetPropString(OB_PROP_S_FULLNAME);
  1001. } else {
  1002. ulCompanyId = KOPANO_UID_EVERYONE; /* No hosted support, Public store owner is Everyone */
  1003. if (lpecSession->GetSessionManager()->IsDistributedSupported()) {
  1004. /*
  1005. * GetObjectDetailsAndSync will return the group details for EVERYONE when called
  1006. * with KOPANO_UID_EVERYONE. But we want the pseudo company for that contains the
  1007. * public store.
  1008. */
  1009. er = lpecSession->GetUserManagement()->GetPublicStoreDetails(&details);
  1010. if (er == KCERR_NO_SUPPORT) {
  1011. /* Not supported: No MultiServer with this plugin, so we're good */
  1012. strStoreServer = strThisServer;
  1013. er = erSuccess;
  1014. } else if (er == erSuccess)
  1015. strStoreServer = details.GetPropString(OB_PROP_S_SERVERNAME);
  1016. else
  1017. return er;
  1018. } else
  1019. strStoreServer = strThisServer;
  1020. }
  1021. if (lpecSession->GetSessionManager()->IsDistributedSupported())
  1022. {
  1023. if (strStoreServer.empty()) {
  1024. if (!strCompanyName.empty())
  1025. ec_log_err("Company \"%s\" has no home server for its public store.", strCompanyName.c_str());
  1026. else
  1027. ec_log_err("Public store has no home server.");
  1028. return KCERR_NOT_FOUND;
  1029. }
  1030. /* Do we own the store? */
  1031. if (strcasecmp(strThisServer.c_str(), strStoreServer.c_str()) != 0)
  1032. {
  1033. if ((ulFlags & EC_OVERRIDE_HOMESERVER) == 0) {
  1034. er = GetBestServerPath(soap, lpecSession, strStoreServer, &strServerPath);
  1035. if (er != erSuccess)
  1036. return er;
  1037. lpsResponse->lpszServerPath = STROUT_FIX_CPY(strServerPath.c_str());
  1038. ec_log_info("Redirecting request to \"%s\"", lpsResponse->lpszServerPath);
  1039. g_lpStatsCollector->Increment(SCN_REDIRECT_COUNT, 1);
  1040. return KCERR_UNABLE_TO_COMPLETE;
  1041. }
  1042. }
  1043. }
  1044. /*
  1045. * The public store is stored in the database with the companyid as owner.
  1046. */
  1047. strQuery =
  1048. "SELECT hierarchy.id, stores.guid, stores.hierarchy_id "
  1049. "FROM stores "
  1050. "JOIN hierarchy on stores.hierarchy_id=hierarchy.parent "
  1051. "WHERE stores.user_id = " + stringify(ulCompanyId) + " LIMIT 1";
  1052. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  1053. if(er != erSuccess)
  1054. return er;
  1055. if (lpDatabase->GetNumRows(lpDBResult) == 0)
  1056. return KCERR_NOT_FOUND;
  1057. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  1058. lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
  1059. if( lpDBRow == NULL || lpDBRow[0] == NULL || lpDBRow[1] == NULL || lpDBRow[2] == NULL ||
  1060. lpDBLen == NULL || lpDBLen[1] == 0)
  1061. {
  1062. ec_log_err("getPublicStore(): no rows from db");
  1063. return KCERR_DATABASE_ERROR; // this should never happen
  1064. }
  1065. er = g_lpSessionManager->GetCacheManager()->GetEntryIdFromObject(atoui(lpDBRow[0]), soap, 0, &lpsResponse->sRootId);
  1066. if(er != erSuccess)
  1067. return er;
  1068. if ((ulFlags & EC_OVERRIDE_HOMESERVER) == 0)
  1069. lpsResponse->lpszServerPath = STROUT_FIX_CPY(string("pseudo://" + strStoreServer).c_str());
  1070. er = g_lpSessionManager->GetCacheManager()->GetEntryIdFromObject(atoui(lpDBRow[2]), soap, ulFlags, &lpsResponse->sStoreId);
  1071. if(er != erSuccess)
  1072. return er;
  1073. lpsResponse->guid.__size= lpDBLen[1];
  1074. lpsResponse->guid.__ptr = s_alloc<unsigned char>(soap, lpDBLen[1]);
  1075. memcpy(lpsResponse->guid.__ptr, lpDBRow[1], lpDBLen[1]);
  1076. return erSuccess;
  1077. }
  1078. SOAP_ENTRY_END()
  1079. /**
  1080. * getStore: get the root entryid, the store entryid and the GUID of a store specified with lpsEntryId
  1081. * FIXME: output store entryid equals input store entryid ?
  1082. * FIXME: output GUID is also in entryid
  1083. */
  1084. SOAP_ENTRY_START(getStore, lpsResponse->er, entryId* lpsEntryId, struct getStoreResponse *lpsResponse)
  1085. {
  1086. unsigned int ulStoreId = 0;
  1087. unsigned int ulUserId = 0;
  1088. objectdetails_t sUserDetails;
  1089. string strServerName;
  1090. string strServerPath;
  1091. USE_DATABASE();
  1092. if (!lpsEntryId) {
  1093. ulUserId = lpecSession->GetSecurity()->GetUserId();
  1094. // Check if the store should be available on this server
  1095. if (lpecSession->GetSessionManager()->IsDistributedSupported() &&
  1096. !lpecSession->GetUserManagement()->IsInternalObject(ulUserId)) {
  1097. er = lpecSession->GetUserManagement()->GetObjectDetails(ulUserId, &sUserDetails);
  1098. if (er != erSuccess)
  1099. return er;
  1100. strServerName = sUserDetails.GetPropString(OB_PROP_S_SERVERNAME);
  1101. if (strServerName.empty())
  1102. return er;
  1103. if (strcasecmp(strServerName.c_str(), g_lpSessionManager->GetConfig()->GetSetting("server_name")) != 0) {
  1104. er = GetBestServerPath(soap, lpecSession, strServerName, &strServerPath);
  1105. if (er != erSuccess)
  1106. return er;
  1107. lpsResponse->lpszServerPath = STROUT_FIX_CPY(strServerPath.c_str());
  1108. ec_log_info("Redirecting request to \"%s\"", lpsResponse->lpszServerPath);
  1109. g_lpStatsCollector->Increment(SCN_REDIRECT_COUNT, 1);
  1110. return KCERR_UNABLE_TO_COMPLETE;
  1111. }
  1112. }
  1113. }
  1114. // If strServerName is empty, we're not running in distributed mode or we're dealing
  1115. // with a local account. Just use the name from the configuration.
  1116. if (strServerName.empty())
  1117. strServerName = g_lpSessionManager->GetConfig()->GetSetting("server_name" ,"", "Unknown");
  1118. // Always return a pseudo URL
  1119. lpsResponse->lpszServerPath = STROUT_FIX_CPY(string("pseudo://" + strServerName).c_str());
  1120. strQuery = "SELECT hierarchy.id, stores.guid, stores.hierarchy_id "
  1121. "FROM stores join hierarchy on stores.hierarchy_id=hierarchy.parent ";
  1122. if(lpsEntryId) {
  1123. er = lpecSession->GetObjectFromEntryId(lpsEntryId, &ulStoreId);
  1124. if(er != erSuccess)
  1125. return er;
  1126. strQuery += "WHERE stores.hierarchy_id=" + stringify(ulStoreId);// FIXME: mysql query
  1127. }else {
  1128. strQuery += "WHERE stores.user_id=" + stringify(ulUserId)
  1129. + " AND stores.type=" + stringify(ECSTORE_TYPE_PRIVATE);
  1130. }
  1131. strQuery += " LIMIT 1";
  1132. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  1133. if(er != erSuccess)
  1134. return er;
  1135. if (lpDatabase->GetNumRows(lpDBResult) == 0)
  1136. return KCERR_NOT_FOUND;
  1137. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  1138. if (lpDBRow == NULL) {
  1139. ec_log_err("getStore(): no rows from db");
  1140. return KCERR_DATABASE_ERROR; // this should never happen
  1141. }
  1142. lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
  1143. /*
  1144. * Avoid processing SQL NULL, or memory blocks that are not
  1145. * NUL-terminated.
  1146. * Ensure the GUID (lpDBRow[1]) is not empty. (Perhaps check for
  1147. * !=16 instead of ==0?)
  1148. */
  1149. if (lpDBLen == NULL || lpDBRow[0] == NULL ||
  1150. lpDBRow[1] == NULL || lpDBRow[2] == NULL ||
  1151. memchr(lpDBRow[0], '\0', lpDBLen[0] + 1) == NULL ||
  1152. memchr(lpDBRow[2], '\0', lpDBLen[2] + 1) == NULL ||
  1153. lpDBLen[1] == 0) {
  1154. ec_log_err("getStore(): received trash rows from db");
  1155. return KCERR_DATABASE_ERROR;
  1156. }
  1157. er = g_lpSessionManager->GetCacheManager()->GetEntryIdFromObject(atoui(lpDBRow[0]), soap, 0, &lpsResponse->sRootId);
  1158. if(er != erSuccess)
  1159. return er;
  1160. er = g_lpSessionManager->GetCacheManager()->GetEntryIdFromObject(atoui(lpDBRow[2]), soap, 0, &lpsResponse->sStoreId);
  1161. if(er != erSuccess)
  1162. return er;
  1163. lpsResponse->guid.__size= lpDBLen[1];
  1164. lpsResponse->guid.__ptr = s_alloc<unsigned char>(soap, lpDBLen[1]);
  1165. memcpy(lpsResponse->guid.__ptr, lpDBRow[1], lpDBLen[1]);
  1166. return erSuccess;
  1167. }
  1168. SOAP_ENTRY_END()
  1169. /**
  1170. * getStoreName: get the PR_DISPLAY_NAME of the store specified in sEntryId
  1171. */
  1172. SOAP_ENTRY_START(getStoreName, lpsResponse->er, entryId sEntryId, struct getStoreNameResponse *lpsResponse)
  1173. {
  1174. unsigned int ulObjId = 0;
  1175. unsigned int ulStoreType = 0;
  1176. er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulObjId);
  1177. if(er != erSuccess)
  1178. return er;
  1179. er = lpecSession->GetSessionManager()->GetCacheManager()->GetStoreAndType(ulObjId, NULL, NULL, &ulStoreType);
  1180. if (er != erSuccess)
  1181. return er;
  1182. er = ECGenProps::GetStoreName(soap, lpecSession, ulObjId, ulStoreType, &lpsResponse->lpszStoreName);
  1183. if(er != erSuccess)
  1184. return er;
  1185. if (!bSupportUnicode)
  1186. lpsResponse->lpszStoreName = STROUT_FIX(lpsResponse->lpszStoreName);
  1187. return erSuccess;
  1188. }
  1189. SOAP_ENTRY_END()
  1190. SOAP_ENTRY_START(getStoreType, lpsResponse->er, entryId sEntryId, struct getStoreTypeResponse *lpsResponse)
  1191. {
  1192. unsigned int ulObjId = 0;
  1193. er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulObjId);
  1194. if (er != erSuccess)
  1195. return er;
  1196. return lpecSession->GetSessionManager()->GetCacheManager()->
  1197. GetStoreAndType(ulObjId, nullptr, nullptr,
  1198. &lpsResponse->ulStoreType);
  1199. }
  1200. SOAP_ENTRY_END()
  1201. static ECRESULT ReadProps(struct soap *soap, ECSession *lpecSession,
  1202. unsigned int ulObjId, unsigned ulObjType, unsigned int ulObjTypeParent,
  1203. CHILDPROPS sChildProps, struct propTagArray *lpsPropTag,
  1204. struct propValArray *lpsPropVal)
  1205. {
  1206. ECRESULT er = erSuccess;
  1207. quotadetails_t sDetails;
  1208. unsigned int ulCompanyId = 0;
  1209. unsigned int ulStoreOwner = 0;
  1210. object_ptr<ECAttachmentStorage> lpAttachmentStorage;
  1211. struct propVal sPropVal;
  1212. ECStringCompat stringCompat(lpecSession->GetCapabilities() & KOPANO_CAP_UNICODE);
  1213. USE_DATABASE();
  1214. if(ulObjType == MAPI_STORE) //fimxe: except public stores
  1215. {
  1216. if (ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_USER_NAME, ulObjId, 0, ulObjId, 0, ulObjType, &sPropVal) == erSuccess) {
  1217. er = FixPropEncoding(soap, stringCompat, Out, &sPropVal);
  1218. if (er != erSuccess)
  1219. return er;
  1220. sChildProps.lpPropTags->AddPropTag(sPropVal.ulPropTag);
  1221. sChildProps.lpPropVals->AddPropVal(sPropVal);
  1222. }
  1223. if (ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_USER_ENTRYID, ulObjId, 0, ulObjId, 0, ulObjType, &sPropVal) == erSuccess) {
  1224. sChildProps.lpPropTags->AddPropTag(PR_USER_ENTRYID);
  1225. sChildProps.lpPropVals->AddPropVal(sPropVal);
  1226. }
  1227. er = lpecSession->GetSecurity()->GetStoreOwner(ulObjId, &ulStoreOwner);
  1228. if (er != erSuccess)
  1229. return er;
  1230. // Quota information
  1231. if (lpecSession->GetSecurity()->GetUserQuota(ulStoreOwner, false, &sDetails) == erSuccess)
  1232. {
  1233. // PR_QUOTA_WARNING_THRESHOLD
  1234. sPropVal.ulPropTag = PR_QUOTA_WARNING_THRESHOLD;
  1235. sPropVal.__union = SOAP_UNION_propValData_ul;
  1236. sPropVal.Value.ul = (unsigned long)(sDetails.llWarnSize / 1024);
  1237. sChildProps.lpPropTags->AddPropTag(PR_QUOTA_WARNING_THRESHOLD);
  1238. sChildProps.lpPropVals->AddPropVal(sPropVal);
  1239. // PR_QUOTA_SEND_THRESHOLD
  1240. sPropVal.ulPropTag = PR_QUOTA_SEND_THRESHOLD;
  1241. sPropVal.__union = SOAP_UNION_propValData_ul;
  1242. sPropVal.Value.ul = (unsigned long)(sDetails.llSoftSize / 1024);
  1243. sChildProps.lpPropTags->AddPropTag(PR_QUOTA_SEND_THRESHOLD);
  1244. sChildProps.lpPropVals->AddPropVal(sPropVal);
  1245. // PR_QUOTA_RECEIVE_THRESHOLD
  1246. sPropVal.ulPropTag = PR_QUOTA_RECEIVE_THRESHOLD;
  1247. sPropVal.__union = SOAP_UNION_propValData_ul;
  1248. sPropVal.Value.ul = (unsigned long)(sDetails.llHardSize / 1024);
  1249. sChildProps.lpPropTags->AddPropTag(PR_QUOTA_RECEIVE_THRESHOLD);
  1250. sChildProps.lpPropVals->AddPropVal(sPropVal);
  1251. }
  1252. if (lpecSession->GetCapabilities() & KOPANO_CAP_MAILBOX_OWNER) {
  1253. // get the companyid to which the logged in user belongs to.
  1254. er = lpecSession->GetSecurity()->GetUserCompany(&ulCompanyId);
  1255. if (er != erSuccess)
  1256. return er;
  1257. // 5.0 client knows how to handle the PR_MAILBOX_OWNER_* properties
  1258. if(ulStoreOwner != KOPANO_UID_EVERYONE && ulStoreOwner != ulCompanyId ) {
  1259. if (ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_MAILBOX_OWNER_NAME, ulObjId, 0, ulObjId, 0, ulObjType, &sPropVal) == erSuccess) {
  1260. er = FixPropEncoding(soap, stringCompat, Out, &sPropVal);
  1261. if (er != erSuccess)
  1262. return er;
  1263. sChildProps.lpPropTags->AddPropTag(sPropVal.ulPropTag);
  1264. sChildProps.lpPropVals->AddPropVal(sPropVal);
  1265. }
  1266. // Add PR_MAILBOX_OWNER_ENTRYID
  1267. if (ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_MAILBOX_OWNER_ENTRYID, ulObjId, 0, ulObjId, 0, ulObjType, &sPropVal) == erSuccess) {
  1268. sChildProps.lpPropTags->AddPropTag(PR_MAILBOX_OWNER_ENTRYID);
  1269. sChildProps.lpPropVals->AddPropVal(sPropVal);
  1270. }
  1271. }
  1272. }
  1273. // Add PR_DISPLAY_NAME
  1274. if (ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_DISPLAY_NAME, ulObjId, 0, ulObjId, 0, ulObjType, &sPropVal) == erSuccess) {
  1275. er = FixPropEncoding(soap, stringCompat, Out, &sPropVal);
  1276. if (er != erSuccess)
  1277. return er;
  1278. sChildProps.lpPropTags->AddPropTag(sPropVal.ulPropTag);
  1279. sChildProps.lpPropVals->AddPropVal(sPropVal);
  1280. }
  1281. if(ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_MAPPING_SIGNATURE, ulObjId, 0, 0, 0, ulObjType, &sPropVal) == erSuccess) {
  1282. sChildProps.lpPropTags->AddPropTag(PR_MAPPING_SIGNATURE);
  1283. sChildProps.lpPropVals->AddPropVal(sPropVal);
  1284. }
  1285. if (!sChildProps.lpPropTags->HasPropTag(PR_SORT_LOCALE_ID)) {
  1286. sPropVal.__union = SOAP_UNION_propValData_ul;
  1287. sPropVal.ulPropTag = PR_SORT_LOCALE_ID;
  1288. sPropVal.Value.ul = lpecSession->GetSessionManager()->GetSortLCID(ulObjId);
  1289. sChildProps.lpPropTags->AddPropTag(PR_SORT_LOCALE_ID);
  1290. sChildProps.lpPropVals->AddPropVal(sPropVal);
  1291. }
  1292. }
  1293. //PR_PARENT_SOURCE_KEY for folders and messages
  1294. if(ulObjType == MAPI_FOLDER || (ulObjType == MAPI_MESSAGE && ulObjTypeParent == MAPI_FOLDER))
  1295. {
  1296. if(ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_PARENT_SOURCE_KEY, ulObjId, 0, 0, 0, ulObjType, &sPropVal) == erSuccess)
  1297. {
  1298. sChildProps.lpPropTags->AddPropTag(sPropVal.ulPropTag);
  1299. sChildProps.lpPropVals->AddPropVal(sPropVal);
  1300. }
  1301. if(ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_SOURCE_KEY, ulObjId, 0, 0, 0, ulObjType, &sPropVal) == erSuccess)
  1302. {
  1303. sChildProps.lpPropTags->AddPropTag(sPropVal.ulPropTag);
  1304. sChildProps.lpPropVals->AddPropVal(sPropVal);
  1305. }
  1306. }
  1307. if (ulObjType == MAPI_MESSAGE || ulObjType == MAPI_ATTACH) {
  1308. ULONG ulPropTag = (ulObjType == MAPI_MESSAGE ? PR_EC_IMAP_EMAIL : PR_ATTACH_DATA_BIN);
  1309. er = CreateAttachmentStorage(lpDatabase, &~lpAttachmentStorage);
  1310. if (er != erSuccess)
  1311. return er;
  1312. if (lpAttachmentStorage->ExistAttachment(ulObjId, PROP_ID(ulPropTag)))
  1313. sChildProps.lpPropTags->AddPropTag(ulPropTag);
  1314. }
  1315. if ((ulObjType == MAPI_MAILUSER || ulObjType == MAPI_DISTLIST) &&
  1316. ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_INSTANCE_KEY, ulObjId, 0, 0, 0, ulObjType, &sPropVal) == erSuccess) {
  1317. sChildProps.lpPropTags->AddPropTag(sPropVal.ulPropTag);
  1318. sChildProps.lpPropVals->AddPropVal(sPropVal);
  1319. }
  1320. // Set the PR_RECORD_KEY
  1321. if ((ulObjType != MAPI_ATTACH || !sChildProps.lpPropTags->HasPropTag(PR_RECORD_KEY)) &&
  1322. ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_RECORD_KEY, ulObjId, 0, 0, 0, ulObjType, &sPropVal) == erSuccess) {
  1323. sChildProps.lpPropTags->AddPropTag(sPropVal.ulPropTag);
  1324. sChildProps.lpPropVals->AddPropVal(sPropVal);
  1325. }
  1326. if (ulObjType == MAPI_FOLDER || ulObjType == MAPI_STORE || ulObjType == MAPI_MESSAGE) {
  1327. // Get PARENT_ENTRYID
  1328. strQuery = "SELECT hierarchy.parent,hierarchy.flags,hierarchy.type FROM hierarchy WHERE hierarchy.id="+stringify(ulObjId) + " LIMIT 1";
  1329. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  1330. if(er != erSuccess)
  1331. return er;
  1332. if(lpDatabase->GetNumRows(lpDBResult) > 0)
  1333. {
  1334. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  1335. if(lpDBRow && lpDBRow[0]) {
  1336. if(ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_PARENT_ENTRYID, ulObjId, 0, 0, 0, ulObjType, &sPropVal) == erSuccess)
  1337. {
  1338. sChildProps.lpPropTags->AddPropTag(sPropVal.ulPropTag);
  1339. sChildProps.lpPropVals->AddPropVal(sPropVal);
  1340. }
  1341. }
  1342. if(lpDBRow && lpDBRow[1] && lpDBRow[2] && atoi(lpDBRow[2]) == 3) {
  1343. // PR_RIGHTS
  1344. if (ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_RIGHTS, ulObjId, 0, 0, 0, ulObjType, &sPropVal) == erSuccess) {
  1345. sChildProps.lpPropTags->AddPropTag(sPropVal.ulPropTag);
  1346. sChildProps.lpPropVals->AddPropVal(sPropVal);
  1347. }
  1348. }
  1349. // Set the flags PR_ACCESS and PR_ACCESS_LEVEL
  1350. if(lpDBRow && lpDBRow[0] && lpDBRow[2] && (atoi(lpDBRow[2]) == 3 || atoi(lpDBRow[2]) == 5) && lpDBRow[1])
  1351. {
  1352. if (ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_ACCESS, ulObjId, 0, 0, 0, ulObjType, &sPropVal) == erSuccess) {
  1353. sChildProps.lpPropTags->AddPropTag(sPropVal.ulPropTag);
  1354. sChildProps.lpPropVals->AddPropVal(sPropVal);
  1355. }
  1356. }
  1357. if(lpDBRow && lpDBRow[2] && lpDBRow[1])
  1358. {
  1359. if (ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_ACCESS_LEVEL, ulObjId, 0, 0, 0, ulObjType, &sPropVal) == erSuccess) {
  1360. sChildProps.lpPropTags->AddPropTag(sPropVal.ulPropTag);
  1361. sChildProps.lpPropVals->AddPropVal(sPropVal);
  1362. }
  1363. }
  1364. }
  1365. }
  1366. er = sChildProps.lpPropTags->GetPropTagArray(lpsPropTag);
  1367. if(er != erSuccess)
  1368. return er;
  1369. return sChildProps.lpPropVals->GetPropValArray(lpsPropVal);
  1370. }
  1371. /**
  1372. * loadProp: Reads a single, large property from the database. No size limit.
  1373. * this can also be a complete attachment
  1374. */
  1375. SOAP_ENTRY_START(loadProp, lpsResponse->er, entryId sEntryId, unsigned int ulObjId, unsigned int ulPropTag, struct loadPropResponse *lpsResponse)
  1376. {
  1377. object_ptr<ECAttachmentStorage> lpAttachmentStorage;
  1378. USE_DATABASE();
  1379. if(ulObjId == 0) {
  1380. er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulObjId);
  1381. if (er != erSuccess)
  1382. goto exit;
  1383. }
  1384. // Check permission
  1385. er = lpecSession->GetSecurity()->CheckPermission(ulObjId, ecSecurityRead);
  1386. if(er != erSuccess)
  1387. goto exit;
  1388. er = lpDatabase->Begin();
  1389. if(er != erSuccess)
  1390. goto exit;
  1391. if (ulPropTag != PR_ATTACH_DATA_BIN && ulPropTag != PR_EC_IMAP_EMAIL) {
  1392. if (ulPropTag & MV_FLAG)
  1393. strQuery = "SELECT " + (std::string)MVPROPCOLORDER + " FROM mvproperties WHERE hierarchyid="+stringify(ulObjId)+ " AND tag = " + stringify(PROP_ID(ulPropTag))+" GROUP BY hierarchyid, tag";
  1394. else
  1395. strQuery = "SELECT " PROPCOLORDER " FROM properties WHERE hierarchyid = " + stringify(ulObjId) + " AND tag = " + stringify(PROP_ID(ulPropTag));
  1396. strQuery += " LIMIT 2";
  1397. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  1398. if(er != erSuccess)
  1399. goto exit;
  1400. if(lpDatabase->GetNumRows(lpDBResult) != 1) {
  1401. er = KCERR_NOT_FOUND;
  1402. goto exit;
  1403. }
  1404. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  1405. lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
  1406. if (lpDBRow == NULL || lpDBLen == NULL)
  1407. {
  1408. er = KCERR_DATABASE_ERROR;
  1409. ec_log_err("loadProp(): no rows from db");
  1410. goto exit;
  1411. }
  1412. lpsResponse->lpPropVal = s_alloc<propVal>(soap);
  1413. memset(lpsResponse->lpPropVal,0,sizeof(struct propVal));
  1414. er = CopyDatabasePropValToSOAPPropVal(soap, lpDBRow, lpDBLen, lpsResponse->lpPropVal);
  1415. if (er != erSuccess)
  1416. goto exit;
  1417. } else {
  1418. lpsResponse->lpPropVal = s_alloc<propVal>(soap);
  1419. memset(lpsResponse->lpPropVal,0,sizeof(struct propVal));
  1420. lpsResponse->lpPropVal->ulPropTag = ulPropTag;
  1421. lpsResponse->lpPropVal->__union = SOAP_UNION_propValData_bin;
  1422. lpsResponse->lpPropVal->Value.bin = s_alloc<struct xsd__base64Binary>(soap);
  1423. memset(lpsResponse->lpPropVal->Value.bin, 0, sizeof(struct xsd__base64Binary));
  1424. er = CreateAttachmentStorage(lpDatabase, &~lpAttachmentStorage);
  1425. if (er != erSuccess)
  1426. goto exit;
  1427. size_t atsize = 0;
  1428. er = lpAttachmentStorage->LoadAttachment(soap, ulObjId, PROP_ID(ulPropTag), &atsize, &lpsResponse->lpPropVal->Value.bin->__ptr);
  1429. lpsResponse->lpPropVal->Value.bin->__size = atsize;
  1430. if (er != erSuccess)
  1431. goto exit;
  1432. }
  1433. er = lpDatabase->Commit();
  1434. if(er != erSuccess)
  1435. goto exit;
  1436. if (!bSupportUnicode) {
  1437. er = FixPropEncoding(soap, stringCompat, Out, lpsResponse->lpPropVal);
  1438. if (er != erSuccess)
  1439. goto exit;
  1440. }
  1441. exit:
  1442. ROLLBACK_ON_ERROR();
  1443. }
  1444. SOAP_ENTRY_END()
  1445. //TODO: flag to get size of normal folder or deleted folders
  1446. static ECRESULT GetFolderSize(ECDatabase *lpDatabase, unsigned int ulFolderId,
  1447. long long *lpllFolderSize)
  1448. {
  1449. ECRESULT er = erSuccess;
  1450. DB_RESULT lpDBResult;
  1451. DB_ROW lpDBRow = NULL;
  1452. long long llSize = 0;
  1453. long long llSubSize = 0;
  1454. std::string strQuery;
  1455. // sum size of all messages in a folder
  1456. 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);
  1457. // except the deleted items!
  1458. strQuery += " AND h.flags & " + stringify(MSGFLAG_DELETED)+ "=0";
  1459. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  1460. if(er != erSuccess)
  1461. return er;
  1462. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  1463. if(lpDBRow == NULL || lpDBRow[0] == NULL)
  1464. llSize = 0;
  1465. else
  1466. llSize = atoll(lpDBRow[0]);
  1467. // Get the subfolders
  1468. strQuery = "SELECT id FROM hierarchy WHERE parent=" + stringify(ulFolderId) + " AND type="+stringify(MAPI_FOLDER);
  1469. // except the deleted items!
  1470. strQuery += " AND flags & " + stringify(MSGFLAG_DELETED)+ "=0";
  1471. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  1472. if(er != erSuccess)
  1473. return er;
  1474. if(lpDatabase->GetNumRows(lpDBResult) > 0)
  1475. {
  1476. // Walk through the folder list
  1477. while( (lpDBRow = lpDatabase->FetchRow(lpDBResult)) )
  1478. {
  1479. if(lpDBRow[0] == NULL)
  1480. continue; //Skip item
  1481. er = GetFolderSize(lpDatabase, atoi(lpDBRow[0]), &llSubSize);
  1482. if(er != erSuccess)
  1483. return er;
  1484. llSize += llSubSize;
  1485. }
  1486. }
  1487. *lpllFolderSize = llSize;
  1488. return erSuccess;
  1489. }
  1490. /**
  1491. * Write properties to an object
  1492. *
  1493. * @param[in] soap
  1494. * @param[in] lpecSession Pointer to the session of the caller; Cannot be NULL.
  1495. * @param[in] lpDatabase Pointer to the database handler of the caller; Cannot be NULL.
  1496. * @param[in] lpAttachmentStorage Pointer to an attachment storage object; Cannot be NULL.
  1497. * @param[in] lpsSaveObj Data object which include the new propery information; Cannot be NULL.
  1498. * @param[in] ulObjId Identify the database object to write the property data to the database.
  1499. * @param[in] fNewItem false for an existing object, true for a new object.
  1500. * @param[in] ulSyncId Client sync identifier.
  1501. * @param[out] lpsReturnObj
  1502. * @param[out] lpfHaveChangeKey
  1503. * @param[out] lpftCreated Receives create time if new object (can be now or passed value in lpsSaveObj)
  1504. * @param[out] lpftModified Receives modification time of written object (can be now or passed value in lpsSaveObj)
  1505. *
  1506. * \remarks
  1507. * Check the permissions and quota before you call this function.
  1508. *
  1509. * @todo unclear comment -> sync id only to saveObject !
  1510. */
  1511. static ECRESULT WriteProps(struct soap *soap, ECSession *lpecSession,
  1512. ECDatabase *lpDatabase, ECAttachmentStorage *lpAttachmentStorage,
  1513. struct saveObject *lpsSaveObj, unsigned int ulObjId, bool fNewItem,
  1514. unsigned int ulSyncId, struct saveObject *lpsReturnObj,
  1515. bool *lpfHaveChangeKey, FILETIME *lpftCreated, FILETIME *lpftModified)
  1516. {
  1517. ECRESULT er;
  1518. std::string strInsertQuery;
  1519. std::string strColName;
  1520. std::string strQuery;
  1521. struct propValArray *lpPropValArray = &lpsSaveObj->modProps;
  1522. unsigned int ulParent = 0;
  1523. unsigned int ulGrandParent = 0;
  1524. unsigned int ulParentType = 0;
  1525. unsigned int ulObjType = 0;
  1526. unsigned int ulOwner = 0;
  1527. unsigned int ulFlags = 0;
  1528. unsigned int ulAffected = 0;
  1529. unsigned int ulPropInserts = 0;
  1530. gsoap_size_t nMVItems;
  1531. unsigned long long ullIMAP = 0;
  1532. std::set<unsigned int> setInserted;
  1533. GUID sGuidServer;
  1534. std::list<ULONG> lstObjIds;
  1535. ULONG ulInstanceId = 0;
  1536. ULONG ulInstanceTag = 0;
  1537. bool bAttachmentStored = false;
  1538. entryId sUserId;
  1539. std::string strUsername;
  1540. ECStringCompat stringCompat(lpecSession->GetCapabilities() & KOPANO_CAP_UNICODE);
  1541. std::string strColData;
  1542. std::string strInsert;
  1543. std::string strInsertTProp;
  1544. SOURCEKEY sSourceKey;
  1545. SOURCEKEY sParentSourceKey;
  1546. DB_RESULT lpDBResult;
  1547. if (lpAttachmentStorage == nullptr)
  1548. return KCERR_INVALID_PARAMETER;
  1549. if (lpfHaveChangeKey)
  1550. *lpfHaveChangeKey = false;
  1551. er = g_lpSessionManager->GetServerGUID(&sGuidServer);
  1552. if (er != erSuccess)
  1553. return er;
  1554. er = g_lpSessionManager->GetCacheManager()->GetObject(ulObjId, &ulParent, &ulOwner, &ulFlags, &ulObjType);
  1555. if (er != erSuccess)
  1556. return er;
  1557. if(ulObjType != MAPI_STORE){
  1558. er = g_lpSessionManager->GetCacheManager()->GetObject(ulParent, &ulGrandParent, NULL, NULL, &ulParentType);
  1559. if(er != erSuccess)
  1560. return er;
  1561. }
  1562. if(ulObjType == MAPI_FOLDER && !ulSyncId) {
  1563. for (gsoap_size_t i = 0; i < lpPropValArray->__size; ++i) {
  1564. // Check whether the requested folder name already exists
  1565. if(lpPropValArray->__ptr[i].ulPropTag == PR_DISPLAY_NAME ) {
  1566. if(lpPropValArray->__ptr[i].Value.lpszA == NULL)
  1567. break; // Name property found, but name isn't present. This is broken, so skip this.
  1568. 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";
  1569. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  1570. if(er != erSuccess) {
  1571. ec_log_err("WriteProps(): DoSelect failed %x", er);
  1572. return er;
  1573. }
  1574. if(lpDatabase->GetNumRows(lpDBResult) > 0) {
  1575. ec_log_err("WriteProps(): Folder already exists while putting folder");
  1576. return KCERR_COLLISION;
  1577. }
  1578. break;
  1579. }
  1580. }// for(...)
  1581. }
  1582. // If we have a recipient, remove the old one. The client does not send a diff of props because ECMemTable cannot do this
  1583. if (!fNewItem && (ulObjType == MAPI_MAILUSER || ulObjType == MAPI_DISTLIST)) {
  1584. strQuery = "DELETE FROM properties WHERE hierarchyid=" + stringify(ulObjId);
  1585. er = lpDatabase->DoDelete(strQuery);
  1586. if(er != erSuccess)
  1587. return er;
  1588. strQuery = "DELETE FROM mvproperties WHERE hierarchyid=" + stringify (ulObjId);
  1589. er = lpDatabase->DoDelete(strQuery);
  1590. if(er != erSuccess)
  1591. return er;
  1592. if (ulObjType != lpsSaveObj->ulObjType) {
  1593. strQuery = "UPDATE hierarchy SET type=" +stringify(lpsSaveObj->ulObjType) +" WHERE id=" + stringify (ulObjId);
  1594. er = lpDatabase->DoUpdate(strQuery);
  1595. if(er != erSuccess)
  1596. return er;
  1597. // Switch the type so the cache will be updated at the end.
  1598. ulObjType = lpsSaveObj->ulObjType;
  1599. }
  1600. }
  1601. /* FIXME: Support multiple InstanceIds */
  1602. if (lpsSaveObj->lpInstanceIds && lpsSaveObj->lpInstanceIds->__size) {
  1603. GUID sGuidTmp;
  1604. if (lpsSaveObj->lpInstanceIds->__size > 1)
  1605. return KCERR_UNKNOWN_INSTANCE_ID;
  1606. er = SIEntryIDToID(&lpsSaveObj->lpInstanceIds->__ptr[0], &sGuidTmp, (unsigned int*)&ulInstanceId, (unsigned int*)&ulInstanceTag);
  1607. if (er != erSuccess)
  1608. return er;
  1609. /* Server GUID must always match */
  1610. if (memcmp(&sGuidTmp, &sGuidServer, sizeof(sGuidTmp)) != 0)
  1611. return KCERR_UNKNOWN_INSTANCE_ID;
  1612. /* The attachment should at least exist */
  1613. if (!lpAttachmentStorage->ExistAttachmentInstance(ulInstanceId))
  1614. return KCERR_UNKNOWN_INSTANCE_ID;
  1615. /*
  1616. * Check if we have access to the instance which is being referenced,
  1617. * a user has access to an instance when he is administrator or owns at
  1618. * least one reference to the instance.
  1619. * For security we won't announce the difference between not finding the instance
  1620. * or not having access to it.
  1621. */
  1622. if (lpecSession->GetSecurity()->GetAdminLevel() != ADMIN_LEVEL_SYSADMIN) {
  1623. er = lpAttachmentStorage->GetSingleInstanceParents(ulInstanceId, &lstObjIds);
  1624. if (er != erSuccess)
  1625. return er;
  1626. er = KCERR_UNKNOWN_INSTANCE_ID;
  1627. for (const auto i : lstObjIds)
  1628. if (lpecSession->GetSecurity()->CheckPermission(i, ecSecurityRead) == erSuccess) {
  1629. er = erSuccess;
  1630. break;
  1631. }
  1632. if (er != erSuccess)
  1633. return er;
  1634. }
  1635. er = lpAttachmentStorage->SaveAttachment(ulObjId, ulInstanceTag, !fNewItem, ulInstanceId, &ulInstanceId);
  1636. if (er != erSuccess)
  1637. return er;
  1638. lpsReturnObj->lpInstanceIds = s_alloc<entryList>(soap);
  1639. lpsReturnObj->lpInstanceIds->__size = 1;
  1640. lpsReturnObj->lpInstanceIds->__ptr = s_alloc<entryId>(soap, lpsReturnObj->lpInstanceIds->__size);
  1641. er = SIIDToEntryID(soap, &sGuidServer, ulInstanceId, ulInstanceTag, &lpsReturnObj->lpInstanceIds->__ptr[0]);
  1642. if (er != erSuccess) {
  1643. lpsReturnObj->lpInstanceIds = NULL;
  1644. return er;
  1645. }
  1646. /* Either by instanceid or attachment data, we have stored the attachment */
  1647. bAttachmentStored = true;
  1648. }
  1649. // Write the properties
  1650. for (gsoap_size_t i = 0; i < lpPropValArray->__size; ++i) {
  1651. // Check if we already inserted this property tag. We only accept the first.
  1652. auto iterInserted = setInserted.find(lpPropValArray->__ptr[i].ulPropTag);
  1653. if (iterInserted != setInserted.cend())
  1654. continue;
  1655. // Check if we actually need to write this property. The client may send us properties
  1656. // that we generate, so we don't need to save them
  1657. if(ECGenProps::IsPropRedundant(lpPropValArray->__ptr[i].ulPropTag, ulObjType) == erSuccess)
  1658. continue;
  1659. // Some properties may only be saved on the first save (fNewItem == TRUE) for folders, stores and messages
  1660. if(!fNewItem && (ulObjType == MAPI_MESSAGE || ulObjType == MAPI_FOLDER || ulObjType == MAPI_STORE)) {
  1661. switch(lpPropValArray->__ptr[i].ulPropTag) {
  1662. case PR_LAST_MODIFICATION_TIME:
  1663. case PR_MESSAGE_FLAGS:
  1664. case PR_SEARCH_KEY:
  1665. case PR_LAST_MODIFIER_NAME:
  1666. case PR_LAST_MODIFIER_ENTRYID:
  1667. if (ulSyncId == 0)
  1668. // Only on first write, unless sent by ICS
  1669. continue;
  1670. break;
  1671. case PR_SOURCE_KEY:
  1672. if (ulParentType == MAPI_FOLDER)
  1673. // Only on first write, unless message-in-message
  1674. continue;
  1675. break;
  1676. }
  1677. }
  1678. if (lpPropValArray->__ptr[i].ulPropTag == PR_LAST_MODIFIER_NAME_W ||
  1679. lpPropValArray->__ptr[i].ulPropTag == PR_LAST_MODIFIER_ENTRYID)
  1680. if(!fNewItem && ulSyncId == 0)
  1681. continue;
  1682. // Same goes for flags in PR_MESSAGE_FLAGS
  1683. if(lpPropValArray->__ptr[i].ulPropTag == PR_MESSAGE_FLAGS) {
  1684. if (ulSyncId == 0)
  1685. // Normalize PR_MESSAGE_FLAGS so that the user cannot set things like MSGFLAG_ASSOCIATED
  1686. lpPropValArray->__ptr[i].Value.ul = (lpPropValArray->__ptr[i].Value.ul & (MSGFLAG_SETTABLE_BY_USER | MSGFLAG_SETTABLE_BY_SPOOLER)) | ulFlags;
  1687. else
  1688. // Normalize PR_MESSAGE_FLAGS so that the user cannot change flags that are also
  1689. // stored in the hierarchy table.
  1690. lpPropValArray->__ptr[i].Value.ul = (lpPropValArray->__ptr[i].Value.ul & ~MSGFLAG_UNSETTABLE) | ulFlags;
  1691. }
  1692. // Make sure we dont have a colliding PR_SOURCE_KEY. This can happen if a user imports an exported message for example.
  1693. if(lpPropValArray->__ptr[i].ulPropTag == PR_SOURCE_KEY)
  1694. {
  1695. // Remove any old (deleted) indexed property if it's there
  1696. er = RemoveStaleIndexedProp(lpDatabase, PR_SOURCE_KEY, lpPropValArray->__ptr[i].Value.bin->__ptr, lpPropValArray->__ptr[i].Value.bin->__size);
  1697. if(er != erSuccess) {
  1698. // Unable to remove the (old) sourcekey in use. This means that it is in use by some other object. We just skip
  1699. // the property so that it is generated later as a new random sourcekey
  1700. er = erSuccess;
  1701. continue;
  1702. }
  1703. // Insert sourcekey, use REPLACE because createfolder already created a sourcekey.
  1704. // Because there is a non-primary unique key on the
  1705. // val_binary part of the table, it will fail if the source key is duplicate.
  1706. 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)+")";
  1707. er = lpDatabase->DoInsert(strQuery);
  1708. if(er != erSuccess)
  1709. return er;
  1710. setInserted.insert(lpPropValArray->__ptr[i].ulPropTag);
  1711. // Remember the source key in the cache
  1712. g_lpSessionManager->GetCacheManager()->SetObjectProp(PROP_ID(PR_SOURCE_KEY), lpPropValArray->__ptr[i].Value.bin->__size, lpPropValArray->__ptr[i].Value.bin->__ptr, ulObjId);
  1713. continue;
  1714. }
  1715. // attachments are in the blob too
  1716. if (lpPropValArray->__ptr[i].ulPropTag == PR_ATTACH_DATA_BIN || lpPropValArray->__ptr[i].ulPropTag == PR_EC_IMAP_EMAIL) {
  1717. /*
  1718. * bAttachmentStored indicates we already processed the attachment.
  1719. * this could happen when the user provided the instance ID but as
  1720. * backup also send the PR_ATTACH_DATA_BIN.
  1721. */
  1722. if (bAttachmentStored)
  1723. continue;
  1724. er = lpAttachmentStorage->SaveAttachment(ulObjId, PROP_ID(lpPropValArray->__ptr[i].ulPropTag), !fNewItem,
  1725. lpPropValArray->__ptr[i].Value.bin->__size,
  1726. lpPropValArray->__ptr[i].Value.bin->__ptr,
  1727. &ulInstanceId);
  1728. if (er != erSuccess)
  1729. return er;
  1730. lpsReturnObj->lpInstanceIds = s_alloc<entryList>(soap);
  1731. lpsReturnObj->lpInstanceIds->__size = 1;
  1732. lpsReturnObj->lpInstanceIds->__ptr = s_alloc<entryId>(soap, lpsReturnObj->lpInstanceIds->__size);
  1733. er = SIIDToEntryID(soap, &sGuidServer, ulInstanceId, PROP_ID(lpPropValArray->__ptr[i].ulPropTag), &lpsReturnObj->lpInstanceIds->__ptr[0]);
  1734. if (er != erSuccess) {
  1735. lpsReturnObj->lpInstanceIds = NULL;
  1736. return er;
  1737. }
  1738. continue;
  1739. }
  1740. // We have to return the values for PR_LAST_MODIFICATION_TIME and PR_CREATION_TIME
  1741. if (lpPropValArray->__ptr[i].ulPropTag == PR_LAST_MODIFICATION_TIME) {
  1742. lpftModified->dwHighDateTime = lpPropValArray->__ptr[i].Value.hilo->hi;
  1743. lpftModified->dwLowDateTime = lpPropValArray->__ptr[i].Value.hilo->lo;
  1744. }
  1745. if (lpPropValArray->__ptr[i].ulPropTag == PR_CREATION_TIME) {
  1746. lpftCreated->dwHighDateTime = lpPropValArray->__ptr[i].Value.hilo->hi;
  1747. lpftCreated->dwLowDateTime = lpPropValArray->__ptr[i].Value.hilo->lo;
  1748. }
  1749. if (lpfHaveChangeKey && lpPropValArray->__ptr[i].ulPropTag == PR_CHANGE_KEY)
  1750. *lpfHaveChangeKey = true;
  1751. if (PROP_TYPE(lpPropValArray->__ptr[i].ulPropTag) & MV_FLAG) {
  1752. // Make sure string prop_types become PT_MV_STRING8
  1753. if (PROP_TYPE(lpPropValArray->__ptr[i].ulPropTag) == PT_MV_UNICODE)
  1754. lpPropValArray->__ptr[i].ulPropTag = CHANGE_PROP_TYPE(lpPropValArray->__ptr[i].ulPropTag, PT_MV_STRING8);
  1755. //Write mv properties
  1756. nMVItems = GetMVItemCount(&lpPropValArray->__ptr[i]);
  1757. for (gsoap_size_t j = 0; j < nMVItems; ++j) {
  1758. assert(PROP_TYPE(lpPropValArray->__ptr[i].ulPropTag) != PT_MV_UNICODE);
  1759. // Make sure string propvals are in UTF8
  1760. if (PROP_TYPE(lpPropValArray->__ptr[i].ulPropTag) == PT_MV_STRING8)
  1761. lpPropValArray->__ptr[i].Value.mvszA.__ptr[j] = stringCompat.to_UTF8(soap, lpPropValArray->__ptr[i].Value.mvszA.__ptr[j]);
  1762. er = CopySOAPPropValToDatabaseMVPropVal(&lpPropValArray->__ptr[i], j, strColName, strColData, lpDatabase);
  1763. if(er != erSuccess)
  1764. continue;
  1765. 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 + ")";
  1766. er = lpDatabase->DoInsert(strQuery, NULL, &ulAffected);
  1767. if(er != erSuccess)
  1768. return er;
  1769. // According to the MySQL documentation (http://dev.mysql.com/doc/refman/5.0/en/mysql-affected-rows.html) ulAffected rows
  1770. // will be 2 if a row was replaced.
  1771. // Interestingly, I (MSw) have observer in a consecutive call to the above replace query, where in both cases an old value
  1772. // was replaced with a new value, that it returned 1 the first time and 2 the second time.
  1773. // We'll allow both though.
  1774. if(ulAffected != 1 && ulAffected != 2) {
  1775. ec_log_err("Unable to update MVProperties during save: %d, object id: %d", ulAffected, ulObjId);
  1776. return KCERR_DATABASE_ERROR;
  1777. }
  1778. }
  1779. if(!fNewItem) {
  1780. strQuery = "DELETE FROM mvproperties WHERE hierarchyid=" + stringify (ulObjId) +
  1781. " AND tag=" + stringify(PROP_ID(lpPropValArray->__ptr[i].ulPropTag)) +
  1782. " AND type=" + stringify(PROP_TYPE(lpPropValArray->__ptr[i].ulPropTag)) +
  1783. " AND orderid >= " + stringify(nMVItems);
  1784. er = lpDatabase->DoDelete(strQuery);
  1785. if(er != erSuccess)
  1786. return er;
  1787. }
  1788. } else {
  1789. // Make sure string propvals are in UTF8 with tag PT_STRING8
  1790. if (PROP_TYPE(lpPropValArray->__ptr[i].ulPropTag) == PT_STRING8 || PROP_TYPE(lpPropValArray->__ptr[i].ulPropTag) == PT_UNICODE) {
  1791. lpPropValArray->__ptr[i].Value.lpszA = stringCompat.to_UTF8(soap, lpPropValArray->__ptr[i].Value.lpszA);
  1792. lpPropValArray->__ptr[i].ulPropTag = CHANGE_PROP_TYPE(lpPropValArray->__ptr[i].ulPropTag, PT_STRING8);
  1793. }
  1794. // Write the property to the database
  1795. er = WriteSingleProp(lpDatabase, ulObjId, ulParent, &lpPropValArray->__ptr[i], false, lpDatabase->GetMaxAllowedPacket(), strInsert);
  1796. if (er == KCERR_TOO_BIG) {
  1797. er = lpDatabase->DoInsert(strInsert);
  1798. if (er == erSuccess) {
  1799. strInsert.clear();
  1800. er = WriteSingleProp(lpDatabase, ulObjId, ulParent, &lpPropValArray->__ptr[i], false, lpDatabase->GetMaxAllowedPacket(), strInsert);
  1801. }
  1802. }
  1803. if(er != erSuccess)
  1804. return er;
  1805. // Write the property to the table properties if needed (only on objects in folders (folders, messages), and if the property is being tracked here.
  1806. if(ulParentType == MAPI_FOLDER) {
  1807. // Cache the written value
  1808. sObjectTableKey key(ulObjId,0);
  1809. g_lpSessionManager->GetCacheManager()->SetCell(&key, lpPropValArray->__ptr[i].ulPropTag, &lpPropValArray->__ptr[i]);
  1810. if (0) {
  1811. // FIXME do we need this code? Currently we get always a deferredupdate!
  1812. // Please also update streamutil.cpp:DeserializeProps
  1813. er = WriteSingleProp(lpDatabase, ulObjId, ulParent, &lpPropValArray->__ptr[i], true, lpDatabase->GetMaxAllowedPacket(), strInsertTProp);
  1814. if (er == KCERR_TOO_BIG) {
  1815. er = lpDatabase->DoInsert(strInsertTProp);
  1816. if (er == erSuccess) {
  1817. strInsertTProp.clear();
  1818. er = WriteSingleProp(lpDatabase, ulObjId, ulParent, &lpPropValArray->__ptr[i], true, lpDatabase->GetMaxAllowedPacket(), strInsertTProp);
  1819. }
  1820. }
  1821. if(er != erSuccess)
  1822. return er;
  1823. }
  1824. }
  1825. }
  1826. setInserted.insert(lpPropValArray->__ptr[i].ulPropTag);
  1827. } // for (i = 0; i < lpPropValArray->__size; ++i)
  1828. if(!strInsert.empty()) {
  1829. er = lpDatabase->DoInsert(strInsert);
  1830. if(er != erSuccess)
  1831. return er;
  1832. }
  1833. if(ulParentType == MAPI_FOLDER) {
  1834. if(0) {
  1835. /* Modification, just directly write the tproperties
  1836. * The idea behind this is that we'd need some serious random-access reads to properties later when flushing
  1837. * tproperties, and we have the properties in memory now anyway. Also, modifications usually are just a few properties, causing
  1838. * only minor random I/O on tproperties, and a tproperties flush reads all the properties, not just the modified ones.
  1839. */
  1840. if(!strInsertTProp.empty()) {
  1841. er = lpDatabase->DoInsert(strInsertTProp);
  1842. if(er != erSuccess)
  1843. return er;
  1844. }
  1845. } else {
  1846. // Instead of writing directly to tproperties, save a delayed write request.
  1847. if(ulParent != CACHE_NO_PARENT) {
  1848. er = ECTPropsPurge::AddDeferredUpdateNoPurge(lpDatabase, ulParent, 0, ulObjId);
  1849. if(er != erSuccess)
  1850. return er;
  1851. }
  1852. }
  1853. }
  1854. // Insert the properties
  1855. if(ulPropInserts > 0) {
  1856. er = lpDatabase->DoInsert(strInsertQuery, NULL, NULL);
  1857. if(er != erSuccess)
  1858. return er;
  1859. }
  1860. if(ulObjType == MAPI_MESSAGE) {
  1861. auto iterInserted = setInserted.find(PR_LAST_MODIFIER_NAME_W);
  1862. // update the PR_LAST_MODIFIER_NAME and PR_LAST_MODIFIER_ENTRYID
  1863. if (iterInserted == setInserted.cend()) {
  1864. er = GetABEntryID(lpecSession->GetSecurity()->GetUserId(), soap, &sUserId);
  1865. if(er != erSuccess)
  1866. return er;
  1867. lpecSession->GetSecurity()->GetUsername(&strUsername);
  1868. strQuery = "REPLACE INTO properties(hierarchyid, tag, type, val_string, val_binary) VALUES(" +
  1869. stringify(ulObjId) + "," +
  1870. stringify(PROP_ID(PR_LAST_MODIFIER_NAME_A)) + "," +
  1871. stringify(PROP_TYPE(PR_LAST_MODIFIER_NAME_A)) + ",\"" +
  1872. lpDatabase->Escape(strUsername) +
  1873. "\", NULL), (" +
  1874. stringify(ulObjId) + "," +
  1875. stringify(PROP_ID(PR_LAST_MODIFIER_ENTRYID)) + "," +
  1876. stringify(PROP_TYPE(PR_LAST_MODIFIER_ENTRYID)) +
  1877. ", NULL, " +
  1878. lpDatabase->EscapeBinary(sUserId.__ptr, sUserId.__size) + ")";
  1879. er = lpDatabase->DoInsert(strQuery);
  1880. if(er != erSuccess)
  1881. return er;
  1882. sObjectTableKey key(ulObjId,0);
  1883. struct propVal sPropVal;
  1884. sPropVal.ulPropTag = PR_LAST_MODIFIER_NAME_A;
  1885. sPropVal.Value.lpszA = (char*)strUsername.c_str();
  1886. sPropVal.__union = SOAP_UNION_propValData_lpszA;
  1887. g_lpSessionManager->GetCacheManager()->SetCell(&key, PR_LAST_MODIFIER_NAME_A, &sPropVal);
  1888. sPropVal.ulPropTag = PR_LAST_MODIFIER_ENTRYID;
  1889. sPropVal.Value.bin = &sUserId;
  1890. sPropVal.__union = SOAP_UNION_propValData_bin;
  1891. g_lpSessionManager->GetCacheManager()->SetCell(&key, PR_LAST_MODIFIER_ENTRYID, &sPropVal);
  1892. }
  1893. }
  1894. if(!fNewItem) {
  1895. // Update, so write the modtime and clear UNMODIFIED flag
  1896. if (setInserted.find(PR_LAST_MODIFICATION_TIME) == setInserted.cend()) {
  1897. struct propVal sProp;
  1898. struct hiloLong sHilo;
  1899. struct sObjectTableKey key;
  1900. FILETIME ft;
  1901. sProp.ulPropTag = PR_LAST_MODIFICATION_TIME;
  1902. sProp.Value.hilo = &sHilo;
  1903. sProp.__union = SOAP_UNION_propValData_hilo;
  1904. UnixTimeToFileTime(time(NULL), &sProp.Value.hilo->hi, &sProp.Value.hilo->lo);
  1905. ft.dwHighDateTime = sProp.Value.hilo->hi;
  1906. ft.dwLowDateTime = sProp.Value.hilo->lo;
  1907. er = WriteProp(lpDatabase, ulObjId, ulParent, &sProp);
  1908. if (er != erSuccess)
  1909. return er;
  1910. *lpftModified = ft;
  1911. // Add to cache
  1912. key.ulObjId = ulObjId;
  1913. key.ulOrderId = 0;
  1914. if(ulParentType == MAPI_FOLDER)
  1915. g_lpSessionManager->GetCacheManager()->SetCell(&key, PR_LAST_MODIFICATION_TIME, &sProp);
  1916. }
  1917. if(ulObjType == MAPI_MESSAGE) {
  1918. // Unset MSGFLAG_UNMODIFIED
  1919. 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));
  1920. er = lpDatabase->DoUpdate(strQuery);
  1921. if(er != erSuccess)
  1922. return er;
  1923. // Update cache
  1924. if(ulParentType == MAPI_FOLDER)
  1925. g_lpSessionManager->GetCacheManager()->UpdateCell(ulObjId, PR_MESSAGE_FLAGS, (unsigned int)MSGFLAG_UNMODIFIED, 0);
  1926. }
  1927. } else {
  1928. // New item, make sure PR_CREATION_TIME and PR_LAST_MODIFICATION_TIME are available
  1929. if (setInserted.find(PR_LAST_MODIFICATION_TIME) == setInserted.cend() ||
  1930. setInserted.find(PR_CREATION_TIME) == setInserted.cend()) {
  1931. struct propVal sPropTime;
  1932. FILETIME ft;
  1933. unsigned int tags[] = { PR_LAST_MODIFICATION_TIME, PR_CREATION_TIME };
  1934. // Get current time
  1935. UnixTimeToFileTime(time(NULL), &ft);
  1936. sPropTime.Value.hilo = s_alloc<struct hiloLong>(soap);
  1937. sPropTime.Value.hilo->hi = ft.dwHighDateTime;
  1938. sPropTime.Value.hilo->lo = ft.dwLowDateTime;
  1939. sPropTime.__union = SOAP_UNION_propValData_hilo;
  1940. // Same thing for both PR_LAST_MODIFICATION_TIME and PR_CREATION_TIME
  1941. for (size_t i = 0; i < ARRAY_SIZE(tags); ++i) {
  1942. if(setInserted.find(tags[i]) == setInserted.end()) {
  1943. sObjectTableKey key;
  1944. sPropTime.ulPropTag = tags[i];
  1945. strQuery.clear();
  1946. WriteSingleProp(lpDatabase, ulObjId, ulParent, &sPropTime, false, 0, strQuery);
  1947. er = lpDatabase->DoInsert(strQuery);
  1948. if(er != erSuccess)
  1949. return er;
  1950. strQuery.clear();
  1951. WriteSingleProp(lpDatabase, ulObjId, ulParent, &sPropTime, true, 0, strQuery);
  1952. er = lpDatabase->DoInsert(strQuery);
  1953. if(er != erSuccess)
  1954. return er;
  1955. if(tags[i] == PR_LAST_MODIFICATION_TIME)
  1956. *lpftModified = ft;
  1957. if(tags[i] == PR_CREATION_TIME)
  1958. *lpftCreated = ft;
  1959. // Add to cache
  1960. key.ulObjId = ulObjId;
  1961. key.ulOrderId = 0;
  1962. if(ulParentType == MAPI_FOLDER)
  1963. g_lpSessionManager->GetCacheManager()->SetCell(&key, tags[i], &sPropTime);
  1964. }
  1965. }
  1966. }
  1967. }
  1968. if(fNewItem && ulObjType == MAPI_MESSAGE) {
  1969. // Add PR_SOURCE_KEY to new messages without a given PR_SOURCE_KEY
  1970. // This isn't for folders, this done in the createfolder function
  1971. auto iterInserted = setInserted.find(PR_SOURCE_KEY);
  1972. if (iterInserted == setInserted.cend()) {
  1973. er = lpecSession->GetNewSourceKey(&sSourceKey);
  1974. if (er != erSuccess)
  1975. return er;
  1976. strQuery = "INSERT INTO indexedproperties(hierarchyid,tag,val_binary) VALUES(" + stringify(ulObjId) + "," + stringify(PROP_ID(PR_SOURCE_KEY)) + "," + lpDatabase->EscapeBinary(sSourceKey, sSourceKey.size()) + ")";
  1977. er = lpDatabase->DoInsert(strQuery);
  1978. if(er != erSuccess)
  1979. return er;
  1980. g_lpSessionManager->GetCacheManager()->SetObjectProp(PROP_ID(PR_SOURCE_KEY), sSourceKey.size(), sSourceKey, ulObjId);
  1981. }
  1982. if(ulParentType == MAPI_FOLDER) {
  1983. // Add a PR_EC_IMAP_ID to the newly created message
  1984. sObjectTableKey key(ulObjId, 0);
  1985. struct propVal sProp;
  1986. er = g_lpSessionManager->GetNewSequence(ECSessionManager::SEQ_IMAP, &ullIMAP);
  1987. if(er != erSuccess)
  1988. return er;
  1989. strQuery = "INSERT INTO properties(hierarchyid, tag, type, val_ulong) VALUES(" +
  1990. stringify(ulObjId) + "," +
  1991. stringify(PROP_ID(PR_EC_IMAP_ID)) + "," +
  1992. stringify(PROP_TYPE(PR_EC_IMAP_ID)) + "," +
  1993. stringify(ullIMAP) +
  1994. ")";
  1995. er = lpDatabase->DoInsert(strQuery);
  1996. if(er != erSuccess)
  1997. return er;
  1998. sProp.ulPropTag = PR_EC_IMAP_ID;
  1999. sProp.Value.ul = ullIMAP;
  2000. sProp.__union = SOAP_UNION_propValData_ul;
  2001. er = g_lpSessionManager->GetCacheManager()->SetCell(&key, PR_EC_IMAP_ID, &sProp);
  2002. if (er != erSuccess)
  2003. return er;
  2004. }
  2005. }
  2006. if(fNewItem && ulParentType == MAPI_FOLDER)
  2007. // Since we have written a new item, we know that the cache contains *all* properties for this object
  2008. g_lpSessionManager->GetCacheManager()->SetComplete(ulObjId);
  2009. // We know the values for the object cache, so add them here
  2010. if(ulObjType == MAPI_MESSAGE || ulObjType == MAPI_FOLDER)
  2011. g_lpSessionManager->GetCacheManager()->SetObject(ulObjId, ulParent, ulOwner, ulFlags, ulObjType);
  2012. return erSuccess;
  2013. }
  2014. // You need to check the permissions before you call this function
  2015. static ECRESULT DeleteProps(ECSession *lpecSession, ECDatabase *lpDatabase,
  2016. ULONG ulObjId, struct propTagArray *lpsPropTags,
  2017. ECAttachmentStorage *at_storage)
  2018. {
  2019. ECRESULT er;
  2020. std::string strQuery;
  2021. sObjectTableKey key;
  2022. struct propVal sPropVal;
  2023. // block removal of certain properties (per object type?), properties handled in WriteProps
  2024. 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};
  2025. std::set<unsigned int> setNotDeletable(ulPropTags, ulPropTags + ARRAY_SIZE(ulPropTags));
  2026. // Delete one or more properties of an object
  2027. for (gsoap_size_t i = 0; i < lpsPropTags->__size; ++i) {
  2028. if (setNotDeletable.find(lpsPropTags->__ptr[i]) != setNotDeletable.cend())
  2029. continue;
  2030. if((lpsPropTags->__ptr[i]&MV_FLAG) == 0)
  2031. strQuery = "DELETE FROM properties WHERE hierarchyid="+stringify(ulObjId)+" AND tag="+stringify(PROP_ID(lpsPropTags->__ptr[i]));
  2032. else // mvprops
  2033. strQuery = "DELETE FROM mvproperties WHERE hierarchyid="+stringify(ulObjId)+" AND tag="+stringify(PROP_ID(lpsPropTags->__ptr[i]) );
  2034. er = lpDatabase->DoDelete(strQuery);
  2035. if(er != erSuccess)
  2036. return er;
  2037. // Remove from tproperties
  2038. if((lpsPropTags->__ptr[i]&MV_FLAG) == 0) {
  2039. strQuery = "DELETE FROM tproperties WHERE hierarchyid="+stringify(ulObjId)+" AND tag="+stringify(PROP_ID(lpsPropTags->__ptr[i]));
  2040. er = lpDatabase->DoDelete(strQuery);
  2041. if(er != erSuccess)
  2042. return er;
  2043. }
  2044. // Remove eml attachment
  2045. if (lpsPropTags->__ptr[i] == PR_EC_IMAP_EMAIL) {
  2046. std::list<ULONG> at_list;
  2047. at_list.push_back(ulObjId);
  2048. at_storage->DeleteAttachments(at_list);
  2049. }
  2050. // Update cache with NOT_FOUND for this property
  2051. key.ulObjId = ulObjId;
  2052. key.ulOrderId = 0;
  2053. sPropVal.ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(lpsPropTags->__ptr[i]));
  2054. sPropVal.Value.ul = KCERR_NOT_FOUND;
  2055. sPropVal.__union = SOAP_UNION_propValData_ul;
  2056. g_lpSessionManager->GetCacheManager()->SetCell(&key, lpsPropTags->__ptr[i], &sPropVal);
  2057. }
  2058. return erSuccess;
  2059. }
  2060. static unsigned int SaveObject(struct soap *soap, ECSession *lpecSession,
  2061. ECDatabase *lpDatabase, ECAttachmentStorage *lpAttachmentStorage,
  2062. unsigned int ulStoreId, unsigned int ulParentObjId,
  2063. unsigned int ulParentType, unsigned int ulFlags, unsigned int ulSyncId,
  2064. struct saveObject *lpsSaveObj, struct saveObject *lpsReturnObj,
  2065. unsigned int ulLevel, bool *lpfHaveChangeKey = NULL)
  2066. {
  2067. ECRESULT er = erSuccess;
  2068. ALLOC_DBRESULT();
  2069. int n;
  2070. unsigned int ulParentObjType = 0;
  2071. unsigned int ulSize = 0;
  2072. unsigned int ulObjId = 0;
  2073. bool fNewItem = false;
  2074. bool fHasAttach = false;
  2075. bool fGenHasAttach = false;
  2076. ECListDeleteItems lstDeleteItems;
  2077. ECListDeleteItems lstDeleted;
  2078. FILETIME ftCreated = { 0,0 };
  2079. FILETIME ftModified = { 0,0 };
  2080. if (ulLevel <= 0) {
  2081. er = KCERR_TOO_COMPLEX;
  2082. goto exit;
  2083. }
  2084. // reset return object
  2085. lpsReturnObj->__size = 0;
  2086. lpsReturnObj->__ptr = NULL;
  2087. lpsReturnObj->delProps.__size = 0;
  2088. lpsReturnObj->delProps.__ptr = NULL;
  2089. lpsReturnObj->modProps.__size = 0;
  2090. lpsReturnObj->modProps.__ptr = NULL;
  2091. lpsReturnObj->bDelete = false;
  2092. lpsReturnObj->ulClientId = lpsSaveObj->ulClientId;
  2093. lpsReturnObj->ulServerId = lpsSaveObj->ulServerId;
  2094. lpsReturnObj->ulObjType = lpsSaveObj->ulObjType;
  2095. lpsReturnObj->lpInstanceIds = NULL;
  2096. if (lpsSaveObj->ulServerId == 0) {
  2097. if (ulParentObjId == 0) {
  2098. er = KCERR_INVALID_PARAMETER;
  2099. goto exit;
  2100. }
  2101. er = CreateObject(lpecSession, lpDatabase, ulParentObjId, ulParentType, lpsSaveObj->ulObjType, ulFlags, &lpsReturnObj->ulServerId);
  2102. if (er != erSuccess)
  2103. goto exit;
  2104. fNewItem = true;
  2105. ulObjId = lpsReturnObj->ulServerId;
  2106. } else {
  2107. ulObjId = lpsSaveObj->ulServerId;
  2108. }
  2109. if (lpsSaveObj->bDelete) {
  2110. // make list of all children object IDs in std::list<int> ?
  2111. ECListInt lstDel;
  2112. lstDel.push_back(lpsSaveObj->ulServerId);
  2113. // we always hard delete, because we can only delete submessages here
  2114. // make sure we also delete message-in-message attachments, so all message related flags are on
  2115. ULONG ulDelFlags = EC_DELETE_CONTAINER | EC_DELETE_MESSAGES | EC_DELETE_RECIPIENTS | EC_DELETE_ATTACHMENTS | EC_DELETE_HARD_DELETE;
  2116. // Collect recursive parent objects, validate item and check the permissions
  2117. er = ExpandDeletedItems(lpecSession, lpDatabase, &lstDel, ulDelFlags, false, &lstDeleteItems);
  2118. if (er != erSuccess) {
  2119. assert(false);
  2120. goto exit;
  2121. }
  2122. er = DeleteObjectHard(lpecSession, lpDatabase, lpAttachmentStorage, ulDelFlags, lstDeleteItems, true, lstDeleted);
  2123. if (er != erSuccess) {
  2124. assert(false);
  2125. goto exit;
  2126. }
  2127. er = DeleteObjectStoreSize(lpecSession, lpDatabase, ulDelFlags, lstDeleted);
  2128. if (er != erSuccess) {
  2129. assert(false);
  2130. goto exit;
  2131. }
  2132. er = DeleteObjectCacheUpdate(lpecSession, ulDelFlags, lstDeleted);
  2133. if (er != erSuccess) {
  2134. assert(false);
  2135. goto exit;
  2136. }
  2137. // object deleted, so we're done
  2138. goto exit;
  2139. }
  2140. // ------
  2141. // the following code is only for objects that still exist
  2142. // ------
  2143. {
  2144. // Do not delete properties if this is a new object: this avoids any delete queries that cause unnecessary locks on the tables
  2145. if (lpsSaveObj->delProps.__size > 0 && !fNewItem) {
  2146. er = DeleteProps(lpecSession, lpDatabase, lpsReturnObj->ulServerId, &lpsSaveObj->delProps, lpAttachmentStorage);
  2147. if (er != erSuccess)
  2148. goto exit;
  2149. }
  2150. if (lpsSaveObj->modProps.__size > 0) {
  2151. er = WriteProps(soap, lpecSession, lpDatabase, lpAttachmentStorage, lpsSaveObj, lpsReturnObj->ulServerId, fNewItem, ulSyncId, lpsReturnObj, lpfHaveChangeKey, &ftCreated, &ftModified);
  2152. if (er != erSuccess)
  2153. goto exit;
  2154. }
  2155. // check children
  2156. if (lpsSaveObj->__size > 0) {
  2157. lpsReturnObj->__size = lpsSaveObj->__size;
  2158. lpsReturnObj->__ptr = s_alloc<struct saveObject>(soap, lpsReturnObj->__size);
  2159. for (gsoap_size_t i = 0; i < lpsSaveObj->__size; ++i) {
  2160. 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);
  2161. if (er != erSuccess)
  2162. goto exit;
  2163. }
  2164. }
  2165. if (lpsReturnObj->ulObjType == MAPI_MESSAGE) {
  2166. // Generate properties that we need to generate (PR_HASATTACH, PR_LAST_MODIFICATION_TIME, PR_CREATION_TIME)
  2167. if (fNewItem) {
  2168. // We have to write PR_HASTTACH since it is a new object
  2169. fGenHasAttach = true;
  2170. // We can generate PR_HASATTACH from the passed object data
  2171. for (gsoap_size_t i = 0; i < lpsSaveObj->__size; ++i)
  2172. if(lpsSaveObj->__ptr[i].ulObjType == MAPI_ATTACH) {
  2173. fHasAttach = true;
  2174. break;
  2175. }
  2176. } else {
  2177. // Modified object. Only change PR_HASATTACH if something has changed
  2178. for (gsoap_size_t i = 0; i < lpsSaveObj->__size; ++i)
  2179. if(lpsSaveObj->__ptr[i].ulObjType == MAPI_ATTACH && (lpsSaveObj->__ptr[i].bDelete || lpsSaveObj->__ptr[i].ulServerId == 0)) {
  2180. // An attachment was deleted or added in this call
  2181. fGenHasAttach = true;
  2182. break;
  2183. }
  2184. if(fGenHasAttach) {
  2185. // An attachment was added or deleted, check the database to see if any attachments are left.
  2186. strQuery = "SELECT id FROM hierarchy WHERE parent=" + stringify(lpsReturnObj->ulServerId) + " AND type=" + stringify(MAPI_ATTACH) + " LIMIT 1";
  2187. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  2188. if(er != erSuccess)
  2189. goto exit;
  2190. fHasAttach = lpDatabase->GetNumRows(lpDBResult) > 0;
  2191. }
  2192. }
  2193. if(fGenHasAttach) {
  2194. // We have to generate/update PR_HASATTACH
  2195. unsigned int ulParentTmp, ulOwnerTmp, ulFlagsTmp, ulTypeTmp;
  2196. sObjectTableKey key(lpsReturnObj->ulServerId, 0);
  2197. struct propVal sPropHasAttach;
  2198. sPropHasAttach.ulPropTag = PR_HASATTACH;
  2199. sPropHasAttach.Value.b = fHasAttach;
  2200. sPropHasAttach.__union = SOAP_UNION_propValData_b;
  2201. // Write in properties
  2202. strQuery.clear();
  2203. WriteSingleProp(lpDatabase, lpsReturnObj->ulServerId, ulParentObjId, &sPropHasAttach, false, 0, strQuery);
  2204. er = lpDatabase->DoInsert(strQuery);
  2205. if(er != erSuccess)
  2206. goto exit;
  2207. // Write in tproperties
  2208. strQuery.clear();
  2209. WriteSingleProp(lpDatabase, lpsReturnObj->ulServerId, ulParentObjId, &sPropHasAttach, true, 0, strQuery);
  2210. er = lpDatabase->DoInsert(strQuery);
  2211. if(er != erSuccess)
  2212. goto exit;
  2213. // Update cache, since it may have been written before by WriteProps with a possibly wrong value
  2214. g_lpSessionManager->GetCacheManager()->SetCell(&key, PR_HASATTACH, &sPropHasAttach);
  2215. // Update MSGFLAG_HASATTACH in the same way. We can assume PR_MESSAGE_FLAGS is already available, so we
  2216. // just do an update (instead of REPLACE INTO)
  2217. 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));
  2218. er = lpDatabase->DoUpdate(strQuery);
  2219. if(er != erSuccess)
  2220. goto exit;
  2221. // Update cache if it's actually in the cache
  2222. if(g_lpSessionManager->GetCacheManager()->GetCell(&key, PR_MESSAGE_FLAGS, &sPropHasAttach, soap, false) == erSuccess) {
  2223. sPropHasAttach.Value.ul &= ~MSGFLAG_HASATTACH;
  2224. sPropHasAttach.Value.ul |= fHasAttach ? MSGFLAG_HASATTACH : 0;
  2225. g_lpSessionManager->GetCacheManager()->SetCell(&key, PR_MESSAGE_FLAGS, &sPropHasAttach);
  2226. }
  2227. // More cache
  2228. if (g_lpSessionManager->GetCacheManager()->GetObject(lpsReturnObj->ulServerId, &ulParentTmp, &ulOwnerTmp, &ulFlagsTmp, &ulTypeTmp) == erSuccess) {
  2229. ulFlags &= ~MSGFLAG_HASATTACH;
  2230. ulFlags |= fHasAttach ? MSGFLAG_HASATTACH : 0;
  2231. g_lpSessionManager->GetCacheManager()->SetObject(lpsReturnObj->ulServerId, ulParentTmp, ulOwnerTmp, ulFlagsTmp, ulTypeTmp);
  2232. }
  2233. }
  2234. }
  2235. // 1. calc size of object, now that all children are saved.
  2236. if (lpsReturnObj->ulObjType == MAPI_MESSAGE || lpsReturnObj->ulObjType == MAPI_ATTACH) {
  2237. // Remove old size
  2238. if (fNewItem != true && lpsReturnObj->ulObjType == MAPI_MESSAGE && ulParentType == MAPI_FOLDER) {
  2239. if (GetObjectSize(lpDatabase, lpsReturnObj->ulServerId, &ulSize) == erSuccess)
  2240. er = UpdateObjectSize(lpDatabase, ulStoreId, MAPI_STORE, UPDATE_SUB, ulSize);
  2241. if (er != erSuccess)
  2242. goto exit;
  2243. }
  2244. // Add new size
  2245. if (CalculateObjectSize(lpDatabase, lpsReturnObj->ulServerId, lpsReturnObj->ulObjType, &ulSize) == erSuccess) {
  2246. er = UpdateObjectSize(lpDatabase, lpsReturnObj->ulServerId, lpsReturnObj->ulObjType, UPDATE_SET, ulSize);
  2247. if (er != erSuccess)
  2248. goto exit;
  2249. if (lpsReturnObj->ulObjType == MAPI_MESSAGE && ulParentType == MAPI_FOLDER) {
  2250. er = UpdateObjectSize(lpDatabase, ulStoreId, MAPI_STORE, UPDATE_ADD, ulSize);
  2251. if (er != erSuccess)
  2252. goto exit;
  2253. }
  2254. } else {
  2255. assert(false);
  2256. }
  2257. }
  2258. // 2. find props to return
  2259. // the server returns the following 4 properties, when the item is new:
  2260. // PR_CREATION_TIME, (PR_PARENT_SOURCE_KEY, PR_SOURCE_KEY /type==5|3) (PR_RECORD_KEY /type==7|5|3)
  2261. // TODO: recipients: PR_INSTANCE_KEY
  2262. // it always sends the following property:
  2263. // PR_LAST_MODIFICATION_TIME
  2264. // currently, it always sends them all
  2265. // 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
  2266. // we can skip PR_ACCESS and PR_ACCESS_LEVEL because the client already inherited those from the parent
  2267. // we need to alloc 2 properties for PR_CHANGE_KEY and PR_PREDECESSOR_CHANGE_LIST
  2268. lpsReturnObj->delProps.__size = 8;
  2269. lpsReturnObj->delProps.__ptr = s_alloc<unsigned int>(soap, lpsReturnObj->delProps.__size);
  2270. lpsReturnObj->modProps.__size = 8;
  2271. lpsReturnObj->modProps.__ptr = s_alloc<struct propVal>(soap, lpsReturnObj->modProps.__size);
  2272. n = 0;
  2273. // set the PR_RECORD_KEY
  2274. // New clients generate the instance key, old clients don't. See if one was provided.
  2275. if (lpsSaveObj->ulObjType == MAPI_ATTACH || lpsSaveObj->ulObjType == MAPI_MESSAGE || lpsSaveObj->ulObjType == MAPI_FOLDER) {
  2276. bool bSkip = false;
  2277. if (lpsSaveObj->ulObjType == MAPI_ATTACH) {
  2278. for (gsoap_size_t i = 0; !bSkip && i < lpsSaveObj->modProps.__size; ++i)
  2279. bSkip = lpsSaveObj->modProps.__ptr[i].ulPropTag == PR_RECORD_KEY;
  2280. // @todo if we don't have a pr_record_key for an attachment, generate a guid for it like the client does
  2281. }
  2282. if (!bSkip && ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_RECORD_KEY, lpsSaveObj->ulServerId, 0, 0, ulParentObjId, lpsSaveObj->ulObjType, &lpsReturnObj->modProps.__ptr[n]) == erSuccess)
  2283. lpsReturnObj->delProps.__ptr[n++] = PR_RECORD_KEY;
  2284. }
  2285. if (lpsSaveObj->ulObjType != MAPI_STORE) {
  2286. er = g_lpSessionManager->GetCacheManager()->GetObject(ulParentObjId, NULL, NULL, NULL, &ulParentObjType);
  2287. if(er != erSuccess)
  2288. goto exit;
  2289. }
  2290. //PR_PARENT_SOURCE_KEY for folders and messages
  2291. if (lpsSaveObj->ulObjType == MAPI_FOLDER || (lpsSaveObj->ulObjType == MAPI_MESSAGE && ulParentObjType == MAPI_FOLDER))
  2292. {
  2293. if(ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_PARENT_SOURCE_KEY, ulObjId, 0, 0, ulParentObjId, lpsSaveObj->ulObjType, &lpsReturnObj->modProps.__ptr[n]) == erSuccess)
  2294. lpsReturnObj->delProps.__ptr[n++] = PR_PARENT_SOURCE_KEY;
  2295. if(ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_SOURCE_KEY, ulObjId, 0, 0, ulParentObjId, lpsSaveObj->ulObjType, &lpsReturnObj->modProps.__ptr[n]) == erSuccess)
  2296. lpsReturnObj->delProps.__ptr[n++] = PR_SOURCE_KEY;
  2297. }
  2298. // PR_LAST_MODIFICATION_TIME
  2299. lpsReturnObj->delProps.__ptr[n] = PR_LAST_MODIFICATION_TIME;
  2300. lpsReturnObj->modProps.__ptr[n].__union = SOAP_UNION_propValData_hilo;
  2301. lpsReturnObj->modProps.__ptr[n].ulPropTag = PR_LAST_MODIFICATION_TIME;
  2302. lpsReturnObj->modProps.__ptr[n].Value.hilo = s_alloc<hiloLong>(soap);
  2303. lpsReturnObj->modProps.__ptr[n].Value.hilo->hi = ftModified.dwHighDateTime;
  2304. lpsReturnObj->modProps.__ptr[n].Value.hilo->lo = ftModified.dwLowDateTime;
  2305. ++n;
  2306. if (fNewItem)
  2307. {
  2308. lpsReturnObj->delProps.__ptr[n] = PR_CREATION_TIME;
  2309. lpsReturnObj->modProps.__ptr[n].__union = SOAP_UNION_propValData_hilo;
  2310. lpsReturnObj->modProps.__ptr[n].ulPropTag = PR_CREATION_TIME;
  2311. lpsReturnObj->modProps.__ptr[n].Value.hilo = s_alloc<hiloLong>(soap);
  2312. lpsReturnObj->modProps.__ptr[n].Value.hilo->hi = ftCreated.dwHighDateTime;
  2313. lpsReturnObj->modProps.__ptr[n].Value.hilo->lo = ftCreated.dwLowDateTime;
  2314. ++n;
  2315. }
  2316. // set actual array size
  2317. lpsReturnObj->delProps.__size = n;
  2318. lpsReturnObj->modProps.__size = n;
  2319. }
  2320. exit:
  2321. FreeDeletedItems(&lstDeleteItems);
  2322. return er;
  2323. }
  2324. SOAP_ENTRY_START(saveObject, lpsLoadObjectResponse->er, entryId sParentEntryId, entryId sEntryId, struct saveObject *lpsSaveObj, unsigned int ulFlags, unsigned int ulSyncId, struct loadObjectResponse *lpsLoadObjectResponse)
  2325. {
  2326. USE_DATABASE();
  2327. unsigned int ulStoreId = 0;
  2328. unsigned int ulGrandParent = 0;
  2329. unsigned int ulGrandParentType = 0;
  2330. unsigned int ulParentObjId = 0;
  2331. unsigned int ulParentObjType = 0;
  2332. unsigned int ulObjType = lpsSaveObj->ulObjType;
  2333. unsigned int ulObjFlags = 0;
  2334. unsigned int ulPrevReadState = 0;
  2335. unsigned int ulNewReadState = 0;
  2336. SOURCEKEY sSourceKey;
  2337. SOURCEKEY sParentSourceKey;
  2338. struct saveObject sReturnObject;
  2339. std::string strChangeKey;
  2340. std::string strChangeList;
  2341. BOOL fNewItem = false;
  2342. bool fHaveChangeKey = false;
  2343. unsigned int ulObjId = 0;
  2344. struct propVal *pvCommitTime = NULL;
  2345. object_ptr<ECAttachmentStorage> lpAttachmentStorage;
  2346. er = CreateAttachmentStorage(lpDatabase, &~lpAttachmentStorage);
  2347. if (er != erSuccess)
  2348. goto exit;
  2349. er = lpAttachmentStorage->Begin();
  2350. if (er != erSuccess)
  2351. goto exit;
  2352. er = lpDatabase->Begin();
  2353. if (er != erSuccess)
  2354. goto exit;
  2355. if (!sParentEntryId.__ptr) {
  2356. // saveObject is called on the store itself (doesn't have a parent)
  2357. ulParentObjType = MAPI_STORE;
  2358. ulStoreId = lpsSaveObj->ulServerId;
  2359. } else {
  2360. if (lpsSaveObj->ulServerId == 0) {
  2361. // new object, parent entry id given by client
  2362. er = lpecSession->GetObjectFromEntryId(&sParentEntryId, &ulParentObjId);
  2363. if (er != erSuccess)
  2364. goto exit;
  2365. fNewItem = true;
  2366. // Lock folder counters now
  2367. strQuery = "SELECT val_ulong FROM properties WHERE hierarchyid = " + stringify(ulParentObjId) + " FOR UPDATE";
  2368. er = lpDatabase->DoSelect(strQuery, NULL);
  2369. if (er != erSuccess)
  2370. goto exit;
  2371. } else {
  2372. // existing item, search parent ourselves because the client just sent its store entryid (see ECMsgStore::OpenEntry())
  2373. er = g_lpSessionManager->GetCacheManager()->GetObject(lpsSaveObj->ulServerId, &ulParentObjId, NULL, &ulObjFlags, &ulObjType);
  2374. if (er != erSuccess)
  2375. goto exit;
  2376. if (ulObjFlags & MSGFLAG_DELETED) {
  2377. er = KCERR_OBJECT_DELETED;
  2378. goto exit;
  2379. }
  2380. fNewItem = false;
  2381. // Lock folder counters now
  2382. strQuery = "SELECT val_ulong FROM properties WHERE hierarchyid = " + stringify(ulParentObjId) + " FOR UPDATE";
  2383. er = lpDatabase->DoSelect(strQuery, NULL);
  2384. if (er != erSuccess)
  2385. goto exit;
  2386. // 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
  2387. // that the read flags can only be modified through saveObject() when using ICS.
  2388. 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";
  2389. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  2390. if (er != erSuccess)
  2391. goto exit;
  2392. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  2393. if (lpDBRow == nullptr || lpDBRow[0] == nullptr)
  2394. ulPrevReadState = 0;
  2395. else
  2396. ulPrevReadState = atoui(lpDBRow[0]) & MSGFLAG_READ;
  2397. ulNewReadState = ulPrevReadState; // Copy read state, may be updated by SaveObject() later
  2398. }
  2399. if (ulParentObjId != CACHE_NO_PARENT) {
  2400. er = g_lpSessionManager->GetCacheManager()->GetObject(ulParentObjId, NULL, NULL, NULL, &ulParentObjType);
  2401. if(er != erSuccess)
  2402. goto exit;
  2403. er = lpecSession->GetSessionManager()->GetCacheManager()->GetStore(ulParentObjId, &ulStoreId, NULL);
  2404. if(er != erSuccess)
  2405. goto exit;
  2406. } else {
  2407. // we get here on create store, but I'm not exactly sure why :|
  2408. ulParentObjType = MAPI_STORE;
  2409. ulStoreId = lpsSaveObj->ulServerId;
  2410. }
  2411. }
  2412. if (ulParentObjId != 0 && ulParentObjType != MAPI_STORE) {
  2413. er = g_lpSessionManager->GetCacheManager()->GetObject(ulParentObjId, &ulGrandParent, NULL, NULL, &ulGrandParentType);
  2414. if(er != erSuccess)
  2415. goto exit;
  2416. }
  2417. // Check permissions
  2418. if (!fNewItem && ulObjType == MAPI_FOLDER)
  2419. er = lpecSession->GetSecurity()->CheckPermission(lpsSaveObj->ulServerId, ecSecurityFolderAccess);
  2420. else if(fNewItem)
  2421. er = lpecSession->GetSecurity()->CheckPermission(ulParentObjId, ecSecurityCreate);
  2422. else
  2423. er = lpecSession->GetSecurity()->CheckPermission(lpsSaveObj->ulServerId, ecSecurityEdit);
  2424. if(er != erSuccess)
  2425. goto exit;
  2426. // Quota check
  2427. if(ulObjType == MAPI_MESSAGE) {
  2428. er = CheckQuota(lpecSession, ulStoreId);
  2429. if(er != erSuccess)
  2430. goto exit;
  2431. // Update folder counts
  2432. if(fNewItem) {
  2433. er = UpdateFolderCounts(lpDatabase, ulParentObjId, ulFlags, &lpsSaveObj->modProps);
  2434. if (er != erSuccess)
  2435. goto exit;
  2436. }
  2437. else if(ulSyncId != 0) {
  2438. // On modified appointments, unread flags may have changed (only possible during ICS import)
  2439. for (gsoap_size_t i = 0; i < lpsSaveObj->modProps.__size; ++i)
  2440. if(lpsSaveObj->modProps.__ptr[i].ulPropTag == PR_MESSAGE_FLAGS) {
  2441. ulNewReadState = lpsSaveObj->modProps.__ptr[i].Value.ul & MSGFLAG_READ;
  2442. break;
  2443. }
  2444. if (ulPrevReadState != ulNewReadState) {
  2445. er = UpdateFolderCount(lpDatabase, ulParentObjId, PR_CONTENT_UNREAD, ulNewReadState == MSGFLAG_READ ? -1 : 1);
  2446. if (er != erSuccess)
  2447. goto exit;
  2448. }
  2449. }
  2450. }
  2451. er = SaveObject(soap, lpecSession, lpDatabase, lpAttachmentStorage, ulStoreId, ulParentObjId, ulParentObjType, ulFlags, ulSyncId, lpsSaveObj, &sReturnObject, atoui(g_lpSessionManager->GetConfig()->GetSetting("embedded_attachment_limit")), &fHaveChangeKey);
  2452. if (er != erSuccess)
  2453. goto exit;
  2454. // update PR_LOCAL_COMMIT_TIME_MAX for disconnected clients who want to know if the folder contents changed
  2455. if (ulObjType == MAPI_MESSAGE && ulParentObjType == MAPI_FOLDER) {
  2456. er = WriteLocalCommitTimeMax(soap, lpDatabase, ulParentObjId, &pvCommitTime);
  2457. if(er != erSuccess)
  2458. goto exit;
  2459. }
  2460. if (lpsSaveObj->ulServerId == 0) {
  2461. gsoap_size_t rki;
  2462. er = MapEntryIdToObjectId(lpecSession, lpDatabase, sReturnObject.ulServerId, sEntryId);
  2463. if (er != erSuccess)
  2464. goto exit;
  2465. ulObjId = sReturnObject.ulServerId;
  2466. // 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)
  2467. for (rki = 0; rki < sReturnObject.modProps.__size; ++rki)
  2468. if (sReturnObject.modProps.__ptr[rki].ulPropTag == PR_RECORD_KEY)
  2469. break;
  2470. // @note static alloc of 8 props in SaveObject. we did not find the record key: make it now
  2471. if (rki == sReturnObject.modProps.__size && rki < 8) {
  2472. ECGenProps::GetPropComputedUncached(soap, NULL, lpecSession, PR_RECORD_KEY, sReturnObject.ulServerId, 0, 0, ulParentObjId,
  2473. lpsSaveObj->ulObjType, &sReturnObject.modProps.__ptr[rki]);
  2474. ++sReturnObject.modProps.__size;
  2475. sReturnObject.delProps.__ptr[rki] = PR_RECORD_KEY;
  2476. ++sReturnObject.delProps.__size;
  2477. }
  2478. } else {
  2479. ulObjId = lpsSaveObj->ulServerId;
  2480. }
  2481. // 3. pr_source_key magic
  2482. if ((sReturnObject.ulObjType == MAPI_MESSAGE && ulParentObjType == MAPI_FOLDER) ||
  2483. (sReturnObject.ulObjType == MAPI_FOLDER && !(ulFlags & FOLDER_SEARCH)))
  2484. {
  2485. GetSourceKey(sReturnObject.ulServerId, &sSourceKey);
  2486. GetSourceKey(ulParentObjId, &sParentSourceKey);
  2487. if (sReturnObject.ulObjType == MAPI_MESSAGE && ulParentObjType == MAPI_FOLDER) {
  2488. if (lpsSaveObj->ulServerId == 0)
  2489. AddChange(lpecSession, ulSyncId, sSourceKey, sParentSourceKey, ICS_MESSAGE_NEW, 0, !fHaveChangeKey, &strChangeKey, &strChangeList);
  2490. else
  2491. AddChange(lpecSession, ulSyncId, sSourceKey, sParentSourceKey, ICS_MESSAGE_CHANGE, 0, !fHaveChangeKey, &strChangeKey, &strChangeList);
  2492. } else if (lpsSaveObj->ulObjType == MAPI_FOLDER && !(ulFlags & FOLDER_SEARCH)) {
  2493. AddChange(lpecSession, ulSyncId, sSourceKey, sParentSourceKey, ICS_FOLDER_CHANGE, 0, !fHaveChangeKey, &strChangeKey, &strChangeList);
  2494. }
  2495. if(!strChangeKey.empty()){
  2496. sReturnObject.delProps.__ptr[sReturnObject.delProps.__size] = PR_CHANGE_KEY;
  2497. ++sReturnObject.delProps.__size;
  2498. sReturnObject.modProps.__ptr[sReturnObject.modProps.__size].ulPropTag = PR_CHANGE_KEY;
  2499. sReturnObject.modProps.__ptr[sReturnObject.modProps.__size].__union = SOAP_UNION_propValData_bin;
  2500. sReturnObject.modProps.__ptr[sReturnObject.modProps.__size].Value.bin = s_alloc<struct xsd__base64Binary>(soap);
  2501. sReturnObject.modProps.__ptr[sReturnObject.modProps.__size].Value.bin->__size = strChangeKey.size();
  2502. sReturnObject.modProps.__ptr[sReturnObject.modProps.__size].Value.bin->__ptr = s_alloc<unsigned char>(soap, strChangeKey.size());
  2503. memcpy(sReturnObject.modProps.__ptr[sReturnObject.modProps.__size].Value.bin->__ptr, strChangeKey.c_str(), strChangeKey.size());
  2504. ++sReturnObject.modProps.__size;
  2505. }
  2506. if(!strChangeList.empty()){
  2507. sReturnObject.delProps.__ptr[sReturnObject.delProps.__size] = PR_PREDECESSOR_CHANGE_LIST;
  2508. ++sReturnObject.delProps.__size;
  2509. sReturnObject.modProps.__ptr[sReturnObject.modProps.__size].ulPropTag = PR_PREDECESSOR_CHANGE_LIST;
  2510. sReturnObject.modProps.__ptr[sReturnObject.modProps.__size].__union = SOAP_UNION_propValData_bin;
  2511. sReturnObject.modProps.__ptr[sReturnObject.modProps.__size].Value.bin = s_alloc<struct xsd__base64Binary>(soap);
  2512. sReturnObject.modProps.__ptr[sReturnObject.modProps.__size].Value.bin->__size = strChangeList.size();
  2513. sReturnObject.modProps.__ptr[sReturnObject.modProps.__size].Value.bin->__ptr = s_alloc<unsigned char>(soap, strChangeList.size());
  2514. memcpy(sReturnObject.modProps.__ptr[sReturnObject.modProps.__size].Value.bin->__ptr, strChangeList.c_str(), strChangeList.size());
  2515. ++sReturnObject.modProps.__size;
  2516. }
  2517. }
  2518. // 5. TODO: ulSyncId updates sync tables
  2519. // 6. process MSGFLAG_SUBMIT if needed
  2520. er = ProcessSubmitFlag(lpDatabase, ulSyncId, ulStoreId, ulObjId, fNewItem, &lpsSaveObj->modProps);
  2521. if (er != erSuccess)
  2522. goto exit;
  2523. if (ulParentObjType == MAPI_FOLDER) {
  2524. er = ECTPropsPurge::NormalizeDeferredUpdates(lpecSession, lpDatabase, ulParentObjId);
  2525. if (er != erSuccess)
  2526. goto exit;
  2527. }
  2528. er = lpAttachmentStorage->Commit();
  2529. if (er != erSuccess)
  2530. goto exit;
  2531. er = lpDatabase->Commit();
  2532. if (er != erSuccess)
  2533. goto exit;
  2534. // 7. notification
  2535. // Only Notify on MAPI_MESSAGE, MAPI_FOLDER and MAPI_STORE
  2536. // but don't nofity if parent object is a store and object type is attachment or message
  2537. CreateNotifications(ulObjId, ulObjType, ulParentObjId, ulGrandParent, fNewItem, &lpsSaveObj->modProps, pvCommitTime);
  2538. lpsLoadObjectResponse->sSaveObject = sReturnObject;
  2539. g_lpStatsCollector->Increment(SCN_DATABASE_MWOPS);
  2540. exit:
  2541. if (er != erSuccess && lpAttachmentStorage)
  2542. lpAttachmentStorage->Rollback();
  2543. ROLLBACK_ON_ERROR();
  2544. }
  2545. SOAP_ENTRY_END()
  2546. static ECRESULT LoadObject(struct soap *soap, ECSession *lpecSession,
  2547. unsigned int ulObjId, unsigned int ulObjType, unsigned int ulParentObjType,
  2548. struct saveObject *lpsSaveObj,
  2549. std::map<unsigned int, CHILDPROPS> *lpChildProps)
  2550. {
  2551. ECRESULT er = erSuccess;
  2552. object_ptr<ECAttachmentStorage> lpAttachmentStorage;
  2553. ULONG ulInstanceId = 0;
  2554. ULONG ulInstanceTag = 0;
  2555. struct saveObject sSavedObject;
  2556. GUID sGuidServer;
  2557. ChildPropsMap mapChildProps;
  2558. ChildPropsMap::const_iterator iterProps;
  2559. USE_DATABASE();
  2560. CHILDPROPS sEmptyProps;
  2561. sEmptyProps.lpPropVals = new DynamicPropValArray(soap);
  2562. sEmptyProps.lpPropTags = new DynamicPropTagArray(soap);
  2563. memset(&sSavedObject, 0, sizeof(saveObject));
  2564. // Check permission
  2565. if (ulObjType == MAPI_STORE || (ulObjType == MAPI_FOLDER && ulParentObjType == MAPI_STORE))
  2566. // Always read rights on the store and the root folder
  2567. er = erSuccess;
  2568. else if (ulObjType == MAPI_FOLDER)
  2569. er = lpecSession->GetSecurity()->CheckPermission(ulObjId, ecSecurityFolderVisible);
  2570. else if (ulObjType == MAPI_MESSAGE)
  2571. er = lpecSession->GetSecurity()->CheckPermission(ulObjId, ecSecurityRead);
  2572. // Allow reading MAPI_MAILUSER and MAPI_ATTACH since that is only called internally
  2573. if (er != erSuccess)
  2574. goto exit;
  2575. sSavedObject.ulClientId = 0;
  2576. sSavedObject.ulServerId = ulObjId;
  2577. sSavedObject.ulObjType = ulObjType;
  2578. if(lpChildProps == NULL) {
  2579. // We were not provided with a property list for this object, get our own now.
  2580. er = PrepareReadProps(soap, lpDatabase, true, lpecSession->GetCapabilities() & KOPANO_CAP_UNICODE, ulObjId, 0, MAX_PROP_SIZE, &mapChildProps, NULL);
  2581. if(er != erSuccess)
  2582. goto exit;
  2583. lpChildProps = &mapChildProps;
  2584. }
  2585. iterProps = lpChildProps->find(ulObjId);
  2586. if (iterProps == lpChildProps->cend())
  2587. er = ReadProps(soap, lpecSession, ulObjId, ulObjType, ulParentObjType, sEmptyProps, &sSavedObject.delProps, &sSavedObject.modProps);
  2588. else
  2589. er = ReadProps(soap, lpecSession, ulObjId, ulObjType, ulParentObjType, iterProps->second, &sSavedObject.delProps, &sSavedObject.modProps);
  2590. if (er != erSuccess)
  2591. goto exit;
  2592. FreeChildProps(&mapChildProps);
  2593. if (ulObjType == MAPI_MESSAGE || ulObjType == MAPI_ATTACH) {
  2594. // Pre-load *all* properties of *all* subobjects for fast accessibility
  2595. er = PrepareReadProps(soap, lpDatabase, true, lpecSession->GetCapabilities() & KOPANO_CAP_UNICODE, 0, ulObjId, MAX_PROP_SIZE, &mapChildProps, NULL);
  2596. if (er != erSuccess)
  2597. goto exit;
  2598. // find subobjects
  2599. strQuery = "SELECT id, type FROM hierarchy WHERE parent="+stringify(ulObjId);
  2600. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  2601. if(er != erSuccess)
  2602. goto exit;
  2603. sSavedObject.__size = lpDatabase->GetNumRows(lpDBResult);
  2604. sSavedObject.__ptr = s_alloc<saveObject>(soap, sSavedObject.__size);
  2605. memset(sSavedObject.__ptr, 0, sizeof(saveObject) * sSavedObject.__size);
  2606. for (gsoap_size_t i = 0; i < sSavedObject.__size; ++i) {
  2607. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  2608. lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
  2609. if(lpDBRow == NULL || lpDBLen == NULL) {
  2610. er = KCERR_DATABASE_ERROR; // this should never happen
  2611. ec_log_err("LoadObject(): no rows from db");
  2612. goto exit;
  2613. }
  2614. LoadObject(soap, lpecSession, atoi(lpDBRow[0]), atoi(lpDBRow[1]), ulObjType, &sSavedObject.__ptr[i], &mapChildProps);
  2615. }
  2616. FreeChildProps(&mapChildProps);
  2617. }
  2618. if (ulObjType == MAPI_MESSAGE || ulObjType == MAPI_ATTACH) {
  2619. er = CreateAttachmentStorage(lpDatabase, &~lpAttachmentStorage);
  2620. if (er != erSuccess)
  2621. goto exit;
  2622. er = g_lpSessionManager->GetServerGUID(&sGuidServer);
  2623. if (er != erSuccess)
  2624. goto exit;
  2625. /* Not having a single instance ID is not critical, we might create it at a later time */
  2626. // @todo this should be GetSingleInstanceIdsWithTags(ulObjId, map<tag, instanceid>);
  2627. if (ulObjType == MAPI_MESSAGE)
  2628. ulInstanceTag = PROP_ID(PR_EC_IMAP_EMAIL);
  2629. else
  2630. ulInstanceTag = PROP_ID(PR_ATTACH_DATA_BIN);
  2631. if (lpAttachmentStorage->GetSingleInstanceId(ulObjId, ulInstanceTag, &ulInstanceId) == erSuccess) {
  2632. sSavedObject.lpInstanceIds = s_alloc<entryList>(soap);
  2633. sSavedObject.lpInstanceIds->__size = 1;
  2634. sSavedObject.lpInstanceIds->__ptr = s_alloc<entryId>(soap, sSavedObject.lpInstanceIds->__size);
  2635. er = SIIDToEntryID(soap, &sGuidServer, ulInstanceId, ulInstanceTag, &sSavedObject.lpInstanceIds->__ptr[0]);
  2636. if (er != erSuccess) {
  2637. sSavedObject.lpInstanceIds->__size = 0;
  2638. goto exit;
  2639. }
  2640. }
  2641. }
  2642. if (ulObjType == MAPI_MESSAGE) {
  2643. // @todo: Check if we can do this on the fly to avoid the additional lookup.
  2644. for (gsoap_size_t i = 0; i < sSavedObject.modProps.__size; ++i) {
  2645. if (sSavedObject.modProps.__ptr[i].ulPropTag != PR_SUBMIT_FLAGS)
  2646. continue;
  2647. if (g_lpSessionManager->GetLockManager()->IsLocked(ulObjId, NULL))
  2648. sSavedObject.modProps.__ptr[i].Value.ul |= SUBMITFLAG_LOCKED;
  2649. else
  2650. sSavedObject.modProps.__ptr[i].Value.ul &= ~SUBMITFLAG_LOCKED;
  2651. }
  2652. }
  2653. *lpsSaveObj = std::move(sSavedObject);
  2654. exit:
  2655. delete sEmptyProps.lpPropVals;
  2656. delete sEmptyProps.lpPropTags;
  2657. return er;
  2658. }
  2659. SOAP_ENTRY_START(loadObject, lpsLoadObjectResponse->er, entryId sEntryId, struct notifySubscribe *lpsNotSubscribe, unsigned int ulFlags, struct loadObjectResponse *lpsLoadObjectResponse)
  2660. {
  2661. unsigned int ulObjId = 0;
  2662. unsigned int ulObjFlags = 0;
  2663. unsigned int ulObjType = 0;
  2664. unsigned int ulParentId = 0;
  2665. unsigned int ulParentObjType = 0;
  2666. unsigned int ulOwnerId = 0;
  2667. unsigned int ulEidFlags = 0;
  2668. USE_DATABASE();
  2669. struct saveObject sSavedObject;
  2670. er = BeginLockFolders(lpDatabase, EntryId(sEntryId), LOCK_SHARED);
  2671. if(er != erSuccess)
  2672. goto exit;
  2673. /*
  2674. * 2 Reasons to send KCERR_UNABLE_TO_COMPLETE (and have the client try to open the store elsewhere):
  2675. * 1. We can't find the object based on the entryid.
  2676. * 2. The owner of the store is not supposed to have a store on this server.
  2677. */
  2678. er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulObjId, &ulEidFlags);
  2679. if ((ulEidFlags & OPENSTORE_OVERRIDE_HOME_MDB) == 0) {
  2680. if (er == KCERR_NOT_FOUND && sEntryId.__size >= (int)min(sizeof(EID), sizeof(EID_V0)) && ((EID*)sEntryId.__ptr)->usType == MAPI_STORE)
  2681. er = KCERR_UNABLE_TO_COMPLETE; // Reason 1
  2682. }
  2683. if (er != erSuccess)
  2684. goto exit;
  2685. er = g_lpSessionManager->GetCacheManager()->GetObject(ulObjId, &ulParentId, &ulOwnerId, &ulObjFlags, &ulObjType);
  2686. if (er != erSuccess)
  2687. goto exit;
  2688. if(ulObjType == MAPI_STORE) {
  2689. if ((ulEidFlags & OPENSTORE_OVERRIDE_HOME_MDB) == 0) {
  2690. if (lpecSession->GetSessionManager()->IsDistributedSupported() && !lpecSession->GetUserManagement()->IsInternalObject(ulOwnerId)) {
  2691. objectdetails_t sUserDetails;
  2692. if (lpecSession->GetUserManagement()->GetObjectDetails(ulOwnerId, &sUserDetails) == erSuccess) {
  2693. unsigned int ulStoreType;
  2694. er = lpecSession->GetSessionManager()->GetCacheManager()->GetStoreAndType(ulObjId, NULL, NULL, &ulStoreType);
  2695. if (er != erSuccess)
  2696. goto exit;
  2697. if (ulStoreType == ECSTORE_TYPE_PRIVATE || ulStoreType == ECSTORE_TYPE_PUBLIC) {
  2698. std::string strServerName = sUserDetails.GetPropString(OB_PROP_S_SERVERNAME);
  2699. if (strServerName.empty()) {
  2700. er = KCERR_NOT_FOUND;
  2701. goto exit;
  2702. }
  2703. if (strcasecmp(strServerName.c_str(), g_lpSessionManager->GetConfig()->GetSetting("server_name")) != 0) {
  2704. er = KCERR_UNABLE_TO_COMPLETE; // Reason 2
  2705. goto exit;
  2706. }
  2707. } else if (ulStoreType == ECSTORE_TYPE_ARCHIVE) {
  2708. // We allow an archive store to be opened by sysadmins even if it's not supposed
  2709. // to exist on this server for a particular user.
  2710. if (lpecSession->GetSecurity()->GetAdminLevel() < ADMIN_LEVEL_SYSADMIN &&
  2711. !sUserDetails.PropListStringContains((property_key_t)PR_EC_ARCHIVE_SERVERS_A, g_lpSessionManager->GetConfig()->GetSetting("server_name"), true))
  2712. {
  2713. er = KCERR_NOT_FOUND;
  2714. goto exit;
  2715. }
  2716. } else {
  2717. er = KCERR_NOT_FOUND;
  2718. goto exit;
  2719. }
  2720. } else {
  2721. // unhooked store of a deleted user
  2722. if(lpecSession->GetSecurity()->GetAdminLevel() < ADMIN_LEVEL_SYSADMIN) {
  2723. er = KCERR_NO_ACCESS;
  2724. goto exit;
  2725. }
  2726. }
  2727. }
  2728. }
  2729. ulParentObjType = 0;
  2730. } else if(ulObjType == MAPI_MESSAGE) {
  2731. // If the object is locked on another session, access should be denied
  2732. ECSESSIONID ulLockedSessionId;
  2733. if (g_lpSessionManager->GetLockManager()->IsLocked(ulObjId, &ulLockedSessionId) && ulLockedSessionId != ulSessionId) {
  2734. er = KCERR_NO_ACCESS;
  2735. goto exit;
  2736. }
  2737. ulParentObjType = MAPI_FOLDER;
  2738. } else if(ulObjType == MAPI_FOLDER) {
  2739. er = g_lpSessionManager->GetCacheManager()->GetObject(ulParentId, NULL, NULL, NULL, &ulParentObjType);
  2740. if (er != erSuccess)
  2741. goto exit;
  2742. // Reset folder counts now (Note: runs in a DB transaction!). Note: we only update counts when lpNotSubscribe is not NULL; this
  2743. // makes sure that we only reset folder counts on the first open of a folder, and not when the folder properties are updated (eg
  2744. // due to counter changes)
  2745. if(lpsNotSubscribe && ulObjFlags != FOLDER_SEARCH && parseBool(g_lpSessionManager->GetConfig()->GetSetting("counter_reset")))
  2746. ResetFolderCount(lpecSession, ulObjId);
  2747. }
  2748. // check if flags were passed, older clients call checkExistObject
  2749. if(ulFlags & 0x80000000) {
  2750. // Flags passed by client, check object flags
  2751. ulFlags = ulFlags & ~0x80000000;
  2752. if((ulObjFlags & MSGFLAG_DELETED) != ulFlags) {
  2753. if (ulObjType == MAPI_STORE)
  2754. er = KCERR_UNABLE_TO_COMPLETE;
  2755. else
  2756. er = KCERR_NOT_FOUND;
  2757. goto exit;
  2758. }
  2759. }
  2760. // Subscribe for notification
  2761. if (lpsNotSubscribe) {
  2762. er = DoNotifySubscribe(lpecSession, ulSessionId, lpsNotSubscribe);
  2763. if (er != erSuccess)
  2764. goto exit;
  2765. }
  2766. er = LoadObject(soap, lpecSession, ulObjId, ulObjType, ulParentObjType, &sSavedObject, NULL);
  2767. if (er != erSuccess)
  2768. goto exit;
  2769. lpsLoadObjectResponse->sSaveObject = sSavedObject;
  2770. g_lpStatsCollector->Increment(SCN_DATABASE_MROPS);
  2771. exit:
  2772. lpDatabase->Commit();
  2773. }
  2774. SOAP_ENTRY_END()
  2775. // if lpsNewEntryId is NULL this function create a new entryid
  2776. // if lpsOrigSourceKey is NULL this function creates a new sourcekey
  2777. static ECRESULT CreateFolder(ECSession *lpecSession, ECDatabase *lpDatabase,
  2778. unsigned int ulParentId, entryId *lpsNewEntryId, unsigned int type,
  2779. const char *name, char *comment, bool openifexists, bool bNotify,
  2780. unsigned int ulSyncId, const struct xsd__base64Binary *lpsOrigSourceKey,
  2781. unsigned int *lpFolderId, bool *lpbExist)
  2782. {
  2783. ECRESULT er = erSuccess;
  2784. ALLOC_DBRESULT();
  2785. unsigned int ulFolderId = 0;
  2786. unsigned int ulLastId = 0;
  2787. unsigned int ulStoreId = 0;
  2788. unsigned int ulGrandParent = 0;
  2789. bool bExist = false;
  2790. bool bFreeNewEntryId = false;
  2791. GUID guid;
  2792. SOURCEKEY sSourceKey;
  2793. 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 };
  2794. unsigned int timeTags [] = { PR_LAST_MODIFICATION_TIME, PR_CREATION_TIME };
  2795. time_t now = 0;
  2796. struct propVal sProp;
  2797. struct hiloLong sHilo;
  2798. er = lpecSession->GetSessionManager()->GetCacheManager()->GetStore(ulParentId, &ulStoreId, &guid);
  2799. if(er != erSuccess)
  2800. goto exit;
  2801. er = g_lpSessionManager->GetCacheManager()->GetParent(ulParentId, &ulGrandParent);
  2802. if(er != erSuccess)
  2803. goto exit;
  2804. // Check whether the requested name already exists
  2805. 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);
  2806. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  2807. if(er != erSuccess)
  2808. goto exit;
  2809. while (!bExist && (lpDBRow = lpDatabase->FetchRow(lpDBResult)) != NULL) {
  2810. if (lpDBRow[0] == NULL || lpDBRow[1] == NULL) {
  2811. er = KCERR_DATABASE_ERROR;
  2812. ec_log_err("CreateFolder(): columns null");
  2813. goto exit;
  2814. }
  2815. if (strcasecmp(lpDBRow[1], name) == 0)
  2816. bExist = true;
  2817. }
  2818. if(bExist && !ulSyncId) {
  2819. // Check folder read access
  2820. er = lpecSession->GetSecurity()->CheckPermission(ulParentId, ecSecurityFolderVisible);
  2821. if(er != erSuccess)
  2822. goto exit;
  2823. // Object exists
  2824. if (!openifexists) {
  2825. er = KCERR_COLLISION;
  2826. ec_log_err("CreateFolder(): folder already exists");
  2827. goto exit;
  2828. }
  2829. ulFolderId = atoi(lpDBRow[0]);
  2830. }
  2831. else {
  2832. // Check write permission of the folder destination
  2833. er = lpecSession->GetSecurity()->CheckPermission(ulParentId, ecSecurityCreateFolder);
  2834. if(er != erSuccess)
  2835. goto exit;
  2836. // Create folder
  2837. strQuery = "INSERT INTO hierarchy (parent, type, flags, owner) values(" + stringify(ulParentId) + "," + stringify(KOPANO_OBJTYPE_FOLDER) + ", " + stringify(type) + ", "+stringify(lpecSession->GetSecurity()->GetUserId(ulParentId))+")";
  2838. er = lpDatabase->DoInsert(strQuery, &ulLastId);
  2839. if(er != erSuccess)
  2840. goto exit;
  2841. if(lpsNewEntryId == NULL) {
  2842. er = CreateEntryId(guid, MAPI_FOLDER, &lpsNewEntryId);
  2843. if(er != erSuccess)
  2844. goto exit;
  2845. bFreeNewEntryId = true;
  2846. }
  2847. //Create entryid, 0x0FFF = PR_ENTRYID
  2848. er = RemoveStaleIndexedProp(lpDatabase, PR_ENTRYID, lpsNewEntryId->__ptr, lpsNewEntryId->__size);
  2849. if(er != erSuccess)
  2850. goto exit;
  2851. strQuery = "INSERT INTO indexedproperties (hierarchyid,tag,val_binary) VALUES("+stringify(ulLastId)+", 0x0FFF, "+lpDatabase->EscapeBinary(lpsNewEntryId->__ptr, lpsNewEntryId->__size)+")";
  2852. er = lpDatabase->DoInsert(strQuery);
  2853. if(er != erSuccess)
  2854. goto exit;
  2855. // Create Displayname
  2856. strQuery = "INSERT INTO properties (hierarchyid, tag, type, val_string) values(" + stringify(ulLastId) + "," + stringify(KOPANO_TAG_DISPLAY_NAME) + "," + stringify(PT_STRING8) + ",'" + lpDatabase->Escape(name) + "')";
  2857. er = lpDatabase->DoInsert(strQuery);
  2858. if(er != erSuccess)
  2859. goto exit;
  2860. // Create Displayname
  2861. 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) + "')";
  2862. er = lpDatabase->DoInsert(strQuery);
  2863. if(er != erSuccess)
  2864. goto exit;
  2865. // Create counters
  2866. for (size_t i = 0; i < ARRAY_SIZE(tags); ++i) {
  2867. sProp.ulPropTag = tags[i];
  2868. sProp.__union = SOAP_UNION_propValData_ul;
  2869. sProp.Value.ul = 0;
  2870. er = WriteProp(lpDatabase, ulLastId, ulParentId, &sProp);
  2871. if(er != erSuccess)
  2872. goto exit;
  2873. }
  2874. // Create PR_SUBFOLDERS
  2875. sProp.ulPropTag = PR_SUBFOLDERS;
  2876. sProp.__union = SOAP_UNION_propValData_b;
  2877. sProp.Value.b = false;
  2878. er = WriteProp(lpDatabase, ulLastId, ulParentId, &sProp);
  2879. if(er != erSuccess)
  2880. goto exit;
  2881. // Create PR_FOLDERTYPE
  2882. sProp.ulPropTag = PR_FOLDER_TYPE;
  2883. sProp.__union = SOAP_UNION_propValData_ul;
  2884. sProp.Value.ul = type;
  2885. er = WriteProp(lpDatabase, ulLastId, ulParentId, &sProp);
  2886. if(er != erSuccess)
  2887. goto exit;
  2888. // Create PR_COMMENT
  2889. if (comment) {
  2890. sProp.ulPropTag = PR_COMMENT_A;
  2891. sProp.__union = SOAP_UNION_propValData_lpszA;
  2892. sProp.Value.lpszA = comment;
  2893. er = WriteProp(lpDatabase, ulLastId, ulParentId, &sProp);
  2894. if(er != erSuccess)
  2895. goto exit;
  2896. }
  2897. // Create PR_LAST_MODIFICATION_TIME and PR_CREATION_TIME
  2898. now = time(NULL);
  2899. for (size_t i = 0; i < ARRAY_SIZE(timeTags); ++i) {
  2900. sProp.ulPropTag = timeTags[i];
  2901. sProp.__union = SOAP_UNION_propValData_hilo;
  2902. sProp.Value.hilo = &sHilo;
  2903. UnixTimeToFileTime(now, &sProp.Value.hilo->hi, &sProp.Value.hilo->lo);
  2904. er = WriteProp(lpDatabase, ulLastId, ulParentId, &sProp);
  2905. if(er != erSuccess)
  2906. goto exit;
  2907. }
  2908. // Create SourceKey
  2909. if (lpsOrigSourceKey && lpsOrigSourceKey->__size > (int)sizeof(GUID) && lpsOrigSourceKey->__ptr){
  2910. sSourceKey = SOURCEKEY(lpsOrigSourceKey->__size, (char*)lpsOrigSourceKey->__ptr);
  2911. }else{
  2912. er = lpecSession->GetNewSourceKey(&sSourceKey);
  2913. if(er != erSuccess)
  2914. goto exit;
  2915. }
  2916. er = RemoveStaleIndexedProp(lpDatabase, PR_SOURCE_KEY, sSourceKey, sSourceKey.size());
  2917. if(er != erSuccess)
  2918. goto exit;
  2919. strQuery = "INSERT INTO indexedproperties(hierarchyid,tag,val_binary) VALUES(" + stringify(ulLastId) + "," + stringify(PROP_ID(PR_SOURCE_KEY)) + "," + lpDatabase->EscapeBinary(sSourceKey, sSourceKey.size()) + ")";
  2920. er = lpDatabase->DoInsert(strQuery);
  2921. if(er != erSuccess)
  2922. goto exit;
  2923. ulFolderId = ulLastId;
  2924. er = UpdateFolderCount(lpDatabase, ulParentId, PR_SUBFOLDERS, 1);
  2925. if(er != erSuccess)
  2926. goto exit;
  2927. er = UpdateFolderCount(lpDatabase, ulParentId, PR_FOLDER_CHILD_COUNT, 1);
  2928. if(er != erSuccess)
  2929. goto exit;
  2930. }
  2931. if(bExist == false && !(type & FOLDER_SEARCH)){
  2932. SOURCEKEY sParentSourceKey;
  2933. GetSourceKey(ulParentId, &sParentSourceKey);
  2934. AddChange(lpecSession, ulSyncId, sSourceKey, sParentSourceKey, ICS_FOLDER_NEW);
  2935. }
  2936. // Notify that the folder has been created
  2937. if(bExist == false && bNotify == true) {
  2938. g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, ulParentId);
  2939. g_lpSessionManager->NotificationCreated(MAPI_FOLDER, ulFolderId, ulParentId);
  2940. g_lpSessionManager->NotificationModified(MAPI_FOLDER, ulParentId);
  2941. // Update all tables viewing this folder
  2942. g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_ADD, 0, ulParentId, ulFolderId, MAPI_FOLDER);
  2943. // Update notification, grandparent of the mainfolder
  2944. g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY, 0, ulGrandParent, ulParentId, MAPI_FOLDER);
  2945. }
  2946. if(lpFolderId)
  2947. *lpFolderId = ulFolderId;
  2948. if(lpbExist)
  2949. *lpbExist = bExist;
  2950. exit:
  2951. if(bFreeNewEntryId == true && lpsNewEntryId)
  2952. FreeEntryId(lpsNewEntryId, true);
  2953. return er;
  2954. }
  2955. /**
  2956. * createFolder: Create a folder object in the hierarchy table, and add a 'PR_DISPLAY_NAME' property.
  2957. *
  2958. * The data model actually supports multiple folders having the same PR_DISPLAY_NAME, however, MAPI does not,
  2959. * so we have to give the engine the knowledge of the PR_DISPLAY_NAME property here, one of the few properties
  2960. * that the backend engine actually knows about.
  2961. *
  2962. * Of course, the frontend could also enforce this constraint, but that would require 2 server accesses (1. check
  2963. * existing, 2. create folder), and we're trying to keep the amount of server accesses as low as possible.
  2964. *
  2965. */
  2966. 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)
  2967. {
  2968. unsigned int ulParentId = 0;
  2969. unsigned int ulFolderId = 0;
  2970. USE_DATABASE();
  2971. if(szName == NULL) {
  2972. er = KCERR_INVALID_PARAMETER;
  2973. goto exit;
  2974. }
  2975. // Fixup the input strings
  2976. szName = STRIN_FIX(szName);
  2977. szComment = STRIN_FIX(szComment);
  2978. er = lpDatabase->Begin();
  2979. if (er != erSuccess)
  2980. goto exit;
  2981. er = lpecSession->GetObjectFromEntryId(&sParentId, &ulParentId);
  2982. if (er != erSuccess)
  2983. goto exit;
  2984. er = CreateFolder(lpecSession, lpDatabase, ulParentId, lpsNewEntryId, ulType, szName, szComment, fOpenIfExists, true, ulSyncId, &sOrigSourceKey, &ulFolderId, NULL);
  2985. if (er != erSuccess)
  2986. goto exit;
  2987. er = lpDatabase->Commit();
  2988. if (er != erSuccess)
  2989. goto exit;
  2990. er = g_lpSessionManager->GetCacheManager()->GetEntryIdFromObject(ulFolderId, soap, 0, &lpsResponse->sEntryId);
  2991. if (er != erSuccess)
  2992. goto exit;
  2993. exit:
  2994. ROLLBACK_ON_ERROR();
  2995. }
  2996. SOAP_ENTRY_END()
  2997. /**
  2998. * tableOpen: Open a mapi table
  2999. *
  3000. * @param[in] lpecSession server session object, cannot be NULL
  3001. * @param[in] sEntryId entryid data
  3002. * @param[in] ulTableType the type of table to open:
  3003. * TABLETYPE_MS
  3004. * For all tables of a messagestore.
  3005. * TABLETYPE_AB
  3006. * For the addressbook tables.
  3007. * TABLETYPE_SPOOLER
  3008. * For the spooler tables, the sEntryId must always the store entryid.
  3009. * ulType and ulFlags are ignored, reserved for future use.
  3010. * TABLE_TYPE_MULTISTORE
  3011. * Special Kopano-only table to have given objects from different stores in one table view.
  3012. * TABLE_TYPE_USERSTORES
  3013. * Special Kopano-only table. Lists all combinations of users and stores on this server. (Used for the orphan management in kopano-admin).
  3014. * TABLE_TYPE_STATS_*
  3015. * Special Kopano-only tables. Used for various statistics and other uses.
  3016. * @param[in] ulType the type of the object you want to open.
  3017. * @param[in] ulFlags ulFlags from the client
  3018. * MAPI_ASSOCIATED
  3019. * List associated messages/folders instead of normal messages/folders.
  3020. * MAPI_UNICODE
  3021. * Default and all columns will contain _W string properties, otherwise _A strings are used.
  3022. * CONVENIENT_DEPTH
  3023. * Returns a convenient depth (flat list) of all folders.
  3024. * SHOW_SOFT_DELETES
  3025. * List deleted items in this table, rewritten to MSGFLAG_DELETED.
  3026. * EC_TABLE_NOCAP
  3027. * Do not cap string entries to 255 bytes, an extension of ours.
  3028. *
  3029. * @param[out] lpulTableId Server table id for this new table.
  3030. */
  3031. static ECRESULT OpenTable(ECSession *lpecSession, entryId sEntryId,
  3032. unsigned int ulTableType, unsigned int ulType, unsigned int ulFlags,
  3033. unsigned int *lpulTableId)
  3034. {
  3035. ECRESULT er;
  3036. objectid_t sExternId;
  3037. unsigned int ulTableId = 0;
  3038. unsigned int ulId = 0;
  3039. unsigned int ulTypeId = 0;
  3040. switch (ulTableType) {
  3041. case TABLETYPE_MS:
  3042. if (ulFlags & SHOW_SOFT_DELETES)
  3043. {
  3044. ulFlags &=~SHOW_SOFT_DELETES;
  3045. ulFlags |= MSGFLAG_DELETED;
  3046. }
  3047. er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulId);
  3048. if (er != erSuccess)
  3049. return er;
  3050. er = lpecSession->GetTableManager()->OpenGenericTable(ulId, ulType, ulFlags, &ulTableId);
  3051. if (er != erSuccess)
  3052. return er;
  3053. break;
  3054. case TABLETYPE_AB:
  3055. er = ABEntryIDToID(&sEntryId, &ulId, &sExternId, &ulTypeId);
  3056. if (er != erSuccess)
  3057. return er;
  3058. // If an extern id is present, we should get an object based on that.
  3059. if (!sExternId.id.empty()) {
  3060. er = g_lpSessionManager->GetCacheManager()->GetUserObject(sExternId, &ulId, NULL, NULL);
  3061. if (er != erSuccess)
  3062. return er;
  3063. }
  3064. er = lpecSession->GetTableManager()->OpenABTable(ulId, ulTypeId, ulType, ulFlags, &ulTableId);
  3065. if (er != erSuccess)
  3066. return er;
  3067. break;
  3068. case TABLETYPE_SPOOLER:
  3069. // sEntryId must be a store entryid or zero for all stores
  3070. if (sEntryId.__size > 0) {
  3071. er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulId);
  3072. if (er != erSuccess)
  3073. return er;
  3074. } else
  3075. ulId = 0; //All stores
  3076. er = lpecSession->GetTableManager()->OpenOutgoingQueueTable(ulId, &ulTableId);
  3077. if (er != erSuccess)
  3078. return er;
  3079. break;
  3080. case TABLETYPE_MULTISTORE:
  3081. er = lpecSession->GetTableManager()->OpenMultiStoreTable(ulType, ulFlags, &ulTableId);
  3082. if (er != erSuccess)
  3083. return er;
  3084. break;
  3085. case TABLETYPE_USERSTORES:
  3086. er = lpecSession->GetTableManager()->OpenUserStoresTable(ulFlags, &ulTableId);
  3087. if (er != erSuccess)
  3088. return er;
  3089. break;
  3090. case TABLETYPE_STATS_SYSTEM:
  3091. case TABLETYPE_STATS_SESSIONS:
  3092. case TABLETYPE_STATS_USERS:
  3093. case TABLETYPE_STATS_COMPANY:
  3094. case TABLETYPE_STATS_SERVERS:
  3095. er = lpecSession->GetTableManager()->OpenStatsTable(ulTableType, ulFlags, &ulTableId);
  3096. if (er != erSuccess)
  3097. return er;
  3098. break;
  3099. case TABLETYPE_MAILBOX:
  3100. er = lpecSession->GetTableManager()->OpenMailBoxTable(ulFlags, &ulTableId);
  3101. if (er != erSuccess)
  3102. return er;
  3103. break;
  3104. default:
  3105. return KCERR_BAD_VALUE;
  3106. break; //Happy compiler
  3107. } // switch (ulTableType)
  3108. *lpulTableId = ulTableId;
  3109. return erSuccess;
  3110. }
  3111. SOAP_ENTRY_START(tableOpen, lpsTableOpenResponse->er, entryId sEntryId, unsigned int ulTableType, unsigned ulType, unsigned int ulFlags, struct tableOpenResponse *lpsTableOpenResponse)
  3112. {
  3113. unsigned int ulTableId = 0;
  3114. er = OpenTable(lpecSession, sEntryId, ulTableType, ulType, ulFlags, &ulTableId);
  3115. if (er != erSuccess)
  3116. return er;
  3117. lpsTableOpenResponse->ulTableId = ulTableId;
  3118. return erSuccess;
  3119. }
  3120. SOAP_ENTRY_END()
  3121. /**
  3122. * tableClose: close the table with the specified table ID
  3123. */
  3124. SOAP_ENTRY_START(tableClose, *result, unsigned int ulTableId, unsigned int *result)
  3125. {
  3126. return lpecSession->GetTableManager()->CloseTable(ulTableId);
  3127. }
  3128. SOAP_ENTRY_END()
  3129. /**
  3130. * tableSetSearchCritieria: set search criteria for a searchfolder
  3131. */
  3132. SOAP_ENTRY_START(tableSetSearchCriteria, *result, entryId sEntryId, struct restrictTable *lpRestrict, struct entryList *lpFolders, unsigned int ulFlags, unsigned int *result)
  3133. {
  3134. unsigned int ulStoreId = 0;
  3135. unsigned int ulParent = 0;
  3136. if (!(ulFlags & STOP_SEARCH) &&
  3137. (lpRestrict == nullptr || lpFolders == nullptr))
  3138. return KCERR_INVALID_PARAMETER;
  3139. er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulParent);
  3140. if(er != erSuccess)
  3141. return er;
  3142. er = lpecSession->GetSessionManager()->GetCacheManager()->GetStore(ulParent, &ulStoreId, NULL);
  3143. if(er != erSuccess)
  3144. return er;
  3145. // Check permission
  3146. er = lpecSession->GetSecurity()->CheckPermission(ulParent, ecSecurityEdit);
  3147. if(er != erSuccess)
  3148. return er;
  3149. // If a STOP was requested, then that's all we need to do
  3150. if(ulFlags & STOP_SEARCH) {
  3151. return lpecSession->GetSessionManager()->GetSearchFolders()->SetSearchCriteria(ulStoreId, ulParent, nullptr);
  3152. } else {
  3153. struct searchCriteria sSearchCriteria;
  3154. if (!bSupportUnicode) {
  3155. er = FixRestrictionEncoding(soap, stringCompat, In, lpRestrict);
  3156. if (er != erSuccess)
  3157. return er;
  3158. }
  3159. sSearchCriteria.lpRestrict = lpRestrict;
  3160. sSearchCriteria.lpFolders = lpFolders;
  3161. sSearchCriteria.ulFlags = ulFlags;
  3162. return lpecSession->GetSessionManager()->GetSearchFolders()->SetSearchCriteria(ulStoreId, ulParent, &sSearchCriteria);
  3163. }
  3164. }
  3165. SOAP_ENTRY_END()
  3166. /**
  3167. * tableGetSearchCriteria: get the search criteria for a searchfolder previously called with tableSetSearchCriteria
  3168. */
  3169. SOAP_ENTRY_START(tableGetSearchCriteria, lpsResponse->er, entryId sEntryId, struct tableGetSearchCriteriaResponse *lpsResponse)
  3170. {
  3171. unsigned int ulFlags = 0;
  3172. unsigned int ulStoreId = 0;
  3173. unsigned int ulId = 0;
  3174. struct searchCriteria *lpSearchCriteria = NULL;
  3175. er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulId);
  3176. if(er != erSuccess)
  3177. goto exit;
  3178. er = g_lpSessionManager->GetCacheManager()->GetStore(ulId, &ulStoreId, NULL);
  3179. if(er != erSuccess)
  3180. goto exit;
  3181. // Check permission
  3182. er = lpecSession->GetSecurity()->CheckPermission(ulId, ecSecurityRead);
  3183. if(er != erSuccess)
  3184. goto exit;
  3185. er = lpecSession->GetSessionManager()->GetSearchFolders()->GetSearchCriteria(ulStoreId, ulId, &lpSearchCriteria, &ulFlags);
  3186. if(er != erSuccess)
  3187. goto exit;
  3188. er = CopyRestrictTable(soap, lpSearchCriteria->lpRestrict, &lpsResponse->lpRestrict);
  3189. if(er != erSuccess)
  3190. goto exit;
  3191. if (!bSupportUnicode) {
  3192. er = FixRestrictionEncoding(soap, stringCompat, Out, lpsResponse->lpRestrict);
  3193. if (er != erSuccess)
  3194. goto exit;
  3195. }
  3196. er = CopyEntryList(soap, lpSearchCriteria->lpFolders, &lpsResponse->lpFolderIDs);
  3197. if(er != erSuccess)
  3198. goto exit;
  3199. lpsResponse->ulFlags = ulFlags;
  3200. exit:
  3201. if(lpSearchCriteria)
  3202. FreeSearchCriteria(lpSearchCriteria);
  3203. }
  3204. SOAP_ENTRY_END()
  3205. /**
  3206. * tableSetColumns: called from IMAPITable::SetColumns()
  3207. */
  3208. SOAP_ENTRY_START(tableSetColumns, *result, unsigned int ulTableId, struct propTagArray *aPropTag, unsigned int *result)
  3209. {
  3210. object_ptr<ECGenericObjectTable> lpTable;
  3211. er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
  3212. if(er != erSuccess)
  3213. return er;
  3214. return lpTable->SetColumns(aPropTag, false);
  3215. }
  3216. SOAP_ENTRY_END()
  3217. /**
  3218. * tableQueryColumns: called from IMAPITable::GetColumns()
  3219. */
  3220. SOAP_ENTRY_START(tableQueryColumns, lpsResponse->er, unsigned int ulTableId, unsigned int ulFlags, struct tableQueryColumnsResponse *lpsResponse)
  3221. {
  3222. object_ptr<ECGenericObjectTable> lpTable;
  3223. struct propTagArray *lpPropTags = NULL;
  3224. // Init
  3225. lpsResponse->sPropTagArray.__size = 0;
  3226. lpsResponse->sPropTagArray.__ptr = NULL;
  3227. er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
  3228. if(er != erSuccess)
  3229. return er;
  3230. er = lpTable->GetColumns(soap, ulFlags, &lpPropTags);
  3231. if(er != erSuccess)
  3232. return er;
  3233. lpsResponse->sPropTagArray.__size = lpPropTags->__size;
  3234. lpsResponse->sPropTagArray.__ptr = lpPropTags->__ptr;
  3235. return erSuccess;
  3236. }
  3237. SOAP_ENTRY_END()
  3238. /**
  3239. * tableRestrict: called from IMAPITable::Restrict()
  3240. */
  3241. SOAP_ENTRY_START(tableRestrict, *result, unsigned int ulTableId, struct restrictTable *lpsRestrict, unsigned int *result)
  3242. {
  3243. object_ptr<ECGenericObjectTable> lpTable;
  3244. er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
  3245. if(er != erSuccess)
  3246. return er;
  3247. if (!bSupportUnicode) {
  3248. er = FixRestrictionEncoding(soap, stringCompat, In, lpsRestrict);
  3249. if (er != erSuccess)
  3250. return er;
  3251. }
  3252. return lpTable->Restrict(lpsRestrict);
  3253. }
  3254. SOAP_ENTRY_END()
  3255. /**
  3256. * tableSort: called from IMAPITable::Sort()
  3257. */
  3258. SOAP_ENTRY_START(tableSort, *result, unsigned int ulTableId, struct sortOrderArray *lpSortOrder, unsigned int ulCategories, unsigned int ulExpanded, unsigned int *result)
  3259. {
  3260. object_ptr<ECGenericObjectTable> lpTable;
  3261. if (lpSortOrder == nullptr)
  3262. return KCERR_INVALID_PARAMETER;
  3263. er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
  3264. if(er != erSuccess)
  3265. return er;
  3266. return lpTable->SetSortOrder(lpSortOrder, ulCategories, ulExpanded);
  3267. }
  3268. SOAP_ENTRY_END()
  3269. /**
  3270. * tableQueryRows: called from IMAPITable::QueryRows()
  3271. */
  3272. SOAP_ENTRY_START(tableQueryRows, lpsResponse->er, unsigned int ulTableId, unsigned int ulRowCount, unsigned int ulFlags, struct tableQueryRowsResponse *lpsResponse)
  3273. {
  3274. object_ptr<ECGenericObjectTable> lpTable;
  3275. struct rowSet *lpRowSet = NULL;
  3276. lpsResponse->sRowSet.__ptr = NULL;
  3277. lpsResponse->sRowSet.__size = 0;
  3278. // Get the table
  3279. er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
  3280. if(er != erSuccess)
  3281. return er;
  3282. // FIXME: Check permission
  3283. er = lpTable->QueryRows(soap, ulRowCount, ulFlags, &lpRowSet);
  3284. if(er != erSuccess)
  3285. return er;
  3286. if (!bSupportUnicode) {
  3287. er = FixRowSetEncoding(soap, stringCompat, Out, lpRowSet);
  3288. if (er != erSuccess)
  3289. return er;
  3290. }
  3291. lpsResponse->sRowSet.__ptr = lpRowSet->__ptr;
  3292. lpsResponse->sRowSet.__size = lpRowSet->__size;
  3293. return erSuccess;
  3294. }
  3295. SOAP_ENTRY_END()
  3296. /**
  3297. * tableGetRowCount: called from IMAPITable::GetRowCount()
  3298. */
  3299. SOAP_ENTRY_START(tableGetRowCount, lpsResponse->er, unsigned int ulTableId, struct tableGetRowCountResponse *lpsResponse)
  3300. {
  3301. object_ptr<ECGenericObjectTable> lpTable;
  3302. //FIXME: security? give rowcount 0 is failed ?
  3303. er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
  3304. if(er != erSuccess)
  3305. return er;
  3306. return lpTable->GetRowCount(&lpsResponse->ulCount, &lpsResponse->ulRow);
  3307. }
  3308. SOAP_ENTRY_END()
  3309. /**
  3310. * tableSeekRow: called from IMAPITable::SeekRow()
  3311. */
  3312. SOAP_ENTRY_START(tableSeekRow, lpsResponse->er, unsigned int ulTableId , unsigned int ulBookmark, int lRows, struct tableSeekRowResponse *lpsResponse)
  3313. {
  3314. object_ptr<ECGenericObjectTable> lpTable;
  3315. er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
  3316. if(er != erSuccess)
  3317. return er;
  3318. return lpTable->SeekRow(ulBookmark, lRows, &lpsResponse->lRowsSought);
  3319. }
  3320. SOAP_ENTRY_END()
  3321. /**
  3322. * tableFindRow: called from IMAPITable::FindRow()
  3323. */
  3324. SOAP_ENTRY_START(tableFindRow, *result, unsigned int ulTableId ,unsigned int ulBookmark, unsigned int ulFlags, struct restrictTable *lpsRestrict, unsigned int *result)
  3325. {
  3326. object_ptr<ECGenericObjectTable> lpTable;
  3327. er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
  3328. if(er != erSuccess)
  3329. return er;
  3330. if (!bSupportUnicode) {
  3331. er = FixRestrictionEncoding(soap, stringCompat, In, lpsRestrict);
  3332. if (er != erSuccess)
  3333. return er;
  3334. }
  3335. return lpTable->FindRow(lpsRestrict, ulBookmark, ulFlags);
  3336. }
  3337. SOAP_ENTRY_END()
  3338. /**
  3339. * tableCreateBookmark: called from IMAPITable::CreateBookmark()
  3340. */
  3341. SOAP_ENTRY_START(tableCreateBookmark, lpsResponse->er, unsigned int ulTableId, struct tableBookmarkResponse *lpsResponse)
  3342. {
  3343. object_ptr<ECGenericObjectTable> lpTable;
  3344. unsigned int ulbkPosition = 0;
  3345. er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
  3346. if(er != erSuccess)
  3347. return er;
  3348. er = lpTable->CreateBookmark(&ulbkPosition);
  3349. if(er != erSuccess)
  3350. return er;
  3351. lpsResponse->ulbkPosition = ulbkPosition;
  3352. return erSuccess;
  3353. }
  3354. SOAP_ENTRY_END()
  3355. /**
  3356. * tableCreateBookmark: called from IMAPITable::FreeBookmark()
  3357. */
  3358. SOAP_ENTRY_START(tableFreeBookmark, *result, unsigned int ulTableId, unsigned int ulbkPosition, unsigned int *result)
  3359. {
  3360. object_ptr<ECGenericObjectTable> lpTable;
  3361. er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
  3362. if(er != erSuccess)
  3363. return er;
  3364. return lpTable->FreeBookmark(ulbkPosition);
  3365. }
  3366. SOAP_ENTRY_END()
  3367. SOAP_ENTRY_START(tableExpandRow, lpsResponse->er, unsigned int ulTableId, xsd__base64Binary sInstanceKey, unsigned int ulRowCount, unsigned int ulFlags, tableExpandRowResponse* lpsResponse)
  3368. {
  3369. object_ptr<ECGenericObjectTable> lpTable;
  3370. struct rowSet *lpRowSet = NULL;
  3371. unsigned int ulMoreRows = 0;
  3372. er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
  3373. if(er != erSuccess)
  3374. return er;
  3375. er = lpTable->ExpandRow(soap, sInstanceKey, ulRowCount, ulFlags, &lpRowSet, &ulMoreRows);
  3376. if(er != erSuccess)
  3377. return er;
  3378. if (!bSupportUnicode) {
  3379. er = FixRowSetEncoding(soap, stringCompat, Out, lpRowSet);
  3380. if (er != erSuccess)
  3381. return er;
  3382. }
  3383. lpsResponse->ulMoreRows = ulMoreRows;
  3384. lpsResponse->rowSet = *lpRowSet;
  3385. return erSuccess;
  3386. }
  3387. SOAP_ENTRY_END()
  3388. SOAP_ENTRY_START(tableCollapseRow, lpsResponse->er, unsigned int ulTableId, xsd__base64Binary sInstanceKey, unsigned int ulFlags, tableCollapseRowResponse* lpsResponse)
  3389. {
  3390. object_ptr<ECGenericObjectTable> lpTable;
  3391. unsigned int ulRows = 0;
  3392. er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
  3393. if(er != erSuccess)
  3394. return er;
  3395. er = lpTable->CollapseRow(sInstanceKey, ulFlags, &ulRows);
  3396. if(er != erSuccess)
  3397. return er;
  3398. lpsResponse->ulRows = ulRows;
  3399. return erSuccess;
  3400. }
  3401. SOAP_ENTRY_END()
  3402. SOAP_ENTRY_START(tableGetCollapseState, lpsResponse->er, unsigned int ulTableId, struct xsd__base64Binary sBookmark, tableGetCollapseStateResponse *lpsResponse)
  3403. {
  3404. object_ptr<ECGenericObjectTable> lpTable;
  3405. er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
  3406. if(er != erSuccess)
  3407. return er;
  3408. return lpTable->GetCollapseState(soap, sBookmark, &lpsResponse->sCollapseState);
  3409. }
  3410. SOAP_ENTRY_END()
  3411. SOAP_ENTRY_START(tableSetCollapseState, lpsResponse->er, unsigned int ulTableId, struct xsd__base64Binary sCollapseState, struct tableSetCollapseStateResponse *lpsResponse);
  3412. {
  3413. object_ptr<ECGenericObjectTable> lpTable;
  3414. er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
  3415. if(er != erSuccess)
  3416. return er;
  3417. return lpTable->SetCollapseState(sCollapseState, &lpsResponse->ulBookmark);
  3418. }
  3419. SOAP_ENTRY_END()
  3420. /**
  3421. * tableSetMultiStoreEntryIDs: client sets content for MultiStoreTable
  3422. */
  3423. SOAP_ENTRY_START(tableSetMultiStoreEntryIDs, *result, unsigned int ulTableId, struct entryList *lpEntryList, unsigned int *result)
  3424. {
  3425. object_ptr<ECGenericObjectTable> lpTable;
  3426. ECMultiStoreTable *lpMultiStoreTable = NULL;
  3427. ECListInt lObjectList;
  3428. if (lpEntryList == nullptr)
  3429. return KCERR_INVALID_PARAMETER;
  3430. er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
  3431. if (er != erSuccess)
  3432. return er;
  3433. // ignore errors
  3434. g_lpSessionManager->GetCacheManager()->GetEntryListToObjectList(lpEntryList, &lObjectList);
  3435. lpMultiStoreTable = dynamic_cast<ECMultiStoreTable *>(lpTable.get());
  3436. if (lpMultiStoreTable == nullptr)
  3437. return KCERR_INVALID_PARAMETER;
  3438. return lpMultiStoreTable->SetEntryIDs(&lObjectList);
  3439. }
  3440. SOAP_ENTRY_END()
  3441. SOAP_ENTRY_START(tableMulti, lpsResponse->er, struct tableMultiRequest sRequest, struct tableMultiResponse *lpsResponse)
  3442. {
  3443. unsigned int ulTableId = sRequest.ulTableId;
  3444. object_ptr<ECGenericObjectTable> lpTable;
  3445. struct rowSet *lpRowSet = NULL;
  3446. if(sRequest.lpOpen) {
  3447. er = OpenTable(lpecSession, sRequest.lpOpen->sEntryId, sRequest.lpOpen->ulTableType, sRequest.lpOpen->ulType, sRequest.lpOpen->ulFlags, &lpsResponse->ulTableId);
  3448. if(er != erSuccess)
  3449. return er;
  3450. ulTableId = lpsResponse->ulTableId;
  3451. }
  3452. er = lpecSession->GetTableManager()->GetTable(ulTableId, &~lpTable);
  3453. if(er != erSuccess)
  3454. return er;
  3455. if(sRequest.lpSort) {
  3456. er = lpTable->SetSortOrder(&sRequest.lpSort->sSortOrder, sRequest.lpSort->ulCategories, sRequest.lpSort->ulExpanded);
  3457. if(er != erSuccess)
  3458. return er;
  3459. }
  3460. if(sRequest.lpSetColumns) {
  3461. er = lpTable->SetColumns(sRequest.lpSetColumns, false);
  3462. if(er != erSuccess)
  3463. return er;
  3464. }
  3465. if(sRequest.lpRestrict || (sRequest.ulFlags&TABLE_MULTI_CLEAR_RESTRICTION)) {
  3466. if (!bSupportUnicode && sRequest.lpRestrict) {
  3467. er = FixRestrictionEncoding(soap, stringCompat, In, sRequest.lpRestrict);
  3468. if (er != erSuccess)
  3469. return er;
  3470. }
  3471. er = lpTable->Restrict(sRequest.lpRestrict);
  3472. if(er != erSuccess)
  3473. return er;
  3474. }
  3475. if(sRequest.lpQueryRows) {
  3476. er = lpTable->QueryRows(soap, sRequest.lpQueryRows->ulCount, sRequest.lpQueryRows->ulFlags, &lpRowSet);
  3477. if(er != erSuccess)
  3478. return er;
  3479. if (!bSupportUnicode) {
  3480. er = FixRowSetEncoding(soap, stringCompat, Out, lpRowSet);
  3481. if (er != erSuccess)
  3482. return er;
  3483. }
  3484. lpsResponse->sRowSet.__ptr = lpRowSet->__ptr;
  3485. lpsResponse->sRowSet.__size = lpRowSet->__size;
  3486. }
  3487. return erSuccess;
  3488. }
  3489. SOAP_ENTRY_END()
  3490. // Delete a set of messages, recipients, or attachments
  3491. SOAP_ENTRY_START(deleteObjects, *result, unsigned int ulFlags, struct entryList *lpEntryList, unsigned int ulSyncId, unsigned int *result)
  3492. {
  3493. ECListInt lObjectList;
  3494. unsigned int ulDeleteFlags = EC_DELETE_ATTACHMENTS | EC_DELETE_RECIPIENTS | EC_DELETE_CONTAINER | EC_DELETE_MESSAGES;
  3495. USE_DATABASE();
  3496. if (lpEntryList == nullptr)
  3497. return KCERR_INVALID_PARAMETER;
  3498. if(ulFlags & DELETE_HARD_DELETE)
  3499. ulDeleteFlags |= EC_DELETE_HARD_DELETE;
  3500. // ignore errors
  3501. g_lpSessionManager->GetCacheManager()->GetEntryListToObjectList(lpEntryList, &lObjectList);
  3502. return DeleteObjects(lpecSession, lpDatabase, &lObjectList, ulDeleteFlags, ulSyncId, false, true);
  3503. }
  3504. SOAP_ENTRY_END()
  3505. // Delete everything in a folder, but not the folder itself
  3506. // Quirk: this works with messages also, deleting attachments and recipients, but not the message itself.
  3507. //FIXME: michel? what with associated messages ?
  3508. SOAP_ENTRY_START(emptyFolder, *result, entryId sEntryId, unsigned int ulFlags, unsigned int ulSyncId, unsigned int *result)
  3509. {
  3510. unsigned int ulDeleteFlags = 0;
  3511. unsigned int ulId = 0;
  3512. ECListInt lObjectIds;
  3513. USE_DATABASE();
  3514. er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulId);
  3515. if(er != erSuccess)
  3516. goto exit;
  3517. // Check Rights set permission
  3518. er = lpecSession->GetSecurity()->CheckPermission(ulId, ecSecurityDelete);
  3519. if(er != erSuccess)
  3520. goto exit;
  3521. // Add object into the list
  3522. lObjectIds.push_back(ulId);
  3523. ulDeleteFlags = EC_DELETE_MESSAGES | EC_DELETE_FOLDERS | EC_DELETE_RECIPIENTS | EC_DELETE_ATTACHMENTS;
  3524. if (ulFlags & DELETE_HARD_DELETE)
  3525. ulDeleteFlags |= EC_DELETE_HARD_DELETE;
  3526. if((ulFlags&DEL_ASSOCIATED) == 0)
  3527. ulDeleteFlags |= EC_DELETE_NOT_ASSOCIATED_MSG;
  3528. er = DeleteObjects(lpecSession, lpDatabase, &lObjectIds, ulDeleteFlags, ulSyncId, false, true);
  3529. if (er != erSuccess)
  3530. goto exit;
  3531. exit:
  3532. ROLLBACK_ON_ERROR();
  3533. }
  3534. SOAP_ENTRY_END()
  3535. /* FIXME
  3536. *
  3537. * Currently, when deleteFolders is called with DEL_FOLDERS but without DEL_MESSAGES, it will return an error
  3538. * when a subfolder of the specified folder contains messages. I don't think this is up to spec. DeleteObjects
  3539. * should therefore be changed so that the check is only done against messages and folders directly under the
  3540. * top-level object.
  3541. */
  3542. // Deletes a complete folder, with optional recursive subfolder and submessage deletion
  3543. SOAP_ENTRY_START(deleteFolder, *result, entryId sEntryId, unsigned int ulFlags, unsigned int ulSyncId, unsigned int *result)
  3544. {
  3545. unsigned int ulDeleteFlags = 0;
  3546. unsigned int ulId = 0;
  3547. unsigned int ulFolderFlags = 0;
  3548. ECListInt lObjectIds;
  3549. USE_DATABASE();
  3550. er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulId);
  3551. if(er != erSuccess)
  3552. goto exit;
  3553. // Check permission
  3554. er = lpecSession->GetSecurity()->CheckPermission(ulId, ecSecurityFolderAccess);
  3555. if(er != erSuccess)
  3556. goto exit;
  3557. er = g_lpSessionManager->GetCacheManager()->GetObjectFlags(ulId, &ulFolderFlags);
  3558. if(er != erSuccess)
  3559. goto exit;
  3560. // insert objectid into the delete list
  3561. lObjectIds.push_back(ulId);
  3562. ulDeleteFlags = EC_DELETE_CONTAINER;
  3563. if(ulFlags & DEL_FOLDERS)
  3564. ulDeleteFlags |= EC_DELETE_FOLDERS;
  3565. if(ulFlags & DEL_MESSAGES)
  3566. ulDeleteFlags |= EC_DELETE_MESSAGES | EC_DELETE_RECIPIENTS | EC_DELETE_ATTACHMENTS;
  3567. if( (ulFlags & DELETE_HARD_DELETE) || ulFolderFlags == FOLDER_SEARCH)
  3568. ulDeleteFlags |= EC_DELETE_HARD_DELETE;
  3569. er = DeleteObjects(lpecSession, lpDatabase, &lObjectIds, ulDeleteFlags, ulSyncId, false, true);
  3570. if (er != erSuccess)
  3571. goto exit;
  3572. exit:
  3573. ROLLBACK_ON_ERROR();
  3574. }
  3575. SOAP_ENTRY_END()
  3576. static ECRESULT DoNotifySubscribe(ECSession *lpecSession,
  3577. unsigned long long ulSessionId, struct notifySubscribe *notifySubscribe)
  3578. {
  3579. ECRESULT er = erSuccess;
  3580. unsigned int ulKey = 0;
  3581. object_ptr<ECGenericObjectTable> lpTable;
  3582. //NOTE: An sKey with size 4 is a table notification id
  3583. if(notifySubscribe->sKey.__size == 4) {
  3584. memcpy(&ulKey, notifySubscribe->sKey.__ptr, 4);
  3585. }else {
  3586. er = lpecSession->GetObjectFromEntryId(&notifySubscribe->sKey, &ulKey);
  3587. if(er != erSuccess)
  3588. return er;
  3589. // Check permissions
  3590. er = lpecSession->GetSecurity()->CheckPermission(ulKey, ecSecurityFolderVisible);
  3591. if(er != erSuccess)
  3592. return er;
  3593. }
  3594. if(notifySubscribe->ulEventMask & fnevTableModified) {
  3595. // An advise has been done on a table. The table ID is in 'ulKey' in this case. When this is done
  3596. // we have to populate the table first since row modifications would otherwise be wrong until the
  3597. // table is populated; if the table is unpopulated and a row changes, the row will be added into the table
  3598. // whenever it is modified, producing a TABLE_ROW_ADDED for that row instead of the correct TABLE_ROW_MODIFIED.
  3599. er = lpecSession->GetTableManager()->GetTable(ulKey, &~lpTable);
  3600. if(er != erSuccess)
  3601. return er;
  3602. er = lpTable->Populate();
  3603. if(er != erSuccess)
  3604. return er;
  3605. }
  3606. er = lpecSession->AddAdvise(notifySubscribe->ulConnection, ulKey, notifySubscribe->ulEventMask);
  3607. if (er == erSuccess)
  3608. TRACE_SOAP(TRACE_INFO, "ns__notifySubscribe", "connectionId: %d SessionId: %d Mask: %d",notifySubscribe->ulConnection, ulSessionId, notifySubscribe->ulEventMask);
  3609. return er;
  3610. }
  3611. SOAP_ENTRY_START(notifySubscribe, *result, struct notifySubscribe *notifySubscribe, unsigned int *result)
  3612. {
  3613. if (notifySubscribe == nullptr)
  3614. return KCERR_INVALID_PARAMETER;
  3615. if (notifySubscribe->ulEventMask == fnevKopanoIcsChange)
  3616. return lpecSession->AddChangeAdvise(notifySubscribe->ulConnection, &notifySubscribe->sSyncState);
  3617. else
  3618. return DoNotifySubscribe(lpecSession, ulSessionId, notifySubscribe);
  3619. }
  3620. SOAP_ENTRY_END()
  3621. SOAP_ENTRY_START(notifySubscribeMulti, *result, struct notifySubscribeArray *notifySubscribeArray, unsigned int *result)
  3622. {
  3623. if (notifySubscribeArray == nullptr)
  3624. return KCERR_INVALID_PARAMETER;
  3625. for (gsoap_size_t i = 0; i < notifySubscribeArray->__size; ++i) {
  3626. if (notifySubscribeArray->__ptr[i].ulEventMask == fnevKopanoIcsChange)
  3627. er = lpecSession->AddChangeAdvise(notifySubscribeArray->__ptr[i].ulConnection, &notifySubscribeArray->__ptr[i].sSyncState);
  3628. else
  3629. er = DoNotifySubscribe(lpecSession, ulSessionId, &notifySubscribeArray->__ptr[i]);
  3630. if (er != erSuccess) {
  3631. for (gsoap_size_t j = 0; j < i; ++j)
  3632. lpecSession->DelAdvise(notifySubscribeArray->__ptr[j].ulConnection);
  3633. break;
  3634. }
  3635. }
  3636. return er;
  3637. }
  3638. SOAP_ENTRY_END()
  3639. SOAP_ENTRY_START(notifyUnSubscribe, *result, unsigned int ulConnection, unsigned int *result)
  3640. {
  3641. return lpecSession->DelAdvise(ulConnection);
  3642. }
  3643. SOAP_ENTRY_END()
  3644. SOAP_ENTRY_START(notifyUnSubscribeMulti, *result, struct mv_long *ulConnectionArray, unsigned int *result)
  3645. {
  3646. unsigned int erTmp = erSuccess;
  3647. unsigned int erFirst = erSuccess;
  3648. if (ulConnectionArray == nullptr)
  3649. return KCERR_INVALID_PARAMETER;
  3650. for (gsoap_size_t i = 0; i < ulConnectionArray->__size; ++i) {
  3651. erTmp = lpecSession->DelAdvise(ulConnectionArray->__ptr[i]);
  3652. if (erTmp != erSuccess && erFirst == erSuccess)
  3653. erFirst = erTmp;
  3654. }
  3655. // return first seen error (if any).
  3656. return erFirst;
  3657. }
  3658. SOAP_ENTRY_END()
  3659. /*
  3660. * Gets notifications queued for the session group that the specified session is attached to; you can access
  3661. * all notifications of a session group via any session on that group. The request itself is handled by the
  3662. * ECNotificationManager class since you don't want to block the calling thread while waiting for notifications.
  3663. */
  3664. int ns__notifyGetItems(struct soap *soap, ULONG64 ulSessionId, struct notifyResponse *notifications)
  3665. {
  3666. ECRESULT er = erSuccess;
  3667. ECSession *lpSession = NULL;
  3668. // Check if the session exists, and discard result
  3669. er = g_lpSessionManager->ValidateSession(soap, ulSessionId, &lpSession, true);
  3670. if(er != erSuccess) {
  3671. // Directly return with error in er
  3672. notifications->er = er;
  3673. // SOAP call itself succeeded
  3674. return SOAP_OK;
  3675. }
  3676. // discard lpSession
  3677. lpSession->Unlock();
  3678. lpSession = NULL;
  3679. g_lpSessionManager->DeferNotificationProcessing(ulSessionId, soap);
  3680. // Return SOAP_NULL so that the caller does *nothing* with the soap struct since we have passed it to the session
  3681. // manager for deferred processing
  3682. throw SOAP_NULL;
  3683. }
  3684. SOAP_ENTRY_START(getRights, lpsRightResponse->er, entryId sEntryId, int ulType, struct rightsResponse *lpsRightResponse)
  3685. {
  3686. unsigned int ulobjid = 0;
  3687. struct rightsArray *lpsRightArray = NULL;
  3688. er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulobjid);
  3689. if(er != erSuccess)
  3690. goto exit;
  3691. lpsRightArray = s_alloc<rightsArray>(nullptr);
  3692. memset(lpsRightArray, 0, sizeof(struct rightsArray));
  3693. er = lpecSession->GetSecurity()->GetRights(ulobjid, ulType, lpsRightArray);
  3694. if(er != erSuccess)
  3695. goto exit;
  3696. er = CopyRightsArrayToSoap(soap, lpsRightArray, &lpsRightResponse->pRightsArray);
  3697. if (er != erSuccess)
  3698. goto exit;
  3699. exit:
  3700. if (lpsRightArray) {
  3701. for (gsoap_size_t i = 0; i < lpsRightArray->__size; ++i)
  3702. s_free(nullptr, lpsRightArray->__ptr[i].sUserId.__ptr);
  3703. s_free(nullptr, lpsRightArray->__ptr);
  3704. s_free(nullptr, lpsRightArray);
  3705. }
  3706. }
  3707. SOAP_ENTRY_END()
  3708. SOAP_ENTRY_START(setRights, *result, entryId sEntryId, struct rightsArray *lpsRightsArray, unsigned int *result)
  3709. {
  3710. unsigned int ulObjId = 0;
  3711. if (lpsRightsArray == nullptr)
  3712. return KCERR_INVALID_PARAMETER;
  3713. er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulObjId);
  3714. if(er != erSuccess)
  3715. return er;
  3716. // Check Rights set permission
  3717. er = lpecSession->GetSecurity()->CheckPermission(ulObjId, ecSecurityFolderAccess);
  3718. if(er != erSuccess)
  3719. return er;
  3720. return lpecSession->GetSecurity()->SetRights(ulObjId, lpsRightsArray);
  3721. }
  3722. SOAP_ENTRY_END()
  3723. /* DEPRICATED:
  3724. * this is only accessable through IECSecurity, and nobody calls this anymore!
  3725. */
  3726. SOAP_ENTRY_START(getUserObjectList, lpsUserObjectResponse->er, unsigned int ulCompanyId, entryId sCompanyId, int ulType, struct userobjectResponse *lpsUserObjectResponse)
  3727. {
  3728. // TODO: if we just throw the soap call away, it has the same result?
  3729. er = KCERR_NO_SUPPORT;
  3730. }
  3731. SOAP_ENTRY_END()
  3732. SOAP_ENTRY_START(getOwner, lpsResponse->er, entryId sEntryId, struct getOwnerResponse *lpsResponse)
  3733. {
  3734. unsigned int ulobjid = 0;
  3735. er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulobjid);
  3736. if(er != erSuccess)
  3737. return er;
  3738. er = lpecSession->GetSecurity()->GetOwner(ulobjid, &lpsResponse->ulOwner);
  3739. if(er != erSuccess)
  3740. return er;
  3741. return GetABEntryID(lpsResponse->ulOwner, soap, &lpsResponse->sOwner);
  3742. }
  3743. SOAP_ENTRY_END()
  3744. SOAP_ENTRY_START(getIDsFromNames, lpsResponse->er, struct namedPropArray *lpsNamedProps, unsigned int ulFlags, struct getIDsFromNamesResponse *lpsResponse)
  3745. {
  3746. std::string strEscapedString;
  3747. std::string strEscapedGUID;
  3748. unsigned int ulLastId = 0;
  3749. USE_DATABASE();
  3750. if(lpsNamedProps == NULL) {
  3751. er = KCERR_INVALID_PARAMETER;
  3752. goto exit;
  3753. }
  3754. er = lpDatabase->Begin();
  3755. if(er != erSuccess)
  3756. goto exit;
  3757. lpsResponse->lpsPropTags.__ptr = s_alloc<unsigned int>(soap, lpsNamedProps->__size);
  3758. lpsResponse->lpsPropTags.__size = 0;
  3759. // One query per named property (too slow ?) FIXME could be faster if brought down to less SQL queries
  3760. for (gsoap_size_t i = 0; i < lpsNamedProps->__size; ++i) {
  3761. strQuery = "SELECT id FROM names WHERE ";
  3762. // ID, then add ID where clause
  3763. if(lpsNamedProps->__ptr[i].lpId != NULL) {
  3764. strQuery += "nameid=" + stringify(*lpsNamedProps->__ptr[i].lpId) + " ";
  3765. }
  3766. // String, then add STRING where clause
  3767. else if(lpsNamedProps->__ptr[i].lpString != NULL) {
  3768. strEscapedString = lpDatabase->Escape(lpsNamedProps->__ptr[i].lpString);
  3769. strQuery += "namestring='" + strEscapedString + "' ";
  3770. }
  3771. // Add a GUID specifier if there
  3772. if(lpsNamedProps->__ptr[i].lpguid != NULL) {
  3773. strEscapedGUID = lpDatabase->EscapeBinary(lpsNamedProps->__ptr[i].lpguid->__ptr, lpsNamedProps->__ptr[i].lpguid->__size);
  3774. strQuery += "AND guid=" + strEscapedGUID;
  3775. }
  3776. strQuery += " LIMIT 1";
  3777. // Run the query
  3778. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  3779. if(er != erSuccess)
  3780. goto exit;
  3781. if(lpDatabase->GetNumRows(lpDBResult) == 0) {
  3782. // No rows found, so the named property has not been registered yet
  3783. if(ulFlags & MAPI_CREATE) {
  3784. // Create requested ? then add a new named property
  3785. // GUID must be specified with create
  3786. if(lpsNamedProps->__ptr[i].lpguid == NULL) {
  3787. er = KCERR_NO_ACCESS;
  3788. goto exit;
  3789. }
  3790. strQuery = "INSERT INTO names (nameid, namestring, guid) VALUES(";
  3791. if (lpsNamedProps->__ptr[i].lpId)
  3792. strQuery += stringify(*lpsNamedProps->__ptr[i].lpId);
  3793. else
  3794. strQuery += "null";
  3795. strQuery += ",";
  3796. if (lpsNamedProps->__ptr[i].lpString)
  3797. strQuery += "'" + strEscapedString + "'";
  3798. else
  3799. strQuery += "null";
  3800. strQuery += ",";
  3801. if (lpsNamedProps->__ptr[i].lpguid)
  3802. strQuery += strEscapedGUID;
  3803. else
  3804. strQuery += "null";
  3805. strQuery += ")";
  3806. er = lpDatabase->DoInsert(strQuery, &ulLastId);
  3807. if(er != erSuccess)
  3808. goto exit;
  3809. lpsResponse->lpsPropTags.__ptr[i] = ulLastId+1; // offset one because 0 is 'not found'
  3810. } else {
  3811. // No create ? Then not found
  3812. lpsResponse->lpsPropTags.__ptr[i] = 0;
  3813. }
  3814. } else {
  3815. // found it
  3816. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  3817. if(lpDBRow!= NULL && lpDBRow[0] != NULL)
  3818. lpsResponse->lpsPropTags.__ptr[i] = atoi(lpDBRow[0])+1;
  3819. else
  3820. lpsResponse->lpsPropTags.__ptr[i] = 0;
  3821. }
  3822. }
  3823. // Everything is done, now set the size
  3824. lpsResponse->lpsPropTags.__size = lpsNamedProps->__size;
  3825. er = lpDatabase->Commit();
  3826. if(er != erSuccess)
  3827. goto exit;
  3828. exit:
  3829. ROLLBACK_ON_ERROR();
  3830. }
  3831. SOAP_ENTRY_END()
  3832. SOAP_ENTRY_START(getNamesFromIDs, lpsResponse->er, struct propTagArray *lpPropTags, struct getNamesFromIDsResponse *lpsResponse)
  3833. {
  3834. struct namedPropArray lpsNames;
  3835. USE_DATABASE();
  3836. if (lpPropTags == nullptr)
  3837. return KCERR_INVALID_PARAMETER;
  3838. er = GetNamesFromIDs(soap, lpDatabase, lpPropTags, &lpsNames);
  3839. if (er != erSuccess)
  3840. return er;
  3841. lpsResponse->lpsNames = lpsNames;
  3842. return erSuccess;
  3843. }
  3844. SOAP_ENTRY_END()
  3845. SOAP_ENTRY_START(getReceiveFolder, lpsReceiveFolder->er, entryId sStoreId, char* msg_class, struct receiveFolderResponse *lpsReceiveFolder)
  3846. {
  3847. const char *lpszMessageClass = msg_class;
  3848. unsigned int ulStoreid = 0;
  3849. const char *lpDest;
  3850. USE_DATABASE();
  3851. lpszMessageClass = STRIN_FIX(lpszMessageClass);
  3852. er = lpecSession->GetObjectFromEntryId(&sStoreId, &ulStoreid);
  3853. if(er != erSuccess)
  3854. return er;
  3855. // Check for default store
  3856. if(lpszMessageClass == NULL)
  3857. lpszMessageClass = "";
  3858. strQuery = "SELECT objid, messageclass FROM receivefolder WHERE storeid="+stringify(ulStoreid)+" AND (";
  3859. strQuery += "messageclass='"+lpDatabase->Escape(lpszMessageClass)+"'";
  3860. lpDest = lpszMessageClass;
  3861. do {
  3862. lpDest = strchr(lpDest, '.');
  3863. if(lpDest){
  3864. strQuery += " OR messageclass='"+lpDatabase->Escape(string(lpszMessageClass, lpDest-lpszMessageClass))+"'";
  3865. ++lpDest;
  3866. }
  3867. }while(lpDest);
  3868. if(strlen(lpszMessageClass) != 0)
  3869. strQuery += " OR messageclass=''";
  3870. strQuery += ") ORDER BY length(messageclass) DESC LIMIT 1";
  3871. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  3872. if(er != erSuccess)
  3873. return er;
  3874. if(lpDatabase->GetNumRows(lpDBResult) == 1) {
  3875. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  3876. if(lpDBRow == NULL || lpDBRow[0] == NULL || lpDBRow[1] == NULL){
  3877. ec_log_err("getReceiveFolder(): row or columns null");
  3878. return KCERR_DATABASE_ERROR;
  3879. }
  3880. er = g_lpSessionManager->GetCacheManager()->GetEntryIdFromObject(atoui(lpDBRow[0]), soap, 0, &lpsReceiveFolder->sReceiveFolder.sEntryId);
  3881. if(er != erSuccess)
  3882. return er;
  3883. lpsReceiveFolder->sReceiveFolder.lpszAExplicitClass = STROUT_FIX_CPY(lpDBRow[1]);
  3884. }else{
  3885. //items not found
  3886. return KCERR_NOT_FOUND;
  3887. }
  3888. return erSuccess;
  3889. }
  3890. SOAP_ENTRY_END()
  3891. // FIXME: should be able to delete an entry too
  3892. SOAP_ENTRY_START(setReceiveFolder, *result, entryId sStoreId, entryId* lpsEntryId, char* msg_class, unsigned int *result)
  3893. {
  3894. const char *lpszMessageClass = msg_class;
  3895. bool bIsUpdate = false;
  3896. unsigned int ulCheckStoreId = 0;
  3897. unsigned int ulStoreid = 0;
  3898. unsigned int ulId = 0;
  3899. USE_DATABASE();
  3900. lpszMessageClass = STRIN_FIX(lpszMessageClass);
  3901. er = lpDatabase->Begin();
  3902. if (er != erSuccess)
  3903. goto exit;
  3904. // Check, lpsEntryId and lpszMessageClass can't both be empty or 0
  3905. if(lpsEntryId == NULL && (lpszMessageClass == NULL || *lpszMessageClass == '\0')){
  3906. er = KCERR_INVALID_TYPE;
  3907. goto exit;
  3908. }
  3909. er = lpecSession->GetObjectFromEntryId(&sStoreId, &ulStoreid);
  3910. if(er != erSuccess)
  3911. goto exit;
  3912. // an empty lpszMessageClass is the default folder
  3913. if(lpszMessageClass == NULL)
  3914. lpszMessageClass = "";
  3915. // If the lpsEntryId parameter is set to NULL then replace the current receive folder with the message store's default.
  3916. if(lpsEntryId)
  3917. {
  3918. // Check if object really exist and the relation between storeid and ulId
  3919. er = lpecSession->GetObjectFromEntryId(lpsEntryId, &ulId);
  3920. if(er != erSuccess)
  3921. goto exit;
  3922. // Check if storeid and ulId have a relation
  3923. er = lpecSession->GetSessionManager()->GetCacheManager()->GetStore(ulId, &ulCheckStoreId, NULL);
  3924. if(er != erSuccess){
  3925. er = KCERR_INVALID_ENTRYID;
  3926. goto exit;
  3927. }
  3928. if(ulStoreid != ulCheckStoreId) {
  3929. er = KCERR_INVALID_ENTRYID;
  3930. goto exit;
  3931. }
  3932. er = lpecSession->GetSecurity()->CheckDeletedParent(ulId);
  3933. if (er != erSuccess)
  3934. goto exit;
  3935. } else {
  3936. // Set MessageClass with the default of the store (that's the empty MessageClass)
  3937. strQuery = "SELECT objid FROM receivefolder WHERE storeid="+stringify(ulStoreid)+" AND messageclass='' LIMIT 2";
  3938. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  3939. if(er != erSuccess)
  3940. goto exit;
  3941. if(lpDatabase->GetNumRows(lpDBResult) == 1)
  3942. {
  3943. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  3944. if(lpDBRow == NULL || lpDBRow[0] == NULL){
  3945. er = KCERR_DATABASE_ERROR;
  3946. ec_log_err("setReceiveFolder(): row or columns null");
  3947. goto exit;
  3948. }
  3949. //Set the default folder
  3950. ulId = atoi(lpDBRow[0]);
  3951. }else{
  3952. er = KCERR_DATABASE_ERROR; //FIXME: no default error ?
  3953. ec_log_err("setReceiveFolder(): unexpected row count");
  3954. goto exit;
  3955. }
  3956. }
  3957. strQuery = "SELECT objid, id FROM receivefolder WHERE storeid="+stringify(ulStoreid)+" AND messageclass='"+lpDatabase->Escape(lpszMessageClass)+"' LIMIT 1";
  3958. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  3959. if(er != erSuccess)
  3960. goto exit;
  3961. bIsUpdate = false;
  3962. // If ok, item already exists, return ok
  3963. if(lpDatabase->GetNumRows(lpDBResult) == 1){
  3964. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  3965. if(lpDBRow == NULL || lpDBRow[0] == NULL || lpDBRow[1] == NULL){
  3966. er = KCERR_DATABASE_ERROR;
  3967. ec_log_err("setReceiveFolder(): row or columns null");
  3968. goto exit;
  3969. }
  3970. // Item exists
  3971. if(ulId == atoui(lpDBRow[0])){
  3972. er = lpDatabase->Rollback(); // Nothing changed, so Commit() would also do.
  3973. goto exit;
  3974. }
  3975. bIsUpdate = true;
  3976. }
  3977. // Check permission
  3978. //FIXME: also on delete?
  3979. if(bIsUpdate)
  3980. er = lpecSession->GetSecurity()->CheckPermission(ulStoreid, ecSecurityEdit);
  3981. else
  3982. er = lpecSession->GetSecurity()->CheckPermission(ulStoreid, ecSecurityCreate);
  3983. if(er != erSuccess)
  3984. goto exit;
  3985. if(bIsUpdate) {
  3986. strQuery = "UPDATE receivefolder SET objid="+stringify(ulId);
  3987. strQuery+= " WHERE storeid="+stringify(ulStoreid)+" AND messageclass='"+lpDatabase->Escape(lpszMessageClass)+"'";
  3988. er = lpDatabase->DoUpdate(strQuery);
  3989. }else{
  3990. strQuery = "INSERT INTO receivefolder (storeid, objid, messageclass) VALUES (";
  3991. strQuery += stringify(ulStoreid)+", "+stringify(ulId)+", '"+lpDatabase->Escape(lpszMessageClass)+"')";
  3992. er = lpDatabase->DoInsert(strQuery);
  3993. }
  3994. if(er != erSuccess)
  3995. goto exit;
  3996. er = lpDatabase->Commit();
  3997. if (er != erSuccess)
  3998. goto exit;
  3999. exit:
  4000. ROLLBACK_ON_ERROR();
  4001. }
  4002. SOAP_ENTRY_END()
  4003. /*
  4004. * WARNING
  4005. *
  4006. * lpsEntryID != NULL && lpMessageList != NULL: messages in lpMessageList must be set, lpsEntryId MUST BE IGNORED (may be entryid of search folder)
  4007. * lpsEntryID == NULL && lpMessageList != NULL: called from IMessage::SetReadFlag, lpMessageList->__size == 1
  4008. * lpsEntryID != NULL && lpMessageList == NULL: 'mark all messages as (un)read'
  4009. *
  4010. * Items are assumed to all be in the same store.
  4011. *
  4012. */
  4013. SOAP_ENTRY_START(setReadFlags, *result, unsigned int ulFlags, entryId* lpsEntryId, struct entryList *lpMessageList, unsigned int ulSyncId, unsigned int *result)
  4014. {
  4015. std::list<unsigned int> lHierarchyIDs;
  4016. std::list<std::pair<unsigned int, unsigned int> > lObjectIds;
  4017. std::string strQueryCache;
  4018. USE_DATABASE();
  4019. unsigned int i = 0;
  4020. unsigned int ulParent = 0;
  4021. unsigned int ulGrandParent = 0;
  4022. unsigned int ulFlagsNotify = 0;
  4023. unsigned int ulFlagsRemove = 0;
  4024. unsigned int ulFlagsAdd = 0;
  4025. unsigned int ulFolderId = 0;
  4026. size_t cObjectSize = 0;
  4027. // List of unique parents
  4028. std::map<unsigned int, int> mapParents;
  4029. std::set<unsigned int> setParents;
  4030. //NOTE: either lpMessageList may be NULL or lpsEntryId may be NULL
  4031. if(ulFlags & GENERATE_RECEIPT_ONLY)
  4032. goto exit;
  4033. if(lpMessageList == NULL && lpsEntryId == NULL) {
  4034. // Bad input
  4035. er = KCERR_INVALID_PARAMETER;
  4036. goto exit;
  4037. }
  4038. if (lpMessageList != nullptr)
  4039. // Ignore errors
  4040. g_lpSessionManager->GetCacheManager()->GetEntryListToObjectList(lpMessageList, &lHierarchyIDs);
  4041. strQuery = "UPDATE properties SET ";
  4042. if ((ulFlags & CLEAR_NRN_PENDING) || (ulFlags & SUPPRESS_RECEIPT) || (ulFlags & GENERATE_RECEIPT_ONLY) )
  4043. ulFlagsRemove |= MSGFLAG_NRN_PENDING;
  4044. if ((ulFlags & CLEAR_RN_PENDING) || (ulFlags & SUPPRESS_RECEIPT) || (ulFlags & GENERATE_RECEIPT_ONLY) )
  4045. ulFlagsRemove |= MSGFLAG_RN_PENDING;
  4046. if (!(ulFlags & GENERATE_RECEIPT_ONLY) && (ulFlags & CLEAR_READ_FLAG))
  4047. ulFlagsRemove |= MSGFLAG_READ;
  4048. else if( !(ulFlags & GENERATE_RECEIPT_ONLY) )
  4049. ulFlagsAdd |= MSGFLAG_READ;
  4050. if(ulFlagsRemove != 0)
  4051. strQuery += "val_ulong=val_ulong & ~" + stringify(ulFlagsRemove);
  4052. if(ulFlagsAdd != 0) {
  4053. strQuery += (ulFlagsRemove!=0)?",":"";
  4054. strQuery += "val_ulong=val_ulong | " + stringify(ulFlagsAdd);
  4055. }
  4056. if (ulFlagsRemove == 0 && ulFlagsAdd == 0)
  4057. // Nothing to update
  4058. goto exit;
  4059. er = lpDatabase->Begin();
  4060. if(er != erSuccess)
  4061. goto exit;
  4062. if(lpMessageList == NULL) {
  4063. // No message list passed, so 'mark all items (un)read'
  4064. er = lpecSession->GetObjectFromEntryId(lpsEntryId, &ulFolderId);
  4065. if(er != erSuccess)
  4066. goto exit;
  4067. er = lpDatabase->DoSelect("SELECT val_ulong FROM properties WHERE hierarchyid=" + stringify(ulFolderId) + " FOR UPDATE", NULL);
  4068. if(er != erSuccess)
  4069. goto exit;
  4070. // Check permission
  4071. er = lpecSession->GetSecurity()->CheckPermission(ulFolderId, ecSecurityRead);
  4072. if(er != erSuccess)
  4073. goto exit;
  4074. // Purge changes
  4075. ECTPropsPurge::PurgeDeferredTableUpdates(lpDatabase, ulFolderId);
  4076. // Get all items MAPI_MESSAGE exclude items with flags MSGFLAG_DELETED AND MSGFLAG_ASSOCIATED of which we will be changing flags
  4077. // 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
  4078. 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";
  4079. er = lpDatabase->DoSelect(strQueryCache, &lpDBResult);
  4080. if(er != erSuccess)
  4081. goto exit;
  4082. while( (lpDBRow = lpDatabase->FetchRow(lpDBResult)) )
  4083. {
  4084. if(lpDBRow[0] == NULL || lpDBRow[1] == NULL){
  4085. er = KCERR_DATABASE_ERROR;
  4086. ec_log_err("setReadFlags(): columns null");
  4087. goto exit;
  4088. }
  4089. lObjectIds.push_back( std::pair<unsigned int, unsigned int>( atoui(lpDBRow[0]), atoui(lpDBRow[1]) ));
  4090. ++i;
  4091. }
  4092. ulParent = ulFolderId;
  4093. } else {
  4094. if(lHierarchyIDs.empty()) {
  4095. // Nothing to do
  4096. lpDatabase->Commit();
  4097. goto exit;
  4098. }
  4099. // Because the messagelist can contain messages from all over the place, we have to check permissions for all the parent folders of the items
  4100. // we are setting 'read' or 'unread'
  4101. for (auto hier_id : lHierarchyIDs) {
  4102. // Get the parent object. Note that the cache will hold this information so the loop below with GetObject() will
  4103. // be done directly from the cache (assuming it's not too large)
  4104. if (g_lpSessionManager->GetCacheManager()->GetObject(hier_id, &ulParent, NULL, NULL) != erSuccess)
  4105. continue;
  4106. setParents.insert(ulParent);
  4107. }
  4108. // Lock parent folders
  4109. for (auto parent_id : setParents) {
  4110. er = lpDatabase->DoSelect("SELECT val_ulong FROM properties WHERE hierarchyid=" + stringify(parent_id) + " FOR UPDATE", NULL);
  4111. if(er != erSuccess)
  4112. goto exit;
  4113. }
  4114. // Check permission
  4115. for (auto parent_id : setParents) {
  4116. er = lpecSession->GetSecurity()->CheckPermission(parent_id, ecSecurityRead);
  4117. if(er != erSuccess)
  4118. goto exit;
  4119. }
  4120. // Now find all messages that will actually change
  4121. 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 (";
  4122. for (auto hier = lHierarchyIDs.cbegin();
  4123. hier != lHierarchyIDs.cend(); ++hier) {
  4124. if (hier != lHierarchyIDs.cbegin())
  4125. strQueryCache += ",";
  4126. strQueryCache += stringify(*hier);
  4127. }
  4128. strQueryCache += ") FOR UPDATE"; // See comment above about FOR UPDATE
  4129. er = lpDatabase->DoSelect(strQueryCache, &lpDBResult);
  4130. if(er != erSuccess)
  4131. goto exit;
  4132. while( (lpDBRow = lpDatabase->FetchRow(lpDBResult)) )
  4133. {
  4134. if(lpDBRow[0] == NULL || lpDBRow[1] == NULL){
  4135. er = KCERR_DATABASE_ERROR;
  4136. ec_log_err("setReadFlags(): columns null(2)");
  4137. goto exit;
  4138. }
  4139. lObjectIds.push_back( std::pair<unsigned int, unsigned int>( atoui(lpDBRow[0]), atoui(lpDBRow[1]) ) );
  4140. ++i;
  4141. }
  4142. }
  4143. // Security passed, and we have a list of all the items that must be changed, and the records are locked
  4144. // Check if there is anything to do
  4145. if(lObjectIds.empty()) {
  4146. lpDatabase->Commit();
  4147. goto exit;
  4148. }
  4149. strQuery += " WHERE properties.hierarchyid IN(";
  4150. lHierarchyIDs.clear();
  4151. for (auto iObjectid = lObjectIds.cbegin(); iObjectid != lObjectIds.cend(); ++iObjectid) {
  4152. if (iObjectid != lObjectIds.cbegin())
  4153. strQuery += ",";
  4154. strQuery += stringify(iObjectid->first);
  4155. lHierarchyIDs.push_back(iObjectid->first);
  4156. }
  4157. strQuery += ")";
  4158. strQuery += " AND properties.tag = " + stringify(PROP_ID(PR_MESSAGE_FLAGS)) + " AND properties.type = " + stringify(PROP_TYPE(PR_MESSAGE_FLAGS));
  4159. // Update the database
  4160. er = lpDatabase->DoUpdate(strQuery);
  4161. if(er != erSuccess)
  4162. goto exit;
  4163. er = UpdateTProp(lpDatabase, PR_MESSAGE_FLAGS, ulParent, &lHierarchyIDs); // FIXME ulParent is not constant for all lHierarchyIDs
  4164. if(er != erSuccess)
  4165. goto exit;
  4166. // Add changes to ICS
  4167. for (const auto &op : lObjectIds) {
  4168. bool read = (ulFlagsRemove & MSGFLAG_READ) ||
  4169. (ulFlagsAdd & MSGFLAG_READ);
  4170. if (!read)
  4171. continue;
  4172. // Only save ICS change when the actual readflag has changed
  4173. SOURCEKEY sSourceKey;
  4174. SOURCEKEY sParentSourceKey;
  4175. if (g_lpSessionManager->GetCacheManager()->GetObject(op.first, &ulParent, NULL, NULL) != erSuccess)
  4176. continue;
  4177. GetSourceKey(op.first, &sSourceKey);
  4178. GetSourceKey(ulParent, &sParentSourceKey);
  4179. // Because we know that ulFlagsRemove && MSGFLAG_READ || ulFlagsAdd & MSGFLAG_READ and we assume
  4180. // that they are never both TRUE, we can ignore ulFlagsRemove and just look at ulFlagsAdd for the new
  4181. // readflag state
  4182. AddChange(lpecSession, ulSyncId, sSourceKey, sParentSourceKey, ICS_MESSAGE_FLAG, ulFlagsAdd & MSGFLAG_READ);
  4183. }
  4184. // Update counters, by counting the number of changes per folder
  4185. for (const auto &op : lObjectIds) {
  4186. er = g_lpSessionManager->GetCacheManager()->GetObject(op.first, &ulParent, NULL, NULL);
  4187. if (er != erSuccess)
  4188. goto exit;
  4189. mapParents.insert(std::pair<unsigned int, unsigned int>(ulParent, 0));
  4190. if (ulFlagsAdd & MSGFLAG_READ &&
  4191. (op.second & MSGFLAG_READ) == 0)
  4192. --mapParents[ulParent]; // Decrease unread count
  4193. if (ulFlagsRemove & MSGFLAG_READ && op.second & MSGFLAG_READ)
  4194. ++mapParents[ulParent]; // Increase unread count
  4195. }
  4196. for (const auto &p : mapParents) {
  4197. if (p.second == 0)
  4198. continue;
  4199. er = g_lpSessionManager->GetCacheManager()->GetParent(p.first, &ulGrandParent);
  4200. if(er != erSuccess)
  4201. goto exit;
  4202. er = UpdateFolderCount(lpDatabase, p.first, PR_CONTENT_UNREAD, p.second);
  4203. if (er != erSuccess)
  4204. goto exit;
  4205. }
  4206. er = lpDatabase->Commit();
  4207. if(er != erSuccess)
  4208. goto exit;
  4209. // Now, update cache and send the notifications
  4210. cObjectSize = lObjectIds.size();
  4211. // Loop through the messages, updating each
  4212. for (const auto &op : lObjectIds) {
  4213. // Remove the item from the cache
  4214. g_lpSessionManager->GetCacheManager()->UpdateCell(op.first,
  4215. PR_MESSAGE_FLAGS,
  4216. (ulFlagsAdd | ulFlagsRemove) & MSGFLAG_READ,
  4217. ulFlagsAdd & MSGFLAG_READ);
  4218. if (g_lpSessionManager->GetCacheManager()->GetObject(op.first,
  4219. &ulParent, NULL, &ulFlagsNotify) != erSuccess) {
  4220. ulParent = 0;
  4221. ulFlagsNotify = 0;
  4222. }
  4223. // Update the message itself in tables and object notification
  4224. g_lpSessionManager->NotificationModified(MAPI_MESSAGE,
  4225. op.first, ulParent);
  4226. if(ulParent && cObjectSize < EC_TABLE_CHANGE_THRESHOLD)
  4227. g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY,
  4228. ulFlagsNotify & MSGFLAG_NOTIFY_FLAGS, ulParent,
  4229. op.first, MAPI_MESSAGE);
  4230. }
  4231. // Loop through all the parent folders of the objects, sending notifications for them
  4232. for (const auto &p : mapParents) {
  4233. // The parent has changed its PR_CONTENT_UNREAD
  4234. g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, p.first);
  4235. g_lpSessionManager->NotificationModified(MAPI_FOLDER, p.first);
  4236. // The grand parent's table view of the parent has changed
  4237. if (g_lpSessionManager->GetCacheManager()->GetObject(p.first,
  4238. &ulGrandParent, NULL, &ulFlagsNotify) == erSuccess)
  4239. g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY,
  4240. ulFlagsNotify & MSGFLAG_NOTIFY_FLAGS,
  4241. ulGrandParent, p.first, MAPI_FOLDER);
  4242. if(cObjectSize >= EC_TABLE_CHANGE_THRESHOLD)
  4243. g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_CHANGE,
  4244. ulFlagsNotify & MSGFLAG_NOTIFY_FLAGS,
  4245. p.first, 0, MAPI_MESSAGE);
  4246. }
  4247. exit:
  4248. ROLLBACK_ON_ERROR();
  4249. }
  4250. SOAP_ENTRY_END()
  4251. SOAP_ENTRY_START(createUser, lpsUserSetResponse->er, struct user *lpsUser, struct setUserResponse *lpsUserSetResponse)
  4252. {
  4253. unsigned int ulUserId = 0;
  4254. objectdetails_t details(ACTIVE_USER); // should this function also be able to createContact?
  4255. if (lpsUser == NULL || lpsUser->lpszUsername == NULL || lpsUser->lpszFullName == NULL || lpsUser->lpszMailAddress == NULL ||
  4256. (lpsUser->lpszPassword == nullptr && lpsUser->ulIsNonActive == 0))
  4257. return KCERR_INVALID_PARAMETER;
  4258. if (!bSupportUnicode) {
  4259. er = FixUserEncoding(soap, stringCompat, In, lpsUser);
  4260. if (er != erSuccess)
  4261. return er;
  4262. }
  4263. er = CopyUserDetailsFromSoap(lpsUser, NULL, &details, soap);
  4264. if (er != erSuccess)
  4265. return er;
  4266. er = lpecSession->GetUserManagement()->UpdateUserDetailsFromClient(&details);
  4267. if (er != erSuccess)
  4268. return er;
  4269. // Check permission
  4270. er = lpecSession->GetSecurity()->IsAdminOverUserObject(details.GetPropInt(OB_PROP_I_COMPANYID));
  4271. if(er != erSuccess)
  4272. return er;
  4273. // Create user and sync
  4274. er = lpecSession->GetUserManagement()->CreateObjectAndSync(details, &ulUserId);
  4275. if(er != erSuccess)
  4276. return er;
  4277. er = GetABEntryID(ulUserId, soap, &lpsUserSetResponse->sUserId);
  4278. if (er != erSuccess)
  4279. return er;
  4280. lpsUserSetResponse->ulUserId = ulUserId;
  4281. return erSuccess;
  4282. }
  4283. SOAP_ENTRY_END()
  4284. SOAP_ENTRY_START(setUser, *result, struct user *lpsUser, unsigned int *result)
  4285. {
  4286. objectdetails_t details;
  4287. objectdetails_t oldDetails;
  4288. unsigned int ulUserId = 0;
  4289. objectid_t sExternId;
  4290. if (lpsUser == nullptr)
  4291. return KCERR_INVALID_PARAMETER;
  4292. if (!bSupportUnicode) {
  4293. er = FixUserEncoding(soap, stringCompat, In, lpsUser);
  4294. if (er != erSuccess)
  4295. return er;
  4296. }
  4297. if (lpsUser->sUserId.__size > 0 && lpsUser->sUserId.__ptr != NULL)
  4298. {
  4299. er = GetLocalId(lpsUser->sUserId, lpsUser->ulUserId, &ulUserId, &sExternId);
  4300. if (er != erSuccess)
  4301. return er;
  4302. }
  4303. else
  4304. ulUserId = lpsUser->ulUserId;
  4305. if(ulUserId) {
  4306. er = lpecSession->GetUserManagement()->GetObjectDetails(ulUserId, &oldDetails);
  4307. if (er != erSuccess)
  4308. return er;
  4309. }
  4310. // Check security
  4311. // @todo add check on anonymous (mv)properties
  4312. if (lpecSession->GetSecurity()->IsAdminOverUserObject(ulUserId) == erSuccess) {
  4313. // admins can update anything of a user
  4314. // FIXME: prevent the user from removing admin rights from itself?
  4315. er = erSuccess;
  4316. } else if (lpecSession->GetSecurity()->GetUserId() == ulUserId) {
  4317. // you're only allowed to set your password, force the lpsUser struct to only contain that update
  4318. if (lpsUser->lpszUsername && oldDetails.GetPropString(OB_PROP_S_LOGIN) != lpsUser->lpszUsername) {
  4319. ec_log_warn("Disallowing user \"%s\" to update their username to \"%s\"",
  4320. oldDetails.GetPropString(OB_PROP_S_LOGIN).c_str(), lpsUser->lpszUsername);
  4321. lpsUser->lpszUsername = NULL;
  4322. }
  4323. // leave lpszPassword
  4324. if (lpsUser->lpszMailAddress && oldDetails.GetPropString(OB_PROP_S_EMAIL) != lpsUser->lpszMailAddress) {
  4325. ec_log_warn("Disallowing user \"%s\" to update their mail address to \"%s\"",
  4326. oldDetails.GetPropString(OB_PROP_S_LOGIN).c_str(), lpsUser->lpszMailAddress);
  4327. lpsUser->lpszMailAddress = NULL;
  4328. }
  4329. if (lpsUser->lpszFullName && oldDetails.GetPropString(OB_PROP_S_FULLNAME) != lpsUser->lpszFullName) {
  4330. ec_log_warn("Disallowing user \"%s\" to update their fullname to \"%s\"",
  4331. oldDetails.GetPropString(OB_PROP_S_LOGIN).c_str(), lpsUser->lpszFullName);
  4332. lpsUser->lpszFullName = NULL;
  4333. }
  4334. if (lpsUser->lpszServername && oldDetails.GetPropString(OB_PROP_S_SERVERNAME) != lpsUser->lpszServername) {
  4335. ec_log_warn("Disallowing user \"%s\" to update their home server to \"%s\"",
  4336. oldDetails.GetPropString(OB_PROP_S_LOGIN).c_str(), lpsUser->lpszServername);
  4337. lpsUser->lpszServername = NULL;
  4338. }
  4339. // FIXME: check OB_PROP_B_NONACTIVE too? NOTE: ulIsNonActive is now ignored.
  4340. if (lpsUser->ulObjClass != (ULONG)-1 && oldDetails.GetClass() != (objectclass_t)lpsUser->ulObjClass) {
  4341. ec_log_warn("Disallowing user \"%s\" to update their active flag to %d",
  4342. oldDetails.GetPropString(OB_PROP_S_LOGIN).c_str(), lpsUser->ulObjClass);
  4343. lpsUser->ulObjClass = (ULONG)-1;
  4344. }
  4345. if (lpsUser->ulIsAdmin != (ULONG)-1 && oldDetails.GetPropInt(OB_PROP_I_ADMINLEVEL) != lpsUser->ulIsAdmin) {
  4346. ec_log_warn("Disallowing user \"%s\" to update their admin flag to %d",
  4347. oldDetails.GetPropString(OB_PROP_S_LOGIN).c_str(), lpsUser->ulIsAdmin);
  4348. lpsUser->ulIsAdmin = (ULONG)-1;
  4349. }
  4350. } else {
  4351. // you cannot set any details if you're not an admin or not yourself
  4352. ec_log_warn(
  4353. "Disallowing user \"%s\" to update details of user \"%s\"",
  4354. oldDetails.GetPropString(OB_PROP_S_LOGIN).c_str(),
  4355. lpsUser->lpszUsername);
  4356. return KCERR_NO_ACCESS;
  4357. }
  4358. details = objectdetails_t(ACTIVE_USER);
  4359. // construct new details
  4360. er = CopyUserDetailsFromSoap(lpsUser, &sExternId.id, &details, soap);
  4361. if (er != erSuccess)
  4362. return er;
  4363. er = lpecSession->GetUserManagement()->UpdateUserDetailsFromClient(&details);
  4364. if (er != erSuccess)
  4365. return er;
  4366. return lpecSession->GetUserManagement()->SetObjectDetailsAndSync(ulUserId, details, nullptr);
  4367. }
  4368. SOAP_ENTRY_END()
  4369. SOAP_ENTRY_START(getUser, lpsGetUserResponse->er, unsigned int ulUserId, entryId sUserId, struct getUserResponse *lpsGetUserResponse)
  4370. {
  4371. objectdetails_t details;
  4372. entryId sTmpUserId = {0};
  4373. er = GetLocalId(sUserId, ulUserId, &ulUserId, NULL);
  4374. if (er != erSuccess)
  4375. return er;
  4376. /* Check if we are able to view the returned userobject */
  4377. er = lpecSession->GetSecurity()->IsUserObjectVisible(ulUserId);
  4378. if (er != erSuccess)
  4379. return er;
  4380. lpsGetUserResponse->lpsUser = s_alloc<user>(soap);
  4381. memset(lpsGetUserResponse->lpsUser, 0, sizeof(struct user));
  4382. if (ulUserId == 0)
  4383. ulUserId = lpecSession->GetSecurity()->GetUserId();
  4384. er = lpecSession->GetUserManagement()->GetObjectDetails(ulUserId, &details);
  4385. if (er != erSuccess)
  4386. return er;
  4387. if (OBJECTCLASS_TYPE(details.GetClass()) != OBJECTTYPE_MAILUSER)
  4388. return KCERR_NOT_FOUND;
  4389. er = GetABEntryID(ulUserId, soap, &sTmpUserId);
  4390. if (er == erSuccess)
  4391. er = CopyUserDetailsToSoap(ulUserId, &sTmpUserId, details, lpecSession->GetCapabilities() & KOPANO_CAP_EXTENDED_ANON, soap, lpsGetUserResponse->lpsUser);
  4392. if (er != erSuccess)
  4393. return er;
  4394. // 6.40.0 stores the object class in the IsNonActive field
  4395. if (lpecSession->ClientVersion() == ZARAFA_VERSION_6_40_0)
  4396. lpsGetUserResponse->lpsUser->ulIsNonActive = lpsGetUserResponse->lpsUser->ulObjClass;
  4397. if (!bSupportUnicode) {
  4398. er = FixUserEncoding(soap, stringCompat, Out, lpsGetUserResponse->lpsUser);
  4399. if (er != erSuccess)
  4400. return er;
  4401. }
  4402. return erSuccess;
  4403. }
  4404. SOAP_ENTRY_END()
  4405. SOAP_ENTRY_START(getUserList, lpsUserList->er, unsigned int ulCompanyId, entryId sCompanyId, struct userListResponse *lpsUserList)
  4406. {
  4407. std::unique_ptr<std::list<localobjectdetails_t> > lpUsers;
  4408. entryId sUserEid = {0};
  4409. er = GetLocalId(sCompanyId, ulCompanyId, &ulCompanyId, NULL);
  4410. if (er != erSuccess)
  4411. return er;
  4412. /* Input check, if ulCompanyId is 0, we want the user's company,
  4413. * otherwise we must check if the requested company is visible for the user. */
  4414. if (ulCompanyId == 0)
  4415. er = lpecSession->GetSecurity()->GetUserCompany(&ulCompanyId);
  4416. else
  4417. er = lpecSession->GetSecurity()->IsUserObjectVisible(ulCompanyId);
  4418. if (er != erSuccess)
  4419. return er;
  4420. er = lpecSession->GetUserManagement()->GetCompanyObjectListAndSync(OBJECTCLASS_USER, ulCompanyId, &unique_tie(lpUsers), 0);
  4421. if(er != erSuccess)
  4422. return er;
  4423. lpsUserList->sUserArray.__size = 0;
  4424. lpsUserList->sUserArray.__ptr = s_alloc<user>(soap, lpUsers->size());
  4425. for (const auto &user : *lpUsers) {
  4426. if (OBJECTCLASS_TYPE(user.GetClass()) != OBJECTTYPE_MAILUSER ||
  4427. user.GetClass() == NONACTIVE_CONTACT)
  4428. continue;
  4429. er = GetABEntryID(user.ulId, soap, &sUserEid);
  4430. if (er != erSuccess)
  4431. return er;
  4432. er = CopyUserDetailsToSoap(user.ulId, &sUserEid, user,
  4433. lpecSession->GetCapabilities() & KOPANO_CAP_EXTENDED_ANON,
  4434. soap, &lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size]);
  4435. if (er != erSuccess)
  4436. return er;
  4437. // 6.40.0 stores the object class in the IsNonActive field
  4438. if (lpecSession->ClientVersion() == ZARAFA_VERSION_6_40_0)
  4439. lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size].ulIsNonActive = lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size].ulObjClass;
  4440. if (!bSupportUnicode) {
  4441. er = FixUserEncoding(soap, stringCompat, Out, lpsUserList->sUserArray.__ptr + lpsUserList->sUserArray.__size);
  4442. if (er != erSuccess)
  4443. return er;
  4444. }
  4445. ++lpsUserList->sUserArray.__size;
  4446. }
  4447. return erSuccess;
  4448. }
  4449. SOAP_ENTRY_END()
  4450. SOAP_ENTRY_START(getSendAsList, lpsUserList->er, unsigned int ulUserId, entryId sUserId, struct userListResponse *lpsUserList)
  4451. {
  4452. objectdetails_t userDetails, senderDetails;
  4453. list<unsigned int> userIds;
  4454. entryId sSenderEid = {0};
  4455. er = GetLocalId(sUserId, ulUserId, &ulUserId, NULL);
  4456. if (er != erSuccess)
  4457. return er;
  4458. er = lpecSession->GetSecurity()->IsUserObjectVisible(ulUserId);
  4459. if (er != erSuccess)
  4460. return er;
  4461. er = lpecSession->GetUserManagement()->GetObjectDetails(ulUserId, &userDetails);
  4462. if (er != erSuccess)
  4463. return er;
  4464. userIds = userDetails.GetPropListInt(OB_PROP_LI_SENDAS);
  4465. lpsUserList->sUserArray.__size = 0;
  4466. lpsUserList->sUserArray.__ptr = s_alloc<user>(soap, userIds.size());
  4467. for (auto user_id : userIds) {
  4468. if (lpecSession->GetSecurity()->IsUserObjectVisible(user_id) != erSuccess)
  4469. continue;
  4470. er = lpecSession->GetUserManagement()->GetObjectDetails(user_id, &senderDetails);
  4471. if (er == KCERR_NOT_FOUND)
  4472. continue;
  4473. if (er != erSuccess)
  4474. return er;
  4475. er = GetABEntryID(user_id, soap, &sSenderEid);
  4476. if (er != erSuccess)
  4477. return er;
  4478. er = CopyUserDetailsToSoap(user_id, &sSenderEid, senderDetails,
  4479. lpecSession->GetCapabilities() & KOPANO_CAP_EXTENDED_ANON,
  4480. soap, &lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size]);
  4481. if (er != erSuccess)
  4482. return er;
  4483. // 6.40.0 stores the object class in the IsNonActive field
  4484. if (lpecSession->ClientVersion() == ZARAFA_VERSION_6_40_0)
  4485. lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size].ulIsNonActive = lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size].ulObjClass;
  4486. if (!bSupportUnicode) {
  4487. er = FixUserEncoding(soap, stringCompat, Out, lpsUserList->sUserArray.__ptr + lpsUserList->sUserArray.__size);
  4488. if (er != erSuccess)
  4489. return er;
  4490. }
  4491. ++lpsUserList->sUserArray.__size;
  4492. }
  4493. return erSuccess;
  4494. }
  4495. SOAP_ENTRY_END()
  4496. SOAP_ENTRY_START(addSendAsUser, *result, unsigned int ulUserId, entryId sUserId, unsigned int ulSenderId, entryId sSenderId, unsigned int *result)
  4497. {
  4498. er = GetLocalId(sUserId, ulUserId, &ulUserId, NULL);
  4499. if (er != erSuccess) {
  4500. ec_log_err("addSendAsUser(): GetLocalId(ulUserId) failed %x", er);
  4501. return er;
  4502. }
  4503. er = GetLocalId(sSenderId, ulSenderId, &ulSenderId, NULL);
  4504. if (er != erSuccess) {
  4505. ec_log_err("addSendAsUser(): GetLocalId(ulSenderId) failed %x", er);
  4506. return er;
  4507. }
  4508. if (ulUserId == ulSenderId) {
  4509. ec_log_err("addSendAsUser(): ulUserId == ulSenderId");
  4510. return KCERR_COLLISION;
  4511. }
  4512. // Check security, only admins can set sendas users, not the user itself
  4513. if(lpecSession->GetSecurity()->IsAdminOverUserObject(ulUserId) != erSuccess) {
  4514. ec_log_err("addSendAsUser(): IsAdminOverUserObject failed %x", er);
  4515. return KCERR_NO_ACCESS;
  4516. }
  4517. // needed?
  4518. er = lpecSession->GetSecurity()->IsUserObjectVisible(ulUserId);
  4519. if (er != erSuccess) {
  4520. ec_log_err("addSendAsUser(): IsUserObjectVisible failed %x", er);
  4521. return er;
  4522. }
  4523. er = lpecSession->GetUserManagement()->AddSubObjectToObjectAndSync(OBJECTRELATION_USER_SENDAS, ulUserId, ulSenderId);
  4524. if (er != erSuccess)
  4525. ec_log_err("addSendAsUser(): AddSubObjectToObjectAndSync failed %x", er);
  4526. return er;
  4527. }
  4528. SOAP_ENTRY_END()
  4529. SOAP_ENTRY_START(delSendAsUser, *result, unsigned int ulUserId, entryId sUserId, unsigned int ulSenderId, entryId sSenderId, unsigned int *result)
  4530. {
  4531. er = GetLocalId(sUserId, ulUserId, &ulUserId, NULL);
  4532. if (er != erSuccess)
  4533. return er;
  4534. er = GetLocalId(sSenderId, ulSenderId, &ulSenderId, NULL);
  4535. if (er != erSuccess)
  4536. return er;
  4537. if (ulUserId == ulSenderId) {
  4538. ec_log_err("delSendAsUser(): ulUserId == ulSenderId");
  4539. return KCERR_COLLISION;
  4540. }
  4541. // Check security, only admins can set sendas users, not the user itself
  4542. if (lpecSession->GetSecurity()->IsAdminOverUserObject(ulUserId) != erSuccess)
  4543. return KCERR_NO_ACCESS;
  4544. // needed ?
  4545. er = lpecSession->GetSecurity()->IsUserObjectVisible(ulUserId);
  4546. if (er != erSuccess)
  4547. return er;
  4548. return lpecSession->GetUserManagement()->DeleteSubObjectFromObjectAndSync(OBJECTRELATION_USER_SENDAS, ulUserId, ulSenderId);
  4549. }
  4550. SOAP_ENTRY_END()
  4551. SOAP_ENTRY_START(purgeSoftDelete, *result, unsigned int ulDays, unsigned int *result)
  4552. {
  4553. unsigned int ulFolders = 0;
  4554. unsigned int ulMessages = 0;
  4555. unsigned int ulStores = 0;
  4556. // Only system-admins may run this
  4557. if (lpecSession->GetSecurity()->GetAdminLevel() < ADMIN_LEVEL_SYSADMIN)
  4558. return KCERR_NO_ACCESS;
  4559. ec_log_info("Start forced softdelete clean up");
  4560. er = PurgeSoftDelete(lpecSession, ulDays * 24 * 60 * 60, &ulMessages, &ulFolders, &ulStores, NULL);
  4561. if (er == erSuccess)
  4562. ec_log_info("Softdelete done: removed %d stores, %d folders, and %d messages", ulStores, ulFolders, ulMessages);
  4563. else if (er == KCERR_BUSY)
  4564. ec_log_info("Softdelete already running");
  4565. else
  4566. ec_log_info("Softdelete failed: removed %d stores, %d folders, and %d messages", ulStores, ulFolders, ulMessages);
  4567. return er;
  4568. }
  4569. SOAP_ENTRY_END()
  4570. static inline void kc_purge_cache_tcmalloc(void)
  4571. {
  4572. #ifdef HAVE_TCMALLOC
  4573. auto rfm = reinterpret_cast<decltype(MallocExtension_ReleaseFreeMemory) *>
  4574. (dlsym(NULL, "MallocExtension_ReleaseFreeMemory"));
  4575. if (rfm != NULL)
  4576. rfm();
  4577. #endif
  4578. }
  4579. SOAP_ENTRY_START(purgeCache, *result, unsigned int ulFlags, unsigned int *result)
  4580. {
  4581. if (lpecSession->GetSecurity()->GetAdminLevel() < ADMIN_LEVEL_SYSADMIN)
  4582. return KCERR_NO_ACCESS;
  4583. er = g_lpSessionManager->GetCacheManager()->PurgeCache(ulFlags);
  4584. kc_purge_cache_tcmalloc();
  4585. g_lpStatsCollector->SetTime(SCN_SERVER_LAST_CACHECLEARED, time(NULL));
  4586. return er;
  4587. }
  4588. SOAP_ENTRY_END()
  4589. //Create a store
  4590. // Info: Userid can also be a group id ('everyone' for public store)
  4591. SOAP_ENTRY_START(createStore, *result, unsigned int ulStoreType, unsigned int ulUserId, entryId sUserId, entryId sStoreId, entryId sRootId, unsigned int ulFlags, unsigned int *result)
  4592. {
  4593. unsigned int ulStoreId = 0;
  4594. unsigned int ulRootMapId = 0;
  4595. objectdetails_t userDetails;
  4596. unsigned int ulCompanyId = 0;
  4597. bool bHasLocalStore = false;
  4598. SOURCEKEY sSourceKey;
  4599. GUID guidStore;
  4600. time_t now;
  4601. unsigned int timeProps[] = { PR_LAST_MODIFICATION_TIME, PR_CREATION_TIME };
  4602. struct propVal sProp;
  4603. struct hiloLong sHilo;
  4604. struct rightsArray srightsArray;
  4605. USE_DATABASE();
  4606. memset(&srightsArray, 0 , sizeof(srightsArray));
  4607. if((unsigned int)sStoreId.__size < sizeof(EID_V0)) {
  4608. er = KCERR_INVALID_PARAMETER;
  4609. goto exit;
  4610. }
  4611. // Normalize flags
  4612. ((EID_V0 *)sStoreId.__ptr)->usFlags = 0;
  4613. er = GetLocalId(sUserId, ulUserId, &ulUserId, NULL);
  4614. if (er != erSuccess)
  4615. goto exit;
  4616. er = CheckUserStore(lpecSession, ulUserId, ulStoreType, &bHasLocalStore);
  4617. if (er != erSuccess)
  4618. goto exit;
  4619. if (!bHasLocalStore && (ulFlags & EC_OVERRIDE_HOMESERVER) == 0) {
  4620. ec_log_err("Create store requested, but store is not on this server, or server property not set for object %d", ulUserId);
  4621. er = KCERR_NOT_FOUND;
  4622. goto exit;
  4623. }
  4624. ec_log_info("Started to create store (userid=%d, type=%d)", ulUserId, ulStoreType);
  4625. er = lpDatabase->Begin();
  4626. if (er != erSuccess)
  4627. goto exit;
  4628. // Check permission
  4629. er = lpecSession->GetSecurity()->IsAdminOverUserObject(ulUserId);
  4630. if (er != erSuccess)
  4631. goto exit;
  4632. // Get object details, and resolve company
  4633. er = lpecSession->GetUserManagement()->GetObjectDetails(ulUserId, &userDetails);
  4634. if (er != erSuccess)
  4635. goto exit;
  4636. if (lpecSession->GetSessionManager()->IsHostedSupported())
  4637. ulCompanyId = userDetails.GetPropInt(OB_PROP_I_COMPANYID);
  4638. // Validate store entryid
  4639. if(ValidateZEntryId(sStoreId.__size, sStoreId.__ptr, MAPI_STORE) == false) {
  4640. er = KCERR_INVALID_ENTRYID;
  4641. goto exit;
  4642. }
  4643. // Validate root entryid
  4644. if(ValidateZEntryId(sRootId.__size, sRootId.__ptr, MAPI_FOLDER) == false) {
  4645. er = KCERR_INVALID_ENTRYID;
  4646. goto exit;
  4647. }
  4648. er = GetStoreGuidFromEntryId(sStoreId.__size, sStoreId.__ptr, &guidStore);
  4649. if(er != erSuccess)
  4650. goto exit;
  4651. // Check if there's already a store for the user or group
  4652. 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";
  4653. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  4654. if(er != erSuccess)
  4655. goto exit;
  4656. if(lpDatabase->GetNumRows(lpDBResult) > 0) {
  4657. er = KCERR_COLLISION;
  4658. ec_log_err("createStore(): already exists");
  4659. goto exit;
  4660. }
  4661. // Create Toplevel of the store
  4662. strQuery = "INSERT INTO hierarchy(parent, type, owner) VALUES(NULL, "+stringify(MAPI_STORE)+", "+ stringify(ulUserId)+")";
  4663. er = lpDatabase->DoInsert(strQuery, &ulStoreId);
  4664. if(er != erSuccess)
  4665. goto exit;
  4666. // Create the rootfolder of a store
  4667. strQuery = "INSERT INTO hierarchy(parent, type, owner) VALUES("+stringify(ulStoreId)+", "+stringify(MAPI_FOLDER)+ ", "+ stringify(ulUserId)+")";
  4668. er = lpDatabase->DoInsert(strQuery, &ulRootMapId);
  4669. if(er != erSuccess)
  4670. goto exit;
  4671. //Init storesize
  4672. er = UpdateObjectSize(lpDatabase, ulStoreId, MAPI_STORE, UPDATE_SET, 0);
  4673. if (er != erSuccess)
  4674. goto exit;
  4675. // Add SourceKey
  4676. er = lpecSession->GetNewSourceKey(&sSourceKey);
  4677. if(er != erSuccess)
  4678. goto exit;
  4679. er = RemoveStaleIndexedProp(lpDatabase, PR_SOURCE_KEY, sSourceKey, sSourceKey.size());
  4680. if (er != erSuccess)
  4681. goto exit;
  4682. strQuery = "INSERT INTO indexedproperties(hierarchyid,tag,val_binary) VALUES(" + stringify(ulRootMapId) + "," + stringify(PROP_ID(PR_SOURCE_KEY)) + "," + lpDatabase->EscapeBinary(sSourceKey, sSourceKey.size()) + ")";
  4683. er = lpDatabase->DoInsert(strQuery);
  4684. if(er != erSuccess)
  4685. goto exit;
  4686. // Add store entryid: 0x0FFF = PR_ENTRYID
  4687. er = RemoveStaleIndexedProp(lpDatabase, PR_ENTRYID, sStoreId.__ptr, sStoreId.__size);
  4688. if (er != erSuccess)
  4689. goto exit;
  4690. strQuery = "INSERT INTO indexedproperties (hierarchyid,tag,val_binary) VALUES("+stringify(ulStoreId)+", 0x0FFF, "+lpDatabase->EscapeBinary(sStoreId.__ptr, sStoreId.__size)+")";
  4691. er = lpDatabase->DoInsert(strQuery);
  4692. if(er != erSuccess)
  4693. goto exit;
  4694. // Add rootfolder entryid: 0x0FFF = PR_ENTRYID
  4695. er = RemoveStaleIndexedProp(lpDatabase, PR_ENTRYID, sRootId.__ptr, sRootId.__size);
  4696. if (er != erSuccess)
  4697. goto exit;
  4698. strQuery = "INSERT INTO indexedproperties (hierarchyid,tag,val_binary) VALUES("+stringify(ulRootMapId)+", 0x0FFF, "+lpDatabase->EscapeBinary(sRootId.__ptr, sRootId.__size)+")";
  4699. er = lpDatabase->DoInsert(strQuery);
  4700. if(er != erSuccess)
  4701. goto exit;
  4702. // Add rootfolder type: 0x3601 = FOLDER_ROOT (= 0)
  4703. strQuery = "INSERT INTO properties (tag,type,hierarchyid,val_ulong) VALUES(0x3601, 0x0003, "+stringify(ulRootMapId)+", 0)";
  4704. er = lpDatabase->DoInsert(strQuery);
  4705. if(er != erSuccess)
  4706. goto exit;
  4707. now = time(NULL);
  4708. for (size_t i = 0; i < ARRAY_SIZE(timeProps); ++i) {
  4709. sProp.ulPropTag = timeProps[i];
  4710. sProp.__union = SOAP_UNION_propValData_hilo;
  4711. sProp.Value.hilo = &sHilo;
  4712. UnixTimeToFileTime(now, &sProp.Value.hilo->hi, &sProp.Value.hilo->lo);
  4713. WriteProp(lpDatabase, ulStoreId, 0, &sProp);
  4714. if(er != erSuccess)
  4715. goto exit;
  4716. WriteProp(lpDatabase, ulRootMapId, 0, &sProp);
  4717. if(er != erSuccess)
  4718. goto exit;
  4719. }
  4720. // Couple store with user
  4721. strQuery = "INSERT INTO stores(hierarchy_id, user_id, type, user_name, company, guid) VALUES(" +
  4722. stringify(ulStoreId) + ", " +
  4723. stringify(ulUserId) + ", " +
  4724. stringify(ulStoreType) + ", " +
  4725. "'" + lpDatabase->Escape(userDetails.GetPropString(OB_PROP_S_LOGIN)) + "', " +
  4726. stringify(ulCompanyId) + ", " +
  4727. lpDatabase->EscapeBinary((unsigned char*)&guidStore , sizeof(GUID))+")";
  4728. er = lpDatabase->DoInsert(strQuery);
  4729. if(er != erSuccess)
  4730. goto exit;
  4731. // Set ACL's on public store
  4732. if(ulStoreType == ECSTORE_TYPE_PUBLIC) {
  4733. // ulUserId == a group
  4734. // ulUserId 1 = group everyone
  4735. srightsArray.__ptr = s_alloc<rights>(nullptr, 1);
  4736. srightsArray.__ptr[0].ulRights = ecRightsDefaultPublic;
  4737. srightsArray.__ptr[0].ulUserid = ulUserId;
  4738. srightsArray.__ptr[0].ulState = RIGHT_NEW|RIGHT_AUTOUPDATE_DENIED;
  4739. srightsArray.__ptr[0].ulType = ACCESS_TYPE_GRANT;
  4740. memset(&srightsArray.__ptr[0].sUserId, 0, sizeof(entryId));
  4741. srightsArray.__size = 1;
  4742. er = lpecSession->GetSecurity()->SetRights(ulStoreId, &srightsArray);
  4743. if(er != erSuccess)
  4744. goto exit;
  4745. }
  4746. er = lpDatabase->Commit();
  4747. if (er != erSuccess)
  4748. goto exit;
  4749. ec_log_info("Finished create store (userid=%d, storeid=%d, type=%d)", ulUserId, ulStoreId, ulStoreType);
  4750. exit:
  4751. if(er == KCERR_NO_ACCESS)
  4752. ec_log_err("Failed to create store access denied");
  4753. else if(er != erSuccess)
  4754. ec_log_err("Failed to create store (id=%d), errorcode=0x%08X", ulUserId, er);
  4755. s_free(nullptr, srightsArray.__ptr);
  4756. ROLLBACK_ON_ERROR();
  4757. }
  4758. SOAP_ENTRY_END()
  4759. SOAP_ENTRY_START(createGroup, lpsSetGroupResponse->er, struct group *lpsGroup, struct setGroupResponse *lpsSetGroupResponse)
  4760. {
  4761. unsigned int ulGroupId = 0;
  4762. objectdetails_t details(DISTLIST_SECURITY); // DB plugin wants to be able to set permissions on groups
  4763. if (lpsGroup == nullptr || lpsGroup->lpszGroupname == nullptr ||
  4764. lpsGroup->lpszFullname == nullptr)
  4765. return KCERR_INVALID_PARAMETER;
  4766. if (!bSupportUnicode) {
  4767. er = FixGroupEncoding(soap, stringCompat, In, lpsGroup);
  4768. if (er != erSuccess)
  4769. return er;
  4770. }
  4771. er = CopyGroupDetailsFromSoap(lpsGroup, NULL, &details, soap);
  4772. if (er != erSuccess)
  4773. return er;
  4774. er = lpecSession->GetUserManagement()->UpdateUserDetailsFromClient(&details);
  4775. if (er != erSuccess)
  4776. return er;
  4777. // Check permission
  4778. er = lpecSession->GetSecurity()->IsAdminOverUserObject(details.GetPropInt(OB_PROP_I_COMPANYID));
  4779. if(er != erSuccess)
  4780. return er;
  4781. er = lpecSession->GetUserManagement()->CreateObjectAndSync(details, &ulGroupId);
  4782. if (er != erSuccess)
  4783. return er;
  4784. er = GetABEntryID(ulGroupId, soap, &lpsSetGroupResponse->sGroupId);
  4785. if (er != erSuccess)
  4786. return er;
  4787. lpsSetGroupResponse->ulGroupId = ulGroupId;
  4788. return erSuccess;
  4789. }
  4790. SOAP_ENTRY_END()
  4791. SOAP_ENTRY_START(setGroup, *result, struct group *lpsGroup, unsigned int *result)
  4792. {
  4793. objectdetails_t details;
  4794. unsigned int ulGroupId = 0;
  4795. objectid_t sExternId;
  4796. if (lpsGroup == nullptr)
  4797. return KCERR_INVALID_PARAMETER;
  4798. if (!bSupportUnicode) {
  4799. er = FixGroupEncoding(soap, stringCompat, In, lpsGroup);
  4800. if (er != erSuccess)
  4801. return er;
  4802. }
  4803. if (lpsGroup->sGroupId.__size > 0 && lpsGroup->sGroupId.__ptr != NULL)
  4804. {
  4805. er = GetLocalId(lpsGroup->sGroupId, lpsGroup->ulGroupId, &ulGroupId, &sExternId);
  4806. if (er != erSuccess)
  4807. return er;
  4808. }
  4809. else
  4810. ulGroupId = lpsGroup->ulGroupId;
  4811. // Check permission
  4812. er = lpecSession->GetSecurity()->IsAdminOverUserObject(ulGroupId);
  4813. if(er != erSuccess)
  4814. return er;
  4815. details = objectdetails_t(DISTLIST_GROUP);
  4816. er = CopyGroupDetailsFromSoap(lpsGroup, &sExternId.id, &details, soap);
  4817. if (er != erSuccess)
  4818. return er;
  4819. er = lpecSession->GetUserManagement()->UpdateUserDetailsFromClient(&details);
  4820. if (er != erSuccess)
  4821. return er;
  4822. return lpecSession->GetUserManagement()->SetObjectDetailsAndSync(ulGroupId, details, nullptr);
  4823. }
  4824. SOAP_ENTRY_END()
  4825. SOAP_ENTRY_START(getGroup, lpsResponse->er, unsigned int ulGroupId, entryId sGroupId, struct getGroupResponse *lpsResponse)
  4826. {
  4827. objectdetails_t details;
  4828. entryId sTmpGroupId = {0};
  4829. er = GetLocalId(sGroupId, ulGroupId, &ulGroupId, NULL);
  4830. if (er != erSuccess)
  4831. return er;
  4832. /* Check if we are able to view the returned userobject */
  4833. er = lpecSession->GetSecurity()->IsUserObjectVisible(ulGroupId);
  4834. if (er != erSuccess)
  4835. return er;
  4836. er = lpecSession->GetUserManagement()->GetObjectDetails(ulGroupId, &details);
  4837. if (er != erSuccess)
  4838. return er;
  4839. if (OBJECTCLASS_TYPE(details.GetClass()) != OBJECTTYPE_DISTLIST)
  4840. return KCERR_NOT_FOUND;
  4841. lpsResponse->lpsGroup = s_alloc<group>(soap);
  4842. er = GetABEntryID(ulGroupId, soap, &sTmpGroupId);
  4843. if (er == erSuccess)
  4844. er = CopyGroupDetailsToSoap(ulGroupId, &sTmpGroupId, details, lpecSession->GetCapabilities() & KOPANO_CAP_EXTENDED_ANON, soap, lpsResponse->lpsGroup);
  4845. if (er != erSuccess)
  4846. return er;
  4847. if (!bSupportUnicode) {
  4848. er = FixGroupEncoding(soap, stringCompat, Out, lpsResponse->lpsGroup);
  4849. if (er != erSuccess)
  4850. return er;
  4851. }
  4852. return erSuccess;
  4853. }
  4854. SOAP_ENTRY_END()
  4855. SOAP_ENTRY_START(getGroupList, lpsGroupList->er, unsigned int ulCompanyId, entryId sCompanyId, struct groupListResponse *lpsGroupList)
  4856. {
  4857. std::unique_ptr<std::list<localobjectdetails_t> > lpGroups;
  4858. entryId sGroupEid = {0};
  4859. er = GetLocalId(sCompanyId, ulCompanyId, &ulCompanyId, NULL);
  4860. if (er != erSuccess)
  4861. return er;
  4862. /* Input check, if ulCompanyId is 0, we want the user's company,
  4863. * otherwise we must check if the requested company is visible for the user. */
  4864. if (ulCompanyId == 0)
  4865. er = lpecSession->GetSecurity()->GetUserCompany(&ulCompanyId);
  4866. else
  4867. er = lpecSession->GetSecurity()->IsUserObjectVisible(ulCompanyId);
  4868. if (er != erSuccess)
  4869. return er;
  4870. er = lpecSession->GetUserManagement()->GetCompanyObjectListAndSync(OBJECTCLASS_DISTLIST, ulCompanyId, &unique_tie(lpGroups), 0);
  4871. if (er != erSuccess)
  4872. return er;
  4873. lpsGroupList->sGroupArray.__size = 0;
  4874. lpsGroupList->sGroupArray.__ptr = s_alloc<group>(soap, lpGroups->size());
  4875. for (const auto &grp : *lpGroups) {
  4876. if (OBJECTCLASS_TYPE(grp.GetClass()) != OBJECTTYPE_DISTLIST)
  4877. continue;
  4878. er = GetABEntryID(grp.ulId, soap, &sGroupEid);
  4879. if (er != erSuccess)
  4880. return er;
  4881. er = CopyGroupDetailsToSoap(grp.ulId, &sGroupEid, grp,
  4882. lpecSession->GetCapabilities() & KOPANO_CAP_EXTENDED_ANON,
  4883. soap, &lpsGroupList->sGroupArray.__ptr[lpsGroupList->sGroupArray.__size]);
  4884. if (er != erSuccess)
  4885. return er;
  4886. if (!bSupportUnicode) {
  4887. er = FixGroupEncoding(soap, stringCompat, Out, lpsGroupList->sGroupArray.__ptr + lpsGroupList->sGroupArray.__size);
  4888. if (er != erSuccess)
  4889. return er;
  4890. }
  4891. ++lpsGroupList->sGroupArray.__size;
  4892. }
  4893. return erSuccess;
  4894. }
  4895. SOAP_ENTRY_END()
  4896. SOAP_ENTRY_START(groupDelete, *result, unsigned int ulGroupId, entryId sGroupId, unsigned int *result)
  4897. {
  4898. er = GetLocalId(sGroupId, ulGroupId, &ulGroupId, NULL);
  4899. if (er != erSuccess)
  4900. return er;
  4901. // Check permission
  4902. er = lpecSession->GetSecurity()->IsAdminOverUserObject(ulGroupId);
  4903. if(er != erSuccess)
  4904. return er;
  4905. return lpecSession->GetUserManagement()->DeleteObjectAndSync(ulGroupId);
  4906. }
  4907. SOAP_ENTRY_END()
  4908. SOAP_ENTRY_START(resolveUsername, lpsResponse->er, char *lpszUsername, struct resolveUserResponse *lpsResponse)
  4909. {
  4910. unsigned int ulUserId = 0;
  4911. if (lpszUsername == nullptr)
  4912. return KCERR_INVALID_PARAMETER;
  4913. er = lpecSession->GetUserManagement()->ResolveObjectAndSync(OBJECTCLASS_USER, STRIN_FIX(lpszUsername), &ulUserId);
  4914. if (er != erSuccess)
  4915. return er;
  4916. /* Check if we are able to view the returned userobject */
  4917. er = lpecSession->GetSecurity()->IsUserObjectVisible(ulUserId);
  4918. if (er != erSuccess)
  4919. return er;
  4920. er = GetABEntryID(ulUserId, soap, &lpsResponse->sUserId);
  4921. if (er != erSuccess)
  4922. return er;
  4923. lpsResponse->ulUserId = ulUserId;
  4924. return erSuccess;
  4925. }
  4926. SOAP_ENTRY_END()
  4927. SOAP_ENTRY_START(resolveGroupname, lpsResponse->er, char *lpszGroupname, struct resolveGroupResponse *lpsResponse)
  4928. {
  4929. unsigned int ulGroupId = 0;
  4930. if (lpszGroupname == nullptr)
  4931. return KCERR_INVALID_PARAMETER;
  4932. er = lpecSession->GetUserManagement()->ResolveObjectAndSync(OBJECTCLASS_DISTLIST, STRIN_FIX(lpszGroupname), &ulGroupId);
  4933. if (er != erSuccess)
  4934. return er;
  4935. /* Check if we are able to view the returned userobject */
  4936. er = lpecSession->GetSecurity()->IsUserObjectVisible(ulGroupId);
  4937. if (er != erSuccess)
  4938. return er;
  4939. er = GetABEntryID(ulGroupId, soap, &lpsResponse->sGroupId);
  4940. if (er != erSuccess)
  4941. return er;
  4942. lpsResponse->ulGroupId = ulGroupId;
  4943. return erSuccess;
  4944. }
  4945. SOAP_ENTRY_END()
  4946. SOAP_ENTRY_START(deleteGroupUser, *result, unsigned int ulGroupId, entryId sGroupId, unsigned int ulUserId, entryId sUserId, unsigned int *result)
  4947. {
  4948. er = GetLocalId(sGroupId, ulGroupId, &ulGroupId, NULL);
  4949. if (er != erSuccess)
  4950. return er;
  4951. // Check permission
  4952. er = lpecSession->GetSecurity()->IsAdminOverUserObject(ulGroupId);
  4953. if(er != erSuccess)
  4954. return er;
  4955. er = GetLocalId(sUserId, ulUserId, &ulUserId, NULL);
  4956. if (er != erSuccess)
  4957. return er;
  4958. return lpecSession->GetUserManagement()->DeleteSubObjectFromObjectAndSync(OBJECTRELATION_GROUP_MEMBER, ulGroupId, ulUserId);
  4959. }
  4960. SOAP_ENTRY_END()
  4961. SOAP_ENTRY_START(addGroupUser, *result, unsigned int ulGroupId, entryId sGroupId, unsigned int ulUserId, entryId sUserId, unsigned int *result)
  4962. {
  4963. er = GetLocalId(sGroupId, ulGroupId, &ulGroupId, NULL);
  4964. if (er != erSuccess)
  4965. return er;
  4966. // Check permission
  4967. er = lpecSession->GetSecurity()->IsAdminOverUserObject(ulGroupId);
  4968. if(er != erSuccess)
  4969. return er;
  4970. er = GetLocalId(sUserId, ulUserId, &ulUserId, NULL);
  4971. if (er != erSuccess)
  4972. return er;
  4973. return lpecSession->GetUserManagement()->AddSubObjectToObjectAndSync(OBJECTRELATION_GROUP_MEMBER, ulGroupId, ulUserId);
  4974. }
  4975. SOAP_ENTRY_END()
  4976. // not only returns users of a group anymore
  4977. // TODO resolve group in group here on the fly?
  4978. SOAP_ENTRY_START(getUserListOfGroup, lpsUserList->er, unsigned int ulGroupId, entryId sGroupId, struct userListResponse *lpsUserList)
  4979. {
  4980. std::unique_ptr<std::list<localobjectdetails_t> > lpUsers;
  4981. entryId sUserEid = {0};
  4982. er = GetLocalId(sGroupId, ulGroupId, &ulGroupId, NULL);
  4983. if (er != erSuccess)
  4984. return er;
  4985. er = lpecSession->GetSecurity()->IsUserObjectVisible(ulGroupId);
  4986. if (er != erSuccess)
  4987. return er;
  4988. er = lpecSession->GetUserManagement()->GetSubObjectsOfObjectAndSync(OBJECTRELATION_GROUP_MEMBER, ulGroupId, &unique_tie(lpUsers));
  4989. if(er != erSuccess)
  4990. return er;
  4991. lpsUserList->sUserArray.__size = 0;
  4992. lpsUserList->sUserArray.__ptr = s_alloc<user>(soap, lpUsers->size());
  4993. for (const auto &user : *lpUsers) {
  4994. if (lpecSession->GetSecurity()->IsUserObjectVisible(user.ulId) != erSuccess)
  4995. continue;
  4996. er = GetABEntryID(user.ulId, soap, &sUserEid);
  4997. if (er != erSuccess)
  4998. return er;
  4999. // @todo Whoops, we can have group-in-groups. But since details of a group are almost identical to user details (eg. name, fullname, email)
  5000. // this copy will succeed without any problems ... but it's definitly not correct.
  5001. er = CopyUserDetailsToSoap(user.ulId, &sUserEid, user,
  5002. lpecSession->GetCapabilities() & KOPANO_CAP_EXTENDED_ANON,
  5003. soap, &lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size]);
  5004. if (er != erSuccess)
  5005. return er;
  5006. // 6.40.0 stores the object class in the IsNonActive field
  5007. if (lpecSession->ClientVersion() == ZARAFA_VERSION_6_40_0)
  5008. lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size].ulIsNonActive = lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size].ulObjClass;
  5009. if (!bSupportUnicode) {
  5010. er = FixUserEncoding(soap, stringCompat, Out, lpsUserList->sUserArray.__ptr + lpsUserList->sUserArray.__size);
  5011. if (er != erSuccess)
  5012. return er;
  5013. }
  5014. ++lpsUserList->sUserArray.__size;
  5015. }
  5016. return erSuccess;
  5017. }
  5018. SOAP_ENTRY_END()
  5019. SOAP_ENTRY_START(getGroupListOfUser, lpsGroupList->er, unsigned int ulUserId, entryId sUserId, struct groupListResponse *lpsGroupList)
  5020. {
  5021. std::unique_ptr<std::list<localobjectdetails_t> > lpGroups;
  5022. entryId sGroupEid = {0};
  5023. er = GetLocalId(sUserId, ulUserId, &ulUserId, NULL);
  5024. if (er != erSuccess)
  5025. return er;
  5026. er = lpecSession->GetSecurity()->IsUserObjectVisible(ulUserId);
  5027. if (er != erSuccess)
  5028. return er;
  5029. er = lpecSession->GetUserManagement()->GetParentObjectsOfObjectAndSync(OBJECTRELATION_GROUP_MEMBER, ulUserId, &unique_tie(lpGroups));
  5030. if(er != erSuccess)
  5031. return er;
  5032. lpsGroupList->sGroupArray.__size = 0;
  5033. lpsGroupList->sGroupArray.__ptr = s_alloc<group>(soap, lpGroups->size());
  5034. for (const auto &grp : *lpGroups) {
  5035. if (lpecSession->GetSecurity()->IsUserObjectVisible(grp.ulId) != erSuccess)
  5036. continue;
  5037. er = GetABEntryID(grp.ulId, soap, &sGroupEid);
  5038. if (er != erSuccess)
  5039. return er;
  5040. er = CopyGroupDetailsToSoap(grp.ulId, &sGroupEid, grp,
  5041. lpecSession->GetCapabilities() & KOPANO_CAP_EXTENDED_ANON,
  5042. soap, &lpsGroupList->sGroupArray.__ptr[lpsGroupList->sGroupArray.__size]);
  5043. if (er != erSuccess)
  5044. return er;
  5045. if (!bSupportUnicode) {
  5046. er = FixGroupEncoding(soap, stringCompat, Out, lpsGroupList->sGroupArray.__ptr + lpsGroupList->sGroupArray.__size);
  5047. if (er != erSuccess)
  5048. return er;
  5049. }
  5050. ++lpsGroupList->sGroupArray.__size;
  5051. }
  5052. return erSuccess;
  5053. }
  5054. SOAP_ENTRY_END()
  5055. SOAP_ENTRY_START(createCompany, lpsResponse->er, struct company *lpsCompany, struct setCompanyResponse *lpsResponse)
  5056. {
  5057. unsigned int ulCompanyId = 0;
  5058. objectdetails_t details(CONTAINER_COMPANY);
  5059. if (lpsCompany == nullptr)
  5060. return KCERR_INVALID_PARAMETER;
  5061. if (!g_lpSessionManager->IsHostedSupported())
  5062. return KCERR_NO_SUPPORT;
  5063. // Check permission, only the system user is allowed to create or delete a company
  5064. er = lpecSession->GetSecurity()->IsAdminOverUserObject(KOPANO_UID_SYSTEM);
  5065. if(er != erSuccess)
  5066. return er;
  5067. if (!bSupportUnicode) {
  5068. er = FixCompanyEncoding(soap, stringCompat, In, lpsCompany);
  5069. if (er != erSuccess)
  5070. return er;
  5071. }
  5072. er = CopyCompanyDetailsFromSoap(lpsCompany, NULL, KOPANO_UID_SYSTEM, &details, soap);
  5073. if (er != erSuccess)
  5074. return er;
  5075. er = lpecSession->GetUserManagement()->UpdateUserDetailsFromClient(&details);
  5076. if (er != erSuccess)
  5077. return er;
  5078. er = lpecSession->GetUserManagement()->CreateObjectAndSync(details, &ulCompanyId);
  5079. if(er != erSuccess)
  5080. return er;
  5081. er = GetABEntryID(ulCompanyId, soap, &lpsResponse->sCompanyId);
  5082. if (er != erSuccess)
  5083. return er;
  5084. lpsResponse->ulCompanyId = ulCompanyId;
  5085. return erSuccess;
  5086. }
  5087. SOAP_ENTRY_END()
  5088. SOAP_ENTRY_START(deleteCompany, *result, unsigned int ulCompanyId, entryId sCompanyId, unsigned int *result)
  5089. {
  5090. if (!g_lpSessionManager->IsHostedSupported())
  5091. return KCERR_NO_SUPPORT;
  5092. // Check permission, only the system user is allowed to create or delete a company
  5093. er = lpecSession->GetSecurity()->IsAdminOverUserObject(KOPANO_UID_SYSTEM);
  5094. if(er != erSuccess)
  5095. return er;
  5096. er = GetLocalId(sCompanyId, ulCompanyId, &ulCompanyId, NULL);
  5097. if (er != erSuccess)
  5098. return er;
  5099. return lpecSession->GetUserManagement()->DeleteObjectAndSync(ulCompanyId);
  5100. }
  5101. SOAP_ENTRY_END()
  5102. SOAP_ENTRY_START(setCompany, *result, struct company *lpsCompany, unsigned int *result)
  5103. {
  5104. objectdetails_t details;
  5105. unsigned int ulCompanyId = 0;
  5106. unsigned int ulAdministrator = 0;
  5107. objectid_t sExternId;
  5108. if (lpsCompany == nullptr)
  5109. return KCERR_INVALID_PARAMETER;
  5110. if (!g_lpSessionManager->IsHostedSupported())
  5111. return KCERR_NO_SUPPORT;
  5112. if (!bSupportUnicode) {
  5113. er = FixCompanyEncoding(soap, stringCompat, In, lpsCompany);
  5114. if (er != erSuccess)
  5115. return er;
  5116. }
  5117. if (lpsCompany->sCompanyId.__size > 0 && lpsCompany->sCompanyId.__ptr != NULL)
  5118. {
  5119. er = GetLocalId(lpsCompany->sCompanyId, lpsCompany->ulCompanyId, &ulCompanyId, &sExternId);
  5120. if (er != erSuccess)
  5121. return er;
  5122. }
  5123. else
  5124. ulCompanyId = lpsCompany->ulCompanyId;
  5125. er = GetLocalId(lpsCompany->sAdministrator, lpsCompany->ulAdministrator, &ulAdministrator, NULL);
  5126. if (er != erSuccess)
  5127. return er;
  5128. // Check permission
  5129. er = lpecSession->GetSecurity()->IsAdminOverUserObject(ulCompanyId);
  5130. if(er != erSuccess)
  5131. return er;
  5132. details = objectdetails_t(CONTAINER_COMPANY);
  5133. er = CopyCompanyDetailsFromSoap(lpsCompany, &sExternId.id, ulAdministrator, &details, soap);
  5134. if (er != erSuccess)
  5135. return er;
  5136. er = lpecSession->GetUserManagement()->UpdateUserDetailsFromClient(&details);
  5137. if (er != erSuccess)
  5138. return er;
  5139. return lpecSession->GetUserManagement()->SetObjectDetailsAndSync(ulCompanyId, details, nullptr);
  5140. }
  5141. SOAP_ENTRY_END()
  5142. SOAP_ENTRY_START(getCompany, lpsResponse->er, unsigned int ulCompanyId, entryId sCompanyId, struct getCompanyResponse *lpsResponse)
  5143. {
  5144. objectdetails_t details;
  5145. unsigned int ulAdmin = 0;
  5146. entryId sAdminEid = {0};
  5147. entryId sTmpCompanyId = {0};
  5148. if (!g_lpSessionManager->IsHostedSupported())
  5149. return KCERR_NO_SUPPORT;
  5150. er = GetLocalId(sCompanyId, ulCompanyId, &ulCompanyId, NULL);
  5151. if (er != erSuccess)
  5152. return er;
  5153. /* Input check, if ulCompanyId is 0, we want the user's company,
  5154. * otherwise we must check if the requested company is visible for the user. */
  5155. if (ulCompanyId == 0)
  5156. er = lpecSession->GetSecurity()->GetUserCompany(&ulCompanyId);
  5157. else
  5158. er = lpecSession->GetSecurity()->IsUserObjectVisible(ulCompanyId);
  5159. if (er != erSuccess)
  5160. return er;
  5161. er = lpecSession->GetUserManagement()->GetObjectDetails(ulCompanyId, &details);
  5162. if(er != erSuccess)
  5163. return er;
  5164. if (details.GetClass() != CONTAINER_COMPANY)
  5165. return KCERR_NOT_FOUND;
  5166. ulAdmin = details.GetPropInt(OB_PROP_I_SYSADMIN);
  5167. er = lpecSession->GetSecurity()->IsUserObjectVisible(ulAdmin);
  5168. if (er != erSuccess)
  5169. return er;
  5170. er = GetABEntryID(ulAdmin, soap, &sAdminEid);
  5171. if (er != erSuccess)
  5172. return er;
  5173. er = GetABEntryID(ulCompanyId, soap, &sTmpCompanyId);
  5174. if (er != erSuccess)
  5175. return er;
  5176. lpsResponse->lpsCompany = s_alloc<company>(soap);
  5177. memset(lpsResponse->lpsCompany, 0, sizeof(company));
  5178. er = CopyCompanyDetailsToSoap(ulCompanyId, &sTmpCompanyId, ulAdmin, &sAdminEid, details, lpecSession->GetCapabilities() & KOPANO_CAP_EXTENDED_ANON, soap, lpsResponse->lpsCompany);
  5179. if (er != erSuccess)
  5180. return er;
  5181. if (!bSupportUnicode) {
  5182. er = FixCompanyEncoding(soap, stringCompat, Out, lpsResponse->lpsCompany);
  5183. if (er != erSuccess)
  5184. return er;
  5185. }
  5186. return erSuccess;
  5187. }
  5188. SOAP_ENTRY_END()
  5189. SOAP_ENTRY_START(resolveCompanyname, lpsResponse->er, char *lpszCompanyname, struct resolveCompanyResponse *lpsResponse)
  5190. {
  5191. unsigned int ulCompanyId = 0;
  5192. if (!g_lpSessionManager->IsHostedSupported())
  5193. return KCERR_NO_SUPPORT;
  5194. if (lpszCompanyname == nullptr)
  5195. return KCERR_INVALID_PARAMETER;
  5196. er = lpecSession->GetUserManagement()->ResolveObjectAndSync(CONTAINER_COMPANY, STRIN_FIX(lpszCompanyname), &ulCompanyId);
  5197. if(er != erSuccess)
  5198. return er;
  5199. /* Check if we are able to view the returned userobject */
  5200. er = lpecSession->GetSecurity()->IsUserObjectVisible(ulCompanyId);
  5201. if (er != erSuccess)
  5202. return er;
  5203. er = GetABEntryID(ulCompanyId, soap, &lpsResponse->sCompanyId);
  5204. if (er != erSuccess)
  5205. return er;
  5206. lpsResponse->ulCompanyId = ulCompanyId;
  5207. return erSuccess;
  5208. }
  5209. SOAP_ENTRY_END()
  5210. SOAP_ENTRY_START(getCompanyList, lpsCompanyList->er, struct companyListResponse *lpsCompanyList)
  5211. {
  5212. unsigned int ulAdmin = 0;
  5213. entryId sCompanyEid = {0};
  5214. entryId sAdminEid = {0};
  5215. std::unique_ptr<std::list<localobjectdetails_t> > lpCompanies;
  5216. if (!g_lpSessionManager->IsHostedSupported())
  5217. return KCERR_NO_SUPPORT;
  5218. er = lpecSession->GetSecurity()->GetViewableCompanyIds(0, &unique_tie(lpCompanies));
  5219. if(er != erSuccess)
  5220. return er;
  5221. lpsCompanyList->sCompanyArray.__size = 0;
  5222. lpsCompanyList->sCompanyArray.__ptr = s_alloc<company>(soap, lpCompanies->size());
  5223. for (const auto &com : *lpCompanies) {
  5224. ulAdmin = com.GetPropInt(OB_PROP_I_SYSADMIN);
  5225. er = lpecSession->GetSecurity()->IsUserObjectVisible(ulAdmin);
  5226. if (er != erSuccess)
  5227. return er;
  5228. er = GetABEntryID(com.ulId, soap, &sCompanyEid);
  5229. if (er != erSuccess)
  5230. return er;
  5231. er = GetABEntryID(ulAdmin, soap, &sAdminEid);
  5232. if (er != erSuccess)
  5233. return er;
  5234. er = CopyCompanyDetailsToSoap(com.ulId, &sCompanyEid,
  5235. ulAdmin, &sAdminEid, com,
  5236. lpecSession->GetCapabilities() & KOPANO_CAP_EXTENDED_ANON,
  5237. soap, &lpsCompanyList->sCompanyArray.__ptr[lpsCompanyList->sCompanyArray.__size]);
  5238. if (er != erSuccess)
  5239. return er;
  5240. if (!bSupportUnicode) {
  5241. er = FixCompanyEncoding(soap, stringCompat, Out, lpsCompanyList->sCompanyArray.__ptr + lpsCompanyList->sCompanyArray.__size);
  5242. if (er != erSuccess)
  5243. return er;
  5244. }
  5245. ++lpsCompanyList->sCompanyArray.__size;
  5246. }
  5247. return erSuccess;
  5248. }
  5249. SOAP_ENTRY_END()
  5250. SOAP_ENTRY_START(addCompanyToRemoteViewList, *result, unsigned int ulSetCompanyId, entryId sSetCompanyId, unsigned int ulCompanyId, entryId sCompanyId, unsigned int *result)
  5251. {
  5252. if (!g_lpSessionManager->IsHostedSupported())
  5253. return KCERR_NO_SUPPORT;
  5254. er = GetLocalId(sCompanyId, ulCompanyId, &ulCompanyId, NULL);
  5255. if (er != erSuccess)
  5256. return er;
  5257. // Check permission
  5258. er = lpecSession->GetSecurity()->IsAdminOverUserObject(ulCompanyId);
  5259. if(er != erSuccess)
  5260. return er;
  5261. er = GetLocalId(sSetCompanyId, ulSetCompanyId, &ulSetCompanyId, NULL);
  5262. if (er != erSuccess)
  5263. return er;
  5264. return lpecSession->GetUserManagement()->AddSubObjectToObjectAndSync(OBJECTRELATION_COMPANY_VIEW, ulCompanyId, ulSetCompanyId);
  5265. }
  5266. SOAP_ENTRY_END()
  5267. SOAP_ENTRY_START(delCompanyFromRemoteViewList, *result, unsigned int ulSetCompanyId, entryId sSetCompanyId, unsigned int ulCompanyId, entryId sCompanyId, unsigned int *result)
  5268. {
  5269. if (!g_lpSessionManager->IsHostedSupported())
  5270. return KCERR_NO_SUPPORT;
  5271. er = GetLocalId(sCompanyId, ulCompanyId, &ulCompanyId, NULL);
  5272. if (er != erSuccess)
  5273. return er;
  5274. // Check permission
  5275. er = lpecSession->GetSecurity()->IsAdminOverUserObject(ulCompanyId);
  5276. if(er != erSuccess)
  5277. return er;
  5278. er = GetLocalId(sSetCompanyId, ulSetCompanyId, &ulSetCompanyId, NULL);
  5279. if (er != erSuccess)
  5280. return er;
  5281. return lpecSession->GetUserManagement()->DeleteSubObjectFromObjectAndSync(OBJECTRELATION_COMPANY_VIEW, ulCompanyId, ulSetCompanyId);
  5282. }
  5283. SOAP_ENTRY_END()
  5284. SOAP_ENTRY_START(getRemoteViewList, lpsCompanyList->er, unsigned int ulCompanyId, entryId sCompanyId, struct companyListResponse *lpsCompanyList)
  5285. {
  5286. unsigned int ulAdmin = 0;
  5287. entryId sCompanyEid = {0};
  5288. entryId sAdminEid = {0};
  5289. std::unique_ptr<std::list<localobjectdetails_t> > lpCompanies;
  5290. if (!g_lpSessionManager->IsHostedSupported())
  5291. return KCERR_NO_SUPPORT;
  5292. er = GetLocalId(sCompanyId, ulCompanyId, &ulCompanyId, NULL);
  5293. if (er != erSuccess)
  5294. return er;
  5295. /* Input check, if ulCompanyId is 0, we want the user's company,
  5296. * otherwise we must check if the requested company is visible for the user. */
  5297. if (ulCompanyId == 0)
  5298. er = lpecSession->GetSecurity()->GetUserCompany(&ulCompanyId);
  5299. else
  5300. er = lpecSession->GetSecurity()->IsUserObjectVisible(ulCompanyId);
  5301. if (er != erSuccess)
  5302. return er;
  5303. er = lpecSession->GetUserManagement()->GetSubObjectsOfObjectAndSync(OBJECTRELATION_COMPANY_VIEW, ulCompanyId, &unique_tie(lpCompanies));
  5304. if(er != erSuccess)
  5305. return er;
  5306. lpsCompanyList->sCompanyArray.__size = 0;
  5307. lpsCompanyList->sCompanyArray.__ptr = s_alloc<company>(soap, lpCompanies->size());
  5308. for (const auto &com : *lpCompanies) {
  5309. if (lpecSession->GetSecurity()->IsUserObjectVisible(com.ulId) != erSuccess)
  5310. continue;
  5311. ulAdmin = com.GetPropInt(OB_PROP_I_SYSADMIN);
  5312. er = lpecSession->GetSecurity()->IsUserObjectVisible(ulAdmin);
  5313. if (er != erSuccess)
  5314. return er;
  5315. er = GetABEntryID(com.ulId, soap, &sCompanyEid);
  5316. if (er != erSuccess)
  5317. return er;
  5318. er = GetABEntryID(ulAdmin, soap, &sAdminEid);
  5319. if (er != erSuccess)
  5320. return er;
  5321. er = CopyCompanyDetailsToSoap(com.ulId, &sCompanyEid,
  5322. ulAdmin, &sAdminEid, com,
  5323. lpecSession->GetCapabilities() & KOPANO_CAP_EXTENDED_ANON,
  5324. soap, &lpsCompanyList->sCompanyArray.__ptr[lpsCompanyList->sCompanyArray.__size]);
  5325. if (er != erSuccess)
  5326. return er;
  5327. if (!bSupportUnicode) {
  5328. er = FixCompanyEncoding(soap, stringCompat, Out, lpsCompanyList->sCompanyArray.__ptr + lpsCompanyList->sCompanyArray.__size);
  5329. if (er != erSuccess)
  5330. return er;
  5331. }
  5332. ++lpsCompanyList->sCompanyArray.__size;
  5333. }
  5334. return erSuccess;
  5335. }
  5336. SOAP_ENTRY_END()
  5337. SOAP_ENTRY_START(addUserToRemoteAdminList, *result, unsigned int ulUserId, entryId sUserId, unsigned int ulCompanyId, entryId sCompanyId, unsigned int *result)
  5338. {
  5339. if (!g_lpSessionManager->IsHostedSupported())
  5340. return KCERR_NO_SUPPORT;
  5341. er = GetLocalId(sCompanyId, ulCompanyId, &ulCompanyId, NULL);
  5342. if (er != erSuccess)
  5343. return er;
  5344. // Check permission
  5345. er = lpecSession->GetSecurity()->IsAdminOverUserObject(ulCompanyId);
  5346. if(er != erSuccess)
  5347. return er;
  5348. er = GetLocalId(sUserId, ulUserId, &ulUserId, NULL);
  5349. if (er != erSuccess)
  5350. return er;
  5351. return lpecSession->GetUserManagement()->AddSubObjectToObjectAndSync(OBJECTRELATION_COMPANY_ADMIN, ulCompanyId, ulUserId);
  5352. }
  5353. SOAP_ENTRY_END()
  5354. SOAP_ENTRY_START(delUserFromRemoteAdminList, *result, unsigned int ulUserId, entryId sUserId, unsigned int ulCompanyId, entryId sCompanyId, unsigned int *result)
  5355. {
  5356. if (!g_lpSessionManager->IsHostedSupported())
  5357. return KCERR_NO_SUPPORT;
  5358. er = GetLocalId(sCompanyId, ulCompanyId, &ulCompanyId, NULL);
  5359. if (er != erSuccess)
  5360. return er;
  5361. // Check permission
  5362. er = lpecSession->GetSecurity()->IsAdminOverUserObject(ulCompanyId);
  5363. if(er != erSuccess)
  5364. return er;
  5365. er = GetLocalId(sUserId, ulUserId, &ulUserId, NULL);
  5366. if (er != erSuccess)
  5367. return er;
  5368. return lpecSession->GetUserManagement()->DeleteSubObjectFromObjectAndSync(OBJECTRELATION_COMPANY_ADMIN, ulCompanyId, ulUserId);
  5369. }
  5370. SOAP_ENTRY_END()
  5371. SOAP_ENTRY_START(getRemoteAdminList, lpsUserList->er, unsigned int ulCompanyId, entryId sCompanyId, struct userListResponse *lpsUserList)
  5372. {
  5373. std::unique_ptr<std::list<localobjectdetails_t> > lpUsers;
  5374. entryId sUserEid = {0};
  5375. if (!g_lpSessionManager->IsHostedSupported())
  5376. return KCERR_NO_SUPPORT;
  5377. er = GetLocalId(sCompanyId, ulCompanyId, &ulCompanyId, NULL);
  5378. if (er != erSuccess)
  5379. return er;
  5380. /* Input check, if ulCompanyId is 0, we want the user's company,
  5381. * otherwise we must check if the requested company is visible for the user. */
  5382. if (ulCompanyId == 0)
  5383. er = lpecSession->GetSecurity()->GetUserCompany(&ulCompanyId);
  5384. else
  5385. er = lpecSession->GetSecurity()->IsUserObjectVisible(ulCompanyId);
  5386. if (er != erSuccess)
  5387. return er;
  5388. // only users can be admins, nonactive users make no sense.
  5389. er = lpecSession->GetUserManagement()->GetSubObjectsOfObjectAndSync(OBJECTRELATION_COMPANY_ADMIN, ulCompanyId, &unique_tie(lpUsers));
  5390. if(er != erSuccess)
  5391. return er;
  5392. lpsUserList->sUserArray.__size = 0;
  5393. lpsUserList->sUserArray.__ptr = s_alloc<user>(soap, lpUsers->size());
  5394. for (const auto &user : *lpUsers) {
  5395. if (lpecSession->GetSecurity()->IsUserObjectVisible(user.ulId) != erSuccess)
  5396. continue;
  5397. er = GetABEntryID(user.ulId, soap, &sUserEid);
  5398. if (er != erSuccess)
  5399. return er;
  5400. er = CopyUserDetailsToSoap(user.ulId, &sUserEid, user,
  5401. lpecSession->GetCapabilities() & KOPANO_CAP_EXTENDED_ANON,
  5402. soap, &lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size]);
  5403. if (er != erSuccess)
  5404. return er;
  5405. // 6.40.0 stores the object class in the IsNonActive field
  5406. if (lpecSession->ClientVersion() == ZARAFA_VERSION_6_40_0)
  5407. lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size].ulIsNonActive = lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size].ulObjClass;
  5408. if (!bSupportUnicode) {
  5409. er = FixUserEncoding(soap, stringCompat, Out, lpsUserList->sUserArray.__ptr + lpsUserList->sUserArray.__size);
  5410. if (er != erSuccess)
  5411. return er;
  5412. }
  5413. ++lpsUserList->sUserArray.__size;
  5414. if (sUserEid.__ptr)
  5415. {
  5416. // sUserEid is placed in userdetails, no need to free
  5417. sUserEid.__ptr = NULL;
  5418. sUserEid.__size = 0;
  5419. }
  5420. }
  5421. return erSuccess;
  5422. }
  5423. SOAP_ENTRY_END()
  5424. SOAP_ENTRY_START(submitMessage, *result, entryId sEntryId, unsigned int ulFlags, unsigned int *result)
  5425. {
  5426. unsigned int ulParentId = 0;
  5427. unsigned int ulObjId = 0;
  5428. unsigned int ulMsgFlags = 0;
  5429. unsigned int ulStoreId = 0;
  5430. unsigned int ulStoreOwner = 0;
  5431. SOURCEKEY sSourceKey;
  5432. SOURCEKEY sParentSourceKey;
  5433. bool bMessageChanged = false;
  5434. eQuotaStatus QuotaStatus;
  5435. long long llStoreSize = 0;
  5436. objectdetails_t details;
  5437. USE_DATABASE();
  5438. er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulObjId);
  5439. if(er != erSuccess)
  5440. goto exit;
  5441. er = lpecSession->GetSessionManager()->GetCacheManager()->GetStore(ulObjId, &ulStoreId, NULL);
  5442. if(er != erSuccess)
  5443. goto exit;
  5444. er = lpecSession->GetSessionManager()->GetCacheManager()->GetObject(ulObjId, &ulParentId, NULL, &ulMsgFlags, NULL);
  5445. if(er != erSuccess)
  5446. goto exit;
  5447. er = lpecSession->GetSessionManager()->GetCacheManager()->GetObject(ulStoreId, NULL, &ulStoreOwner, NULL, NULL);
  5448. if(er != erSuccess)
  5449. goto exit;
  5450. er = lpecSession->GetUserManagement()->GetObjectDetails(ulStoreOwner, &details);
  5451. if(er != erSuccess)
  5452. goto exit;
  5453. // Cannot submit a message in a public store
  5454. if(OBJECTCLASS_TYPE(details.GetClass()) != OBJECTTYPE_MAILUSER) {
  5455. er = KCERR_NO_ACCESS;
  5456. goto exit;
  5457. }
  5458. // Check permission
  5459. er = lpecSession->GetSecurity()->CheckPermission(ulStoreId, ecSecurityOwner);
  5460. if(er != erSuccess)
  5461. goto exit;
  5462. // Quota check
  5463. er = lpecSession->GetSecurity()->GetStoreSize(ulStoreId, &llStoreSize);
  5464. if(er != erSuccess)
  5465. goto exit;
  5466. er = lpecSession->GetSecurity()->CheckQuota(ulStoreId, llStoreSize, &QuotaStatus);
  5467. if(er != erSuccess)
  5468. goto exit;
  5469. if(QuotaStatus == QUOTA_SOFTLIMIT || QuotaStatus == QUOTA_HARDLIMIT) {
  5470. er = KCERR_STORE_FULL;
  5471. goto exit;
  5472. }
  5473. er = lpDatabase->Begin();
  5474. if(er != erSuccess)
  5475. goto exit;
  5476. // Set PR_MESSAGE_FLAGS to MSGFLAG_SUBMIT|MSGFLAG_UNSENT
  5477. if(!(ulFlags & EC_SUBMIT_MASTER)) {
  5478. // Set the submit flag (because it has just been submitted), and set it to UNSENT, as it has definitely
  5479. // not been sent if the user has just submitted it.
  5480. 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));
  5481. er = lpDatabase->DoUpdate(strQuery);
  5482. if(er != erSuccess)
  5483. goto exit;
  5484. // Add change to ICS
  5485. GetSourceKey(ulObjId, &sSourceKey);
  5486. GetSourceKey(ulParentId, &sParentSourceKey);
  5487. AddChange(lpecSession, 0, sSourceKey, sParentSourceKey, ICS_MESSAGE_CHANGE);
  5488. // Mask for notification
  5489. bMessageChanged = true;
  5490. }
  5491. er = UpdateTProp(lpDatabase, PR_MESSAGE_FLAGS, ulParentId, ulObjId);
  5492. if(er != erSuccess)
  5493. goto exit;
  5494. // Insert the message into the outgoing queue
  5495. strQuery = "INSERT IGNORE INTO outgoingqueue (store_id, hierarchy_id, flags) VALUES("+stringify(ulStoreId)+", "+stringify(ulObjId)+","+stringify(ulFlags)+")";
  5496. er = lpDatabase->DoInsert(strQuery);
  5497. if(er != erSuccess)
  5498. goto exit;
  5499. er = lpDatabase->Commit();
  5500. if(er != erSuccess)
  5501. goto exit;
  5502. if (bMessageChanged) {
  5503. // Update cache
  5504. g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, ulObjId);
  5505. g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, ulParentId);
  5506. // Notify
  5507. g_lpSessionManager->NotificationModified(MAPI_MESSAGE, ulObjId, ulParentId);
  5508. g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY, 0, ulParentId, ulObjId, MAPI_MESSAGE);
  5509. }
  5510. g_lpSessionManager->UpdateOutgoingTables(ECKeyTable::TABLE_ROW_ADD, ulStoreId, ulObjId, ulFlags, MAPI_MESSAGE);
  5511. exit:
  5512. ROLLBACK_ON_ERROR();
  5513. }
  5514. SOAP_ENTRY_END()
  5515. SOAP_ENTRY_START(finishedMessage, *result, entryId sEntryId, unsigned int ulFlags, unsigned int *result)
  5516. {
  5517. unsigned int ulParentId = 0;
  5518. unsigned int ulGrandParentId = 0;
  5519. unsigned int ulAffectedRows = 0;
  5520. unsigned int ulStoreId = (unsigned int)-1; // not 0 security issue
  5521. unsigned int ulObjId = 0;
  5522. unsigned int ulPrevFlags = 0;
  5523. bool bMessageChanged = false;
  5524. SOURCEKEY sSourceKey;
  5525. SOURCEKEY sParentSourceKey;
  5526. USE_DATABASE();
  5527. er = lpDatabase->Begin();
  5528. if(er != erSuccess)
  5529. goto exit;
  5530. er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulObjId);
  5531. if(er != erSuccess)
  5532. goto exit;
  5533. er = g_lpSessionManager->GetCacheManager()->GetParent(ulObjId, &ulParentId);
  5534. if(er != erSuccess)
  5535. goto exit;
  5536. //Get storeid
  5537. er = lpecSession->GetSessionManager()->GetCacheManager()->GetStore(ulObjId, &ulStoreId, NULL);
  5538. switch (er) {
  5539. case erSuccess:
  5540. break;
  5541. case KCERR_NOT_FOUND:
  5542. // ulObjId should be in outgoingtable, but the ulStoreId cannot be retrieved
  5543. // because ulObjId does not exist in the hierarchy table, so we remove the message
  5544. // fix table and notify and pass error to caller
  5545. ec_log_warn("Unable to find store for hierarchy id %d", ulObjId);
  5546. ulStoreId = 0;
  5547. goto table;
  5548. default:
  5549. goto exit; // database error
  5550. }
  5551. // Check permission
  5552. er = lpecSession->GetSecurity()->CheckPermission(ulStoreId, ecSecurityOwner);
  5553. if(er != erSuccess)
  5554. goto exit;
  5555. 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";
  5556. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  5557. if(er != erSuccess)
  5558. goto exit;
  5559. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  5560. if(lpDBRow == NULL || lpDBRow[0] == NULL) {
  5561. er = KCERR_DATABASE_ERROR;
  5562. ec_log_err("finishedMessages(): row/col null");
  5563. goto exit;
  5564. }
  5565. ulPrevFlags = atoui(lpDBRow[0]);
  5566. strQuery = "UPDATE properties ";
  5567. if (!(ulFlags & EC_SUBMIT_MASTER))
  5568. // Removing from local queue; remove submit flag and unsent flag
  5569. strQuery += " SET val_ulong=val_ulong&~"+stringify(MSGFLAG_SUBMIT|MSGFLAG_UNSENT);
  5570. else if (ulFlags & EC_SUBMIT_DOSENTMAIL)
  5571. // Removing from master queue
  5572. // Spooler sent message and moved, remove submit flag and unsent flag
  5573. strQuery += " SET val_ulong=val_ulong&~" +stringify(MSGFLAG_SUBMIT|MSGFLAG_UNSENT);
  5574. else
  5575. // Spooler only sent message
  5576. strQuery += " SET val_ulong=val_ulong&~" +stringify(MSGFLAG_UNSENT);
  5577. // Always set message read
  5578. 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));
  5579. er = lpDatabase->DoUpdate(strQuery);
  5580. if(er != erSuccess)
  5581. goto exit;
  5582. er = UpdateTProp(lpDatabase, PR_MESSAGE_FLAGS, ulParentId, ulObjId);
  5583. if(er != erSuccess)
  5584. goto exit;
  5585. if(!(ulPrevFlags & MSGFLAG_READ)) {
  5586. // The item has been set read, decrease the unread counter for the folder
  5587. er = UpdateFolderCount(lpDatabase, ulParentId, PR_CONTENT_UNREAD, -1);
  5588. if(er != erSuccess)
  5589. goto exit;
  5590. }
  5591. GetSourceKey(ulObjId, &sSourceKey);
  5592. GetSourceKey(ulParentId, &sParentSourceKey);
  5593. AddChange(lpecSession, 0, sSourceKey, sParentSourceKey, ICS_MESSAGE_CHANGE);
  5594. // NOTE: Unlock message is done in client
  5595. // Mark for notification
  5596. bMessageChanged = true;
  5597. table:
  5598. // delete the message from the outgoing queue
  5599. strQuery = "DELETE FROM outgoingqueue WHERE hierarchy_id="+stringify(ulObjId) + " AND flags & 1=" + stringify(ulFlags & 1);
  5600. er = lpDatabase->DoDelete(strQuery, &ulAffectedRows);
  5601. if(er != erSuccess)
  5602. goto exit;
  5603. er = lpDatabase->Commit();
  5604. if(er != erSuccess)
  5605. goto exit;
  5606. // Remove messge from the outgoing queue
  5607. g_lpSessionManager->UpdateOutgoingTables(ECKeyTable::TABLE_ROW_DELETE, ulStoreId, ulObjId, ulFlags, MAPI_MESSAGE);
  5608. // The flags have changed, so we have to send a modified
  5609. if (bMessageChanged) {
  5610. lpecSession->GetSessionManager()->GetCacheManager()->Update(fnevObjectModified, ulObjId);
  5611. g_lpSessionManager->NotificationModified(MAPI_MESSAGE, ulObjId, ulParentId);
  5612. if(g_lpSessionManager->GetCacheManager()->GetParent(ulObjId, &ulParentId) == erSuccess) {
  5613. g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, ulParentId);
  5614. g_lpSessionManager->NotificationModified(MAPI_FOLDER, ulParentId);
  5615. g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY, 0, ulParentId, ulObjId, MAPI_MESSAGE);
  5616. if (g_lpSessionManager->GetCacheManager()->GetParent(ulParentId, &ulGrandParentId) == erSuccess)
  5617. g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY, 0, ulGrandParentId, ulParentId, MAPI_FOLDER);
  5618. }
  5619. }
  5620. exit:
  5621. ROLLBACK_ON_ERROR();
  5622. }
  5623. SOAP_ENTRY_END()
  5624. SOAP_ENTRY_START(abortSubmit, *result, entryId sEntryId, unsigned int *result)
  5625. {
  5626. unsigned int ulParentId = 0;
  5627. unsigned int ulStoreId = 0;
  5628. unsigned int ulSubmitFlags = 0;
  5629. unsigned int ulGrandParentId = 0;
  5630. unsigned int ulObjId = 0;
  5631. SOURCEKEY sSourceKey;
  5632. SOURCEKEY sParentSourceKey;
  5633. USE_DATABASE();
  5634. er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulObjId);
  5635. if(er != erSuccess)
  5636. goto exit;
  5637. er = g_lpSessionManager->GetCacheManager()->GetParent(ulObjId, &ulParentId);
  5638. if(er != erSuccess)
  5639. goto exit;
  5640. // Check permission
  5641. er = lpecSession->GetSecurity()->CheckPermission(ulObjId, ecSecurityOwner);
  5642. if(er != erSuccess)
  5643. goto exit;
  5644. //Get storeid
  5645. er = lpecSession->GetSessionManager()->GetCacheManager()->GetStore(ulObjId, &ulStoreId, NULL);
  5646. if(er != erSuccess)
  5647. goto exit;
  5648. er = lpDatabase->Begin();
  5649. if(er != erSuccess)
  5650. goto exit;
  5651. // Get storeid and check if the message into the queue
  5652. strQuery = "SELECT store_id, flags FROM outgoingqueue WHERE hierarchy_id="+stringify(ulObjId) + " LIMIT 2";
  5653. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  5654. if(er != erSuccess)
  5655. goto exit;
  5656. // FIXME: can be also more than 2??
  5657. if(lpDatabase->GetNumRows(lpDBResult) != 1){
  5658. er = KCERR_NOT_IN_QUEUE;
  5659. goto exit;
  5660. }
  5661. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  5662. if(lpDBRow == NULL || lpDBRow[0] == NULL || lpDBRow[1] == NULL) {
  5663. er = KCERR_DATABASE_ERROR;
  5664. ec_log_err("abortSubmit(): row/col null");
  5665. goto exit;
  5666. }
  5667. ulStoreId = atoui(lpDBRow[0]);
  5668. ulSubmitFlags = atoi(lpDBRow[1]);
  5669. // delete the message from the outgoing queue
  5670. strQuery = "DELETE FROM outgoingqueue WHERE hierarchy_id="+stringify(ulObjId);
  5671. er = lpDatabase->DoDelete(strQuery);
  5672. if(er != erSuccess)
  5673. goto exit;
  5674. // remove in property PR_MESSAGE_FLAGS the MSGFLAG_SUBMIT flag
  5675. 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));
  5676. er = lpDatabase->DoUpdate(strQuery);
  5677. if(er != erSuccess)
  5678. goto exit;
  5679. er = UpdateTProp(lpDatabase, PR_MESSAGE_FLAGS, ulParentId, ulObjId);
  5680. if(er != erSuccess)
  5681. goto exit;
  5682. // Update ICS system
  5683. GetSourceKey(ulObjId, &sSourceKey);
  5684. GetSourceKey(ulParentId, &sParentSourceKey);
  5685. AddChange(lpecSession, 0, sSourceKey, sParentSourceKey, ICS_MESSAGE_CHANGE);
  5686. er = lpDatabase->Commit();
  5687. if(er != erSuccess)
  5688. goto exit;
  5689. g_lpSessionManager->UpdateOutgoingTables(ECKeyTable::TABLE_ROW_DELETE, ulStoreId, ulObjId, ulSubmitFlags, MAPI_MESSAGE);
  5690. if(g_lpSessionManager->GetCacheManager()->GetParent(ulObjId, &ulParentId) == erSuccess) {
  5691. g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, ulObjId);
  5692. g_lpSessionManager->NotificationModified(MAPI_MESSAGE, ulObjId, ulParentId);
  5693. g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, ulParentId);
  5694. g_lpSessionManager->NotificationModified(MAPI_FOLDER, ulParentId);
  5695. g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY, 0, ulParentId, ulObjId, MAPI_MESSAGE);
  5696. if (g_lpSessionManager->GetCacheManager()->GetParent(ulParentId, &ulGrandParentId) == erSuccess)
  5697. g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY, 0, ulGrandParentId, ulParentId, MAPI_FOLDER);
  5698. }
  5699. exit:
  5700. ROLLBACK_ON_ERROR();
  5701. }
  5702. SOAP_ENTRY_END()
  5703. SOAP_ENTRY_START(isMessageInQueue, *result, entryId sEntryId, unsigned int *result)
  5704. {
  5705. unsigned int ulObjId = 0;
  5706. USE_DATABASE();
  5707. er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulObjId);
  5708. if(er != erSuccess)
  5709. return er;
  5710. // Checks if message is unsent
  5711. strQuery = "SELECT hierarchy_id FROM outgoingqueue WHERE hierarchy_id=" + stringify(ulObjId) + " AND flags & " + stringify(EC_SUBMIT_MASTER) + " LIMIT 1";
  5712. if(lpDatabase->DoSelect(strQuery, &lpDBResult) != erSuccess) {
  5713. ec_log_err("isMessageInQueue(): select failed");
  5714. return KCERR_DATABASE_ERROR;
  5715. }
  5716. if (lpDatabase->GetNumRows(lpDBResult) == 0)
  5717. return KCERR_NOT_FOUND;
  5718. return erSuccess;
  5719. }
  5720. SOAP_ENTRY_END()
  5721. SOAP_ENTRY_START(resolveStore, lpsResponse->er, struct xsd__base64Binary sStoreGuid, struct resolveUserStoreResponse *lpsResponse)
  5722. {
  5723. USE_DATABASE();
  5724. string strStoreGuid;
  5725. if (sStoreGuid.__ptr == nullptr || sStoreGuid.__size == 0)
  5726. return KCERR_INVALID_PARAMETER;
  5727. strStoreGuid = lpDatabase->EscapeBinary(sStoreGuid.__ptr, sStoreGuid.__size);
  5728. // @todo: Check if this is supposed to work with public stores.
  5729. strQuery =
  5730. "SELECT u.id, s.hierarchy_id, s.guid, s.company "
  5731. "FROM stores AS s "
  5732. "LEFT JOIN users AS u "
  5733. "ON s.user_id = u.id "
  5734. "WHERE s.guid=" + strStoreGuid + " LIMIT 2";
  5735. if(lpDatabase->DoSelect(strQuery, &lpDBResult) != erSuccess) {
  5736. ec_log_err("resolveStore(): select failed %x", er);
  5737. return KCERR_DATABASE_ERROR;
  5738. }
  5739. if (lpDatabase->GetNumRows(lpDBResult) != 1)
  5740. return KCERR_NOT_FOUND;
  5741. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  5742. lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
  5743. if (lpDBRow == nullptr || lpDBRow[1] == nullptr ||
  5744. lpDBRow[2] == nullptr || lpDBRow[3] == nullptr ||
  5745. lpDBLen == nullptr)
  5746. return KCERR_NOT_FOUND;
  5747. if (lpDBRow[0] == NULL) {
  5748. // check if we're admin over the store object
  5749. er = lpecSession->GetSecurity()->IsAdminOverUserObject(atoi(lpDBRow[3]));
  5750. if (er != erSuccess)
  5751. return er;
  5752. lpsResponse->ulUserId = 0;
  5753. lpsResponse->sUserId.__size = 0;
  5754. lpsResponse->sUserId.__ptr = NULL;
  5755. } else {
  5756. lpsResponse->ulUserId = atoi(lpDBRow[0]);
  5757. er = GetABEntryID(lpsResponse->ulUserId, soap, &lpsResponse->sUserId);
  5758. if (er != erSuccess)
  5759. return er;
  5760. }
  5761. er = g_lpSessionManager->GetCacheManager()->GetEntryIdFromObject(atoui(lpDBRow[1]), soap, OPENSTORE_OVERRIDE_HOME_MDB, &lpsResponse->sStoreId);
  5762. if(er != erSuccess)
  5763. return er;
  5764. lpsResponse->guid.__size = lpDBLen[2];
  5765. lpsResponse->guid.__ptr = s_alloc<unsigned char>(soap, lpDBLen[2]);
  5766. memcpy(lpsResponse->guid.__ptr, lpDBRow[2], lpDBLen[2]);
  5767. return erSuccess;
  5768. }
  5769. SOAP_ENTRY_END()
  5770. SOAP_ENTRY_START(resolveUserStore, lpsResponse->er, char *szUserName, unsigned int ulStoreTypeMask, unsigned int ulFlags, struct resolveUserStoreResponse *lpsResponse)
  5771. {
  5772. unsigned int ulObjectId = 0;
  5773. objectdetails_t sUserDetails;
  5774. string strServerName;
  5775. USE_DATABASE();
  5776. if (szUserName == nullptr)
  5777. return KCERR_INVALID_PARAMETER;
  5778. szUserName = STRIN_FIX(szUserName);
  5779. if (ulStoreTypeMask == 0)
  5780. ulStoreTypeMask = ECSTORE_TYPE_MASK_PRIVATE | ECSTORE_TYPE_MASK_PUBLIC;
  5781. er = lpecSession->GetUserManagement()->ResolveObjectAndSync(OBJECTCLASS_USER, szUserName, &ulObjectId);
  5782. if ((er == KCERR_NOT_FOUND || er == KCERR_INVALID_PARAMETER) && lpecSession->GetSessionManager()->IsHostedSupported())
  5783. // FIXME: this function is being misused, szUserName can also be a company name
  5784. er = lpecSession->GetUserManagement()->ResolveObjectAndSync(CONTAINER_COMPANY, szUserName, &ulObjectId);
  5785. if (er != erSuccess)
  5786. return er;
  5787. /* If we are allowed to view the user, we are allowed to know the store exists */
  5788. er = lpecSession->GetSecurity()->IsUserObjectVisible(ulObjectId);
  5789. if (er != erSuccess)
  5790. return er;
  5791. er = lpecSession->GetUserManagement()->GetObjectDetails(ulObjectId, &sUserDetails);
  5792. if (er != erSuccess)
  5793. return er;
  5794. /* Only users and companies have a store */
  5795. if ((OBJECTCLASS_TYPE(sUserDetails.GetClass()) == OBJECTTYPE_MAILUSER && sUserDetails.GetClass() == NONACTIVE_CONTACT) ||
  5796. (OBJECTCLASS_TYPE(sUserDetails.GetClass()) != OBJECTTYPE_MAILUSER && sUserDetails.GetClass() != CONTAINER_COMPANY))
  5797. return KCERR_NOT_FOUND;
  5798. if (lpecSession->GetSessionManager()->IsDistributedSupported() &&
  5799. !lpecSession->GetUserManagement()->IsInternalObject(ulObjectId))
  5800. {
  5801. if (ulStoreTypeMask & (ECSTORE_TYPE_MASK_PRIVATE | ECSTORE_TYPE_MASK_PUBLIC)) {
  5802. /* Check if this is the correct server for its store */
  5803. string strServerName = sUserDetails.GetPropString(OB_PROP_S_SERVERNAME);
  5804. if (strServerName.empty())
  5805. return KCERR_NOT_FOUND;
  5806. if (strcasecmp(strServerName.c_str(), g_lpSessionManager->GetConfig()->GetSetting("server_name")) != 0) {
  5807. if ((ulFlags & OPENSTORE_OVERRIDE_HOME_MDB) == 0) {
  5808. string strServerPath;
  5809. er = GetBestServerPath(soap, lpecSession, strServerName, &strServerPath);
  5810. if (er != erSuccess)
  5811. return er;
  5812. lpsResponse->lpszServerPath = STROUT_FIX_CPY(strServerPath.c_str());
  5813. ec_log_info("Redirecting request to \"%s\'", lpsResponse->lpszServerPath);
  5814. g_lpStatsCollector->Increment(SCN_REDIRECT_COUNT, 1);
  5815. return KCERR_UNABLE_TO_COMPLETE;
  5816. }
  5817. }
  5818. }
  5819. else if (ulStoreTypeMask & ECSTORE_TYPE_MASK_ARCHIVE) {
  5820. // We allow an archive store to be resolved by sysadmins even if it's not supposed
  5821. // to exist on this server for a particular user.
  5822. if (lpecSession->GetSecurity()->GetAdminLevel() < ADMIN_LEVEL_SYSADMIN &&
  5823. !sUserDetails.PropListStringContains((property_key_t)PR_EC_ARCHIVE_SERVERS_A, g_lpSessionManager->GetConfig()->GetSetting("server_name"), true))
  5824. // No redirect with archive stores because there can be multiple archive stores.
  5825. return KCERR_NOT_FOUND;
  5826. }
  5827. else {
  5828. assert(false);
  5829. return KCERR_NOT_FOUND;
  5830. }
  5831. }
  5832. strQuery = "SELECT hierarchy_id, guid FROM stores WHERE user_id = " + stringify(ulObjectId) + " AND (1 << type) & " + stringify(ulStoreTypeMask) + " LIMIT 1";
  5833. if ((er = lpDatabase->DoSelect(strQuery, &lpDBResult)) != erSuccess) {
  5834. ec_log_err("resolveUserStore(): select failed %x", er);
  5835. return KCERR_DATABASE_ERROR;
  5836. }
  5837. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  5838. lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
  5839. if (lpDBRow == nullptr)
  5840. return KCERR_NOT_FOUND;
  5841. if (lpDBRow[0] == NULL || lpDBRow[1] == NULL || lpDBLen == NULL || lpDBLen[1] == 0) {
  5842. ec_log_err("resolveUserStore(): row/col null");
  5843. return KCERR_DATABASE_ERROR;
  5844. }
  5845. /* We found the store, so we don't need to check if this is the correct server. */
  5846. strServerName = g_lpSessionManager->GetConfig()->GetSetting("server_name", "", "Unknown");
  5847. // Always return the pseudo URL.
  5848. lpsResponse->lpszServerPath = STROUT_FIX_CPY(string("pseudo://" + strServerName).c_str());
  5849. er = g_lpSessionManager->GetCacheManager()->GetEntryIdFromObject(atoui(lpDBRow[0]), soap, ulFlags & OPENSTORE_OVERRIDE_HOME_MDB, &lpsResponse->sStoreId);
  5850. if(er != erSuccess)
  5851. return er;
  5852. er = GetABEntryID(ulObjectId, soap, &lpsResponse->sUserId);
  5853. if (er != erSuccess)
  5854. return er;
  5855. lpsResponse->ulUserId = ulObjectId;
  5856. lpsResponse->guid.__size = lpDBLen[1];
  5857. lpsResponse->guid.__ptr = s_alloc<unsigned char>(soap, lpDBLen[1]);
  5858. memcpy(lpsResponse->guid.__ptr, lpDBRow[1], lpDBLen[1]);
  5859. return erSuccess;
  5860. }
  5861. SOAP_ENTRY_END()
  5862. struct COPYITEM {
  5863. unsigned int ulId;
  5864. unsigned int ulType;
  5865. unsigned int ulParent;
  5866. unsigned int ulNewId;
  5867. unsigned int ulFlags;
  5868. unsigned int ulMessageFlags;
  5869. unsigned int ulOwner;
  5870. SOURCEKEY sSourceKey;
  5871. SOURCEKEY sParentSourceKey;
  5872. SOURCEKEY sNewSourceKey;
  5873. EntryId sOldEntryId;
  5874. EntryId sNewEntryId;
  5875. bool bMoved;
  5876. };
  5877. // Move one or more messages and/or moved a softdeleted message to a normal message
  5878. static ECRESULT MoveObjects(ECSession *lpSession, ECDatabase *lpDatabase,
  5879. ECListInt* lplObjectIds, unsigned int ulDestFolderId,
  5880. unsigned int ulSyncId)
  5881. {
  5882. ECRESULT er = erSuccess;
  5883. bool bPartialCompletion = false;
  5884. unsigned int ulGrandParent = 0;
  5885. COPYITEM sItem;
  5886. unsigned int ulItemSize = 0;
  5887. unsigned int ulSourceStoreId = 0;
  5888. unsigned int ulDestStoreId = 0;
  5889. long long llStoreSize;
  5890. eQuotaStatus QuotaStatus;
  5891. bool bUpdateDeletedSize = false;
  5892. ECListIntIterator iObjectId;
  5893. size_t cCopyItems = 0;
  5894. FILETIME ft;
  5895. unsigned long long ullIMAP = 0;
  5896. std::list<unsigned int> lstParent;
  5897. std::list<unsigned int> lstGrandParent;
  5898. std::list<COPYITEM> lstCopyItems;
  5899. SOURCEKEY sDestFolderSourceKey;
  5900. std::map<unsigned int, PARENTINFO> mapFolderCounts;
  5901. entryId* lpsNewEntryId = NULL;
  5902. entryId* lpsOldEntryId = NULL;
  5903. GUID guidStore;
  5904. ALLOC_DBRESULT();
  5905. if(lplObjectIds == NULL) {
  5906. ec_log_err("MoveObjects: no list of objects given");
  5907. er = KCERR_INVALID_PARAMETER;
  5908. goto exit;
  5909. }
  5910. if(lplObjectIds->empty())
  5911. goto exit; // Nothing to do
  5912. GetSystemTimeAsFileTime(&ft);
  5913. // Check permission, Destination folder
  5914. er = lpSession->GetSecurity()->CheckPermission(ulDestFolderId, ecSecurityCreate);
  5915. if (er != erSuccess) {
  5916. ec_log_err("MoveObjects: failed checking permissions on %u: %s (%x)", ulDestFolderId, GetMAPIErrorMessage(er), er);
  5917. goto exit;
  5918. }
  5919. er = lpSession->GetSessionManager()->GetCacheManager()->GetStore(ulDestFolderId, &ulDestStoreId, &guidStore);
  5920. if (er != erSuccess) {
  5921. ec_log_err("MoveObjects: failed retrieving store of %u: %s (%x)", ulDestFolderId, GetMAPIErrorMessage(er), er);
  5922. goto exit;
  5923. }
  5924. GetSourceKey(ulDestFolderId, &sDestFolderSourceKey);
  5925. // Get all items for the object list
  5926. 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)) +
  5927. " 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(";
  5928. for (iObjectId = lplObjectIds->begin();
  5929. iObjectId != lplObjectIds->end(); ++iObjectId) {
  5930. if(iObjectId != lplObjectIds->begin())
  5931. strQuery += ",";
  5932. strQuery += stringify(*iObjectId);
  5933. }
  5934. strQuery += ")";
  5935. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  5936. if (er != erSuccess) {
  5937. ec_log_err("MoveObjects: failed retrieving list objects from database: %s (%x)", GetMAPIErrorMessage(er), er);
  5938. goto exit;
  5939. }
  5940. // First, put all the root objects in the list
  5941. while( (lpDBRow = lpDatabase->FetchRow(lpDBResult)) != NULL)
  5942. {
  5943. if(lpDBRow[0] == NULL || lpDBRow[1] == NULL || lpDBRow[2] == NULL || lpDBRow[3] == NULL || lpDBRow[4] == NULL) // no id, type or parent folder?
  5944. continue;
  5945. sItem.bMoved = false;
  5946. sItem.ulId = atoi(lpDBRow[0]);
  5947. sItem.ulParent = atoi(lpDBRow[1]);
  5948. sItem.ulType = atoi(lpDBRow[2]);
  5949. sItem.ulFlags = atoi(lpDBRow[3]);
  5950. sItem.ulOwner = atoi(lpDBRow[4]);
  5951. sItem.ulMessageFlags = lpDBRow[6] ? atoi(lpDBRow[6]) : 0;
  5952. if (sItem.ulType != MAPI_MESSAGE) {
  5953. bPartialCompletion = true;
  5954. continue;
  5955. }
  5956. GetSourceKey(sItem.ulId, &sItem.sSourceKey);
  5957. GetSourceKey(sItem.ulParent, &sItem.sParentSourceKey);
  5958. // Check permission, source messages
  5959. er = lpSession->GetSecurity()->CheckPermission(sItem.ulId, ecSecurityDelete);
  5960. if(er == erSuccess) {
  5961. // Check if the source and dest the same store
  5962. er = lpSession->GetSessionManager()->GetCacheManager()->GetStore(sItem.ulId, &ulSourceStoreId, NULL);
  5963. if(er != erSuccess || ulSourceStoreId != ulDestStoreId) {
  5964. bPartialCompletion = true;
  5965. er = erSuccess;
  5966. continue;
  5967. }
  5968. lstCopyItems.push_back(sItem);
  5969. ulItemSize += (lpDBRow[5] != NULL)? atoi(lpDBRow[5]) : 0;
  5970. // check if it a deleted item
  5971. if (lpDBRow[3] != NULL && atoi(lpDBRow[3]) & MSGFLAG_DELETED)
  5972. bUpdateDeletedSize = true;
  5973. }else {
  5974. bPartialCompletion = true;
  5975. er = erSuccess;
  5976. }
  5977. }
  5978. // Check the quota size when the item is a softdelete item
  5979. if(bUpdateDeletedSize == true)
  5980. {
  5981. // Quota check
  5982. er = lpSession->GetSecurity()->GetStoreSize(ulDestFolderId, &llStoreSize);
  5983. if (er != erSuccess) {
  5984. ec_log_err("MoveObjects: GetStoreSize(%u) failed: %s (%x)", ulDestFolderId, GetMAPIErrorMessage(er), er);
  5985. goto exit;
  5986. }
  5987. // substract itemsize and check
  5988. llStoreSize -= (llStoreSize >= (long long)ulItemSize)?(long long)ulItemSize:0;
  5989. er = lpSession->GetSecurity()->CheckQuota(ulDestFolderId, llStoreSize, &QuotaStatus);
  5990. if (er != erSuccess) {
  5991. ec_log_err("MoveObjects: CheckQuota(%u) failed: %s (%x)", ulDestFolderId, GetMAPIErrorMessage(er), er);
  5992. goto exit;
  5993. }
  5994. if(QuotaStatus == QUOTA_HARDLIMIT) {
  5995. er = KCERR_STORE_FULL;
  5996. goto exit;
  5997. }
  5998. }
  5999. cCopyItems = lstCopyItems.size();
  6000. // Move the messages to another folder
  6001. for (auto &cop : lstCopyItems) {
  6002. sObjectTableKey key(cop.ulId, 0);
  6003. struct propVal sPropIMAPId;
  6004. // Check whether it is a move to the same parent, and if so, skip them.
  6005. if (cop.ulParent == ulDestFolderId &&
  6006. (cop.ulFlags & MSGFLAG_DELETED) == 0)
  6007. continue;
  6008. er = g_lpSessionManager->GetCacheManager()->GetEntryIdFromObject(cop.ulId, NULL, 0, &lpsOldEntryId);
  6009. if(er != erSuccess) {
  6010. // FIXME isn't this an error?
  6011. ec_log_err("MoveObjects: problem retrieving entry id of object %u: %s (%x)",
  6012. cop.ulId, GetMAPIErrorMessage(er), er);
  6013. bPartialCompletion = true;
  6014. er = erSuccess;
  6015. // FIXME: Delete from list: cop
  6016. continue;
  6017. }
  6018. cop.sOldEntryId = EntryId(lpsOldEntryId);
  6019. FreeEntryId(lpsOldEntryId, true);
  6020. lpsOldEntryId = NULL;
  6021. er = CreateEntryId(guidStore, MAPI_MESSAGE, &lpsNewEntryId);
  6022. if (er != erSuccess) {
  6023. ec_log_err("MoveObjects: CreateEntryID for type MAPI_MESSAGE failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6024. goto exit;
  6025. }
  6026. cop.sNewEntryId = EntryId(lpsNewEntryId);
  6027. FreeEntryId(lpsNewEntryId, true);
  6028. lpsNewEntryId = NULL;
  6029. // Update entryid (changes on move)
  6030. strQuery = "REPLACE INTO indexedproperties(hierarchyid,tag,val_binary) VALUES (" +
  6031. stringify(cop.ulId) + ", 0x0FFF," +
  6032. lpDatabase->EscapeBinary(cop.sNewEntryId, cop.sNewEntryId.size()) + ")";
  6033. er = lpDatabase->DoUpdate(strQuery);
  6034. if (er != erSuccess) {
  6035. ec_log_err("MoveObjects: problem setting new entry id: %s (%x)", GetMAPIErrorMessage(er), er);
  6036. goto exit;
  6037. }
  6038. er = lpSession->GetNewSourceKey(&cop.sNewSourceKey);
  6039. if (er != erSuccess) {
  6040. ec_log_err("MoveObjects: GetNewSourceKey failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6041. goto exit;
  6042. }
  6043. // Update source key (changes on move)
  6044. strQuery = "REPLACE INTO indexedproperties(hierarchyid,tag,val_binary) VALUES (" +
  6045. stringify(cop.ulId) + "," +
  6046. stringify(PROP_ID(PR_SOURCE_KEY)) + "," +
  6047. lpDatabase->EscapeBinary(cop.sNewSourceKey, cop.sNewSourceKey.size()) + ")";
  6048. er = lpDatabase->DoUpdate(strQuery);
  6049. if (er != erSuccess) {
  6050. ec_log_err("MoveObjects: Update source key for %u failed: %s (%x)",
  6051. cop.ulId, GetMAPIErrorMessage(er), er);
  6052. goto exit;
  6053. }
  6054. // Update IMAP ID (changes on move)
  6055. er = g_lpSessionManager->GetNewSequence(ECSessionManager::SEQ_IMAP, &ullIMAP);
  6056. if (er != erSuccess) {
  6057. ec_log_err("MoveObjects: problem retrieving new IMAP ID: %s (%x)", GetMAPIErrorMessage(er), er);
  6058. goto exit;
  6059. }
  6060. strQuery = "INSERT INTO properties(hierarchyid, tag, type, val_ulong) VALUES(" +
  6061. stringify(cop.ulId) + "," +
  6062. stringify(PROP_ID(PR_EC_IMAP_ID)) + "," +
  6063. stringify(PROP_TYPE(PR_EC_IMAP_ID)) + "," +
  6064. stringify(ullIMAP) +
  6065. ") ON DUPLICATE KEY UPDATE val_ulong=" +
  6066. stringify(ullIMAP);
  6067. er = lpDatabase->DoInsert(strQuery);
  6068. if (er != erSuccess) {
  6069. ec_log_err("MoveObjects: problem updating new IMAP ID for %u to %llu: %s (%x)",
  6070. cop.ulId, ullIMAP, GetMAPIErrorMessage(er), er);
  6071. goto exit;
  6072. }
  6073. sPropIMAPId.ulPropTag = PR_EC_IMAP_ID;
  6074. sPropIMAPId.Value.ul = ullIMAP;
  6075. sPropIMAPId.__union = SOAP_UNION_propValData_ul;
  6076. er = g_lpSessionManager->GetCacheManager()->SetCell(&key, PR_EC_IMAP_ID, &sPropIMAPId);
  6077. if (er != erSuccess) {
  6078. ec_log_err("MoveObjects: problem cache sell for IMAP ID %llu: %s (%x)", ullIMAP, GetMAPIErrorMessage(er), er);
  6079. goto exit;
  6080. }
  6081. strQuery = "UPDATE hierarchy SET parent=" +
  6082. stringify(ulDestFolderId) + ", flags=flags&" +
  6083. stringify(~MSGFLAG_DELETED) + " WHERE id=" +
  6084. stringify(cop.ulId);
  6085. er = lpDatabase->DoUpdate(strQuery);
  6086. if (er != erSuccess) {
  6087. // FIXME isn't this an error?
  6088. ec_log_debug("MoveObjects: problem updating hierarchy id for %u in %u: %s (%x)",
  6089. cop.ulId, ulDestFolderId,
  6090. GetMAPIErrorMessage(er), er);
  6091. bPartialCompletion = true;
  6092. er = erSuccess;
  6093. // FIXME: Delete from list: cop
  6094. continue;
  6095. }
  6096. // update last modification time
  6097. // PR_LAST_MODIFICATION_TIME (ZCP-11897)
  6098. strQuery = "INSERT INTO properties(hierarchyid, tag, type, val_lo, val_hi) VALUES(" +
  6099. stringify(cop.ulId) + "," +
  6100. stringify(PROP_ID(PR_LAST_MODIFICATION_TIME)) + "," +
  6101. stringify(PROP_TYPE(PR_LAST_MODIFICATION_TIME)) + "," +
  6102. stringify(ft.dwLowDateTime) + "," +
  6103. stringify(ft.dwHighDateTime) +
  6104. ") ON DUPLICATE KEY UPDATE val_lo=" +
  6105. stringify(ft.dwLowDateTime) + ", val_hi=" +
  6106. stringify(ft.dwHighDateTime);
  6107. er = lpDatabase->DoUpdate(strQuery);
  6108. if (er != erSuccess)
  6109. goto exit;
  6110. g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, cop.ulId);
  6111. // remove PR_DELETED_ON, This is on a softdeleted message
  6112. strQuery = "DELETE FROM properties WHERE hierarchyid=" +
  6113. stringify(cop.ulId) + " AND tag=" +
  6114. stringify(PROP_ID(PR_DELETED_ON)) + " AND type=" +
  6115. stringify(PROP_TYPE(PR_DELETED_ON));
  6116. er = lpDatabase->DoDelete(strQuery);
  6117. if(er != erSuccess) {
  6118. ec_log_debug("MoveObjects: problem removing PR_DELETED_ON for %u: %s (%x)",
  6119. cop.ulId, GetMAPIErrorMessage(er), er);
  6120. bPartialCompletion = true;
  6121. er = erSuccess; //ignore error // FIXME WHY?!
  6122. }
  6123. // a move is a delete in the originating folder and a new in the destination folder except for softdelete that is a change
  6124. if (cop.ulParent != ulDestFolderId) {
  6125. AddChange(lpSession, ulSyncId, cop.sSourceKey, cop.sParentSourceKey, ICS_MESSAGE_HARD_DELETE);
  6126. AddChange(lpSession, ulSyncId, cop.sNewSourceKey, sDestFolderSourceKey, ICS_MESSAGE_NEW);
  6127. } else if (cop.ulFlags & MSGFLAG_DELETED) {
  6128. // Restore a softdeleted message
  6129. AddChange(lpSession, ulSyncId, cop.sNewSourceKey, sDestFolderSourceKey, ICS_MESSAGE_NEW);
  6130. }
  6131. er = ECTPropsPurge::AddDeferredUpdate(lpSession, lpDatabase,
  6132. ulDestFolderId, cop.ulParent, cop.ulId);
  6133. if (er != erSuccess) {
  6134. ec_log_debug("MoveObjects: ECTPropsPurge::AddDeferredUpdate failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6135. goto exit;
  6136. }
  6137. // Track folder count changes
  6138. if (cop.ulType == MAPI_MESSAGE) {
  6139. if (cop.ulFlags & MSGFLAG_DELETED) {
  6140. // Undelete
  6141. if (cop.ulFlags & MAPI_ASSOCIATED) {
  6142. // Associated message undeleted
  6143. --mapFolderCounts[cop.ulParent].lDeletedAssoc;
  6144. ++mapFolderCounts[ulDestFolderId].lAssoc;
  6145. } else {
  6146. // Message undeleted
  6147. --mapFolderCounts[cop.ulParent].lDeleted;
  6148. ++mapFolderCounts[ulDestFolderId].lItems;
  6149. if ((cop.ulMessageFlags & MSGFLAG_READ) == 0)
  6150. // Undeleted message was unread
  6151. ++mapFolderCounts[ulDestFolderId].lUnread;
  6152. }
  6153. } else {
  6154. // Move
  6155. --mapFolderCounts[cop.ulParent].lItems;
  6156. ++mapFolderCounts[ulDestFolderId].lItems;
  6157. if ((cop.ulMessageFlags & MSGFLAG_READ) == 0) {
  6158. --mapFolderCounts[cop.ulParent].lUnread;
  6159. ++mapFolderCounts[ulDestFolderId].lUnread;
  6160. }
  6161. }
  6162. }
  6163. cop.bMoved = true;
  6164. }
  6165. er = ApplyFolderCounts(lpDatabase, mapFolderCounts);
  6166. if (er != erSuccess) {
  6167. ec_log_debug("MoveObjects: ApplyFolderCounts failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6168. goto exit;
  6169. }
  6170. // change the size if it is a soft delete item
  6171. if(bUpdateDeletedSize == true) {
  6172. er = UpdateObjectSize(lpDatabase, ulDestStoreId, MAPI_STORE, UPDATE_ADD, ulItemSize);
  6173. if (er != erSuccess) {
  6174. ec_log_debug("MoveObjects: UpdateObjectSize(store %u) failed: %s (%x)", ulDestStoreId, GetMAPIErrorMessage(er), er);
  6175. goto exit;
  6176. }
  6177. }
  6178. for (const auto &cop : lstCopyItems) {
  6179. if (!cop.bMoved)
  6180. continue;
  6181. // Cache update for object
  6182. g_lpSessionManager->GetCacheManager()->SetObject(cop.ulId,
  6183. ulDestFolderId, cop.ulOwner,
  6184. cop.ulFlags & ~MSGFLAG_DELETED /* possible undelete */,
  6185. cop.ulType);
  6186. // Remove old sourcekey and entryid and add them
  6187. g_lpSessionManager->GetCacheManager()->RemoveIndexData(cop.ulId);
  6188. g_lpSessionManager->GetCacheManager()->SetObjectProp(PROP_ID(PR_SOURCE_KEY),
  6189. cop.sNewSourceKey.size(), cop.sNewSourceKey, cop.ulId);
  6190. g_lpSessionManager->GetCacheManager()->SetObjectProp(PROP_ID(PR_ENTRYID),
  6191. cop.sNewEntryId.size(), cop.sNewEntryId, cop.ulId);
  6192. }
  6193. er = lpDatabase->Commit();
  6194. if (er != erSuccess) {
  6195. ec_log_debug("MoveObjects: database commit failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6196. goto exit;
  6197. }
  6198. for (auto &cop : lstCopyItems) {
  6199. if (!cop.bMoved)
  6200. continue;
  6201. // update destenation folder after PR_ENTRYID update
  6202. if (cCopyItems < EC_TABLE_CHANGE_THRESHOLD) {
  6203. // Update messages
  6204. g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_DELETE,
  6205. 0, cop.ulParent, cop.ulId, cop.ulType);
  6206. // Update destenation folder
  6207. g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_ADD,
  6208. 0, ulDestFolderId, cop.ulId, cop.ulType);
  6209. }
  6210. // Update Store object
  6211. g_lpSessionManager->NotificationMoved(cop.ulType, cop.ulId,
  6212. ulDestFolderId, cop.ulParent, cop.sOldEntryId);
  6213. lstParent.push_back(cop.ulParent);
  6214. }
  6215. lstParent.sort();
  6216. lstParent.unique();
  6217. //Update message folders
  6218. for (auto pa_id : lstParent) {
  6219. if (cCopyItems >= EC_TABLE_CHANGE_THRESHOLD)
  6220. g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_CHANGE,
  6221. 0, pa_id, 0, MAPI_MESSAGE);
  6222. // update the source parent folder for disconnected clients
  6223. WriteLocalCommitTimeMax(NULL, lpDatabase, pa_id, NULL);
  6224. // ignore error, no need to set partial even.
  6225. // Get the grandparent
  6226. g_lpSessionManager->GetCacheManager()->GetParent(pa_id, &ulGrandParent);
  6227. g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, pa_id);
  6228. g_lpSessionManager->NotificationModified(MAPI_FOLDER, pa_id);
  6229. g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY,
  6230. 0, ulGrandParent, pa_id, MAPI_FOLDER);
  6231. }
  6232. // update the destination folder for disconnected clients
  6233. WriteLocalCommitTimeMax(NULL, lpDatabase, ulDestFolderId, NULL);
  6234. // ignore error, no need to set partial even.
  6235. if(cCopyItems >= EC_TABLE_CHANGE_THRESHOLD)
  6236. g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_CHANGE, 0, ulDestFolderId, 0, MAPI_MESSAGE);
  6237. //Update destination folder
  6238. g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, ulDestFolderId);
  6239. g_lpSessionManager->NotificationModified(MAPI_FOLDER, ulDestFolderId);
  6240. // Update the grandfolder of dest. folder
  6241. g_lpSessionManager->GetCacheManager()->GetParent(ulDestFolderId, &ulGrandParent);
  6242. g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY, 0, ulGrandParent, ulDestFolderId, MAPI_FOLDER);
  6243. if(bPartialCompletion && er == erSuccess)
  6244. er = KCWARN_PARTIAL_COMPLETION;
  6245. exit:
  6246. if(lpDatabase && er != erSuccess && er != KCWARN_PARTIAL_COMPLETION)
  6247. lpDatabase->Rollback();
  6248. if(lpsNewEntryId)
  6249. FreeEntryId(lpsNewEntryId, true);
  6250. if(lpsOldEntryId)
  6251. FreeEntryId(lpsOldEntryId, true);
  6252. return er;
  6253. }
  6254. /**
  6255. * Copy one message with his parent data like attachments and recipient
  6256. *
  6257. * @param[in] lpecSession Pointer to a session object; cannot be NULL.
  6258. * @param[in] lpAttachmentStorage Pointer to an attachment storage object. If NULL is passed in lpAttachmentStorage,
  6259. * a default storage object with transaction enabled will be used.
  6260. * @param[in] ulObjId Source object that identify the message, recipient or attachment to copy.
  6261. * @param[in] ulDestFolderId Destenation object to received the copied message, recipient or attachment.
  6262. * @param[in] bIsRoot Identify the root object; For callers this should be true;
  6263. * @param[in] bDoNotification true if you want to send object notifications.
  6264. * @param[in] bDoTableNotification true if you want to send table notifications.
  6265. * @param[in] ulSyncId Client sync identify.
  6266. *
  6267. * @FIXME It is possible to send notifications before a commit, this can give issues with the cache!
  6268. * This function should be refactored
  6269. */
  6270. static ECRESULT CopyObject(ECSession *lpecSession,
  6271. ECAttachmentStorage *lpAttachmentStorage, unsigned int ulObjId,
  6272. unsigned int ulDestFolderId, bool bIsRoot, bool bDoNotification,
  6273. bool bDoTableNotification, unsigned int ulSyncId)
  6274. {
  6275. ECRESULT er = erSuccess;
  6276. ECDatabase *lpDatabase = NULL;
  6277. std::string strQuery;
  6278. DB_RESULT lpDBResult;
  6279. DB_ROW lpDBRow;
  6280. unsigned int ulNewObjectId = 0;
  6281. std::string strExclude;
  6282. long long llStoreSize;
  6283. unsigned int ulStoreId = 0;
  6284. unsigned int ulSize;
  6285. unsigned int ulObjType;
  6286. unsigned int ulParent = 0;
  6287. unsigned int ulFlags = 0;
  6288. GUID guidStore;
  6289. eQuotaStatus QuotaStatus;
  6290. SOURCEKEY sSourceKey;
  6291. SOURCEKEY sParentSourceKey;
  6292. entryId* lpsNewEntryId = NULL;
  6293. unsigned long long ullIMAP = 0;
  6294. object_ptr<ECAttachmentStorage> lpInternalAttachmentStorage;
  6295. er = lpecSession->GetDatabase(&lpDatabase);
  6296. if (er != erSuccess) {
  6297. ec_log_err("CopyObject: cannot retrieve database: %s (%x)", GetMAPIErrorMessage(er), er);
  6298. goto exit;
  6299. }
  6300. if (!lpAttachmentStorage) {
  6301. if (!bIsRoot) {
  6302. ec_log_err("CopyObject: \"!attachmentstore && !isroot\" clause failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6303. er = KCERR_INVALID_PARAMETER;
  6304. goto exit;
  6305. }
  6306. er = CreateAttachmentStorage(lpDatabase, &~lpInternalAttachmentStorage);
  6307. if (er != erSuccess) {
  6308. ec_log_err("CopyObject: CreateAttachmentStorage failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6309. goto exit;
  6310. }
  6311. lpAttachmentStorage = lpInternalAttachmentStorage;
  6312. // Hack, when lpInternalAttachmentStorage exist your are in a transaction!
  6313. }
  6314. er = lpecSession->GetSessionManager()->GetCacheManager()->GetStore(ulDestFolderId, &ulStoreId, &guidStore);
  6315. if (er != erSuccess) {
  6316. ec_log_err("CopyObject: GetStore(destination folder %u) failed: %s (%x)", ulDestFolderId, GetMAPIErrorMessage(er), er);
  6317. goto exit;
  6318. }
  6319. // Check permission
  6320. if(bIsRoot == true)
  6321. {
  6322. er = lpecSession->GetSecurity()->CheckPermission(ulObjId, ecSecurityRead);
  6323. if (er != erSuccess) {
  6324. ec_log_err("CopyObject: check permissions of %u failed: %s (%x)", ulObjId, GetMAPIErrorMessage(er), er);
  6325. goto exit;
  6326. }
  6327. // Quota check
  6328. er = lpecSession->GetSecurity()->GetStoreSize(ulDestFolderId, &llStoreSize);
  6329. if (er != erSuccess) {
  6330. ec_log_err("CopyObject: store size of dest folder %u failed: %s (%x)", ulDestFolderId, GetMAPIErrorMessage(er), er);
  6331. goto exit;
  6332. }
  6333. er = lpecSession->GetSecurity()->CheckQuota(ulDestFolderId, llStoreSize, &QuotaStatus);
  6334. if (er != erSuccess) {
  6335. ec_log_err("CopyObject: check quota of dest folder %u failed: %s (%x)", ulDestFolderId, GetMAPIErrorMessage(er), er);
  6336. goto exit;
  6337. }
  6338. if(QuotaStatus == QUOTA_HARDLIMIT) {
  6339. er = KCERR_STORE_FULL;
  6340. goto exit;
  6341. }
  6342. // Start tranaction
  6343. if (lpInternalAttachmentStorage) {
  6344. er = lpInternalAttachmentStorage->Begin();
  6345. if (er != erSuccess) {
  6346. ec_log_err("CopyObject: starting transaction in attachment storage failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6347. goto exit;
  6348. }
  6349. er = lpDatabase->Begin();
  6350. if (er != erSuccess) {
  6351. ec_log_err("CopyObject: starting transaction in database failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6352. goto exit;
  6353. }
  6354. }
  6355. }
  6356. // Get the hierarchy messageroot but not the deleted items
  6357. 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";
  6358. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  6359. if (er != erSuccess) {
  6360. ec_log_err("CopyObject: failed retrieving hierarchy message root: %s (%x)", GetMAPIErrorMessage(er), er);
  6361. goto exit;
  6362. }
  6363. if(lpDatabase->GetNumRows(lpDBResult) < 1) {
  6364. er = KCERR_NOT_FOUND;// FIXME: right error?
  6365. goto exit;
  6366. }
  6367. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  6368. if( lpDBRow == NULL || lpDBRow[0] == NULL || lpDBRow[1] == NULL) {
  6369. er = KCERR_NOT_FOUND;
  6370. goto exit;
  6371. }
  6372. ulObjType = atoui(lpDBRow[1]);
  6373. ulParent = atoui(lpDBRow[0]);
  6374. if (lpDBRow[2])
  6375. ulFlags = atoui(lpDBRow[2]);
  6376. if (bIsRoot == true && ulObjType != MAPI_MESSAGE) {
  6377. ec_log_err("CopyObject: \"isRoot && != MAPI_MESSAGE\" fail");
  6378. er = KCERR_INVALID_ENTRYID;
  6379. goto exit;
  6380. }
  6381. //FIXME: Why do we always use the mod and create time of the old object? Create time can always be NOW
  6382. //Create new message (Only valid flag in hierarchy is MSGFLAG_ASSOCIATED)
  6383. strQuery = "INSERT INTO hierarchy(parent, type, flags, owner) VALUES(" +
  6384. stringify(ulDestFolderId) + ", " +
  6385. (string)lpDBRow[1] + ", " +
  6386. stringify(ulFlags) + "&" + stringify(MSGFLAG_ASSOCIATED) + "," +
  6387. stringify(lpecSession->GetSecurity()->GetUserId()) + ") ";
  6388. er = lpDatabase->DoInsert(strQuery, &ulNewObjectId);
  6389. if (er != erSuccess) {
  6390. ec_log_err("CopyObject: failed inserting entry in hierarchy table: %s (%x)", GetMAPIErrorMessage(er), er);
  6391. goto exit;
  6392. }
  6393. if(bIsRoot == true) {
  6394. sObjectTableKey key(ulNewObjectId, 0);
  6395. propVal sProp;
  6396. // Create message entry
  6397. er = CreateEntryId(guidStore, MAPI_MESSAGE, &lpsNewEntryId);
  6398. if (er != erSuccess) {
  6399. ec_log_err("CopyObject: CreateEntryId failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6400. goto exit;
  6401. }
  6402. //0x0FFF = PR_ENTRYID
  6403. strQuery = "INSERT INTO indexedproperties (hierarchyid,tag,val_binary) VALUES("+stringify(ulNewObjectId)+", 0x0FFF, "+lpDatabase->EscapeBinary(lpsNewEntryId->__ptr, lpsNewEntryId->__size)+")";
  6404. er = lpDatabase->DoInsert(strQuery);
  6405. if (er != erSuccess) {
  6406. ec_log_err("CopyObject: PR_ENTRYID property insert failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6407. goto exit;
  6408. }
  6409. // Add a PR_EC_IMAP_ID
  6410. er = g_lpSessionManager->GetNewSequence(ECSessionManager::SEQ_IMAP, &ullIMAP);
  6411. if (er != erSuccess) {
  6412. ec_log_err("CopyObject: retrieving new seqnr for PR_EC_IMAP_ID failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6413. goto exit;
  6414. }
  6415. strQuery = "INSERT INTO properties(hierarchyid, tag, type, val_ulong) VALUES(" +
  6416. stringify(ulNewObjectId) + "," +
  6417. stringify(PROP_ID(PR_EC_IMAP_ID)) + "," +
  6418. stringify(PROP_TYPE(PR_EC_IMAP_ID)) + "," +
  6419. stringify(ullIMAP) +
  6420. ")";
  6421. er = lpDatabase->DoInsert(strQuery);
  6422. if (er != erSuccess) {
  6423. ec_log_err("CopyObject: PR_EC_IMAP_ID property insert failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6424. goto exit;
  6425. }
  6426. sProp.ulPropTag = PR_EC_IMAP_ID;
  6427. sProp.Value.ul = ullIMAP;
  6428. sProp.__union = SOAP_UNION_propValData_ul;
  6429. er = g_lpSessionManager->GetCacheManager()->SetCell(&key, PR_EC_IMAP_ID, &sProp);
  6430. if (er != erSuccess) {
  6431. ec_log_err("CopyObject: updating PR_EC_IMAP_ID sell in cache failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6432. goto exit;
  6433. }
  6434. }
  6435. if (lpsNewEntryId != NULL) {
  6436. FreeEntryId(lpsNewEntryId, true);
  6437. lpsNewEntryId = NULL;
  6438. }
  6439. // Get child items of the message like , attachment, recipient...
  6440. strQuery = "SELECT id FROM hierarchy WHERE parent="+stringify(ulObjId);
  6441. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  6442. if (er != erSuccess) {
  6443. ec_log_err("CopyObject: failed retrieving child items of message: %s (%x)", GetMAPIErrorMessage(er), er);
  6444. goto exit;
  6445. }
  6446. if(lpDatabase->GetNumRows(lpDBResult) > 0) {
  6447. while( (lpDBRow = lpDatabase->FetchRow(lpDBResult)) )
  6448. {
  6449. if(lpDBRow[0] == NULL)
  6450. continue; // FIXME: Skip, give an error/warning ?
  6451. er = CopyObject(lpecSession, lpAttachmentStorage, atoui(lpDBRow[0]), ulNewObjectId, false, false, false, ulSyncId);
  6452. if (er != erSuccess && er != KCERR_NOT_FOUND) {
  6453. ec_log_err("CopyObject: CopyObject(%s) failed: %s (%x)", lpDBRow[0], GetMAPIErrorMessage(er), er);
  6454. goto exit;
  6455. } else {
  6456. er = erSuccess;
  6457. }
  6458. }
  6459. }
  6460. // Exclude properties
  6461. // PR_DELETED_ON
  6462. strExclude = " AND NOT (tag="+stringify(PROP_ID(PR_DELETED_ON))+" AND type="+stringify(PROP_TYPE(PR_DELETED_ON))+")";
  6463. //Exclude PR_SOURCE_KEY, PR_CHANGE_KEY, PR_PREDECESSOR_CHANGE_LIST
  6464. strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_SOURCE_KEY))+" AND type="+stringify(PROP_TYPE(PR_SOURCE_KEY))+")";
  6465. strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_CHANGE_KEY))+" AND type="+stringify(PROP_TYPE(PR_CHANGE_KEY))+")";
  6466. strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_PREDECESSOR_CHANGE_LIST))+" AND type="+stringify(PROP_TYPE(PR_PREDECESSOR_CHANGE_LIST))+")";
  6467. strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_EC_IMAP_ID))+" AND type="+stringify(PROP_TYPE(PR_EC_IMAP_ID))+")";
  6468. // because of #7699, messages contain PR_LOCAL_COMMIT_TIME_MAX
  6469. strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_LOCAL_COMMIT_TIME_MAX))+" AND type="+stringify(PROP_TYPE(PR_LOCAL_COMMIT_TIME_MAX))+")";
  6470. // Copy properties...
  6471. 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;
  6472. er = lpDatabase->DoInsert(strQuery);
  6473. if (er != erSuccess) {
  6474. ec_log_err("CopyObject: copy properties failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6475. goto exit;
  6476. }
  6477. // Copy MVproperties...
  6478. 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);
  6479. er = lpDatabase->DoInsert(strQuery);
  6480. if (er != erSuccess) {
  6481. ec_log_err("CopyObject: copy MVproperties failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6482. goto exit;
  6483. }
  6484. // Copy large objects... if present
  6485. er = lpAttachmentStorage->CopyAttachment(ulObjId, ulNewObjectId);
  6486. if (er != erSuccess && er != KCERR_NOT_FOUND) {
  6487. ec_log_err("CopyObject: CopyAttachment(%u -> %u) failed: %s (%x)", ulObjId, ulNewObjectId, GetMAPIErrorMessage(er), er);
  6488. goto exit;
  6489. }
  6490. er = erSuccess;
  6491. if(bIsRoot == true)
  6492. {
  6493. // Create indexedproperties, Add new PR_SOURCE_KEY
  6494. er = lpecSession->GetNewSourceKey(&sSourceKey);
  6495. if (er != erSuccess) {
  6496. ec_log_err("CopyObject: GetNewSourceKey failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6497. goto exit;
  6498. }
  6499. strQuery = "INSERT INTO indexedproperties(hierarchyid,tag,val_binary) VALUES(" + stringify(ulNewObjectId) + "," + stringify(PROP_ID(PR_SOURCE_KEY)) + "," + lpDatabase->EscapeBinary(sSourceKey, sSourceKey.size()) + ")";
  6500. er = lpDatabase->DoInsert(strQuery);
  6501. if (er != erSuccess) {
  6502. ec_log_err("CopyObject: insert %u in indexedproperties failed: %s (%x)", ulNewObjectId, GetMAPIErrorMessage(er), er);
  6503. goto exit;
  6504. }
  6505. // Track folder count changes
  6506. // Can we copy deleted items?
  6507. if(ulFlags & MAPI_ASSOCIATED) {
  6508. // Associated message undeleted
  6509. er = UpdateFolderCount(lpDatabase, ulDestFolderId, PR_ASSOC_CONTENT_COUNT, 1);
  6510. } else {
  6511. // Message undeleted
  6512. er = UpdateFolderCount(lpDatabase, ulDestFolderId, PR_CONTENT_COUNT, 1);
  6513. if (er == erSuccess && (ulFlags & MSGFLAG_READ) == 0)
  6514. // Undeleted message was unread
  6515. er = UpdateFolderCount(lpDatabase, ulDestFolderId, PR_CONTENT_UNREAD, 1);
  6516. }
  6517. if (er != erSuccess) {
  6518. ec_log_err("CopyObject: UpdateFolderCount (%u) failed: %s (%x)", ulDestFolderId, GetMAPIErrorMessage(er), er);
  6519. goto exit;
  6520. }
  6521. // Update ICS system
  6522. GetSourceKey(ulDestFolderId, &sParentSourceKey);
  6523. AddChange(lpecSession, ulSyncId, sSourceKey, sParentSourceKey, ICS_MESSAGE_NEW);
  6524. // Hack, when lpInternalAttachmentStorage exist your are in a transaction!
  6525. if (lpInternalAttachmentStorage) {
  6526. // Deferred tproperties
  6527. er = ECTPropsPurge::AddDeferredUpdate(lpecSession, lpDatabase, ulDestFolderId, 0, ulNewObjectId);
  6528. if (er != erSuccess) {
  6529. ec_log_err("CopyObject: ECTPropsPurge::AddDeferredUpdate(%u): %s (%x)", ulDestFolderId, GetMAPIErrorMessage(er), er);
  6530. goto exit;
  6531. }
  6532. er = lpInternalAttachmentStorage->Commit();
  6533. if (er != erSuccess) {
  6534. ec_log_err("CopyObject: attachmentstorage commit failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6535. goto exit;
  6536. }
  6537. er = lpDatabase->Commit();
  6538. if (er != erSuccess) {
  6539. ec_log_err("CopyObject: database commit failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6540. goto exit;
  6541. }
  6542. } else {
  6543. // Deferred tproperties, let the caller handle the purge so we won't purge every 20 messages on a copy
  6544. // of a complete folder.
  6545. er = ECTPropsPurge::AddDeferredUpdateNoPurge(lpDatabase, ulDestFolderId, 0, ulNewObjectId);
  6546. if(er != erSuccess) {
  6547. ec_log_err("CopyObject: ECTPropsPurge::AddDeferredUpdateNoPurge(%u, %u) failed: %s (%x)", ulDestFolderId, ulNewObjectId, GetMAPIErrorMessage(er), er);
  6548. goto exit;
  6549. }
  6550. }
  6551. g_lpSessionManager->GetCacheManager()->SetObjectProp(PROP_ID(PR_SOURCE_KEY), sSourceKey.size(), sSourceKey, ulNewObjectId);
  6552. // Update Size
  6553. if(GetObjectSize(lpDatabase, ulNewObjectId, &ulSize) == erSuccess)
  6554. {
  6555. if(lpecSession->GetSessionManager()->GetCacheManager()->GetStore(ulNewObjectId, &ulStoreId, NULL) == erSuccess) {
  6556. er = UpdateObjectSize(lpDatabase, ulStoreId, MAPI_STORE, UPDATE_ADD, ulSize);
  6557. if (er != erSuccess) {
  6558. ec_log_err("CopyObject: UpdateObjectSize(store %u) failed: %s (%x)", ulStoreId, GetMAPIErrorMessage(er), er);
  6559. goto exit;
  6560. }
  6561. }
  6562. }
  6563. }
  6564. g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, ulDestFolderId);
  6565. if(bDoNotification){
  6566. // Update destenation folder
  6567. if (bDoTableNotification)
  6568. g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_ADD, 0, ulDestFolderId, ulNewObjectId, MAPI_MESSAGE);
  6569. g_lpSessionManager->NotificationModified(MAPI_FOLDER, ulDestFolderId);
  6570. // Notify object is copied
  6571. g_lpSessionManager->NotificationCopied(MAPI_MESSAGE, ulNewObjectId, ulDestFolderId, ulObjId, ulParent);
  6572. }
  6573. exit:
  6574. if(er != erSuccess && lpInternalAttachmentStorage) {
  6575. // Rollback attachments and database!
  6576. lpInternalAttachmentStorage->Rollback();
  6577. lpDatabase->Rollback();
  6578. }
  6579. if(lpsNewEntryId)
  6580. FreeEntryId(lpsNewEntryId, true);
  6581. return er;
  6582. }
  6583. /**
  6584. * Copy folder and his childs
  6585. *
  6586. * @note please check the object type before you call this function, the type should be MAPI_FOLDER
  6587. */
  6588. static ECRESULT CopyFolderObjects(struct soap *soap, ECSession *lpecSession,
  6589. unsigned int ulFolderFrom, unsigned int ulDestFolderId,
  6590. char *lpszNewFolderName, bool bCopySubFolder, unsigned int ulSyncId)
  6591. {
  6592. ECRESULT er = erSuccess;
  6593. ECDatabase *lpDatabase = NULL;
  6594. std::string strQuery, strSubQuery, strExclude;
  6595. DB_RESULT lpDBResult;
  6596. DB_ROW lpDBRow;
  6597. unsigned int ulNewDestFolderId = 0;
  6598. unsigned int ulItems = 0;
  6599. unsigned int ulGrandParent = 0;
  6600. unsigned int ulDestStoreId = 0;
  6601. unsigned int ulSourceStoreId = 0;
  6602. bool bPartialCompletion = false;
  6603. long long llStoreSize = 0;
  6604. eQuotaStatus QuotaStatus;
  6605. SOURCEKEY sSourceKey;
  6606. SOURCEKEY sParentSourceKey;
  6607. object_ptr<ECAttachmentStorage> lpAttachmentStorage;
  6608. if(lpszNewFolderName == NULL) {
  6609. ec_log_err("CopyFolderObjects: \"new folder name\" missing");
  6610. er = KCERR_INVALID_PARAMETER;
  6611. goto exit;
  6612. }
  6613. er = lpecSession->GetDatabase(&lpDatabase);
  6614. if (er != erSuccess) {
  6615. ec_log_err("CopyFolderObjects: cannot retrieve database: %s (%x)", GetMAPIErrorMessage(er), er);
  6616. goto exit;
  6617. }
  6618. er = CreateAttachmentStorage(lpDatabase, &~lpAttachmentStorage);
  6619. if (er != erSuccess) {
  6620. ec_log_err("CopyFolderObjects: CreateAttachmentStorage failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6621. goto exit;
  6622. }
  6623. er = lpecSession->GetSessionManager()->GetCacheManager()->GetStore(ulDestFolderId, &ulDestStoreId, NULL);
  6624. if (er != erSuccess) {
  6625. ec_log_err("CopyFolderObjects: GetStore for %u (from cache) failed: %s (%x)", ulDestFolderId, GetMAPIErrorMessage(er), er);
  6626. goto exit;
  6627. }
  6628. er = lpecSession->GetSessionManager()->GetCacheManager()->GetStore(ulFolderFrom, &ulSourceStoreId, NULL);
  6629. if (er != erSuccess) {
  6630. ec_log_err("CopyFolderObjects: GetStore for %u (from cache) failed: %s (%x)", ulFolderFrom, GetMAPIErrorMessage(er), er);
  6631. goto exit;
  6632. }
  6633. er = lpAttachmentStorage->Begin();
  6634. if (er != erSuccess) {
  6635. ec_log_err("CopyFolderObjects: Begin() on attachment storage failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6636. goto exit;
  6637. }
  6638. er = lpDatabase->Begin();
  6639. if (er != erSuccess) {
  6640. ec_log_err("CopyFolderObjects: Begin() on database failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6641. goto exit;
  6642. }
  6643. // Quota check
  6644. er = lpecSession->GetSecurity()->GetStoreSize(ulDestFolderId, &llStoreSize);
  6645. if (er != erSuccess) {
  6646. ec_log_err("CopyFolderObjects: GetStoreSize failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6647. goto exit;
  6648. }
  6649. er = lpecSession->GetSecurity()->CheckQuota(ulDestFolderId, llStoreSize, &QuotaStatus);
  6650. if (er != erSuccess) {
  6651. ec_log_err("CopyFolderObjects: CheckQuota failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6652. goto exit;
  6653. }
  6654. if(QuotaStatus == QUOTA_HARDLIMIT) {
  6655. er = KCERR_STORE_FULL;
  6656. goto exit;
  6657. }
  6658. // Create folder (with a sourcekey)
  6659. er = CreateFolder(lpecSession, lpDatabase, ulDestFolderId, NULL, FOLDER_GENERIC, lpszNewFolderName, NULL, false, true, ulSyncId, NULL, &ulNewDestFolderId, NULL);
  6660. if (er != erSuccess) {
  6661. ec_log_err("CopyFolderObjects: CreateFolder \"%s\" in %u failed: %s (%x)", lpszNewFolderName, ulDestFolderId, GetMAPIErrorMessage(er), er);
  6662. goto exit;
  6663. }
  6664. // Always use the string version if you want to exclude properties
  6665. strExclude = " AND NOT (tag="+stringify(PROP_ID(PR_DELETED_ON))+" AND type="+stringify(PROP_TYPE(PR_DELETED_ON))+")";
  6666. strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_DISPLAY_NAME_A))+" AND type="+stringify(PROP_TYPE(PR_DISPLAY_NAME_A))+")";
  6667. //Exclude PR_SOURCE_KEY, PR_CHANGE_KEY, PR_PREDECESSOR_CHANGE_LIST
  6668. strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_SOURCE_KEY))+" AND type="+stringify(PROP_TYPE(PR_SOURCE_KEY))+")";
  6669. strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_CHANGE_KEY))+" AND type="+stringify(PROP_TYPE(PR_CHANGE_KEY))+")";
  6670. strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_PREDECESSOR_CHANGE_LIST))+" AND type="+stringify(PROP_TYPE(PR_PREDECESSOR_CHANGE_LIST))+")";
  6671. // Exclude the counters
  6672. strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_CONTENT_COUNT))+" AND type="+stringify(PROP_TYPE(PR_CONTENT_COUNT))+")";
  6673. strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_CONTENT_UNREAD))+" AND type="+stringify(PROP_TYPE(PR_CONTENT_UNREAD))+")";
  6674. strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_FOLDER_CHILD_COUNT))+" AND type="+stringify(PROP_TYPE(PR_FOLDER_CHILD_COUNT))+")";
  6675. strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_ASSOC_CONTENT_COUNT))+" AND type="+stringify(PROP_TYPE(PR_ASSOC_CONTENT_COUNT))+")";
  6676. strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_DELETED_MSG_COUNT))+" AND type="+stringify(PROP_TYPE(PR_DELETED_MSG_COUNT))+")";
  6677. strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_DELETED_FOLDER_COUNT))+" AND type="+stringify(PROP_TYPE(PR_DELETED_FOLDER_COUNT))+")";
  6678. strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_DELETED_ASSOC_MSG_COUNT))+" AND type="+stringify(PROP_TYPE(PR_DELETED_ASSOC_MSG_COUNT))+")";
  6679. strExclude += " AND NOT (tag="+stringify(PROP_ID(PR_SUBFOLDERS))+" AND type="+stringify(PROP_TYPE(PR_SUBFOLDER))+")";
  6680. // Copy properties...
  6681. 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;
  6682. er = lpDatabase->DoInsert(strQuery);
  6683. if (er != erSuccess) {
  6684. ec_log_err("CopyFolderObjects: copy properties step failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6685. goto exit;
  6686. }
  6687. // Copy MVproperties...
  6688. 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);
  6689. er = lpDatabase->DoInsert(strQuery);
  6690. if (er != erSuccess) {
  6691. ec_log_err("CopyFolderObjects: copy mvproperties step failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6692. goto exit;
  6693. }
  6694. // Copy large objects... if present .. probably not, on a folder
  6695. er = lpAttachmentStorage->CopyAttachment(ulFolderFrom, ulNewDestFolderId);
  6696. if (er != erSuccess && er != KCERR_NOT_FOUND) {
  6697. ec_log_err("CopyFolderObjects: copy attachment step failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6698. goto exit;
  6699. }
  6700. er = erSuccess;
  6701. // update ICS sytem with a change
  6702. GetSourceKey(ulDestFolderId, &sParentSourceKey);
  6703. GetSourceKey(ulNewDestFolderId, &sSourceKey);
  6704. AddChange(lpecSession, ulSyncId, sSourceKey, sParentSourceKey, ICS_FOLDER_CHANGE);
  6705. //Select all Messages of the home folder
  6706. // Skip deleted and associated items
  6707. strQuery = "SELECT id FROM hierarchy WHERE parent="+stringify(ulFolderFrom)+ " AND type="+stringify(MAPI_MESSAGE)+" AND flags & " + stringify(MSGFLAG_DELETED|MSGFLAG_ASSOCIATED) + " = 0";
  6708. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  6709. if (er != erSuccess) {
  6710. ec_log_err("CopyFolderObjects: retrieving list of messages from home folder failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6711. goto exit;
  6712. }
  6713. ulItems = lpDatabase->GetNumRows(lpDBResult);
  6714. // Walk through the messages list
  6715. while( (lpDBRow = lpDatabase->FetchRow(lpDBResult)) )
  6716. {
  6717. if(lpDBRow[0] == NULL)
  6718. continue; //FIXME: error show ???
  6719. er = CopyObject(lpecSession, lpAttachmentStorage, atoui(lpDBRow[0]), ulNewDestFolderId, true, false, false, ulSyncId);
  6720. // FIXME: handle KCERR_STORE_FULL
  6721. if(er == KCERR_NOT_FOUND) {
  6722. bPartialCompletion = true;
  6723. } else if (er != erSuccess) {
  6724. ec_log_err("CopyFolderObjects: CopyObject %s failed failed: %s (%x)", lpDBRow[0], GetMAPIErrorMessage(er), er);
  6725. goto exit;
  6726. }
  6727. }
  6728. // update the destination folder for disconnected clients
  6729. er = WriteLocalCommitTimeMax(NULL, lpDatabase, ulNewDestFolderId, NULL);
  6730. if (er != erSuccess) {
  6731. ec_log_err("CopyFolderObjects: WriteLocalCommitTimeMax failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6732. goto exit;
  6733. }
  6734. er = ECTPropsPurge::AddDeferredUpdate(lpecSession, lpDatabase, ulDestFolderId, 0, ulNewDestFolderId);
  6735. if (er != erSuccess) {
  6736. ec_log_err("CopyFolderObjects: ECTPropsPurge::AddDeferredUpdate failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6737. goto exit;
  6738. }
  6739. er = lpDatabase->Commit();
  6740. if (er != erSuccess) {
  6741. ec_log_err("CopyFolderObjects: database commit failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6742. goto exit;
  6743. }
  6744. er = lpAttachmentStorage->Commit();
  6745. if (er != erSuccess) {
  6746. ec_log_err("CopyFolderObjects: attachment storage commit failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6747. goto exit;
  6748. }
  6749. // Notifications
  6750. if(ulItems > 0)
  6751. {
  6752. //Update destenation folder
  6753. g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_CHANGE, 0, ulNewDestFolderId, 0, MAPI_MESSAGE);
  6754. // Update the grandfolder of dest. folder
  6755. g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY, 0, ulDestFolderId, ulNewDestFolderId, MAPI_FOLDER);
  6756. //Update destination folder
  6757. g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, ulNewDestFolderId);
  6758. g_lpSessionManager->NotificationModified(MAPI_FOLDER, ulNewDestFolderId);
  6759. }
  6760. g_lpSessionManager->GetCacheManager()->GetParent(ulFolderFrom ,&ulGrandParent);
  6761. g_lpSessionManager->NotificationCopied(MAPI_FOLDER, ulNewDestFolderId, ulDestFolderId, ulFolderFrom, ulGrandParent);
  6762. if(bCopySubFolder) {
  6763. //Select all folders of the home folder
  6764. // Skip deleted folders
  6765. 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);
  6766. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  6767. if (er != erSuccess) {
  6768. ec_log_err("CopyFolderObjects: retrieving list of folders from home folder failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6769. goto exit;
  6770. }
  6771. if(lpDatabase->GetNumRows(lpDBResult) > 0) {
  6772. // Walk through the folder list
  6773. while( (lpDBRow = lpDatabase->FetchRow(lpDBResult)) )
  6774. {
  6775. if(lpDBRow[0] == NULL || lpDBRow[1] == NULL)
  6776. continue; // ignore
  6777. // Create SubFolder with messages. This object type checking is done in the where of the query
  6778. er = CopyFolderObjects(soap, lpecSession, atoui(lpDBRow[0]), ulNewDestFolderId, lpDBRow[1], true, ulSyncId);
  6779. if (er == KCWARN_PARTIAL_COMPLETION) {
  6780. bPartialCompletion = true;
  6781. } else if (er != erSuccess) {
  6782. ec_log_err("CopyFolderObjects: CopyFolderObjects %s failed failed: %s (%x)", lpDBRow[0], GetMAPIErrorMessage(er), er);
  6783. goto exit;
  6784. }
  6785. }
  6786. }
  6787. }
  6788. if(bPartialCompletion && er == erSuccess)
  6789. er = KCWARN_PARTIAL_COMPLETION;
  6790. exit:
  6791. if(lpDatabase && er != erSuccess && er != KCWARN_PARTIAL_COMPLETION) {
  6792. lpDatabase->Rollback();
  6793. if (lpAttachmentStorage)
  6794. lpAttachmentStorage->Rollback();
  6795. }
  6796. return er;
  6797. }
  6798. /**
  6799. * Copy one or more messages to a destenation
  6800. */
  6801. SOAP_ENTRY_START(copyObjects, *result, struct entryList *aMessages, entryId sDestFolderId, unsigned int ulFlags, unsigned int ulSyncId, unsigned int *result)
  6802. {
  6803. bool bPartialCompletion = false;
  6804. unsigned int ulGrandParent=0;
  6805. unsigned int ulDestFolderId = 0;
  6806. ECListInt lObjectIds;
  6807. ECListIntIterator iObjectId;
  6808. size_t cObjectItems = 0;
  6809. std::set<EntryId> setEntryIds;
  6810. USE_DATABASE();
  6811. const EntryId dstEntryId(&sDestFolderId);
  6812. if(aMessages == NULL) {
  6813. ec_log_err("SOAP::copyObjects: list of messages (entryList) missing");
  6814. er = KCERR_INVALID_PARAMETER;
  6815. goto exit;
  6816. }
  6817. for (unsigned int i = 0; i < aMessages->__size; ++i)
  6818. setEntryIds.insert(EntryId(aMessages->__ptr[i]));
  6819. setEntryIds.insert(EntryId(sDestFolderId));
  6820. er = BeginLockFolders(lpDatabase, setEntryIds, LOCK_EXCLUSIVE);
  6821. if (er != erSuccess) {
  6822. ec_log_err("SOAP::copyObjects: failed locking folders: %s (%x)", GetMAPIErrorMessage(er), er);
  6823. goto exit;
  6824. }
  6825. er = lpecSession->GetObjectFromEntryId(&sDestFolderId, &ulDestFolderId);
  6826. if (er != erSuccess) {
  6827. std::string dstEntryIdStr = dstEntryId;
  6828. ec_log_err("SOAP::copyObjects: failed obtaining object by entry id (%s): %s (%x)", dstEntryIdStr.c_str(), GetMAPIErrorMessage(er), er);
  6829. goto exit;
  6830. }
  6831. // Check permission, Destination folder
  6832. er = lpecSession->GetSecurity()->CheckPermission(ulDestFolderId, ecSecurityCreate);
  6833. if (er != erSuccess) {
  6834. ec_log_err("SOAP::copyObjects: failed checking permissions for folder id %u: %s (%x)", ulDestFolderId, GetMAPIErrorMessage(er), er);
  6835. goto exit;
  6836. }
  6837. if(g_lpSessionManager->GetCacheManager()->GetEntryListToObjectList(aMessages, &lObjectIds) != erSuccess)
  6838. bPartialCompletion = true;
  6839. // @note The object type checking wille be done in MoveObjects or CopyObject
  6840. //check copy or a move
  6841. if(ulFlags & FOLDER_MOVE ) { // A move
  6842. er = MoveObjects(lpecSession, lpDatabase, &lObjectIds, ulDestFolderId, ulSyncId);
  6843. if (er != erSuccess) {
  6844. ec_log_err("SOAP::copyObjects: MoveObjects failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6845. goto exit;
  6846. }
  6847. }else { // A copy
  6848. cObjectItems = lObjectIds.size();
  6849. for (iObjectId = lObjectIds.begin();
  6850. iObjectId != lObjectIds.end(); ++iObjectId) {
  6851. er = CopyObject(lpecSession, NULL, *iObjectId, ulDestFolderId, true, true, cObjectItems < EC_TABLE_CHANGE_THRESHOLD, ulSyncId);
  6852. if(er != erSuccess) {
  6853. ec_log_err("SOAP::copyObjects: failed copying object %u: %s (%x)", *iObjectId, GetMAPIErrorMessage(er), er);
  6854. bPartialCompletion = true;
  6855. er = erSuccess;
  6856. }
  6857. }
  6858. // update the destination folder for disconnected clients
  6859. er = WriteLocalCommitTimeMax(NULL, lpDatabase, ulDestFolderId, NULL);
  6860. if (er != erSuccess) {
  6861. ec_log_err("SOAP::copyObjects: WriteLocalCommitTimeMax failed: %s (%x)", GetMAPIErrorMessage(er), er);
  6862. goto exit;
  6863. }
  6864. if(cObjectItems >= EC_TABLE_CHANGE_THRESHOLD)
  6865. g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_CHANGE, 0, ulDestFolderId, 0, MAPI_MESSAGE);
  6866. // Update the grandfolder of dest. folder
  6867. g_lpSessionManager->GetCacheManager()->GetParent(ulDestFolderId, &ulGrandParent);
  6868. g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY, 0, ulGrandParent, ulDestFolderId, MAPI_FOLDER);
  6869. //Update destination folder
  6870. g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, ulDestFolderId);
  6871. g_lpSessionManager->NotificationModified(MAPI_FOLDER, ulDestFolderId);
  6872. }
  6873. if(bPartialCompletion && er == erSuccess)
  6874. er = KCWARN_PARTIAL_COMPLETION;
  6875. exit:
  6876. lpDatabase->Commit();
  6877. }
  6878. SOAP_ENTRY_END()
  6879. SOAP_ENTRY_START(copyFolder, *result, entryId sEntryId, entryId sDestFolderId, char *lpszNewFolderName, unsigned int ulFlags, unsigned int ulSyncId, unsigned int *result)
  6880. {
  6881. unsigned int ulAffRows = 0;
  6882. unsigned int ulOldParent = 0;
  6883. unsigned int ulGrandParent = 0;
  6884. unsigned int ulParentCycle = 0;
  6885. unsigned int ulDestStoreId = 0;
  6886. unsigned int ulSourceStoreId = 0;
  6887. unsigned int ulObjFlags = 0;
  6888. unsigned int ulOldGrandParent = 0;
  6889. unsigned int ulFolderId = 0;
  6890. unsigned int ulDestFolderId = 0;
  6891. unsigned int ulSourceType = 0;
  6892. unsigned int ulDestType = 0;
  6893. long long llFolderSize = 0;
  6894. SOURCEKEY sSourceKey;
  6895. SOURCEKEY sParentSourceKey; // Old parent
  6896. SOURCEKEY sDestSourceKey; // New parent
  6897. std::string strSubQuery;
  6898. USE_DATABASE();
  6899. const EntryId srcEntryId(&sEntryId);
  6900. const EntryId dstEntryId(&sDestFolderId);
  6901. std::string name;
  6902. // NOTE: lpszNewFolderName can be NULL
  6903. if (lpszNewFolderName)
  6904. lpszNewFolderName = STRIN_FIX(lpszNewFolderName);
  6905. er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulFolderId);
  6906. if (er != erSuccess) {
  6907. const std::string srcEntryIdStr = srcEntryId;
  6908. ec_log_err("SOAP::copyFolder GetObjectFromEntryId failed for %s: %s (%x)", srcEntryIdStr.c_str(), GetMAPIErrorMessage(er), er);
  6909. return er;
  6910. }
  6911. // Get source store
  6912. er = g_lpSessionManager->GetCacheManager()->GetStore(ulFolderId, &ulSourceStoreId, NULL);
  6913. if (er != erSuccess) {
  6914. ec_log_err("SOAP::copyFolder GetStore failed for folder id %ul: %s (%x)", ulFolderId, GetMAPIErrorMessage(er), er);
  6915. return er;
  6916. }
  6917. er = lpecSession->GetObjectFromEntryId(&sDestFolderId, &ulDestFolderId);
  6918. if (er != erSuccess) {
  6919. const std::string dstEntryIdStr = dstEntryId;
  6920. ec_log_err("SOAP::copyFolder GetObjectFromEntryId failed for %s: %s (%x)", dstEntryIdStr.c_str(), GetMAPIErrorMessage(er), er);
  6921. return er;
  6922. }
  6923. // Get dest store
  6924. er = g_lpSessionManager->GetCacheManager()->GetStore(ulDestFolderId, &ulDestStoreId, NULL);
  6925. if (er != erSuccess) {
  6926. ec_log_err("SOAP::copyFolder GetStore for folder %d failed: %s (%x)", ulDestFolderId, GetMAPIErrorMessage(er), er);
  6927. return er;
  6928. }
  6929. if(ulDestStoreId != ulSourceStoreId) {
  6930. ec_log_err("SOAP::copyFolder copy from/to different stores (from %u to %u) is not supported", ulSourceStoreId, ulDestStoreId);
  6931. assert(false);
  6932. return KCERR_NO_SUPPORT;
  6933. }
  6934. // Check permission
  6935. er = lpecSession->GetSecurity()->CheckPermission(ulDestFolderId, ecSecurityCreateFolder);
  6936. if (er != erSuccess) {
  6937. ec_log_debug("SOAP::copyFolder copy folder (to %u) is not allowed: %s (%x)", ulDestFolderId, GetMAPIErrorMessage(er), er);
  6938. return er;
  6939. }
  6940. if(ulFlags & FOLDER_MOVE ) // is the folder editable?
  6941. er = lpecSession->GetSecurity()->CheckPermission(ulFolderId, ecSecurityFolderAccess);
  6942. else // is the folder readable
  6943. er = lpecSession->GetSecurity()->CheckPermission(ulFolderId, ecSecurityRead);
  6944. if (er != erSuccess) {
  6945. ec_log_debug("SOAP::copyFolder folder (%u) is not editable: %s (%x)", ulFolderId, GetMAPIErrorMessage(er), er);
  6946. return er;
  6947. }
  6948. // Check MAPI_E_FOLDER_CYCLE
  6949. if(ulFolderId == ulDestFolderId) {
  6950. ec_log_err("SOAP::copyFolder target folder (%u) cannot be the same as source folder", ulDestFolderId);
  6951. return KCERR_FOLDER_CYCLE;
  6952. }
  6953. // Get the parent id, for notification and copy
  6954. er = g_lpSessionManager->GetCacheManager()->GetObject(ulFolderId, &ulOldParent, NULL, &ulObjFlags, &ulSourceType);
  6955. if (er != erSuccess) {
  6956. ec_log_err("SOAP::copyFolder cannot get parent folder id for %u: %s (%x)", ulFolderId, GetMAPIErrorMessage(er), er);
  6957. return er;
  6958. }
  6959. er = g_lpSessionManager->GetCacheManager()->GetObject(ulDestFolderId, NULL, NULL, NULL, &ulDestType);
  6960. if (er != erSuccess) {
  6961. ec_log_err("SOAP::copyFolder cannot get type of destination folder (%u): %s (%x)", ulDestFolderId, GetMAPIErrorMessage(er), er);
  6962. return er;
  6963. }
  6964. if (ulSourceType != MAPI_FOLDER || ulDestType != MAPI_FOLDER) {
  6965. const std::string srcEntryIdStr = srcEntryId;
  6966. const std::string dstEntryIdStr = dstEntryId;
  6967. 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());
  6968. return KCERR_INVALID_ENTRYID;
  6969. }
  6970. // Check folder and dest folder are the same
  6971. if (!(ulObjFlags & MSGFLAG_DELETED) && (ulFlags & FOLDER_MOVE) &&
  6972. ulDestFolderId == ulOldParent) {
  6973. ec_log_debug("SOAP::copyFolder destination (%u) == source", ulDestFolderId);
  6974. return erSuccess; // Do nothing... folder already on the right place
  6975. }
  6976. ulParentCycle = ulDestFolderId;
  6977. while (g_lpSessionManager->GetCacheManager()->GetParent(ulParentCycle, &ulParentCycle) == erSuccess) {
  6978. if(ulFolderId == ulParentCycle)
  6979. {
  6980. ec_log_debug("SOAP::copyFolder infinite loop detected for %u", ulDestFolderId);
  6981. return KCERR_FOLDER_CYCLE;
  6982. }
  6983. }
  6984. // Check whether the requested name already exists
  6985. 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);
  6986. if(lpszNewFolderName) {
  6987. name = lpszNewFolderName;
  6988. strQuery+= " AND properties.val_string = '" + lpDatabase->Escape(lpszNewFolderName) + "'";
  6989. } else {
  6990. name = format("%u", ulFolderId);
  6991. 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);
  6992. strQuery+= " AND properties.val_string = ("+strSubQuery+")";
  6993. }
  6994. strQuery += " LIMIT 1";
  6995. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  6996. if (er != erSuccess) {
  6997. ec_log_debug("SOAP::copyFolder check for existing name (%s) failed: %s (%x)", name.c_str(), GetMAPIErrorMessage(er), er);
  6998. return er;
  6999. }
  7000. if(lpDatabase->GetNumRows(lpDBResult) > 0 && !ulSyncId) {
  7001. ec_log_err("SOAP::copyFolder(): target name (%s) already exists", name.c_str());
  7002. return KCERR_COLLISION;
  7003. }
  7004. if(lpszNewFolderName == NULL)
  7005. {
  7006. 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";
  7007. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  7008. if (er != erSuccess) {
  7009. ec_log_err("SOAP::copyFolder(): problem retrieving source name for %u: %s (%x)", ulFolderId, GetMAPIErrorMessage(er), er);
  7010. return er;
  7011. }
  7012. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  7013. if( lpDBRow == NULL || lpDBRow[0] == NULL) {
  7014. ec_log_err("SOAP::copyFolder(): source name (%s) not known", name.c_str());
  7015. return KCERR_NOT_FOUND;
  7016. }
  7017. lpszNewFolderName = s_alloc<char>(soap, strlen(lpDBRow[0])+1);
  7018. memcpy(lpszNewFolderName, lpDBRow[0], strlen(lpDBRow[0])+1);
  7019. }
  7020. //check copy or a move
  7021. if(ulFlags & FOLDER_MOVE ) {
  7022. if (ulObjFlags & MSGFLAG_DELETED) {
  7023. /*
  7024. * The folder we are moving used to be deleted. This
  7025. * effictively makes this call an un-delete. We need to
  7026. * get the folder size for quota management.
  7027. */
  7028. er = GetFolderSize(lpDatabase, ulFolderId, &llFolderSize);
  7029. if (er != erSuccess) {
  7030. ec_log_err("SOAP::copyFolder(): cannot find size of folder %u: %s (%x)", ulFolderId, GetMAPIErrorMessage(er), er);
  7031. return er;
  7032. }
  7033. }
  7034. // Get grandParent of the old folder
  7035. g_lpSessionManager->GetCacheManager()->GetParent(ulOldParent, &ulOldGrandParent);
  7036. er = lpDatabase->Begin();
  7037. if (er != erSuccess) {
  7038. ec_log_err("SOAP::copyFolder(): cannot start transaction: %s (%x)", GetMAPIErrorMessage(er), er);
  7039. return er;
  7040. }
  7041. // Move the folder to the dest. folder
  7042. // FIXME update modtime
  7043. strQuery = "UPDATE hierarchy SET parent="+stringify(ulDestFolderId)+", flags=flags&"+stringify(~MSGFLAG_DELETED)+" WHERE id="+stringify(ulFolderId);
  7044. if ((er = lpDatabase->DoUpdate(strQuery, &ulAffRows)) != erSuccess) {
  7045. lpDatabase->Rollback();
  7046. ec_log_err("SOAP::copyFolder(): update of modification time failed: %s (%x)", GetMAPIErrorMessage(er), er);
  7047. return KCERR_DATABASE_ERROR;
  7048. }
  7049. if(ulAffRows != 1) {
  7050. lpDatabase->Rollback();
  7051. ec_log_err("SOAP::copyFolder(): unexpected number of affected rows (expected: 1, got: %u)", ulAffRows);
  7052. return KCERR_DATABASE_ERROR;
  7053. }
  7054. // Update the folder to the destination folder
  7055. //Info: Always an update, It's not faster first check and than update/or not
  7056. 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);
  7057. if ((er = lpDatabase->DoUpdate(strQuery, &ulAffRows)) != erSuccess) {
  7058. ec_log_err("SOAP::copyFolder(): actual move of folder %s failed: %s (%x)", lpszNewFolderName, GetMAPIErrorMessage(er), er);
  7059. lpDatabase->Rollback();
  7060. return KCERR_DATABASE_ERROR;
  7061. }
  7062. // remove PR_DELETED_ON, as the folder is a softdelete folder
  7063. strQuery = "DELETE FROM properties WHERE hierarchyid="+stringify(ulFolderId)+" AND tag="+stringify(PROP_ID(PR_DELETED_ON))+" AND type="+stringify(PROP_TYPE(PR_DELETED_ON));
  7064. er = lpDatabase->DoDelete(strQuery);
  7065. if(er != erSuccess) {
  7066. ec_log_err("SOAP::copyFolder(): cannot remove PR_DELETED_ON property for %u: %s (%x)", ulFolderId, GetMAPIErrorMessage(er), er);
  7067. lpDatabase->Rollback();
  7068. return KCERR_DATABASE_ERROR;
  7069. }
  7070. // Update the store size if we did an undelete. Note ulSourceStoreId == ulDestStoreId.
  7071. if (llFolderSize > 0) {
  7072. er = UpdateObjectSize(lpDatabase, ulSourceStoreId, MAPI_STORE, UPDATE_ADD, llFolderSize);
  7073. if (er != erSuccess) {
  7074. ec_log_err("SOAP::copyFolder(): problem updating store (%u) size: %s (%x)", ulSourceStoreId, GetMAPIErrorMessage(er), er);
  7075. return er;
  7076. }
  7077. }
  7078. // ICS
  7079. GetSourceKey(ulFolderId, &sSourceKey);
  7080. GetSourceKey(ulDestFolderId, &sDestSourceKey);
  7081. GetSourceKey(ulOldParent, &sParentSourceKey);
  7082. AddChange(lpecSession, ulSyncId, sSourceKey, sParentSourceKey, ICS_FOLDER_CHANGE);
  7083. AddChange(lpecSession, ulSyncId, sSourceKey, sDestSourceKey, ICS_FOLDER_CHANGE);
  7084. // Update folder counters
  7085. if (ulObjFlags & MSGFLAG_DELETED) {
  7086. // Undelete
  7087. er = UpdateFolderCount(lpDatabase, ulOldParent, PR_DELETED_FOLDER_COUNT, -1);
  7088. if (er == erSuccess)
  7089. er = UpdateFolderCount(lpDatabase, ulDestFolderId, PR_SUBFOLDERS, 1);
  7090. if (er == erSuccess)
  7091. er = UpdateFolderCount(lpDatabase, ulDestFolderId, PR_FOLDER_CHILD_COUNT, 1);
  7092. } else {
  7093. // Move
  7094. er = UpdateFolderCount(lpDatabase, ulOldParent, PR_SUBFOLDERS, -1);
  7095. if (er == erSuccess)
  7096. er = UpdateFolderCount(lpDatabase, ulOldParent, PR_FOLDER_CHILD_COUNT, -1);
  7097. if (er == erSuccess)
  7098. er = UpdateFolderCount(lpDatabase, ulDestFolderId, PR_SUBFOLDERS, 1);
  7099. if (er == erSuccess)
  7100. er = UpdateFolderCount(lpDatabase, ulDestFolderId, PR_FOLDER_CHILD_COUNT, 1);
  7101. }
  7102. if (er != erSuccess) {
  7103. ec_log_err("SOAP::copyFolder(): updating folder counts failed: %s (%x)", GetMAPIErrorMessage(er), er);
  7104. return er;
  7105. }
  7106. er = ECTPropsPurge::AddDeferredUpdate(lpecSession, lpDatabase, ulDestFolderId, ulOldParent, ulFolderId);
  7107. if (er != erSuccess) {
  7108. ec_log_err("SOAP::copyFolder(): ECTPropsPurge::AddDeferredUpdate failed: %s (%x)", GetMAPIErrorMessage(er), er);
  7109. return er;
  7110. }
  7111. er = lpDatabase->Commit();
  7112. if(er != erSuccess) {
  7113. ec_log_err("SOAP::copyFolder(): database commit failed: %s (%x)", GetMAPIErrorMessage(er), er);
  7114. lpDatabase->Rollback();
  7115. return er;
  7116. }
  7117. // Cache update for objects
  7118. g_lpSessionManager->GetCacheManager()->Update(fnevObjectMoved, ulFolderId);
  7119. // Notify that the folder has moved
  7120. g_lpSessionManager->NotificationMoved(MAPI_FOLDER, ulFolderId, ulDestFolderId, ulOldParent);
  7121. // Update the old folder
  7122. g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, ulOldParent);
  7123. g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_DELETE, 0, ulOldParent, ulFolderId, MAPI_FOLDER);
  7124. g_lpSessionManager->NotificationModified(MAPI_FOLDER, ulOldParent);
  7125. // Update the old folder's parent
  7126. g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY, 0, ulOldGrandParent, ulOldParent, MAPI_FOLDER);
  7127. // Update the destination folder
  7128. g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, ulDestFolderId);
  7129. g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_ADD, 0, ulDestFolderId, ulFolderId, MAPI_FOLDER);
  7130. g_lpSessionManager->NotificationModified(MAPI_FOLDER, ulDestFolderId);
  7131. // Update the destination's parent
  7132. g_lpSessionManager->GetCacheManager()->GetParent(ulDestFolderId, &ulGrandParent);
  7133. g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY, 0, ulGrandParent, ulDestFolderId, MAPI_FOLDER);
  7134. }else {// a copy
  7135. er = CopyFolderObjects(soap, lpecSession, ulFolderId, ulDestFolderId, lpszNewFolderName, !!(ulFlags&COPY_SUBFOLDERS), ulSyncId);
  7136. if (er != erSuccess) {
  7137. ec_log_err("SOAP::copyFolder(): CopyFolderObjects (src folder: %u, dest folder: %u, new name: \"%s\") failed: %s (%x)", ulFolderId, ulDestFolderId, lpszNewFolderName, GetMAPIErrorMessage(er), er);
  7138. return er;
  7139. }
  7140. }
  7141. return erSuccess;
  7142. }
  7143. SOAP_ENTRY_END()
  7144. SOAP_ENTRY_START(notify, *result, struct notification sNotification, unsigned int *result)
  7145. {
  7146. unsigned int ulKey = 0;
  7147. USE_DATABASE();
  7148. // You are only allowed to send newmail notifications at the moment. This could currently
  7149. // only be misused to send other users new mail popup notification for e-mails that aren't
  7150. // new at all ...
  7151. if (sNotification.ulEventType != fnevNewMail)
  7152. return KCERR_NO_ACCESS;
  7153. if (sNotification.newmail == nullptr ||
  7154. sNotification.newmail->pParentId == nullptr ||
  7155. sNotification.newmail->pEntryId == nullptr)
  7156. return KCERR_INVALID_PARAMETER;
  7157. if (!bSupportUnicode && sNotification.newmail->lpszMessageClass != NULL)
  7158. sNotification.newmail->lpszMessageClass = STRIN_FIX(sNotification.newmail->lpszMessageClass);
  7159. er = lpecSession->GetObjectFromEntryId(sNotification.newmail->pParentId, &ulKey);
  7160. if(er != erSuccess)
  7161. return er;
  7162. sNotification.ulConnection = ulKey;
  7163. return g_lpSessionManager->AddNotification(&sNotification, ulKey);
  7164. }
  7165. SOAP_ENTRY_END()
  7166. SOAP_ENTRY_START(getReceiveFolderTable, lpsReceiveFolderTable->er, entryId sStoreId, struct receiveFolderTableResponse *lpsReceiveFolderTable)
  7167. {
  7168. int ulRows = 0;
  7169. int i;
  7170. unsigned int ulStoreid = 0;
  7171. USE_DATABASE();
  7172. er = lpecSession->GetObjectFromEntryId(&sStoreId, &ulStoreid);
  7173. if(er != erSuccess)
  7174. return er;
  7175. // Check permission
  7176. er = lpecSession->GetSecurity()->CheckPermission(ulStoreid, ecSecurityRead);
  7177. if(er != erSuccess)
  7178. return er;
  7179. strQuery = "SELECT objid, messageclass FROM receivefolder WHERE storeid="+stringify(ulStoreid);
  7180. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  7181. if(er != erSuccess)
  7182. return er;
  7183. ulRows = lpDatabase->GetNumRows(lpDBResult);
  7184. lpsReceiveFolderTable->sFolderArray.__ptr = s_alloc<receiveFolder>(soap, ulRows);
  7185. lpsReceiveFolderTable->sFolderArray.__size = 0;
  7186. i = 0;
  7187. while((lpDBRow = lpDatabase->FetchRow(lpDBResult)) )
  7188. {
  7189. if(lpDBRow == NULL || lpDBRow[0] == NULL || lpDBRow[1] == NULL){
  7190. ec_log_err("getReceiveFolderTable(): row or col null");
  7191. return KCERR_DATABASE_ERROR;
  7192. }
  7193. er = g_lpSessionManager->GetCacheManager()->GetEntryIdFromObject(atoui(lpDBRow[0]), soap, 0, &lpsReceiveFolderTable->sFolderArray.__ptr[i].sEntryId);
  7194. if(er != erSuccess){
  7195. er = erSuccess;
  7196. continue;
  7197. }
  7198. lpsReceiveFolderTable->sFolderArray.__ptr[i].lpszAExplicitClass = STROUT_FIX_CPY(lpDBRow[1]);
  7199. ++i;
  7200. }
  7201. lpsReceiveFolderTable->sFolderArray.__size = i;
  7202. return erSuccess;
  7203. }
  7204. SOAP_ENTRY_END()
  7205. SOAP_ENTRY_START(deleteUser, *result, unsigned int ulUserId, entryId sUserId, unsigned int *result)
  7206. {
  7207. er = GetLocalId(sUserId, ulUserId, &ulUserId, NULL);
  7208. if (er != erSuccess)
  7209. return er;
  7210. // Check permission
  7211. er = lpecSession->GetSecurity()->IsAdminOverUserObject(ulUserId);
  7212. if(er != erSuccess)
  7213. return er;
  7214. return lpecSession->GetUserManagement()->DeleteObjectAndSync(ulUserId);
  7215. }
  7216. SOAP_ENTRY_END()
  7217. SOAP_ENTRY_START(unhookStore, *result, unsigned int ulStoreType, entryId sUserId, unsigned int ulSyncId, unsigned int *result)
  7218. {
  7219. unsigned int ulUserId = 0;
  7220. unsigned int ulAffected = 0;
  7221. std::string strGUID = "Unknown";
  7222. USE_DATABASE();
  7223. // do not use GetLocalId since the user may exist on a different server,
  7224. // but will be migrated here and we need to remove the previous store with different guid.
  7225. er = ABEntryIDToID(&sUserId, &ulUserId, NULL, NULL);
  7226. if(er != erSuccess)
  7227. goto exit;
  7228. if (ulUserId == 0 || ulUserId == KOPANO_UID_SYSTEM || !ECSTORE_TYPE_ISVALID(ulStoreType))
  7229. {
  7230. er = KCERR_INVALID_PARAMETER;
  7231. goto exit;
  7232. }
  7233. er = lpDatabase->Begin();
  7234. if (er != erSuccess)
  7235. goto exit;
  7236. strQuery = "SELECT guid FROM stores WHERE user_id=" + stringify(ulUserId) + " AND type=" + stringify(ulStoreType) + " LIMIT 1";
  7237. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  7238. if (er != erSuccess)
  7239. goto exit;
  7240. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  7241. lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
  7242. if (lpDBRow == NULL || lpDBRow[0] == NULL) {
  7243. // store not on this server
  7244. er = KCERR_NOT_FOUND;
  7245. goto exit;
  7246. }
  7247. strGUID = bin2hex(lpDBLen[0], (unsigned char*)lpDBRow[0]);
  7248. strQuery = "UPDATE stores SET user_id=0 WHERE user_id=" + stringify(ulUserId) + " AND type=" + stringify(ulStoreType);
  7249. er = lpDatabase->DoUpdate(strQuery, &ulAffected);
  7250. if (er != erSuccess)
  7251. goto exit;
  7252. // ulAffected == 0: The user was already orphaned
  7253. // ulAffected == 1: correctly disowned owner of store
  7254. if (ulAffected > 1) {
  7255. er = KCERR_COLLISION;
  7256. ec_log_err("unhookStore(): more than expected");
  7257. goto exit;
  7258. }
  7259. er = lpDatabase->Commit();
  7260. if (er != erSuccess)
  7261. goto exit;
  7262. exit:
  7263. if (er != erSuccess)
  7264. 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);
  7265. else
  7266. ec_log_err("Unhook of store (type %d) with userid %d and GUID %s succeeded", ulStoreType, ulUserId, strGUID.c_str());
  7267. ROLLBACK_ON_ERROR();
  7268. }
  7269. SOAP_ENTRY_END()
  7270. SOAP_ENTRY_START(hookStore, *result, unsigned int ulStoreType, entryId sUserId, struct xsd__base64Binary sStoreGuid, unsigned int ulSyncId, unsigned int *result)
  7271. {
  7272. unsigned int ulUserId = 0;
  7273. unsigned int ulAffected = 0;
  7274. objectdetails_t sUserDetails;
  7275. USE_DATABASE();
  7276. // do not use GetLocalId since the user may exist on a different server,
  7277. // but will be migrated here and we need to hook an old store with specified guid.
  7278. er = ABEntryIDToID(&sUserId, &ulUserId, NULL, NULL);
  7279. if(er != erSuccess)
  7280. goto exit;
  7281. if (ulUserId == 0 || ulUserId == KOPANO_UID_SYSTEM || !ECSTORE_TYPE_ISVALID(ulStoreType))
  7282. {
  7283. er = KCERR_INVALID_PARAMETER;
  7284. goto exit;
  7285. }
  7286. // get user details, see if this is the correct server
  7287. er = lpecSession->GetUserManagement()->GetObjectDetails(ulUserId, &sUserDetails);
  7288. if (er != erSuccess)
  7289. goto exit;
  7290. // check if store currently is owned and the correct type
  7291. 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 = ";
  7292. strQuery += lpDatabase->EscapeBinary(sStoreGuid.__ptr, sStoreGuid.__size);
  7293. strQuery += " LIMIT 1";
  7294. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  7295. if (er != erSuccess)
  7296. goto exit;
  7297. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  7298. lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
  7299. if(lpDBRow == NULL || lpDBLen == NULL) {
  7300. er = KCERR_NOT_FOUND;
  7301. goto exit;
  7302. }
  7303. if (lpDBRow[4] == NULL) {
  7304. er = KCERR_DATABASE_ERROR;
  7305. ec_log_err("hookStore(): col null");
  7306. goto exit;
  7307. }
  7308. if (lpDBRow[0]) {
  7309. // this store already belongs to a user
  7310. er = KCERR_COLLISION;
  7311. ec_log_err("hookStore(): store already belongs to a user");
  7312. goto exit;
  7313. }
  7314. if (atoui(lpDBRow[4]) != ulStoreType) {
  7315. ec_log_err("Requested store type is %u, actual store type is %s", ulStoreType, lpDBRow[4]);
  7316. er = KCERR_INVALID_TYPE;
  7317. goto exit;
  7318. }
  7319. ec_log_info("Hooking store \"%s\" to user %d", lpDBRow[1], ulUserId);
  7320. // 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.
  7321. // update user with new store id
  7322. er = lpDatabase->Begin();
  7323. if (er != erSuccess)
  7324. goto exit;
  7325. // remove previous user of store
  7326. strQuery = "UPDATE stores SET user_id = " + string(lpDBRow[2]) + " WHERE user_id = " + stringify(ulUserId) + " AND type = " + stringify(ulStoreType);
  7327. er = lpDatabase->DoUpdate(strQuery, &ulAffected);
  7328. if (er != erSuccess)
  7329. goto exit;
  7330. // ulAffected == 0: The user was already orphaned
  7331. // ulAffected == 1: correctly disowned previous owner of store
  7332. if (ulAffected > 1) {
  7333. er = KCERR_COLLISION;
  7334. ec_log_err("hookStore(): owned by multiple users");
  7335. goto exit;
  7336. }
  7337. // set new store
  7338. strQuery = "UPDATE stores SET user_id = " + stringify(ulUserId) + " WHERE guid = ";
  7339. strQuery += lpDatabase->EscapeBinary(sStoreGuid.__ptr, sStoreGuid.__size);
  7340. er = lpDatabase->DoUpdate(strQuery, &ulAffected);
  7341. if (er != erSuccess)
  7342. goto exit;
  7343. // we can't have one store being owned by multiple users
  7344. if (ulAffected != 1) {
  7345. er = KCERR_COLLISION;
  7346. ec_log_err("hookStore(): owned by multiple users (2)");
  7347. goto exit;
  7348. }
  7349. // update owner of store
  7350. strQuery = "UPDATE hierarchy SET owner = " + stringify(ulUserId) + " WHERE id = " + string(lpDBRow[3]);
  7351. er = lpDatabase->DoUpdate(strQuery, &ulAffected);
  7352. if (er != erSuccess)
  7353. goto exit;
  7354. // one store has only one entry point in the hierarchy
  7355. // (may be zero, when the user returns to its original store, so the owner field stays the same)
  7356. if (ulAffected > 1) {
  7357. er = KCERR_COLLISION;
  7358. ec_log_err("hookStore(): owned by multiple users (3)");
  7359. goto exit;
  7360. }
  7361. er = lpDatabase->Commit();
  7362. if (er != erSuccess)
  7363. goto exit;
  7364. // remove store cache item
  7365. g_lpSessionManager->GetCacheManager()->Update(fnevObjectMoved, atoi(lpDBRow[3]));
  7366. exit:
  7367. if (er != erSuccess)
  7368. ec_log_err("Hook of store failed: 0x%x", er);
  7369. ROLLBACK_ON_ERROR();
  7370. }
  7371. SOAP_ENTRY_END()
  7372. // Used to move the store to the public, but this isn't possible due to multi-server setups.
  7373. // And is was super slow because of the storeid column in the properties table.
  7374. SOAP_ENTRY_START(deleteStore, *result, unsigned int ulStoreId, unsigned int ulSyncId, unsigned int *result)
  7375. {
  7376. er = KCERR_NOT_IMPLEMENTED;
  7377. }
  7378. SOAP_ENTRY_END()
  7379. // softdelete the store from the database, so this function returns quickly
  7380. SOAP_ENTRY_START(removeStore, *result, struct xsd__base64Binary sStoreGuid, unsigned int ulSyncId, unsigned int *result)
  7381. {
  7382. unsigned int ulCompanyId = 0;
  7383. unsigned int ulStoreHierarchyId = 0;
  7384. objectdetails_t sObjectDetails;
  7385. std::string strUsername;
  7386. USE_DATABASE();
  7387. // find store id and company of guid
  7388. 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 = ";
  7389. strQuery += lpDatabase->EscapeBinary(sStoreGuid.__ptr, sStoreGuid.__size);
  7390. strQuery += " LIMIT 1";
  7391. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  7392. if (er != erSuccess)
  7393. goto exit;
  7394. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  7395. lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
  7396. if(lpDBRow == NULL || lpDBLen == NULL) {
  7397. er = KCERR_NOT_FOUND;
  7398. goto exit;
  7399. }
  7400. // 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.
  7401. if (lpDBRow[0] != NULL) {
  7402. // trying to remove store from existing user
  7403. strUsername = lpDBRow[4];
  7404. if (lpecSession->GetUserManagement()->GetObjectDetails(atoi(lpDBRow[0]), &sObjectDetails) == erSuccess)
  7405. strUsername = sObjectDetails.GetPropString(OB_PROP_S_LOGIN); // fullname?
  7406. ec_log_err("Unable to remove store: store is in use by user \"%s\"", strUsername.c_str());
  7407. er = KCERR_COLLISION;
  7408. goto exit;
  7409. }
  7410. // these are all 'not null' columns
  7411. ulStoreHierarchyId = atoi(lpDBRow[2]);
  7412. ulCompanyId = atoi(lpDBRow[3]);
  7413. // Must be administrator over the company to be able to remove the store
  7414. er = lpecSession->GetSecurity()->IsAdminOverUserObject(ulCompanyId);
  7415. if (er != erSuccess)
  7416. goto exit;
  7417. 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]);
  7418. er = lpDatabase->Begin();
  7419. if(er != hrSuccess)
  7420. goto exit;
  7421. // Soft delete store
  7422. er = MarkStoreAsDeleted(lpecSession, lpDatabase, ulStoreHierarchyId, ulSyncId);
  7423. if(er != erSuccess)
  7424. goto exit;
  7425. // Remove the store entry
  7426. strQuery = "DELETE FROM stores WHERE guid=" + lpDatabase->EscapeBinary((unsigned char*)lpDBRow[1], lpDBLen[1]);
  7427. er = lpDatabase->DoDelete(strQuery);
  7428. if(er != erSuccess)
  7429. goto exit;
  7430. // Remove receivefolder entries
  7431. strQuery = "DELETE FROM receivefolder WHERE storeid="+stringify(ulStoreHierarchyId);
  7432. er = lpDatabase->DoDelete(strQuery);
  7433. if(er != erSuccess)
  7434. goto exit;
  7435. // Remove the acls
  7436. strQuery = "DELETE FROM acl WHERE hierarchy_id="+stringify(ulStoreHierarchyId);
  7437. er = lpDatabase->DoDelete(strQuery);
  7438. if(er != erSuccess)
  7439. goto exit;
  7440. // TODO: acl cache!
  7441. er = lpDatabase->Commit();
  7442. if(er != erSuccess)
  7443. goto exit;
  7444. ec_log_info("Finished remove store (%s)", bin2hex(lpDBLen[1], reinterpret_cast<const unsigned char *>(lpDBRow[1])).c_str());
  7445. exit:
  7446. if(er == KCERR_NO_ACCESS)
  7447. ec_log_err("Failed to remove store access denied");
  7448. else if(er != erSuccess) {
  7449. lpDatabase->Rollback();
  7450. ec_log_err("Failed to remove store, errorcode=0x%08X", er);
  7451. }
  7452. }
  7453. SOAP_ENTRY_END()
  7454. namespace KC {
  7455. void *SoftDeleteRemover(void *lpTmpMain)
  7456. {
  7457. ECRESULT er = erSuccess;
  7458. ECRESULT* lper = NULL;
  7459. const char *lpszSetting = NULL;
  7460. unsigned int ulDeleteTime = 0;
  7461. unsigned int ulFolders = 0;
  7462. unsigned int ulStores = 0;
  7463. unsigned int ulMessages = 0;
  7464. ECSession *lpecSession = NULL;
  7465. lpszSetting = g_lpSessionManager->GetConfig()->GetSetting("softdelete_lifetime");
  7466. if(lpszSetting)
  7467. ulDeleteTime = atoi(lpszSetting) * 24 * 60 * 60;
  7468. if(ulDeleteTime == 0)
  7469. goto exit;
  7470. er = g_lpSessionManager->CreateSessionInternal(&lpecSession);
  7471. if(er != erSuccess)
  7472. goto exit;
  7473. // Lock the session
  7474. lpecSession->Lock();
  7475. ec_log_info("Start scheduled softdelete clean up");
  7476. er = PurgeSoftDelete(lpecSession, ulDeleteTime, &ulMessages, &ulFolders, &ulStores, (bool*)lpTmpMain);
  7477. exit:
  7478. if(ulDeleteTime > 0) {
  7479. if (er == erSuccess)
  7480. ec_log_info("Softdelete done: removed %d stores, %d folders, and %d messages", ulStores, ulFolders, ulMessages);
  7481. else if (er == KCERR_BUSY)
  7482. ec_log_info("Softdelete already running");
  7483. else
  7484. ec_log_info("Softdelete failed: removed %d stores, %d folders, and %d messages", ulStores, ulFolders, ulMessages);
  7485. }
  7486. if(lpecSession) {
  7487. lpecSession->Unlock();
  7488. g_lpSessionManager->RemoveSessionInternal(lpecSession);
  7489. }
  7490. // Exit with the error result
  7491. lper = new ECRESULT;
  7492. *lper = er;
  7493. return (void *)lper;
  7494. }
  7495. } /* namespace */
  7496. SOAP_ENTRY_START(checkExistObject, *result, entryId sEntryId, unsigned int ulFlags, unsigned int *result)
  7497. {
  7498. unsigned int ulObjId = 0;
  7499. unsigned int ulObjType = 0;
  7500. unsigned int ulDBFlags = 0;
  7501. USE_DATABASE();
  7502. er = BeginLockFolders(lpDatabase, EntryId(sEntryId), LOCK_SHARED);
  7503. if(er != erSuccess)
  7504. goto exit;
  7505. er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulObjId);
  7506. if(er != erSuccess)
  7507. goto exit;
  7508. er = g_lpSessionManager->GetCacheManager()->GetObject(ulObjId, NULL, NULL, &ulDBFlags, &ulObjType);
  7509. if(er != erSuccess)
  7510. goto exit;
  7511. if(ulFlags & SHOW_SOFT_DELETES) {
  7512. if(!(ulDBFlags & MSGFLAG_DELETED)) {
  7513. er = KCERR_NOT_FOUND;
  7514. goto exit;
  7515. }
  7516. } else {
  7517. if(ulDBFlags & MSGFLAG_DELETED) {
  7518. er = KCERR_NOT_FOUND;
  7519. goto exit;
  7520. }
  7521. }
  7522. exit:
  7523. lpDatabase->Commit();
  7524. }
  7525. SOAP_ENTRY_END()
  7526. SOAP_ENTRY_START(readABProps, readPropsResponse->er, entryId sEntryId, struct readPropsResponse *readPropsResponse)
  7527. {
  7528. // FIXME: when props are PT_ERROR, they shouldn't be send to the client
  7529. // now we have properties in the client which are MAPI_E_NOT_ENOUGH_MEMORY,
  7530. // while they shouldn't be present (or atleast MAPI_E_NOT_FOUND)
  7531. // These properties must be of type PT_UNICODE for string properties
  7532. unsigned int sProps[] = {
  7533. /* Don't touch the order of the first 7 elements!!! */
  7534. PR_ENTRYID, PR_CONTAINER_FLAGS, PR_DEPTH, PR_EMS_AB_CONTAINERID, PR_DISPLAY_NAME, PR_EMS_AB_IS_MASTER, PR_EMS_AB_PARENT_ENTRYID,
  7535. PR_EMAIL_ADDRESS, PR_OBJECT_TYPE, PR_DISPLAY_TYPE, PR_SEARCH_KEY, PR_PARENT_ENTRYID, PR_ADDRTYPE, PR_RECORD_KEY, PR_ACCOUNT,
  7536. PR_SMTP_ADDRESS, PR_TRANSMITABLE_DISPLAY_NAME, PR_EMS_AB_HOME_MDB, PR_EMS_AB_HOME_MTA, PR_EMS_AB_PROXY_ADDRESSES,
  7537. PR_EC_ADMINISTRATOR, PR_EC_NONACTIVE, PR_EC_COMPANY_NAME, PR_EMS_AB_X509_CERT, PR_AB_PROVIDER_ID, PR_EMS_AB_HIERARCHY_PATH,
  7538. 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),
  7539. 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,
  7540. PR_ASSISTANT
  7541. };
  7542. unsigned int sPropsContainerRoot[] = {
  7543. /* Don't touch the order of the first 7 elements!!! */
  7544. PR_ENTRYID, PR_CONTAINER_FLAGS, PR_DEPTH, PR_EMS_AB_CONTAINERID, PR_DISPLAY_NAME, PR_EMS_AB_IS_MASTER, PR_EMS_AB_PARENT_ENTRYID,
  7545. 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,
  7546. PR_EC_HOMESERVER_NAME, PR_EC_COMPANY_NAME
  7547. };
  7548. struct propTagArray ptaProps;
  7549. unsigned int ulId = 0;
  7550. unsigned int ulTypeId = 0;
  7551. ECDatabase* lpDatabase = NULL;
  7552. objectid_t sExternId;
  7553. std::unique_ptr<abprops_t> lExtraProps;
  7554. unsigned int *lpProps = NULL;
  7555. unsigned int ulProps = 0;
  7556. int i = 0;
  7557. er = lpecSession->GetDatabase(&lpDatabase);
  7558. if (er != erSuccess)
  7559. return er;
  7560. er = ABEntryIDToID(&sEntryId, &ulId, &sExternId, &ulTypeId);
  7561. if(er != erSuccess)
  7562. return er;
  7563. // A v1 EntryID would return a non-empty extern id string.
  7564. if (!sExternId.id.empty())
  7565. {
  7566. er = lpecSession->GetSessionManager()->GetCacheManager()->GetUserObject(sExternId, &ulId, NULL, NULL);
  7567. if (er != erSuccess)
  7568. return er;
  7569. }
  7570. er = lpecSession->GetSecurity()->IsUserObjectVisible(ulId);
  7571. if (er != erSuccess)
  7572. return er;
  7573. if (ulTypeId == MAPI_ABCONT) {
  7574. lpProps = sPropsContainerRoot;
  7575. ulProps = ARRAY_SIZE(sPropsContainerRoot);
  7576. } else if (ulTypeId == MAPI_MAILUSER || ulTypeId == MAPI_DISTLIST) {
  7577. lpProps = sProps;
  7578. ulProps = ARRAY_SIZE(sProps);
  7579. } else {
  7580. return KCERR_INVALID_PARAMETER;
  7581. }
  7582. /* Load the additional addressbook properties */
  7583. try {
  7584. UserPlugin *lpPlugin = NULL;
  7585. if (GetThreadLocalPlugin(g_lpSessionManager->GetPluginFactory(), &lpPlugin) == erSuccess)
  7586. lExtraProps = lpPlugin->getExtraAddressbookProperties();
  7587. } catch (...) { }
  7588. ptaProps.__size = ulProps;
  7589. if (lExtraProps.get())
  7590. ptaProps.__size += lExtraProps->size();
  7591. ptaProps.__ptr = s_alloc<unsigned int>(soap, ptaProps.__size);
  7592. /* Copy fixed properties */
  7593. memcpy(ptaProps.__ptr, lpProps, ulProps * sizeof(unsigned int));
  7594. i = ulProps;
  7595. /* Copy extra properties */
  7596. if (lExtraProps.get()) {
  7597. for (const auto &prop : *lExtraProps) {
  7598. ptaProps.__ptr[i] = prop;
  7599. /* The client requires some properties with non-standard types */
  7600. switch ( PROP_ID(ptaProps.__ptr[i]) ) {
  7601. case PROP_ID(PR_MANAGER_NAME):
  7602. case PROP_ID(PR_EMS_AB_MANAGER):
  7603. /* Rename PR_MANAGER_NAME to PR_EMS_AB_MANAGER and provide the PT_BINARY version with the entryid */
  7604. ptaProps.__ptr[i] = CHANGE_PROP_TYPE(PR_EMS_AB_MANAGER, PT_BINARY);
  7605. break;
  7606. case PROP_ID(PR_EMS_AB_REPORTS):
  7607. ptaProps.__ptr[i] = CHANGE_PROP_TYPE(PR_EMS_AB_REPORTS, PT_MV_BINARY);
  7608. break;
  7609. case PROP_ID(PR_EMS_AB_OWNER):
  7610. /* Also provide the PT_BINARY version with the entryid */
  7611. ptaProps.__ptr[i] = CHANGE_PROP_TYPE(PR_EMS_AB_OWNER, PT_BINARY);
  7612. break;
  7613. default:
  7614. // @note plugin most likely returns PT_STRING8 and PT_MV_STRING8 types
  7615. // Since CopyDatabasePropValToSOAPPropVal() always returns PT_UNICODE types, we will convert these here too
  7616. // Therefore, the sProps / sPropsContainerRoot must contain PT_UNICODE types only!
  7617. if (PROP_TYPE(ptaProps.__ptr[i]) == PT_MV_STRING8)
  7618. ptaProps.__ptr[i] = CHANGE_PROP_TYPE(ptaProps.__ptr[i], PT_MV_UNICODE);
  7619. else if (PROP_TYPE(ptaProps.__ptr[i]) == PT_STRING8)
  7620. ptaProps.__ptr[i] = CHANGE_PROP_TYPE(ptaProps.__ptr[i], PT_UNICODE);
  7621. break;
  7622. }
  7623. ++i;
  7624. }
  7625. }
  7626. /* Update the total size, the previously set value might not be accurate */
  7627. ptaProps.__size = i;
  7628. /* Read properties */
  7629. if (ulTypeId == MAPI_ABCONT) {
  7630. er = lpecSession->GetUserManagement()->GetContainerProps(soap, ulId, &ptaProps, &readPropsResponse->aPropVal);
  7631. if (er != erSuccess)
  7632. return er;
  7633. } else {
  7634. er = lpecSession->GetUserManagement()->GetProps(soap, ulId, &ptaProps, &readPropsResponse->aPropVal);
  7635. if (er != erSuccess)
  7636. return er;
  7637. }
  7638. /* Copy properties which have been correctly read to tag array */
  7639. readPropsResponse->aPropTag.__size = 0;
  7640. readPropsResponse->aPropTag.__ptr = s_alloc<unsigned int>(soap, ptaProps.__size);
  7641. for (gsoap_size_t i = 0; i < readPropsResponse->aPropVal.__size; ++i) {
  7642. if (!bSupportUnicode) {
  7643. er = FixPropEncoding(soap, stringCompat, Out, readPropsResponse->aPropVal.__ptr + i);
  7644. if (er != erSuccess)
  7645. return er;
  7646. }
  7647. if(PROP_TYPE(readPropsResponse->aPropVal.__ptr[i].ulPropTag) != PT_ERROR)
  7648. readPropsResponse->aPropTag.__ptr[readPropsResponse->aPropTag.__size++] = readPropsResponse->aPropVal.__ptr[i].ulPropTag;
  7649. }
  7650. return erSuccess;
  7651. }
  7652. SOAP_ENTRY_END()
  7653. SOAP_ENTRY_START(writeABProps, *result, entryId sEntryId, struct propValArray *aPropVal, unsigned int *result)
  7654. {
  7655. er = KCERR_NOT_FOUND;
  7656. }
  7657. SOAP_ENTRY_END()
  7658. SOAP_ENTRY_START(deleteABProps, *result, entryId sEntryId, struct propTagArray *lpsPropTags, unsigned int *result)
  7659. {
  7660. er = KCERR_NOT_FOUND;
  7661. }
  7662. SOAP_ENTRY_END()
  7663. SOAP_ENTRY_START(loadABProp, lpsResponse->er, entryId sEntryId, unsigned int ulPropTag, struct loadPropResponse *lpsResponse)
  7664. {
  7665. return SOAP_OK;
  7666. }
  7667. SOAP_ENTRY_END()
  7668. /**
  7669. * ns__abResolveNames
  7670. *
  7671. * @param[in] lpaPropTag SOAP proptag array containing requested properties.
  7672. * @param[in] lpsRowSet Rows with possible search request, if matching flag in lpaFlags is MAPI_UNRESOLVED.
  7673. * @param[in] lpaFlags Status of row.
  7674. * @param[in] ulFlags Client ulFlags for IABContainer::ResolveNames()
  7675. * @param[out] lpsABResolveNames copies of new rows and flags.
  7676. */
  7677. SOAP_ENTRY_START(abResolveNames, lpsABResolveNames->er, struct propTagArray* lpaPropTag, struct rowSet* lpsRowSet, struct flagArray* lpaFlags, unsigned int ulFlags, struct abResolveNamesResponse* lpsABResolveNames)
  7678. {
  7679. unsigned int ulFlag = 0;
  7680. unsigned int ulObjectId = 0;
  7681. char* search = NULL;
  7682. struct propValArray sPropValArrayDst;
  7683. struct propVal *lpDisplayName = NULL;
  7684. lpsABResolveNames->aFlags.__size = lpaFlags->__size;
  7685. lpsABResolveNames->aFlags.__ptr = s_alloc<unsigned int>(soap, lpaFlags->__size);
  7686. memset(lpsABResolveNames->aFlags.__ptr, 0, sizeof(unsigned int) * lpaFlags->__size);
  7687. lpsABResolveNames->sRowSet.__size = lpsRowSet->__size;
  7688. lpsABResolveNames->sRowSet.__ptr = s_alloc<propValArray>(soap, lpsRowSet->__size);
  7689. memset(lpsABResolveNames->sRowSet.__ptr, 0, sizeof(struct propValArray) * lpsRowSet->__size);
  7690. if (!bSupportUnicode) {
  7691. er = FixRowSetEncoding(soap, stringCompat, In, lpsRowSet);
  7692. if (er != erSuccess)
  7693. return er;
  7694. }
  7695. for (gsoap_size_t i = 0; i < lpsRowSet->__size; ++i) {
  7696. lpsABResolveNames->aFlags.__ptr[i] = lpaFlags->__ptr[i];
  7697. if(lpaFlags->__ptr[i] == MAPI_RESOLVED)
  7698. continue; // Client knows the information
  7699. lpDisplayName = FindProp(&lpsRowSet->__ptr[i], CHANGE_PROP_TYPE(PR_DISPLAY_NAME, PT_UNSPECIFIED));
  7700. if(lpDisplayName == NULL || (PROP_TYPE(lpDisplayName->ulPropTag) != PT_STRING8 && PROP_TYPE(lpDisplayName->ulPropTag) != PT_UNICODE))
  7701. continue; // No display name
  7702. /* Blackberry likes it to put a '=' in front of the username */
  7703. search = strrchr(lpDisplayName->Value.lpszA, '=');
  7704. if (search) {
  7705. ++search;
  7706. ulFlags |= EMS_AB_ADDRESS_LOOKUP;
  7707. } else {
  7708. search = lpDisplayName->Value.lpszA;
  7709. }
  7710. /* NOTE: ECUserManagement is responsible for calling ECSecurity::IsUserObjectVisible(ulObjectId) for found objects */
  7711. switch (lpecSession->GetUserManagement()->SearchObjectAndSync(search, ulFlags, &ulObjectId)) {
  7712. case KCERR_COLLISION:
  7713. ulFlag = MAPI_AMBIGUOUS;
  7714. break;
  7715. case erSuccess:
  7716. ulFlag = MAPI_RESOLVED;
  7717. break;
  7718. case KCERR_NOT_FOUND:
  7719. default:
  7720. ulFlag = MAPI_UNRESOLVED;
  7721. break;
  7722. }
  7723. lpsABResolveNames->aFlags.__ptr[i] = ulFlag;
  7724. if(lpsABResolveNames->aFlags.__ptr[i] == MAPI_RESOLVED) {
  7725. er = lpecSession->GetUserManagement()->GetProps(soap, ulObjectId, lpaPropTag, &sPropValArrayDst);
  7726. if(er != erSuccess)
  7727. return er;
  7728. er = MergePropValArray(soap, &lpsRowSet->__ptr[i], &sPropValArrayDst, &lpsABResolveNames->sRowSet.__ptr[i]);
  7729. if(er != erSuccess)
  7730. return er;
  7731. }
  7732. }
  7733. if (!bSupportUnicode) {
  7734. er = FixRowSetEncoding(soap, stringCompat, Out, &lpsABResolveNames->sRowSet);
  7735. if (er != erSuccess)
  7736. return er;
  7737. }
  7738. return erSuccess;
  7739. }
  7740. SOAP_ENTRY_END()
  7741. /**
  7742. * Syncs a new list of companies, and for each company syncs the users.
  7743. *
  7744. * @param[in] ulCompanyId unused, id of company to sync
  7745. * @param[in] sCompanyId unused, entryid of company to sync
  7746. * @param[out] result kopano error code
  7747. *
  7748. * @return soap error code
  7749. */
  7750. SOAP_ENTRY_START(syncUsers, *result, unsigned int ulCompanyId, entryId sCompanyId, unsigned int *result)
  7751. {
  7752. er = lpecSession->GetUserManagement()->SyncAllObjects();
  7753. }
  7754. SOAP_ENTRY_END()
  7755. // Quota
  7756. SOAP_ENTRY_START(GetQuota, lpsQuota->er, unsigned int ulUserid, entryId sUserId, bool bGetUserDefault, struct quotaResponse* lpsQuota)
  7757. {
  7758. quotadetails_t quotadetails;
  7759. er = GetLocalId(sUserId, ulUserid, &ulUserid, NULL);
  7760. if (er != erSuccess)
  7761. return er;
  7762. // Check permission
  7763. if(lpecSession->GetSecurity()->IsAdminOverUserObject(ulUserid) != erSuccess &&
  7764. (lpecSession->GetSecurity()->GetUserId() != ulUserid))
  7765. return KCERR_NO_ACCESS;
  7766. er = lpecSession->GetSecurity()->GetUserQuota(ulUserid, bGetUserDefault, &quotadetails);
  7767. if(er != erSuccess)
  7768. return er;
  7769. lpsQuota->sQuota.bUseDefaultQuota = quotadetails.bUseDefaultQuota;
  7770. lpsQuota->sQuota.bIsUserDefaultQuota = quotadetails.bIsUserDefaultQuota;
  7771. lpsQuota->sQuota.llHardSize = quotadetails.llHardSize;
  7772. lpsQuota->sQuota.llSoftSize = quotadetails.llSoftSize;
  7773. lpsQuota->sQuota.llWarnSize = quotadetails.llWarnSize;
  7774. return erSuccess;
  7775. }
  7776. SOAP_ENTRY_END()
  7777. SOAP_ENTRY_START(SetQuota, *result, unsigned int ulUserid, entryId sUserId, struct quota* lpsQuota, unsigned int *result)
  7778. {
  7779. quotadetails_t quotadetails;
  7780. er = GetLocalId(sUserId, ulUserid, &ulUserid, NULL);
  7781. if (er != erSuccess)
  7782. return er;
  7783. // Check permission
  7784. if(lpecSession->GetSecurity()->IsAdminOverUserObject(ulUserid) != erSuccess &&
  7785. (lpecSession->GetSecurity()->GetUserId() != ulUserid))
  7786. return KCERR_NO_ACCESS;
  7787. quotadetails.bUseDefaultQuota = lpsQuota->bUseDefaultQuota;
  7788. quotadetails.bIsUserDefaultQuota = lpsQuota->bIsUserDefaultQuota;
  7789. quotadetails.llHardSize = lpsQuota->llHardSize;
  7790. quotadetails.llSoftSize = lpsQuota->llSoftSize;
  7791. quotadetails.llWarnSize = lpsQuota->llWarnSize;
  7792. return lpecSession->GetUserManagement()->SetQuotaDetailsAndSync(ulUserid, quotadetails);
  7793. }
  7794. SOAP_ENTRY_END()
  7795. SOAP_ENTRY_START(AddQuotaRecipient, *result, unsigned int ulCompanyid, entryId sCompanyId, unsigned int ulRecipientId, entryId sRecipientId, unsigned int ulType, unsigned int *result);
  7796. {
  7797. er = GetLocalId(sCompanyId, ulCompanyid, &ulCompanyid, NULL);
  7798. if (er != erSuccess)
  7799. return er;
  7800. if (lpecSession->GetSecurity()->IsAdminOverUserObject(ulCompanyid) != erSuccess)
  7801. return KCERR_NO_ACCESS;
  7802. er = GetLocalId(sRecipientId, ulRecipientId, &ulRecipientId, NULL);
  7803. if (er != erSuccess)
  7804. return er;
  7805. if (OBJECTCLASS_TYPE(ulType) == OBJECTTYPE_MAILUSER)
  7806. er = lpecSession->GetUserManagement()->AddSubObjectToObjectAndSync(OBJECTRELATION_QUOTA_USERRECIPIENT, ulCompanyid, ulRecipientId);
  7807. else if (ulType == CONTAINER_COMPANY)
  7808. er = lpecSession->GetUserManagement()->AddSubObjectToObjectAndSync(OBJECTRELATION_QUOTA_COMPANYRECIPIENT, ulCompanyid, ulRecipientId);
  7809. else
  7810. er = KCERR_INVALID_TYPE;
  7811. return er;
  7812. }
  7813. SOAP_ENTRY_END()
  7814. SOAP_ENTRY_START(DeleteQuotaRecipient, *result, unsigned int ulCompanyid, entryId sCompanyId, unsigned int ulRecipientId, entryId sRecipientId, unsigned int ulType, unsigned int *result);
  7815. {
  7816. er = GetLocalId(sCompanyId, ulCompanyid, &ulCompanyid, NULL);
  7817. if (er != erSuccess)
  7818. return er;
  7819. if (lpecSession->GetSecurity()->IsAdminOverUserObject(ulCompanyid) != erSuccess)
  7820. return KCERR_NO_ACCESS;
  7821. er = GetLocalId(sRecipientId, ulRecipientId, &ulRecipientId, NULL);
  7822. if (er != erSuccess)
  7823. return er;
  7824. if (OBJECTCLASS_TYPE(ulType) == OBJECTTYPE_MAILUSER)
  7825. er = lpecSession->GetUserManagement()->DeleteSubObjectFromObjectAndSync(OBJECTRELATION_QUOTA_USERRECIPIENT, ulCompanyid, ulRecipientId);
  7826. else if (ulType == CONTAINER_COMPANY)
  7827. er = lpecSession->GetUserManagement()->DeleteSubObjectFromObjectAndSync(OBJECTRELATION_QUOTA_COMPANYRECIPIENT, ulCompanyid, ulRecipientId);
  7828. else
  7829. er = KCERR_INVALID_TYPE;
  7830. return er;
  7831. }
  7832. SOAP_ENTRY_END()
  7833. SOAP_ENTRY_START(GetQuotaRecipients, lpsUserList->er, unsigned int ulUserid, entryId sUserId, struct userListResponse *lpsUserList)
  7834. {
  7835. std::unique_ptr<std::list<localobjectdetails_t> > lpUsers;
  7836. objectid_t sExternId;
  7837. objectdetails_t details;
  7838. userobject_relation_t relation;
  7839. unsigned int ulCompanyId;
  7840. bool bHasLocalStore = false;
  7841. entryId sUserEid = {0};
  7842. // does not return full class in sExternId.objclass
  7843. er = GetLocalId(sUserId, ulUserid, &ulUserid, &sExternId);
  7844. if (er != erSuccess)
  7845. return er;
  7846. // re-evaluate userid to externid to get the full class (mapi clients only know MAPI_ABCONT for companies)
  7847. er = g_lpSessionManager->GetCacheManager()->GetUserObject(ulUserid, &sExternId, NULL, NULL);
  7848. if (er != erSuccess)
  7849. return er;
  7850. er = CheckUserStore(lpecSession, ulUserid, ECSTORE_TYPE_PRIVATE, &bHasLocalStore);
  7851. if (er != erSuccess)
  7852. return er;
  7853. if (!bHasLocalStore)
  7854. return KCERR_NOT_FOUND;
  7855. //Check permission
  7856. if (lpecSession->GetSecurity()->IsAdminOverUserObject(ulUserid) != erSuccess)
  7857. return KCERR_NO_ACCESS;
  7858. /* Not all objectclasses support quota */
  7859. if ((sExternId.objclass == NONACTIVE_CONTACT) ||
  7860. (OBJECTCLASS_TYPE(sExternId.objclass) == OBJECTTYPE_DISTLIST) ||
  7861. (sExternId.objclass == CONTAINER_ADDRESSLIST))
  7862. return KCERR_INVALID_TYPE;
  7863. er = lpecSession->GetUserManagement()->GetObjectDetails(ulUserid, &details);
  7864. if (er != erSuccess)
  7865. return er;
  7866. if (OBJECTCLASS_TYPE(details.GetClass())== OBJECTTYPE_MAILUSER) {
  7867. ulCompanyId = details.GetPropInt(OB_PROP_I_COMPANYID);
  7868. relation = OBJECTRELATION_QUOTA_USERRECIPIENT;
  7869. } else if (details.GetClass() == CONTAINER_COMPANY) {
  7870. ulCompanyId = ulUserid;
  7871. relation = OBJECTRELATION_QUOTA_COMPANYRECIPIENT;
  7872. } else {
  7873. return KCERR_INVALID_TYPE;
  7874. }
  7875. /* When uLCompanyId is 0 then there are no recipient relations we could request,
  7876. * in that case we should manually allocate the list so it is safe to add the user
  7877. * to the list. */
  7878. if (ulCompanyId != 0) {
  7879. er = lpecSession->GetUserManagement()->GetSubObjectsOfObjectAndSync(relation, ulCompanyId, &unique_tie(lpUsers));
  7880. if (er != erSuccess)
  7881. return er;
  7882. } else
  7883. lpUsers.reset(new std::list<localobjectdetails_t>);
  7884. if (OBJECTCLASS_TYPE(details.GetClass())== OBJECTTYPE_MAILUSER) {
  7885. /* The main recipient (the user over quota) must be the first entry */
  7886. lpUsers->push_front(localobjectdetails_t(ulUserid, details));
  7887. } else if (details.GetClass() == CONTAINER_COMPANY) {
  7888. /* Append the system administrator for the company */
  7889. unsigned int ulSystem;
  7890. objectdetails_t systemdetails;
  7891. ulSystem = details.GetPropInt(OB_PROP_I_SYSADMIN);
  7892. er = lpecSession->GetSecurity()->IsUserObjectVisible(ulSystem);
  7893. if (er != erSuccess)
  7894. return er;
  7895. er = lpecSession->GetUserManagement()->GetObjectDetails(ulSystem, &systemdetails);
  7896. if (er != erSuccess)
  7897. return er;
  7898. lpUsers->push_front(localobjectdetails_t(ulSystem, systemdetails));
  7899. /* The main recipient (the company's public store) must be the first entry */
  7900. lpUsers->push_front(localobjectdetails_t(ulUserid, details));
  7901. }
  7902. lpsUserList->sUserArray.__size = 0;
  7903. lpsUserList->sUserArray.__ptr = s_alloc<user>(soap, lpUsers->size());
  7904. for (const auto &user : *lpUsers) {
  7905. if ((OBJECTCLASS_TYPE(user.GetClass()) != OBJECTTYPE_MAILUSER) ||
  7906. (details.GetClass() == NONACTIVE_CONTACT))
  7907. continue;
  7908. if (lpecSession->GetSecurity()->IsUserObjectVisible(user.ulId) != erSuccess)
  7909. continue;
  7910. er = GetABEntryID(user.ulId, soap, &sUserEid);
  7911. if (er != erSuccess)
  7912. return er;
  7913. er = CopyUserDetailsToSoap(user.ulId, &sUserEid, user,
  7914. lpecSession->GetCapabilities() & KOPANO_CAP_EXTENDED_ANON,
  7915. soap, &lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size]);
  7916. if (er != erSuccess)
  7917. return er;
  7918. // 6.40.0 stores the object class in the IsNonActive field
  7919. if (lpecSession->ClientVersion() == ZARAFA_VERSION_6_40_0)
  7920. lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size].ulIsNonActive = lpsUserList->sUserArray.__ptr[lpsUserList->sUserArray.__size].ulObjClass;
  7921. if (!bSupportUnicode) {
  7922. er = FixUserEncoding(soap, stringCompat, Out, lpsUserList->sUserArray.__ptr + lpsUserList->sUserArray.__size);
  7923. if (er != erSuccess)
  7924. return er;
  7925. }
  7926. ++lpsUserList->sUserArray.__size;
  7927. if (sUserEid.__ptr)
  7928. {
  7929. // sUserEid is placed in userdetails, no need to free
  7930. sUserEid.__ptr = NULL;
  7931. sUserEid.__size = 0;
  7932. }
  7933. }
  7934. return erSuccess;
  7935. }
  7936. SOAP_ENTRY_END()
  7937. SOAP_ENTRY_START(GetQuotaStatus, lpsQuotaStatus->er, unsigned int ulUserid, entryId sUserId, struct quotaStatus* lpsQuotaStatus)
  7938. {
  7939. quotadetails_t quotadetails;
  7940. long long llStoreSize = 0;
  7941. objectid_t sExternId;
  7942. bool bHasLocalStore = false;
  7943. eQuotaStatus QuotaStatus;
  7944. //Set defaults
  7945. lpsQuotaStatus->llStoreSize = 0;
  7946. // does not return full class in sExternId.objclass
  7947. er = GetLocalId(sUserId, ulUserid, &ulUserid, &sExternId);
  7948. if (er != erSuccess)
  7949. return er;
  7950. // re-evaluate userid to externid to get the full class (mapi clients only know MAPI_ABCONT for companies)
  7951. er = g_lpSessionManager->GetCacheManager()->GetUserObject(ulUserid, &sExternId, NULL, NULL);
  7952. if (er != erSuccess)
  7953. return er;
  7954. er = CheckUserStore(lpecSession, ulUserid, ECSTORE_TYPE_PRIVATE, &bHasLocalStore);
  7955. if (er != erSuccess)
  7956. return er;
  7957. if (!bHasLocalStore)
  7958. return KCERR_NOT_FOUND;
  7959. // Check permission
  7960. if(lpecSession->GetSecurity()->IsAdminOverUserObject(ulUserid) != erSuccess &&
  7961. (lpecSession->GetSecurity()->GetUserId() != ulUserid))
  7962. return KCERR_NO_ACCESS;
  7963. /* Not all objectclasses support quota */
  7964. if ((sExternId.objclass == NONACTIVE_CONTACT) ||
  7965. (OBJECTCLASS_TYPE(sExternId.objclass) == OBJECTTYPE_DISTLIST) ||
  7966. (sExternId.objclass == CONTAINER_ADDRESSLIST))
  7967. return KCERR_INVALID_TYPE;
  7968. if (OBJECTCLASS_TYPE(sExternId.objclass) == OBJECTTYPE_MAILUSER || sExternId.objclass == CONTAINER_COMPANY) {
  7969. er = lpecSession->GetSecurity()->GetUserSize(ulUserid, &llStoreSize);
  7970. if(er != erSuccess)
  7971. return er;
  7972. } else {
  7973. return KCERR_INVALID_PARAMETER;
  7974. }
  7975. // check the store quota status
  7976. er = lpecSession->GetSecurity()->CheckUserQuota(ulUserid, llStoreSize, &QuotaStatus);
  7977. if(er != erSuccess)
  7978. return er;
  7979. lpsQuotaStatus->llStoreSize = llStoreSize;
  7980. lpsQuotaStatus->ulQuotaStatus = (unsigned int)QuotaStatus;
  7981. return erSuccess;
  7982. }
  7983. SOAP_ENTRY_END()
  7984. SOAP_ENTRY_START(getMessageStatus, lpsStatus->er, entryId sEntryId, unsigned int ulFlags, struct messageStatus* lpsStatus)
  7985. {
  7986. unsigned int ulMsgStatus = 0;
  7987. unsigned int ulId = 0;
  7988. USE_DATABASE();
  7989. er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulId);
  7990. if(er != erSuccess)
  7991. return er;
  7992. //Check security
  7993. er = lpecSession->GetSecurity()->CheckPermission(ulId, ecSecurityRead);
  7994. if(er != erSuccess)
  7995. return er;
  7996. // Get the old flags
  7997. strQuery = "SELECT val_ulong FROM properties WHERE hierarchyid="+stringify(ulId)+" AND tag=3607 AND type=3 LIMIT 2";
  7998. if ((er = lpDatabase->DoSelect(strQuery, &lpDBResult)) != erSuccess) {
  7999. ec_log_err("getMessageStatus(): select failed %x", er);
  8000. return KCERR_DATABASE_ERROR;
  8001. }
  8002. if (lpDatabase->GetNumRows(lpDBResult) == 1) {
  8003. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  8004. if(lpDBRow == NULL || lpDBRow[0] == NULL) {
  8005. ec_log_err("getMessageStatus(): row or col null");
  8006. return KCERR_DATABASE_ERROR;
  8007. }
  8008. ulMsgStatus = atoui(lpDBRow[0]);
  8009. }
  8010. lpsStatus->ulMessageStatus = ulMsgStatus;
  8011. return erSuccess;
  8012. }
  8013. SOAP_ENTRY_END()
  8014. SOAP_ENTRY_START(setMessageStatus, lpsOldStatus->er, entryId sEntryId, unsigned int ulNewStatus, unsigned int ulNewStatusMask, unsigned int ulSyncId, struct messageStatus* lpsOldStatus)
  8015. {
  8016. unsigned int ulOldMsgStatus = 0;
  8017. unsigned int ulNewMsgStatus = 0;
  8018. unsigned int ulRows = 0;
  8019. unsigned int ulId = 0;
  8020. unsigned int ulParent = 0;
  8021. unsigned int ulObjFlags = 0;
  8022. SOURCEKEY sSourceKey;
  8023. SOURCEKEY sParentSourceKey;
  8024. USE_DATABASE();
  8025. er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulId);
  8026. if(er != erSuccess)
  8027. goto exit;
  8028. //Check security
  8029. er = lpecSession->GetSecurity()->CheckPermission(ulId, ecSecurityEdit);
  8030. if(er != erSuccess)
  8031. goto exit;
  8032. er = lpDatabase->Begin();
  8033. if(er != erSuccess)
  8034. goto exit;
  8035. er = g_lpSessionManager->GetCacheManager()->GetObject(ulId, &ulParent, NULL, &ulObjFlags);
  8036. if(er != erSuccess)
  8037. goto exit;
  8038. // Get the old flags (PR_MSG_STATUS)
  8039. strQuery = "SELECT val_ulong FROM properties WHERE hierarchyid="+stringify(ulId)+" AND tag=3607 AND type=3 LIMIT 2";
  8040. if((er = lpDatabase->DoSelect(strQuery, &lpDBResult)) != erSuccess) {
  8041. ec_log_err("setMessageStatus(): select failed %x", er);
  8042. er = KCERR_DATABASE_ERROR;
  8043. goto exit;
  8044. }
  8045. ulRows = lpDatabase->GetNumRows(lpDBResult);
  8046. if (ulRows == 1) {
  8047. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  8048. if(lpDBRow == NULL || lpDBRow[0] == NULL) {
  8049. er = KCERR_DATABASE_ERROR;
  8050. ec_log_err("setMessageStatus(): row or col null");
  8051. goto exit;
  8052. }
  8053. ulOldMsgStatus = atoui(lpDBRow[0]);
  8054. }
  8055. // Set the new flags
  8056. ulNewMsgStatus = (ulOldMsgStatus &~ulNewStatusMask) | (ulNewStatusMask & ulNewStatus);
  8057. if(ulRows > 0){
  8058. strQuery = "UPDATE properties SET val_ulong="+stringify(ulNewMsgStatus)+" WHERE hierarchyid="+stringify(ulId)+" AND tag=3607 AND type=3";
  8059. er = lpDatabase->DoUpdate(strQuery);
  8060. }else {
  8061. strQuery = "INSERT INTO properties(hierarchyid, tag, type, val_ulong) VALUES("+stringify(ulId)+", 3607, 3,"+stringify(ulNewMsgStatus)+")";
  8062. er = lpDatabase->DoInsert(strQuery);
  8063. }
  8064. if(er != erSuccess) {
  8065. ec_log_err("setMessageStatus(): query failed");
  8066. er = KCERR_DATABASE_ERROR;
  8067. goto exit;
  8068. }
  8069. lpsOldStatus->ulMessageStatus = ulOldMsgStatus;
  8070. GetSourceKey(ulId, &sSourceKey);
  8071. GetSourceKey(ulParent, &sParentSourceKey);
  8072. AddChange(lpecSession, ulSyncId, sSourceKey, sParentSourceKey, ICS_MESSAGE_CHANGE);
  8073. er = lpDatabase->Commit();
  8074. if(er != erSuccess)
  8075. goto exit;
  8076. // Now, send the notifications
  8077. g_lpSessionManager->GetCacheManager()->Update(fnevObjectModified, ulId);
  8078. g_lpSessionManager->NotificationModified(MAPI_MESSAGE, ulId, ulParent);
  8079. g_lpSessionManager->UpdateTables(ECKeyTable::TABLE_ROW_MODIFY, ulObjFlags&MSGFLAG_NOTIFY_FLAGS, ulParent, ulId, MAPI_MESSAGE);
  8080. exit:
  8081. ROLLBACK_ON_ERROR();
  8082. }
  8083. SOAP_ENTRY_END()
  8084. 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)
  8085. {
  8086. icsChangesArray *lpChanges = NULL;
  8087. SOURCEKEY sSourceKey(sSourceKeyFolder.__size, (char *)sSourceKeyFolder.__ptr);
  8088. er = GetChanges(soap, lpecSession, sSourceKey, ulSyncId, ulChangeId, ulChangeType, ulFlags, lpsRestrict, &lpsChangesResponse->ulMaxChangeId, &lpChanges);
  8089. if(er != erSuccess)
  8090. return er;
  8091. lpsChangesResponse->sChangesArray = *lpChanges;
  8092. return erSuccess;
  8093. }
  8094. SOAP_ENTRY_END()
  8095. 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)
  8096. {
  8097. SOURCEKEY sSourceKey(sSourceKeyFolder.__size, (char *)sSourceKeyFolder.__ptr);
  8098. unsigned int ulFolderId = 0, dummy = 0;
  8099. USE_DATABASE();
  8100. er = lpDatabase->Begin();
  8101. if (er != erSuccess)
  8102. goto exit;
  8103. if(sSourceKey.size()) {
  8104. er = g_lpSessionManager->GetCacheManager()->GetObjectFromProp(PROP_ID(PR_SOURCE_KEY), sSourceKey.size(), sSourceKey, &ulFolderId);
  8105. if(er != erSuccess)
  8106. goto exit;
  8107. } else {
  8108. ulFolderId = 0;
  8109. }
  8110. if(ulFolderId == 0) {
  8111. if(lpecSession->GetSecurity()->GetAdminLevel() != ADMIN_LEVEL_SYSADMIN)
  8112. er = KCERR_NO_ACCESS;
  8113. }
  8114. // Check security
  8115. else if (ulChangeType == ICS_SYNC_CONTENTS)
  8116. er = lpecSession->GetSecurity()->CheckPermission(ulFolderId, ecSecurityRead);
  8117. else if (ulChangeType == ICS_SYNC_HIERARCHY)
  8118. er = lpecSession->GetSecurity()->CheckPermission(ulFolderId, ecSecurityFolderVisible);
  8119. else
  8120. er = KCERR_INVALID_TYPE;
  8121. if(er != erSuccess)
  8122. goto exit;
  8123. if(ulSyncId == 0){
  8124. // SyncID is 0, which means the client will be requesting an initial sync from this new sync. The change_id will
  8125. // be updated in another call done when the synchronization is complete.
  8126. 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))+"))";
  8127. er = lpDatabase->DoInsert(strQuery, &ulSyncId);
  8128. if (er == erSuccess) {
  8129. er = lpDatabase->Commit();
  8130. lpsResponse->ulSyncId = ulSyncId;
  8131. }
  8132. goto exit;
  8133. }
  8134. strQuery = "SELECT sourcekey, change_id, sync_type FROM syncs WHERE id ="+stringify(ulSyncId)+" FOR UPDATE";
  8135. //TODO check existing sync
  8136. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  8137. if(er != erSuccess)
  8138. goto exit;
  8139. if(lpDatabase->GetNumRows(lpDBResult) == 0){
  8140. er = KCERR_NOT_FOUND;
  8141. goto exit;
  8142. }
  8143. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  8144. lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
  8145. if( lpDBRow == NULL || lpDBRow[0] == NULL || lpDBRow[1] == NULL || lpDBRow[2] == NULL){
  8146. er = KCERR_DATABASE_ERROR; // this should never happen
  8147. ec_log_err("setSyncStatus(): row/col NULL");
  8148. goto exit;
  8149. }
  8150. if(lpDBLen[0] != sSourceKey.size() || memcmp(lpDBRow[0], sSourceKey, sSourceKey.size()) != 0){
  8151. er = KCERR_COLLISION;
  8152. ec_log_err("setSyncStatus(): collision");
  8153. goto exit;
  8154. }
  8155. if((dummy = atoui(lpDBRow[2])) != ulChangeType){
  8156. er = KCERR_COLLISION;
  8157. ec_log_err("SetSyncStatus(): unexpected change type %u/%u", dummy, ulChangeType);
  8158. goto exit;
  8159. }
  8160. strQuery = "UPDATE syncs SET change_id = "+stringify(ulChangeId)+", sync_time = FROM_UNIXTIME("+stringify(time(NULL))+") WHERE id = "+stringify(ulSyncId);
  8161. er = lpDatabase->DoUpdate(strQuery);
  8162. if(er != erSuccess)
  8163. goto exit;
  8164. er = lpDatabase->Commit();
  8165. if (er != erSuccess)
  8166. goto exit;
  8167. lpsResponse->ulSyncId = ulSyncId;
  8168. exit:
  8169. ROLLBACK_ON_ERROR();
  8170. }
  8171. SOAP_ENTRY_END()
  8172. SOAP_ENTRY_START(getEntryIDFromSourceKey, lpsResponse->er, entryId sStoreId, struct xsd__base64Binary folderSourceKey, struct xsd__base64Binary messageSourceKey, struct getEntryIDFromSourceKeyResponse *lpsResponse)
  8173. {
  8174. unsigned int ulObjType = 0;
  8175. unsigned int ulObjId = 0;
  8176. unsigned int ulMessageId = 0;
  8177. unsigned int ulFolderId = 0;
  8178. unsigned int ulParent = 0;
  8179. unsigned int ulStoreId = 0;
  8180. unsigned int ulStoreFound = 0;
  8181. EID eid;
  8182. USE_DATABASE();
  8183. er = BeginLockFolders(lpDatabase, SOURCEKEY(folderSourceKey), LOCK_SHARED);
  8184. if(er != erSuccess)
  8185. goto exit;
  8186. er = lpecSession->GetObjectFromEntryId(&sStoreId, &ulStoreId);
  8187. if(er != erSuccess)
  8188. goto exit;
  8189. er = g_lpSessionManager->GetCacheManager()->GetObjectFromProp(PROP_ID(PR_SOURCE_KEY), folderSourceKey.__size, folderSourceKey.__ptr, &ulFolderId);
  8190. if(er != erSuccess)
  8191. goto exit;
  8192. if(messageSourceKey.__size != 0) {
  8193. er = g_lpSessionManager->GetCacheManager()->GetObjectFromProp(PROP_ID(PR_SOURCE_KEY), messageSourceKey.__size, messageSourceKey.__ptr, &ulMessageId);
  8194. if(er != erSuccess)
  8195. goto exit;
  8196. // Check if given sourcekey is in the given parent sourcekey
  8197. er = g_lpSessionManager->GetCacheManager()->GetParent(ulMessageId, &ulParent);
  8198. if(er != erSuccess || ulFolderId != ulParent) {
  8199. er = KCERR_NOT_FOUND;
  8200. goto exit;
  8201. }
  8202. ulObjId = ulMessageId;
  8203. ulObjType = MAPI_MESSAGE;
  8204. } else {
  8205. ulObjId = ulFolderId;
  8206. ulObjType = MAPI_FOLDER;
  8207. }
  8208. // Check if the folder given is actually in the store we're working on (may not be so if cache
  8209. // is out-of-date during a re-import of a store that has been deleted and re-imported). In this case
  8210. // we return NOT FOUND, which really is true since we cannot found the given sourcekey in this store.
  8211. er = g_lpSessionManager->GetCacheManager()->GetStore(ulFolderId, &ulStoreFound, NULL);
  8212. if(er != erSuccess || ulStoreFound != ulStoreId) {
  8213. er = KCERR_NOT_FOUND;
  8214. goto exit;
  8215. }
  8216. // Check security
  8217. if (ulObjType == MAPI_FOLDER)
  8218. er = lpecSession->GetSecurity()->CheckPermission(ulObjId, ecSecurityFolderVisible);
  8219. else
  8220. er = lpecSession->GetSecurity()->CheckPermission(ulObjId, ecSecurityRead);
  8221. if(er != erSuccess)
  8222. goto exit;
  8223. er = g_lpSessionManager->GetCacheManager()->GetEntryIdFromObject(ulObjId, soap, 0, &lpsResponse->sEntryId);
  8224. if(er != erSuccess)
  8225. goto exit;
  8226. exit:
  8227. lpDatabase->Commit();
  8228. }
  8229. SOAP_ENTRY_END()
  8230. SOAP_ENTRY_START(getSyncStates, lpsResponse->er, struct mv_long ulaSyncId, struct getSyncStatesReponse *lpsResponse)
  8231. {
  8232. er = GetSyncStates(soap, lpecSession, ulaSyncId, &lpsResponse->sSyncStates);
  8233. }
  8234. SOAP_ENTRY_END()
  8235. SOAP_ENTRY_START(getLicenseAuth, lpsResponse->er, struct xsd__base64Binary sAuthData, struct getLicenseAuthResponse *lpsResponse)
  8236. {
  8237. void *data = NULL;
  8238. er = ECLicenseClient().Auth(sAuthData.__ptr, sAuthData.__size, &data, reinterpret_cast<unsigned int *>(&lpsResponse->sAuthResponse.__size));
  8239. if (er != erSuccess)
  8240. goto exit;
  8241. lpsResponse->sAuthResponse.__ptr = s_alloc<unsigned char>(soap, lpsResponse->sAuthResponse.__size);
  8242. memcpy(lpsResponse->sAuthResponse.__ptr, data, lpsResponse->sAuthResponse.__size);
  8243. exit:
  8244. free(data);
  8245. }
  8246. SOAP_ENTRY_END()
  8247. SOAP_ENTRY_START(getLicenseCapa, lpsResponse->er, unsigned int ulServiceType, struct getLicenseCapaResponse *lpsResponse)
  8248. {
  8249. std::vector<std::string> lstCapabilities;
  8250. er = ECLicenseClient().GetCapabilities(ulServiceType, lstCapabilities);
  8251. if(er != erSuccess)
  8252. return er;
  8253. lpsResponse->sCapabilities.__size = lstCapabilities.size();
  8254. lpsResponse->sCapabilities.__ptr = s_alloc<char *>(soap, lstCapabilities.size());
  8255. for (unsigned int i = 0; i < lstCapabilities.size(); ++i)
  8256. lpsResponse->sCapabilities.__ptr[i] = s_strcpy(soap, lstCapabilities[i].c_str());
  8257. return erSuccess;
  8258. }
  8259. SOAP_ENTRY_END()
  8260. SOAP_ENTRY_START(getLicenseUsers, lpsResponse->er, unsigned int ulServiceType, struct getLicenseUsersResponse *lpsResponse)
  8261. {
  8262. unsigned int ulUsers = 0;
  8263. std::vector<std::string> lstCapabilities;
  8264. er = ECLicenseClient().GetInfo(ulServiceType, &ulUsers);
  8265. if(er != erSuccess)
  8266. return er;
  8267. lpsResponse->ulUsers = ulUsers;
  8268. return erSuccess;
  8269. }
  8270. SOAP_ENTRY_END()
  8271. SOAP_ENTRY_START(resolvePseudoUrl, lpsResponse->er, char *lpszPseudoUrl, struct resolvePseudoUrlResponse* lpsResponse)
  8272. {
  8273. std::string strServerPath;
  8274. if (!lpecSession->GetSessionManager()->IsDistributedSupported())
  8275. {
  8276. /**
  8277. * Non distributed environments do issue pseudo url's, but merely to
  8278. * make upgrading later possible. We'll just return the passed pseudo url
  8279. * and say that we're the peer. That would cause the client to keep on
  8280. * using the current connection.
  8281. **/
  8282. lpsResponse->lpszServerPath = lpszPseudoUrl;
  8283. lpsResponse->bIsPeer = true;
  8284. return erSuccess;
  8285. }
  8286. lpszPseudoUrl = STRIN_FIX(lpszPseudoUrl);
  8287. if (strncmp(lpszPseudoUrl, "pseudo://", 9))
  8288. return KCERR_INVALID_PARAMETER;
  8289. er = GetBestServerPath(soap, lpecSession, lpszPseudoUrl + 9, &strServerPath);
  8290. if (er != erSuccess)
  8291. return er;
  8292. lpsResponse->lpszServerPath = STROUT_FIX_CPY(strServerPath.c_str());
  8293. lpsResponse->bIsPeer = strcasecmp(g_lpSessionManager->GetConfig()->GetSetting("server_name"), lpszPseudoUrl + 9) == 0;
  8294. return erSuccess;
  8295. }
  8296. SOAP_ENTRY_END()
  8297. SOAP_ENTRY_START(getServerDetails, lpsResponse->er, struct mv_string8 szaSvrNameList, unsigned int ulFlags, struct getServerDetailsResponse* lpsResponse)
  8298. {
  8299. serverdetails_t sDetails;
  8300. std::string strServerPath;
  8301. std::string strPublicServer;
  8302. objectdetails_t details;
  8303. if (!lpecSession->GetSessionManager()->IsDistributedSupported())
  8304. /**
  8305. * We want to pretend the method doesn't exist. The best way to do that is to return
  8306. * SOAP_NO_METHOD. But that doesn't fit well in the macro famework. And since the
  8307. * client would translate that to a KCERR_NETWORK_ERROR anyway, we'll just return
  8308. * that instead.
  8309. **/
  8310. return KCERR_NETWORK_ERROR;
  8311. // lookup server which contains the public
  8312. if (lpecSession->GetUserManagement()->GetPublicStoreDetails(&details) == erSuccess)
  8313. strPublicServer = details.GetPropString(OB_PROP_S_SERVERNAME);
  8314. if (ulFlags & ~(EC_SERVERDETAIL_NO_NAME | EC_SERVERDETAIL_FILEPATH |
  8315. EC_SERVERDETAIL_HTTPPATH | EC_SERVERDETAIL_SSLPATH |
  8316. EC_SERVERDETAIL_PREFEREDPATH))
  8317. return KCERR_UNKNOWN_FLAGS;
  8318. if (ulFlags == 0)
  8319. ulFlags = EC_SERVERDETAIL_FILEPATH|EC_SERVERDETAIL_HTTPPATH|EC_SERVERDETAIL_SSLPATH |EC_SERVERDETAIL_PREFEREDPATH;
  8320. if (szaSvrNameList.__size > 0 && szaSvrNameList.__ptr != NULL) {
  8321. lpsResponse->sServerList.__size = szaSvrNameList.__size;
  8322. lpsResponse->sServerList.__ptr = s_alloc<struct server>(soap, szaSvrNameList.__size);
  8323. memset(lpsResponse->sServerList.__ptr, 0, szaSvrNameList.__size * sizeof *lpsResponse->sServerList.__ptr);
  8324. for (gsoap_size_t i = 0; i < szaSvrNameList.__size; ++i) {
  8325. er = lpecSession->GetUserManagement()->GetServerDetails(STRIN_FIX(szaSvrNameList.__ptr[i]), &sDetails);
  8326. if (er != erSuccess)
  8327. return er;
  8328. if (strcasecmp(sDetails.GetServerName().c_str(), g_lpSessionManager->GetConfig()->GetSetting("server_name")) == 0)
  8329. lpsResponse->sServerList.__ptr[i].ulFlags |= EC_SDFLAG_IS_PEER;
  8330. // note: "contains a public of a company" is also a possibility
  8331. if (!strPublicServer.empty() && strcasecmp(sDetails.GetServerName().c_str(), strPublicServer.c_str()) == 0)
  8332. lpsResponse->sServerList.__ptr[i].ulFlags |= EC_SDFLAG_HAS_PUBLIC;
  8333. if (!(ulFlags & EC_SERVERDETAIL_NO_NAME))
  8334. lpsResponse->sServerList.__ptr[i].lpszName = STROUT_FIX_CPY(sDetails.GetServerName().c_str());
  8335. if (ulFlags & EC_SERVERDETAIL_FILEPATH)
  8336. lpsResponse->sServerList.__ptr[i].lpszFilePath = STROUT_FIX_CPY(sDetails.GetFilePath().c_str());
  8337. if (ulFlags & EC_SERVERDETAIL_HTTPPATH)
  8338. lpsResponse->sServerList.__ptr[i].lpszHttpPath = STROUT_FIX_CPY(sDetails.GetHttpPath().c_str());
  8339. if (ulFlags & EC_SERVERDETAIL_SSLPATH)
  8340. lpsResponse->sServerList.__ptr[i].lpszSslPath = STROUT_FIX_CPY(sDetails.GetSslPath().c_str());
  8341. if (ulFlags & EC_SERVERDETAIL_PREFEREDPATH &&
  8342. GetBestServerPath(soap, lpecSession, sDetails.GetServerName(), &strServerPath) == erSuccess)
  8343. lpsResponse->sServerList.__ptr[i].lpszPreferedPath = STROUT_FIX_CPY(strServerPath.c_str());
  8344. }
  8345. }
  8346. return erSuccess;
  8347. }
  8348. SOAP_ENTRY_END()
  8349. // legacy calls required for 6.30 clients
  8350. SOAP_ENTRY_START(getServerBehavior, lpsResponse->er, struct getServerBehaviorResponse* lpsResponse)
  8351. {
  8352. lpsResponse->ulBehavior = 1;
  8353. }
  8354. SOAP_ENTRY_END()
  8355. SOAP_ENTRY_START(setServerBehavior, *result, unsigned int ulBehavior, unsigned int *result)
  8356. {
  8357. er = KCERR_NO_SUPPORT;
  8358. }
  8359. SOAP_ENTRY_END()
  8360. typedef ECDeferredFunc<ECRESULT, ECRESULT(*)(void*), void*> task_type;
  8361. struct MTOMStreamInfo;
  8362. struct MTOMSessionInfo {
  8363. ECSession *lpecSession;
  8364. ECDatabase *lpSharedDatabase;
  8365. ECDatabase *lpDatabase;
  8366. ECAttachmentStorage *lpAttachmentStorage;
  8367. ECRESULT er;
  8368. ECThreadPool *lpThreadPool;
  8369. MTOMStreamInfo *lpCurrentWriteStream; /* This is only tracked for cleanup at session exit */
  8370. MTOMStreamInfo *lpCurrentReadStream; /* This is only tracked for cleanup at session exit */
  8371. };
  8372. struct MTOMStreamInfo {
  8373. ECFifoBuffer *lpFifoBuffer;
  8374. unsigned int ulObjectId;
  8375. unsigned int ulStoreId;
  8376. bool bNewItem;
  8377. unsigned long long ullIMAP;
  8378. GUID sGuid;
  8379. ULONG ulFlags;
  8380. task_type *lpTask;
  8381. struct propValArray *lpPropValArray;
  8382. MTOMSessionInfo *lpSessionInfo;
  8383. };
  8384. typedef MTOMStreamInfo * LPMTOMStreamInfo;
  8385. static ECRESULT SerializeObject(void *arg)
  8386. {
  8387. ECRESULT er = erSuccess;
  8388. LPMTOMStreamInfo lpStreamInfo = NULL;
  8389. ECSerializer *lpSink = NULL;
  8390. lpStreamInfo = (LPMTOMStreamInfo)arg;
  8391. assert(lpStreamInfo != NULL);
  8392. lpStreamInfo->lpSessionInfo->lpSharedDatabase->ThreadInit();
  8393. lpSink = new ECFifoSerializer(lpStreamInfo->lpFifoBuffer, ECFifoSerializer::serialize);
  8394. er = SerializeMessage(lpStreamInfo->lpSessionInfo->lpecSession, lpStreamInfo->lpSessionInfo->lpSharedDatabase, lpStreamInfo->lpSessionInfo->lpAttachmentStorage, NULL, lpStreamInfo->ulObjectId, MAPI_MESSAGE, lpStreamInfo->ulStoreId, &lpStreamInfo->sGuid, lpStreamInfo->ulFlags, lpSink, true);
  8395. delete lpSink;
  8396. lpStreamInfo->lpSessionInfo->lpSharedDatabase->ThreadEnd();
  8397. lpStreamInfo->lpSessionInfo->er = er;
  8398. return er;
  8399. }
  8400. static void *MTOMReadOpen(struct soap *soap, void *handle, const char *id,
  8401. const char* /*type*/, const char* /*options*/)
  8402. {
  8403. LPMTOMStreamInfo lpStreamInfo = NULL;
  8404. lpStreamInfo = (LPMTOMStreamInfo)handle;
  8405. assert(lpStreamInfo != NULL);
  8406. if (lpStreamInfo->lpSessionInfo->er != erSuccess) {
  8407. soap->error = SOAP_FATAL_ERROR;
  8408. return NULL;
  8409. }
  8410. lpStreamInfo->lpFifoBuffer = new ECFifoBuffer();
  8411. if (strncmp(id, "emcas-", 6) == 0) {
  8412. std::unique_ptr<task_type> ptrTask(new task_type(SerializeObject, lpStreamInfo));
  8413. if (ptrTask->dispatchOn(lpStreamInfo->lpSessionInfo->lpThreadPool) == false) {
  8414. ec_log_err("Failed to dispatch serialization task for \"%s\"", id);
  8415. soap->error = SOAP_FATAL_ERROR;
  8416. delete lpStreamInfo->lpFifoBuffer;
  8417. lpStreamInfo->lpFifoBuffer = NULL;
  8418. return NULL;
  8419. }
  8420. lpStreamInfo->lpTask = ptrTask.release();
  8421. } else {
  8422. ec_log_err("Got stream request for unknown ID \"%s\"", id);
  8423. soap->error = SOAP_FATAL_ERROR;
  8424. delete lpStreamInfo->lpFifoBuffer;
  8425. lpStreamInfo->lpFifoBuffer = NULL;
  8426. return NULL;
  8427. }
  8428. lpStreamInfo->lpSessionInfo->lpCurrentReadStream = lpStreamInfo; // Track currently opened stream info
  8429. return (void *)lpStreamInfo;
  8430. }
  8431. static size_t MTOMRead(struct soap * /*soap*/, void *handle,
  8432. char *buf, size_t len)
  8433. {
  8434. ECRESULT er = erSuccess;
  8435. LPMTOMStreamInfo lpStreamInfo = (LPMTOMStreamInfo)handle;
  8436. ECFifoBuffer::size_type cbRead = 0;
  8437. assert(lpStreamInfo->lpFifoBuffer != NULL);
  8438. er = lpStreamInfo->lpFifoBuffer->Read(buf, len, STR_DEF_TIMEOUT, &cbRead);
  8439. if (er != erSuccess)
  8440. ec_log_err("Failed to read data. er=%s", stringify(er).c_str());
  8441. return cbRead;
  8442. }
  8443. static void MTOMReadClose(struct soap *soap, void *handle)
  8444. {
  8445. LPMTOMStreamInfo lpStreamInfo = (LPMTOMStreamInfo)handle;
  8446. assert(lpStreamInfo->lpFifoBuffer != NULL);
  8447. lpStreamInfo->lpSessionInfo->lpCurrentReadStream = NULL; // Cleanup done
  8448. // We get here when the last call to MTOMRead returned 0 OR when
  8449. // an error occurred within gSOAP's bowels. In the latter case, we need
  8450. // to close the FIFO to make sure the writing thread will not lock up.
  8451. // Since gSOAP will not be reading from the FIFO in any case once we
  8452. // read this point, it is safe to just close the FIFO.
  8453. lpStreamInfo->lpFifoBuffer->Close(ECFifoBuffer::cfRead);
  8454. if (lpStreamInfo->lpTask) {
  8455. lpStreamInfo->lpTask->wait(); // Todo: use result() to wait and get result
  8456. delete lpStreamInfo->lpTask;
  8457. }
  8458. delete lpStreamInfo->lpFifoBuffer;
  8459. lpStreamInfo->lpFifoBuffer = NULL;
  8460. }
  8461. static void MTOMWriteClose(struct soap *soap, void *handle);
  8462. static void MTOMSessionDone(struct soap *soap, void *param)
  8463. {
  8464. auto lpInfo = static_cast<MTOMSessionInfo *>(param);
  8465. if (lpInfo->lpCurrentWriteStream != NULL)
  8466. // Apparently a write stream was opened but not closed by gSOAP by calling MTOMWriteClose. Do it now.
  8467. MTOMWriteClose(soap, lpInfo->lpCurrentWriteStream);
  8468. else if (lpInfo->lpCurrentReadStream != NULL)
  8469. // Same but for MTOMReadClose()
  8470. MTOMReadClose(soap, lpInfo->lpCurrentReadStream);
  8471. // We can now safely remove sessions, etc since nobody is using them.
  8472. lpInfo->lpAttachmentStorage->Release();
  8473. lpInfo->lpecSession->Unlock();
  8474. delete lpInfo->lpSharedDatabase;
  8475. delete lpInfo->lpThreadPool;
  8476. delete lpInfo;
  8477. }
  8478. SOAP_ENTRY_START(exportMessageChangesAsStream, lpsResponse->er, unsigned int ulFlags, struct propTagArray sPropTags, struct sourceKeyPairArray sSourceKeyPairs, unsigned int ulPropTag, exportMessageChangesAsStreamResponse *lpsResponse)
  8479. {
  8480. LPMTOMStreamInfo lpStreamInfo = NULL;
  8481. object_ptr<ECAttachmentStorage> lpAttachmentStorage;
  8482. unsigned int ulObjectId = 0;
  8483. unsigned int ulParentId = 0;
  8484. unsigned int ulParentCheck = 0;
  8485. unsigned int ulObjFlags = 0;
  8486. unsigned int ulStoreId = 0;
  8487. unsigned long ulObjCnt = 0;
  8488. GUID sGuid;
  8489. ECObjectTableList rows;
  8490. struct rowSet *lpRowSet = NULL; // Do not free, used in response data
  8491. ECODStore ecODStore;
  8492. ECDatabase *lpBatchDB;
  8493. unsigned int ulDepth = 20;
  8494. unsigned int ulMode = 0;
  8495. MTOMSessionInfo *lpMTOMSessionInfo = NULL;
  8496. bool bUseSQLMulti = parseBool(g_lpSessionManager->GetConfig()->GetSetting("enable_sql_procedures"));
  8497. // Backward compat, old clients do not send ulPropTag
  8498. if(!ulPropTag)
  8499. ulPropTag = PR_SOURCE_KEY;
  8500. if(ulFlags & SYNC_BEST_BODY)
  8501. ulMode = 1;
  8502. else if(ulFlags & SYNC_LIMITED_IMESSAGE)
  8503. ulMode = 2;
  8504. USE_DATABASE();
  8505. if(ulPropTag == PR_ENTRYID) {
  8506. std::set<EntryId> setEntryIDs;
  8507. for (gsoap_size_t i = 0; i < sSourceKeyPairs.__size; ++i)
  8508. setEntryIDs.insert(EntryId(sSourceKeyPairs.__ptr[i].sObjectKey));
  8509. er = BeginLockFolders(lpDatabase, setEntryIDs, LOCK_SHARED);
  8510. } else if (ulPropTag == PR_SOURCE_KEY) {
  8511. std::set<SOURCEKEY> setParentSourcekeys;
  8512. for (gsoap_size_t i = 0; i < sSourceKeyPairs.__size; ++i)
  8513. setParentSourcekeys.insert(SOURCEKEY(sSourceKeyPairs.__ptr[i].sParentKey));
  8514. er = BeginLockFolders(lpDatabase, setParentSourcekeys, LOCK_SHARED);
  8515. } else
  8516. er = KCERR_INVALID_PARAMETER;
  8517. if (er == KCERR_NOT_FOUND) {
  8518. // BeginLockFolders returns KCERR_NOT_FOUND when there's no
  8519. // folder to lock, which can only happen if none of the passed
  8520. // objects exist.
  8521. // This is not an error as that's perfectly valid when performing
  8522. // a selective export. So we'll just return an empty batch, which
  8523. // will be interpreted by the caller as 'all message are deleted'.
  8524. er = erSuccess;
  8525. goto exit;
  8526. } else if (er != erSuccess)
  8527. goto exit;
  8528. ulDepth = atoui(lpecSession->GetSessionManager()->GetConfig()->GetSetting("embedded_attachment_limit"));
  8529. er = lpecSession->GetAdditionalDatabase(&lpBatchDB);
  8530. if (er != erSuccess)
  8531. goto exit;
  8532. if ((lpecSession->GetCapabilities() & KOPANO_CAP_ENHANCED_ICS) == 0) {
  8533. er = KCERR_NO_SUPPORT;
  8534. goto exit;
  8535. }
  8536. er = CreateAttachmentStorage(lpDatabase, &~lpAttachmentStorage);
  8537. if (er != erSuccess)
  8538. goto exit;
  8539. lpMTOMSessionInfo = new MTOMSessionInfo;
  8540. lpMTOMSessionInfo->lpCurrentWriteStream = NULL;
  8541. lpMTOMSessionInfo->lpCurrentReadStream = NULL;
  8542. lpMTOMSessionInfo->lpAttachmentStorage = lpAttachmentStorage;
  8543. lpMTOMSessionInfo->lpAttachmentStorage->AddRef();
  8544. lpMTOMSessionInfo->lpecSession = lpecSession; // Should be unlocked after MTOM is done
  8545. lpMTOMSessionInfo->lpecSession->Lock();
  8546. lpMTOMSessionInfo->lpSharedDatabase = lpBatchDB;
  8547. lpMTOMSessionInfo->er = erSuccess;
  8548. lpMTOMSessionInfo->lpThreadPool = new ECThreadPool(1);
  8549. soap_info(soap)->fdone = MTOMSessionDone;
  8550. soap_info(soap)->fdoneparam = lpMTOMSessionInfo;
  8551. lpsResponse->sMsgStreams.__ptr = s_alloc<messageStream>(soap, sSourceKeyPairs.__size);
  8552. for (gsoap_size_t i = 0; i < sSourceKeyPairs.__size; ++i) {
  8553. // Progress information
  8554. lpsResponse->sMsgStreams.__ptr[ulObjCnt].ulStep = i;
  8555. // Find the correct object
  8556. er = g_lpSessionManager->GetCacheManager()->GetObjectFromProp(PROP_ID(ulPropTag),
  8557. sSourceKeyPairs.__ptr[i].sObjectKey.__size,
  8558. sSourceKeyPairs.__ptr[i].sObjectKey.__ptr, &ulObjectId);
  8559. if(er != erSuccess) {
  8560. er = erSuccess;
  8561. goto next_object;
  8562. }
  8563. if(sSourceKeyPairs.__ptr[i].sParentKey.__size) {
  8564. er = g_lpSessionManager->GetCacheManager()->GetObjectFromProp(PROP_ID(ulPropTag),
  8565. sSourceKeyPairs.__ptr[i].sParentKey.__size,
  8566. sSourceKeyPairs.__ptr[i].sParentKey.__ptr, &ulParentId);
  8567. if(er != erSuccess) {
  8568. er = erSuccess;
  8569. goto next_object;
  8570. }
  8571. er = g_lpSessionManager->GetCacheManager()->GetObject(ulObjectId, &ulParentCheck, NULL, &ulObjFlags, NULL);
  8572. if (er != erSuccess) {
  8573. er = erSuccess;
  8574. goto next_object;
  8575. }
  8576. if (ulParentId != ulParentCheck) {
  8577. assert(false);
  8578. goto next_object;
  8579. }
  8580. }
  8581. if ((ulObjFlags & MSGFLAG_DELETED) != (ulFlags & MSGFLAG_DELETED))
  8582. goto next_object;
  8583. // Check security
  8584. er = lpecSession->GetSecurity()->CheckPermission(ulObjectId, ecSecurityRead);
  8585. if (er != erSuccess) {
  8586. er = erSuccess;
  8587. goto next_object;
  8588. }
  8589. // Get store
  8590. er = g_lpSessionManager->GetCacheManager()->GetStore(ulObjectId, &ulStoreId, &sGuid);
  8591. if(er != erSuccess) {
  8592. er = erSuccess;
  8593. goto next_object;
  8594. }
  8595. lpStreamInfo = (MTOMStreamInfo *)soap_malloc(soap, sizeof(MTOMStreamInfo));
  8596. lpStreamInfo->ulObjectId = ulObjectId;
  8597. lpStreamInfo->ulStoreId = ulStoreId;
  8598. lpStreamInfo->bNewItem = false;
  8599. lpStreamInfo->ullIMAP = 0;
  8600. lpStreamInfo->sGuid = sGuid;
  8601. lpStreamInfo->ulFlags = ulFlags;
  8602. lpStreamInfo->lpPropValArray = NULL;
  8603. lpStreamInfo->lpTask = NULL;
  8604. lpStreamInfo->lpSessionInfo = lpMTOMSessionInfo;
  8605. if(bUseSQLMulti)
  8606. strQuery += "call StreamObj(" + stringify(ulObjectId) + "," + stringify(ulDepth) + ", " + stringify(ulMode) + ");";
  8607. // Setup the MTOM Attachments
  8608. memset(&lpsResponse->sMsgStreams.__ptr[ulObjCnt].sStreamData, 0, sizeof(lpsResponse->sMsgStreams.__ptr[ulObjCnt].sStreamData));
  8609. lpsResponse->sMsgStreams.__ptr[ulObjCnt].sStreamData.xop__Include.__ptr = (unsigned char*)lpStreamInfo;
  8610. lpsResponse->sMsgStreams.__ptr[ulObjCnt].sStreamData.xop__Include.type = s_strcpy(soap, "application/binary");
  8611. lpsResponse->sMsgStreams.__ptr[ulObjCnt].sStreamData.xop__Include.id = s_strcpy(soap, string("emcas-" + stringify(ulObjCnt, false)).c_str());
  8612. ++ulObjCnt;
  8613. // Remember the object ID since we need it later
  8614. rows.push_back(sObjectTableKey(ulObjectId, 0));
  8615. next_object:
  8616. ;
  8617. }
  8618. lpsResponse->sMsgStreams.__size = ulObjCnt;
  8619. // The results of this query will be consumed by the MTOMRead function
  8620. if(!strQuery.empty()) {
  8621. er = lpBatchDB->DoSelectMulti(strQuery);
  8622. if(er != erSuccess)
  8623. goto exit;
  8624. }
  8625. memset(&ecODStore, 0, sizeof(ECODStore));
  8626. ecODStore.ulObjType = MAPI_MESSAGE;
  8627. // Get requested properties for all rows
  8628. er = ECStoreObjectTable::QueryRowData(NULL, soap, lpecSession, &rows, &sPropTags, &ecODStore, &lpRowSet, true, true);
  8629. if (er != erSuccess)
  8630. goto exit;
  8631. assert(lpRowSet->__size == static_cast<gsoap_size_t>(ulObjCnt));
  8632. for (gsoap_size_t i = 0; i < lpRowSet->__size; ++i)
  8633. lpsResponse->sMsgStreams.__ptr[i].sPropVals = lpRowSet->__ptr[i];
  8634. soap->fmimereadopen = &MTOMReadOpen;
  8635. soap->fmimeread = &MTOMRead;
  8636. soap->fmimereadclose = &MTOMReadClose;
  8637. g_lpStatsCollector->Increment(SCN_DATABASE_MROPS, (int)ulObjCnt);
  8638. exit:
  8639. lpDatabase->Commit();
  8640. if (er != erSuccess)
  8641. // Do not output any streams
  8642. lpsResponse->sMsgStreams.__size = 0;
  8643. soap->mode &= ~SOAP_XML_TREE;
  8644. soap->omode &= ~SOAP_XML_TREE;
  8645. }
  8646. SOAP_ENTRY_END()
  8647. static ECRESULT DeserializeObject(void *arg)
  8648. {
  8649. LPMTOMStreamInfo lpStreamInfo = NULL;
  8650. ECSerializer *lpSource = NULL;
  8651. ECRESULT er = erSuccess;
  8652. lpStreamInfo = (LPMTOMStreamInfo)arg;
  8653. assert(lpStreamInfo != NULL);
  8654. lpSource = new ECFifoSerializer(lpStreamInfo->lpFifoBuffer, ECFifoSerializer::deserialize);
  8655. 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);
  8656. delete lpSource;
  8657. return er;
  8658. }
  8659. static void *MTOMWriteOpen(struct soap *soap, void *handle,
  8660. const char * /*id*/, const char * /*type*/, const char * /*description*/,
  8661. enum soap_mime_encoding /*encoding*/)
  8662. {
  8663. LPMTOMStreamInfo lpStreamInfo = NULL;
  8664. lpStreamInfo = (LPMTOMStreamInfo)handle;
  8665. // Just return the handle (needed for gsoap to operate properly
  8666. lpStreamInfo->lpFifoBuffer = new ECFifoBuffer();
  8667. std::unique_ptr<task_type> ptrTask(new task_type(DeserializeObject, lpStreamInfo));
  8668. if (ptrTask->dispatchOn(lpStreamInfo->lpSessionInfo->lpThreadPool) == false) {
  8669. ec_log_err("Failed to dispatch deserialization task");
  8670. lpStreamInfo->lpSessionInfo->er = KCERR_UNABLE_TO_COMPLETE;
  8671. soap->error = SOAP_FATAL_ERROR;
  8672. return NULL;
  8673. }
  8674. lpStreamInfo->lpTask = ptrTask.release();
  8675. lpStreamInfo->lpSessionInfo->lpCurrentWriteStream = lpStreamInfo; // Remember that MTOMWriteOpen was called, and that a cleanup is needed
  8676. return handle;
  8677. }
  8678. static int MTOMWrite(struct soap *soap, void *handle,
  8679. const char *buf, size_t len)
  8680. {
  8681. ECRESULT er = erSuccess;
  8682. LPMTOMStreamInfo lpStreamInfo = NULL;
  8683. lpStreamInfo = (LPMTOMStreamInfo)handle;
  8684. assert(lpStreamInfo != NULL);
  8685. // Only write data if a reader thread is available
  8686. if (lpStreamInfo->lpTask) {
  8687. er = lpStreamInfo->lpFifoBuffer->Write(buf, len, STR_DEF_TIMEOUT, NULL);
  8688. if (er != erSuccess) {
  8689. lpStreamInfo->lpSessionInfo->er = er;
  8690. return SOAP_EOF;
  8691. }
  8692. }
  8693. return SOAP_OK;
  8694. }
  8695. static void MTOMWriteClose(struct soap *soap, void *handle)
  8696. {
  8697. ECRESULT er = erSuccess;
  8698. LPMTOMStreamInfo lpStreamInfo = NULL;
  8699. lpStreamInfo = (LPMTOMStreamInfo)handle;
  8700. assert(lpStreamInfo != NULL);
  8701. lpStreamInfo->lpSessionInfo->lpCurrentWriteStream = NULL; // Since we are cleaning up ourselves, another cleanup is not necessary
  8702. lpStreamInfo->lpFifoBuffer->Close(ECFifoBuffer::cfWrite);
  8703. if (lpStreamInfo->lpTask) {
  8704. lpStreamInfo->lpTask->wait(); // Todo: use result() to wait and get result
  8705. er = lpStreamInfo->lpTask->result();
  8706. delete lpStreamInfo->lpTask;
  8707. }
  8708. delete lpStreamInfo->lpFifoBuffer;
  8709. lpStreamInfo->lpFifoBuffer = NULL;
  8710. // Signal error to caller
  8711. if(er != erSuccess) {
  8712. lpStreamInfo->lpSessionInfo->er = er;
  8713. soap->error = SOAP_FATAL_ERROR;
  8714. }
  8715. }
  8716. 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)
  8717. {
  8718. MTOMStreamInfo *lpsStreamInfo = NULL;
  8719. unsigned int ulObjectId = 0;
  8720. unsigned int ulParentId = 0;
  8721. unsigned int ulParentType = 0;
  8722. unsigned int ulGrandParentId = 0;
  8723. unsigned int ulStoreId = 0;
  8724. unsigned int ulOwner = 0;
  8725. unsigned long long ullIMAP = 0;
  8726. GUID sGuid = {0};
  8727. SOURCEKEY sSourceKey;
  8728. SOURCEKEY sParentSourceKey;
  8729. ECListInt lObjectList;
  8730. gsoap_size_t nMVItems = 0;
  8731. std::string strColName;
  8732. std::string strColData;
  8733. unsigned int ulAffected = 0;
  8734. unsigned int ulDeleteFlags = EC_DELETE_ATTACHMENTS | EC_DELETE_RECIPIENTS | EC_DELETE_CONTAINER | EC_DELETE_MESSAGES | EC_DELETE_HARD_DELETE;
  8735. ECListDeleteItems lstDeleteItems;
  8736. ECListDeleteItems lstDeleted;
  8737. object_ptr<ECAttachmentStorage> lpAttachmentStorage;
  8738. MTOMSessionInfo *lpMTOMSessionInfo = NULL;
  8739. USE_DATABASE();
  8740. er = CreateAttachmentStorage(lpDatabase, &~lpAttachmentStorage);
  8741. if (er != erSuccess)
  8742. goto exit;
  8743. lpMTOMSessionInfo = new MTOMSessionInfo;
  8744. lpMTOMSessionInfo->lpCurrentWriteStream = NULL;
  8745. lpMTOMSessionInfo->lpCurrentReadStream = NULL;
  8746. lpMTOMSessionInfo->lpAttachmentStorage = lpAttachmentStorage;
  8747. lpMTOMSessionInfo->lpAttachmentStorage->AddRef();
  8748. lpMTOMSessionInfo->lpecSession = lpecSession; // Should be unlocked after MTOM is done
  8749. lpMTOMSessionInfo->lpecSession->Lock();
  8750. lpMTOMSessionInfo->lpDatabase = lpDatabase;
  8751. lpMTOMSessionInfo->lpSharedDatabase = NULL;
  8752. lpMTOMSessionInfo->er = erSuccess;
  8753. lpMTOMSessionInfo->lpThreadPool = new ECThreadPool(1);
  8754. soap_info(soap)->fdone = MTOMSessionDone;
  8755. soap_info(soap)->fdoneparam = lpMTOMSessionInfo;
  8756. er = lpAttachmentStorage->Begin();
  8757. if (er != erSuccess)
  8758. goto exit;
  8759. er = lpDatabase->Begin();
  8760. if (er != erSuccess)
  8761. goto exit;
  8762. // Get the parent object id.
  8763. er = lpecSession->GetObjectFromEntryId(&sFolderEntryId, &ulParentId);
  8764. if (er != erSuccess)
  8765. goto exit;
  8766. // Lock the parent folder
  8767. strQuery = "SELECT val_ulong FROM properties WHERE hierarchyid = " + stringify(ulParentId) + " FOR UPDATE";
  8768. er = lpDatabase->DoSelect(strQuery, NULL);
  8769. if (er != erSuccess)
  8770. goto exit;
  8771. if (!bIsNew) {
  8772. // Delete the existing message and recreate it
  8773. er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulObjectId);
  8774. if (er != erSuccess)
  8775. goto exit;
  8776. // When a message is update the flags are not passed. So obtain the old flags before
  8777. // deleting so we can pass them to CreateObject.
  8778. er = g_lpSessionManager->GetCacheManager()->GetObjectFlags(ulObjectId, &ulFlags);
  8779. if (er != erSuccess)
  8780. goto exit;
  8781. // Get the original IMAP ID
  8782. strQuery = "SELECT val_ulong FROM properties WHERE"
  8783. " hierarchyid=" + stringify(ulObjectId) +
  8784. " and tag=" + stringify(PROP_ID(PR_EC_IMAP_ID)) +
  8785. " and type=" + stringify(PROP_TYPE(PR_EC_IMAP_ID)) +
  8786. " LIMIT 1";
  8787. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  8788. if (er != erSuccess)
  8789. goto exit;
  8790. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  8791. if (lpDBRow == NULL || lpDBRow[0] == NULL)
  8792. // Items created in previous versions of Kopano will not have a PR_EC_IMAP_ID. The rule
  8793. // is that an item has a PR_EC_IMAP_ID that is equal to the hierarchyid in that case.
  8794. ullIMAP = ulObjectId;
  8795. else
  8796. // atoui return a unsigned int at best, but since PR_EC_IMAP_ID is a PT_LONG, the same conversion
  8797. // will be done when getting the property through MAPI.
  8798. ullIMAP = atoui(lpDBRow[0]);
  8799. lObjectList.push_back(ulObjectId);
  8800. // Collect recursive parent objects, validate item and check the permissions
  8801. er = ExpandDeletedItems(lpecSession, lpDatabase, &lObjectList, ulDeleteFlags, true, &lstDeleteItems);
  8802. if (er != erSuccess) {
  8803. assert(false);
  8804. goto exit;
  8805. }
  8806. // Delete the items hard
  8807. er = DeleteObjectHard(lpecSession, lpDatabase, lpAttachmentStorage, ulDeleteFlags, lstDeleteItems, true, lstDeleted);
  8808. if (er != erSuccess) {
  8809. assert(false);
  8810. goto exit;
  8811. }
  8812. // Update storesize
  8813. er = DeleteObjectStoreSize(lpecSession, lpDatabase, ulDeleteFlags, lstDeleted);
  8814. if (er != erSuccess) {
  8815. assert(false);
  8816. goto exit;
  8817. }
  8818. // Update cache
  8819. er = DeleteObjectCacheUpdate(lpecSession, ulDeleteFlags, lstDeleted);
  8820. if (er != erSuccess) {
  8821. assert(false);
  8822. goto exit;
  8823. }
  8824. }
  8825. // Create the message
  8826. if (bIsNew) {
  8827. er = CreateObject(lpecSession, lpDatabase, ulParentId, MAPI_FOLDER, MAPI_MESSAGE, ulFlags, &ulObjectId);
  8828. if (er != erSuccess)
  8829. goto exit;
  8830. } else {
  8831. ulOwner = lpecSession->GetSecurity()->GetUserId(ulParentId); // Owner of object is either the current user or the owner of the folder
  8832. // Reinsert the entry in the hierarchy table with the same id so the change notification later doesn't
  8833. // become a add notification because the id is different.
  8834. strQuery = "INSERT INTO hierarchy (id, parent, type, flags, owner) values("+stringify(ulObjectId)+", "+stringify(ulParentId)+", "+stringify(MAPI_MESSAGE)+", "+stringify(ulFlags)+", "+stringify(ulOwner)+")";
  8835. er = lpDatabase->DoInsert(strQuery);
  8836. if(er != erSuccess)
  8837. goto exit;
  8838. }
  8839. // Get store
  8840. er = g_lpSessionManager->GetCacheManager()->GetStore(ulObjectId, &ulStoreId, &sGuid);
  8841. if(er != erSuccess)
  8842. goto exit;
  8843. // Quota check
  8844. er = CheckQuota(lpecSession, ulStoreId);
  8845. if (er != erSuccess)
  8846. goto exit;
  8847. // Map entryId <-> ulObjectId
  8848. er = MapEntryIdToObjectId(lpecSession, lpDatabase, ulObjectId, sEntryId);
  8849. if (er != erSuccess)
  8850. goto exit;
  8851. // Deserialize the streamed message
  8852. soap->fmimewriteopen = &MTOMWriteOpen;
  8853. soap->fmimewrite = &MTOMWrite;
  8854. soap->fmimewriteclose= &MTOMWriteClose;
  8855. // We usually do not pass database objects to other threads. However, since
  8856. // we want to be able to perform a complete rollback we need to pass it
  8857. // to thread that processes the data and puts it in the database.
  8858. lpsStreamInfo = (MTOMStreamInfo *)soap_malloc(soap, sizeof(MTOMStreamInfo));
  8859. lpsStreamInfo->ulObjectId = ulObjectId;
  8860. lpsStreamInfo->ulStoreId = ulStoreId;
  8861. lpsStreamInfo->bNewItem = bIsNew;
  8862. lpsStreamInfo->ullIMAP = ullIMAP;
  8863. lpsStreamInfo->sGuid = sGuid;
  8864. lpsStreamInfo->ulFlags = ulFlags;
  8865. lpsStreamInfo->lpPropValArray = NULL;
  8866. lpsStreamInfo->lpTask = NULL;
  8867. lpsStreamInfo->lpSessionInfo = lpMTOMSessionInfo;
  8868. if (soap_check_mime_attachments(soap)) {
  8869. struct soap_multipart *content;
  8870. content = soap_get_mime_attachment(soap, (void*)lpsStreamInfo);
  8871. if (!content) {
  8872. er = (lpMTOMSessionInfo->er ? lpMTOMSessionInfo->er : KCERR_CALL_FAILED);
  8873. goto exit;
  8874. }
  8875. // Flush remaining attachments (that shouldn't even be there)
  8876. while (true) {
  8877. content = soap_get_mime_attachment(soap, (void*)lpsStreamInfo);
  8878. if (!content)
  8879. break;
  8880. };
  8881. }
  8882. er = g_lpSessionManager->GetCacheManager()->GetObject(ulParentId, &ulGrandParentId, NULL, NULL, &ulParentType);
  8883. if (er != erSuccess)
  8884. goto exit;
  8885. // pr_source_key magic
  8886. if (ulParentType == MAPI_FOLDER) {
  8887. GetSourceKey(ulObjectId, &sSourceKey);
  8888. GetSourceKey(ulParentId, &sParentSourceKey);
  8889. AddChange(lpecSession, ulSyncId, sSourceKey, sParentSourceKey, bIsNew ? ICS_MESSAGE_NEW : ICS_MESSAGE_CHANGE);
  8890. }
  8891. // Update the folder counts
  8892. er = UpdateFolderCounts(lpDatabase, ulParentId, ulFlags, lpsStreamInfo->lpPropValArray);
  8893. if (er != erSuccess)
  8894. goto exit;
  8895. // Set PR_CONFLICT_ITEMS if available
  8896. if (lpsConflictItems != NULL && lpsConflictItems->ulPropTag == PR_CONFLICT_ITEMS) {
  8897. // Delete to be sure
  8898. strQuery = "DELETE FROM mvproperties WHERE hierarchyid=" + stringify(ulObjectId) + " AND tag=" + stringify(PROP_ID(PR_CONFLICT_ITEMS)) + " AND type=" + stringify(PROP_TYPE(PR_CONFLICT_ITEMS));
  8899. er = lpDatabase->DoDelete(strQuery);
  8900. if (er != erSuccess)
  8901. goto exit;
  8902. nMVItems = GetMVItemCount(lpsConflictItems);
  8903. for (gsoap_size_t i = 0; i < nMVItems; ++i) {
  8904. er = CopySOAPPropValToDatabaseMVPropVal(lpsConflictItems, i, strColName, strColData, lpDatabase);
  8905. if (er != erSuccess)
  8906. goto exit;
  8907. 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 + ")";
  8908. er = lpDatabase->DoInsert(strQuery, NULL, &ulAffected);
  8909. if (er != erSuccess)
  8910. goto exit;
  8911. if (ulAffected != 1) {
  8912. er = KCERR_DATABASE_ERROR;
  8913. ec_log_err("importMessageFromStream(): affected row count != 1");
  8914. goto exit;
  8915. }
  8916. }
  8917. }
  8918. // Process MSGFLAG_SUBMIT
  8919. // If the messages was saved by an ICS syncer, then we need to sync the PR_MESSAGE_FLAGS for MSGFLAG_SUBMIT if it
  8920. // was included in the save.
  8921. er = ProcessSubmitFlag(lpDatabase, ulSyncId, ulStoreId, ulObjectId, bIsNew, lpsStreamInfo->lpPropValArray);
  8922. if (er != erSuccess)
  8923. goto exit;
  8924. if (ulParentType == MAPI_FOLDER) {
  8925. er = ECTPropsPurge::NormalizeDeferredUpdates(lpecSession, lpDatabase, ulParentId);
  8926. if (er != erSuccess)
  8927. goto exit;
  8928. }
  8929. er = lpAttachmentStorage->Commit();
  8930. if (er != erSuccess)
  8931. goto exit;
  8932. er = lpDatabase->Commit();
  8933. if (er != erSuccess)
  8934. goto exit;
  8935. // Notification
  8936. CreateNotifications(ulObjectId, MAPI_MESSAGE, ulParentId, ulGrandParentId, bIsNew, lpsStreamInfo->lpPropValArray, NULL);
  8937. g_lpStatsCollector->Increment(SCN_DATABASE_MWOPS);
  8938. exit:
  8939. if (lpsStreamInfo != NULL && lpsStreamInfo->lpPropValArray != NULL)
  8940. FreePropValArray(lpsStreamInfo->lpPropValArray, true);
  8941. FreeDeletedItems(&lstDeleteItems);
  8942. if(lpAttachmentStorage && er != erSuccess)
  8943. lpAttachmentStorage->Rollback();
  8944. ROLLBACK_ON_ERROR();
  8945. if (er != erSuccess) {
  8946. // remove from cache, else we can get sync issue, with missing messages offline
  8947. lpecSession->GetSessionManager()->GetCacheManager()->RemoveIndexData(ulObjectId);
  8948. lpecSession->GetSessionManager()->GetCacheManager()->Update(fnevObjectDeleted, ulObjectId);
  8949. }
  8950. }
  8951. SOAP_ENTRY_END()
  8952. SOAP_ENTRY_START(getChangeInfo, lpsResponse->er, entryId sEntryId, struct getChangeInfoResponse *lpsResponse)
  8953. {
  8954. unsigned int ulObjId = 0;
  8955. USE_DATABASE();
  8956. // Get object
  8957. er = lpecSession->GetObjectFromEntryId(&sEntryId, &ulObjId);
  8958. if (er != erSuccess)
  8959. return er;
  8960. // Check security
  8961. er = lpecSession->GetSecurity()->CheckPermission(ulObjId, ecSecurityRead);
  8962. if (er != erSuccess)
  8963. return er;
  8964. // Get the Change Key
  8965. strQuery = "SELECT val_binary FROM properties "
  8966. "WHERE tag = " + stringify(PROP_ID(PR_CHANGE_KEY)) +
  8967. " AND type = " + stringify(PROP_TYPE(PR_CHANGE_KEY)) +
  8968. " AND hierarchyid = " + stringify(ulObjId) + " LIMIT 1";
  8969. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  8970. if(er != erSuccess)
  8971. return er;
  8972. if (lpDatabase->GetNumRows(lpDBResult) > 0) {
  8973. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  8974. lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
  8975. lpsResponse->sPropCK.ulPropTag = PR_CHANGE_KEY;
  8976. lpsResponse->sPropCK.__union = SOAP_UNION_propValData_bin;
  8977. lpsResponse->sPropCK.Value.bin = s_alloc<xsd__base64Binary>(soap, 1);
  8978. lpsResponse->sPropCK.Value.bin->__size = lpDBLen[0];
  8979. lpsResponse->sPropCK.Value.bin->__ptr = s_alloc<unsigned char>(soap, lpDBLen[0]);
  8980. memcpy(lpsResponse->sPropCK.Value.bin->__ptr, lpDBRow[0], lpDBLen[0]);
  8981. } else {
  8982. return KCERR_NOT_FOUND;
  8983. }
  8984. // Get the Predecessor Change List
  8985. strQuery = "SELECT val_binary FROM properties "
  8986. "WHERE tag = " + stringify(PROP_ID(PR_PREDECESSOR_CHANGE_LIST)) +
  8987. " AND type = " + stringify(PROP_TYPE(PR_PREDECESSOR_CHANGE_LIST)) +
  8988. " AND hierarchyid = " + stringify(ulObjId) + " LIMIT 1";
  8989. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  8990. if(er != erSuccess)
  8991. return er;
  8992. if (lpDatabase->GetNumRows(lpDBResult) > 0) {
  8993. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  8994. lpDBLen = lpDatabase->FetchRowLengths(lpDBResult);
  8995. lpsResponse->sPropPCL.ulPropTag = PR_PREDECESSOR_CHANGE_LIST;
  8996. lpsResponse->sPropPCL.__union = SOAP_UNION_propValData_bin;
  8997. lpsResponse->sPropPCL.Value.bin = s_alloc<xsd__base64Binary>(soap, 1);
  8998. lpsResponse->sPropPCL.Value.bin->__size = lpDBLen[0];
  8999. lpsResponse->sPropPCL.Value.bin->__ptr = s_alloc<unsigned char>(soap, lpDBLen[0]);
  9000. memcpy(lpsResponse->sPropPCL.Value.bin->__ptr, lpDBRow[0], lpDBLen[0]);
  9001. } else {
  9002. return KCERR_NOT_FOUND;
  9003. }
  9004. return erSuccess;
  9005. }
  9006. SOAP_ENTRY_END()
  9007. SOAP_ENTRY_START(purgeDeferredUpdates, lpsResponse->er, struct purgeDeferredUpdatesResponse *lpsResponse)
  9008. {
  9009. unsigned int ulFolderId = 0;
  9010. USE_DATABASE();
  9011. // Only system-admins may run this
  9012. if (lpecSession->GetSecurity()->GetAdminLevel() < ADMIN_LEVEL_SYSADMIN)
  9013. return KCERR_NO_ACCESS;
  9014. er = ECTPropsPurge::GetLargestFolderId(lpDatabase, &ulFolderId);
  9015. if(er == KCERR_NOT_FOUND) {
  9016. // Nothing to purge
  9017. lpsResponse->ulDeferredRemaining = 0;
  9018. return er;
  9019. }
  9020. if(er != erSuccess)
  9021. return er;
  9022. er = ECTPropsPurge::PurgeDeferredTableUpdates(lpDatabase, ulFolderId);
  9023. if(er != erSuccess)
  9024. return er;
  9025. return ECTPropsPurge::GetDeferredCount(lpDatabase, &lpsResponse->ulDeferredRemaining);
  9026. }
  9027. SOAP_ENTRY_END()
  9028. SOAP_ENTRY_START(testPerform, *result, const char *szCommand,
  9029. struct testPerformArgs sPerform, unsigned int *result)
  9030. {
  9031. if(parseBool(g_lpSessionManager->GetConfig()->GetSetting("enable_test_protocol")))
  9032. er = TestPerform(soap, lpecSession, szCommand, sPerform.__size, sPerform.__ptr);
  9033. else
  9034. er = KCERR_NO_ACCESS;
  9035. }
  9036. SOAP_ENTRY_END()
  9037. SOAP_ENTRY_START(testSet, *result, const char *szVarName, const char *szValue,
  9038. unsigned int *result)
  9039. {
  9040. if(parseBool(g_lpSessionManager->GetConfig()->GetSetting("enable_test_protocol")))
  9041. er = TestSet(soap, lpecSession, szVarName, szValue);
  9042. else
  9043. er = KCERR_NO_ACCESS;
  9044. }
  9045. SOAP_ENTRY_END()
  9046. SOAP_ENTRY_START(testGet, lpsResponse->er, const char *szVarName,
  9047. struct testGetResponse *lpsResponse)
  9048. {
  9049. if(parseBool(g_lpSessionManager->GetConfig()->GetSetting("enable_test_protocol")))
  9050. er = TestGet(soap, lpecSession, szVarName, &lpsResponse->szValue);
  9051. else
  9052. er = KCERR_NO_ACCESS;
  9053. }
  9054. SOAP_ENTRY_END()
  9055. SOAP_ENTRY_START(setLockState, *result, entryId sEntryId, bool bLocked, unsigned int *result)
  9056. {
  9057. unsigned int ulObjId = 0;
  9058. unsigned int ulOwner = 0;
  9059. unsigned int ulObjType = 0;
  9060. er = g_lpSessionManager->GetCacheManager()->GetObjectFromEntryId(&sEntryId, &ulObjId);
  9061. if (er != erSuccess)
  9062. return er;
  9063. er = g_lpSessionManager->GetCacheManager()->GetObject(ulObjId, NULL, &ulOwner, NULL, &ulObjType);
  9064. if (er != erSuccess)
  9065. return er;
  9066. if (ulObjType != MAPI_MESSAGE)
  9067. return KCERR_NO_SUPPORT;
  9068. // Do we need to be owner?
  9069. er = lpecSession->GetSecurity()->CheckPermission(ulObjId, ecSecurityOwner);
  9070. if (er != erSuccess)
  9071. return er;
  9072. if (bLocked) {
  9073. er = lpecSession->LockObject(ulObjId);
  9074. if (er == KCERR_NO_ACCESS)
  9075. er = KCERR_SUBMITTED;
  9076. } else {
  9077. er = lpecSession->UnlockObject(ulObjId);
  9078. }
  9079. return er;
  9080. }
  9081. SOAP_ENTRY_END()
  9082. SOAP_ENTRY_START(getUserClientUpdateStatus, lpsResponse->er, entryId sUserId, struct userClientUpdateStatusResponse *lpsResponse)
  9083. {
  9084. USE_DATABASE();
  9085. objectid_t sExternId;
  9086. unsigned int ulUserId = 0;
  9087. bool bHasLocalStore = false;
  9088. if (!parseBool(g_lpSessionManager->GetConfig()->GetSetting("client_update_enabled")))
  9089. return KCERR_NO_SUPPORT;
  9090. er = GetLocalId(sUserId, 0, &ulUserId, &sExternId);
  9091. if (er != erSuccess)
  9092. return er;
  9093. er = CheckUserStore(lpecSession, ulUserId, ECSTORE_TYPE_PRIVATE, &bHasLocalStore);
  9094. if (er != erSuccess)
  9095. return er;
  9096. if (!bHasLocalStore)
  9097. return KCERR_NOT_FOUND;
  9098. strQuery = "SELECT trackid, UNIX_TIMESTAMP(updatetime), currentversion, latestversion, computername, status FROM clientupdatestatus WHERE userid="+stringify(ulUserId) + " LIMIT 1";
  9099. er = lpDatabase->DoSelect(strQuery, &lpDBResult);
  9100. if(er != erSuccess)
  9101. return er;
  9102. lpDBRow = lpDatabase->FetchRow(lpDBResult);
  9103. if (lpDBRow == nullptr)
  9104. return MAPI_E_NOT_FOUND;
  9105. if(lpDBRow[0]) lpsResponse->ulTrackId = atoui(lpDBRow[0]);
  9106. if(lpDBRow[1]) lpsResponse->tUpdatetime = atoui(lpDBRow[1]);
  9107. if(lpDBRow[2]) lpsResponse->lpszCurrentversion = s_strcpy(soap, lpDBRow[2]);
  9108. if(lpDBRow[3]) lpsResponse->lpszLatestversion = s_strcpy(soap, lpDBRow[3]);
  9109. if(lpDBRow[4]) lpsResponse->lpszComputername = s_strcpy(soap, lpDBRow[4]);
  9110. if(lpDBRow[5]) lpsResponse->ulStatus = atoui(lpDBRow[5]);
  9111. return erSuccess;
  9112. }
  9113. SOAP_ENTRY_END()
  9114. SOAP_ENTRY_START(removeAllObjects, *result, entryId sExceptUserId, unsigned int *result)
  9115. {
  9116. er = KCERR_NO_SUPPORT;
  9117. }
  9118. SOAP_ENTRY_END()
  9119. SOAP_ENTRY_START(resetFolderCount, lpsResponse->er, entryId sEntryId, struct resetFolderCountResponse *lpsResponse)
  9120. {
  9121. unsigned int ulObjId = 0;
  9122. unsigned int ulOwner = 0;
  9123. unsigned int ulObjType = 0;
  9124. er = g_lpSessionManager->GetCacheManager()->GetObjectFromEntryId(&sEntryId, &ulObjId);
  9125. if (er != erSuccess)
  9126. return er;
  9127. er = g_lpSessionManager->GetCacheManager()->GetObject(ulObjId, NULL, &ulOwner, NULL, &ulObjType);
  9128. if (er != erSuccess)
  9129. return er;
  9130. if (ulObjType != MAPI_FOLDER)
  9131. return KCERR_INVALID_TYPE;
  9132. er = lpecSession->GetSecurity()->CheckPermission(ulObjId, ecSecurityOwner);
  9133. if (er != erSuccess)
  9134. return er;
  9135. return ResetFolderCount(lpecSession, ulObjId, &lpsResponse->ulUpdates);
  9136. }
  9137. SOAP_ENTRY_END()