ECUserManagement.cpp 155 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608
  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 <exception>
  19. #include <memory>
  20. #include <mutex>
  21. #include <set>
  22. #include <stdexcept>
  23. #include <string>
  24. #include <utility>
  25. #include <vector>
  26. #include <mapidefs.h>
  27. #include <mapitags.h>
  28. #include <kopano/mapiext.h>
  29. #include <kopano/tie.hpp>
  30. #include <map>
  31. #include <memory>
  32. #include <algorithm>
  33. #include "kcore.hpp"
  34. #include <kopano/stringutil.h>
  35. #include "ECUserManagement.h"
  36. #include "ECSessionManager.h"
  37. #include "ECPluginFactory.h"
  38. #include "ECSecurity.h"
  39. #include <kopano/ECIConv.h>
  40. #include "SymmetricCrypt.h"
  41. #include "ECPamAuth.h"
  42. #include "ECKrbAuth.h"
  43. #include <kopano/UnixUtil.h>
  44. #include "ECICS.h"
  45. #include "ics.h"
  46. #include <kopano/ECGuid.h>
  47. #include <kopano/ECDefs.h>
  48. #include "ECFeatureList.h"
  49. #include <kopano/EMSAbTag.h>
  50. #include <kopano/charset/convert.h>
  51. #include <kopano/charset/utf8string.h>
  52. #ifndef AB_UNICODE_OK
  53. #define AB_UNICODE_OK ((ULONG) 0x00000040)
  54. #endif
  55. using namespace KCHL;
  56. namespace KC {
  57. extern ECSessionManager* g_lpSessionManager;
  58. static bool execute_script(const char *scriptname, ...)
  59. {
  60. va_list v;
  61. char *envname;
  62. const char *env[32];
  63. std::list<std::string> lstEnv;
  64. std::string strEnv;
  65. int n = 0;
  66. va_start(v, scriptname);
  67. /* Set environment */
  68. for (;;) {
  69. envname = va_arg(v, char *);
  70. if (!envname)
  71. break;
  72. auto envval = va_arg(v, char *);
  73. if (!envval)
  74. break;
  75. strEnv = envname;
  76. strEnv += '=';
  77. strEnv += envval;
  78. lstEnv.push_back(std::move(strEnv));
  79. }
  80. va_end(v);
  81. ec_log_debug("Running script `%s`", scriptname);
  82. for (const auto &s : lstEnv)
  83. env[n++] = s.c_str();
  84. env[n] = NULL;
  85. return unix_system(scriptname, scriptname, env);
  86. }
  87. static const char *ObjectClassToName(objectclass_t objclass)
  88. {
  89. switch (objclass) {
  90. case OBJECTCLASS_UNKNOWN:
  91. return "unknown";
  92. case OBJECTCLASS_USER:
  93. return "unknown user";
  94. case ACTIVE_USER:
  95. return "user";
  96. case NONACTIVE_USER:
  97. return "nonactive user";
  98. case NONACTIVE_ROOM:
  99. return "room";
  100. case NONACTIVE_EQUIPMENT:
  101. return "equipment";
  102. case NONACTIVE_CONTACT:
  103. return "contact";
  104. case OBJECTCLASS_DISTLIST:
  105. return "unknown group";
  106. case DISTLIST_GROUP:
  107. return "group";
  108. case DISTLIST_SECURITY:
  109. return "security group";
  110. case DISTLIST_DYNAMIC:
  111. return "dynamic group";
  112. case OBJECTCLASS_CONTAINER:
  113. return "unknown container";
  114. case CONTAINER_COMPANY:
  115. return "company";
  116. case CONTAINER_ADDRESSLIST:
  117. return "address list";
  118. default:
  119. return "Unknown object";
  120. };
  121. }
  122. static const char *RelationTypeToName(userobject_relation_t type)
  123. {
  124. switch(type) {
  125. case OBJECTRELATION_GROUP_MEMBER:
  126. return "groupmember";
  127. case OBJECTRELATION_COMPANY_VIEW:
  128. return "company-view";
  129. case OBJECTRELATION_COMPANY_ADMIN:
  130. return "company-admin";
  131. case OBJECTRELATION_QUOTA_USERRECIPIENT:
  132. return "userquota-recipient";
  133. case OBJECTRELATION_QUOTA_COMPANYRECIPIENT:
  134. return "companyquota-recipient";
  135. case OBJECTRELATION_USER_SENDAS:
  136. return "sendas-user-allowed";
  137. case OBJECTRELATION_ADDRESSLIST_MEMBER:
  138. return "addressbook-member";
  139. };
  140. return "Unknown relation type";
  141. }
  142. ECUserManagement::ECUserManagement(BTSession *lpSession,
  143. ECPluginFactory *lpPluginFactory, ECConfig *lpConfig)
  144. {
  145. this->m_lpSession = lpSession;
  146. this->m_lpPluginFactory = lpPluginFactory;
  147. this->m_lpConfig = lpConfig;
  148. }
  149. // Authenticate a user (NOTE: ECUserManagement will never authenticate SYSTEM unless it is actually
  150. // authenticated by the remote server, which normally never happens)
  151. ECRESULT ECUserManagement::AuthUserAndSync(const char* szLoginname, const char* szPassword, unsigned int *lpulUserId) {
  152. ECRESULT er;
  153. objectsignature_t external;
  154. UserPlugin *lpPlugin = NULL;
  155. string username;
  156. string companyname;
  157. string password;
  158. bool bHosted = m_lpSession->GetSessionManager()->IsHostedSupported();
  159. objectid_t sCompany(CONTAINER_COMPANY);
  160. string error;
  161. const char *szAuthMethod = NULL;
  162. er = GetThreadLocalPlugin(m_lpPluginFactory, &lpPlugin);
  163. if(er != erSuccess)
  164. return er;
  165. /*
  166. * GetUserAndCompanyFromLoginName() will return KCWARN_PARTIAL_COMPLETION
  167. * when only the username was resolved and not the companyname.
  168. * When that happens continue like nothing happens because perhaps the
  169. * LDAP plugin is used and the login attribute is the email address.
  170. * We will resolve the company later when the session is created and the
  171. * company is requested through getDetails().
  172. */
  173. er = GetUserAndCompanyFromLoginName(szLoginname, &username, &companyname);
  174. if (er != erSuccess && er != KCWARN_PARTIAL_COMPLETION)
  175. return er;
  176. if (SymmetricIsCrypted(szPassword))
  177. password = SymmetricDecrypt(szPassword);
  178. else
  179. password = szPassword;
  180. if (bHosted && !companyname.empty()) {
  181. er = ResolveObject(CONTAINER_COMPANY, companyname, objectid_t(), &sCompany);
  182. if (er != erSuccess || sCompany.objclass != CONTAINER_COMPANY) {
  183. ec_log_warn("Company \"%s\" not found for authentication by plugin failed for user \"%s\"", companyname.c_str(), szLoginname);
  184. return er;
  185. }
  186. }
  187. szAuthMethod = m_lpConfig->GetSetting("auth_method");
  188. if (szAuthMethod && strcmp(szAuthMethod, "pam") == 0) {
  189. // authenticate through pam
  190. er = ECPAMAuthenticateUser(m_lpConfig->GetSetting("pam_service"), username, password, &error);
  191. if (er != erSuccess) {
  192. ec_log_warn("Authentication through PAM failed for user \"%s\": %s", szLoginname, error.c_str());
  193. return er;
  194. }
  195. try {
  196. external = lpPlugin->resolveName(ACTIVE_USER, username, sCompany);
  197. }
  198. catch (objectnotfound &e) {
  199. ec_log_warn("Authentication through PAM succeeded for \"%s\", but not found by plugin: %s", szLoginname, e.what());
  200. return KCERR_LOGON_FAILED;
  201. }
  202. catch (std::exception &e) {
  203. ec_log_warn("Authentication through PAM succeeded for \"%s\", but plugin returned an error: %s", szLoginname, e.what());
  204. return KCERR_LOGON_FAILED;
  205. }
  206. } else if (szAuthMethod && strcmp(szAuthMethod, "kerberos") == 0) {
  207. // authenticate through kerberos
  208. er = ECKrb5AuthenticateUser(username, password, &error);
  209. if (er != erSuccess) {
  210. ec_log_warn("Authentication through Kerberos failed for user \"%s\": %s", szLoginname, error.c_str());
  211. return er;
  212. }
  213. try {
  214. external = lpPlugin->resolveName(ACTIVE_USER, username, sCompany);
  215. }
  216. catch (objectnotfound &e) {
  217. ec_log_warn("Authentication through Kerberos succeeded for \"%s\", but not found by plugin: %s", szLoginname, e.what());
  218. return KCERR_LOGON_FAILED;
  219. }
  220. catch (std::exception &e) {
  221. ec_log_warn("Authentication through Kerberos succeeded for \"%s\", but plugin returned an error: %s", szLoginname, e.what());
  222. return KCERR_LOGON_FAILED;
  223. }
  224. } else
  225. {
  226. // default method is plugin
  227. try {
  228. external = lpPlugin->authenticateUser(username, password, sCompany);
  229. } catch (login_error &e) {
  230. ec_log_warn("Authentication by plugin failed for user \"%s\": %s", szLoginname, e.what());
  231. return KCERR_LOGON_FAILED;
  232. } catch (objectnotfound &e) {
  233. ec_log_warn("Authentication by plugin failed for user \"%s\": %s", szLoginname, e.what());
  234. return KCERR_NOT_FOUND;
  235. } catch (std::exception &e) {
  236. ec_log_warn("Authentication error by plugin for user \"%s\": %s", szLoginname, e.what());
  237. return KCERR_PLUGIN_ERROR;
  238. }
  239. }
  240. return GetLocalObjectIdOrCreate(external, lpulUserId);
  241. }
  242. /*
  243. * I would prefer the next method to have
  244. * bool ECUserManagement::MustHide(ECSecurity& security, unsigned int ulFlags, const objectdetails_t details)
  245. * as a prototype, but that would mean percolating constness in class ECSecurity's methods, which proved to
  246. * be a task beyond a simple edit. NS 11 fFebruary 2014
  247. */
  248. bool ECUserManagement::MustHide(/*const*/ ECSecurity &security,
  249. unsigned int ulFlags, const objectdetails_t &details) const
  250. {
  251. return (security.GetUserId() != KOPANO_UID_SYSTEM) &&
  252. (security.GetAdminLevel() == 0) &&
  253. ((ulFlags & USERMANAGEMENT_SHOW_HIDDEN) == 0) &&
  254. details.GetPropBool(OB_PROP_B_AB_HIDDEN);
  255. }
  256. // Get details for an object
  257. ECRESULT ECUserManagement::GetObjectDetails(unsigned int ulObjectId, objectdetails_t *lpDetails)
  258. {
  259. if (IsInternalObject(ulObjectId))
  260. return GetLocalObjectDetails(ulObjectId, lpDetails);
  261. return GetExternalObjectDetails(ulObjectId, lpDetails);
  262. }
  263. ECRESULT ECUserManagement::GetLocalObjectListFromSignatures(const list<objectsignature_t> &lstSignatures, const std::map<objectid_t, unsigned int> &mapExternToLocal, unsigned int ulFlags, list<localobjectdetails_t> *lpDetails)
  264. {
  265. ECRESULT er;
  266. ECSecurity *lpSecurity = NULL;
  267. // Extern details
  268. list<objectid_t> lstExternIds;
  269. std::unique_ptr<map<objectid_t, objectdetails_t> > lpExternDetails;
  270. objectdetails_t details;
  271. unsigned int ulObjectId = 0;
  272. UserPlugin *lpPlugin = NULL;
  273. er = GetThreadLocalPlugin(m_lpPluginFactory, &lpPlugin);
  274. if(er != erSuccess)
  275. return er;
  276. er = GetSecurity(&lpSecurity);
  277. if (er != erSuccess)
  278. return er;
  279. for (const auto &sig : lstSignatures) {
  280. auto iterExternLocal = mapExternToLocal.find(sig.id);
  281. if (iterExternLocal == mapExternToLocal.cend())
  282. continue;
  283. ulObjectId = iterExternLocal->second;
  284. // Check if the cache contains the details or if we are going to request
  285. // it from the plugin.
  286. er = m_lpSession->GetSessionManager()->GetCacheManager()->GetUserDetails(ulObjectId, &details);
  287. if (er != erSuccess) {
  288. lstExternIds.push_back(sig.id);
  289. er = erSuccess;
  290. continue;
  291. }
  292. if (ulFlags & USERMANAGEMENT_ADDRESSBOOK &&
  293. MustHide(*lpSecurity, ulFlags, details))
  294. continue;
  295. // remove details, but keep the class information
  296. if (ulFlags & USERMANAGEMENT_IDS_ONLY)
  297. details = objectdetails_t(details.GetClass());
  298. lpDetails->push_back(localobjectdetails_t(ulObjectId, details));
  299. }
  300. if (lstExternIds.empty())
  301. return hrSuccess;
  302. // We have a list of all objects which still require details from plugin
  303. try {
  304. // Get all pending details
  305. lpExternDetails = lpPlugin->getObjectDetails(lstExternIds);
  306. } catch (notsupported &) {
  307. return KCERR_NO_SUPPORT;
  308. } catch (objectnotfound &) {
  309. return KCERR_NOT_FOUND;
  310. } catch(std::exception &e) {
  311. ec_log_warn("Unable to retrieve details from external user source: %s", e.what());
  312. return KCERR_PLUGIN_ERROR;
  313. }
  314. for (const auto &ext_det : *lpExternDetails) {
  315. auto iterExternLocal = mapExternToLocal.find(ext_det.first);
  316. if (iterExternLocal == mapExternToLocal.cend())
  317. continue;
  318. ulObjectId = iterExternLocal->second;
  319. if (ulFlags & USERMANAGEMENT_ADDRESSBOOK)
  320. if (MustHide(*lpSecurity, ulFlags, ext_det.second))
  321. continue;
  322. if (ulFlags & USERMANAGEMENT_IDS_ONLY)
  323. lpDetails->push_back(localobjectdetails_t(ulObjectId, objectdetails_t(ext_det.second.GetClass())));
  324. else
  325. lpDetails->push_back(localobjectdetails_t(ulObjectId, ext_det.second));
  326. // Update cache
  327. m_lpSession->GetSessionManager()->GetCacheManager()->SetUserDetails(ulObjectId, &ext_det.second);
  328. }
  329. return erSuccess;
  330. }
  331. /**
  332. * Get object list
  333. *
  334. * Retrieves the object list with local object IDs for the specified object class. In hosted mode, ulCompanyId can
  335. * specify which company to retrieve objects for.
  336. *
  337. * @param objclass Object class to return list of
  338. * @param ulCompanyId Company ID to retrieve list for (may be 0 for non-hosted mode, or when getting company list)
  339. * @param lppObjects returned object list
  340. * @param ulFlags USERMANAGEMENT_IDS_ONLY: return only IDs of objects, not details, USERMANAGEMENT_ADDRESSBOOK: returned
  341. * objects will be used in the addressbook, so filter 'hidden' items, USERMANAGEMENT_FORCE_SYNC: always
  342. * perform a sync with the plugin, even if sync_gab_realtime is set to 'no'
  343. * @return result
  344. */
  345. ECRESULT ECUserManagement::GetCompanyObjectListAndSync(objectclass_t objclass, unsigned int ulCompanyId, std::list<localobjectdetails_t> **lppObjects, unsigned int ulFlags)
  346. {
  347. ECRESULT er = erSuccess;
  348. bool bSync = ulFlags & USERMANAGEMENT_FORCE_SYNC || parseBool(m_lpConfig->GetSetting("sync_gab_realtime"));
  349. bool bIsSafeMode = parseBool(m_lpConfig->GetSetting("user_safe_mode"));
  350. // Return data
  351. std::unique_ptr<std::list<localobjectdetails_t> > lpObjects(new std::list<localobjectdetails_t>);
  352. // Local ids
  353. std::unique_ptr<std::list<unsigned int> > lpLocalIds;
  354. // Extern ids
  355. std::unique_ptr<signatures_t> lpExternSignatures;
  356. // Extern -> Local
  357. std::map<objectid_t, unsigned int> mapExternIdToLocal;
  358. std::map<objectid_t, std::pair<unsigned int, std::string> > mapSignatureIdToLocal;
  359. objectid_t extcompany;
  360. objectid_t externid;
  361. string signature;
  362. unsigned ulObjectId = 0;
  363. bool bMoved = false;
  364. ECSecurity *lpSecurity = NULL;
  365. UserPlugin *lpPlugin = NULL;
  366. er = GetThreadLocalPlugin(m_lpPluginFactory, &lpPlugin);
  367. if (er != erSuccess)
  368. return er;
  369. er = GetSecurity(&lpSecurity);
  370. if (er != erSuccess)
  371. return er;
  372. if (m_lpSession->GetSessionManager()->IsHostedSupported()) {
  373. /* When hosted is enabled, the companyid _must_ have an external id,
  374. * unless we are requesting the companylist in which case the companyid is 0 and doesn't
  375. * need to be resolved at all.*/
  376. if (objclass != CONTAINER_COMPANY && !IsInternalObject(ulCompanyId)) {
  377. er = GetExternalId(ulCompanyId, &extcompany);
  378. if (er != erSuccess)
  379. return er;
  380. }
  381. } else {
  382. if (objclass == CONTAINER_COMPANY)
  383. return KCERR_NO_SUPPORT;
  384. /* When hosted is disabled, use company id 0 */
  385. ulCompanyId = 0;
  386. }
  387. // Get all the items of the requested type
  388. er = GetLocalObjectIdList(objclass, ulCompanyId, &unique_tie(lpLocalIds));
  389. if (er != erSuccess)
  390. return er;
  391. for (const auto &loc_id : *lpLocalIds) {
  392. if (IsInternalObject(loc_id)) {
  393. // Local user, add it to the result array directly
  394. objectdetails_t details = objectdetails_t();
  395. er = GetLocalObjectDetails(loc_id, &details);
  396. if(er != erSuccess)
  397. return er;
  398. if (ulFlags & USERMANAGEMENT_ADDRESSBOOK)
  399. if (MustHide(*lpSecurity, ulFlags, details))
  400. continue;
  401. // Reset details, this saves time copying unwanted data, but keep the correct class
  402. if (ulFlags & USERMANAGEMENT_IDS_ONLY)
  403. details = objectdetails_t(details.GetClass());
  404. lpObjects->push_back(localobjectdetails_t(loc_id, details));
  405. } else if (GetExternalId(loc_id, &externid, NULL, &signature) == erSuccess) {
  406. mapSignatureIdToLocal.insert(
  407. std::map<objectid_t, std::pair<unsigned int, std::string> >::value_type(externid, make_pair(loc_id, signature)));
  408. } else {
  409. // cached externid not found for local object id
  410. }
  411. }
  412. if (bSync && !bIsSafeMode) {
  413. // We now have a map, mapping external IDs to local user IDs (and their signatures)
  414. try {
  415. // Get full user list
  416. lpExternSignatures = lpPlugin->getAllObjects(extcompany, objclass);
  417. // TODO: check requested 'objclass'
  418. } catch (notsupported &) {
  419. return KCERR_NO_SUPPORT;
  420. } catch (objectnotfound &) {
  421. return KCERR_NOT_FOUND;
  422. } catch(std::exception &e) {
  423. ec_log_warn("Unable to retrieve list from external user source: %s", e.what());
  424. return KCERR_PLUGIN_ERROR;
  425. }
  426. // Loop through all the external signatures, adding them to the lpUsers list which we're going to be returning
  427. for (const auto &ext_sig : *lpExternSignatures) {
  428. auto iterSignatureIdToLocal = mapSignatureIdToLocal.find(ext_sig.id);
  429. if (iterSignatureIdToLocal == mapSignatureIdToLocal.cend()) {
  430. // User is in external user database, but not in local, so add
  431. er = MoveOrCreateLocalObject(ext_sig, &ulObjectId, &bMoved);
  432. if(er != erSuccess) {
  433. // Create failed, so skip this entry
  434. er = erSuccess;
  435. continue;
  436. }
  437. // Entry was moved rather then created, this means that in our localIdList we have
  438. // an entry which matches this object.
  439. if (bMoved)
  440. for (auto i = mapSignatureIdToLocal.begin();
  441. i != mapSignatureIdToLocal.cend(); ++i)
  442. if (i->second.first == ulObjectId) {
  443. mapSignatureIdToLocal.erase(i);
  444. break;
  445. }
  446. } else {
  447. ulObjectId = iterSignatureIdToLocal->second.first;
  448. er = CheckObjectModified(ulObjectId, // Object id
  449. iterSignatureIdToLocal->second.second, // local signature
  450. ext_sig.signature); // remote signature
  451. if (er != erSuccess)
  452. return er;
  453. // Remove the external user from the list of internal IDs,
  454. // since we don't need this entry for the plugin details match
  455. mapSignatureIdToLocal.erase(iterSignatureIdToLocal);
  456. }
  457. // Add to conversion map so we can obtain the details
  458. mapExternIdToLocal.insert(make_pair(ext_sig.id, ulObjectId));
  459. }
  460. } else {
  461. if (bIsSafeMode)
  462. ec_log_info("user_safe_mode: skipping retrieve/sync users from LDAP");
  463. lpExternSignatures.reset(new signatures_t());
  464. // Dont sync, just use whatever is in the local user database
  465. for (const auto &sil : mapSignatureIdToLocal) {
  466. lpExternSignatures->push_back(objectsignature_t(sil.first, sil.second.second));
  467. mapExternIdToLocal.insert(std::make_pair(sil.first, sil.second.first));
  468. }
  469. }
  470. er = GetLocalObjectListFromSignatures(*lpExternSignatures, mapExternIdToLocal, ulFlags, lpObjects.get());
  471. if (er != erSuccess)
  472. return er;
  473. // mapSignatureIdToLocal is now a map of objects that were NOT in the external user database
  474. if(bSync) {
  475. if (bIsSafeMode)
  476. ec_log_err("user_safe_mode: would normally now delete %lu local users (you may see this message more often as the delete is now omitted)", static_cast<unsigned long>(mapExternIdToLocal.size() - lpExternSignatures->size()));
  477. else
  478. for (const auto &sil : mapSignatureIdToLocal)
  479. /* second == map value, first == id */
  480. MoveOrDeleteLocalObject(sil.second.first, sil.first.objclass);
  481. }
  482. // Convert details for client usage
  483. if (!(ulFlags & USERMANAGEMENT_IDS_ONLY)) {
  484. for (auto &obj : *lpObjects) {
  485. if (IsInternalObject(obj.ulId))
  486. continue;
  487. er = UpdateUserDetailsToClient(&obj);
  488. if (er != erSuccess)
  489. return er;
  490. }
  491. }
  492. if (lppObjects) {
  493. lpObjects->sort();
  494. *lppObjects = lpObjects.release();
  495. }
  496. return erSuccess;
  497. }
  498. ECRESULT ECUserManagement::GetSubObjectsOfObjectAndSync(userobject_relation_t relation, unsigned int ulParentId,
  499. std::list<localobjectdetails_t> **lppObjects, unsigned int ulFlags)
  500. {
  501. ECRESULT er = erSuccess;
  502. // Return data
  503. std::unique_ptr<std::list<localobjectdetails_t> > lpObjects(new std::list<localobjectdetails_t>);
  504. std::unique_ptr<std::list<localobjectdetails_t> > lpCompanies;
  505. // Extern ids
  506. std::unique_ptr<signatures_t> lpSignatures;
  507. // Extern -> Local
  508. map<objectid_t, unsigned int> mapExternIdToLocal;
  509. objectid_t objectid;
  510. objectdetails_t details;
  511. UserPlugin *lpPlugin = NULL;
  512. er = GetThreadLocalPlugin(m_lpPluginFactory, &lpPlugin);
  513. if(er != erSuccess)
  514. return er;
  515. if (!m_lpSession->GetSessionManager()->IsHostedSupported() &&
  516. (relation == OBJECTRELATION_COMPANY_VIEW ||
  517. relation == OBJECTRELATION_COMPANY_ADMIN))
  518. return KCERR_NO_SUPPORT;
  519. // The 'everyone' group contains all visible users for the currently logged in user.
  520. if (relation == OBJECTRELATION_GROUP_MEMBER && ulParentId == KOPANO_UID_EVERYONE) {
  521. ECSecurity *lpSecurity = NULL;
  522. er = GetSecurity(&lpSecurity);
  523. if (er != erSuccess)
  524. return er;
  525. er = lpSecurity->GetViewableCompanyIds(ulFlags, &unique_tie(lpCompanies));
  526. if (er != erSuccess)
  527. return er;
  528. /* Fallback in case hosted is not supported */
  529. if (lpCompanies->empty())
  530. lpCompanies->push_back(localobjectdetails_t(0, CONTAINER_COMPANY));
  531. for (const auto &obj : *lpCompanies) {
  532. std::unique_ptr<std::list<localobjectdetails_t> > lpObjectsTmp;
  533. er = GetCompanyObjectListAndSync(OBJECTCLASS_UNKNOWN,
  534. obj.ulId, &unique_tie(lpObjectsTmp),
  535. ulFlags | USERMANAGEMENT_SHOW_HIDDEN);
  536. if (er != erSuccess)
  537. return er;
  538. lpObjects->merge(*lpObjectsTmp);
  539. }
  540. // TODO: remove excessive objects from lpObjects ? seems that this list is going to contain a lot... maybe too much?
  541. } else {
  542. er = GetExternalId(ulParentId, &objectid);
  543. if(er != erSuccess)
  544. return er;
  545. try {
  546. lpSignatures = lpPlugin->getSubObjectsForObject(relation, objectid);
  547. } catch(objectnotfound &) {
  548. MoveOrDeleteLocalObject(ulParentId, objectid.objclass);
  549. return KCERR_NOT_FOUND;
  550. } catch (notsupported &) {
  551. return KCERR_NO_SUPPORT;
  552. } catch(std::exception &e) {
  553. ec_log_warn("Unable to retrieve members for relation %s: %s.", RelationTypeToName(relation), e.what());
  554. return KCERR_PLUGIN_ERROR;
  555. }
  556. er = GetLocalObjectsIdsOrCreate(*lpSignatures, &mapExternIdToLocal);
  557. if (er != erSuccess)
  558. return er;
  559. er = GetLocalObjectListFromSignatures(*lpSignatures, mapExternIdToLocal, ulFlags | USERMANAGEMENT_SHOW_HIDDEN, lpObjects.get());
  560. if (er != erSuccess)
  561. return er;
  562. }
  563. // Convert details for client usage
  564. if (!(ulFlags & USERMANAGEMENT_IDS_ONLY)) {
  565. for (auto &obj : *lpObjects) {
  566. if (IsInternalObject(obj.ulId))
  567. continue;
  568. er = UpdateUserDetailsToClient(&obj);
  569. if (er != erSuccess)
  570. return er;
  571. }
  572. }
  573. /*
  574. * We now have the full list of objects coming from the plugin.
  575. * NOTE: This list is not fool proof, the caller should check
  576. * which entries are visible to the user and which ones are not.
  577. * We cannot do that here since ECSecurity calls this function
  578. * when determining if an object is visible. And endless recursive
  579. * loops are not really a pretty sight.
  580. */
  581. if (lppObjects) {
  582. lpObjects->sort();
  583. lpObjects->unique();
  584. *lppObjects = lpObjects.release();
  585. }
  586. return er;
  587. }
  588. // Get parent objects which an object belongs, with on-the-fly delete of the specified parent object
  589. ECRESULT ECUserManagement::GetParentObjectsOfObjectAndSync(userobject_relation_t relation, unsigned int ulChildId,
  590. std::list<localobjectdetails_t> **lppObjects, unsigned int ulFlags) {
  591. ECRESULT er = erSuccess;
  592. // Return data
  593. std::unique_ptr<std::list<localobjectdetails_t> > lpObjects(new std::list<localobjectdetails_t>());
  594. // Extern ids
  595. std::unique_ptr<signatures_t> lpSignatures;
  596. // Extern -> Local
  597. map<objectid_t, unsigned int> mapExternIdToLocal;
  598. objectid_t objectid;
  599. objectdetails_t details;
  600. unsigned int ulObjectId = 0;
  601. ECSecurity *lpSecurity = NULL;
  602. UserPlugin *lpPlugin = NULL;
  603. er = GetThreadLocalPlugin(m_lpPluginFactory, &lpPlugin);
  604. if (er != erSuccess)
  605. return er;
  606. er = GetSecurity(&lpSecurity);
  607. if (er != erSuccess)
  608. return er;
  609. if (!m_lpSession->GetSessionManager()->IsHostedSupported() &&
  610. (relation == OBJECTRELATION_COMPANY_VIEW ||
  611. relation == OBJECTRELATION_COMPANY_ADMIN))
  612. return KCERR_NO_SUPPORT;
  613. if (relation == OBJECTRELATION_GROUP_MEMBER && ulChildId == KOPANO_UID_SYSTEM) {
  614. // System has no objects
  615. } else {
  616. er = GetExternalId(ulChildId, &objectid);
  617. if (er != erSuccess)
  618. return er;
  619. try {
  620. lpSignatures = lpPlugin->getParentObjectsForObject(relation, objectid);
  621. } catch(objectnotfound &) {
  622. MoveOrDeleteLocalObject(ulChildId, objectid.objclass);
  623. return KCERR_NOT_FOUND;
  624. } catch (notsupported &) {
  625. return KCERR_NO_SUPPORT;
  626. } catch(std::exception &e) {
  627. ec_log_warn("Unable to retrieve parents for relation %s: %s.", RelationTypeToName(relation), e.what());
  628. return KCERR_PLUGIN_ERROR;
  629. }
  630. er = GetLocalObjectsIdsOrCreate(*lpSignatures, &mapExternIdToLocal);
  631. if (er != erSuccess)
  632. return er;
  633. er = GetLocalObjectListFromSignatures(*lpSignatures, mapExternIdToLocal, ulFlags, lpObjects.get());
  634. if (er != erSuccess)
  635. return er;
  636. }
  637. // If we are requesting group membership we should always insert the everyone, since everyone is member of EVERYONE except for SYSTEM
  638. if (relation == OBJECTRELATION_GROUP_MEMBER && ulObjectId != KOPANO_UID_SYSTEM) {
  639. if ((!(ulFlags & USERMANAGEMENT_IDS_ONLY)) || (ulFlags & USERMANAGEMENT_ADDRESSBOOK)) {
  640. er = GetLocalObjectDetails(KOPANO_UID_EVERYONE, &details);
  641. if(er != erSuccess)
  642. return er;
  643. if (!(ulFlags & USERMANAGEMENT_ADDRESSBOOK) ||
  644. (lpSecurity->GetUserId() != KOPANO_UID_SYSTEM &&
  645. !details.GetPropBool(OB_PROP_B_AB_HIDDEN)))
  646. {
  647. if (ulFlags & USERMANAGEMENT_IDS_ONLY)
  648. details = objectdetails_t(details.GetClass());
  649. lpObjects->push_back(localobjectdetails_t(KOPANO_UID_EVERYONE, details));
  650. }
  651. } else
  652. lpObjects->push_back(localobjectdetails_t(KOPANO_UID_EVERYONE, objectdetails_t(DISTLIST_SECURITY)));
  653. }
  654. // Convert details for client usage
  655. if (!(ulFlags & USERMANAGEMENT_IDS_ONLY)) {
  656. for (auto &obj : *lpObjects) {
  657. if (IsInternalObject(obj.ulId))
  658. continue;
  659. er = UpdateUserDetailsToClient(&obj);
  660. if (er != erSuccess)
  661. return er;
  662. }
  663. }
  664. /*
  665. * We now have the full list of objects coming from the plugin.
  666. * NOTE: This list is not fool proof, the caller should check
  667. * which entries are visible to the user and which ones are not.
  668. * We cannot do that here since ECSecurity calls this function
  669. * when determining if an object is visible. And endless recursive
  670. * loops are not really a pretty sight.
  671. */
  672. if (lppObjects) {
  673. lpObjects->sort();
  674. lpObjects->unique();
  675. *lppObjects = lpObjects.release();
  676. }
  677. return er;
  678. }
  679. // Set data for a single object, with on-the-fly delete of the specified object id
  680. ECRESULT ECUserManagement::SetObjectDetailsAndSync(unsigned int ulObjectId, const objectdetails_t &sDetails, std::list<std::string> *lpRemoveProps) {
  681. ECRESULT er;
  682. objectid_t objectid;
  683. UserPlugin *lpPlugin = NULL;
  684. er = GetThreadLocalPlugin(m_lpPluginFactory, &lpPlugin);
  685. if(er != erSuccess)
  686. return er;
  687. // Resolve the objectname and sync the object into local database
  688. er = GetExternalId(ulObjectId, &objectid);
  689. if (er != erSuccess)
  690. return er;
  691. try {
  692. lpPlugin->changeObject(objectid, sDetails, lpRemoveProps);
  693. } catch (objectnotfound &) {
  694. MoveOrDeleteLocalObject(ulObjectId, objectid.objclass);
  695. return KCERR_NOT_FOUND;
  696. } catch (notimplemented &e) {
  697. ec_log_warn("Unable to set details for external %s: %s", ObjectClassToName(objectid.objclass), e.what());
  698. return KCERR_NOT_IMPLEMENTED;
  699. } catch (std::exception &e) {
  700. ec_log_warn("Unable to set details for external %s: %s", ObjectClassToName(objectid.objclass), e.what());
  701. return KCERR_PLUGIN_ERROR;
  702. }
  703. // Purge cache
  704. m_lpSession->GetSessionManager()->GetCacheManager()->UpdateUser(ulObjectId);
  705. return erSuccess;
  706. }
  707. // Create an object with specific ID or modify an existing object. Used for sync purposes
  708. ECRESULT ECUserManagement::CreateOrModifyObject(const objectid_t &sExternId, const objectdetails_t &sDetails, unsigned int ulPreferredId, std::list<std::string> *lpRemoveProps) {
  709. ECRESULT er;
  710. objectsignature_t signature;
  711. UserPlugin *lpPlugin = NULL;
  712. unsigned int ulObjectId = 0;
  713. // Local items have no external id
  714. if (sExternId.id.empty())
  715. return erSuccess;
  716. er = GetThreadLocalPlugin(m_lpPluginFactory, &lpPlugin);
  717. if(er != erSuccess)
  718. return er;
  719. // Resolve the objectname and sync the object into local database
  720. if (GetLocalId(sExternId, &ulObjectId, &signature.signature) == erSuccess) {
  721. try {
  722. lpPlugin->changeObject(sExternId, sDetails, lpRemoveProps);
  723. } catch (objectnotfound &) {
  724. MoveOrDeleteLocalObject(ulObjectId, sExternId.objclass);
  725. return KCERR_NOT_FOUND;
  726. } catch (notimplemented &e) {
  727. ec_log_warn("Unable to set details for external %s (1): %s", ObjectClassToName(sExternId.objclass), e.what());
  728. return KCERR_NOT_IMPLEMENTED;
  729. } catch (std::exception &e) {
  730. ec_log_warn("Unable to set details for external %s (2): %s", ObjectClassToName(sExternId.objclass), e.what());
  731. return KCERR_PLUGIN_ERROR;
  732. }
  733. } else {
  734. // Object does not exist yet, create in external database
  735. try {
  736. signature = lpPlugin->createObject(sDetails);
  737. } catch (notimplemented &e) {
  738. ec_log_warn("Unable to create %s in external database(1): %s", ObjectClassToName(sExternId.objclass), e.what());
  739. return KCERR_NOT_IMPLEMENTED;
  740. } catch (collision_error &e) {
  741. ec_log_warn("Unable to create %s in external database(2): %s", ObjectClassToName(sExternId.objclass), e.what());
  742. return KCERR_COLLISION;
  743. } catch (std::exception &e) {
  744. ec_log_warn("Unable to create %s in external database(3): %s", ObjectClassToName(sExternId.objclass), e.what());
  745. return KCERR_PLUGIN_ERROR;
  746. }
  747. er = CreateLocalObjectSimple(signature, ulPreferredId);
  748. if(er != erSuccess) {
  749. /*
  750. * Failed to create object locally, it should be removed from external DB.
  751. * Otherwise it will remain present in the external DB and synced later while
  752. * ECSync will also perform a retry to create the object. Obviously this
  753. * can lead to unexpected behavior because the Offline server should never(!)
  754. * sync between plugin and ECUsermanagement somewhere else other than this
  755. * function
  756. */
  757. try {
  758. lpPlugin->deleteObject(signature.id);
  759. } catch (std::exception &e) {
  760. ec_log_warn("Unable to delete %s from external database after failed (partial) creation: %s",
  761. ObjectClassToName(sExternId.objclass), e.what());
  762. }
  763. return er;
  764. }
  765. }
  766. return erSuccess;
  767. }
  768. // Add a member to a group, with on-the-fly delete of the specified group id
  769. ECRESULT ECUserManagement::AddSubObjectToObjectAndSync(userobject_relation_t relation, unsigned int ulParentId, unsigned int ulChildId) {
  770. ECRESULT er;
  771. ABEID eid(MAPI_ABCONT, MUIDECSAB, 1);
  772. objectid_t parentid;
  773. objectid_t childid;
  774. SOURCEKEY sSourceKey;
  775. UserPlugin *lpPlugin = NULL;
  776. /* We don't support operations on local items */
  777. if (IsInternalObject(ulParentId) || IsInternalObject(ulChildId))
  778. return KCERR_INVALID_PARAMETER;
  779. er = GetThreadLocalPlugin(m_lpPluginFactory, &lpPlugin);
  780. if(er != erSuccess)
  781. return er;
  782. er = GetExternalId(ulParentId, &parentid);
  783. if(er != erSuccess)
  784. return er;
  785. er = GetExternalId(ulChildId, &childid);
  786. if(er != erSuccess)
  787. return er;
  788. try {
  789. lpPlugin->addSubObjectRelation(relation, parentid, childid);
  790. } catch(objectnotfound &) {
  791. MoveOrDeleteLocalObject(ulParentId, parentid.objclass);
  792. return KCERR_NOT_FOUND;
  793. } catch (notimplemented &) {
  794. return KCERR_NOT_IMPLEMENTED;
  795. } catch (collision_error &) {
  796. ec_log_crit("ECUserManagement::AddSubObjectToObjectAndSync(): addSubObjectRelation failed with a collision error");
  797. return KCERR_COLLISION;
  798. } catch(std::exception &e) {
  799. ec_log_warn("Unable to add relation %s to external user database: %s", RelationTypeToName(relation), e.what());
  800. return KCERR_PLUGIN_ERROR;
  801. }
  802. /*
  803. * Add addressbook change, note that setting the objectrelation won't update the signature
  804. * which means we have to run this update manually. Also note that when adding a child to a
  805. * group the _group_ has changed, but when adding a child to a company the _child_ has changed.
  806. * This will cause the sync to recreate the correct object with the updated rights.
  807. */
  808. if (relation == OBJECTRELATION_GROUP_MEMBER)
  809. er = GetABSourceKeyV1(ulParentId, &sSourceKey);
  810. else if (relation == OBJECTRELATION_COMPANY_ADMIN || relation == OBJECTRELATION_COMPANY_VIEW)
  811. er = GetABSourceKeyV1(ulChildId, &sSourceKey);
  812. if (relation == OBJECTRELATION_GROUP_MEMBER || relation == OBJECTRELATION_COMPANY_ADMIN || relation == OBJECTRELATION_COMPANY_VIEW)
  813. {
  814. if (er != erSuccess)
  815. return er;
  816. AddABChange(m_lpSession, ICS_AB_CHANGE, sSourceKey, SOURCEKEY(CbABEID(&eid), (char *)&eid));
  817. }
  818. m_lpSession->GetSessionManager()->GetCacheManager()->UpdateUser(ulParentId);
  819. return er;
  820. }
  821. ECRESULT ECUserManagement::DeleteSubObjectFromObjectAndSync(userobject_relation_t relation, unsigned int ulParentId, unsigned int ulChildId) {
  822. ECRESULT er;
  823. ABEID eid(MAPI_ABCONT, MUIDECSAB, 1);
  824. objectid_t parentid;
  825. objectid_t childid;
  826. SOURCEKEY sSourceKey;
  827. UserPlugin *lpPlugin = NULL;
  828. /* We don't support operations on local items */
  829. if (IsInternalObject(ulParentId) || IsInternalObject(ulChildId))
  830. return KCERR_INVALID_PARAMETER;
  831. er = GetThreadLocalPlugin(m_lpPluginFactory, &lpPlugin);
  832. if(er != erSuccess)
  833. return er;
  834. er = GetExternalId(ulParentId, &parentid);
  835. if(er != erSuccess)
  836. return er;
  837. er = GetExternalId(ulChildId, &childid);
  838. if(er != erSuccess)
  839. return er;
  840. try {
  841. lpPlugin->deleteSubObjectRelation(relation, parentid, childid);
  842. } catch(objectnotfound &) {
  843. // We can't delete the parent because we don't know whether the child or the entire parent was not found
  844. return KCERR_NOT_FOUND;
  845. } catch (notimplemented &) {
  846. return KCERR_NOT_IMPLEMENTED;
  847. } catch(std::exception &e) {
  848. ec_log_warn("Unable to remove relation %s from external user database: %s.", RelationTypeToName(relation), e.what());
  849. return KCERR_PLUGIN_ERROR;
  850. }
  851. /*
  852. * Add addressbook change, note that setting the objectrelation won't update the signature
  853. * which means we have to run this update manually. Also note that when deleting a child from a
  854. * group the _group_ has changed, but when deleting a child from a company the _child_ has changed.
  855. * This will cause the sync to recreate the correct object with the updated rights.
  856. */
  857. if (relation == OBJECTRELATION_GROUP_MEMBER)
  858. er = GetABSourceKeyV1(ulParentId, &sSourceKey);
  859. else if (relation == OBJECTRELATION_COMPANY_ADMIN || relation == OBJECTRELATION_COMPANY_VIEW)
  860. er = GetABSourceKeyV1(ulChildId, &sSourceKey);
  861. if (relation == OBJECTRELATION_GROUP_MEMBER || relation == OBJECTRELATION_COMPANY_ADMIN || relation == OBJECTRELATION_COMPANY_VIEW)
  862. {
  863. if (er != erSuccess)
  864. return er;
  865. AddABChange(m_lpSession, ICS_AB_CHANGE, sSourceKey, SOURCEKEY(CbABEID(&eid), (char *)&eid));
  866. }
  867. m_lpSession->GetSessionManager()->GetCacheManager()->UpdateUser(ulParentId);
  868. return er;
  869. }
  870. // TODO: cache these values ?
  871. ECRESULT ECUserManagement::ResolveObject(objectclass_t objclass, const std::string &strName, const objectid_t &sCompany, objectid_t *lpsExternId)
  872. {
  873. ECRESULT er;
  874. objectid_t sObjectId;
  875. UserPlugin *lpPlugin = NULL;
  876. objectsignature_t objectsignature;
  877. er = GetThreadLocalPlugin(m_lpPluginFactory, &lpPlugin);
  878. if(er != erSuccess)
  879. return er;
  880. try {
  881. objectsignature = lpPlugin->resolveName(objclass, strName, sCompany);
  882. }
  883. catch (objectnotfound &) {
  884. return KCERR_NOT_FOUND;
  885. }
  886. catch (std::exception &) {
  887. return KCERR_PLUGIN_ERROR;
  888. }
  889. *lpsExternId = std::move(objectsignature.id);
  890. return erSuccess;
  891. }
  892. // Resolve an object name to an object id, with on-the-fly create of the specified object class
  893. ECRESULT ECUserManagement::ResolveObjectAndSync(objectclass_t objclass, const char* szName, unsigned int* lpulObjectId) {
  894. ECRESULT er = KCERR_INVALID_PARAMETER;
  895. objectsignature_t objectsignature;
  896. string username;
  897. string companyname;
  898. UserPlugin *lpPlugin = NULL;
  899. bool bHosted = m_lpSession->GetSessionManager()->IsHostedSupported();
  900. objectid_t sCompany(CONTAINER_COMPANY);
  901. if (!szName) {
  902. ec_log_crit("Invalid argument szName in call to ECUserManagement::ResolveObjectAndSync()");
  903. return er;
  904. }
  905. if (!lpulObjectId) {
  906. ec_log_crit("Invalid argument lpulObjectId call to ECUserManagement::ResolveObjectAndSync()");
  907. return er;
  908. }
  909. er = erSuccess;
  910. if ((OBJECTCLASS_TYPE(objclass) == OBJECTTYPE_UNKNOWN ||
  911. objclass == OBJECTCLASS_USER ||
  912. objclass == ACTIVE_USER) &&
  913. strcasecmp(szName, KOPANO_ACCOUNT_SYSTEM) == 0)
  914. {
  915. *lpulObjectId = KOPANO_UID_SYSTEM;
  916. return er;
  917. } else if ((OBJECTCLASS_TYPE(objclass) == OBJECTTYPE_UNKNOWN ||
  918. objclass == OBJECTCLASS_DISTLIST ||
  919. objclass == DISTLIST_SECURITY) &&
  920. strcasecmp(szName, KOPANO_FULLNAME_EVERYONE) == 0)
  921. {
  922. *lpulObjectId = KOPANO_UID_EVERYONE;
  923. return er;
  924. } else if ((OBJECTCLASS_TYPE(objclass) == OBJECTTYPE_UNKNOWN ||
  925. objclass == OBJECTCLASS_CONTAINER ||
  926. objclass == CONTAINER_COMPANY) &&
  927. strlen(szName) == 0)
  928. {
  929. *lpulObjectId = 0;
  930. return er;
  931. }
  932. if (bHosted &&
  933. (OBJECTCLASS_TYPE(objclass) == OBJECTTYPE_UNKNOWN ||
  934. OBJECTCLASS_TYPE(objclass) == OBJECTTYPE_MAILUSER ||
  935. OBJECTCLASS_TYPE(objclass) == OBJECTTYPE_DISTLIST) &&
  936. objclass != NONACTIVE_CONTACT) {
  937. /*
  938. * GetUserAndCompanyFromLoginName() will return KCWARN_PARTIAL_COMPLETION
  939. * when the companyname could not be determined from the username. This isn't
  940. * something bad since perhaps the username is unique between all companies.
  941. */
  942. er = GetUserAndCompanyFromLoginName(szName, &username, &companyname);
  943. if (er == KCWARN_PARTIAL_COMPLETION)
  944. er = erSuccess;
  945. else if (er != erSuccess)
  946. return er;
  947. } else {
  948. username = szName;
  949. companyname.clear();
  950. }
  951. if (bHosted && !companyname.empty()) {
  952. er = ResolveObject(CONTAINER_COMPANY, companyname, objectid_t(), &sCompany);
  953. if (er == KCERR_NOT_FOUND) {
  954. ec_log_warn("Search company error by plugin for company \"%s\"", companyname.c_str());
  955. return er;
  956. }
  957. }
  958. er = GetThreadLocalPlugin(m_lpPluginFactory, &lpPlugin);
  959. if(er != erSuccess)
  960. return er;
  961. try {
  962. objectsignature = lpPlugin->resolveName(objclass, username, sCompany);
  963. } catch (notsupported &) {
  964. return KCERR_NO_SUPPORT;
  965. } catch (objectnotfound &e) {
  966. ec_log_warn("Object not found %s \"%s\": %s", ObjectClassToName(objclass), szName, e.what());
  967. return KCERR_NOT_FOUND;
  968. } catch(std::exception &e) {
  969. ec_log_warn("Unable to resolve %s \"%s\": %s", ObjectClassToName(objclass), szName, e.what());
  970. return KCERR_NOT_FOUND;
  971. }
  972. return GetLocalObjectIdOrCreate(objectsignature, lpulObjectId);
  973. }
  974. // Get MAPI property data for a group or user/group id, with on-the-fly delete of the specified user/group
  975. ECRESULT ECUserManagement::GetProps(struct soap *soap, unsigned int ulId, struct propTagArray *lpPropTagArray, struct propValArray *lpPropValArray) {
  976. ECRESULT er;
  977. objectdetails_t objectdetails;
  978. er = GetObjectDetails(ulId, &objectdetails);
  979. if (er != erSuccess)
  980. return KCERR_NOT_FOUND;
  981. return ConvertObjectDetailsToProps(soap, ulId, &objectdetails, lpPropTagArray, lpPropValArray);
  982. }
  983. ECRESULT ECUserManagement::GetContainerProps(struct soap *soap, unsigned int ulObjectId, struct propTagArray *lpPropTagArray, struct propValArray *lpPropValArray)
  984. {
  985. ECRESULT er;
  986. ECSecurity *lpSecurity = NULL;
  987. objectdetails_t objectdetails;
  988. if (ulObjectId == KOPANO_UID_ADDRESS_BOOK ||
  989. ulObjectId == KOPANO_UID_GLOBAL_ADDRESS_BOOK ||
  990. ulObjectId == KOPANO_UID_GLOBAL_ADDRESS_LISTS)
  991. {
  992. er = ConvertABContainerToProps(soap, ulObjectId, lpPropTagArray, lpPropValArray);
  993. } else {
  994. er = GetObjectDetails(ulObjectId, &objectdetails);
  995. if (er != erSuccess)
  996. return KCERR_NOT_FOUND;
  997. er = GetSecurity(&lpSecurity);
  998. if (er != erSuccess)
  999. return er;
  1000. er = ConvertContainerObjectDetailsToProps(soap, ulObjectId, &objectdetails, lpPropTagArray, lpPropValArray);
  1001. }
  1002. return er;
  1003. }
  1004. // Get local details
  1005. ECRESULT ECUserManagement::GetLocalObjectDetails(unsigned int ulId, objectdetails_t *lpDetails) {
  1006. ECRESULT er = erSuccess;
  1007. objectdetails_t sDetails;
  1008. objectdetails_t sPublicStoreDetails;
  1009. ECSecurity *lpSecurity = NULL;
  1010. if(ulId == KOPANO_UID_SYSTEM) {
  1011. sDetails = objectdetails_t(ACTIVE_USER);
  1012. sDetails.SetPropString(OB_PROP_S_LOGIN, KOPANO_ACCOUNT_SYSTEM);
  1013. sDetails.SetPropString(OB_PROP_S_PASSWORD, "");
  1014. sDetails.SetPropString(OB_PROP_S_FULLNAME, KOPANO_FULLNAME_SYSTEM);
  1015. sDetails.SetPropString(OB_PROP_S_EMAIL, m_lpConfig->GetSetting("system_email_address"));
  1016. sDetails.SetPropInt(OB_PROP_I_ADMINLEVEL, ADMIN_LEVEL_SYSADMIN);
  1017. sDetails.SetPropBool(OB_PROP_B_AB_HIDDEN, parseBool(m_lpConfig->GetSetting("hide_system")));
  1018. sDetails.SetPropString(OB_PROP_S_SERVERNAME, m_lpConfig->GetSetting("server_name", "", "Unknown"));
  1019. } else if (ulId == KOPANO_UID_EVERYONE) {
  1020. er = GetSecurity(&lpSecurity);
  1021. if (er != erSuccess)
  1022. return er;
  1023. sDetails = objectdetails_t(DISTLIST_SECURITY);
  1024. sDetails.SetPropString(OB_PROP_S_LOGIN, KOPANO_ACCOUNT_EVERYONE);
  1025. sDetails.SetPropString(OB_PROP_S_FULLNAME, KOPANO_FULLNAME_EVERYONE);
  1026. sDetails.SetPropBool(OB_PROP_B_AB_HIDDEN, parseBool(m_lpConfig->GetSetting("hide_everyone")) && lpSecurity->GetAdminLevel() == 0);
  1027. if (m_lpSession->GetSessionManager()->IsDistributedSupported() &&
  1028. GetPublicStoreDetails(&sPublicStoreDetails) == erSuccess)
  1029. sDetails.SetPropString(OB_PROP_S_SERVERNAME, sPublicStoreDetails.GetPropString(OB_PROP_S_SERVERNAME));
  1030. }
  1031. *lpDetails = std::move(sDetails);
  1032. return erSuccess;
  1033. }
  1034. // Get remote details
  1035. ECRESULT ECUserManagement::GetExternalObjectDetails(unsigned int ulId, objectdetails_t *lpDetails)
  1036. {
  1037. ECRESULT er;
  1038. std::unique_ptr<objectdetails_t> details;
  1039. objectdetails_t detailscached;
  1040. objectid_t externid;
  1041. UserPlugin *lpPlugin = NULL;
  1042. er = GetExternalId(ulId, &externid);
  1043. if (er != erSuccess)
  1044. return er;
  1045. er = m_lpSession->GetSessionManager()->GetCacheManager()->GetUserDetails(ulId, &detailscached);
  1046. if (er == erSuccess) {
  1047. // @todo should compare signature, and see if we need new details for this user
  1048. er = UpdateUserDetailsToClient(&detailscached);
  1049. if (er != erSuccess)
  1050. return er;
  1051. *lpDetails = std::move(detailscached);
  1052. return erSuccess;
  1053. }
  1054. er = GetThreadLocalPlugin(m_lpPluginFactory, &lpPlugin);
  1055. if(er != erSuccess)
  1056. return er;
  1057. try {
  1058. details = lpPlugin->getObjectDetails(externid);
  1059. } catch (objectnotfound &) {
  1060. /*
  1061. * MoveOrDeleteLocalObject() will call this function to determine if the
  1062. * object has been moved. So do _not_ call MoveOrDeleteLocalObject() from
  1063. * this location!.
  1064. */
  1065. DeleteLocalObject(ulId, externid.objclass);
  1066. return KCERR_NOT_FOUND;
  1067. } catch (notsupported &) {
  1068. return KCERR_NO_SUPPORT;
  1069. } catch (std::exception &e) {
  1070. ec_log_warn("Unable to get %s details for object id %d: %s", ObjectClassToName(externid.objclass), ulId, e.what());
  1071. return KCERR_NOT_FOUND;
  1072. }
  1073. /* Update cache so we don't have to bug the plugin until the data has changed.
  1074. * Note that we don't care if the update succeeded, if it fails we will retry
  1075. * when the user details are requested for a second time. */
  1076. m_lpSession->GetSessionManager()->GetCacheManager()->SetUserDetails(ulId, details.get());
  1077. if (! IsInternalObject(ulId)) {
  1078. er = UpdateUserDetailsToClient(details.get());
  1079. if (er != erSuccess)
  1080. return er;
  1081. }
  1082. *lpDetails = *details;
  1083. return erSuccess;
  1084. }
  1085. // **************************************************************************************************************************************
  1086. //
  1087. // Functions that actually do the SQL
  1088. //
  1089. // **************************************************************************************************************************************
  1090. ECRESULT ECUserManagement::GetExternalId(unsigned int ulId, objectid_t *lpExternId, unsigned int *lpulCompanyId, std::string *lpSignature)
  1091. {
  1092. if (IsInternalObject(ulId))
  1093. return KCERR_INVALID_PARAMETER;
  1094. auto mgr = m_lpSession->GetSessionManager()->GetCacheManager();
  1095. return mgr->GetUserObject(ulId, lpExternId, lpulCompanyId, lpSignature);
  1096. }
  1097. ECRESULT ECUserManagement::GetLocalId(const objectid_t &sExternId, unsigned int *lpulId, std::string *lpSignature)
  1098. {
  1099. return m_lpSession->GetSessionManager()->GetCacheManager()->GetUserObject(sExternId, lpulId, NULL, lpSignature);
  1100. }
  1101. ECRESULT ECUserManagement::GetLocalObjectIdOrCreate(const objectsignature_t &sSignature, unsigned int *lpulObjectId)
  1102. {
  1103. ECRESULT er;
  1104. unsigned ulObjectId = 0;
  1105. er = m_lpSession->GetSessionManager()->GetCacheManager()->GetUserObject(sSignature.id, &ulObjectId, NULL, NULL);
  1106. if (er == KCERR_NOT_FOUND)
  1107. er = MoveOrCreateLocalObject(sSignature, &ulObjectId, NULL);
  1108. if (er != erSuccess)
  1109. return er;
  1110. if (lpulObjectId)
  1111. *lpulObjectId = ulObjectId;
  1112. return erSuccess;
  1113. }
  1114. ECRESULT ECUserManagement::GetLocalObjectsIdsOrCreate(const list<objectsignature_t> &lstSignatures, map<objectid_t, unsigned int> *lpmapLocalObjIds)
  1115. {
  1116. ECRESULT er;
  1117. list<objectid_t> lstExternObjIds;
  1118. unsigned int ulObjectId;
  1119. for (const auto &sig : lstSignatures)
  1120. lstExternObjIds.push_back(sig.id);
  1121. er = m_lpSession->GetSessionManager()->GetCacheManager()->GetUserObjects(lstExternObjIds, lpmapLocalObjIds);
  1122. if (er != erSuccess)
  1123. return er;
  1124. for (const auto &sig : lstSignatures) {
  1125. auto result = lpmapLocalObjIds->insert(make_pair(sig.id, 0));
  1126. if (result.second == false)
  1127. // object already exists
  1128. continue;
  1129. er = MoveOrCreateLocalObject(sig, &ulObjectId, NULL);
  1130. if (er != erSuccess) {
  1131. lpmapLocalObjIds->erase(result.first);
  1132. return er;
  1133. }
  1134. result.first->second = ulObjectId;
  1135. }
  1136. return erSuccess;
  1137. }
  1138. ECRESULT ECUserManagement::GetLocalObjectIdList(objectclass_t objclass, unsigned int ulCompanyId, std::list<unsigned int> **lppObjects)
  1139. {
  1140. ECRESULT er = erSuccess;
  1141. ECDatabase *lpDatabase = NULL;
  1142. DB_RESULT lpResult;
  1143. DB_ROW lpRow = NULL;
  1144. std::unique_ptr<std::list<unsigned int> > lpObjects(new std::list<unsigned int>);
  1145. string strQuery;
  1146. er = m_lpSession->GetDatabase(&lpDatabase);
  1147. if (er != erSuccess)
  1148. return er;
  1149. strQuery =
  1150. "SELECT id FROM users "
  1151. "WHERE " + OBJECTCLASS_COMPARE_SQL("objectclass", objclass);
  1152. /* As long as the Offline server has partial hosted support,
  1153. * we must comment out this additional where statement... */
  1154. if (m_lpSession->GetSessionManager()->IsHostedSupported())
  1155. /* Everyone and SYSTEM don't have a company but must be
  1156. * included by the query, so write exception case for them */
  1157. strQuery +=
  1158. " AND ("
  1159. "company = " + stringify(ulCompanyId) + " "
  1160. "OR id = " + stringify(ulCompanyId) + " "
  1161. "OR id = " + stringify(KOPANO_UID_SYSTEM) + " "
  1162. "OR id = " + stringify(KOPANO_UID_EVERYONE) + ")";
  1163. er = lpDatabase->DoSelect(strQuery, &lpResult);
  1164. if (er != erSuccess)
  1165. return er;
  1166. while(1) {
  1167. lpRow = lpDatabase->FetchRow(lpResult);
  1168. if(lpRow == NULL)
  1169. break;
  1170. if(lpRow[0] == NULL)
  1171. continue;
  1172. lpObjects->push_back(atoi(lpRow[0]));
  1173. }
  1174. *lppObjects = lpObjects.release();
  1175. return erSuccess;
  1176. }
  1177. ECRESULT ECUserManagement::CreateObjectAndSync(const objectdetails_t &details, unsigned int *lpulId)
  1178. {
  1179. ECRESULT er;
  1180. objectsignature_t objectsignature;
  1181. UserPlugin *lpPlugin = NULL;
  1182. er = GetThreadLocalPlugin(m_lpPluginFactory, &lpPlugin);
  1183. if(er != erSuccess)
  1184. return er;
  1185. try {
  1186. objectsignature = lpPlugin->createObject(details);
  1187. } catch (notimplemented &e) {
  1188. ec_log_warn("Unable to create %s in user database(1): %s", ObjectClassToName(details.GetClass()), e.what());
  1189. return KCERR_NOT_IMPLEMENTED;
  1190. } catch (collision_error &e) {
  1191. ec_log_warn("Unable to create %s in user database(2): %s", ObjectClassToName(details.GetClass()), e.what());
  1192. return KCERR_COLLISION;
  1193. } catch(std::exception &e) {
  1194. ec_log_warn("Unable to create %s in user database(3): %s", ObjectClassToName(details.GetClass()), e.what());
  1195. return KCERR_PLUGIN_ERROR;
  1196. }
  1197. er = MoveOrCreateLocalObject(objectsignature, lpulId, NULL);
  1198. if(er != erSuccess) {
  1199. // We could not create the object in the local database. This means that we have to rollback
  1200. // our object creation in the plugin, because otherwise the database and the plugin would stay
  1201. // out-of-sync until you add new licenses. Also, it would be impossible for the user to manually
  1202. // rollback the object creation, as you need a Kopano object id to delete an object, and we don't have
  1203. // one of those, because CreateLocalObject() failed.
  1204. try {
  1205. lpPlugin->deleteObject(objectsignature.id);
  1206. } catch(std::exception &e) {
  1207. ec_log_warn("Unable to delete %s from plugin database after failed creation: %s", ObjectClassToName(details.GetClass()), e.what());
  1208. return KCERR_PLUGIN_ERROR;
  1209. }
  1210. }
  1211. return er;
  1212. }
  1213. ECRESULT ECUserManagement::DeleteObjectAndSync(unsigned int ulId)
  1214. {
  1215. ECRESULT er;
  1216. objectid_t objectid;
  1217. UserPlugin *lpPlugin = NULL;
  1218. er = GetThreadLocalPlugin(m_lpPluginFactory, &lpPlugin);
  1219. if(er != erSuccess)
  1220. return er;
  1221. er = GetExternalId(ulId, &objectid);
  1222. if (er != erSuccess)
  1223. return er;
  1224. try {
  1225. lpPlugin->deleteObject(objectid);
  1226. } catch (objectnotfound &) {
  1227. return KCERR_NOT_FOUND;
  1228. } catch (notimplemented &e) {
  1229. ec_log_warn("Unable to delete %s in user database: %s", ObjectClassToName(objectid.objclass), e.what());
  1230. return KCERR_NOT_IMPLEMENTED;
  1231. } catch(std::exception &e) {
  1232. ec_log_warn("Unable to delete %s in user database: %s", ObjectClassToName(objectid.objclass), e.what());
  1233. return KCERR_PLUGIN_ERROR;
  1234. }
  1235. return DeleteLocalObject(ulId, objectid.objclass);
  1236. }
  1237. ECRESULT ECUserManagement::SetQuotaDetailsAndSync(unsigned int ulId, const quotadetails_t &details)
  1238. {
  1239. ECRESULT er;
  1240. objectid_t externid;
  1241. UserPlugin *lpPlugin = NULL;
  1242. er = GetThreadLocalPlugin(m_lpPluginFactory, &lpPlugin);
  1243. if(er != erSuccess)
  1244. return er;
  1245. er = GetExternalId(ulId, &externid);
  1246. if (er != erSuccess)
  1247. return er;
  1248. try {
  1249. lpPlugin->setQuota(externid, details);
  1250. } catch(objectnotfound &) {
  1251. MoveOrDeleteLocalObject(ulId, externid.objclass);
  1252. return KCERR_NOT_FOUND;
  1253. } catch (notimplemented &e) {
  1254. ec_log_warn("Unable to set quota for %s: %s", ObjectClassToName(externid.objclass), e.what());
  1255. return KCERR_NOT_IMPLEMENTED;
  1256. } catch(std::exception &e) {
  1257. ec_log_warn("Unable to set quota for %s: %s", ObjectClassToName(externid.objclass), e.what());
  1258. return KCERR_PLUGIN_ERROR;
  1259. }
  1260. m_lpSession->GetSessionManager()->GetCacheManager()->UpdateUser(ulId);
  1261. return erSuccess;
  1262. }
  1263. ECRESULT ECUserManagement::GetQuotaDetailsAndSync(unsigned int ulId, quotadetails_t* lpDetails, bool bGetUserDefault)
  1264. {
  1265. ECRESULT er = erSuccess;
  1266. objectid_t userid;
  1267. quotadetails_t details;
  1268. UserPlugin *lpPlugin = NULL;
  1269. if(IsInternalObject(ulId)) {
  1270. if (bGetUserDefault)
  1271. er = KCERR_NO_SUPPORT;
  1272. else {
  1273. lpDetails->bUseDefaultQuota = false;
  1274. lpDetails->bIsUserDefaultQuota = false;
  1275. lpDetails->llWarnSize = 0;
  1276. lpDetails->llSoftSize = 0;
  1277. lpDetails->llHardSize = 0;
  1278. }
  1279. return er;
  1280. }
  1281. er = m_lpSession->GetSessionManager()->GetCacheManager()->GetQuota(ulId, bGetUserDefault, lpDetails);
  1282. if (er == erSuccess)
  1283. return erSuccess; /* Cache contained requested information, we're done. */
  1284. er = GetThreadLocalPlugin(m_lpPluginFactory, &lpPlugin);
  1285. if(er != erSuccess)
  1286. return er;
  1287. er = GetExternalId(ulId, &userid);
  1288. if (er != erSuccess)
  1289. return er;
  1290. if ((userid.objclass == CONTAINER_COMPANY && !m_lpSession->GetSessionManager()->IsHostedSupported()) ||
  1291. (userid.objclass != CONTAINER_COMPANY && bGetUserDefault))
  1292. return KCERR_NO_SUPPORT;
  1293. try {
  1294. details = *lpPlugin->getQuota(userid, bGetUserDefault);
  1295. } catch(objectnotfound &) {
  1296. MoveOrDeleteLocalObject(ulId, userid.objclass);
  1297. return KCERR_NOT_FOUND;
  1298. } catch(std::exception &e) {
  1299. ec_log_warn("Unable to get quota for %s: %s", ObjectClassToName(userid.objclass), e.what());
  1300. return KCERR_PLUGIN_ERROR;
  1301. }
  1302. /* Update cache so we don't have to bug the plugin until the data has changed.
  1303. * Note that we don't care if the update succeeded, if it fails we will retry
  1304. * when the quota is requested for a second time. */
  1305. m_lpSession->GetSessionManager()->GetCacheManager()->SetQuota(ulId, bGetUserDefault, details);
  1306. *lpDetails = std::move(details);
  1307. return erSuccess;
  1308. }
  1309. ECRESULT ECUserManagement::SearchObjectAndSync(const char* szSearchString, unsigned int ulFlags, unsigned int *lpulID)
  1310. {
  1311. ECRESULT er;
  1312. objectsignature_t objectsignature;
  1313. std::unique_ptr<signatures_t> lpObjectsignatures;
  1314. unsigned int ulId = 0;
  1315. string strUsername;
  1316. string strCompanyname;
  1317. ECSecurity *lpSecurity = NULL;
  1318. UserPlugin *lpPlugin = NULL;
  1319. const char *szHideEveryone = NULL;
  1320. const char *szHideSystem = NULL;
  1321. objectid_t sCompanyId;
  1322. std::map<unsigned int, std::list<unsigned int> > mapMatches;
  1323. er = GetSecurity(&lpSecurity);
  1324. if (er != erSuccess)
  1325. return er;
  1326. // Special case: SYSTEM
  1327. if (strcasecmp(szSearchString, KOPANO_ACCOUNT_SYSTEM) == 0) {
  1328. // Hide user SYSTEM when requested
  1329. if (lpSecurity->GetUserId() != KOPANO_UID_SYSTEM) {
  1330. szHideSystem = m_lpConfig->GetSetting("hide_system");
  1331. if (szHideSystem && parseBool(szHideSystem))
  1332. return KCERR_NOT_FOUND;
  1333. }
  1334. *lpulID = KOPANO_UID_SYSTEM;
  1335. return erSuccess;
  1336. } else if (strcasecmp(szSearchString, KOPANO_ACCOUNT_EVERYONE) == 0) {
  1337. // Hide group everyone when requested
  1338. if (lpSecurity->GetUserId() != KOPANO_UID_SYSTEM) {
  1339. szHideEveryone = m_lpConfig->GetSetting("hide_everyone");
  1340. if (szHideEveryone && parseBool(szHideEveryone) && lpSecurity->GetAdminLevel() == 0)
  1341. return KCERR_NOT_FOUND;
  1342. }
  1343. *lpulID = KOPANO_UID_EVERYONE;
  1344. return erSuccess;
  1345. }
  1346. er = GetThreadLocalPlugin(m_lpPluginFactory, &lpPlugin);
  1347. if(er != erSuccess)
  1348. return er;
  1349. /*
  1350. * The search string might be the following:
  1351. * 1) Portion of the fullname
  1352. * 2) Portion of the username
  1353. * 3) Portion of the username including company name
  1354. * 4) Portion of the email address
  1355. *
  1356. * Regular search in the plugin will correctly handle (1), (2) and (4),
  1357. * but not (3) since the username, company name combination depends on the
  1358. * server.cfg settings. Because of that we need to check if we can convert
  1359. * the search string into a username & companyname which will tell us if
  1360. * we are indeed received a string of type (3) and should use resolveName().
  1361. * However it is common that '@' is used as seperation character, which could
  1362. * mean that email addresses are also identified as (3). So as fallback we
  1363. * should still call searchObject() if the search string contains an '@'.
  1364. */
  1365. /*
  1366. * We don't care about the return argument, when the call failed
  1367. * strCompanyname will remain empty and we will catch that when
  1368. * deciding if we are going to call resolveName() or searchObject().
  1369. */
  1370. GetUserAndCompanyFromLoginName(szSearchString, &strUsername, &strCompanyname);
  1371. if (!strCompanyname.empty()) {
  1372. /*
  1373. * We need to resolve the company first
  1374. * if we can't, just search for the complete string,
  1375. * otherwise we can try to go for a full resolve
  1376. */
  1377. er = ResolveObject(CONTAINER_COMPANY, strCompanyname, objectid_t(), &sCompanyId);
  1378. if (er == erSuccess) {
  1379. objectsignature_t resolved;
  1380. try {
  1381. // plugin should try all types to match 1 exact
  1382. resolved = lpPlugin->resolveName(OBJECTCLASS_UNKNOWN, strUsername, sCompanyId);
  1383. /* Loginname correctly resolved, this is an exact match so we don't
  1384. * have to do anything else for this object type.
  1385. * TODO: Optimize this further by sending message to caller that this
  1386. * entry is a 100% match and doesn't need to try to resolve any other
  1387. * object type. (IMPORTANT: when doing this, make sure we still check
  1388. * if the returned object is actually visible to the user or not!) */
  1389. lpObjectsignatures.reset(new signatures_t());
  1390. lpObjectsignatures->push_back(resolved);
  1391. goto done;
  1392. }
  1393. catch (...) {
  1394. // retry with search object on any error
  1395. }
  1396. }
  1397. }
  1398. try {
  1399. lpObjectsignatures = lpPlugin->searchObject(szSearchString, ulFlags);
  1400. } catch(notimplemented &) {
  1401. return KCERR_NOT_FOUND;
  1402. } catch(objectnotfound &) {
  1403. return KCERR_NOT_FOUND;
  1404. } catch(notsupported &) {
  1405. return KCERR_NO_SUPPORT;
  1406. } catch(std::exception &e) {
  1407. ec_log_warn("Unable to perform search for string \"%s\" on user database: %s", szSearchString, e.what());
  1408. return KCERR_PLUGIN_ERROR;
  1409. }
  1410. if (lpObjectsignatures->empty())
  1411. return KCERR_NOT_FOUND;
  1412. lpObjectsignatures->sort();
  1413. lpObjectsignatures->unique();
  1414. done:
  1415. /* Check each returned entry to see which one we are allowed to view
  1416. * TODO: check with a point system,
  1417. * if you have 2 objects, one have a match of 99% and one 50%
  1418. * use the one with 99% */
  1419. for (const auto &sig : *lpObjectsignatures) {
  1420. unsigned int ulIdTmp = 0;
  1421. er = GetLocalObjectIdOrCreate(sig, &ulIdTmp);
  1422. if (er != erSuccess)
  1423. return er;
  1424. if (lpSecurity->IsUserObjectVisible(ulIdTmp) != erSuccess)
  1425. continue;
  1426. if (!(ulFlags & EMS_AB_ADDRESS_LOOKUP)) {
  1427. /* We can only report a single (visible) entry, when we find multiple entries report a collision */
  1428. if (ulId == 0) {
  1429. ulId = ulIdTmp;
  1430. } else if (ulId != ulIdTmp) {
  1431. ec_log_crit("ECUserManagement::SearchObjectAndSync() unexpected id %u/%u", ulId, ulIdTmp);
  1432. return KCERR_COLLISION;
  1433. }
  1434. } else {
  1435. /* EMS_AB_ADDRESS_LOOKUP specified. We use a different algorithm for disambiguating: We look at the object types
  1436. * and order them by USER, GROUP, OTHER. If there is only one match for any type, we return that as the matching
  1437. * entry, starting with OBJECTTYPE_MAILUSER, then OBJECTTYPE_DISTLIST, then OBJECTTYPE_CONTAINER, then
  1438. * NONACTIVE_CONTACT (full type).
  1439. *
  1440. * The reason for this is that various parts of the software use EMS_AB_ADDRESS_LOOKUP to specifically locate a
  1441. * user entryid, which should not be ambiguated by a contact or group name. However, unique group names should
  1442. * also be resolvable by this method.
  1443. */
  1444. // combine on object class, and place contacts in their own place in the map, so they don't conflict on users.
  1445. // since they're indexed on the full object type, they'll be at the end of the map.
  1446. ULONG combine = OBJECTCLASS_TYPE(sig.id.objclass);
  1447. if (sig.id.objclass == NONACTIVE_CONTACT)
  1448. combine = sig.id.objclass;
  1449. // Store the matching entry for later analysis
  1450. mapMatches[combine].push_back(ulIdTmp);
  1451. }
  1452. }
  1453. if(ulFlags & EMS_AB_ADDRESS_LOOKUP) {
  1454. if (mapMatches.empty())
  1455. return KCERR_NOT_FOUND;
  1456. // mapMatches is already sorted numerically, so it's OBJECTTYPE_MAILUSER, OBJECTTYPE_DISTLIST, OBJECTTYPE_CONTAINER, NONACTIVE_CONTACT
  1457. if(mapMatches.begin()->second.size() != 1) {
  1458. // size() cannot be 0. Otherwise, it would not be there at all. So apparently, it is > 1, so it is ambiguous.
  1459. ec_log_info("Resolved multiple users for search \"%s\".", szSearchString);
  1460. return KCERR_COLLISION;
  1461. }
  1462. ulId = *mapMatches.begin()->second.begin();
  1463. }
  1464. if(ulId == 0)
  1465. // Nothing matched..
  1466. return KCERR_NOT_FOUND;
  1467. *lpulID = ulId;
  1468. return er;
  1469. }
  1470. ECRESULT ECUserManagement::QueryContentsRowData(struct soap *soap, ECObjectTableList *lpRowList, struct propTagArray *lpPropTagArray, struct rowSet **lppRowSet)
  1471. {
  1472. ECRESULT er = erSuccess;
  1473. int i = 0;
  1474. struct rowSet *lpsRowSet = NULL;
  1475. objectid_t externid;
  1476. list<objectid_t> lstObjects;
  1477. map<objectid_t, objectdetails_t> mapAllObjectDetails;
  1478. std::unique_ptr<map<objectid_t, objectdetails_t> > mapExternObjectDetails;
  1479. map<objectid_t, unsigned int> mapExternIdToRowId;
  1480. map<objectid_t, unsigned int> mapExternIdToObjectId;
  1481. objectdetails_t details;
  1482. UserPlugin *lpPlugin = NULL;
  1483. string signature;
  1484. er = GetThreadLocalPlugin(m_lpPluginFactory, &lpPlugin);
  1485. if (er != erSuccess)
  1486. goto exit;
  1487. assert(lpRowList != NULL);
  1488. lpsRowSet = s_alloc<struct rowSet>(soap);
  1489. lpsRowSet->__size = 0;
  1490. lpsRowSet->__ptr = NULL;
  1491. if (lpRowList->empty()) {
  1492. *lppRowSet = lpsRowSet;
  1493. goto exit; // success
  1494. }
  1495. // We return a square array with all the values
  1496. lpsRowSet->__size = lpRowList->size();
  1497. lpsRowSet->__ptr = s_alloc<propValArray>(soap, lpRowList->size());
  1498. memset(lpsRowSet->__ptr, 0, sizeof(propValArray) * lpRowList->size());
  1499. // Get Extern ID and Types for all items
  1500. i = 0;
  1501. for (const auto &row : *lpRowList) {
  1502. er = GetExternalId(row.ulObjId, &externid);
  1503. if (er != erSuccess) {
  1504. ++i;
  1505. er = erSuccess; /* Skip entry, but don't complain */
  1506. continue;
  1507. }
  1508. // See if the item data is cached
  1509. if (m_lpSession->GetSessionManager()->GetCacheManager()->GetUserDetails(row.ulObjId, &mapAllObjectDetails[externid]) != erSuccess) {
  1510. // Item needs to be retrieved from the plugin
  1511. lstObjects.push_back(externid);
  1512. // remove from all map, since the address reference added an empty entry in the map
  1513. mapAllObjectDetails.erase(externid);
  1514. }
  1515. mapExternIdToRowId.insert(std::make_pair(externid, i));
  1516. mapExternIdToObjectId.insert(std::make_pair(externid, row.ulObjId));
  1517. ++i;
  1518. }
  1519. // Do one request to the plugin for each type of object requested
  1520. try {
  1521. mapExternObjectDetails = lpPlugin->getObjectDetails(lstObjects);
  1522. // Copy each item over
  1523. for (const auto &eod : *mapExternObjectDetails) {
  1524. mapAllObjectDetails.insert(std::make_pair(eod.first, eod.second));
  1525. // Get the local object id for the item
  1526. auto iterObjectId = mapExternIdToObjectId.find(eod.first);
  1527. if (iterObjectId == mapExternIdToObjectId.cend())
  1528. continue;
  1529. // Add data to the cache
  1530. m_lpSession->GetSessionManager()->GetCacheManager()->SetUserDetails(iterObjectId->second, &eod.second);
  1531. }
  1532. /* We convert user and companyname to loginname later this function */
  1533. } catch (objectnotfound &) {
  1534. er = KCERR_NOT_FOUND;
  1535. goto exit;
  1536. } catch (notsupported &) {
  1537. er = KCERR_NO_SUPPORT;
  1538. goto exit;
  1539. } catch(std::exception &e) {
  1540. ec_log_warn("Unable to retrieve details map from external user source: %s", e.what());
  1541. er = KCERR_PLUGIN_ERROR;
  1542. goto exit;
  1543. }
  1544. // mapAllObjectDetails now contains the details per type of all objects that we need.
  1545. // Loop through user data and fill in data in rowset
  1546. for (auto &eod : mapAllObjectDetails) {
  1547. objectid_t objectid = eod.first;
  1548. objectdetails_t &objectdetails = eod.second; // reference, no need to copy
  1549. // If the objectid is not found, the plugin returned too many "hits"
  1550. auto iterRowId = mapExternIdToRowId.find(objectid);
  1551. if (iterRowId == mapExternIdToRowId.cend())
  1552. continue;
  1553. // Get the local object id for the item
  1554. auto iterObjectId = mapExternIdToObjectId.find(objectid);
  1555. if (iterObjectId == mapExternIdToObjectId.cend())
  1556. continue;
  1557. // objectdetails can only be from an extern object here, no need to check for SYSTEM or EVERYONE
  1558. er = UpdateUserDetailsToClient(&objectdetails);
  1559. if (er != erSuccess)
  1560. goto exit;
  1561. ConvertObjectDetailsToProps(soap, iterObjectId->second, &objectdetails, lpPropTagArray, &lpsRowSet->__ptr[iterRowId->second]);
  1562. // Ignore the error, missing rows will be filled in automatically later (assumes lpsRowSet is untouched on error!!)
  1563. }
  1564. // Loop through items, set local data and set other non-found data to NOT FOUND
  1565. i = 0;
  1566. for (const auto &row : *lpRowList) {
  1567. // Fill in any missing data with KCERR_NOT_FOUND
  1568. if (IsInternalObject(row.ulObjId)) {
  1569. objectdetails_t details;
  1570. er = GetLocalObjectDetails(row.ulObjId, &details);
  1571. if (er != erSuccess)
  1572. goto exit;
  1573. er = ConvertObjectDetailsToProps(soap, row.ulObjId, &details, lpPropTagArray, &lpsRowSet->__ptr[i]);
  1574. if(er != erSuccess)
  1575. goto exit;
  1576. } else if (lpsRowSet->__ptr[i].__ptr == NULL) {
  1577. lpsRowSet->__ptr[i].__ptr = s_alloc<propVal>(soap, lpPropTagArray->__size);
  1578. lpsRowSet->__ptr[i].__size = lpPropTagArray->__size;
  1579. for (gsoap_size_t j = 0; j < lpPropTagArray->__size; ++j) {
  1580. lpsRowSet->__ptr[i].__ptr[j].ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(lpPropTagArray->__ptr[j]));
  1581. lpsRowSet->__ptr[i].__ptr[j].Value.ul = KCERR_NOT_FOUND;
  1582. lpsRowSet->__ptr[i].__ptr[j].__union = SOAP_UNION_propValData_ul;
  1583. }
  1584. }
  1585. ++i;
  1586. }
  1587. // All done
  1588. *lppRowSet = lpsRowSet;
  1589. exit:
  1590. if (er != erSuccess && lpsRowSet != NULL) {
  1591. s_free(soap, lpsRowSet->__ptr);
  1592. s_free(soap, lpsRowSet);
  1593. }
  1594. return er;
  1595. }
  1596. ECRESULT ECUserManagement::QueryHierarchyRowData(struct soap *soap, ECObjectTableList *lpRowList, struct propTagArray *lpPropTagArray, struct rowSet **lppRowSet)
  1597. {
  1598. ECRESULT er = erSuccess;
  1599. struct rowSet *lpsRowSet = NULL;
  1600. unsigned int i = 0;
  1601. assert(lpRowList != NULL);
  1602. lpsRowSet = s_alloc_nothrow<struct rowSet>(soap);
  1603. if (lpsRowSet == NULL)
  1604. return KCERR_NOT_ENOUGH_MEMORY;
  1605. lpsRowSet->__size = 0;
  1606. lpsRowSet->__ptr = NULL;
  1607. if (lpRowList->empty()) {
  1608. *lppRowSet = lpsRowSet;
  1609. goto exit; // success
  1610. }
  1611. // We return a square array with all the values
  1612. lpsRowSet->__size = lpRowList->size();
  1613. lpsRowSet->__ptr = s_alloc<propValArray>(soap, lpRowList->size());
  1614. memset(lpsRowSet->__ptr, 0, sizeof(propValArray) * lpRowList->size());
  1615. for (const auto &row : *lpRowList) {
  1616. /* Although it propbably doesn't make a lot of sense, we need to check for company containers here.
  1617. * this is because with convenient depth tables, the children of the Kopano Address Book will not
  1618. * only be the Global Address Book, but also the company containers. */
  1619. if (row.ulObjId == KOPANO_UID_ADDRESS_BOOK ||
  1620. row.ulObjId == KOPANO_UID_GLOBAL_ADDRESS_BOOK ||
  1621. row.ulObjId == KOPANO_UID_GLOBAL_ADDRESS_LISTS) {
  1622. er = ConvertABContainerToProps(soap, row.ulObjId, lpPropTagArray, &lpsRowSet->__ptr[i]);
  1623. if (er != erSuccess)
  1624. goto exit;
  1625. ++i;
  1626. continue;
  1627. }
  1628. objectdetails_t details;
  1629. objectid_t sExternId;
  1630. er = GetObjectDetails(row.ulObjId, &details);
  1631. if (er != erSuccess)
  1632. goto exit;
  1633. er = GetExternalId(row.ulObjId, &sExternId);
  1634. if (er != erSuccess)
  1635. goto exit;
  1636. er = ConvertContainerObjectDetailsToProps(soap, row.ulObjId, &details, lpPropTagArray, &lpsRowSet->__ptr[i]);
  1637. if (er != erSuccess)
  1638. goto exit;
  1639. ++i;
  1640. }
  1641. // All done
  1642. *lppRowSet = lpsRowSet;
  1643. exit:
  1644. if (er != erSuccess) {
  1645. s_free(soap, lpsRowSet->__ptr);
  1646. s_free(soap, lpsRowSet);
  1647. }
  1648. return er;
  1649. }
  1650. ECRESULT ECUserManagement::GetUserAndCompanyFromLoginName(const std::string &strLoginName,
  1651. std::string *username, std::string *companyname)
  1652. {
  1653. ECRESULT er = erSuccess;
  1654. string format = m_lpConfig->GetSetting("loginname_format");
  1655. bool bHosted = m_lpSession->GetSessionManager()->IsHostedSupported();
  1656. size_t pos_u = format.find("%u");
  1657. size_t pos_c = format.find("%c");
  1658. string start, middle, end;
  1659. size_t pos_s, pos_m, pos_e;
  1660. size_t pos_a, pos_b;
  1661. if (!bHosted || pos_u == string::npos || pos_c == string::npos) {
  1662. /* When hosted is enabled, return a warning. Otherwise,
  1663. * this call was successful. */
  1664. if (bHosted)
  1665. er = KCWARN_PARTIAL_COMPLETION;
  1666. *username = strLoginName;
  1667. companyname->clear();
  1668. return er;
  1669. }
  1670. pos_a = (pos_u < pos_c) ? pos_u : pos_c;
  1671. pos_b = (pos_u < pos_c) ? pos_c : pos_u;
  1672. /*
  1673. * Read strLoginName to determine the fields, this check should
  1674. * keep in mind that there can be characters before, inbetween and
  1675. * after the different fields.
  1676. */
  1677. start = format.substr(0, pos_a);
  1678. middle = format.substr(pos_a + 2, pos_b - pos_a - 2);
  1679. end = format.substr(pos_b + 2, string::npos);
  1680. /*
  1681. * There must be some sort of seperator between username and companyname.
  1682. */
  1683. if (middle.empty())
  1684. return KCERR_INVALID_PARAMETER;
  1685. pos_s = !start.empty() ? strLoginName.find(start, 0) : 0;
  1686. pos_m = strLoginName.find(middle, pos_s + start.size());
  1687. pos_e = !end.empty() ? strLoginName.find(end, pos_m + middle.size()) : string::npos;
  1688. if ((!start.empty() && pos_s == string::npos) ||
  1689. (!middle.empty() && pos_m == string::npos) ||
  1690. (!end.empty() && pos_e == string::npos)) {
  1691. /* When hosted is enabled, return a warning. Otherwise,
  1692. * this call was successful. */
  1693. if (strLoginName != KOPANO_ACCOUNT_SYSTEM &&
  1694. strLoginName != KOPANO_ACCOUNT_EVERYONE)
  1695. er = KCERR_INVALID_PARAMETER;
  1696. *username = strLoginName;
  1697. companyname->clear();
  1698. return er;
  1699. }
  1700. if (pos_s == string::npos)
  1701. pos_s = 0;
  1702. if (pos_m == string::npos)
  1703. pos_m = pos_b;
  1704. if (pos_u < pos_c) {
  1705. *username = strLoginName.substr(pos_a, pos_m - pos_a);
  1706. *companyname = strLoginName.substr(pos_m + middle.size(), pos_e - pos_m - middle.size());
  1707. } else {
  1708. *username = strLoginName.substr(pos_m + middle.size(), pos_e - pos_m - middle.size());
  1709. *companyname = strLoginName.substr(pos_a, pos_m - pos_a);
  1710. }
  1711. /* Neither username or companyname are allowed to be empty */
  1712. if (username->empty() || companyname->empty())
  1713. return KCERR_NO_ACCESS;
  1714. return er;
  1715. }
  1716. ECRESULT ECUserManagement::ConvertLoginToUserAndCompany(objectdetails_t *lpDetails)
  1717. {
  1718. ECRESULT er;
  1719. bool bHosted = m_lpSession->GetSessionManager()->IsHostedSupported();
  1720. string loginname;
  1721. string companyname;
  1722. unsigned int ulCompanyId = 0;
  1723. objectid_t sCompanyId;
  1724. if ((OBJECTCLASS_TYPE(lpDetails->GetClass()) != OBJECTTYPE_MAILUSER &&
  1725. OBJECTCLASS_TYPE(lpDetails->GetClass()) != OBJECTTYPE_DISTLIST) ||
  1726. lpDetails->GetClass() == NONACTIVE_CONTACT || lpDetails->GetPropString(OB_PROP_S_LOGIN).empty())
  1727. return erSuccess;
  1728. er = GetUserAndCompanyFromLoginName(lpDetails->GetPropString(OB_PROP_S_LOGIN), &loginname, &companyname);
  1729. /* GetUserAndCompanyFromLoginName() uses KCWARN_PARTIAL_COMPLETION to indicate
  1730. * it could not fully convert the loginname. This means the user provided an
  1731. * invalid parameter and we should give the proper code back to the user */
  1732. if (er == KCWARN_PARTIAL_COMPLETION)
  1733. er = KCERR_INVALID_PARAMETER;
  1734. if (er != erSuccess)
  1735. return er;
  1736. if (bHosted) {
  1737. er = ResolveObject(CONTAINER_COMPANY, companyname, objectid_t(), &sCompanyId);
  1738. if (er != erSuccess)
  1739. return er;
  1740. er = GetLocalId(sCompanyId, &ulCompanyId);
  1741. if (er != erSuccess)
  1742. return er;
  1743. lpDetails->SetPropObject(OB_PROP_O_COMPANYID, sCompanyId);
  1744. lpDetails->SetPropInt(OB_PROP_I_COMPANYID, ulCompanyId);
  1745. }
  1746. lpDetails->SetPropString(OB_PROP_S_LOGIN, loginname);
  1747. /* Groups require fullname to be updated as well */
  1748. if (OBJECTCLASS_TYPE(lpDetails->GetClass()) == OBJECTTYPE_DISTLIST)
  1749. lpDetails->SetPropString(OB_PROP_S_FULLNAME, loginname);
  1750. return erSuccess;
  1751. }
  1752. ECRESULT ECUserManagement::ConvertUserAndCompanyToLogin(objectdetails_t *lpDetails)
  1753. {
  1754. ECRESULT er;
  1755. string format;
  1756. bool bHosted = m_lpSession->GetSessionManager()->IsHostedSupported();
  1757. string login;
  1758. string company;
  1759. size_t pos = 0;
  1760. objectid_t sCompany;
  1761. unsigned int ulCompanyId;
  1762. objectdetails_t sCompanyDetails;
  1763. /*
  1764. * We don't have to do anything when hosted is disabled,
  1765. * when we perform this operation on SYSTEM or EVERYONE (since they don't belong to a company),
  1766. * or when the user objecttype does not need any conversions.
  1767. */
  1768. if (!bHosted || login == KOPANO_ACCOUNT_SYSTEM || login == KOPANO_ACCOUNT_EVERYONE || lpDetails->GetClass() == CONTAINER_COMPANY)
  1769. return erSuccess;
  1770. format = m_lpConfig->GetSetting("loginname_format");
  1771. login = lpDetails->GetPropString(OB_PROP_S_LOGIN);
  1772. /*
  1773. * Since we already need the company name here, we convert the
  1774. * OB_PROP_O_COMPANYID to the internal ID too
  1775. */
  1776. sCompany = lpDetails->GetPropObject(OB_PROP_O_COMPANYID);
  1777. er = GetLocalId(sCompany, &ulCompanyId);
  1778. if (er != erSuccess) {
  1779. ec_log_crit("Unable to find company id for object " + lpDetails->GetPropString(OB_PROP_S_FULLNAME));
  1780. return er;
  1781. }
  1782. // update company local id and company name
  1783. lpDetails->SetPropInt(OB_PROP_I_COMPANYID, ulCompanyId);
  1784. // skip login conversion for non-login objects
  1785. if ((OBJECTCLASS_TYPE(lpDetails->GetClass()) != OBJECTTYPE_MAILUSER && OBJECTCLASS_TYPE(lpDetails->GetClass()) != OBJECTTYPE_DISTLIST) ||
  1786. (lpDetails->GetClass() == NONACTIVE_CONTACT))
  1787. return er;
  1788. // find company name for object
  1789. er = GetObjectDetails(ulCompanyId, &sCompanyDetails);
  1790. if (er != erSuccess)
  1791. return er;
  1792. company = sCompanyDetails.GetPropString(OB_PROP_S_FULLNAME);
  1793. /*
  1794. * This really shouldn't happen, the loginname must contain *something* under any circumstance,
  1795. * company must contain *something* when hosted is enabled, and we already confirmed that this
  1796. * is the case.
  1797. */
  1798. if (login.empty() || company.empty())
  1799. return KCERR_UNABLE_TO_COMPLETE;
  1800. pos = format.find("%u");
  1801. if (pos != string::npos)
  1802. format.replace(pos, 2, login);
  1803. pos = format.find("%c");
  1804. if (pos != string::npos)
  1805. format.replace(pos, 2, company);
  1806. lpDetails->SetPropString(OB_PROP_S_LOGIN, format);
  1807. /* Groups require fullname to be updated as well */
  1808. if (OBJECTCLASS_TYPE(lpDetails->GetClass()) == OBJECTTYPE_DISTLIST)
  1809. lpDetails->SetPropString(OB_PROP_S_FULLNAME, format);
  1810. return erSuccess;
  1811. }
  1812. ECRESULT ECUserManagement::ConvertExternIDsToLocalIDs(objectdetails_t *lpDetails)
  1813. {
  1814. ECRESULT er;
  1815. list<objectid_t> lstExternIDs;
  1816. map<objectid_t, unsigned int> mapLocalIDs;
  1817. objectid_t sExternID;
  1818. unsigned int ulLocalID = 0;
  1819. // details == info, list contains 1) active_users or 2) groups
  1820. switch (lpDetails->GetClass()) {
  1821. case ACTIVE_USER:
  1822. case NONACTIVE_USER:
  1823. case NONACTIVE_ROOM:
  1824. case NONACTIVE_EQUIPMENT:
  1825. case NONACTIVE_CONTACT:
  1826. case DISTLIST_GROUP:
  1827. case DISTLIST_SECURITY:
  1828. lstExternIDs = lpDetails->GetPropListObject(OB_PROP_LO_SENDAS);
  1829. if (lstExternIDs.empty())
  1830. break;
  1831. er = m_lpSession->GetSessionManager()->GetCacheManager()->GetUserObjects(lstExternIDs, &mapLocalIDs);
  1832. if (er != erSuccess)
  1833. return er;
  1834. for (const auto &loc_id : mapLocalIDs) {
  1835. if (loc_id.first.objclass != ACTIVE_USER &&
  1836. OBJECTCLASS_TYPE(loc_id.first.objclass) != OBJECTTYPE_DISTLIST)
  1837. continue;
  1838. lpDetails->AddPropInt(OB_PROP_LI_SENDAS, loc_id.second);
  1839. }
  1840. break;
  1841. case CONTAINER_COMPANY:
  1842. sExternID = lpDetails->GetPropObject(OB_PROP_O_SYSADMIN);
  1843. // avoid cache and SQL query when using internal sysadmin object
  1844. if (sExternID.id == "SYSTEM")
  1845. ulLocalID = KOPANO_UID_SYSTEM;
  1846. else if (GetLocalId(sExternID, &ulLocalID) != erSuccess)
  1847. ulLocalID = KOPANO_UID_SYSTEM;
  1848. lpDetails->SetPropInt(OB_PROP_I_SYSADMIN, ulLocalID);
  1849. break;
  1850. default:
  1851. // Nothing to do
  1852. break;
  1853. }
  1854. return erSuccess;
  1855. }
  1856. ECRESULT ECUserManagement::ConvertLocalIDsToExternIDs(objectdetails_t *lpDetails)
  1857. {
  1858. ECRESULT er;
  1859. objectid_t sExternID;
  1860. unsigned int ulLocalID = 0;
  1861. switch (lpDetails->GetClass()) {
  1862. case CONTAINER_COMPANY:
  1863. ulLocalID = lpDetails->GetPropInt(OB_PROP_I_SYSADMIN);
  1864. if (!ulLocalID || IsInternalObject(ulLocalID))
  1865. break;
  1866. er = GetExternalId(ulLocalID, &sExternID);
  1867. if (er != erSuccess)
  1868. return er;
  1869. lpDetails->SetPropObject(OB_PROP_O_SYSADMIN, sExternID);
  1870. break;
  1871. default:
  1872. // Nothing to do
  1873. break;
  1874. }
  1875. return erSuccess;
  1876. }
  1877. /**
  1878. * Add default server settings of enabled/disabled features to the
  1879. * user explicit feature lists.
  1880. *
  1881. * @param[in,out] lpDetails plugin feature list to edit
  1882. *
  1883. * @return erSuccess
  1884. */
  1885. ECRESULT ECUserManagement::ComplementDefaultFeatures(objectdetails_t *lpDetails)
  1886. {
  1887. if (OBJECTCLASS_TYPE(lpDetails->GetClass()) != OBJECTTYPE_MAILUSER) {
  1888. // clear settings for anything but users
  1889. std::list<std::string> e;
  1890. lpDetails->SetPropListString((property_key_t)PR_EC_ENABLED_FEATURES_A, e);
  1891. lpDetails->SetPropListString((property_key_t)PR_EC_DISABLED_FEATURES_A, e);
  1892. return erSuccess;
  1893. }
  1894. set<string> defaultEnabled = getFeatures();
  1895. list<string> userEnabled = lpDetails->GetPropListString((property_key_t)PR_EC_ENABLED_FEATURES_A);
  1896. list<string> userDisabled = lpDetails->GetPropListString((property_key_t)PR_EC_DISABLED_FEATURES_A);
  1897. std::vector<std::string> ddv = tokenize(m_lpConfig->GetSetting("disabled_features"), "\t ");
  1898. std::set<std::string> defaultDisabled(ddv.begin(), ddv.end());
  1899. for (auto i = defaultDisabled.cbegin(); i != defaultDisabled.cend(); ) {
  1900. if (i->empty()) {
  1901. // nasty side effect of boost split, when input consists only of a split predicate.
  1902. defaultDisabled.erase(i++);
  1903. continue;
  1904. }
  1905. defaultEnabled.erase(*i);
  1906. ++i;
  1907. }
  1908. // explicit enable remove from default disable, and add in defaultEnabled
  1909. for (const auto &s : userEnabled) {
  1910. defaultDisabled.erase(s);
  1911. defaultEnabled.insert(s);
  1912. }
  1913. // explicit disable remove from default enable, and add in defaultDisabled
  1914. for (const auto &s : userDisabled) {
  1915. defaultEnabled.erase(s);
  1916. defaultDisabled.insert(s);
  1917. }
  1918. userEnabled.assign(defaultEnabled.begin(), defaultEnabled.end());
  1919. userDisabled.assign(defaultDisabled.begin(), defaultDisabled.end());
  1920. // save lists back to user details
  1921. lpDetails->SetPropListString((property_key_t)PR_EC_ENABLED_FEATURES_A, userEnabled);
  1922. lpDetails->SetPropListString((property_key_t)PR_EC_DISABLED_FEATURES_A, userDisabled);
  1923. return erSuccess;
  1924. }
  1925. class filterDefaults {
  1926. public:
  1927. filterDefaults(const set<string>& d) : def(d) {};
  1928. bool operator()(const string& x) const {
  1929. return def.find(x) != def.end();
  1930. }
  1931. private:
  1932. const set<string>& def;
  1933. };
  1934. /**
  1935. * Make the enabled and disabled feature list of user details an explicit list.
  1936. * This way, changing the default in the server will have a direct effect.
  1937. *
  1938. * @param[in,out] lpDetails incoming user details to fix
  1939. *
  1940. * @return erSuccess
  1941. */
  1942. ECRESULT ECUserManagement::RemoveDefaultFeatures(objectdetails_t *lpDetails)
  1943. {
  1944. if (lpDetails->GetClass() != ACTIVE_USER)
  1945. return erSuccess;
  1946. set<string> defaultEnabled = getFeatures();
  1947. list<string> userEnabled = lpDetails->GetPropListString((property_key_t)PR_EC_ENABLED_FEATURES_A);
  1948. list<string> userDisabled = lpDetails->GetPropListString((property_key_t)PR_EC_DISABLED_FEATURES_A);
  1949. std::vector<std::string> ddv = tokenize(m_lpConfig->GetSetting("disabled_features"), "\t ");
  1950. std::set<std::string> defaultDisabled(ddv.begin(), ddv.end());
  1951. // remove all default disabled from enabled and user explicit list
  1952. for (const auto &s : defaultDisabled) {
  1953. defaultEnabled.erase(s);
  1954. userDisabled.remove(s);
  1955. }
  1956. // remove all default enabled features from explicit list
  1957. userEnabled.remove_if(filterDefaults(defaultEnabled));
  1958. // save lists back to user details
  1959. lpDetails->SetPropListString((property_key_t)PR_EC_ENABLED_FEATURES_A, userEnabled);
  1960. lpDetails->SetPropListString((property_key_t)PR_EC_DISABLED_FEATURES_A, userDisabled);
  1961. return erSuccess;
  1962. }
  1963. /**
  1964. * Convert some properties in the details object from plugin info to
  1965. * client info.
  1966. *
  1967. * @todo this function is called *very* often, and that should be
  1968. * reduced.
  1969. *
  1970. * @param[in,out] lpDetails details to update
  1971. *
  1972. * @return Kopano error code
  1973. */
  1974. ECRESULT ECUserManagement::UpdateUserDetailsToClient(objectdetails_t *lpDetails)
  1975. {
  1976. ECRESULT er;
  1977. er = ConvertUserAndCompanyToLogin(lpDetails);
  1978. if (er != erSuccess)
  1979. return er;
  1980. er = ConvertExternIDsToLocalIDs(lpDetails);
  1981. if (er != erSuccess)
  1982. return er;
  1983. er = ComplementDefaultFeatures(lpDetails);
  1984. if (er != erSuccess)
  1985. return er;
  1986. return erSuccess;
  1987. }
  1988. /**
  1989. * Update client details to db-plugin info
  1990. *
  1991. * @param[in,out] lpDetails user details to update
  1992. *
  1993. * @return Kopano error code
  1994. */
  1995. ECRESULT ECUserManagement::UpdateUserDetailsFromClient(objectdetails_t *lpDetails)
  1996. {
  1997. ECRESULT er;
  1998. er = ConvertLoginToUserAndCompany(lpDetails);
  1999. if (er != erSuccess)
  2000. return er;
  2001. er = ConvertLocalIDsToExternIDs(lpDetails);
  2002. if (er != erSuccess)
  2003. return er;
  2004. er = RemoveDefaultFeatures(lpDetails);
  2005. if (er != erSuccess)
  2006. return er;
  2007. return erSuccess;
  2008. }
  2009. // ******************************************************************************************************
  2010. //
  2011. // Create/Delete routines
  2012. //
  2013. // ******************************************************************************************************
  2014. // Perform a license check
  2015. ECRESULT ECUserManagement::CheckUserLicense(unsigned int *lpulLicenseStatus)
  2016. {
  2017. ECRESULT er;
  2018. unsigned int ulTotalUsers = 0;
  2019. unsigned int ulActive = 0;
  2020. unsigned int ulNonActive = 0;
  2021. unsigned int ulLicensedUsers = 0;
  2022. unsigned int ulActiveLimit = 0;
  2023. unsigned int ulNonActiveLimit = 0;
  2024. // NOTE: this function is only a precaution
  2025. *lpulLicenseStatus = 0;
  2026. er = GetUserCount(&ulActive, &ulNonActive);
  2027. if (er != erSuccess) {
  2028. ec_log_crit("Unable to query user count");
  2029. return er;
  2030. }
  2031. ulTotalUsers = ulActive + ulNonActive;
  2032. er = m_lpSession->GetSessionManager()->GetLicensedUsers(0 /*SERVICE_TYPE_ZCP*/, &ulLicensedUsers);
  2033. if (er != erSuccess) {
  2034. ec_log_crit("Unable to query license user count");
  2035. return er;
  2036. }
  2037. /* Active limit is always licensed users limit when the limit is 0 we have unlimited users,
  2038. * Inactive limit is minimally the licensed user limit + 25 and maximally the licensed user limit + 150% (*2.5) licensed user limit */
  2039. ulActiveLimit = ulLicensedUsers;
  2040. ulNonActiveLimit = ulLicensedUsers ? std::max(ulLicensedUsers + 25, (ulLicensedUsers * 5) / 2) : 0;
  2041. if (ulActiveLimit) {
  2042. if (ulActive == ulActiveLimit)
  2043. *lpulLicenseStatus |= USERMANAGEMENT_LIMIT_ACTIVE_USERS;
  2044. if (ulActive > ulActiveLimit)
  2045. *lpulLicenseStatus |= USERMANAGEMENT_EXCEED_ACTIVE_USERS;
  2046. }
  2047. if (ulNonActiveLimit) {
  2048. if (ulTotalUsers == ulNonActiveLimit)
  2049. *lpulLicenseStatus |= USERMANAGEMENT_LIMIT_NONACTIVE_USERS;
  2050. if (ulTotalUsers > ulNonActiveLimit)
  2051. *lpulLicenseStatus |= USERMANAGEMENT_EXCEED_NONACTIVE_USERS;
  2052. }
  2053. return erSuccess;
  2054. }
  2055. // Create a local user corresponding to the given userid on the external database
  2056. ECRESULT ECUserManagement::CreateLocalObject(const objectsignature_t &signature, unsigned int *lpulObjectId) {
  2057. ECRESULT er;
  2058. ECDatabase *lpDatabase = NULL;
  2059. std::string strQuery;
  2060. objectdetails_t details;
  2061. unsigned int ulId;
  2062. unsigned int ulCompanyId;
  2063. ABEID eid(MAPI_ABCONT, MUIDECSAB, 1);
  2064. unsigned int ulLicenseStatus;
  2065. SOURCEKEY sSourceKey;
  2066. UserPlugin *lpPlugin = NULL;
  2067. string strUserServer;
  2068. string strThisServer = m_lpConfig->GetSetting("server_name");
  2069. bool bDistributed = m_lpSession->GetSessionManager()->IsDistributedSupported();
  2070. er = m_lpSession->GetDatabase(&lpDatabase);
  2071. if(er != erSuccess)
  2072. return er;
  2073. er = GetThreadLocalPlugin(m_lpPluginFactory, &lpPlugin);
  2074. if(er != erSuccess)
  2075. return er;
  2076. if (OBJECTCLASS_TYPE(signature.id.objclass) == OBJECTTYPE_MAILUSER && signature.id.objclass != NONACTIVE_CONTACT) {
  2077. er = CheckUserLicense(&ulLicenseStatus);
  2078. if (er != erSuccess)
  2079. return er;
  2080. if (signature.id.objclass == ACTIVE_USER && (ulLicenseStatus & USERMANAGEMENT_BLOCK_CREATE_ACTIVE_USER)) {
  2081. ec_log_crit("Unable to create active user: Your license does not permit this amount of users.");
  2082. return KCERR_UNABLE_TO_COMPLETE;
  2083. } else if (ulLicenseStatus & USERMANAGEMENT_BLOCK_CREATE_NONACTIVE_USER) {
  2084. ec_log_crit("Unable to create non-active user: Your license does not permit this amount of users.");
  2085. return KCERR_UNABLE_TO_COMPLETE;
  2086. }
  2087. }
  2088. ec_log_info("Auto-creating %s from external source", ObjectClassToName(signature.id.objclass));
  2089. try {
  2090. details = *lpPlugin->getObjectDetails(signature.id);
  2091. /*
  2092. * The property OB_PROP_S_LOGIN is mandatory, when that property is an empty string
  2093. * somebody (aka: system administrator) has messed up its LDAP tree or messed around
  2094. * with the Database.
  2095. */
  2096. if (signature.id.objclass != NONACTIVE_CONTACT && details.GetPropString(OB_PROP_S_LOGIN).empty()) {
  2097. ec_log_warn("Unable to create object in local database: %s has no name", ObjectClassToName(signature.id.objclass));
  2098. return KCERR_UNKNOWN_OBJECT;
  2099. }
  2100. // details is from externid, no need to check for SYSTEM or EVERYONE
  2101. er = UpdateUserDetailsToClient(&details);
  2102. if (er != erSuccess)
  2103. return er;
  2104. } catch (objectnotfound &) {
  2105. return KCERR_NOT_FOUND;
  2106. } catch (notsupported &) {
  2107. return KCERR_NO_SUPPORT;
  2108. } catch(std::exception &e) {
  2109. ec_log_warn("Unable to get object data while adding new %s: %s", ObjectClassToName(signature.id.objclass), e.what());
  2110. return KCERR_PLUGIN_ERROR;
  2111. }
  2112. if (parseBool(m_lpConfig->GetSetting("user_safe_mode"))) {
  2113. ec_log_crit("user_safe_mode: Would create new %s with name \"%s\"", ObjectClassToName(signature.id.objclass), details.GetPropString(OB_PROP_S_FULLNAME).c_str());
  2114. return er;
  2115. }
  2116. /*
  2117. * 1) we create the object in the database, for users/groups we set the company to 0
  2118. * 2) Resolve companyid for users/groups to a companyid
  2119. * 3) Update object in the database to correctly set the company column
  2120. *
  2121. * Now why do we use this approach:
  2122. * Suppose we have user A who is member of Company B and is the sysadmin for B.
  2123. * The following will happen when we insert all information in a single shot:
  2124. * 1) GetLocalObjectIdOrCreate(A) is called
  2125. * 2) Database does not contain A, so CreateLocalObject(A) is called
  2126. * 3) CreateLocalObject(A) calls ResolveObjectAndSync(B) for COMPANYID
  2127. * 3.1) B is resolved, GetLocalObjectIdOrCreate(B) is called
  2128. * 3.2) Database does not contain B, so CreateLocalObject(B) is called
  2129. * 3.3) B is inserted into the database, createcompany script is called
  2130. * 3.4) createcompany script calls getCompany(B)
  2131. * 3.5) Server obtains details for B, finds A as sysadmin for B
  2132. * 3.6) Server calls GetLocalObjectIdOrCreate(A)
  2133. * 3.7) Database does not contain A, so CreateLocalObject(A) is called
  2134. * 3.8) CreateLocalObject(A) inserts A into the database
  2135. * 3.9) createcompany script does its work and completes
  2136. * 4) CreateLocalObject(A) inserts A into the database
  2137. * 5) BOOM! Primary key violation since A is already present in the database
  2138. */
  2139. strQuery =
  2140. "INSERT INTO users (externid, objectclass, signature) "
  2141. "VALUES("
  2142. "'" + lpDatabase->Escape(signature.id.id) + "', " +
  2143. stringify(signature.id.objclass) + ", " +
  2144. "'" + lpDatabase->Escape(signature.signature) + "')";
  2145. er = lpDatabase->DoInsert(strQuery, &ulId);
  2146. if(er != erSuccess)
  2147. return er;
  2148. if (signature.id.objclass == CONTAINER_COMPANY)
  2149. ulCompanyId = 0;
  2150. else
  2151. ulCompanyId = details.GetPropInt(OB_PROP_I_COMPANYID);
  2152. if (ulCompanyId) {
  2153. strQuery =
  2154. "UPDATE users "
  2155. "SET company = " + stringify(ulCompanyId) + " "
  2156. "WHERE id = " + stringify(ulId);
  2157. er = lpDatabase->DoUpdate(strQuery);
  2158. if (er != erSuccess)
  2159. return er;
  2160. }
  2161. switch(signature.id.objclass) {
  2162. case ACTIVE_USER:
  2163. case NONACTIVE_USER:
  2164. case NONACTIVE_ROOM:
  2165. case NONACTIVE_EQUIPMENT:
  2166. strUserServer = details.GetPropString(OB_PROP_S_SERVERNAME);
  2167. if (!bDistributed || strcasecmp(strUserServer.c_str(), strThisServer.c_str()) == 0)
  2168. execute_script(m_lpConfig->GetSetting("createuser_script"),
  2169. "KOPANO_USER", details.GetPropString(OB_PROP_S_LOGIN).c_str(),
  2170. NULL);
  2171. break;
  2172. case DISTLIST_GROUP:
  2173. case DISTLIST_SECURITY:
  2174. execute_script(m_lpConfig->GetSetting("creategroup_script"),
  2175. "KOPANO_GROUP", details.GetPropString(OB_PROP_S_LOGIN).c_str(),
  2176. NULL);
  2177. break;
  2178. case CONTAINER_COMPANY:
  2179. strUserServer = details.GetPropString(OB_PROP_S_SERVERNAME);
  2180. if (!bDistributed || strcasecmp(strUserServer.c_str(), strThisServer.c_str()) == 0)
  2181. execute_script(m_lpConfig->GetSetting("createcompany_script"),
  2182. "KOPANO_COMPANY", details.GetPropString(OB_PROP_S_FULLNAME).c_str(),
  2183. NULL);
  2184. break;
  2185. default:
  2186. break;
  2187. }
  2188. // Log the change to ICS
  2189. er = GetABSourceKeyV1(ulId, &sSourceKey);
  2190. if (er != erSuccess)
  2191. return er;
  2192. AddABChange(m_lpSession, ICS_AB_NEW, sSourceKey, SOURCEKEY(CbABEID(&eid), (char *)&eid));
  2193. *lpulObjectId = ulId;
  2194. return erSuccess;
  2195. }
  2196. // Creates a local object under a specific object ID
  2197. ECRESULT ECUserManagement::CreateLocalObjectSimple(const objectsignature_t &signature, unsigned int ulPreferredId) {
  2198. ECRESULT er = erSuccess;
  2199. ECDatabase *lpDatabase = NULL;
  2200. objectdetails_t details;
  2201. std::string strQuery;
  2202. unsigned int ulCompanyId;
  2203. UserPlugin *lpPlugin = NULL;
  2204. DB_RESULT lpResult;
  2205. std::string strUserId;
  2206. bool bLocked = false;
  2207. er = m_lpSession->GetDatabase(&lpDatabase);
  2208. if(er != erSuccess)
  2209. goto exit;
  2210. // No user count checking or script starting in this function; it is only used in for addressbook synchronization in the offline server
  2211. er = GetThreadLocalPlugin(m_lpPluginFactory, &lpPlugin);
  2212. if(er != erSuccess)
  2213. goto exit;
  2214. try {
  2215. details = *lpPlugin->getObjectDetails(signature.id);
  2216. /* No need to convert the user and company name to login, since we are not using
  2217. * the loginname in this function. */
  2218. } catch (objectnotfound &) {
  2219. er = KCERR_NOT_FOUND;
  2220. goto exit;
  2221. } catch (notsupported &) {
  2222. er = KCERR_NO_SUPPORT;
  2223. goto exit;
  2224. } catch(std::exception &e) {
  2225. ec_log_warn("Unable to get details while adding new %s: %s", ObjectClassToName(signature.id.objclass), e.what());
  2226. er = KCERR_PLUGIN_ERROR;
  2227. goto exit;
  2228. }
  2229. if (signature.id.objclass == CONTAINER_COMPANY)
  2230. ulCompanyId = 0;
  2231. else
  2232. ulCompanyId = details.GetPropInt(OB_PROP_I_COMPANYID);
  2233. strQuery = "LOCK TABLES users WRITE";
  2234. er = lpDatabase->DoInsert(strQuery);
  2235. if (er != erSuccess)
  2236. goto exit;
  2237. bLocked = true;
  2238. // Check if we can use the preferred id.
  2239. strQuery = "SELECT 0 FROM users WHERE id=" + stringify(ulPreferredId);
  2240. er = lpDatabase->DoSelect(strQuery, &lpResult);
  2241. if (er != erSuccess)
  2242. goto exit;
  2243. if (lpDatabase->GetNumRows(lpResult) == 1)
  2244. strUserId = "NULL"; // Let mysql insert the userid with auto increment
  2245. else
  2246. strUserId = stringify(ulPreferredId); // force userid on preferred id
  2247. strQuery =
  2248. "INSERT INTO users (id, externid, objectclass, company, signature) "
  2249. "VALUES ("
  2250. + strUserId + ", " +
  2251. "'" + lpDatabase->Escape(signature.id.id) + "', " +
  2252. stringify(signature.id.objclass) + ", " +
  2253. stringify(ulCompanyId) + ", " +
  2254. "'" + lpDatabase->Escape(signature.signature) + "')";
  2255. er = lpDatabase->DoInsert(strQuery);
  2256. if(er != erSuccess)
  2257. goto exit;
  2258. exit:
  2259. if (bLocked)
  2260. lpDatabase->DoInsert("UNLOCK TABLES");
  2261. return er;
  2262. }
  2263. /**
  2264. * Modify the objectclass of a user in the database, or delete the user if the modification is not allowed
  2265. *
  2266. * This function modifies the objectclass of the object in the database to match the external object type. This
  2267. * allows switching from an active to a non-active user for example. However, some switches are not allowed like
  2268. * changing from a user to a group; In this case the object is deleted and will be re-created as the other type
  2269. * later, since the object is then missing from the locale database.
  2270. *
  2271. * Conversion are allowed if the OBJECTCLASS_TYPE of the object remains unchanged. Exception is converting to/from
  2272. * a CONTACT type since we want to do store initialization or deletion in that case (contact is the only USER type
  2273. * that doesn't have a store)
  2274. *
  2275. * @param[in] sExternId Extern ID of the object to be updated (the externid contains the object type)
  2276. * @param[out] lpulObjectId Object ID of object if not deleted
  2277. * @return ECRESULT KCERR_NOT_FOUND if the user is not found OR must be deleted due to type change
  2278. */
  2279. ECRESULT ECUserManagement::UpdateObjectclassOrDelete(const objectid_t &sExternId, unsigned int *lpulObjectId)
  2280. {
  2281. ECRESULT er = erSuccess;
  2282. ECDatabase *lpDatabase = NULL;
  2283. DB_RESULT lpResult;
  2284. DB_ROW lpRow = NULL;
  2285. string strQuery;
  2286. unsigned int ulObjectId;
  2287. objectclass_t objClass;
  2288. SOURCEKEY sSourceKey;
  2289. ABEID eid(MAPI_ABCONT, MUIDECSAB, 1);
  2290. er = m_lpSession->GetDatabase(&lpDatabase);
  2291. if(er != erSuccess)
  2292. return er;
  2293. strQuery = "SELECT id, objectclass FROM users WHERE externid='" + lpDatabase->Escape(sExternId.id) + "' AND " +
  2294. OBJECTCLASS_COMPARE_SQL("objectclass", OBJECTCLASS_CLASSTYPE(sExternId.objclass));
  2295. er = lpDatabase->DoSelect(strQuery, &lpResult);
  2296. if (er != erSuccess)
  2297. return er;
  2298. lpRow = lpDatabase->FetchRow(lpResult);
  2299. if (lpRow == nullptr)
  2300. return KCERR_NOT_FOUND;
  2301. ulObjectId = atoui(lpRow[0]);
  2302. objClass = (objectclass_t)atoui(lpRow[1]);
  2303. /* Exception: converting from contact to other user type or other user type to contact is NOT allowed -> this is to force store create/remove */
  2304. bool illegal = objClass == NONACTIVE_CONTACT && sExternId.objclass != NONACTIVE_CONTACT;
  2305. illegal |= objClass != NONACTIVE_CONTACT && sExternId.objclass == NONACTIVE_CONTACT;
  2306. if (OBJECTCLASS_TYPE(objClass) != OBJECTCLASS_TYPE(sExternId.objclass) || illegal) {
  2307. // type of object changed, so we must fully delete the object first.
  2308. er = DeleteLocalObject(ulObjectId, objClass);
  2309. if (er != erSuccess)
  2310. return er;
  2311. return KCERR_NOT_FOUND;
  2312. }
  2313. if (parseBool(m_lpConfig->GetSetting("user_safe_mode"))) {
  2314. ec_log_crit("user_safe_mode: Would update %d from %s to %s",
  2315. ulObjectId, ObjectClassToName(objClass),
  2316. ObjectClassToName(sExternId.objclass));
  2317. return er;
  2318. }
  2319. /*
  2320. * Probable situation: change ACTIVE_USER to NONACTIVE_USER (or
  2321. * room/equipment), or change group to security group.
  2322. */
  2323. strQuery = "UPDATE users SET objectclass = " + stringify(sExternId.objclass) + " WHERE id = " + stringify(ulObjectId);
  2324. er = lpDatabase->DoUpdate(strQuery);
  2325. if (er != erSuccess)
  2326. return er;
  2327. /* Log the change to ICS */
  2328. er = GetABSourceKeyV1(ulObjectId, &sSourceKey);
  2329. if (er != erSuccess)
  2330. return er;
  2331. AddABChange(m_lpSession, ICS_AB_CHANGE, sSourceKey, SOURCEKEY(CbABEID(&eid), reinterpret_cast<char *>(&eid)));
  2332. if (lpulObjectId != nullptr)
  2333. *lpulObjectId = ulObjectId;
  2334. return erSuccess;
  2335. }
  2336. // Check if an object has moved to a new company, or if it was created as new
  2337. ECRESULT ECUserManagement::MoveOrCreateLocalObject(const objectsignature_t &signature, unsigned int *lpulObjectId, bool *lpbMoved)
  2338. {
  2339. ECRESULT er;
  2340. objectdetails_t details;
  2341. unsigned int ulObjectId = 0;
  2342. unsigned int ulNewCompanyId = 0;
  2343. bool bMoved = false;
  2344. /*
  2345. * This function is called when an object with an external id has appeared
  2346. * there are various reasons why this might happen:
  2347. * 1) Object was created, and we should create the local item.
  2348. * 2) Object was moved to different company, we should move the local item
  2349. * 3) Object hasn't changed, but multicompany support was toggled, we should move the local item
  2350. *
  2351. * The obvious benefit from moving an user will be that it preserves the store, when
  2352. * deleting and recreating a user when it has moved the same will happen for the store
  2353. * belonging to that user. When we perform a move operation we will only update some
  2354. * columns in the database and the entire store is moved along with the user, preserving
  2355. * all messages in that store.
  2356. *
  2357. * How to determine if an object was created or moved:
  2358. * We arrived here when a search for an object within its currently known company succeeded,
  2359. * and we found a (until then unknown) objectid. What we need now is to perform a search
  2360. * in the database without announcing the company.
  2361. * If that does produce a result, then the user has moved, and we need to determine
  2362. * the new company.
  2363. */
  2364. /* We don't support moving entire companies, create it. */
  2365. if (signature.id.objclass == CONTAINER_COMPANY) {
  2366. er = CreateLocalObject(signature, &ulObjectId);
  2367. if (er != erSuccess)
  2368. return er;
  2369. goto done;
  2370. }
  2371. /* If object doesn't have a local id, we should create it. */
  2372. er = GetLocalId(signature.id, &ulObjectId);
  2373. if (er != erSuccess) {
  2374. // special case: if the objectclass of an object changed, we need to update or delete the previous version!
  2375. // function returns not found if object is deleted or really not found (same thing)
  2376. er = UpdateObjectclassOrDelete(signature.id, &ulObjectId);
  2377. if (er == KCERR_NOT_FOUND) {
  2378. er = CreateLocalObject(signature, &ulObjectId);
  2379. if (er != erSuccess)
  2380. return er;
  2381. } else if (er == erSuccess)
  2382. bMoved = true;
  2383. goto done;
  2384. }
  2385. /* Delete cache, to force call to plugin */
  2386. er = m_lpSession->GetSessionManager()->GetCacheManager()->UpdateUser(ulObjectId);
  2387. if(er != erSuccess)
  2388. return er;
  2389. /* Result new object details */
  2390. er = GetObjectDetails(ulObjectId, &details);
  2391. if (er != erSuccess)
  2392. return er;
  2393. ulNewCompanyId = details.GetPropInt(OB_PROP_I_COMPANYID);
  2394. er = MoveLocalObject(ulObjectId, signature.id.objclass, ulNewCompanyId, details.GetPropString(OB_PROP_S_LOGIN));
  2395. if (er != erSuccess)
  2396. return er;
  2397. bMoved = true;
  2398. done:
  2399. if (lpulObjectId)
  2400. *lpulObjectId = ulObjectId;
  2401. if (lpbMoved)
  2402. *lpbMoved = bMoved;
  2403. return er;
  2404. }
  2405. // Check if an object has moved to a new company, or if it was deleted completely
  2406. ECRESULT ECUserManagement::MoveOrDeleteLocalObject(unsigned int ulObjectId, objectclass_t objclass)
  2407. {
  2408. ECRESULT er;
  2409. objectdetails_t details;
  2410. unsigned int ulOldCompanyId = 0;
  2411. unsigned int ulNewCompanyId = 0;
  2412. if (IsInternalObject(ulObjectId))
  2413. return KCERR_NO_ACCESS;
  2414. /*
  2415. * This function is called when an object with an external id has disappeared,
  2416. * there are various reasons why this might happen:
  2417. * 1) Object was deleted, and we should delete the local item.
  2418. * 2) Object was moved to different company, we should move the local item
  2419. * 3) Object hasn't changed, but multicompany support was toggled, we should move the local item
  2420. *
  2421. * The obvious benefit from moving an user will be that it preserves the store, when
  2422. * deleting and recreating a user when it has moved the same will happen for the store
  2423. * belonging to that user. When we perform a move operation we will only update some
  2424. * columns in the database and the entire store is moved along with the user, preserving
  2425. * all messages in that store.
  2426. *
  2427. * How to determine if an object was deleted or moved:
  2428. * We arrived here when a search for an object within its currently known company failed,
  2429. * what we need now is to perform a search in the plugin without announcing the company.
  2430. * If that does produce a result, then the user has moved, and we need to determine
  2431. * the new company. Fortunately, the best call to determine if a user still exists
  2432. * is GetObjectDetailsAndSync() which automatically gives us the new company.
  2433. */
  2434. /* We don't support moving entire companies, delete it. */
  2435. if (objclass == CONTAINER_COMPANY)
  2436. return DeleteLocalObject(ulObjectId, objclass);
  2437. /* Request old company */
  2438. er = GetExternalId(ulObjectId, NULL, &ulOldCompanyId);
  2439. if (er != erSuccess)
  2440. return er;
  2441. /* Delete cache, to force call to plugin */
  2442. er = m_lpSession->GetSessionManager()->GetCacheManager()->UpdateUser(ulObjectId);
  2443. if(er != erSuccess)
  2444. return er;
  2445. /* Result new object details */
  2446. er = GetObjectDetails(ulObjectId, &details);
  2447. if (er == KCERR_NOT_FOUND)
  2448. /* Object was indeed deleted, GetObjectDetails() has called DeleteLocalObject() */
  2449. return erSuccess;
  2450. else if (er != erSuccess)
  2451. return er;
  2452. ulNewCompanyId = details.GetPropInt(OB_PROP_I_COMPANYID);
  2453. /*
  2454. * We got the object details, if the company is different,
  2455. * the the object was moved. Otherwise there is a bug, since
  2456. * the object was not deleted, but was not moved either...
  2457. */
  2458. if (ulOldCompanyId != ulNewCompanyId) {
  2459. er = MoveLocalObject(ulObjectId, objclass, ulNewCompanyId, details.GetPropString(OB_PROP_S_LOGIN));
  2460. if (er != erSuccess)
  2461. return er;
  2462. } else
  2463. ec_log_err("Unable to move object %s \"%s\" (id=%d)", ObjectClassToName(objclass), details.GetPropString(OB_PROP_S_LOGIN).c_str(), ulObjectId);
  2464. return erSuccess;
  2465. }
  2466. // Move a local user with the specified id to a new company
  2467. ECRESULT ECUserManagement::MoveLocalObject(unsigned int ulObjectId, objectclass_t objclass, unsigned int ulCompanyId, const string &strNewUserName)
  2468. {
  2469. ECRESULT er = erSuccess;
  2470. ECDatabase *lpDatabase = NULL;
  2471. std::string strQuery;
  2472. ABEID eid(MAPI_ABCONT, MUIDECSAB, 1);
  2473. bool bTransaction = false;
  2474. SOURCEKEY sSourceKey;
  2475. if(IsInternalObject(ulObjectId)) {
  2476. er = KCERR_NO_ACCESS;
  2477. goto exit;
  2478. }
  2479. if (objclass == CONTAINER_COMPANY) {
  2480. er = KCERR_NO_ACCESS;
  2481. goto exit;
  2482. }
  2483. if (parseBool(m_lpConfig->GetSetting("user_safe_mode"))) {
  2484. ec_log_crit("user_safe_mode: Would move %s %d to company %d", ObjectClassToName(objclass), ulObjectId, ulCompanyId);
  2485. goto exit;
  2486. }
  2487. ec_log_info("Auto-moving %s to different company from external source", ObjectClassToName(objclass));
  2488. er = m_lpSession->GetDatabase(&lpDatabase);
  2489. if(er != erSuccess)
  2490. goto exit;
  2491. bTransaction = true;
  2492. er = lpDatabase->Begin();
  2493. if(er != erSuccess)
  2494. goto exit;
  2495. /*
  2496. * Moving a user to a different company consists of the following tasks:
  2497. * 1) Change 'company' column in 'users' table
  2498. * 2) Change 'company' column in 'stores' table
  2499. * 3) Change 'user_name' column in 'stores table
  2500. */
  2501. strQuery =
  2502. "UPDATE users "
  2503. "SET company=" + stringify(ulCompanyId) + " "
  2504. "WHERE id=" + stringify(ulObjectId);
  2505. er = lpDatabase->DoUpdate(strQuery);
  2506. if(er != erSuccess)
  2507. goto exit;
  2508. strQuery =
  2509. "UPDATE stores "
  2510. "SET company=" + stringify(ulCompanyId) + ", "
  2511. "user_name='" + lpDatabase->Escape(strNewUserName) + "' "
  2512. "WHERE user_id=" + stringify(ulObjectId);
  2513. er = lpDatabase->DoUpdate(strQuery);
  2514. if(er != erSuccess)
  2515. goto exit;
  2516. /* Log the change to ICS */
  2517. er = GetABSourceKeyV1(ulObjectId, &sSourceKey);
  2518. if (er != erSuccess)
  2519. goto exit;
  2520. er = AddABChange(m_lpSession, ICS_AB_CHANGE, sSourceKey, SOURCEKEY(CbABEID(&eid), (char *)&eid));
  2521. if(er != erSuccess)
  2522. goto exit;
  2523. er = lpDatabase->Commit();
  2524. if(er != erSuccess)
  2525. goto exit;
  2526. bTransaction = false;
  2527. er = m_lpSession->GetSessionManager()->GetCacheManager()->UpdateUser(ulObjectId);
  2528. if(er != erSuccess)
  2529. goto exit;
  2530. exit:
  2531. if (lpDatabase && bTransaction && er != erSuccess)
  2532. lpDatabase->Rollback();
  2533. return er;
  2534. }
  2535. // Delete a local user with the specified id
  2536. ECRESULT ECUserManagement::DeleteLocalObject(unsigned int ulObjectId, objectclass_t objclass) {
  2537. ECRESULT er = erSuccess;
  2538. ECDatabase *lpDatabase = NULL;
  2539. DB_RESULT lpResult;
  2540. DB_ROW lpRow = NULL;
  2541. unsigned int ulDeletedRows = 0;
  2542. std::string strQuery;
  2543. ABEID eid(MAPI_ABCONT, MUIDECSAB, 1);
  2544. bool bTransaction = false;
  2545. SOURCEKEY sSourceKey;
  2546. if(IsInternalObject(ulObjectId)) {
  2547. er = KCERR_NO_ACCESS;
  2548. goto exit;
  2549. }
  2550. if (parseBool(m_lpConfig->GetSetting("user_safe_mode"))) {
  2551. ec_log_crit("user_safe_mode: Would delete %s %d", ObjectClassToName(objclass), ulObjectId);
  2552. if (objclass == CONTAINER_COMPANY)
  2553. ec_log_crit("user_safe_mode: Would delete all objects from company %d", ulObjectId);
  2554. goto exit;
  2555. }
  2556. er = m_lpSession->GetDatabase(&lpDatabase);
  2557. if(er != erSuccess)
  2558. goto exit;
  2559. ec_log_info("Auto-deleting %s %d from external source", ObjectClassToName(objclass), ulObjectId);
  2560. if (objclass == CONTAINER_COMPANY) {
  2561. /* We are removing a company, delete all company members as well since
  2562. * those members no longer exist either and we won't get a delete event
  2563. * for those users since we already get a KCERR_NOT_FOUND for the
  2564. * company... */
  2565. strQuery =
  2566. "SELECT id, objectclass FROM users "
  2567. "WHERE company = " + stringify(ulObjectId);
  2568. er = lpDatabase->DoSelect(strQuery, &lpResult);
  2569. if (er != erSuccess)
  2570. goto exit;
  2571. ec_log_info("Start auto-deleting %s members", ObjectClassToName(objclass));
  2572. while (1) {
  2573. lpRow = lpDatabase->FetchRow(lpResult);
  2574. if (lpRow == NULL || lpRow[0] == NULL || lpRow[1] == NULL)
  2575. break;
  2576. /* Perhaps the user was moved to a different company */
  2577. er = MoveOrDeleteLocalObject(atoui(lpRow[0]), (objectclass_t)atoui(lpRow[1]));
  2578. if (er != erSuccess)
  2579. goto exit;
  2580. }
  2581. lpRow = NULL;
  2582. ec_log_info("Done auto-deleting %s members", ObjectClassToName(objclass));
  2583. }
  2584. bTransaction = true;
  2585. er = lpDatabase->Begin();
  2586. if(er != erSuccess)
  2587. goto exit;
  2588. // Request source key before deleting the entry
  2589. er = GetABSourceKeyV1(ulObjectId, &sSourceKey);
  2590. if (er != erSuccess)
  2591. goto exit;
  2592. strQuery =
  2593. "DELETE FROM users "
  2594. "WHERE id=" + stringify(ulObjectId) + " "
  2595. "AND " + OBJECTCLASS_COMPARE_SQL("objectclass", objclass);
  2596. er = lpDatabase->DoDelete(strQuery, &ulDeletedRows);
  2597. if(er != erSuccess)
  2598. goto exit;
  2599. // Delete the user ACL's
  2600. strQuery =
  2601. "DELETE FROM acl "
  2602. "WHERE id=" + stringify(ulObjectId);
  2603. er = lpDatabase->DoDelete(strQuery);
  2604. if(er != erSuccess)
  2605. goto exit;
  2606. // Remove client update history
  2607. strQuery = "DELETE FROM clientupdatestatus where userid=" + stringify(ulObjectId);
  2608. er = lpDatabase->DoDelete(strQuery);
  2609. if(er != erSuccess)
  2610. goto exit;
  2611. // Object didn't exist locally, so no delete has occurred
  2612. if (ulDeletedRows == 0) {
  2613. er = lpDatabase->Commit();
  2614. if (er != erSuccess)
  2615. goto exit;
  2616. er = m_lpSession->GetSessionManager()->GetCacheManager()->UpdateUser(ulObjectId);
  2617. if (er != erSuccess)
  2618. goto exit;
  2619. // Done, no ICS change, no userscript action required
  2620. goto exit;
  2621. }
  2622. // Log the change to ICS
  2623. er = AddABChange(m_lpSession, ICS_AB_DELETE, sSourceKey, SOURCEKEY(CbABEID(&eid), (char *)&eid));
  2624. if(er != erSuccess)
  2625. goto exit;
  2626. er = lpDatabase->Commit();
  2627. if(er != erSuccess)
  2628. goto exit;
  2629. bTransaction = false;
  2630. // Purge the usercache because we also need to remove sendas relations
  2631. er = m_lpSession->GetSessionManager()->GetCacheManager()->PurgeCache(PURGE_CACHE_USEROBJECT | PURGE_CACHE_EXTERNID | PURGE_CACHE_USERDETAILS);
  2632. if(er != erSuccess)
  2633. goto exit;
  2634. switch (objclass) {
  2635. case ACTIVE_USER:
  2636. case NONACTIVE_USER:
  2637. case NONACTIVE_ROOM:
  2638. case NONACTIVE_EQUIPMENT:
  2639. strQuery = "SELECT HEX(guid) FROM stores WHERE user_id=" + stringify(ulObjectId);
  2640. er = lpDatabase->DoSelect(strQuery, &lpResult);
  2641. if(er != erSuccess)
  2642. goto exit;
  2643. lpRow = lpDatabase->FetchRow(lpResult);
  2644. if(lpRow == NULL) {
  2645. ec_log_info("User script not executed. No store exists.");
  2646. goto exit;
  2647. } else if (lpRow[0] == NULL) {
  2648. er = KCERR_DATABASE_ERROR;
  2649. ec_log_err("ECUserManagement::DeleteLocalObject(): column null");
  2650. goto exit;
  2651. }
  2652. execute_script(m_lpConfig->GetSetting("deleteuser_script"),
  2653. "KOPANO_STOREGUID", lpRow[0],
  2654. NULL);
  2655. break;
  2656. case DISTLIST_GROUP:
  2657. case DISTLIST_SECURITY:
  2658. execute_script(m_lpConfig->GetSetting("deletegroup_script"),
  2659. "KOPANO_GROUPID", stringify(ulObjectId).c_str(),
  2660. NULL);
  2661. break;
  2662. case CONTAINER_COMPANY:
  2663. execute_script(m_lpConfig->GetSetting("deletecompany_script"),
  2664. "KOPANO_COMPANYID", stringify(ulObjectId).c_str(),
  2665. NULL);
  2666. break;
  2667. default:
  2668. break;
  2669. }
  2670. exit:
  2671. if (er)
  2672. ec_log_info("Auto-deleting %s %d done. Error code 0x%08X", ObjectClassToName(objclass), ulObjectId, er);
  2673. else
  2674. ec_log_info("Auto-deleting %s %d done.", ObjectClassToName(objclass), ulObjectId);
  2675. if (lpDatabase != nullptr && bTransaction && er != erSuccess)
  2676. lpDatabase->Rollback();
  2677. return er;
  2678. }
  2679. bool ECUserManagement::IsInternalObject(unsigned int ulUserId)
  2680. {
  2681. return ulUserId == KOPANO_UID_SYSTEM || ulUserId == KOPANO_UID_EVERYONE;
  2682. }
  2683. /**
  2684. * Returns property values for "anonymous" properties, which are tags
  2685. * from plugin::getExtraAddressbookProperties().
  2686. *
  2687. * @param[in] soap soap struct for memory allocation
  2688. * @param[in] lpDetails details object of an addressbook object to get the values from
  2689. * @param[in] ulPropTag property to fetch from the details
  2690. * @param[out] lpPropVal soap propVal to return to the caller
  2691. *
  2692. * @return internal error code
  2693. * @retval erSuccess value is set
  2694. * @retval KCERR_UNKNOWN value for proptag is not found
  2695. */
  2696. ECRESULT ECUserManagement::ConvertAnonymousObjectDetailToProp(struct soap *soap, objectdetails_t *lpDetails, unsigned int ulPropTag, struct propVal *lpPropVal)
  2697. {
  2698. ECRESULT er;
  2699. struct propVal sPropVal;
  2700. std::string strValue;
  2701. std::list<std::string> lstrValues;
  2702. std::list<objectid_t> lobjValues;
  2703. unsigned int i = 0;
  2704. unsigned int ulGetPropTag;
  2705. // Force PT_STRING8 versions for anonymous getprops
  2706. if ((PROP_TYPE(ulPropTag) & PT_MV_STRING8) == PT_STRING8)
  2707. ulGetPropTag = CHANGE_PROP_TYPE(ulPropTag, PT_STRING8);
  2708. else if ((PROP_TYPE(ulPropTag) & PT_MV_STRING8) == PT_MV_STRING8)
  2709. ulGetPropTag = CHANGE_PROP_TYPE(ulPropTag, PT_MV_STRING8);
  2710. else
  2711. ulGetPropTag = ulPropTag;
  2712. if (PROP_TYPE(ulPropTag) & MV_FLAG) {
  2713. lstrValues = lpDetails->GetPropListString((property_key_t)ulGetPropTag);
  2714. if (lstrValues.empty())
  2715. return KCERR_UNKNOWN;
  2716. } else {
  2717. strValue = lpDetails->GetPropString((property_key_t)ulGetPropTag);
  2718. if (strValue.empty())
  2719. return KCERR_UNKNOWN;
  2720. }
  2721. /* Special properties which cannot be copied blindly */
  2722. switch (ulPropTag) {
  2723. case 0x80050102: /* PR_EMS_AB_MANAGER | PT_BINARY */
  2724. case 0x800C0102: /* PR_EMS_AB_OWNER | PT_BINARY */
  2725. er = CreateABEntryID(soap, lpDetails->GetPropObject((property_key_t)ulPropTag), lpPropVal);
  2726. if (er != erSuccess)
  2727. return er;
  2728. lpPropVal->ulPropTag = ulPropTag;
  2729. return erSuccess;
  2730. case 0x80081102: /* PR_EMS_AB_IS_MEMBER_OF_DL | PT_MV_BINARY */
  2731. case 0x800E1102: /* PR_EMS_AB_REPORTS | PT_MV_BINARY */
  2732. lobjValues = lpDetails->GetPropListObject((property_key_t)ulPropTag);
  2733. lpPropVal->__union = SOAP_UNION_propValData_mvbin;
  2734. lpPropVal->ulPropTag = ulPropTag;
  2735. lpPropVal->Value.mvbin.__size = 0;
  2736. lpPropVal->Value.mvbin.__ptr = s_alloc<struct xsd__base64Binary>(soap, lobjValues.size());
  2737. i = 0;
  2738. for (const auto &obj : lobjValues) {
  2739. er = CreateABEntryID(soap, obj, &sPropVal);
  2740. if (er != erSuccess)
  2741. continue;
  2742. lpPropVal->Value.mvbin.__ptr[i].__ptr = sPropVal.Value.bin->__ptr;
  2743. lpPropVal->Value.mvbin.__ptr[i++].__size = sPropVal.Value.bin->__size;
  2744. }
  2745. lpPropVal->Value.mvbin.__size = i;
  2746. return erSuccess;
  2747. default:
  2748. break;
  2749. }
  2750. switch (PROP_TYPE(ulPropTag)) {
  2751. case PT_BOOLEAN:
  2752. lpPropVal->ulPropTag = ulPropTag;
  2753. lpPropVal->Value.b = parseBool(strValue.c_str());
  2754. lpPropVal->__union = SOAP_UNION_propValData_b;
  2755. break;
  2756. case PT_SHORT:
  2757. lpPropVal->ulPropTag = ulPropTag;
  2758. lpPropVal->Value.i = atoi(strValue.c_str());
  2759. lpPropVal->__union = SOAP_UNION_propValData_i;
  2760. break;
  2761. case PT_LONG:
  2762. lpPropVal->ulPropTag = ulPropTag;
  2763. lpPropVal->Value.ul = atoi(strValue.c_str());
  2764. lpPropVal->__union = SOAP_UNION_propValData_ul;
  2765. break;
  2766. case PT_LONGLONG:
  2767. lpPropVal->ulPropTag = ulPropTag;
  2768. lpPropVal->Value.li = atol(strValue.c_str());
  2769. lpPropVal->__union = SOAP_UNION_propValData_li;
  2770. break;
  2771. case PT_STRING8:
  2772. case PT_UNICODE:
  2773. lpPropVal->ulPropTag = ulPropTag;
  2774. lpPropVal->Value.lpszA = s_strcpy(soap, strValue.c_str());
  2775. lpPropVal->__union = SOAP_UNION_propValData_lpszA;
  2776. break;
  2777. case PT_MV_STRING8:
  2778. case PT_MV_UNICODE:
  2779. lpPropVal->ulPropTag = ulPropTag;
  2780. lpPropVal->Value.mvszA.__size = lstrValues.size();
  2781. lpPropVal->Value.mvszA.__ptr = s_alloc<char *>(soap, lstrValues.size());
  2782. i = 0;
  2783. for (const auto &val : lstrValues)
  2784. lpPropVal->Value.mvszA.__ptr[i++] = s_strcpy(soap, val.c_str());
  2785. lpPropVal->__union = SOAP_UNION_propValData_mvszA;
  2786. break;
  2787. case PT_BINARY:
  2788. lpPropVal->ulPropTag = ulPropTag;
  2789. lpPropVal->Value.bin = s_alloc<struct xsd__base64Binary>(soap);
  2790. lpPropVal->Value.bin->__size = strValue.size();
  2791. lpPropVal->Value.bin->__ptr = s_alloc<unsigned char>(soap, strValue.size());
  2792. memcpy(lpPropVal->Value.bin->__ptr, strValue.data(), strValue.size());
  2793. lpPropVal->__union = SOAP_UNION_propValData_bin;
  2794. break;
  2795. case PT_MV_BINARY:
  2796. lpPropVal->ulPropTag = ulPropTag;
  2797. lpPropVal->Value.mvbin.__size = lstrValues.size();
  2798. lpPropVal->Value.mvbin.__ptr = s_alloc<struct xsd__base64Binary>(soap, lstrValues.size());
  2799. i = 0;
  2800. for (const auto &val : lstrValues) {
  2801. lpPropVal->Value.mvbin.__ptr[i].__size = val.size();
  2802. lpPropVal->Value.mvbin.__ptr[i].__ptr = s_alloc<unsigned char>(soap, val.size());
  2803. memcpy(lpPropVal->Value.mvbin.__ptr[i++].__ptr, val.data(), val.size());
  2804. }
  2805. lpPropVal->__union = SOAP_UNION_propValData_mvbin;
  2806. break;
  2807. default:
  2808. return KCERR_UNKNOWN;
  2809. }
  2810. return erSuccess;
  2811. }
  2812. /**
  2813. * Convert objectdetails_t to a set of MAPI properties
  2814. *
  2815. * Creates properties for a row in the addressbook or resolvenames
  2816. * entries. Input proptags is what the client requested, and may
  2817. * contain PT_UNSPECIFIED. String properties are returned in UTF-8,
  2818. * but ulPropTag is set to the correct proptype.
  2819. *
  2820. * @param[in] soap Pointer to soap struct, used for s_alloc
  2821. * @param[in] ulId Internal user id
  2822. * @param[in] lpDetails Pointer to details of the user
  2823. * @param[in] lpPropTags Array of properties the client requested
  2824. * @param[out] lpPropValsRet Property values to return to client
  2825. * @return ECRESULT Kopano error code
  2826. *
  2827. * @todo make sure all strings in lpszA are valid UTF-8
  2828. */
  2829. ECRESULT ECUserManagement::ConvertObjectDetailsToProps(struct soap *soap, unsigned int ulId, objectdetails_t *lpDetails, struct propTagArray *lpPropTags, struct propValArray *lpPropValsRet)
  2830. {
  2831. ECRESULT er = erSuccess;
  2832. struct propVal *lpPropVal;
  2833. unsigned int ulOrder = 0;
  2834. ECSecurity *lpSecurity = NULL;
  2835. struct propValArray sPropVals{__gszeroinit};
  2836. struct propValArray *lpPropVals = &sPropVals;
  2837. ULONG ulMapiType = 0;
  2838. er = GetSecurity(&lpSecurity);
  2839. if (er != erSuccess)
  2840. goto exit;
  2841. er = lpSecurity->IsUserObjectVisible(ulId);
  2842. if (er != erSuccess)
  2843. goto exit;
  2844. er = TypeToMAPIType(lpDetails->GetClass(), &ulMapiType);
  2845. if (er != erSuccess)
  2846. goto exit;
  2847. lpPropVals->__ptr = s_alloc<struct propVal>(soap, lpPropTags->__size);
  2848. lpPropVals->__size = lpPropTags->__size;
  2849. for (gsoap_size_t i = 0; i < lpPropTags->__size; ++i) {
  2850. lpPropVal = &lpPropVals->__ptr[i];
  2851. lpPropVal->ulPropTag = lpPropTags->__ptr[i];
  2852. switch(lpDetails->GetClass()) {
  2853. default:
  2854. ec_log_err("Details failed for object id %d (type %d)", ulId, lpDetails->GetClass());
  2855. er = KCERR_NOT_FOUND;
  2856. goto exit;
  2857. case ACTIVE_USER:
  2858. case NONACTIVE_USER:
  2859. case NONACTIVE_ROOM:
  2860. case NONACTIVE_EQUIPMENT:
  2861. case NONACTIVE_CONTACT:
  2862. {
  2863. switch(NormalizePropTag(lpPropTags->__ptr[i])) {
  2864. case PR_ENTRYID: {
  2865. er = CreateABEntryID(soap, ulId, ulMapiType, lpPropVal);
  2866. if (er != erSuccess)
  2867. goto exit;
  2868. break;
  2869. }
  2870. case PR_EC_NONACTIVE:
  2871. lpPropVal->Value.b = (lpDetails->GetClass() != ACTIVE_USER);
  2872. lpPropVal->__union = SOAP_UNION_propValData_b;
  2873. break;
  2874. case PR_EC_ADMINISTRATOR:
  2875. lpPropVal->Value.ul = lpDetails->GetPropInt(OB_PROP_I_ADMINLEVEL);
  2876. lpPropVal->__union = SOAP_UNION_propValData_ul;
  2877. break;
  2878. case PR_EC_COMPANY_NAME: {
  2879. if (IsInternalObject(ulId) || (! m_lpSession->GetSessionManager()->IsHostedSupported())) {
  2880. lpPropVal->Value.lpszA = s_strcpy(soap, "");
  2881. } else {
  2882. objectdetails_t sCompanyDetails;
  2883. er = GetObjectDetails(lpDetails->GetPropInt(OB_PROP_I_COMPANYID), &sCompanyDetails);
  2884. if (er != erSuccess)
  2885. goto exit;
  2886. lpPropVal->Value.lpszA = s_strcpy(soap, sCompanyDetails.GetPropString(OB_PROP_S_FULLNAME).c_str());
  2887. }
  2888. lpPropVal->__union = SOAP_UNION_propValData_lpszA;
  2889. break;
  2890. }
  2891. case PR_SMTP_ADDRESS:
  2892. lpPropVal->Value.lpszA = s_strcpy(soap, lpDetails->GetPropString(OB_PROP_S_EMAIL).c_str());
  2893. lpPropVal->__union = SOAP_UNION_propValData_lpszA;
  2894. break;
  2895. case PR_MESSAGE_CLASS:
  2896. lpPropVal->Value.lpszA = s_strcpy(soap, "IPM.Contact");
  2897. lpPropVal->__union = SOAP_UNION_propValData_lpszA;
  2898. break;
  2899. case PR_NORMALIZED_SUBJECT:
  2900. case PR_7BIT_DISPLAY_NAME:
  2901. case PR_DISPLAY_NAME:
  2902. case PR_TRANSMITABLE_DISPLAY_NAME:
  2903. lpPropVal->Value.lpszA = s_strcpy(soap, lpDetails->GetPropString(OB_PROP_S_FULLNAME).c_str());
  2904. lpPropVal->__union = SOAP_UNION_propValData_lpszA;
  2905. break;
  2906. case PR_INSTANCE_KEY:
  2907. lpPropVal->Value.bin = s_alloc<struct xsd__base64Binary>(soap);
  2908. lpPropVal->Value.bin->__ptr = s_alloc<unsigned char>(soap, 2 * sizeof(ULONG));
  2909. lpPropVal->Value.bin->__size = 2*sizeof(ULONG);
  2910. lpPropVal->__union = SOAP_UNION_propValData_bin;
  2911. memcpy(lpPropVal->Value.bin->__ptr, &ulId, sizeof(ULONG));
  2912. memcpy(lpPropVal->Value.bin->__ptr+sizeof(ULONG), &ulOrder, sizeof(ULONG));
  2913. break;
  2914. case PR_OBJECT_TYPE:
  2915. lpPropVal->Value.ul = MAPI_MAILUSER;
  2916. lpPropVal->__union = SOAP_UNION_propValData_ul;
  2917. break;
  2918. case PR_DISPLAY_TYPE:
  2919. if (lpDetails->GetClass() != NONACTIVE_CONTACT)
  2920. lpPropVal->Value.ul = DT_MAILUSER;
  2921. else
  2922. lpPropVal->Value.ul = DT_REMOTE_MAILUSER;
  2923. lpPropVal->__union = SOAP_UNION_propValData_ul;
  2924. break;
  2925. case PR_DISPLAY_TYPE_EX:
  2926. switch(lpDetails->GetClass()) {
  2927. default:
  2928. case ACTIVE_USER:
  2929. lpPropVal->Value.ul = DT_MAILUSER | DTE_FLAG_ACL_CAPABLE;
  2930. break;
  2931. case NONACTIVE_USER:
  2932. lpPropVal->Value.ul = DT_MAILUSER;
  2933. break;
  2934. case NONACTIVE_ROOM:
  2935. lpPropVal->Value.ul = DT_ROOM;
  2936. break;
  2937. case NONACTIVE_EQUIPMENT:
  2938. lpPropVal->Value.ul = DT_EQUIPMENT;
  2939. break;
  2940. case NONACTIVE_CONTACT:
  2941. lpPropVal->Value.ul = DT_REMOTE_MAILUSER;
  2942. break;
  2943. };
  2944. lpPropVal->__union = SOAP_UNION_propValData_ul;
  2945. break;
  2946. case PR_EMS_AB_ROOM_CAPACITY:
  2947. lpPropVal->Value.ul = lpDetails->GetPropInt(OB_PROP_I_RESOURCE_CAPACITY);
  2948. lpPropVal->__union = SOAP_UNION_propValData_ul;
  2949. break;
  2950. case PR_EMS_AB_ROOM_DESCRIPTION:
  2951. if (lpDetails->GetClass() != ACTIVE_USER) {
  2952. std::string strDesc = lpDetails->GetPropString(OB_PROP_S_RESOURCE_DESCRIPTION);
  2953. if (strDesc.empty()) {
  2954. switch (lpDetails->GetClass()) {
  2955. case NONACTIVE_ROOM:
  2956. strDesc = "Room";
  2957. break;
  2958. case NONACTIVE_EQUIPMENT:
  2959. strDesc = "Equipment";
  2960. break;
  2961. default:
  2962. // actually to keep the compiler happy
  2963. strDesc = "Invalid";
  2964. break;
  2965. }
  2966. }
  2967. lpPropVal->ulPropTag = lpPropTags->__ptr[i];
  2968. lpPropVal->Value.lpszA = s_strcpy(soap, strDesc.c_str());
  2969. lpPropVal->__union = SOAP_UNION_propValData_lpszA;
  2970. } else {
  2971. lpPropVal->ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(lpPropTags->__ptr[i]));
  2972. lpPropVal->Value.ul = KCERR_NOT_FOUND;
  2973. lpPropVal->__union = SOAP_UNION_propValData_ul;
  2974. }
  2975. break;
  2976. case PR_SEARCH_KEY: {
  2977. std::string strSearchKey = (std::string)"ZARAFA:" + strToUpper(lpDetails->GetPropString(OB_PROP_S_EMAIL));
  2978. lpPropVal->Value.bin = s_alloc<xsd__base64Binary>(soap);
  2979. lpPropVal->Value.bin->__ptr = s_alloc<unsigned char>(soap, strSearchKey.size()+1);
  2980. lpPropVal->Value.bin->__size = strSearchKey.size()+1;
  2981. lpPropVal->__union = SOAP_UNION_propValData_bin;
  2982. strcpy((char *)lpPropVal->Value.bin->__ptr, strSearchKey.c_str());
  2983. break;
  2984. }
  2985. case PR_ADDRTYPE:
  2986. lpPropVal->Value.lpszA = s_strcpy(soap, "ZARAFA");
  2987. lpPropVal->__union = SOAP_UNION_propValData_lpszA;
  2988. break;
  2989. case PR_RECORD_KEY:
  2990. lpPropVal->Value.bin = s_alloc<struct xsd__base64Binary>(soap);
  2991. lpPropVal->Value.bin->__ptr = s_alloc<unsigned char>(soap, sizeof(ULONG));
  2992. lpPropVal->Value.bin->__size = sizeof(ULONG);
  2993. lpPropVal->__union = SOAP_UNION_propValData_bin;
  2994. memcpy(lpPropVal->Value.bin->__ptr, &ulId, sizeof(ULONG));
  2995. break;
  2996. case PR_EMS_AB_HOME_MDB: {
  2997. /* Make BlackBerry happy */
  2998. std::string serverName = lpDetails->GetPropString(OB_PROP_S_SERVERNAME);
  2999. if (serverName.empty())
  3000. serverName = "Unknown";
  3001. std::string hostname =
  3002. "/o=Domain/ou=Location/cn=Configuration/cn=Servers/cn=" + serverName + "/cn=Microsoft Private MDB";
  3003. lpPropVal->Value.lpszA = s_strcpy(soap, hostname.c_str());
  3004. lpPropVal->__union = SOAP_UNION_propValData_lpszA;
  3005. break;
  3006. }
  3007. case PR_EMS_AB_HOME_MTA: {
  3008. /* Make BlackBerry happy */
  3009. std::string serverName = lpDetails->GetPropString(OB_PROP_S_SERVERNAME);
  3010. if (serverName.empty())
  3011. serverName = "Unknown";
  3012. std::string hostname =
  3013. "/o=KOPANO/ou=First Administrative Group/cn=Configuration/cn=Servers/cn=" + serverName + "/cn=Microsoft MTA";
  3014. lpPropVal->Value.lpszA = s_strcpy(soap, hostname.c_str());
  3015. lpPropVal->__union = SOAP_UNION_propValData_lpszA;
  3016. break;
  3017. }
  3018. case PR_ACCOUNT:
  3019. case PR_EMAIL_ADDRESS:
  3020. // Dont use login name for NONACTIVE_CONTACT since it doesn't have a login name
  3021. if(lpDetails->GetClass() != NONACTIVE_CONTACT)
  3022. lpPropVal->Value.lpszA = s_strcpy(soap, lpDetails->GetPropString(OB_PROP_S_LOGIN).c_str());
  3023. else
  3024. lpPropVal->Value.lpszA = s_strcpy(soap, lpDetails->GetPropString(OB_PROP_S_FULLNAME).c_str());
  3025. lpPropVal->__union = SOAP_UNION_propValData_lpszA;
  3026. break;
  3027. case PR_SEND_INTERNET_ENCODING:
  3028. lpPropVal->Value.ul = 0;
  3029. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3030. break;
  3031. case PR_SEND_RICH_INFO:
  3032. lpPropVal->Value.b = true;
  3033. lpPropVal->__union = SOAP_UNION_propValData_b;
  3034. break;
  3035. case PR_AB_PROVIDER_ID:
  3036. lpPropVal->Value.bin = s_alloc<struct xsd__base64Binary>(soap);
  3037. lpPropVal->Value.bin->__ptr = s_alloc<unsigned char>(soap, sizeof(GUID));
  3038. lpPropVal->Value.bin->__size = sizeof(GUID);
  3039. lpPropVal->__union = SOAP_UNION_propValData_bin;
  3040. memcpy(lpPropVal->Value.bin->__ptr, &MUIDECSAB, sizeof(GUID));
  3041. break;
  3042. case PR_EMS_AB_X509_CERT: {
  3043. list<string> strCerts = lpDetails->GetPropListString(OB_PROP_LS_CERTIFICATE);
  3044. if (strCerts.empty())
  3045. /* This is quite annoying. The LDAP plugin loads it in a specific location, while the db plugin
  3046. saves it through the anonymous map into the proptag location.
  3047. */
  3048. strCerts = lpDetails->GetPropListString((property_key_t)lpPropTags->__ptr[i]);
  3049. if (!strCerts.empty()) {
  3050. unsigned int i = 0;
  3051. lpPropVal->__union = SOAP_UNION_propValData_mvbin;
  3052. lpPropVal->Value.mvbin.__size = strCerts.size();
  3053. lpPropVal->Value.mvbin.__ptr = s_alloc<struct xsd__base64Binary>(soap, strCerts.size());
  3054. for (const auto &cert : strCerts) {
  3055. lpPropVal->Value.mvbin.__ptr[i].__size = cert.size();
  3056. lpPropVal->Value.mvbin.__ptr[i].__ptr = s_alloc<unsigned char>(soap, cert.size());
  3057. memcpy(lpPropVal->Value.mvbin.__ptr[i++].__ptr, cert.data(), cert.size());
  3058. }
  3059. } else {
  3060. lpPropVal->ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(lpPropTags->__ptr[i]));
  3061. lpPropVal->Value.ul = KCERR_NOT_FOUND;
  3062. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3063. }
  3064. break;
  3065. }
  3066. case PR_EC_SENDAS_USER_ENTRYIDS: {
  3067. list<objectid_t> userIds = lpDetails->GetPropListObject(OB_PROP_LO_SENDAS);
  3068. if (!userIds.empty()) {
  3069. unsigned int i;
  3070. struct propVal sPropVal;
  3071. lpPropVal->__union = SOAP_UNION_propValData_mvbin;
  3072. lpPropVal->Value.mvbin.__size = 0;
  3073. lpPropVal->Value.mvbin.__ptr = s_alloc<struct xsd__base64Binary>(soap, userIds.size());
  3074. i = 0;
  3075. for (const auto &uid : userIds) {
  3076. er = CreateABEntryID(soap, uid, &sPropVal);
  3077. if (er != erSuccess)
  3078. continue;
  3079. lpPropVal->Value.mvbin.__ptr[i].__ptr = sPropVal.Value.bin->__ptr;
  3080. lpPropVal->Value.mvbin.__ptr[i++].__size = sPropVal.Value.bin->__size;
  3081. }
  3082. lpPropVal->Value.mvbin.__size = i;
  3083. } else {
  3084. lpPropVal->ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(lpPropTags->__ptr[i]));
  3085. lpPropVal->Value.ul = KCERR_NOT_FOUND;
  3086. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3087. }
  3088. break;
  3089. }
  3090. case PR_EMS_AB_PROXY_ADDRESSES: {
  3091. // Use upper-case 'SMTP' for primary
  3092. std::string strPrefix("SMTP:");
  3093. std::string address(lpDetails->GetPropString(OB_PROP_S_EMAIL));
  3094. std::list<std::string> lstAliases = lpDetails->GetPropListString(OB_PROP_LS_ALIASES);
  3095. ULONG nAliases = lstAliases.size();
  3096. ULONG i = 0;
  3097. lpPropVal->__union = SOAP_UNION_propValData_mvszA;
  3098. lpPropVal->Value.mvszA.__ptr = s_alloc<char *>(soap, 1 + nAliases);
  3099. if (!address.empty()) {
  3100. address = strPrefix + address;
  3101. lpPropVal->Value.mvszA.__ptr[i++] = s_strcpy(soap, address.c_str());
  3102. }
  3103. // Use lower-case 'smtp' prefix for aliases
  3104. strPrefix = "smtp:";
  3105. for (const auto &alias : lstAliases)
  3106. lpPropVal->Value.mvszA.__ptr[i++] = s_strcpy(soap, (strPrefix + alias).c_str());
  3107. lpPropVal->Value.mvszA.__size = i;
  3108. break;
  3109. }
  3110. case PR_EC_EXCHANGE_DN: {
  3111. std::string exchangeDN = lpDetails->GetPropString(OB_PROP_S_EXCH_DN);
  3112. if (!exchangeDN.empty()) {
  3113. lpPropVal->Value.lpszA = s_strcpy(soap, exchangeDN.c_str());
  3114. lpPropVal->__union = SOAP_UNION_propValData_lpszA;
  3115. } else {
  3116. lpPropVal->ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(lpPropTags->__ptr[i]));
  3117. lpPropVal->Value.ul = KCERR_NOT_FOUND;
  3118. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3119. }
  3120. break;
  3121. }
  3122. case PR_EC_HOMESERVER_NAME: {
  3123. std::string serverName = lpDetails->GetPropString(OB_PROP_S_SERVERNAME);
  3124. if (!serverName.empty()) {
  3125. lpPropVal->Value.lpszA = s_strcpy(soap, serverName.c_str());
  3126. lpPropVal->__union = SOAP_UNION_propValData_lpszA;
  3127. } else {
  3128. lpPropVal->ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(lpPropTags->__ptr[i]));
  3129. lpPropVal->Value.ul = KCERR_NOT_FOUND;
  3130. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3131. }
  3132. break;
  3133. }
  3134. default:
  3135. /* Property not handled in switch, try checking if user has mapped the property personally */
  3136. if (ConvertAnonymousObjectDetailToProp(soap, lpDetails, lpPropTags->__ptr[i], lpPropVal) != erSuccess) {
  3137. lpPropVal->ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(lpPropTags->__ptr[i]));
  3138. lpPropVal->Value.ul = KCERR_NOT_FOUND;
  3139. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3140. }
  3141. break;
  3142. }
  3143. break;
  3144. } // end case ACTIVE_USER*
  3145. case CONTAINER_COMPANY:
  3146. case DISTLIST_GROUP:
  3147. case DISTLIST_SECURITY:
  3148. case DISTLIST_DYNAMIC: {
  3149. switch(NormalizePropTag(lpPropTags->__ptr[i])) {
  3150. case PR_EC_COMPANY_NAME: {
  3151. if (IsInternalObject(ulId) || (! m_lpSession->GetSessionManager()->IsHostedSupported())) {
  3152. lpPropVal->Value.lpszA = s_strcpy(soap, "");
  3153. } else {
  3154. objectdetails_t sCompanyDetails;
  3155. er = GetObjectDetails(lpDetails->GetPropInt(OB_PROP_I_COMPANYID), &sCompanyDetails);
  3156. if (er != erSuccess)
  3157. goto exit;
  3158. lpPropVal->Value.lpszA = s_strcpy(soap, sCompanyDetails.GetPropString(OB_PROP_S_FULLNAME).c_str());
  3159. }
  3160. lpPropVal->__union = SOAP_UNION_propValData_lpszA;
  3161. break;
  3162. }
  3163. case PR_SEARCH_KEY: {
  3164. std::string strSearchKey = (std::string)"ZARAFA:" + strToUpper(lpDetails->GetPropString(OB_PROP_S_FULLNAME));
  3165. lpPropVal->Value.bin = s_alloc<struct xsd__base64Binary>(soap);
  3166. lpPropVal->Value.bin->__ptr = s_alloc<unsigned char>(soap, strSearchKey.size()+1);
  3167. lpPropVal->Value.bin->__size = strSearchKey.size()+1;
  3168. lpPropVal->__union = SOAP_UNION_propValData_bin;
  3169. strcpy((char *)lpPropVal->Value.bin->__ptr, strSearchKey.c_str());
  3170. break;
  3171. }
  3172. case PR_ADDRTYPE:
  3173. lpPropVal->Value.lpszA = s_strcpy(soap, "ZARAFA");
  3174. lpPropVal->__union = SOAP_UNION_propValData_lpszA;
  3175. break;
  3176. case PR_EMAIL_ADDRESS:
  3177. lpPropVal->Value.lpszA = s_strcpy(soap, lpDetails->GetPropString(OB_PROP_S_LOGIN).c_str());
  3178. lpPropVal->__union = SOAP_UNION_propValData_lpszA;
  3179. break;
  3180. case PR_MESSAGE_CLASS:
  3181. lpPropVal->Value.lpszA = s_strcpy(soap, "IPM.DistList");
  3182. lpPropVal->__union = SOAP_UNION_propValData_lpszA;
  3183. break;
  3184. case PR_ENTRYID:
  3185. er = CreateABEntryID(soap, ulId, ulMapiType, lpPropVal);
  3186. if (er != erSuccess)
  3187. goto exit;
  3188. break;
  3189. case PR_NORMALIZED_SUBJECT:
  3190. case PR_DISPLAY_NAME:
  3191. case PR_TRANSMITABLE_DISPLAY_NAME:
  3192. lpPropVal->Value.lpszA = s_strcpy(soap, lpDetails->GetPropString(OB_PROP_S_FULLNAME).c_str());
  3193. lpPropVal->__union = SOAP_UNION_propValData_lpszA;
  3194. break;
  3195. case PR_SMTP_ADDRESS:
  3196. if (lpDetails->HasProp(OB_PROP_S_EMAIL)) {
  3197. lpPropVal->Value.lpszA = s_strcpy(soap, lpDetails->GetPropString(OB_PROP_S_EMAIL).c_str());
  3198. lpPropVal->__union = SOAP_UNION_propValData_lpszA;
  3199. } else {
  3200. lpPropVal->ulPropTag = CHANGE_PROP_TYPE(lpPropVal->ulPropTag, PT_ERROR);
  3201. lpPropVal->Value.ul = MAPI_E_NOT_FOUND;
  3202. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3203. }
  3204. break;
  3205. case PR_INSTANCE_KEY:
  3206. lpPropVal->Value.bin = s_alloc<xsd__base64Binary>(soap);
  3207. lpPropVal->Value.bin->__ptr = s_alloc<unsigned char>(soap, 2 * sizeof(ULONG));
  3208. lpPropVal->Value.bin->__size = 2*sizeof(ULONG);
  3209. lpPropVal->__union = SOAP_UNION_propValData_bin;
  3210. memcpy(lpPropVal->Value.bin->__ptr, &ulId, sizeof(ULONG));
  3211. memcpy(lpPropVal->Value.bin->__ptr+sizeof(ULONG), &ulOrder, sizeof(ULONG));
  3212. break;
  3213. case PR_OBJECT_TYPE:
  3214. lpPropVal->Value.ul = MAPI_DISTLIST;
  3215. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3216. break;
  3217. case PR_DISPLAY_TYPE:
  3218. if (lpDetails->GetClass() == CONTAINER_COMPANY)
  3219. lpPropVal->Value.ul = DT_ORGANIZATION;
  3220. else
  3221. lpPropVal->Value.ul = DT_DISTLIST;
  3222. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3223. break;
  3224. case PR_DISPLAY_TYPE_EX:
  3225. switch (lpDetails->GetClass()) {
  3226. case DISTLIST_GROUP:
  3227. lpPropVal->Value.ul = DT_DISTLIST;
  3228. break;
  3229. case DISTLIST_SECURITY:
  3230. lpPropVal->Value.ul = DT_SEC_DISTLIST | DTE_FLAG_ACL_CAPABLE;
  3231. break;
  3232. case DISTLIST_DYNAMIC:
  3233. lpPropVal->Value.ul = DT_AGENT;
  3234. break;
  3235. case CONTAINER_COMPANY:
  3236. lpPropVal->Value.ul = DT_ORGANIZATION | DTE_FLAG_ACL_CAPABLE;
  3237. break;
  3238. default:
  3239. lpPropVal->ulPropTag = CHANGE_PROP_TYPE(lpPropVal->ulPropTag, PT_ERROR);
  3240. lpPropVal->Value.ul = MAPI_E_NOT_FOUND;
  3241. break;
  3242. }
  3243. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3244. break;
  3245. case PR_RECORD_KEY:
  3246. lpPropVal->Value.bin = s_alloc<struct xsd__base64Binary>(soap);
  3247. lpPropVal->Value.bin->__ptr = s_alloc<unsigned char>(soap, sizeof(ULONG));
  3248. lpPropVal->Value.bin->__size = sizeof(ULONG);
  3249. lpPropVal->__union = SOAP_UNION_propValData_bin;
  3250. memcpy(lpPropVal->Value.bin->__ptr, &ulId, sizeof(ULONG));
  3251. break;
  3252. case PR_ACCOUNT:
  3253. lpPropVal->Value.lpszA = s_strcpy(soap, lpDetails->GetPropString(OB_PROP_S_LOGIN).c_str());
  3254. lpPropVal->__union = SOAP_UNION_propValData_lpszA;
  3255. break;
  3256. case PR_AB_PROVIDER_ID:
  3257. lpPropVal->Value.bin = s_alloc<struct xsd__base64Binary>(soap);
  3258. lpPropVal->Value.bin->__ptr = s_alloc<unsigned char>(soap, sizeof(GUID));
  3259. lpPropVal->Value.bin->__size = sizeof(GUID);
  3260. lpPropVal->__union = SOAP_UNION_propValData_bin;
  3261. memcpy(lpPropVal->Value.bin->__ptr, &MUIDECSAB, sizeof(GUID));
  3262. break;
  3263. case PR_EC_SENDAS_USER_ENTRYIDS: {
  3264. list<objectid_t> userIds = lpDetails->GetPropListObject(OB_PROP_LO_SENDAS);
  3265. if (!userIds.empty()) {
  3266. unsigned int i;
  3267. struct propVal sPropVal;
  3268. lpPropVal->__union = SOAP_UNION_propValData_mvbin;
  3269. lpPropVal->Value.mvbin.__size = 0;
  3270. lpPropVal->Value.mvbin.__ptr = s_alloc<struct xsd__base64Binary>(soap, userIds.size());
  3271. i = 0;
  3272. for (const auto &uid : userIds) {
  3273. er = CreateABEntryID(soap, uid, &sPropVal);
  3274. if (er != erSuccess)
  3275. continue;
  3276. lpPropVal->Value.mvbin.__ptr[i].__ptr = sPropVal.Value.bin->__ptr;
  3277. lpPropVal->Value.mvbin.__ptr[i++].__size = sPropVal.Value.bin->__size;
  3278. }
  3279. lpPropVal->Value.mvbin.__size = i;
  3280. } else {
  3281. lpPropVal->ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(lpPropTags->__ptr[i]));
  3282. lpPropVal->Value.ul = KCERR_NOT_FOUND;
  3283. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3284. }
  3285. break;
  3286. }
  3287. case PR_EMS_AB_PROXY_ADDRESSES: {
  3288. // Use upper-case 'SMTP' for primary
  3289. std::string strPrefix("SMTP:");
  3290. std::string address(lpDetails->GetPropString(OB_PROP_S_EMAIL));
  3291. std::list<std::string> lstAliases = lpDetails->GetPropListString(OB_PROP_LS_ALIASES);
  3292. ULONG nAliases = lstAliases.size();
  3293. ULONG i = 0;
  3294. lpPropVal->__union = SOAP_UNION_propValData_mvszA;
  3295. lpPropVal->Value.mvszA.__ptr = s_alloc<char *>(soap, 1 + nAliases);
  3296. if (!address.empty()) {
  3297. address = strPrefix + address;
  3298. lpPropVal->Value.mvszA.__ptr[i++] = s_strcpy(soap, address.c_str());
  3299. }
  3300. // Use lower-case 'smtp' prefix for aliases
  3301. strPrefix = "smtp:";
  3302. for (const auto &alias : lstAliases)
  3303. lpPropVal->Value.mvszA.__ptr[i++] = s_strcpy(soap, (strPrefix + alias).c_str());
  3304. lpPropVal->Value.mvszA.__size = i;
  3305. break;
  3306. }
  3307. case PR_EC_HOMESERVER_NAME: {
  3308. std::string serverName = lpDetails->GetPropString(OB_PROP_S_SERVERNAME);
  3309. if (!serverName.empty()) {
  3310. lpPropVal->Value.lpszA = s_strcpy(soap, serverName.c_str());
  3311. lpPropVal->__union = SOAP_UNION_propValData_lpszA;
  3312. } else {
  3313. lpPropVal->ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(lpPropTags->__ptr[i]));
  3314. lpPropVal->Value.ul = KCERR_NOT_FOUND;
  3315. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3316. }
  3317. break;
  3318. }
  3319. default:
  3320. if (ConvertAnonymousObjectDetailToProp(soap, lpDetails, lpPropTags->__ptr[i], lpPropVal) != erSuccess) {
  3321. lpPropVal->ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(lpPropTags->__ptr[i]));
  3322. lpPropVal->Value.ul = KCERR_NOT_FOUND;
  3323. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3324. }
  3325. break;
  3326. }
  3327. break;
  3328. } // end case DISTLIST_GROUP
  3329. } // end switch(objclass)
  3330. }
  3331. // copies __size and __ptr values into return struct
  3332. *lpPropValsRet = *lpPropVals;
  3333. exit:
  3334. if (er != erSuccess && soap == NULL)
  3335. s_free(nullptr, lpPropVals->__ptr);
  3336. return er;
  3337. }
  3338. // Convert a userdetails_t to a set of MAPI properties
  3339. ECRESULT ECUserManagement::ConvertContainerObjectDetailsToProps(struct soap *soap, unsigned int ulId, objectdetails_t *lpDetails, struct propTagArray *lpPropTags, struct propValArray *lpPropVals)
  3340. {
  3341. ECRESULT er;
  3342. struct propVal *lpPropVal;
  3343. unsigned int ulOrder = 0;
  3344. ECSecurity *lpSecurity = NULL;
  3345. ULONG ulMapiType = 0;
  3346. er = GetSecurity(&lpSecurity);
  3347. if (er != erSuccess)
  3348. return er;
  3349. er = lpSecurity->IsUserObjectVisible(ulId);
  3350. if (er != erSuccess)
  3351. return er;
  3352. er = TypeToMAPIType(lpDetails->GetClass(), &ulMapiType);
  3353. if (er != erSuccess)
  3354. return er;
  3355. lpPropVals->__ptr = s_alloc<struct propVal>(soap, lpPropTags->__size);
  3356. lpPropVals->__size = lpPropTags->__size;
  3357. for (gsoap_size_t i = 0; i < lpPropTags->__size; ++i) {
  3358. lpPropVal = &lpPropVals->__ptr[i];
  3359. lpPropVal->ulPropTag = lpPropTags->__ptr[i];
  3360. switch(lpDetails->GetClass()) {
  3361. default:
  3362. er = KCERR_NOT_FOUND;
  3363. break;
  3364. case CONTAINER_ADDRESSLIST: {
  3365. switch(NormalizePropTag(lpPropTags->__ptr[i])) {
  3366. case PR_ENTRYID: {
  3367. er = CreateABEntryID(soap, ulId, ulMapiType, lpPropVal);
  3368. if (er != erSuccess)
  3369. return er;
  3370. break;
  3371. }
  3372. case PR_PARENT_ENTRYID: {
  3373. ABEID abeid;
  3374. abeid.ulType = MAPI_ABCONT;
  3375. abeid.ulId = 1;
  3376. memcpy(&abeid.guid, &MUIDECSAB, sizeof(GUID));
  3377. lpPropVal->Value.bin = s_alloc<struct xsd__base64Binary>(soap);
  3378. lpPropVal->Value.bin->__ptr = s_alloc<unsigned char>(soap, sizeof(ABEID));
  3379. lpPropVal->Value.bin->__size = sizeof(ABEID);
  3380. lpPropVal->__union = SOAP_UNION_propValData_bin;
  3381. *(ABEID *)lpPropVal->Value.bin->__ptr = abeid;
  3382. break;
  3383. }
  3384. case PR_NORMALIZED_SUBJECT:
  3385. case PR_DISPLAY_NAME:
  3386. case 0x3A20001E:// PR_TRANSMITABLE_DISPLAY_NAME
  3387. lpPropVal->Value.lpszA = s_strcpy(soap, lpDetails->GetPropString(OB_PROP_S_FULLNAME).c_str());
  3388. lpPropVal->__union = SOAP_UNION_propValData_lpszA;
  3389. break;
  3390. case PR_INSTANCE_KEY:
  3391. lpPropVal->Value.bin = s_alloc<xsd__base64Binary>(soap);
  3392. lpPropVal->Value.bin->__ptr = s_alloc<unsigned char>(soap, 2 * sizeof(ULONG));
  3393. lpPropVal->Value.bin->__size = 2*sizeof(ULONG);
  3394. lpPropVal->__union = SOAP_UNION_propValData_bin;
  3395. memcpy(lpPropVal->Value.bin->__ptr, &ulId, sizeof(ULONG));
  3396. memcpy(lpPropVal->Value.bin->__ptr+sizeof(ULONG), &ulOrder, sizeof(ULONG));
  3397. break;
  3398. case PR_OBJECT_TYPE:
  3399. lpPropVal->Value.ul = MAPI_ABCONT;
  3400. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3401. break;
  3402. case PR_CONTAINER_FLAGS:
  3403. lpPropVal->Value.ul = AB_RECIPIENTS | AB_UNMODIFIABLE | AB_UNICODE_OK;
  3404. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3405. break;
  3406. case PR_DISPLAY_TYPE:
  3407. lpPropVal->Value.ul = DT_FOLDER;
  3408. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3409. break;
  3410. case PR_RECORD_KEY:
  3411. lpPropVal->Value.bin = s_alloc<struct xsd__base64Binary>(soap);
  3412. lpPropVal->Value.bin->__ptr = s_alloc<unsigned char>(soap, sizeof(ULONG));
  3413. lpPropVal->Value.bin->__size = sizeof(ULONG);
  3414. lpPropVal->__union = SOAP_UNION_propValData_bin;
  3415. memcpy(lpPropVal->Value.bin->__ptr, &ulId, sizeof(ULONG));
  3416. break;
  3417. case PR_AB_PROVIDER_ID:
  3418. lpPropVal->Value.bin = s_alloc<struct xsd__base64Binary>(soap);
  3419. lpPropVal->Value.bin->__ptr = s_alloc<unsigned char>(soap, sizeof(GUID));
  3420. lpPropVal->Value.bin->__size = sizeof(GUID);
  3421. lpPropVal->__union = SOAP_UNION_propValData_bin;
  3422. memcpy(lpPropVal->Value.bin->__ptr, &MUIDECSAB, sizeof(GUID));
  3423. break;
  3424. default:
  3425. lpPropVal->ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(lpPropTags->__ptr[i]));
  3426. lpPropVal->Value.ul = KCERR_NOT_FOUND;
  3427. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3428. break;
  3429. }
  3430. break;
  3431. } // end case CONTAINER_ADDRESSLIST
  3432. case CONTAINER_COMPANY: {
  3433. switch (NormalizePropTag(lpPropTags->__ptr[i])) {
  3434. case PR_CONTAINER_CLASS:
  3435. lpPropVal->Value.lpszA = s_strcpy(soap, "IPM.Contact");
  3436. lpPropVal->__union = SOAP_UNION_propValData_lpszA;
  3437. break;
  3438. case PR_ENTRYID: {
  3439. er = CreateABEntryID(soap, ulId, ulMapiType, lpPropVal);
  3440. if (er != erSuccess)
  3441. return er;
  3442. break;
  3443. }
  3444. case PR_EMS_AB_PARENT_ENTRYID:
  3445. case PR_PARENT_ENTRYID: {
  3446. ABEID abeid;
  3447. abeid.ulType = MAPI_ABCONT;
  3448. abeid.ulId = 1;
  3449. memcpy(&abeid.guid, &MUIDECSAB, sizeof(GUID));
  3450. lpPropVal->Value.bin = s_alloc<struct xsd__base64Binary>(soap);
  3451. lpPropVal->Value.bin->__ptr = s_alloc<unsigned char>(soap, sizeof(ABEID));
  3452. lpPropVal->Value.bin->__size = sizeof(ABEID);
  3453. lpPropVal->__union = SOAP_UNION_propValData_bin;
  3454. *(ABEID *)lpPropVal->Value.bin->__ptr = abeid;
  3455. break;
  3456. }
  3457. case PR_ACCOUNT:
  3458. case PR_NORMALIZED_SUBJECT:
  3459. case PR_DISPLAY_NAME:
  3460. case 0x3A20001E:// PR_TRANSMITABLE_DISPLAY_NAME
  3461. lpPropVal->Value.lpszA = s_strcpy(soap, lpDetails->GetPropString(OB_PROP_S_FULLNAME).c_str());
  3462. lpPropVal->__union = SOAP_UNION_propValData_lpszA;
  3463. break;
  3464. case PR_EC_COMPANY_NAME:
  3465. lpPropVal->Value.lpszA = s_strcpy(soap, lpDetails->GetPropString(OB_PROP_S_FULLNAME).c_str());
  3466. lpPropVal->__union = SOAP_UNION_propValData_lpszA;
  3467. break;
  3468. case PR_INSTANCE_KEY:
  3469. lpPropVal->Value.bin = s_alloc<xsd__base64Binary>(soap);
  3470. lpPropVal->Value.bin->__ptr = s_alloc<unsigned char>(soap, 2 * sizeof(ULONG));
  3471. lpPropVal->Value.bin->__size = 2 * sizeof(ULONG);
  3472. lpPropVal->__union = SOAP_UNION_propValData_bin;
  3473. memcpy(lpPropVal->Value.bin->__ptr, &ulId, sizeof(ULONG));
  3474. memcpy(lpPropVal->Value.bin->__ptr + sizeof(ULONG), &ulOrder, sizeof(ULONG));
  3475. break;
  3476. case PR_OBJECT_TYPE:
  3477. lpPropVal->Value.ul = MAPI_ABCONT;
  3478. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3479. break;
  3480. case PR_DISPLAY_TYPE:
  3481. lpPropVal->Value.ul = DT_ORGANIZATION;
  3482. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3483. break;
  3484. case PR_RECORD_KEY:
  3485. lpPropVal->Value.bin = s_alloc<struct xsd__base64Binary>(soap);
  3486. lpPropVal->Value.bin->__ptr = s_alloc<unsigned char>(soap, sizeof(ULONG));
  3487. lpPropVal->Value.bin->__size = sizeof(ULONG);
  3488. lpPropVal->__union = SOAP_UNION_propValData_bin;
  3489. memcpy(lpPropVal->Value.bin->__ptr, &ulId, sizeof(ULONG));
  3490. break;
  3491. case PR_CONTAINER_FLAGS:
  3492. lpPropVal->Value.ul = AB_RECIPIENTS | AB_UNMODIFIABLE | AB_UNICODE_OK;
  3493. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3494. break;
  3495. case PR_AB_PROVIDER_ID:
  3496. lpPropVal->Value.bin = s_alloc<struct xsd__base64Binary>(soap);
  3497. lpPropVal->Value.bin->__ptr = s_alloc<unsigned char>(soap, sizeof(GUID));
  3498. lpPropVal->Value.bin->__size = sizeof(GUID);
  3499. lpPropVal->__union = SOAP_UNION_propValData_bin;
  3500. memcpy(lpPropVal->Value.bin->__ptr, &MUIDECSAB, sizeof(GUID));
  3501. break;
  3502. case PR_EMS_AB_IS_MASTER:
  3503. lpPropVal->Value.b = false;
  3504. lpPropVal->__union = SOAP_UNION_propValData_b;
  3505. break;
  3506. case PR_EMS_AB_CONTAINERID:
  3507. lpPropVal->Value.ul = ulId;
  3508. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3509. break;
  3510. case PR_EC_HOMESERVER_NAME: {
  3511. std::string serverName = lpDetails->GetPropString(OB_PROP_S_SERVERNAME);
  3512. if (!serverName.empty()) {
  3513. lpPropVal->Value.lpszA = s_strcpy(soap, serverName.c_str());
  3514. lpPropVal->__union = SOAP_UNION_propValData_lpszA;
  3515. } else {
  3516. lpPropVal->ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(lpPropTags->__ptr[i]));
  3517. lpPropVal->Value.ul = KCERR_NOT_FOUND;
  3518. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3519. }
  3520. break;
  3521. }
  3522. default:
  3523. if (ConvertAnonymousObjectDetailToProp(soap, lpDetails, lpPropTags->__ptr[i], lpPropVal) != erSuccess) {
  3524. lpPropVal->ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(lpPropTags->__ptr[i]));
  3525. lpPropVal->Value.ul = KCERR_NOT_FOUND;
  3526. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3527. }
  3528. break;
  3529. }
  3530. } // end CONTAINER_COMPANY
  3531. } // end switch(objclass)
  3532. }
  3533. return er;
  3534. }
  3535. ECRESULT ECUserManagement::ConvertABContainerToProps(struct soap *soap, unsigned int ulId, struct propTagArray *lpPropTagArray, struct propValArray *lpPropValArray)
  3536. {
  3537. struct propVal *lpPropVal;
  3538. std::string strName;
  3539. ABEID abeid;
  3540. char MUIDEMSAB[] = "\xDC\xA7\x40\xC8\xC0\x42\x10\x1A\xB4\xB9\x08\x00\x2B\x2F\xE1\x82";
  3541. lpPropValArray->__ptr = s_alloc<struct propVal>(soap, lpPropTagArray->__size);
  3542. lpPropValArray->__size = lpPropTagArray->__size;
  3543. abeid.ulType = MAPI_ABCONT;
  3544. memcpy(&abeid.guid, &MUIDECSAB, sizeof(GUID));
  3545. abeid.ulId = ulId;
  3546. // FIXME: Should this name be hardcoded like this?
  3547. // Are there any other values that might be passed as name?
  3548. if (ulId == KOPANO_UID_ADDRESS_BOOK)
  3549. strName = KOPANO_FULLNAME_ADDRESS_BOOK;
  3550. else if (ulId == KOPANO_UID_GLOBAL_ADDRESS_BOOK)
  3551. strName = KOPANO_FULLNAME_GLOBAL_ADDRESS_BOOK;
  3552. else if (ulId == KOPANO_UID_GLOBAL_ADDRESS_LISTS)
  3553. strName = KOPANO_FULLNAME_GLOBAL_ADDRESS_LISTS;
  3554. else
  3555. return KCERR_INVALID_PARAMETER;
  3556. for (gsoap_size_t i = 0; i < lpPropTagArray->__size; ++i) {
  3557. lpPropVal = &lpPropValArray->__ptr[i];
  3558. lpPropVal->ulPropTag = lpPropTagArray->__ptr[i];
  3559. switch (NormalizePropTag(lpPropTagArray->__ptr[i])) {
  3560. case PR_SEARCH_KEY:
  3561. lpPropVal->Value.bin = s_alloc<struct xsd__base64Binary>(soap);
  3562. lpPropVal->Value.bin->__ptr = s_alloc<unsigned char>(soap, sizeof(ABEID));
  3563. lpPropVal->Value.bin->__size = sizeof(ABEID);
  3564. lpPropVal->__union = SOAP_UNION_propValData_bin;
  3565. memcpy(lpPropVal->Value.bin->__ptr, &abeid, sizeof(ABEID));
  3566. break;
  3567. case PR_CONTAINER_CLASS:
  3568. lpPropVal->Value.lpszA = s_strcpy(soap, "IPM.Contact");
  3569. lpPropVal->__union = SOAP_UNION_propValData_lpszA;
  3570. break;
  3571. case PR_ENTRYID:
  3572. lpPropVal->Value.bin = s_alloc<struct xsd__base64Binary>(soap);
  3573. lpPropVal->Value.bin->__ptr = s_alloc<unsigned char>(soap, sizeof(ABEID));
  3574. lpPropVal->Value.bin->__size = sizeof(ABEID);
  3575. lpPropVal->__union = SOAP_UNION_propValData_bin;
  3576. memcpy(lpPropVal->Value.bin->__ptr, &abeid, sizeof(ABEID));
  3577. break;
  3578. case PR_ACCOUNT:
  3579. case PR_NORMALIZED_SUBJECT:
  3580. case PR_DISPLAY_NAME:
  3581. case 0x3A20001E:// PR_TRANSMITABLE_DISPLAY_NAME
  3582. lpPropVal->Value.lpszA = s_strcpy(soap, strName.c_str());
  3583. lpPropVal->__union = SOAP_UNION_propValData_lpszA;
  3584. break;
  3585. case PR_INSTANCE_KEY:
  3586. lpPropVal->Value.bin = s_alloc<xsd__base64Binary>(soap);
  3587. lpPropVal->Value.bin->__ptr = s_alloc<unsigned char>(soap, 2 * sizeof(ULONG));
  3588. lpPropVal->Value.bin->__size = 2 * sizeof(ULONG);
  3589. lpPropVal->__union = SOAP_UNION_propValData_bin;
  3590. memcpy(lpPropVal->Value.bin->__ptr, &ulId, sizeof(ULONG));
  3591. memcpy(lpPropVal->Value.bin->__ptr + sizeof(ULONG), &ulId, sizeof(ULONG));
  3592. break;
  3593. case PR_OBJECT_TYPE:
  3594. lpPropVal->Value.ul = MAPI_ABCONT;
  3595. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3596. break;
  3597. case PR_DISPLAY_TYPE:
  3598. /*
  3599. * Outlook 2000 requires DT_GLOBAL
  3600. * Outlook 2003 requires DT_GLOBAL
  3601. * Outlook 2007 requires something which is not DT_GLOBAL,
  3602. * allowed values is DT_CONTAINER (0x00000100), DT_LOCAL or
  3603. * DT_NOT_SPECIFIC
  3604. *
  3605. * Only Outlook 2007 really complains when it gets a value
  3606. * which is different then what it epxects.
  3607. */
  3608. if (ulId == KOPANO_UID_ADDRESS_BOOK)
  3609. lpPropVal->Value.ul = DT_GLOBAL;
  3610. else
  3611. lpPropVal->Value.ul = DT_NOT_SPECIFIC;
  3612. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3613. break;
  3614. case PR_RECORD_KEY:
  3615. lpPropVal->Value.bin = s_alloc<struct xsd__base64Binary>(soap);
  3616. lpPropVal->Value.bin->__ptr = s_alloc<unsigned char>(soap, sizeof(ABEID));
  3617. lpPropVal->Value.bin->__size = sizeof(ABEID);
  3618. lpPropVal->__union = SOAP_UNION_propValData_bin;
  3619. memcpy(lpPropVal->Value.bin->__ptr, &abeid, sizeof(ABEID));
  3620. break;
  3621. case PR_CONTAINER_FLAGS:
  3622. /*
  3623. * We support subcontainers, but don't even consider setting the
  3624. * flag AB_SUBCONTAINERS for the Global Address Book since that
  3625. * will cause Outlook 2007 to bug() which then doesn't place
  3626. * the Global Address Book in the SearchPath.
  3627. */
  3628. lpPropVal->Value.ul = AB_RECIPIENTS | AB_UNMODIFIABLE | AB_UNICODE_OK;
  3629. if (ulId == KOPANO_UID_ADDRESS_BOOK)
  3630. lpPropVal->Value.ul |= AB_SUBCONTAINERS;
  3631. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3632. break;
  3633. case PR_AB_PROVIDER_ID: {
  3634. std::string strApp;
  3635. ECSession *lpSession = NULL;
  3636. lpPropVal->Value.bin = s_alloc<struct xsd__base64Binary>(soap);
  3637. lpPropVal->Value.bin->__ptr = s_alloc<unsigned char>(soap, sizeof(GUID));
  3638. lpPropVal->Value.bin->__size = sizeof(GUID);
  3639. lpPropVal->__union = SOAP_UNION_propValData_bin;
  3640. lpSession = dynamic_cast<ECSession *>(m_lpSession);
  3641. if(lpSession)
  3642. lpSession->GetClientApp(&strApp);
  3643. if (strncasecmp(strApp.c_str(), "blackberry", 10) == 0)
  3644. // For blackberry, we pose as being the Exchange AddressList. We have to do this
  3645. // since it searches for the GAB by restricting by this GUID, otherwise the Lookup
  3646. // function will not function properly.
  3647. // Multiple blackberry binaries need to be able to access, including BlackBerryAgent.exe
  3648. // and BlackBerryMailStore.exe
  3649. memcpy(lpPropVal->Value.bin->__ptr, MUIDEMSAB, sizeof(GUID));
  3650. else
  3651. memcpy(lpPropVal->Value.bin->__ptr, &MUIDECSAB, sizeof(GUID));
  3652. break;
  3653. }
  3654. case PR_EMS_AB_IS_MASTER:
  3655. lpPropVal->Value.b = (ulId == KOPANO_UID_ADDRESS_BOOK);
  3656. lpPropVal->__union = SOAP_UNION_propValData_b;
  3657. break;
  3658. case PR_EMS_AB_CONTAINERID:
  3659. // 'Global Address Book' should be container ID 0, rest follows
  3660. if(ulId != KOPANO_UID_ADDRESS_BOOK) {
  3661. // 'All Address Lists' has ID 7000 in MSEX
  3662. lpPropVal->Value.ul = ulId == KOPANO_UID_GLOBAL_ADDRESS_LISTS ? 7000 : KOPANO_UID_ADDRESS_BOOK;
  3663. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3664. } else {
  3665. // id 0 (kopano address book) has no container id
  3666. lpPropVal->ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(lpPropTagArray->__ptr[i]));
  3667. lpPropVal->Value.ul = KCERR_NOT_FOUND;
  3668. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3669. }
  3670. break;
  3671. case PR_EMS_AB_PARENT_ENTRYID:
  3672. case PR_PARENT_ENTRYID:
  3673. if (ulId != KOPANO_UID_ADDRESS_BOOK) {
  3674. ABEID abeid;
  3675. abeid.ulType = MAPI_ABCONT;
  3676. abeid.ulId = KOPANO_UID_ADDRESS_BOOK;
  3677. memcpy(&abeid.guid, &MUIDECSAB, sizeof(GUID));
  3678. lpPropVal->Value.bin = s_alloc<struct xsd__base64Binary>(soap);
  3679. lpPropVal->Value.bin->__ptr = s_alloc<unsigned char>(soap, sizeof(ABEID));
  3680. lpPropVal->Value.bin->__size = sizeof(ABEID);
  3681. lpPropVal->__union = SOAP_UNION_propValData_bin;
  3682. *(ABEID *)lpPropVal->Value.bin->__ptr = abeid;
  3683. } else { /* Kopano Address Book */
  3684. lpPropVal->ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(lpPropTagArray->__ptr[i]));
  3685. lpPropVal->Value.ul = KCERR_NOT_FOUND;
  3686. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3687. }
  3688. break;
  3689. default:
  3690. lpPropVal->ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(lpPropTagArray->__ptr[i]));
  3691. lpPropVal->Value.ul = KCERR_NOT_FOUND;
  3692. lpPropVal->__union = SOAP_UNION_propValData_ul;
  3693. break;
  3694. }
  3695. }
  3696. return erSuccess;
  3697. }
  3698. ECRESULT ECUserManagement::GetUserCount(unsigned int *lpulActive, unsigned int *lpulNonActive)
  3699. {
  3700. ECRESULT er;
  3701. usercount_t userCount;
  3702. er = GetUserCount(&userCount);
  3703. if (er != erSuccess)
  3704. return er;
  3705. if (lpulActive)
  3706. *lpulActive = userCount[usercount_t::ucActiveUser];
  3707. if (lpulNonActive)
  3708. *lpulNonActive = userCount[usercount_t::ucNonActiveTotal];
  3709. return erSuccess;
  3710. }
  3711. ECRESULT ECUserManagement::GetUserCount(usercount_t *lpUserCount)
  3712. {
  3713. ECRESULT er = erSuccess;
  3714. ECDatabase *lpDatabase = NULL;
  3715. DB_RESULT lpResult;
  3716. DB_ROW lpRow = NULL;
  3717. std::string strQuery;
  3718. unsigned int ulActive = 0;
  3719. unsigned int ulNonActiveUser = 0;
  3720. unsigned int ulRoom = 0;
  3721. unsigned int ulEquipment = 0;
  3722. unsigned int ulContact = 0;
  3723. er = m_lpSession->GetDatabase(&lpDatabase);
  3724. if (er != erSuccess)
  3725. return er;
  3726. strQuery =
  3727. "SELECT COUNT(*), objectclass "
  3728. "FROM users "
  3729. "WHERE externid IS NOT NULL " // Keep local entries outside of COUNT()
  3730. "AND " + OBJECTCLASS_COMPARE_SQL("objectclass", OBJECTCLASS_USER) + " "
  3731. "GROUP BY objectclass";
  3732. er = lpDatabase->DoSelect(strQuery, &lpResult);
  3733. if (er != erSuccess)
  3734. return er;
  3735. while((lpRow = lpDatabase->FetchRow(lpResult)) != NULL) {
  3736. if(lpRow[0] == NULL || lpRow[1] == NULL)
  3737. continue;
  3738. switch (atoi(lpRow[1])) {
  3739. case ACTIVE_USER:
  3740. ulActive = atoi(lpRow[0]);
  3741. break;
  3742. case NONACTIVE_USER:
  3743. ulNonActiveUser = atoi(lpRow[0]);
  3744. break;
  3745. case NONACTIVE_ROOM:
  3746. ulRoom = atoi(lpRow[0]);
  3747. break;
  3748. case NONACTIVE_EQUIPMENT:
  3749. ulEquipment = atoi(lpRow[0]);
  3750. break;
  3751. case NONACTIVE_CONTACT:
  3752. ulContact= atoi(lpRow[0]);
  3753. break;
  3754. }
  3755. }
  3756. if (lpUserCount)
  3757. lpUserCount->assign(ulActive, ulNonActiveUser, ulRoom, ulEquipment, ulContact);
  3758. {
  3759. std::lock_guard<std::recursive_mutex> lock(m_hMutex);
  3760. m_userCount.assign(ulActive, ulNonActiveUser, ulRoom, ulEquipment, ulContact);
  3761. m_usercount_ts = time(NULL);
  3762. }
  3763. return erSuccess;
  3764. }
  3765. ECRESULT ECUserManagement::GetCachedUserCount(usercount_t *lpUserCount)
  3766. {
  3767. std::lock_guard<std::recursive_mutex> lock(m_hMutex);
  3768. if (!m_userCount.isValid() || m_usercount_ts - time(NULL) > 5*60)
  3769. return GetUserCount(lpUserCount);
  3770. if (lpUserCount)
  3771. lpUserCount->assign(m_userCount);
  3772. return erSuccess;
  3773. }
  3774. ECRESULT ECUserManagement::GetPublicStoreDetails(objectdetails_t *lpDetails)
  3775. {
  3776. ECRESULT er;
  3777. std::unique_ptr<objectdetails_t> details;
  3778. UserPlugin *lpPlugin = NULL;
  3779. /* We pretend that the Public store is a company. So request (and later store) it as such. */
  3780. // type passed was , CONTAINER_COMPANY .. still working?
  3781. er = m_lpSession->GetSessionManager()->GetCacheManager()->GetUserDetails(KOPANO_UID_EVERYONE, lpDetails);
  3782. if (er == erSuccess)
  3783. return erSuccess; /* Cache contained requested information, we're done.*/
  3784. er = GetThreadLocalPlugin(m_lpPluginFactory, &lpPlugin);
  3785. if(er != erSuccess)
  3786. return er;
  3787. try {
  3788. details = lpPlugin->getPublicStoreDetails();
  3789. } catch (objectnotfound &) {
  3790. return KCERR_NOT_FOUND;
  3791. } catch (notsupported &) {
  3792. return KCERR_NO_SUPPORT;
  3793. } catch (std::exception &e) {
  3794. ec_log_warn("Unable to get %s details for object id %d: %s", ObjectClassToName(CONTAINER_COMPANY), KOPANO_UID_EVERYONE, e.what());
  3795. return KCERR_NOT_FOUND;
  3796. }
  3797. /* Update cache so we don't have to bug the plugin until the data has changed.
  3798. * Note that we don't care if the update succeeded, if it fails we will retry
  3799. * when the user details are requested for a second time. */
  3800. m_lpSession->GetSessionManager()->GetCacheManager()->SetUserDetails(KOPANO_UID_EVERYONE, details.get());
  3801. *lpDetails = *details;
  3802. return erSuccess;
  3803. }
  3804. ECRESULT ECUserManagement::GetServerDetails(const std::string &strServer, serverdetails_t *lpDetails)
  3805. {
  3806. ECRESULT er;
  3807. std::unique_ptr<serverdetails_t> details;
  3808. UserPlugin *lpPlugin = NULL;
  3809. // Try the cache first
  3810. er = m_lpSession->GetSessionManager()->GetCacheManager()->GetServerDetails(strServer, lpDetails);
  3811. if (er == erSuccess)
  3812. return erSuccess;
  3813. er = GetThreadLocalPlugin(m_lpPluginFactory, &lpPlugin);
  3814. if(er != erSuccess)
  3815. return er;
  3816. try {
  3817. details = lpPlugin->getServerDetails(strServer);
  3818. m_lpSession->GetSessionManager()->GetCacheManager()->SetServerDetails(strServer, *details);
  3819. } catch (objectnotfound &) {
  3820. return KCERR_NOT_FOUND;
  3821. } catch (notsupported &) {
  3822. return KCERR_NO_SUPPORT;
  3823. } catch (std::exception &e) {
  3824. ec_log_warn("Unable to get server details for \"%s\": %s", strServer.c_str(), e.what());
  3825. return KCERR_NOT_FOUND;
  3826. }
  3827. *lpDetails = *details;
  3828. return erSuccess;
  3829. }
  3830. /**
  3831. * Get list of servers from LDAP
  3832. *
  3833. * Note that the list of servers may include servers that are completely idle since
  3834. * they are not listed as a home server for a user or as a public store server.
  3835. *
  3836. * The server names returned are suitable for passing to GetServerDetails()
  3837. *
  3838. * @param[out] lpServerList List of servers
  3839. * @return result
  3840. */
  3841. ECRESULT ECUserManagement::GetServerList(serverlist_t *lpServerList)
  3842. {
  3843. ECRESULT er;
  3844. std::unique_ptr<serverlist_t> list;
  3845. UserPlugin *lpPlugin = NULL;
  3846. er = GetThreadLocalPlugin(m_lpPluginFactory, &lpPlugin);
  3847. if(er != erSuccess)
  3848. return er;
  3849. try {
  3850. list = lpPlugin->getServers();
  3851. } catch (std::exception &e) {
  3852. ec_log_warn("Unable to get server list: %s", e.what());
  3853. return KCERR_NOT_FOUND;
  3854. }
  3855. *lpServerList = *list;
  3856. return erSuccess;
  3857. }
  3858. ECRESULT ECUserManagement::CheckObjectModified(unsigned int ulObjectId, const string &localsignature, const string &remotesignature)
  3859. {
  3860. ECRESULT er = erSuccess;
  3861. int localChange = 0, remoteChange = 0;
  3862. char *end = NULL;
  3863. remoteChange = strtoul(remotesignature.c_str(), &end, 10);
  3864. if (end && *end == '\0') {
  3865. // ADS uses uSNChanged incrementing attribute
  3866. // whenChanged and modifyTimestamp attributes are not replicated, giving too many changes
  3867. localChange = strtoul(localsignature.c_str(), &end, 10);
  3868. if (!end || *end != '\0' || localChange < remoteChange)
  3869. ProcessModification(ulObjectId, remotesignature);
  3870. } else if (localsignature != remotesignature) {
  3871. ProcessModification(ulObjectId, remotesignature);
  3872. }
  3873. return er;
  3874. }
  3875. ECRESULT ECUserManagement::ProcessModification(unsigned int ulId, const std::string &newsignature)
  3876. {
  3877. ECRESULT er;
  3878. ECDatabase *lpDatabase = NULL;
  3879. ABEID eid(MAPI_ABCONT, MUIDECSAB, 1);
  3880. SOURCEKEY sSourceKey;
  3881. // Log the change to ICS
  3882. er = GetABSourceKeyV1(ulId, &sSourceKey);
  3883. if (er != erSuccess)
  3884. return er;
  3885. AddABChange(m_lpSession, ICS_AB_CHANGE, sSourceKey, SOURCEKEY(CbABEID(&eid), (char *)&eid));
  3886. // Ignore ICS error
  3887. // Save the new signature
  3888. er = m_lpSession->GetDatabase(&lpDatabase);
  3889. if(er != erSuccess)
  3890. return er;
  3891. er = lpDatabase->DoUpdate("UPDATE users SET signature=" + lpDatabase->EscapeBinary((unsigned char *)newsignature.c_str(), newsignature.size()) + " WHERE id=" + stringify(ulId));
  3892. if(er != erSuccess)
  3893. return er;
  3894. er = m_lpSession->GetSessionManager()->GetCacheManager()->UpdateUser(ulId);
  3895. if (er != erSuccess)
  3896. return er;
  3897. return erSuccess;
  3898. }
  3899. ECRESULT ECUserManagement::GetABSourceKeyV1(unsigned int ulUserId, SOURCEKEY *lpsSourceKey)
  3900. {
  3901. objectid_t sExternId;
  3902. ULONG ulType = 0;
  3903. ECRESULT er = GetExternalId(ulUserId, &sExternId);
  3904. if (er != erSuccess)
  3905. return er;
  3906. auto strEncExId = base64_encode(reinterpret_cast<const unsigned char *>(sExternId.id.c_str()), sExternId.id.size());
  3907. er = TypeToMAPIType(sExternId.objclass, &ulType);
  3908. if (er != erSuccess)
  3909. return er;
  3910. unsigned int ulLen = CbNewABEID(strEncExId.c_str());
  3911. std::unique_ptr<char[]> abchar(new char[ulLen]);
  3912. memset(abchar.get(), 0, ulLen);
  3913. auto lpAbeid = reinterpret_cast<ABEID *>(abchar.get());
  3914. lpAbeid->ulId = ulUserId;
  3915. lpAbeid->ulType = ulType;
  3916. memcpy(&lpAbeid->guid, &MUIDECSAB, sizeof(GUID));
  3917. if (!strEncExId.empty())
  3918. {
  3919. lpAbeid->ulVersion = 1;
  3920. // avoid FORTIFY_SOURCE checks in strcpy to an address that the compiler thinks is 1 size large
  3921. memcpy(lpAbeid->szExId, strEncExId.c_str(), strEncExId.length()+1);
  3922. }
  3923. *lpsSourceKey = SOURCEKEY(ulLen, abchar.get());
  3924. return erSuccess;
  3925. }
  3926. ECRESULT ECUserManagement::CreateABEntryID(struct soap *soap, const objectid_t &sExternId, struct propVal *lpPropVal)
  3927. {
  3928. ECRESULT er;
  3929. unsigned int ulObjId;
  3930. ULONG ulObjType;
  3931. er = GetLocalId(sExternId, &ulObjId);
  3932. if (er != erSuccess)
  3933. return er;
  3934. er = TypeToMAPIType(sExternId.objclass, &ulObjType);
  3935. if (er != erSuccess)
  3936. return er;
  3937. return CreateABEntryID(soap, ulObjId, ulObjType, lpPropVal);
  3938. }
  3939. ECRESULT ECUserManagement::CreateABEntryID(struct soap *soap,
  3940. unsigned int ulVersion, unsigned int ulObjId, unsigned int ulType,
  3941. objectid_t *lpExternId, gsoap_size_t *lpcbEID, ABEID **lppEid)
  3942. {
  3943. ABEID *lpEid = NULL;
  3944. gsoap_size_t ulSize = 0;
  3945. std::string strEncExId;
  3946. if (IsInternalObject(ulObjId)) {
  3947. if (ulVersion != 0)
  3948. throw std::runtime_error("Internal objects must always have v0 ABEIDs");
  3949. lpEid = reinterpret_cast<ABEID *>(s_alloc<unsigned char>(soap, sizeof(ABEID)));
  3950. memset(lpEid, 0, sizeof(ABEID));
  3951. ulSize = sizeof(ABEID);
  3952. } else {
  3953. if(ulVersion == 0) {
  3954. lpEid = reinterpret_cast<ABEID *>(s_alloc<unsigned char>(soap, sizeof(ABEID)));
  3955. memset(lpEid, 0, sizeof(ABEID));
  3956. ulSize = sizeof(ABEID);
  3957. } else if(ulVersion == 1) {
  3958. if (lpExternId == NULL)
  3959. return KCERR_INVALID_PARAMETER;
  3960. strEncExId = base64_encode((unsigned char*)lpExternId->id.c_str(), lpExternId->id.size());
  3961. ulSize = CbNewABEID(strEncExId.c_str());
  3962. lpEid = reinterpret_cast<ABEID *>(s_alloc<unsigned char>(soap, ulSize));
  3963. memset(lpEid, 0, ulSize);
  3964. // avoid FORTIFY_SOURCE checks in strcpy to an address that the compiler thinks is 1 size large
  3965. memcpy(lpEid->szExId, strEncExId.c_str(), strEncExId.length()+1);
  3966. } else {
  3967. throw std::runtime_error("Unknown user entry version " + stringify(ulVersion));
  3968. }
  3969. }
  3970. lpEid->ulVersion = ulVersion;
  3971. lpEid->ulType = ulType;
  3972. lpEid->ulId = ulObjId;
  3973. memcpy(&lpEid->guid, &MUIDECSAB, sizeof(lpEid->guid));
  3974. *lppEid = lpEid;
  3975. *lpcbEID = ulSize;
  3976. return erSuccess;
  3977. }
  3978. /**
  3979. * Create an AB entryid
  3980. *
  3981. * @param soap SOAP struct for memory allocations
  3982. * @param ulObjId Object ID to make entryid for
  3983. * @param ulType MAPI type for the object (MAPI_MAILUSER, MAPI_DISTLIST, etc)
  3984. * @param lpPropVal Output of the entryid will be stored as binary data in lpPropVal
  3985. * @return result
  3986. */
  3987. ECRESULT ECUserManagement::CreateABEntryID(struct soap *soap, unsigned int ulObjId, unsigned int ulType, struct propVal *lpPropVal)
  3988. {
  3989. ECRESULT er;
  3990. objectid_t sExternId;
  3991. unsigned int ulVersion = 1;
  3992. if (!IsInternalObject(ulObjId)) {
  3993. er = GetExternalId(ulObjId, &sExternId);
  3994. if (er != erSuccess)
  3995. return er;
  3996. ulVersion = 1;
  3997. } else {
  3998. ulVersion = 0;
  3999. }
  4000. lpPropVal->Value.bin = s_alloc<struct xsd__base64Binary>(soap);
  4001. lpPropVal->__union = SOAP_UNION_propValData_bin;
  4002. return CreateABEntryID(soap, ulVersion, ulObjId, ulType, &sExternId,
  4003. &lpPropVal->Value.bin->__size,
  4004. reinterpret_cast<ABEID **>(&lpPropVal->Value.bin->__ptr));
  4005. }
  4006. ECRESULT ECUserManagement::GetSecurity(ECSecurity **lppSecurity)
  4007. {
  4008. ECSession *lpecSession = NULL;
  4009. lpecSession = dynamic_cast<ECSession*>(m_lpSession);
  4010. if (lpecSession == NULL)
  4011. return KCERR_INVALID_PARAMETER;
  4012. if (lppSecurity)
  4013. *lppSecurity = lpecSession->GetSecurity();
  4014. return erSuccess;
  4015. }
  4016. ECRESULT ECUserManagement::SyncAllObjects()
  4017. {
  4018. ECRESULT er = erSuccess;
  4019. ECCacheManager *lpCacheManager = m_lpSession->GetSessionManager()->GetCacheManager(); // Don't delete
  4020. std::unique_ptr<std::list<localobjectdetails_t> > lplstCompanyObjects;
  4021. std::unique_ptr<std::list<localobjectdetails_t> > lplstUserObjects;
  4022. unsigned int ulFlags = USERMANAGEMENT_IDS_ONLY | USERMANAGEMENT_FORCE_SYNC;
  4023. /*
  4024. * When syncing the users we first start emptying the cache, this makes sure the
  4025. * second step won't be accidently "optimized" by caching.
  4026. * The second step is requesting all user objects from the plugin, ECUserManagement
  4027. * will then sync all results into the user database. And because the cache was
  4028. * cleared all signatures in the database will be checked against the signatures
  4029. * from the plugin.
  4030. * When this function has completed we can be sure that the cache has been repopulated
  4031. * with the userobject types, external ids and signatures of all user objects. This
  4032. * means that we have only "lost" the user details which will be repopulated later.
  4033. */
  4034. er = lpCacheManager->PurgeCache(PURGE_CACHE_USEROBJECT | PURGE_CACHE_EXTERNID | PURGE_CACHE_USERDETAILS | PURGE_CACHE_SERVER);
  4035. if (er != erSuccess)
  4036. return er;
  4037. // request all companies
  4038. er = GetCompanyObjectListAndSync(CONTAINER_COMPANY, 0, &unique_tie(lplstCompanyObjects), ulFlags);
  4039. if (er == KCERR_NO_SUPPORT) {
  4040. er = erSuccess;
  4041. } else if (er != erSuccess) {
  4042. ec_log_err("Error synchronizing company list: %08X", er);
  4043. return er;
  4044. } else {
  4045. ec_log_info("Synchronized company list");
  4046. }
  4047. if (!lplstCompanyObjects || lplstCompanyObjects->empty()) {
  4048. // get all users of server
  4049. er = GetCompanyObjectListAndSync(OBJECTCLASS_UNKNOWN, 0, &unique_tie(lplstUserObjects), ulFlags);
  4050. if (er != erSuccess) {
  4051. ec_log_err("Error synchronizing user list: %08X", er);
  4052. return er;
  4053. }
  4054. ec_log_info("Synchronized user list");
  4055. return erSuccess;
  4056. }
  4057. // per company, get all users
  4058. for (const auto &com : *lplstCompanyObjects) {
  4059. er = GetCompanyObjectListAndSync(OBJECTCLASS_UNKNOWN, com.ulId, &unique_tie(lplstUserObjects), ulFlags);
  4060. if (er != erSuccess) {
  4061. ec_log_err("Error synchronizing user list for company %d: %08X", com.ulId, er);
  4062. return er;
  4063. }
  4064. ec_log_info("Synchronized list for company %d", com.ulId);
  4065. }
  4066. return erSuccess;
  4067. }
  4068. } /* namespace */