You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

RemotePlugin.php 6.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  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) <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'], ['limit' => $limit, 'offset' => $offset]);
  65. foreach ($addressBookContacts as $contact) {
  66. if (isset($contact['isLocalSystemBook'])) {
  67. continue;
  68. }
  69. if (isset($contact['CLOUD'])) {
  70. $cloudIds = $contact['CLOUD'];
  71. if (is_string($cloudIds)) {
  72. $cloudIds = [$cloudIds];
  73. }
  74. $lowerSearch = strtolower($search);
  75. foreach ($cloudIds as $cloudId) {
  76. $cloudIdType = '';
  77. if (\is_array($cloudId)) {
  78. $cloudIdData = $cloudId;
  79. $cloudId = $cloudIdData['value'];
  80. $cloudIdType = $cloudIdData['type'];
  81. }
  82. try {
  83. [$remoteUser, $serverUrl] = $this->splitUserRemote($cloudId);
  84. } catch (\InvalidArgumentException $e) {
  85. continue;
  86. }
  87. $localUser = $this->userManager->get($remoteUser);
  88. /**
  89. * Add local share if remote cloud id matches a local user ones
  90. */
  91. if ($localUser !== null && $remoteUser !== $this->userId && $cloudId === $localUser->getCloudId()) {
  92. $result['wide'][] = [
  93. 'label' => $contact['FN'],
  94. 'uuid' => $contact['UID'],
  95. 'value' => [
  96. 'shareType' => IShare::TYPE_USER,
  97. 'shareWith' => $remoteUser
  98. ],
  99. 'shareWithDisplayNameUnique' => $contact['EMAIL'] !== null && $contact['EMAIL'] !== '' ? $contact['EMAIL'] : $contact['UID'],
  100. ];
  101. }
  102. if (strtolower($contact['FN']) === $lowerSearch || strtolower($cloudId) === $lowerSearch) {
  103. if (strtolower($cloudId) === $lowerSearch) {
  104. $searchResult->markExactIdMatch($resultType);
  105. }
  106. $result['exact'][] = [
  107. 'label' => $contact['FN'] . " ($cloudId)",
  108. 'uuid' => $contact['UID'],
  109. 'name' => $contact['FN'],
  110. 'type' => $cloudIdType,
  111. 'value' => [
  112. 'shareType' => IShare::TYPE_REMOTE,
  113. 'shareWith' => $cloudId,
  114. 'server' => $serverUrl,
  115. ],
  116. ];
  117. } else {
  118. $result['wide'][] = [
  119. 'label' => $contact['FN'] . " ($cloudId)",
  120. 'uuid' => $contact['UID'],
  121. 'name' => $contact['FN'],
  122. 'type' => $cloudIdType,
  123. 'value' => [
  124. 'shareType' => IShare::TYPE_REMOTE,
  125. 'shareWith' => $cloudId,
  126. 'server' => $serverUrl,
  127. ],
  128. ];
  129. }
  130. }
  131. }
  132. }
  133. if (!$this->shareeEnumeration) {
  134. $result['wide'] = [];
  135. } else {
  136. $result['wide'] = array_slice($result['wide'], $offset, $limit);
  137. }
  138. /**
  139. * Add generic share with remote item for valid cloud ids that are not users of the local instance
  140. */
  141. if (!$searchResult->hasExactIdMatch($resultType) && $this->cloudIdManager->isValidCloudId($search) && $offset === 0) {
  142. try {
  143. [$remoteUser, $serverUrl] = $this->splitUserRemote($search);
  144. $localUser = $this->userManager->get($remoteUser);
  145. if ($localUser === null || $search !== $localUser->getCloudId()) {
  146. $result['exact'][] = [
  147. 'label' => $remoteUser . " ($serverUrl)",
  148. 'uuid' => $remoteUser,
  149. 'name' => $remoteUser,
  150. 'value' => [
  151. 'shareType' => IShare::TYPE_REMOTE,
  152. 'shareWith' => $search,
  153. 'server' => $serverUrl,
  154. ],
  155. ];
  156. }
  157. } catch (\InvalidArgumentException $e) {
  158. }
  159. }
  160. $searchResult->addResultSet($resultType, $result['wide'], $result['exact']);
  161. return true;
  162. }
  163. /**
  164. * split user and remote from federated cloud id
  165. *
  166. * @param string $address federated share address
  167. * @return array [user, remoteURL]
  168. * @throws \InvalidArgumentException
  169. */
  170. public function splitUserRemote($address) {
  171. try {
  172. $cloudId = $this->cloudIdManager->resolveCloudId($address);
  173. return [$cloudId->getUser(), $cloudId->getRemote()];
  174. } catch (\InvalidArgumentException $e) {
  175. throw new \InvalidArgumentException('Invalid Federated Cloud ID', 0, $e);
  176. }
  177. }
  178. }