RemotePlugin.php 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de>
  4. *
  5. * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
  6. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  7. * @author Joas Schilling <coding@schilljs.com>
  8. * @author John Molakvoæ <skjnldsv@protonmail.com>
  9. * @author Julius Härtl <jus@bitgrid.net>
  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\Collaboration\Collaborators;
  28. use OCP\Collaboration\Collaborators\ISearchPlugin;
  29. use OCP\Collaboration\Collaborators\ISearchResult;
  30. use OCP\Collaboration\Collaborators\SearchResultType;
  31. use OCP\Contacts\IManager;
  32. use OCP\Federation\ICloudIdManager;
  33. use OCP\IConfig;
  34. use OCP\IUserManager;
  35. use OCP\IUserSession;
  36. use OCP\Share\IShare;
  37. class RemotePlugin implements ISearchPlugin {
  38. protected $shareeEnumeration;
  39. /** @var IManager */
  40. private $contactsManager;
  41. /** @var ICloudIdManager */
  42. private $cloudIdManager;
  43. /** @var IConfig */
  44. private $config;
  45. /** @var IUserManager */
  46. private $userManager;
  47. /** @var string */
  48. private $userId = '';
  49. public function __construct(IManager $contactsManager, ICloudIdManager $cloudIdManager, IConfig $config, IUserManager $userManager, IUserSession $userSession) {
  50. $this->contactsManager = $contactsManager;
  51. $this->cloudIdManager = $cloudIdManager;
  52. $this->config = $config;
  53. $this->userManager = $userManager;
  54. $user = $userSession->getUser();
  55. if ($user !== null) {
  56. $this->userId = $user->getUID();
  57. }
  58. $this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
  59. }
  60. public function search($search, $limit, $offset, ISearchResult $searchResult) {
  61. $result = ['wide' => [], 'exact' => []];
  62. $resultType = new SearchResultType('remotes');
  63. // Search in contacts
  64. $addressBookContacts = $this->contactsManager->search($search, ['CLOUD', 'FN'], [
  65. 'limit' => $limit,
  66. 'offset' => $offset,
  67. 'enumeration' => false,
  68. 'fullmatch' => false,
  69. ]);
  70. foreach ($addressBookContacts as $contact) {
  71. if (isset($contact['isLocalSystemBook'])) {
  72. continue;
  73. }
  74. if (isset($contact['CLOUD'])) {
  75. $cloudIds = $contact['CLOUD'];
  76. if (is_string($cloudIds)) {
  77. $cloudIds = [$cloudIds];
  78. }
  79. $lowerSearch = strtolower($search);
  80. foreach ($cloudIds as $cloudId) {
  81. $cloudIdType = '';
  82. if (\is_array($cloudId)) {
  83. $cloudIdData = $cloudId;
  84. $cloudId = $cloudIdData['value'];
  85. $cloudIdType = $cloudIdData['type'];
  86. }
  87. try {
  88. [$remoteUser, $serverUrl] = $this->splitUserRemote($cloudId);
  89. } catch (\InvalidArgumentException $e) {
  90. continue;
  91. }
  92. $localUser = $this->userManager->get($remoteUser);
  93. /**
  94. * Add local share if remote cloud id matches a local user ones
  95. */
  96. if ($localUser !== null && $remoteUser !== $this->userId && $cloudId === $localUser->getCloudId()) {
  97. $result['wide'][] = [
  98. 'label' => $contact['FN'],
  99. 'uuid' => $contact['UID'],
  100. 'value' => [
  101. 'shareType' => IShare::TYPE_USER,
  102. 'shareWith' => $remoteUser
  103. ],
  104. 'shareWithDisplayNameUnique' => $contact['EMAIL'] !== null && $contact['EMAIL'] !== '' ? $contact['EMAIL'] : $contact['UID'],
  105. ];
  106. }
  107. if (strtolower($contact['FN']) === $lowerSearch || strtolower($cloudId) === $lowerSearch) {
  108. if (strtolower($cloudId) === $lowerSearch) {
  109. $searchResult->markExactIdMatch($resultType);
  110. }
  111. $result['exact'][] = [
  112. 'label' => $contact['FN'] . " ($cloudId)",
  113. 'uuid' => $contact['UID'],
  114. 'name' => $contact['FN'],
  115. 'type' => $cloudIdType,
  116. 'value' => [
  117. 'shareType' => IShare::TYPE_REMOTE,
  118. 'shareWith' => $cloudId,
  119. 'server' => $serverUrl,
  120. ],
  121. ];
  122. } else {
  123. $result['wide'][] = [
  124. 'label' => $contact['FN'] . " ($cloudId)",
  125. 'uuid' => $contact['UID'],
  126. 'name' => $contact['FN'],
  127. 'type' => $cloudIdType,
  128. 'value' => [
  129. 'shareType' => IShare::TYPE_REMOTE,
  130. 'shareWith' => $cloudId,
  131. 'server' => $serverUrl,
  132. ],
  133. ];
  134. }
  135. }
  136. }
  137. }
  138. if (!$this->shareeEnumeration) {
  139. $result['wide'] = [];
  140. } else {
  141. $result['wide'] = array_slice($result['wide'], $offset, $limit);
  142. }
  143. /**
  144. * Add generic share with remote item for valid cloud ids that are not users of the local instance
  145. */
  146. if (!$searchResult->hasExactIdMatch($resultType) && $this->cloudIdManager->isValidCloudId($search) && $offset === 0) {
  147. try {
  148. [$remoteUser, $serverUrl] = $this->splitUserRemote($search);
  149. $localUser = $this->userManager->get($remoteUser);
  150. if ($localUser === null || $search !== $localUser->getCloudId()) {
  151. $result['exact'][] = [
  152. 'label' => $remoteUser . " ($serverUrl)",
  153. 'uuid' => $remoteUser,
  154. 'name' => $remoteUser,
  155. 'value' => [
  156. 'shareType' => IShare::TYPE_REMOTE,
  157. 'shareWith' => $search,
  158. 'server' => $serverUrl,
  159. ],
  160. ];
  161. }
  162. } catch (\InvalidArgumentException $e) {
  163. }
  164. }
  165. $searchResult->addResultSet($resultType, $result['wide'], $result['exact']);
  166. return true;
  167. }
  168. /**
  169. * split user and remote from federated cloud id
  170. *
  171. * @param string $address federated share address
  172. * @return array [user, remoteURL]
  173. * @throws \InvalidArgumentException
  174. */
  175. public function splitUserRemote($address) {
  176. try {
  177. $cloudId = $this->cloudIdManager->resolveCloudId($address);
  178. return [$cloudId->getUser(), $cloudId->getRemote()];
  179. } catch (\InvalidArgumentException $e) {
  180. throw new \InvalidArgumentException('Invalid Federated Cloud ID', 0, $e);
  181. }
  182. }
  183. }