ContactsStore.php 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. <?php
  2. /**
  3. * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
  4. * @copyright 2017 Lukas Reschke <lukas@statuscode.ch>
  5. *
  6. * @author Christoph Wurst <christoph@owncloud.com>
  7. * @author Georg Ehrke <oc.list@georgehrke.com>
  8. * @author Lukas Reschke <lukas@statuscode.ch>
  9. * @author Tobia De Koninck <tobia@ledfan.be>
  10. *
  11. * @license GNU AGPL version 3 or any later version
  12. *
  13. * This program is free software: you can redistribute it and/or modify
  14. * it under the terms of the GNU Affero General Public License as
  15. * published by the Free Software Foundation, either version 3 of the
  16. * License, or (at your option) any later version.
  17. *
  18. * This program is distributed in the hope that it will be useful,
  19. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. * GNU Affero General Public License for more details.
  22. *
  23. * You should have received a copy of the GNU Affero General Public License
  24. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  25. *
  26. */
  27. namespace OC\Contacts\ContactsMenu;
  28. use OCP\Contacts\ContactsMenu\IEntry;
  29. use OCP\Contacts\IManager;
  30. use OCP\IConfig;
  31. use OCP\IGroupManager;
  32. use OCP\IUser;
  33. use OCP\IUserManager;
  34. use OCP\Contacts\ContactsMenu\IContactsStore;
  35. class ContactsStore implements IContactsStore {
  36. /** @var IManager */
  37. private $contactsManager;
  38. /** @var IConfig */
  39. private $config;
  40. /** @var IUserManager */
  41. private $userManager;
  42. /** @var IGroupManager */
  43. private $groupManager;
  44. /**
  45. * @param IManager $contactsManager
  46. * @param IConfig $config
  47. * @param IUserManager $userManager
  48. * @param IGroupManager $groupManager
  49. */
  50. public function __construct(IManager $contactsManager,
  51. IConfig $config,
  52. IUserManager $userManager,
  53. IGroupManager $groupManager) {
  54. $this->contactsManager = $contactsManager;
  55. $this->config = $config;
  56. $this->userManager = $userManager;
  57. $this->groupManager = $groupManager;
  58. }
  59. /**
  60. * @param IUser $user
  61. * @param string|null $filter
  62. * @return IEntry[]
  63. */
  64. public function getContacts(IUser $user, $filter) {
  65. $allContacts = $this->contactsManager->search($filter ?: '', [
  66. 'FN',
  67. 'EMAIL'
  68. ]);
  69. $entries = array_map(function(array $contact) {
  70. return $this->contactArrayToEntry($contact);
  71. }, $allContacts);
  72. return $this->filterContacts(
  73. $user,
  74. $entries,
  75. $filter
  76. );
  77. }
  78. /**
  79. * Filters the contacts. Applies 3 filters:
  80. * 1. filter the current user
  81. * 2. if the `shareapi_allow_share_dialog_user_enumeration` config option is
  82. * enabled it will filter all local users
  83. * 3. if the `shareapi_exclude_groups` config option is enabled and the
  84. * current user is in an excluded group it will filter all local users.
  85. * 4. if the `shareapi_only_share_with_group_members` config option is
  86. * enabled it will filter all users which doens't have a common group
  87. * with the current user.
  88. *
  89. * @param IUser $self
  90. * @param Entry[] $entries
  91. * @param string $filter
  92. * @return Entry[] the filtered contacts
  93. */
  94. private function filterContacts(IUser $self,
  95. array $entries,
  96. $filter) {
  97. $disallowEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') !== 'yes';
  98. $excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes';
  99. // whether to filter out local users
  100. $skipLocal = false;
  101. // whether to filter out all users which doesn't have the same group as the current user
  102. $ownGroupsOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes';
  103. $selfGroups = $this->groupManager->getUserGroupIds($self);
  104. if ($excludedGroups) {
  105. $excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', '');
  106. $decodedExcludeGroups = json_decode($excludedGroups, true);
  107. $excludeGroupsList = ($decodedExcludeGroups !== null) ? $decodedExcludeGroups : [];
  108. if (count(array_intersect($excludeGroupsList, $selfGroups)) !== 0) {
  109. // a group of the current user is excluded -> filter all local users
  110. $skipLocal = true;
  111. }
  112. }
  113. $selfUID = $self->getUID();
  114. return array_values(array_filter($entries, function(IEntry $entry) use ($self, $skipLocal, $ownGroupsOnly, $selfGroups, $selfUID, $disallowEnumeration, $filter) {
  115. if ($skipLocal && $entry->getProperty('isLocalSystemBook') === true) {
  116. return false;
  117. }
  118. // Prevent enumerating local users
  119. if($disallowEnumeration && $entry->getProperty('isLocalSystemBook')) {
  120. $filterUser = true;
  121. $mailAddresses = $entry->getEMailAddresses();
  122. foreach($mailAddresses as $mailAddress) {
  123. if($mailAddress === $filter) {
  124. $filterUser = false;
  125. break;
  126. }
  127. }
  128. if($entry->getProperty('UID') && $entry->getProperty('UID') === $filter) {
  129. $filterUser = false;
  130. }
  131. if($filterUser) {
  132. return false;
  133. }
  134. }
  135. if ($ownGroupsOnly && $entry->getProperty('isLocalSystemBook') === true) {
  136. $uid = $this->userManager->get($entry->getProperty('UID'));
  137. if ($uid === NULL) {
  138. return false;
  139. }
  140. $contactGroups = $this->groupManager->getUserGroupIds($uid);
  141. if (count(array_intersect($contactGroups, $selfGroups)) === 0) {
  142. // no groups in common, so shouldn't see the contact
  143. return false;
  144. }
  145. }
  146. return $entry->getProperty('UID') !== $selfUID;
  147. }));
  148. }
  149. /**
  150. * @param IUser $user
  151. * @param integer $shareType
  152. * @param string $shareWith
  153. * @return IEntry|null
  154. */
  155. public function findOne(IUser $user, $shareType, $shareWith) {
  156. switch($shareType) {
  157. case 0:
  158. case 6:
  159. $filter = ['UID'];
  160. break;
  161. case 4:
  162. $filter = ['EMAIL'];
  163. break;
  164. default:
  165. return null;
  166. }
  167. $userId = $user->getUID();
  168. $allContacts = $this->contactsManager->search($shareWith, $filter);
  169. $contacts = array_filter($allContacts, function($contact) use ($userId) {
  170. return $contact['UID'] !== $userId;
  171. });
  172. $match = null;
  173. foreach ($contacts as $contact) {
  174. if ($shareType === 4 && isset($contact['EMAIL'])) {
  175. if (in_array($shareWith, $contact['EMAIL'])) {
  176. $match = $contact;
  177. break;
  178. }
  179. }
  180. if ($shareType === 0 || $shareType === 6) {
  181. $isLocal = $contact['isLocalSystemBook'] ?? false;
  182. if ($contact['UID'] === $shareWith && $isLocal === true) {
  183. $match = $contact;
  184. break;
  185. }
  186. }
  187. }
  188. if ($match) {
  189. $match = $this->filterContacts($user, [$this->contactArrayToEntry($match)], $shareWith);
  190. if (count($match) === 1) {
  191. $match = $match[0];
  192. } else {
  193. $match = null;
  194. }
  195. }
  196. return $match;
  197. }
  198. /**
  199. * @param array $contact
  200. * @return Entry
  201. */
  202. private function contactArrayToEntry(array $contact) {
  203. $entry = new Entry();
  204. if (isset($contact['id'])) {
  205. $entry->setId($contact['id']);
  206. }
  207. if (isset($contact['FN'])) {
  208. $entry->setFullName($contact['FN']);
  209. }
  210. $avatarPrefix = "VALUE=uri:";
  211. if (isset($contact['PHOTO']) && strpos($contact['PHOTO'], $avatarPrefix) === 0) {
  212. $entry->setAvatar(substr($contact['PHOTO'], strlen($avatarPrefix)));
  213. }
  214. if (isset($contact['EMAIL'])) {
  215. foreach ($contact['EMAIL'] as $email) {
  216. $entry->addEMailAddress($email);
  217. }
  218. }
  219. // Attach all other properties to the entry too because some
  220. // providers might make use of it.
  221. $entry->setProperties($contact);
  222. return $entry;
  223. }
  224. }