ECABObjectTable.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. /*
  2. * Copyright 2005 - 2016 Zarafa and its licensors
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU Affero General Public License, version 3,
  6. * as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. *
  16. */
  17. #include <kopano/platform.h>
  18. #include <memory>
  19. #include <new>
  20. #include "kcore.hpp"
  21. #include <kopano/lockhelper.hpp>
  22. #include <kopano/kcodes.h>
  23. #include <kopano/tie.hpp>
  24. #include <mapidefs.h>
  25. #include <mapitags.h>
  26. #include <kopano/mapiext.h>
  27. #include <kopano/EMSAbTag.h>
  28. #include "SOAPUtils.h"
  29. #include "ECABObjectTable.h"
  30. #include "ECSecurity.h"
  31. #include "ECSession.h"
  32. #include "ECSessionManager.h"
  33. #include <kopano/stringutil.h>
  34. using namespace KCHL;
  35. namespace KC {
  36. ECABObjectTable::ECABObjectTable(ECSession *lpSession, unsigned int ulABId, unsigned int ulABType, unsigned int ulABParentId, unsigned int ulABParentType, unsigned int ulFlags, const ECLocale &locale) : ECGenericObjectTable(lpSession, ulABType, ulFlags, locale)
  37. {
  38. auto lpODAB = new ECODAB;
  39. lpODAB->ulABId = ulABId;
  40. lpODAB->ulABType = ulABType;
  41. lpODAB->ulABParentId = ulABParentId;
  42. lpODAB->ulABParentType = ulABParentType;
  43. // Set dataobject
  44. m_lpObjectData = lpODAB;
  45. // Set callback function for queryrowdata
  46. m_lpfnQueryRowData = QueryRowData;
  47. // Filter out all objects which are hidden from addressbook. We don't need details either.
  48. m_ulUserManagementFlags = USERMANAGEMENT_IDS_ONLY | USERMANAGEMENT_ADDRESSBOOK;
  49. // Set the default sort order for Address Book entries
  50. struct sortOrder sObjectType[] = {
  51. {
  52. /*
  53. * The Global Address Book must be the first entry
  54. * in getHierarchyTable() otherwise it will not
  55. * be set as default directory!
  56. *
  57. * To support future hierarchy additions like
  58. * company subcontainers, or other global containers
  59. * besides the GAB, we should sort the table using the
  60. * PR_EMS_AB_HIERARCHY_PATH property which is formatted
  61. * as followed for the GAB and Address Lists :
  62. *
  63. * /Global Address Book
  64. * /Global Address Book/Company A
  65. * /Global Address Book/Company B
  66. * /Global Address Lists/Addresslist A
  67. * /Global Address Lists/Addresslist B
  68. */
  69. PR_EMS_AB_HIERARCHY_PATH,
  70. EC_TABLE_SORT_ASCEND,
  71. },
  72. };
  73. struct sortOrderArray sDefaultSortOrder = {
  74. sObjectType,
  75. ARRAY_SIZE(sObjectType),
  76. };
  77. SetSortOrder(&sDefaultSortOrder, 0, 0);
  78. }
  79. ECABObjectTable::~ECABObjectTable()
  80. {
  81. delete (ECODAB *)m_lpObjectData;
  82. }
  83. ECRESULT ECABObjectTable::Create(ECSession *lpSession, unsigned int ulABId, unsigned int ulABType, unsigned int ulABParentId, unsigned int ulABParentType, unsigned int ulFlags, const ECLocale &locale, ECABObjectTable **lppTable)
  84. {
  85. *lppTable = new(std::nothrow) ECABObjectTable(lpSession, ulABId,
  86. ulABType, ulABParentId, ulABParentType, ulFlags, locale);
  87. if (*lppTable == nullptr)
  88. return KCERR_NOT_ENOUGH_MEMORY;
  89. (*lppTable)->AddRef();
  90. return erSuccess;
  91. }
  92. ECRESULT ECABObjectTable::GetColumnsAll(ECListInt* lplstProps)
  93. {
  94. ECRESULT er = erSuccess;
  95. auto lpODAB = static_cast<ECODAB *>(m_lpObjectData);
  96. scoped_rlock lock(m_hLock);
  97. assert(lplstProps != NULL);
  98. //List always empty
  99. lplstProps->clear();
  100. // Add some generated and standard properties
  101. lplstProps->push_back(PR_DISPLAY_NAME);
  102. lplstProps->push_back(PR_ENTRYID);
  103. lplstProps->push_back(PR_DISPLAY_TYPE);
  104. lplstProps->push_back(PR_DISPLAY_TYPE_EX);
  105. lplstProps->push_back(PR_INSTANCE_KEY);
  106. lplstProps->push_back(PR_RECORD_KEY);
  107. lplstProps->push_back(PR_OBJECT_TYPE);
  108. lplstProps->push_back(PR_AB_PROVIDER_ID);
  109. lplstProps->push_back(PR_LAST_MODIFICATION_TIME);
  110. lplstProps->push_back(PR_CREATION_TIME);
  111. if(lpODAB->ulABType == MAPI_ABCONT) {
  112. // Hierarchy table
  113. lplstProps->push_back(PR_CONTAINER_FLAGS);
  114. lplstProps->push_back(PR_DEPTH);
  115. lplstProps->push_back(PR_EMS_AB_CONTAINERID);
  116. } else {
  117. // Contents table
  118. lplstProps->push_back(PR_SMTP_ADDRESS);
  119. }
  120. return er;
  121. }
  122. ECRESULT ECABObjectTable::ReloadTableMVData(ECObjectTableList* lplistRows, ECListInt* lplistMVPropTag)
  123. {
  124. assert(lplistMVPropTag->size() < 2); //FIXME: Limit of one 1 MV column
  125. // scan for MV-props and add rows
  126. // Add items to list
  127. return erSuccess;
  128. }
  129. ECRESULT ECABObjectTable::GetMVRowCount(unsigned int ulObjId, unsigned int *lpulCount)
  130. {
  131. scoped_rlock lock(m_hLock);
  132. *lpulCount = 0;
  133. return erSuccess;
  134. }
  135. ECRESULT ECABObjectTable::QueryRowData(ECGenericObjectTable *lpThis, struct soap *soap, ECSession *lpSession, ECObjectTableList* lpRowList, struct propTagArray *lpsPropTagArray, void* lpObjectData, struct rowSet **lppRowSet, bool bTableData, bool bTableLimit)
  136. {
  137. ECRESULT er = erSuccess;
  138. auto lpODAB = static_cast<ECODAB *>(lpObjectData);
  139. struct rowSet *lpsRowSet = NULL;
  140. assert(lpRowList != NULL);
  141. if (lpODAB->ulABType == MAPI_ABCONT)
  142. er = lpSession->GetUserManagement()->QueryHierarchyRowData(soap, lpRowList, lpsPropTagArray, &lpsRowSet);
  143. else if (lpODAB->ulABType == MAPI_MAILUSER || lpODAB->ulABType == MAPI_DISTLIST)
  144. er = lpSession->GetUserManagement()->QueryContentsRowData(soap, lpRowList, lpsPropTagArray, &lpsRowSet);
  145. else
  146. er = KCERR_INVALID_TYPE;
  147. if(er != erSuccess)
  148. return er;
  149. *lppRowSet = lpsRowSet;
  150. return erSuccess;
  151. }
  152. struct filter_objects {
  153. unsigned int m_ulFlags;
  154. filter_objects(unsigned int ulFlags) : m_ulFlags(ulFlags) {}
  155. bool operator()(const localobjectdetails_t &details) const
  156. {
  157. return
  158. ((m_ulFlags & AB_FILTER_SYSTEM) &&
  159. (details.GetClass() == DISTLIST_SECURITY && details.ulId == KOPANO_UID_EVERYONE)) ||
  160. ((m_ulFlags & AB_FILTER_SYSTEM) &&
  161. (details.GetClass() == ACTIVE_USER && details.ulId == KOPANO_UID_SYSTEM)) ||
  162. ((m_ulFlags & AB_FILTER_ADDRESSLIST) &&
  163. (details.GetClass() == CONTAINER_ADDRESSLIST)) ||
  164. ((m_ulFlags & AB_FILTER_CONTACTS) &&
  165. (details.GetClass() == NONACTIVE_CONTACT));
  166. }
  167. };
  168. ECRESULT ECABObjectTable::LoadHierarchyAddressList(unsigned int ulObjectId, unsigned int ulFlags,
  169. list<localobjectdetails_t> **lppObjects)
  170. {
  171. ECRESULT er = erSuccess;
  172. std::unique_ptr<std::list<localobjectdetails_t> > lpObjects;
  173. if (ulObjectId == KOPANO_UID_GLOBAL_ADDRESS_BOOK ||
  174. ulObjectId == KOPANO_UID_GLOBAL_ADDRESS_LISTS)
  175. {
  176. /* Global Address Book, load addresslist of the users own company */
  177. er = lpSession->GetSecurity()->GetUserCompany(&ulObjectId);
  178. if (er != erSuccess)
  179. return er;
  180. }
  181. er = lpSession->GetUserManagement()->GetCompanyObjectListAndSync(CONTAINER_ADDRESSLIST,
  182. ulObjectId, &unique_tie(lpObjects), m_ulUserManagementFlags);
  183. if (er != erSuccess)
  184. return er;
  185. /* Filter objects */
  186. if (ulFlags)
  187. lpObjects->remove_if(filter_objects(ulFlags));
  188. if (lppObjects != nullptr)
  189. *lppObjects = lpObjects.release();
  190. return erSuccess;
  191. }
  192. ECRESULT ECABObjectTable::LoadHierarchyCompany(unsigned int ulObjectId, unsigned int ulFlags,
  193. list<localobjectdetails_t> **lppObjects)
  194. {
  195. ECRESULT er = erSuccess;
  196. std::unique_ptr<std::list<localobjectdetails_t> > lpObjects;
  197. ECSecurity *lpSecurity = lpSession->GetSecurity();
  198. er = lpSecurity->GetViewableCompanyIds(m_ulUserManagementFlags, &unique_tie(lpObjects));
  199. if (er != erSuccess)
  200. return er;
  201. /*
  202. * This looks a bit hackish, and it probably can be considered as such.
  203. * If the size of lpObjects is 1 then the user can only view a single company space (namely his own).
  204. * There are 3 reasons this could happen.
  205. * 1. When working in a non-hosted environment.
  206. * 2. When the company does not have enough permissions to view any other company spaces.
  207. * 3. There's only one company.
  208. *
  209. * Having a Global Address Book _and_ a container for the company will only look strange and
  210. * confusing for the user. So lets delete the entry altogether for everybody except SYSTEM
  211. * and SYSADMIN users.
  212. */
  213. if (lpObjects->size() == 1 && lpSecurity->GetAdminLevel() < ADMIN_LEVEL_SYSADMIN)
  214. lpObjects->clear();
  215. if (lppObjects != nullptr)
  216. *lppObjects = lpObjects.release();
  217. return erSuccess;
  218. }
  219. ECRESULT ECABObjectTable::LoadHierarchyContainer(unsigned int ulObjectId, unsigned int ulFlags,
  220. list<localobjectdetails_t> **lppObjects)
  221. {
  222. ECRESULT er = erSuccess;
  223. std::unique_ptr<std::list<localobjectdetails_t> > lpObjects;
  224. objectid_t objectid;
  225. if (ulObjectId == KOPANO_UID_ADDRESS_BOOK) {
  226. /*
  227. * Kopano Address Book
  228. * The Kopano Address Book contains 2 containers,
  229. * the first container is the Global Address Book,
  230. * the second is the Global Address Lists container.
  231. */
  232. lpObjects.reset(new std::list<localobjectdetails_t>());
  233. lpObjects->push_back(localobjectdetails_t(KOPANO_UID_GLOBAL_ADDRESS_BOOK, CONTAINER_COMPANY));
  234. if (!(m_ulUserManagementFlags & USERMANAGEMENT_IDS_ONLY))
  235. lpObjects->back().SetPropString(OB_PROP_S_LOGIN, KOPANO_ACCOUNT_GLOBAL_ADDRESS_BOOK);
  236. lpObjects->push_back(localobjectdetails_t(KOPANO_UID_GLOBAL_ADDRESS_LISTS, CONTAINER_ADDRESSLIST));
  237. if (!(m_ulUserManagementFlags & USERMANAGEMENT_IDS_ONLY))
  238. lpObjects->back().SetPropString(OB_PROP_S_LOGIN, KOPANO_ACCOUNT_GLOBAL_ADDRESS_LISTS);
  239. } else if (ulObjectId == KOPANO_UID_GLOBAL_ADDRESS_BOOK) {
  240. /*
  241. * Global Address Book
  242. * The hierarchy for the Global Address Book contains all visible
  243. * companies and the addresslists belonging to this company.
  244. *
  245. * When in offline mode we only support the Global Address Book container,
  246. * there are 2 reasons for this:
  247. * 1) Currently offline mode does not assign users to companies, which means the lists are empty
  248. * except for the company itself and the System user.
  249. * 2) Outlook generates the hierarchy view once during startup, when permissions change and
  250. * during sync one of the companies becomes unavailable, it will still be present
  251. * in the hierarchy view. The user will not be allowed to open it, so it isn't a real security risk,
  252. * but since we still have issue (1) open, we might as well disable the hierarchy view
  253. * containers completely. */
  254. er = LoadHierarchyCompany(ulObjectId, ulFlags, &unique_tie(lpObjects));
  255. if (er != erSuccess)
  256. return er;
  257. } else if (ulObjectId == KOPANO_UID_GLOBAL_ADDRESS_LISTS) {
  258. if (lpSession->GetSecurity()->GetUserId() == KOPANO_UID_SYSTEM)
  259. return KCERR_INVALID_PARAMETER;
  260. er = LoadHierarchyAddressList(ulObjectId, ulFlags, &unique_tie(lpObjects));
  261. if (er != erSuccess)
  262. return er;
  263. } else {
  264. /*
  265. * Normal container
  266. * Containers don't have additional subcontainers
  267. */
  268. lpObjects.reset(new std::list<localobjectdetails_t>());
  269. }
  270. if (lppObjects != nullptr)
  271. *lppObjects = lpObjects.release();
  272. return erSuccess;
  273. }
  274. ECRESULT ECABObjectTable::LoadContentsAddressList(unsigned int ulObjectId, unsigned int ulFlags,
  275. list<localobjectdetails_t> **lppObjects)
  276. {
  277. ECRESULT er = erSuccess;
  278. std::unique_ptr<std::list<localobjectdetails_t> > lpObjects;
  279. er = lpSession->GetUserManagement()->GetSubObjectsOfObjectAndSync(OBJECTRELATION_ADDRESSLIST_MEMBER,
  280. ulObjectId, &unique_tie(lpObjects), m_ulUserManagementFlags);
  281. if (er != erSuccess)
  282. return er;
  283. /* Filter objects */
  284. if (ulFlags)
  285. lpObjects->remove_if(filter_objects(ulFlags));
  286. if (lppObjects != nullptr)
  287. *lppObjects = lpObjects.release();
  288. return erSuccess;
  289. }
  290. ECRESULT ECABObjectTable::LoadContentsCompany(unsigned int ulObjectId, unsigned int ulFlags,
  291. list<localobjectdetails_t> **lppObjects)
  292. {
  293. ECRESULT er = erSuccess;
  294. std::unique_ptr<std::list<localobjectdetails_t> > lpObjects;
  295. er = lpSession->GetUserManagement()->GetCompanyObjectListAndSync(OBJECTCLASS_UNKNOWN,
  296. ulObjectId, &unique_tie(lpObjects), m_ulUserManagementFlags);
  297. if (er != erSuccess)
  298. return er;
  299. /* Filter objects */
  300. if (ulFlags)
  301. lpObjects->remove_if(filter_objects(ulFlags));
  302. if (lppObjects != nullptr)
  303. *lppObjects = lpObjects.release();
  304. return erSuccess;
  305. }
  306. ECRESULT ECABObjectTable::LoadContentsDistlist(unsigned int ulObjectId, unsigned int ulFlags,
  307. list<localobjectdetails_t> **lppObjects)
  308. {
  309. ECRESULT er = erSuccess;
  310. std::unique_ptr<std::list<localobjectdetails_t> > lpObjects;
  311. er = lpSession->GetUserManagement()->GetSubObjectsOfObjectAndSync(OBJECTRELATION_GROUP_MEMBER,
  312. ulObjectId, &unique_tie(lpObjects), m_ulUserManagementFlags);
  313. if (er != erSuccess)
  314. return er;
  315. /* Filter objects */
  316. if (ulFlags)
  317. lpObjects->remove_if(filter_objects(ulFlags));
  318. if (lppObjects != nullptr)
  319. *lppObjects = lpObjects.release();
  320. return erSuccess;
  321. }
  322. ECRESULT ECABObjectTable::Load()
  323. {
  324. ECRESULT er = erSuccess;
  325. auto lpODAB = static_cast<ECODAB *>(m_lpObjectData);
  326. sObjectTableKey sRowItem;
  327. std::unique_ptr<std::list<localobjectdetails_t> > lpObjects;
  328. std::list<unsigned int> lstObjects;
  329. unsigned int ulObjectId = 0;
  330. unsigned int ulObjectFilter = 0;
  331. objectid_t objectid;
  332. // If the GAB is disabled, don't show any entries except the top-level object
  333. if(lpODAB->ulABParentId != 0 && parseBool(lpSession->GetSessionManager()->GetConfig()->GetSetting("enable_gab")) == false && lpSession->GetSecurity()->GetAdminLevel() == 0)
  334. return er;
  335. er = lpSession->GetSecurity()->IsUserObjectVisible(lpODAB->ulABParentId);
  336. if (er != erSuccess)
  337. return er;
  338. /*
  339. * Check if we are loading the contents or hierarchy
  340. */
  341. if (lpODAB->ulABType == MAPI_ABCONT) {
  342. /*
  343. * Load hierarchy containing companies and addresslists
  344. */
  345. if (lpODAB->ulABParentType != MAPI_ABCONT)
  346. return KCERR_INVALID_PARAMETER;
  347. er = LoadHierarchyContainer(lpODAB->ulABParentId, 0, &unique_tie(lpObjects));
  348. if (er != erSuccess)
  349. return er;
  350. } else if (lpODAB->ulABParentId == KOPANO_UID_GLOBAL_ADDRESS_BOOK && lpODAB->ulABParentType == MAPI_ABCONT) {
  351. /*
  352. * Load contents of Global Address Book
  353. */
  354. er = lpSession->GetSecurity()->GetUserCompany(&ulObjectId);
  355. if (er != erSuccess)
  356. return er;
  357. er = LoadContentsCompany(ulObjectId, AB_FILTER_ADDRESSLIST, &unique_tie(lpObjects));
  358. if (er != erSuccess)
  359. return er;
  360. /*
  361. * The company container always contains itself as distlist,
  362. * unless ulCompanyId is 0 which is the default company and
  363. * that one isn't visible.
  364. */
  365. if (!ulObjectId)
  366. lpObjects->push_front(localobjectdetails_t(ulObjectId, CONTAINER_COMPANY));
  367. } else if (lpODAB->ulABParentId == KOPANO_UID_GLOBAL_ADDRESS_LISTS && lpODAB->ulABParentType == MAPI_ABCONT) {
  368. /*
  369. * Load contents of Global Address Lists
  370. */
  371. return KCERR_INVALID_PARAMETER;
  372. } else {
  373. /*
  374. * Load contents of distlist, company or addresslist
  375. */
  376. if (!lpSession->GetUserManagement()->IsInternalObject(lpODAB->ulABParentId)) {
  377. er = lpSession->GetUserManagement()->GetExternalId(lpODAB->ulABParentId, &objectid);
  378. if (er != erSuccess)
  379. return er;
  380. /* Security distribution lists (i.e. companies) should not contain contacts. Note that containers
  381. * *do* contain contacts
  382. */
  383. if (objectid.objclass == CONTAINER_COMPANY && lpODAB->ulABParentType == MAPI_DISTLIST)
  384. ulObjectFilter |= AB_FILTER_CONTACTS;
  385. } else if (lpODAB->ulABParentId == KOPANO_UID_SYSTEM) {
  386. objectid.objclass = ACTIVE_USER;
  387. } else if (lpODAB->ulABParentId == KOPANO_UID_EVERYONE) {
  388. objectid.objclass = DISTLIST_SECURITY;
  389. /* Security distribution lists should not contain contacts */
  390. ulObjectFilter |= AB_FILTER_CONTACTS;
  391. }
  392. /*
  393. * System objects don't belong in container/distlist contents.
  394. * Addresslists should only appear in hierarchy lists.
  395. */
  396. ulObjectFilter |= AB_FILTER_SYSTEM | AB_FILTER_ADDRESSLIST;
  397. switch (objectid.objclass) {
  398. case DISTLIST_GROUP:
  399. case DISTLIST_SECURITY:
  400. case DISTLIST_DYNAMIC:
  401. er = LoadContentsDistlist(lpODAB->ulABParentId, ulObjectFilter, &unique_tie(lpObjects));
  402. if (er != erSuccess)
  403. return er;
  404. break;
  405. case CONTAINER_COMPANY:
  406. er = LoadContentsCompany(lpODAB->ulABParentId, ulObjectFilter, &unique_tie(lpObjects));
  407. if (er != erSuccess)
  408. return er;
  409. break;
  410. case CONTAINER_ADDRESSLIST:
  411. er = LoadContentsAddressList(lpODAB->ulABParentId, ulObjectFilter, &unique_tie(lpObjects));
  412. if (er != erSuccess)
  413. return er;
  414. break;
  415. default:
  416. return KCERR_INVALID_PARAMETER;
  417. }
  418. }
  419. for (const auto &obj : *lpObjects) {
  420. /* Only add visible items */
  421. if (lpSession->GetSecurity()->IsUserObjectVisible(obj.ulId) != erSuccess)
  422. continue;
  423. lstObjects.push_back(obj.ulId);
  424. }
  425. return LoadRows(&lstObjects, 0);
  426. }
  427. } /* namespace */