1<?php 2 3declare(strict_types=1); 4 5/** 6 * @copyright Copyright (c) 2017, Robin Appelman <robin@icewind.nl> 7 * 8 * @author Arthur Schiwon <blizzz@arthur-schiwon.de> 9 * @author Christoph Wurst <christoph@winzerhof-wurst.at> 10 * @author Guillaume Virlet <github@virlet.org> 11 * @author Joas Schilling <coding@schilljs.com> 12 * @author Robin Appelman <robin@icewind.nl> 13 * @author Roeland Jago Douma <roeland@famdouma.nl> 14 * 15 * @license GNU AGPL version 3 or any later version 16 * 17 * This program is free software: you can redistribute it and/or modify 18 * it under the terms of the GNU Affero General Public License as 19 * published by the Free Software Foundation, either version 3 of the 20 * License, or (at your option) any later version. 21 * 22 * This program is distributed in the hope that it will be useful, 23 * but WITHOUT ANY WARRANTY; without even the implied warranty of 24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 * GNU Affero General Public License for more details. 26 * 27 * You should have received a copy of the GNU Affero General Public License 28 * along with this program. If not, see <http://www.gnu.org/licenses/>. 29 * 30 */ 31namespace OC\Federation; 32 33use OCP\Contacts\IManager; 34use OCP\Federation\ICloudId; 35use OCP\Federation\ICloudIdManager; 36use OCP\IURLGenerator; 37use OCP\IUserManager; 38 39class CloudIdManager implements ICloudIdManager { 40 /** @var IManager */ 41 private $contactsManager; 42 /** @var IURLGenerator */ 43 private $urlGenerator; 44 /** @var IUserManager */ 45 private $userManager; 46 47 public function __construct(IManager $contactsManager, IURLGenerator $urlGenerator, IUserManager $userManager) { 48 $this->contactsManager = $contactsManager; 49 $this->urlGenerator = $urlGenerator; 50 $this->userManager = $userManager; 51 } 52 53 /** 54 * @param string $cloudId 55 * @return ICloudId 56 * @throws \InvalidArgumentException 57 */ 58 public function resolveCloudId(string $cloudId): ICloudId { 59 // TODO magic here to get the url and user instead of just splitting on @ 60 61 if (!$this->isValidCloudId($cloudId)) { 62 throw new \InvalidArgumentException('Invalid cloud id'); 63 } 64 65 // Find the first character that is not allowed in user names 66 $id = $this->fixRemoteURL($cloudId); 67 $posSlash = strpos($id, '/'); 68 $posColon = strpos($id, ':'); 69 70 if ($posSlash === false && $posColon === false) { 71 $invalidPos = \strlen($id); 72 } elseif ($posSlash === false) { 73 $invalidPos = $posColon; 74 } elseif ($posColon === false) { 75 $invalidPos = $posSlash; 76 } else { 77 $invalidPos = min($posSlash, $posColon); 78 } 79 80 $lastValidAtPos = strrpos($id, '@', $invalidPos - strlen($id)); 81 82 if ($lastValidAtPos !== false) { 83 $user = substr($id, 0, $lastValidAtPos); 84 $remote = substr($id, $lastValidAtPos + 1); 85 if (!empty($user) && !empty($remote)) { 86 return new CloudId($id, $user, $remote, $this->getDisplayNameFromContact($id)); 87 } 88 } 89 throw new \InvalidArgumentException('Invalid cloud id'); 90 } 91 92 protected function getDisplayNameFromContact(string $cloudId): ?string { 93 $addressBookEntries = $this->contactsManager->search($cloudId, ['CLOUD']); 94 foreach ($addressBookEntries as $entry) { 95 if (isset($entry['CLOUD'])) { 96 foreach ($entry['CLOUD'] as $cloudID) { 97 if ($cloudID === $cloudId) { 98 // Warning, if user decides to make his full name local only, 99 // no FN is found on federated servers 100 if (isset($entry['FN'])) { 101 return $entry['FN']; 102 } else { 103 return $cloudID; 104 } 105 } 106 } 107 } 108 } 109 return null; 110 } 111 112 /** 113 * @param string $user 114 * @param string|null $remote 115 * @return CloudId 116 */ 117 public function getCloudId(string $user, ?string $remote): ICloudId { 118 if ($remote === null) { 119 $remote = rtrim($this->removeProtocolFromUrl($this->urlGenerator->getAbsoluteURL('/')), '/'); 120 $fixedRemote = $this->fixRemoteURL($remote); 121 $localUser = $this->userManager->get($user); 122 $displayName = !is_null($localUser) ? $localUser->getDisplayName() : ''; 123 } else { 124 // TODO check what the correct url is for remote (asking the remote) 125 $fixedRemote = $this->fixRemoteURL($remote); 126 $host = $this->removeProtocolFromUrl($fixedRemote); 127 $displayName = $this->getDisplayNameFromContact($user . '@' . $host); 128 } 129 $id = $user . '@' . $remote; 130 return new CloudId($id, $user, $fixedRemote, $displayName); 131 } 132 133 /** 134 * @param string $url 135 * @return string 136 */ 137 private function removeProtocolFromUrl($url) { 138 if (strpos($url, 'https://') === 0) { 139 return substr($url, strlen('https://')); 140 } elseif (strpos($url, 'http://') === 0) { 141 return substr($url, strlen('http://')); 142 } 143 144 return $url; 145 } 146 147 /** 148 * Strips away a potential file names and trailing slashes: 149 * - http://localhost 150 * - http://localhost/ 151 * - http://localhost/index.php 152 * - http://localhost/index.php/s/{shareToken} 153 * 154 * all return: http://localhost 155 * 156 * @param string $remote 157 * @return string 158 */ 159 protected function fixRemoteURL(string $remote): string { 160 $remote = str_replace('\\', '/', $remote); 161 if ($fileNamePosition = strpos($remote, '/index.php')) { 162 $remote = substr($remote, 0, $fileNamePosition); 163 } 164 $remote = rtrim($remote, '/'); 165 166 return $remote; 167 } 168 169 /** 170 * @param string $cloudId 171 * @return bool 172 */ 173 public function isValidCloudId(string $cloudId): bool { 174 return strpos($cloudId, '@') !== false; 175 } 176} 177