@@ -27,14 +27,14 @@ discard block |
||
| 27 | 27 | |
| 28 | 28 | $lastConfirm = (int) \OC::$server->getSession()->get('last-password-confirm'); |
| 29 | 29 | if ($lastConfirm < (time() - 30 * 60 + 15)) { // allow 15 seconds delay |
| 30 | - $l = \OC::$server->getL10N('core'); |
|
| 31 | - OC_JSON::error(array( 'data' => array( 'message' => $l->t('Password confirmation is required')))); |
|
| 32 | - exit(); |
|
| 30 | + $l = \OC::$server->getL10N('core'); |
|
| 31 | + OC_JSON::error(array( 'data' => array( 'message' => $l->t('Password confirmation is required')))); |
|
| 32 | + exit(); |
|
| 33 | 33 | } |
| 34 | 34 | |
| 35 | 35 | if (!array_key_exists('appid', $_POST)) { |
| 36 | - OC_JSON::error(); |
|
| 37 | - exit; |
|
| 36 | + OC_JSON::error(); |
|
| 37 | + exit; |
|
| 38 | 38 | } |
| 39 | 39 | |
| 40 | 40 | $appId = (string)$_POST['appid']; |
@@ -42,11 +42,11 @@ discard block |
||
| 42 | 42 | |
| 43 | 43 | $result = OC_App::removeApp($appId); |
| 44 | 44 | if($result !== false) { |
| 45 | - // FIXME: Clear the cache - move that into some sane helper method |
|
| 46 | - \OC::$server->getMemCacheFactory()->createDistributed('settings')->remove('listApps-0'); |
|
| 47 | - \OC::$server->getMemCacheFactory()->createDistributed('settings')->remove('listApps-1'); |
|
| 48 | - OC_JSON::success(array('data' => array('appid' => $appId))); |
|
| 45 | + // FIXME: Clear the cache - move that into some sane helper method |
|
| 46 | + \OC::$server->getMemCacheFactory()->createDistributed('settings')->remove('listApps-0'); |
|
| 47 | + \OC::$server->getMemCacheFactory()->createDistributed('settings')->remove('listApps-1'); |
|
| 48 | + OC_JSON::success(array('data' => array('appid' => $appId))); |
|
| 49 | 49 | } else { |
| 50 | - $l = \OC::$server->getL10N('settings'); |
|
| 51 | - OC_JSON::error(array('data' => array( 'message' => $l->t("Couldn't remove app.") ))); |
|
| 50 | + $l = \OC::$server->getL10N('settings'); |
|
| 51 | + OC_JSON::error(array('data' => array( 'message' => $l->t("Couldn't remove app.") ))); |
|
| 52 | 52 | } |
@@ -28,7 +28,7 @@ discard block |
||
| 28 | 28 | $lastConfirm = (int) \OC::$server->getSession()->get('last-password-confirm'); |
| 29 | 29 | if ($lastConfirm < (time() - 30 * 60 + 15)) { // allow 15 seconds delay |
| 30 | 30 | $l = \OC::$server->getL10N('core'); |
| 31 | - OC_JSON::error(array( 'data' => array( 'message' => $l->t('Password confirmation is required')))); |
|
| 31 | + OC_JSON::error(array('data' => array('message' => $l->t('Password confirmation is required')))); |
|
| 32 | 32 | exit(); |
| 33 | 33 | } |
| 34 | 34 | |
@@ -37,16 +37,16 @@ discard block |
||
| 37 | 37 | exit; |
| 38 | 38 | } |
| 39 | 39 | |
| 40 | -$appId = (string)$_POST['appid']; |
|
| 40 | +$appId = (string) $_POST['appid']; |
|
| 41 | 41 | $appId = OC_App::cleanAppId($appId); |
| 42 | 42 | |
| 43 | 43 | $result = OC_App::removeApp($appId); |
| 44 | -if($result !== false) { |
|
| 44 | +if ($result !== false) { |
|
| 45 | 45 | // FIXME: Clear the cache - move that into some sane helper method |
| 46 | 46 | \OC::$server->getMemCacheFactory()->createDistributed('settings')->remove('listApps-0'); |
| 47 | 47 | \OC::$server->getMemCacheFactory()->createDistributed('settings')->remove('listApps-1'); |
| 48 | 48 | OC_JSON::success(array('data' => array('appid' => $appId))); |
| 49 | 49 | } else { |
| 50 | 50 | $l = \OC::$server->getL10N('settings'); |
| 51 | - OC_JSON::error(array('data' => array( 'message' => $l->t("Couldn't remove app.") ))); |
|
| 51 | + OC_JSON::error(array('data' => array('message' => $l->t("Couldn't remove app.")))); |
|
| 52 | 52 | } |
@@ -42,328 +42,328 @@ |
||
| 42 | 42 | use OCP\Files\StorageNotAvailableException; |
| 43 | 43 | |
| 44 | 44 | class Storage extends DAV implements ISharedStorage { |
| 45 | - /** @var ICloudId */ |
|
| 46 | - private $cloudId; |
|
| 47 | - /** @var string */ |
|
| 48 | - private $mountPoint; |
|
| 49 | - /** @var string */ |
|
| 50 | - private $token; |
|
| 51 | - /** @var \OCP\ICacheFactory */ |
|
| 52 | - private $memcacheFactory; |
|
| 53 | - /** @var \OCP\Http\Client\IClientService */ |
|
| 54 | - private $httpClient; |
|
| 55 | - /** @var bool */ |
|
| 56 | - private $updateChecked = false; |
|
| 57 | - |
|
| 58 | - /** |
|
| 59 | - * @var \OCA\Files_Sharing\External\Manager |
|
| 60 | - */ |
|
| 61 | - private $manager; |
|
| 62 | - |
|
| 63 | - public function __construct($options) { |
|
| 64 | - $this->memcacheFactory = \OC::$server->getMemCacheFactory(); |
|
| 65 | - $this->httpClient = $options['HttpClientService']; |
|
| 66 | - |
|
| 67 | - $this->manager = $options['manager']; |
|
| 68 | - $this->cloudId = $options['cloudId']; |
|
| 69 | - $discoveryService = \OC::$server->query(\OCP\OCS\IDiscoveryService::class); |
|
| 70 | - |
|
| 71 | - list($protocol, $remote) = explode('://', $this->cloudId->getRemote()); |
|
| 72 | - if (strpos($remote, '/')) { |
|
| 73 | - list($host, $root) = explode('/', $remote, 2); |
|
| 74 | - } else { |
|
| 75 | - $host = $remote; |
|
| 76 | - $root = ''; |
|
| 77 | - } |
|
| 78 | - $secure = $protocol === 'https'; |
|
| 79 | - $federatedSharingEndpoints = $discoveryService->discover($this->cloudId->getRemote(), 'FEDERATED_SHARING'); |
|
| 80 | - $webDavEndpoint = isset($federatedSharingEndpoints['webdav']) ? $federatedSharingEndpoints['webdav'] : '/public.php/webdav'; |
|
| 81 | - $root = rtrim($root, '/') . $webDavEndpoint; |
|
| 82 | - $this->mountPoint = $options['mountpoint']; |
|
| 83 | - $this->token = $options['token']; |
|
| 84 | - |
|
| 85 | - parent::__construct(array( |
|
| 86 | - 'secure' => $secure, |
|
| 87 | - 'host' => $host, |
|
| 88 | - 'root' => $root, |
|
| 89 | - 'user' => $options['token'], |
|
| 90 | - 'password' => (string)$options['password'] |
|
| 91 | - )); |
|
| 92 | - } |
|
| 93 | - |
|
| 94 | - public function getWatcher($path = '', $storage = null) { |
|
| 95 | - if (!$storage) { |
|
| 96 | - $storage = $this; |
|
| 97 | - } |
|
| 98 | - if (!isset($this->watcher)) { |
|
| 99 | - $this->watcher = new Watcher($storage); |
|
| 100 | - $this->watcher->setPolicy(\OC\Files\Cache\Watcher::CHECK_ONCE); |
|
| 101 | - } |
|
| 102 | - return $this->watcher; |
|
| 103 | - } |
|
| 104 | - |
|
| 105 | - public function getRemoteUser() { |
|
| 106 | - return $this->cloudId->getUser(); |
|
| 107 | - } |
|
| 108 | - |
|
| 109 | - public function getRemote() { |
|
| 110 | - return $this->cloudId->getRemote(); |
|
| 111 | - } |
|
| 112 | - |
|
| 113 | - public function getMountPoint() { |
|
| 114 | - return $this->mountPoint; |
|
| 115 | - } |
|
| 116 | - |
|
| 117 | - public function getToken() { |
|
| 118 | - return $this->token; |
|
| 119 | - } |
|
| 120 | - |
|
| 121 | - public function getPassword() { |
|
| 122 | - return $this->password; |
|
| 123 | - } |
|
| 124 | - |
|
| 125 | - /** |
|
| 126 | - * @brief get id of the mount point |
|
| 127 | - * @return string |
|
| 128 | - */ |
|
| 129 | - public function getId() { |
|
| 130 | - return 'shared::' . md5($this->token . '@' . $this->getRemote()); |
|
| 131 | - } |
|
| 132 | - |
|
| 133 | - public function getCache($path = '', $storage = null) { |
|
| 134 | - if (is_null($this->cache)) { |
|
| 135 | - $this->cache = new Cache($this, $this->cloudId); |
|
| 136 | - } |
|
| 137 | - return $this->cache; |
|
| 138 | - } |
|
| 139 | - |
|
| 140 | - /** |
|
| 141 | - * @param string $path |
|
| 142 | - * @param \OC\Files\Storage\Storage $storage |
|
| 143 | - * @return \OCA\Files_Sharing\External\Scanner |
|
| 144 | - */ |
|
| 145 | - public function getScanner($path = '', $storage = null) { |
|
| 146 | - if (!$storage) { |
|
| 147 | - $storage = $this; |
|
| 148 | - } |
|
| 149 | - if (!isset($this->scanner)) { |
|
| 150 | - $this->scanner = new Scanner($storage); |
|
| 151 | - } |
|
| 152 | - return $this->scanner; |
|
| 153 | - } |
|
| 154 | - |
|
| 155 | - /** |
|
| 156 | - * check if a file or folder has been updated since $time |
|
| 157 | - * |
|
| 158 | - * @param string $path |
|
| 159 | - * @param int $time |
|
| 160 | - * @throws \OCP\Files\StorageNotAvailableException |
|
| 161 | - * @throws \OCP\Files\StorageInvalidException |
|
| 162 | - * @return bool |
|
| 163 | - */ |
|
| 164 | - public function hasUpdated($path, $time) { |
|
| 165 | - // since for owncloud webdav servers we can rely on etag propagation we only need to check the root of the storage |
|
| 166 | - // because of that we only do one check for the entire storage per request |
|
| 167 | - if ($this->updateChecked) { |
|
| 168 | - return false; |
|
| 169 | - } |
|
| 170 | - $this->updateChecked = true; |
|
| 171 | - try { |
|
| 172 | - return parent::hasUpdated('', $time); |
|
| 173 | - } catch (StorageInvalidException $e) { |
|
| 174 | - // check if it needs to be removed |
|
| 175 | - $this->checkStorageAvailability(); |
|
| 176 | - throw $e; |
|
| 177 | - } catch (StorageNotAvailableException $e) { |
|
| 178 | - // check if it needs to be removed or just temp unavailable |
|
| 179 | - $this->checkStorageAvailability(); |
|
| 180 | - throw $e; |
|
| 181 | - } |
|
| 182 | - } |
|
| 183 | - |
|
| 184 | - public function test() { |
|
| 185 | - try { |
|
| 186 | - return parent::test(); |
|
| 187 | - } catch (StorageInvalidException $e) { |
|
| 188 | - // check if it needs to be removed |
|
| 189 | - $this->checkStorageAvailability(); |
|
| 190 | - throw $e; |
|
| 191 | - } catch (StorageNotAvailableException $e) { |
|
| 192 | - // check if it needs to be removed or just temp unavailable |
|
| 193 | - $this->checkStorageAvailability(); |
|
| 194 | - throw $e; |
|
| 195 | - } |
|
| 196 | - } |
|
| 197 | - |
|
| 198 | - /** |
|
| 199 | - * Check whether this storage is permanently or temporarily |
|
| 200 | - * unavailable |
|
| 201 | - * |
|
| 202 | - * @throws \OCP\Files\StorageNotAvailableException |
|
| 203 | - * @throws \OCP\Files\StorageInvalidException |
|
| 204 | - */ |
|
| 205 | - public function checkStorageAvailability() { |
|
| 206 | - // see if we can find out why the share is unavailable |
|
| 207 | - try { |
|
| 208 | - $this->getShareInfo(); |
|
| 209 | - } catch (NotFoundException $e) { |
|
| 210 | - // a 404 can either mean that the share no longer exists or there is no Nextcloud on the remote |
|
| 211 | - if ($this->testRemote()) { |
|
| 212 | - // valid Nextcloud instance means that the public share no longer exists |
|
| 213 | - // since this is permanent (re-sharing the file will create a new token) |
|
| 214 | - // we remove the invalid storage |
|
| 215 | - $this->manager->removeShare($this->mountPoint); |
|
| 216 | - $this->manager->getMountManager()->removeMount($this->mountPoint); |
|
| 217 | - throw new StorageInvalidException(); |
|
| 218 | - } else { |
|
| 219 | - // Nextcloud instance is gone, likely to be a temporary server configuration error |
|
| 220 | - throw new StorageNotAvailableException(); |
|
| 221 | - } |
|
| 222 | - } catch (ForbiddenException $e) { |
|
| 223 | - // auth error, remove share for now (provide a dialog in the future) |
|
| 224 | - $this->manager->removeShare($this->mountPoint); |
|
| 225 | - $this->manager->getMountManager()->removeMount($this->mountPoint); |
|
| 226 | - throw new StorageInvalidException(); |
|
| 227 | - } catch (\GuzzleHttp\Exception\ConnectException $e) { |
|
| 228 | - throw new StorageNotAvailableException(); |
|
| 229 | - } catch (\GuzzleHttp\Exception\RequestException $e) { |
|
| 230 | - throw new StorageNotAvailableException(); |
|
| 231 | - } catch (\Exception $e) { |
|
| 232 | - throw $e; |
|
| 233 | - } |
|
| 234 | - } |
|
| 235 | - |
|
| 236 | - public function file_exists($path) { |
|
| 237 | - if ($path === '') { |
|
| 238 | - return true; |
|
| 239 | - } else { |
|
| 240 | - return parent::file_exists($path); |
|
| 241 | - } |
|
| 242 | - } |
|
| 243 | - |
|
| 244 | - /** |
|
| 245 | - * check if the configured remote is a valid federated share provider |
|
| 246 | - * |
|
| 247 | - * @return bool |
|
| 248 | - */ |
|
| 249 | - protected function testRemote() { |
|
| 250 | - try { |
|
| 251 | - return $this->testRemoteUrl($this->getRemote() . '/ocs-provider/index.php') |
|
| 252 | - || $this->testRemoteUrl($this->getRemote() . '/ocs-provider/') |
|
| 253 | - || $this->testRemoteUrl($this->getRemote() . '/status.php'); |
|
| 254 | - } catch (\Exception $e) { |
|
| 255 | - return false; |
|
| 256 | - } |
|
| 257 | - } |
|
| 258 | - |
|
| 259 | - /** |
|
| 260 | - * @param string $url |
|
| 261 | - * @return bool |
|
| 262 | - */ |
|
| 263 | - private function testRemoteUrl($url) { |
|
| 264 | - $cache = $this->memcacheFactory->createDistributed('files_sharing_remote_url'); |
|
| 265 | - if($cache->hasKey($url)) { |
|
| 266 | - return (bool)$cache->get($url); |
|
| 267 | - } |
|
| 268 | - |
|
| 269 | - $client = $this->httpClient->newClient(); |
|
| 270 | - try { |
|
| 271 | - $result = $client->get($url, [ |
|
| 272 | - 'timeout' => 10, |
|
| 273 | - 'connect_timeout' => 10, |
|
| 274 | - ])->getBody(); |
|
| 275 | - $data = json_decode($result); |
|
| 276 | - $returnValue = (is_object($data) && !empty($data->version)); |
|
| 277 | - } catch (ConnectException $e) { |
|
| 278 | - $returnValue = false; |
|
| 279 | - } catch (ClientException $e) { |
|
| 280 | - $returnValue = false; |
|
| 281 | - } |
|
| 282 | - |
|
| 283 | - $cache->set($url, $returnValue, 60*60*24); |
|
| 284 | - return $returnValue; |
|
| 285 | - } |
|
| 286 | - |
|
| 287 | - /** |
|
| 288 | - * Whether the remote is an ownCloud/Nextcloud, used since some sharing features are not |
|
| 289 | - * standardized. Let's use this to detect whether to use it. |
|
| 290 | - * |
|
| 291 | - * @return bool |
|
| 292 | - */ |
|
| 293 | - public function remoteIsOwnCloud() { |
|
| 294 | - if(defined('PHPUNIT_RUN') || !$this->testRemoteUrl($this->getRemote() . '/status.php')) { |
|
| 295 | - return false; |
|
| 296 | - } |
|
| 297 | - return true; |
|
| 298 | - } |
|
| 299 | - |
|
| 300 | - /** |
|
| 301 | - * @return mixed |
|
| 302 | - * @throws ForbiddenException |
|
| 303 | - * @throws NotFoundException |
|
| 304 | - * @throws \Exception |
|
| 305 | - */ |
|
| 306 | - public function getShareInfo() { |
|
| 307 | - $remote = $this->getRemote(); |
|
| 308 | - $token = $this->getToken(); |
|
| 309 | - $password = $this->getPassword(); |
|
| 310 | - |
|
| 311 | - // If remote is not an ownCloud do not try to get any share info |
|
| 312 | - if(!$this->remoteIsOwnCloud()) { |
|
| 313 | - return ['status' => 'unsupported']; |
|
| 314 | - } |
|
| 315 | - |
|
| 316 | - $url = rtrim($remote, '/') . '/index.php/apps/files_sharing/shareinfo?t=' . $token; |
|
| 317 | - |
|
| 318 | - // TODO: DI |
|
| 319 | - $client = \OC::$server->getHTTPClientService()->newClient(); |
|
| 320 | - try { |
|
| 321 | - $response = $client->post($url, [ |
|
| 322 | - 'body' => ['password' => $password], |
|
| 323 | - 'timeout' => 10, |
|
| 324 | - 'connect_timeout' => 10, |
|
| 325 | - ]); |
|
| 326 | - } catch (\GuzzleHttp\Exception\RequestException $e) { |
|
| 327 | - if ($e->getCode() === Http::STATUS_UNAUTHORIZED || $e->getCode() === Http::STATUS_FORBIDDEN) { |
|
| 328 | - throw new ForbiddenException(); |
|
| 329 | - } |
|
| 330 | - if ($e->getCode() === Http::STATUS_NOT_FOUND) { |
|
| 331 | - throw new NotFoundException(); |
|
| 332 | - } |
|
| 333 | - // throw this to be on the safe side: the share will still be visible |
|
| 334 | - // in the UI in case the failure is intermittent, and the user will |
|
| 335 | - // be able to decide whether to remove it if it's really gone |
|
| 336 | - throw new StorageNotAvailableException(); |
|
| 337 | - } |
|
| 338 | - |
|
| 339 | - return json_decode($response->getBody(), true); |
|
| 340 | - } |
|
| 341 | - |
|
| 342 | - public function getOwner($path) { |
|
| 343 | - return $this->cloudId->getDisplayId(); |
|
| 344 | - } |
|
| 345 | - |
|
| 346 | - public function isSharable($path) { |
|
| 347 | - if (\OCP\Util::isSharingDisabledForUser() || !\OC\Share\Share::isResharingAllowed()) { |
|
| 348 | - return false; |
|
| 349 | - } |
|
| 350 | - return ($this->getPermissions($path) & \OCP\Constants::PERMISSION_SHARE); |
|
| 351 | - } |
|
| 352 | - |
|
| 353 | - public function getPermissions($path) { |
|
| 354 | - $response = $this->propfind($path); |
|
| 355 | - if (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) { |
|
| 356 | - $permissions = $response['{http://open-collaboration-services.org/ns}share-permissions']; |
|
| 357 | - } else { |
|
| 358 | - // use default permission if remote server doesn't provide the share permissions |
|
| 359 | - if ($this->is_dir($path)) { |
|
| 360 | - $permissions = \OCP\Constants::PERMISSION_ALL; |
|
| 361 | - } else { |
|
| 362 | - $permissions = \OCP\Constants::PERMISSION_ALL & ~\OCP\Constants::PERMISSION_CREATE; |
|
| 363 | - } |
|
| 364 | - } |
|
| 365 | - |
|
| 366 | - return $permissions; |
|
| 367 | - } |
|
| 45 | + /** @var ICloudId */ |
|
| 46 | + private $cloudId; |
|
| 47 | + /** @var string */ |
|
| 48 | + private $mountPoint; |
|
| 49 | + /** @var string */ |
|
| 50 | + private $token; |
|
| 51 | + /** @var \OCP\ICacheFactory */ |
|
| 52 | + private $memcacheFactory; |
|
| 53 | + /** @var \OCP\Http\Client\IClientService */ |
|
| 54 | + private $httpClient; |
|
| 55 | + /** @var bool */ |
|
| 56 | + private $updateChecked = false; |
|
| 57 | + |
|
| 58 | + /** |
|
| 59 | + * @var \OCA\Files_Sharing\External\Manager |
|
| 60 | + */ |
|
| 61 | + private $manager; |
|
| 62 | + |
|
| 63 | + public function __construct($options) { |
|
| 64 | + $this->memcacheFactory = \OC::$server->getMemCacheFactory(); |
|
| 65 | + $this->httpClient = $options['HttpClientService']; |
|
| 66 | + |
|
| 67 | + $this->manager = $options['manager']; |
|
| 68 | + $this->cloudId = $options['cloudId']; |
|
| 69 | + $discoveryService = \OC::$server->query(\OCP\OCS\IDiscoveryService::class); |
|
| 70 | + |
|
| 71 | + list($protocol, $remote) = explode('://', $this->cloudId->getRemote()); |
|
| 72 | + if (strpos($remote, '/')) { |
|
| 73 | + list($host, $root) = explode('/', $remote, 2); |
|
| 74 | + } else { |
|
| 75 | + $host = $remote; |
|
| 76 | + $root = ''; |
|
| 77 | + } |
|
| 78 | + $secure = $protocol === 'https'; |
|
| 79 | + $federatedSharingEndpoints = $discoveryService->discover($this->cloudId->getRemote(), 'FEDERATED_SHARING'); |
|
| 80 | + $webDavEndpoint = isset($federatedSharingEndpoints['webdav']) ? $federatedSharingEndpoints['webdav'] : '/public.php/webdav'; |
|
| 81 | + $root = rtrim($root, '/') . $webDavEndpoint; |
|
| 82 | + $this->mountPoint = $options['mountpoint']; |
|
| 83 | + $this->token = $options['token']; |
|
| 84 | + |
|
| 85 | + parent::__construct(array( |
|
| 86 | + 'secure' => $secure, |
|
| 87 | + 'host' => $host, |
|
| 88 | + 'root' => $root, |
|
| 89 | + 'user' => $options['token'], |
|
| 90 | + 'password' => (string)$options['password'] |
|
| 91 | + )); |
|
| 92 | + } |
|
| 93 | + |
|
| 94 | + public function getWatcher($path = '', $storage = null) { |
|
| 95 | + if (!$storage) { |
|
| 96 | + $storage = $this; |
|
| 97 | + } |
|
| 98 | + if (!isset($this->watcher)) { |
|
| 99 | + $this->watcher = new Watcher($storage); |
|
| 100 | + $this->watcher->setPolicy(\OC\Files\Cache\Watcher::CHECK_ONCE); |
|
| 101 | + } |
|
| 102 | + return $this->watcher; |
|
| 103 | + } |
|
| 104 | + |
|
| 105 | + public function getRemoteUser() { |
|
| 106 | + return $this->cloudId->getUser(); |
|
| 107 | + } |
|
| 108 | + |
|
| 109 | + public function getRemote() { |
|
| 110 | + return $this->cloudId->getRemote(); |
|
| 111 | + } |
|
| 112 | + |
|
| 113 | + public function getMountPoint() { |
|
| 114 | + return $this->mountPoint; |
|
| 115 | + } |
|
| 116 | + |
|
| 117 | + public function getToken() { |
|
| 118 | + return $this->token; |
|
| 119 | + } |
|
| 120 | + |
|
| 121 | + public function getPassword() { |
|
| 122 | + return $this->password; |
|
| 123 | + } |
|
| 124 | + |
|
| 125 | + /** |
|
| 126 | + * @brief get id of the mount point |
|
| 127 | + * @return string |
|
| 128 | + */ |
|
| 129 | + public function getId() { |
|
| 130 | + return 'shared::' . md5($this->token . '@' . $this->getRemote()); |
|
| 131 | + } |
|
| 132 | + |
|
| 133 | + public function getCache($path = '', $storage = null) { |
|
| 134 | + if (is_null($this->cache)) { |
|
| 135 | + $this->cache = new Cache($this, $this->cloudId); |
|
| 136 | + } |
|
| 137 | + return $this->cache; |
|
| 138 | + } |
|
| 139 | + |
|
| 140 | + /** |
|
| 141 | + * @param string $path |
|
| 142 | + * @param \OC\Files\Storage\Storage $storage |
|
| 143 | + * @return \OCA\Files_Sharing\External\Scanner |
|
| 144 | + */ |
|
| 145 | + public function getScanner($path = '', $storage = null) { |
|
| 146 | + if (!$storage) { |
|
| 147 | + $storage = $this; |
|
| 148 | + } |
|
| 149 | + if (!isset($this->scanner)) { |
|
| 150 | + $this->scanner = new Scanner($storage); |
|
| 151 | + } |
|
| 152 | + return $this->scanner; |
|
| 153 | + } |
|
| 154 | + |
|
| 155 | + /** |
|
| 156 | + * check if a file or folder has been updated since $time |
|
| 157 | + * |
|
| 158 | + * @param string $path |
|
| 159 | + * @param int $time |
|
| 160 | + * @throws \OCP\Files\StorageNotAvailableException |
|
| 161 | + * @throws \OCP\Files\StorageInvalidException |
|
| 162 | + * @return bool |
|
| 163 | + */ |
|
| 164 | + public function hasUpdated($path, $time) { |
|
| 165 | + // since for owncloud webdav servers we can rely on etag propagation we only need to check the root of the storage |
|
| 166 | + // because of that we only do one check for the entire storage per request |
|
| 167 | + if ($this->updateChecked) { |
|
| 168 | + return false; |
|
| 169 | + } |
|
| 170 | + $this->updateChecked = true; |
|
| 171 | + try { |
|
| 172 | + return parent::hasUpdated('', $time); |
|
| 173 | + } catch (StorageInvalidException $e) { |
|
| 174 | + // check if it needs to be removed |
|
| 175 | + $this->checkStorageAvailability(); |
|
| 176 | + throw $e; |
|
| 177 | + } catch (StorageNotAvailableException $e) { |
|
| 178 | + // check if it needs to be removed or just temp unavailable |
|
| 179 | + $this->checkStorageAvailability(); |
|
| 180 | + throw $e; |
|
| 181 | + } |
|
| 182 | + } |
|
| 183 | + |
|
| 184 | + public function test() { |
|
| 185 | + try { |
|
| 186 | + return parent::test(); |
|
| 187 | + } catch (StorageInvalidException $e) { |
|
| 188 | + // check if it needs to be removed |
|
| 189 | + $this->checkStorageAvailability(); |
|
| 190 | + throw $e; |
|
| 191 | + } catch (StorageNotAvailableException $e) { |
|
| 192 | + // check if it needs to be removed or just temp unavailable |
|
| 193 | + $this->checkStorageAvailability(); |
|
| 194 | + throw $e; |
|
| 195 | + } |
|
| 196 | + } |
|
| 197 | + |
|
| 198 | + /** |
|
| 199 | + * Check whether this storage is permanently or temporarily |
|
| 200 | + * unavailable |
|
| 201 | + * |
|
| 202 | + * @throws \OCP\Files\StorageNotAvailableException |
|
| 203 | + * @throws \OCP\Files\StorageInvalidException |
|
| 204 | + */ |
|
| 205 | + public function checkStorageAvailability() { |
|
| 206 | + // see if we can find out why the share is unavailable |
|
| 207 | + try { |
|
| 208 | + $this->getShareInfo(); |
|
| 209 | + } catch (NotFoundException $e) { |
|
| 210 | + // a 404 can either mean that the share no longer exists or there is no Nextcloud on the remote |
|
| 211 | + if ($this->testRemote()) { |
|
| 212 | + // valid Nextcloud instance means that the public share no longer exists |
|
| 213 | + // since this is permanent (re-sharing the file will create a new token) |
|
| 214 | + // we remove the invalid storage |
|
| 215 | + $this->manager->removeShare($this->mountPoint); |
|
| 216 | + $this->manager->getMountManager()->removeMount($this->mountPoint); |
|
| 217 | + throw new StorageInvalidException(); |
|
| 218 | + } else { |
|
| 219 | + // Nextcloud instance is gone, likely to be a temporary server configuration error |
|
| 220 | + throw new StorageNotAvailableException(); |
|
| 221 | + } |
|
| 222 | + } catch (ForbiddenException $e) { |
|
| 223 | + // auth error, remove share for now (provide a dialog in the future) |
|
| 224 | + $this->manager->removeShare($this->mountPoint); |
|
| 225 | + $this->manager->getMountManager()->removeMount($this->mountPoint); |
|
| 226 | + throw new StorageInvalidException(); |
|
| 227 | + } catch (\GuzzleHttp\Exception\ConnectException $e) { |
|
| 228 | + throw new StorageNotAvailableException(); |
|
| 229 | + } catch (\GuzzleHttp\Exception\RequestException $e) { |
|
| 230 | + throw new StorageNotAvailableException(); |
|
| 231 | + } catch (\Exception $e) { |
|
| 232 | + throw $e; |
|
| 233 | + } |
|
| 234 | + } |
|
| 235 | + |
|
| 236 | + public function file_exists($path) { |
|
| 237 | + if ($path === '') { |
|
| 238 | + return true; |
|
| 239 | + } else { |
|
| 240 | + return parent::file_exists($path); |
|
| 241 | + } |
|
| 242 | + } |
|
| 243 | + |
|
| 244 | + /** |
|
| 245 | + * check if the configured remote is a valid federated share provider |
|
| 246 | + * |
|
| 247 | + * @return bool |
|
| 248 | + */ |
|
| 249 | + protected function testRemote() { |
|
| 250 | + try { |
|
| 251 | + return $this->testRemoteUrl($this->getRemote() . '/ocs-provider/index.php') |
|
| 252 | + || $this->testRemoteUrl($this->getRemote() . '/ocs-provider/') |
|
| 253 | + || $this->testRemoteUrl($this->getRemote() . '/status.php'); |
|
| 254 | + } catch (\Exception $e) { |
|
| 255 | + return false; |
|
| 256 | + } |
|
| 257 | + } |
|
| 258 | + |
|
| 259 | + /** |
|
| 260 | + * @param string $url |
|
| 261 | + * @return bool |
|
| 262 | + */ |
|
| 263 | + private function testRemoteUrl($url) { |
|
| 264 | + $cache = $this->memcacheFactory->createDistributed('files_sharing_remote_url'); |
|
| 265 | + if($cache->hasKey($url)) { |
|
| 266 | + return (bool)$cache->get($url); |
|
| 267 | + } |
|
| 268 | + |
|
| 269 | + $client = $this->httpClient->newClient(); |
|
| 270 | + try { |
|
| 271 | + $result = $client->get($url, [ |
|
| 272 | + 'timeout' => 10, |
|
| 273 | + 'connect_timeout' => 10, |
|
| 274 | + ])->getBody(); |
|
| 275 | + $data = json_decode($result); |
|
| 276 | + $returnValue = (is_object($data) && !empty($data->version)); |
|
| 277 | + } catch (ConnectException $e) { |
|
| 278 | + $returnValue = false; |
|
| 279 | + } catch (ClientException $e) { |
|
| 280 | + $returnValue = false; |
|
| 281 | + } |
|
| 282 | + |
|
| 283 | + $cache->set($url, $returnValue, 60*60*24); |
|
| 284 | + return $returnValue; |
|
| 285 | + } |
|
| 286 | + |
|
| 287 | + /** |
|
| 288 | + * Whether the remote is an ownCloud/Nextcloud, used since some sharing features are not |
|
| 289 | + * standardized. Let's use this to detect whether to use it. |
|
| 290 | + * |
|
| 291 | + * @return bool |
|
| 292 | + */ |
|
| 293 | + public function remoteIsOwnCloud() { |
|
| 294 | + if(defined('PHPUNIT_RUN') || !$this->testRemoteUrl($this->getRemote() . '/status.php')) { |
|
| 295 | + return false; |
|
| 296 | + } |
|
| 297 | + return true; |
|
| 298 | + } |
|
| 299 | + |
|
| 300 | + /** |
|
| 301 | + * @return mixed |
|
| 302 | + * @throws ForbiddenException |
|
| 303 | + * @throws NotFoundException |
|
| 304 | + * @throws \Exception |
|
| 305 | + */ |
|
| 306 | + public function getShareInfo() { |
|
| 307 | + $remote = $this->getRemote(); |
|
| 308 | + $token = $this->getToken(); |
|
| 309 | + $password = $this->getPassword(); |
|
| 310 | + |
|
| 311 | + // If remote is not an ownCloud do not try to get any share info |
|
| 312 | + if(!$this->remoteIsOwnCloud()) { |
|
| 313 | + return ['status' => 'unsupported']; |
|
| 314 | + } |
|
| 315 | + |
|
| 316 | + $url = rtrim($remote, '/') . '/index.php/apps/files_sharing/shareinfo?t=' . $token; |
|
| 317 | + |
|
| 318 | + // TODO: DI |
|
| 319 | + $client = \OC::$server->getHTTPClientService()->newClient(); |
|
| 320 | + try { |
|
| 321 | + $response = $client->post($url, [ |
|
| 322 | + 'body' => ['password' => $password], |
|
| 323 | + 'timeout' => 10, |
|
| 324 | + 'connect_timeout' => 10, |
|
| 325 | + ]); |
|
| 326 | + } catch (\GuzzleHttp\Exception\RequestException $e) { |
|
| 327 | + if ($e->getCode() === Http::STATUS_UNAUTHORIZED || $e->getCode() === Http::STATUS_FORBIDDEN) { |
|
| 328 | + throw new ForbiddenException(); |
|
| 329 | + } |
|
| 330 | + if ($e->getCode() === Http::STATUS_NOT_FOUND) { |
|
| 331 | + throw new NotFoundException(); |
|
| 332 | + } |
|
| 333 | + // throw this to be on the safe side: the share will still be visible |
|
| 334 | + // in the UI in case the failure is intermittent, and the user will |
|
| 335 | + // be able to decide whether to remove it if it's really gone |
|
| 336 | + throw new StorageNotAvailableException(); |
|
| 337 | + } |
|
| 338 | + |
|
| 339 | + return json_decode($response->getBody(), true); |
|
| 340 | + } |
|
| 341 | + |
|
| 342 | + public function getOwner($path) { |
|
| 343 | + return $this->cloudId->getDisplayId(); |
|
| 344 | + } |
|
| 345 | + |
|
| 346 | + public function isSharable($path) { |
|
| 347 | + if (\OCP\Util::isSharingDisabledForUser() || !\OC\Share\Share::isResharingAllowed()) { |
|
| 348 | + return false; |
|
| 349 | + } |
|
| 350 | + return ($this->getPermissions($path) & \OCP\Constants::PERMISSION_SHARE); |
|
| 351 | + } |
|
| 352 | + |
|
| 353 | + public function getPermissions($path) { |
|
| 354 | + $response = $this->propfind($path); |
|
| 355 | + if (isset($response['{http://open-collaboration-services.org/ns}share-permissions'])) { |
|
| 356 | + $permissions = $response['{http://open-collaboration-services.org/ns}share-permissions']; |
|
| 357 | + } else { |
|
| 358 | + // use default permission if remote server doesn't provide the share permissions |
|
| 359 | + if ($this->is_dir($path)) { |
|
| 360 | + $permissions = \OCP\Constants::PERMISSION_ALL; |
|
| 361 | + } else { |
|
| 362 | + $permissions = \OCP\Constants::PERMISSION_ALL & ~\OCP\Constants::PERMISSION_CREATE; |
|
| 363 | + } |
|
| 364 | + } |
|
| 365 | + |
|
| 366 | + return $permissions; |
|
| 367 | + } |
|
| 368 | 368 | |
| 369 | 369 | } |
@@ -78,7 +78,7 @@ discard block |
||
| 78 | 78 | $secure = $protocol === 'https'; |
| 79 | 79 | $federatedSharingEndpoints = $discoveryService->discover($this->cloudId->getRemote(), 'FEDERATED_SHARING'); |
| 80 | 80 | $webDavEndpoint = isset($federatedSharingEndpoints['webdav']) ? $federatedSharingEndpoints['webdav'] : '/public.php/webdav'; |
| 81 | - $root = rtrim($root, '/') . $webDavEndpoint; |
|
| 81 | + $root = rtrim($root, '/').$webDavEndpoint; |
|
| 82 | 82 | $this->mountPoint = $options['mountpoint']; |
| 83 | 83 | $this->token = $options['token']; |
| 84 | 84 | |
@@ -87,7 +87,7 @@ discard block |
||
| 87 | 87 | 'host' => $host, |
| 88 | 88 | 'root' => $root, |
| 89 | 89 | 'user' => $options['token'], |
| 90 | - 'password' => (string)$options['password'] |
|
| 90 | + 'password' => (string) $options['password'] |
|
| 91 | 91 | )); |
| 92 | 92 | } |
| 93 | 93 | |
@@ -127,7 +127,7 @@ discard block |
||
| 127 | 127 | * @return string |
| 128 | 128 | */ |
| 129 | 129 | public function getId() { |
| 130 | - return 'shared::' . md5($this->token . '@' . $this->getRemote()); |
|
| 130 | + return 'shared::'.md5($this->token.'@'.$this->getRemote()); |
|
| 131 | 131 | } |
| 132 | 132 | |
| 133 | 133 | public function getCache($path = '', $storage = null) { |
@@ -248,9 +248,9 @@ discard block |
||
| 248 | 248 | */ |
| 249 | 249 | protected function testRemote() { |
| 250 | 250 | try { |
| 251 | - return $this->testRemoteUrl($this->getRemote() . '/ocs-provider/index.php') |
|
| 252 | - || $this->testRemoteUrl($this->getRemote() . '/ocs-provider/') |
|
| 253 | - || $this->testRemoteUrl($this->getRemote() . '/status.php'); |
|
| 251 | + return $this->testRemoteUrl($this->getRemote().'/ocs-provider/index.php') |
|
| 252 | + || $this->testRemoteUrl($this->getRemote().'/ocs-provider/') |
|
| 253 | + || $this->testRemoteUrl($this->getRemote().'/status.php'); |
|
| 254 | 254 | } catch (\Exception $e) { |
| 255 | 255 | return false; |
| 256 | 256 | } |
@@ -262,8 +262,8 @@ discard block |
||
| 262 | 262 | */ |
| 263 | 263 | private function testRemoteUrl($url) { |
| 264 | 264 | $cache = $this->memcacheFactory->createDistributed('files_sharing_remote_url'); |
| 265 | - if($cache->hasKey($url)) { |
|
| 266 | - return (bool)$cache->get($url); |
|
| 265 | + if ($cache->hasKey($url)) { |
|
| 266 | + return (bool) $cache->get($url); |
|
| 267 | 267 | } |
| 268 | 268 | |
| 269 | 269 | $client = $this->httpClient->newClient(); |
@@ -280,7 +280,7 @@ discard block |
||
| 280 | 280 | $returnValue = false; |
| 281 | 281 | } |
| 282 | 282 | |
| 283 | - $cache->set($url, $returnValue, 60*60*24); |
|
| 283 | + $cache->set($url, $returnValue, 60 * 60 * 24); |
|
| 284 | 284 | return $returnValue; |
| 285 | 285 | } |
| 286 | 286 | |
@@ -291,7 +291,7 @@ discard block |
||
| 291 | 291 | * @return bool |
| 292 | 292 | */ |
| 293 | 293 | public function remoteIsOwnCloud() { |
| 294 | - if(defined('PHPUNIT_RUN') || !$this->testRemoteUrl($this->getRemote() . '/status.php')) { |
|
| 294 | + if (defined('PHPUNIT_RUN') || !$this->testRemoteUrl($this->getRemote().'/status.php')) { |
|
| 295 | 295 | return false; |
| 296 | 296 | } |
| 297 | 297 | return true; |
@@ -309,11 +309,11 @@ discard block |
||
| 309 | 309 | $password = $this->getPassword(); |
| 310 | 310 | |
| 311 | 311 | // If remote is not an ownCloud do not try to get any share info |
| 312 | - if(!$this->remoteIsOwnCloud()) { |
|
| 312 | + if (!$this->remoteIsOwnCloud()) { |
|
| 313 | 313 | return ['status' => 'unsupported']; |
| 314 | 314 | } |
| 315 | 315 | |
| 316 | - $url = rtrim($remote, '/') . '/index.php/apps/files_sharing/shareinfo?t=' . $token; |
|
| 316 | + $url = rtrim($remote, '/').'/index.php/apps/files_sharing/shareinfo?t='.$token; |
|
| 317 | 317 | |
| 318 | 318 | // TODO: DI |
| 319 | 319 | $client = \OC::$server->getHTTPClientService()->newClient(); |
@@ -44,342 +44,342 @@ |
||
| 44 | 44 | |
| 45 | 45 | class ThemingDefaults extends \OC_Defaults { |
| 46 | 46 | |
| 47 | - /** @var IConfig */ |
|
| 48 | - private $config; |
|
| 49 | - /** @var IL10N */ |
|
| 50 | - private $l; |
|
| 51 | - /** @var IURLGenerator */ |
|
| 52 | - private $urlGenerator; |
|
| 53 | - /** @var IAppData */ |
|
| 54 | - private $appData; |
|
| 55 | - /** @var ICacheFactory */ |
|
| 56 | - private $cacheFactory; |
|
| 57 | - /** @var Util */ |
|
| 58 | - private $util; |
|
| 59 | - /** @var IAppManager */ |
|
| 60 | - private $appManager; |
|
| 61 | - /** @var string */ |
|
| 62 | - private $name; |
|
| 63 | - /** @var string */ |
|
| 64 | - private $title; |
|
| 65 | - /** @var string */ |
|
| 66 | - private $entity; |
|
| 67 | - /** @var string */ |
|
| 68 | - private $url; |
|
| 69 | - /** @var string */ |
|
| 70 | - private $slogan; |
|
| 71 | - /** @var string */ |
|
| 72 | - private $color; |
|
| 73 | - |
|
| 74 | - /** @var string */ |
|
| 75 | - private $iTunesAppId; |
|
| 76 | - /** @var string */ |
|
| 77 | - private $iOSClientUrl; |
|
| 78 | - /** @var string */ |
|
| 79 | - private $AndroidClientUrl; |
|
| 80 | - |
|
| 81 | - /** |
|
| 82 | - * ThemingDefaults constructor. |
|
| 83 | - * |
|
| 84 | - * @param IConfig $config |
|
| 85 | - * @param IL10N $l |
|
| 86 | - * @param IURLGenerator $urlGenerator |
|
| 87 | - * @param \OC_Defaults $defaults |
|
| 88 | - * @param IAppData $appData |
|
| 89 | - * @param ICacheFactory $cacheFactory |
|
| 90 | - * @param Util $util |
|
| 91 | - * @param IAppManager $appManager |
|
| 92 | - */ |
|
| 93 | - public function __construct(IConfig $config, |
|
| 94 | - IL10N $l, |
|
| 95 | - IURLGenerator $urlGenerator, |
|
| 96 | - IAppData $appData, |
|
| 97 | - ICacheFactory $cacheFactory, |
|
| 98 | - Util $util, |
|
| 99 | - IAppManager $appManager |
|
| 100 | - ) { |
|
| 101 | - parent::__construct(); |
|
| 102 | - $this->config = $config; |
|
| 103 | - $this->l = $l; |
|
| 104 | - $this->urlGenerator = $urlGenerator; |
|
| 105 | - $this->appData = $appData; |
|
| 106 | - $this->cacheFactory = $cacheFactory; |
|
| 107 | - $this->util = $util; |
|
| 108 | - $this->appManager = $appManager; |
|
| 109 | - |
|
| 110 | - $this->name = parent::getName(); |
|
| 111 | - $this->title = parent::getTitle(); |
|
| 112 | - $this->entity = parent::getEntity(); |
|
| 113 | - $this->url = parent::getBaseUrl(); |
|
| 114 | - $this->slogan = parent::getSlogan(); |
|
| 115 | - $this->color = parent::getColorPrimary(); |
|
| 116 | - $this->iTunesAppId = parent::getiTunesAppId(); |
|
| 117 | - $this->iOSClientUrl = parent::getiOSClientUrl(); |
|
| 118 | - $this->AndroidClientUrl = parent::getAndroidClientUrl(); |
|
| 119 | - } |
|
| 120 | - |
|
| 121 | - public function getName() { |
|
| 122 | - return strip_tags($this->config->getAppValue('theming', 'name', $this->name)); |
|
| 123 | - } |
|
| 124 | - |
|
| 125 | - public function getHTMLName() { |
|
| 126 | - return $this->config->getAppValue('theming', 'name', $this->name); |
|
| 127 | - } |
|
| 128 | - |
|
| 129 | - public function getTitle() { |
|
| 130 | - return strip_tags($this->config->getAppValue('theming', 'name', $this->title)); |
|
| 131 | - } |
|
| 132 | - |
|
| 133 | - public function getEntity() { |
|
| 134 | - return strip_tags($this->config->getAppValue('theming', 'name', $this->entity)); |
|
| 135 | - } |
|
| 136 | - |
|
| 137 | - public function getBaseUrl() { |
|
| 138 | - return $this->config->getAppValue('theming', 'url', $this->url); |
|
| 139 | - } |
|
| 140 | - |
|
| 141 | - public function getSlogan() { |
|
| 142 | - return \OCP\Util::sanitizeHTML($this->config->getAppValue('theming', 'slogan', $this->slogan)); |
|
| 143 | - } |
|
| 144 | - |
|
| 145 | - public function getShortFooter() { |
|
| 146 | - $slogan = $this->getSlogan(); |
|
| 147 | - $footer = '<a href="'. $this->getBaseUrl() . '" target="_blank"' . |
|
| 148 | - ' rel="noreferrer noopener">' .$this->getEntity() . '</a>'. |
|
| 149 | - ($slogan !== '' ? ' – ' . $slogan : ''); |
|
| 150 | - |
|
| 151 | - return $footer; |
|
| 152 | - } |
|
| 153 | - |
|
| 154 | - /** |
|
| 155 | - * Color that is used for the header as well as for mail headers |
|
| 156 | - * |
|
| 157 | - * @return string |
|
| 158 | - */ |
|
| 159 | - public function getColorPrimary() { |
|
| 160 | - return $this->config->getAppValue('theming', 'color', $this->color); |
|
| 161 | - } |
|
| 162 | - |
|
| 163 | - /** |
|
| 164 | - * Themed logo url |
|
| 165 | - * |
|
| 166 | - * @param bool $useSvg Whether to point to the SVG image or a fallback |
|
| 167 | - * @return string |
|
| 168 | - */ |
|
| 169 | - public function getLogo($useSvg = true) { |
|
| 170 | - $logo = $this->config->getAppValue('theming', 'logoMime', false); |
|
| 171 | - |
|
| 172 | - $logoExists = true; |
|
| 173 | - try { |
|
| 174 | - $this->appData->getFolder('images')->getFile('logo'); |
|
| 175 | - } catch (\Exception $e) { |
|
| 176 | - $logoExists = false; |
|
| 177 | - } |
|
| 178 | - |
|
| 179 | - $cacheBusterCounter = $this->config->getAppValue('theming', 'cachebuster', '0'); |
|
| 180 | - |
|
| 181 | - if(!$logo || !$logoExists) { |
|
| 182 | - if($useSvg) { |
|
| 183 | - $logo = $this->urlGenerator->imagePath('core', 'logo.svg'); |
|
| 184 | - } else { |
|
| 185 | - $logo = $this->urlGenerator->imagePath('core', 'logo.png'); |
|
| 186 | - } |
|
| 187 | - return $logo . '?v=' . $cacheBusterCounter; |
|
| 188 | - } |
|
| 189 | - |
|
| 190 | - return $this->urlGenerator->linkToRoute('theming.Theming.getLogo') . '?v=' . $cacheBusterCounter; |
|
| 191 | - } |
|
| 192 | - |
|
| 193 | - /** |
|
| 194 | - * Themed background image url |
|
| 195 | - * |
|
| 196 | - * @return string |
|
| 197 | - */ |
|
| 198 | - public function getBackground() { |
|
| 199 | - $backgroundLogo = $this->config->getAppValue('theming', 'backgroundMime',false); |
|
| 200 | - |
|
| 201 | - $backgroundExists = true; |
|
| 202 | - try { |
|
| 203 | - $this->appData->getFolder('images')->getFile('background'); |
|
| 204 | - } catch (\Exception $e) { |
|
| 205 | - $backgroundExists = false; |
|
| 206 | - } |
|
| 207 | - |
|
| 208 | - $cacheBusterCounter = $this->config->getAppValue('theming', 'cachebuster', '0'); |
|
| 209 | - |
|
| 210 | - if(!$backgroundLogo || !$backgroundExists) { |
|
| 211 | - return $this->urlGenerator->imagePath('core','background.png') . '?v=' . $cacheBusterCounter; |
|
| 212 | - } |
|
| 213 | - |
|
| 214 | - return $this->urlGenerator->linkToRoute('theming.Theming.getLoginBackground') . '?v=' . $cacheBusterCounter; |
|
| 215 | - } |
|
| 216 | - |
|
| 217 | - /** |
|
| 218 | - * @return string |
|
| 219 | - */ |
|
| 220 | - public function getiTunesAppId() { |
|
| 221 | - return $this->config->getAppValue('theming', 'iTunesAppId', $this->iTunesAppId); |
|
| 222 | - } |
|
| 223 | - |
|
| 224 | - /** |
|
| 225 | - * @return string |
|
| 226 | - */ |
|
| 227 | - public function getiOSClientUrl() { |
|
| 228 | - return $this->config->getAppValue('theming', 'iOSClientUrl', $this->iOSClientUrl); |
|
| 229 | - } |
|
| 230 | - |
|
| 231 | - /** |
|
| 232 | - * @return string |
|
| 233 | - */ |
|
| 234 | - public function getAndroidClientUrl() { |
|
| 235 | - return $this->config->getAppValue('theming', 'AndroidClientUrl', $this->AndroidClientUrl); |
|
| 236 | - } |
|
| 237 | - |
|
| 238 | - |
|
| 239 | - /** |
|
| 240 | - * @return array scss variables to overwrite |
|
| 241 | - */ |
|
| 242 | - public function getScssVariables() { |
|
| 243 | - $cache = $this->cacheFactory->createDistributed('theming'); |
|
| 244 | - if ($value = $cache->get('getScssVariables')) { |
|
| 245 | - return $value; |
|
| 246 | - } |
|
| 247 | - |
|
| 248 | - $variables = [ |
|
| 249 | - 'theming-cachebuster' => "'" . $this->config->getAppValue('theming', 'cachebuster', '0') . "'", |
|
| 250 | - 'theming-logo-mime' => "'" . $this->config->getAppValue('theming', 'logoMime', '') . "'", |
|
| 251 | - 'theming-background-mime' => "'" . $this->config->getAppValue('theming', 'backgroundMime', '') . "'" |
|
| 252 | - ]; |
|
| 253 | - |
|
| 254 | - $variables['image-logo'] = "'".$this->urlGenerator->getAbsoluteURL($this->getLogo())."'"; |
|
| 255 | - $variables['image-login-background'] = "'".$this->urlGenerator->getAbsoluteURL($this->getBackground())."'"; |
|
| 256 | - $variables['image-login-plain'] = 'false'; |
|
| 257 | - |
|
| 258 | - if ($this->config->getAppValue('theming', 'color', null) !== null) { |
|
| 259 | - $variables['color-primary'] = $this->getColorPrimary(); |
|
| 260 | - $variables['color-primary-text'] = $this->getTextColorPrimary(); |
|
| 261 | - $variables['color-primary-element'] = $this->util->elementColor($this->getColorPrimary()); |
|
| 262 | - } |
|
| 263 | - |
|
| 264 | - if ($this->config->getAppValue('theming', 'backgroundMime', null) === 'backgroundColor') { |
|
| 265 | - $variables['image-login-plain'] = 'true'; |
|
| 266 | - } |
|
| 267 | - $cache->set('getScssVariables', $variables); |
|
| 268 | - return $variables; |
|
| 269 | - } |
|
| 270 | - |
|
| 271 | - /** |
|
| 272 | - * Check if the image should be replaced by the theming app |
|
| 273 | - * and return the new image location then |
|
| 274 | - * |
|
| 275 | - * @param string $app name of the app |
|
| 276 | - * @param string $image filename of the image |
|
| 277 | - * @return bool|string false if image should not replaced, otherwise the location of the image |
|
| 278 | - */ |
|
| 279 | - public function replaceImagePath($app, $image) { |
|
| 280 | - if($app==='') { |
|
| 281 | - $app = 'core'; |
|
| 282 | - } |
|
| 283 | - $cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0'); |
|
| 284 | - |
|
| 285 | - if ($image === 'favicon.ico' && $this->shouldReplaceIcons()) { |
|
| 286 | - return $this->urlGenerator->linkToRoute('theming.Icon.getFavicon', ['app' => $app]) . '?v=' . $cacheBusterValue; |
|
| 287 | - } |
|
| 288 | - if ($image === 'favicon-touch.png' && $this->shouldReplaceIcons()) { |
|
| 289 | - return $this->urlGenerator->linkToRoute('theming.Icon.getTouchIcon', ['app' => $app]) . '?v=' . $cacheBusterValue; |
|
| 290 | - } |
|
| 291 | - if ($image === 'manifest.json') { |
|
| 292 | - try { |
|
| 293 | - $appPath = $this->appManager->getAppPath($app); |
|
| 294 | - if (file_exists($appPath . '/img/manifest.json')) { |
|
| 295 | - return false; |
|
| 296 | - } |
|
| 297 | - } catch (AppPathNotFoundException $e) {} |
|
| 298 | - return $this->urlGenerator->linkToRoute('theming.Theming.getManifest') . '?v=' . $cacheBusterValue; |
|
| 299 | - } |
|
| 300 | - return false; |
|
| 301 | - } |
|
| 302 | - |
|
| 303 | - /** |
|
| 304 | - * Check if Imagemagick is enabled and if SVG is supported |
|
| 305 | - * otherwise we can't render custom icons |
|
| 306 | - * |
|
| 307 | - * @return bool |
|
| 308 | - */ |
|
| 309 | - public function shouldReplaceIcons() { |
|
| 310 | - $cache = $this->cacheFactory->createDistributed('theming'); |
|
| 311 | - if($value = $cache->get('shouldReplaceIcons')) { |
|
| 312 | - return (bool)$value; |
|
| 313 | - } |
|
| 314 | - $value = false; |
|
| 315 | - if(extension_loaded('imagick')) { |
|
| 316 | - $checkImagick = new \Imagick(); |
|
| 317 | - if (count($checkImagick->queryFormats('SVG')) >= 1) { |
|
| 318 | - $value = true; |
|
| 319 | - } |
|
| 320 | - $checkImagick->clear(); |
|
| 321 | - } |
|
| 322 | - $cache->set('shouldReplaceIcons', $value); |
|
| 323 | - return $value; |
|
| 324 | - } |
|
| 325 | - |
|
| 326 | - /** |
|
| 327 | - * Increases the cache buster key |
|
| 328 | - */ |
|
| 329 | - private function increaseCacheBuster() { |
|
| 330 | - $cacheBusterKey = $this->config->getAppValue('theming', 'cachebuster', '0'); |
|
| 331 | - $this->config->setAppValue('theming', 'cachebuster', (int)$cacheBusterKey+1); |
|
| 332 | - $this->cacheFactory->createDistributed('theming')->clear('getScssVariables'); |
|
| 333 | - } |
|
| 334 | - |
|
| 335 | - /** |
|
| 336 | - * Update setting in the database |
|
| 337 | - * |
|
| 338 | - * @param string $setting |
|
| 339 | - * @param string $value |
|
| 340 | - */ |
|
| 341 | - public function set($setting, $value) { |
|
| 342 | - $this->config->setAppValue('theming', $setting, $value); |
|
| 343 | - $this->increaseCacheBuster(); |
|
| 344 | - } |
|
| 345 | - |
|
| 346 | - /** |
|
| 347 | - * Revert settings to the default value |
|
| 348 | - * |
|
| 349 | - * @param string $setting setting which should be reverted |
|
| 350 | - * @return string default value |
|
| 351 | - */ |
|
| 352 | - public function undo($setting) { |
|
| 353 | - $this->config->deleteAppValue('theming', $setting); |
|
| 354 | - $this->increaseCacheBuster(); |
|
| 355 | - |
|
| 356 | - switch ($setting) { |
|
| 357 | - case 'name': |
|
| 358 | - $returnValue = $this->getEntity(); |
|
| 359 | - break; |
|
| 360 | - case 'url': |
|
| 361 | - $returnValue = $this->getBaseUrl(); |
|
| 362 | - break; |
|
| 363 | - case 'slogan': |
|
| 364 | - $returnValue = $this->getSlogan(); |
|
| 365 | - break; |
|
| 366 | - case 'color': |
|
| 367 | - $returnValue = $this->getColorPrimary(); |
|
| 368 | - break; |
|
| 369 | - default: |
|
| 370 | - $returnValue = ''; |
|
| 371 | - break; |
|
| 372 | - } |
|
| 373 | - |
|
| 374 | - return $returnValue; |
|
| 375 | - } |
|
| 376 | - |
|
| 377 | - /** |
|
| 378 | - * Color of text in the header and primary buttons |
|
| 379 | - * |
|
| 380 | - * @return string |
|
| 381 | - */ |
|
| 382 | - public function getTextColorPrimary() { |
|
| 383 | - return $this->util->invertTextColor($this->getColorPrimary()) ? '#000000' : '#ffffff'; |
|
| 384 | - } |
|
| 47 | + /** @var IConfig */ |
|
| 48 | + private $config; |
|
| 49 | + /** @var IL10N */ |
|
| 50 | + private $l; |
|
| 51 | + /** @var IURLGenerator */ |
|
| 52 | + private $urlGenerator; |
|
| 53 | + /** @var IAppData */ |
|
| 54 | + private $appData; |
|
| 55 | + /** @var ICacheFactory */ |
|
| 56 | + private $cacheFactory; |
|
| 57 | + /** @var Util */ |
|
| 58 | + private $util; |
|
| 59 | + /** @var IAppManager */ |
|
| 60 | + private $appManager; |
|
| 61 | + /** @var string */ |
|
| 62 | + private $name; |
|
| 63 | + /** @var string */ |
|
| 64 | + private $title; |
|
| 65 | + /** @var string */ |
|
| 66 | + private $entity; |
|
| 67 | + /** @var string */ |
|
| 68 | + private $url; |
|
| 69 | + /** @var string */ |
|
| 70 | + private $slogan; |
|
| 71 | + /** @var string */ |
|
| 72 | + private $color; |
|
| 73 | + |
|
| 74 | + /** @var string */ |
|
| 75 | + private $iTunesAppId; |
|
| 76 | + /** @var string */ |
|
| 77 | + private $iOSClientUrl; |
|
| 78 | + /** @var string */ |
|
| 79 | + private $AndroidClientUrl; |
|
| 80 | + |
|
| 81 | + /** |
|
| 82 | + * ThemingDefaults constructor. |
|
| 83 | + * |
|
| 84 | + * @param IConfig $config |
|
| 85 | + * @param IL10N $l |
|
| 86 | + * @param IURLGenerator $urlGenerator |
|
| 87 | + * @param \OC_Defaults $defaults |
|
| 88 | + * @param IAppData $appData |
|
| 89 | + * @param ICacheFactory $cacheFactory |
|
| 90 | + * @param Util $util |
|
| 91 | + * @param IAppManager $appManager |
|
| 92 | + */ |
|
| 93 | + public function __construct(IConfig $config, |
|
| 94 | + IL10N $l, |
|
| 95 | + IURLGenerator $urlGenerator, |
|
| 96 | + IAppData $appData, |
|
| 97 | + ICacheFactory $cacheFactory, |
|
| 98 | + Util $util, |
|
| 99 | + IAppManager $appManager |
|
| 100 | + ) { |
|
| 101 | + parent::__construct(); |
|
| 102 | + $this->config = $config; |
|
| 103 | + $this->l = $l; |
|
| 104 | + $this->urlGenerator = $urlGenerator; |
|
| 105 | + $this->appData = $appData; |
|
| 106 | + $this->cacheFactory = $cacheFactory; |
|
| 107 | + $this->util = $util; |
|
| 108 | + $this->appManager = $appManager; |
|
| 109 | + |
|
| 110 | + $this->name = parent::getName(); |
|
| 111 | + $this->title = parent::getTitle(); |
|
| 112 | + $this->entity = parent::getEntity(); |
|
| 113 | + $this->url = parent::getBaseUrl(); |
|
| 114 | + $this->slogan = parent::getSlogan(); |
|
| 115 | + $this->color = parent::getColorPrimary(); |
|
| 116 | + $this->iTunesAppId = parent::getiTunesAppId(); |
|
| 117 | + $this->iOSClientUrl = parent::getiOSClientUrl(); |
|
| 118 | + $this->AndroidClientUrl = parent::getAndroidClientUrl(); |
|
| 119 | + } |
|
| 120 | + |
|
| 121 | + public function getName() { |
|
| 122 | + return strip_tags($this->config->getAppValue('theming', 'name', $this->name)); |
|
| 123 | + } |
|
| 124 | + |
|
| 125 | + public function getHTMLName() { |
|
| 126 | + return $this->config->getAppValue('theming', 'name', $this->name); |
|
| 127 | + } |
|
| 128 | + |
|
| 129 | + public function getTitle() { |
|
| 130 | + return strip_tags($this->config->getAppValue('theming', 'name', $this->title)); |
|
| 131 | + } |
|
| 132 | + |
|
| 133 | + public function getEntity() { |
|
| 134 | + return strip_tags($this->config->getAppValue('theming', 'name', $this->entity)); |
|
| 135 | + } |
|
| 136 | + |
|
| 137 | + public function getBaseUrl() { |
|
| 138 | + return $this->config->getAppValue('theming', 'url', $this->url); |
|
| 139 | + } |
|
| 140 | + |
|
| 141 | + public function getSlogan() { |
|
| 142 | + return \OCP\Util::sanitizeHTML($this->config->getAppValue('theming', 'slogan', $this->slogan)); |
|
| 143 | + } |
|
| 144 | + |
|
| 145 | + public function getShortFooter() { |
|
| 146 | + $slogan = $this->getSlogan(); |
|
| 147 | + $footer = '<a href="'. $this->getBaseUrl() . '" target="_blank"' . |
|
| 148 | + ' rel="noreferrer noopener">' .$this->getEntity() . '</a>'. |
|
| 149 | + ($slogan !== '' ? ' – ' . $slogan : ''); |
|
| 150 | + |
|
| 151 | + return $footer; |
|
| 152 | + } |
|
| 153 | + |
|
| 154 | + /** |
|
| 155 | + * Color that is used for the header as well as for mail headers |
|
| 156 | + * |
|
| 157 | + * @return string |
|
| 158 | + */ |
|
| 159 | + public function getColorPrimary() { |
|
| 160 | + return $this->config->getAppValue('theming', 'color', $this->color); |
|
| 161 | + } |
|
| 162 | + |
|
| 163 | + /** |
|
| 164 | + * Themed logo url |
|
| 165 | + * |
|
| 166 | + * @param bool $useSvg Whether to point to the SVG image or a fallback |
|
| 167 | + * @return string |
|
| 168 | + */ |
|
| 169 | + public function getLogo($useSvg = true) { |
|
| 170 | + $logo = $this->config->getAppValue('theming', 'logoMime', false); |
|
| 171 | + |
|
| 172 | + $logoExists = true; |
|
| 173 | + try { |
|
| 174 | + $this->appData->getFolder('images')->getFile('logo'); |
|
| 175 | + } catch (\Exception $e) { |
|
| 176 | + $logoExists = false; |
|
| 177 | + } |
|
| 178 | + |
|
| 179 | + $cacheBusterCounter = $this->config->getAppValue('theming', 'cachebuster', '0'); |
|
| 180 | + |
|
| 181 | + if(!$logo || !$logoExists) { |
|
| 182 | + if($useSvg) { |
|
| 183 | + $logo = $this->urlGenerator->imagePath('core', 'logo.svg'); |
|
| 184 | + } else { |
|
| 185 | + $logo = $this->urlGenerator->imagePath('core', 'logo.png'); |
|
| 186 | + } |
|
| 187 | + return $logo . '?v=' . $cacheBusterCounter; |
|
| 188 | + } |
|
| 189 | + |
|
| 190 | + return $this->urlGenerator->linkToRoute('theming.Theming.getLogo') . '?v=' . $cacheBusterCounter; |
|
| 191 | + } |
|
| 192 | + |
|
| 193 | + /** |
|
| 194 | + * Themed background image url |
|
| 195 | + * |
|
| 196 | + * @return string |
|
| 197 | + */ |
|
| 198 | + public function getBackground() { |
|
| 199 | + $backgroundLogo = $this->config->getAppValue('theming', 'backgroundMime',false); |
|
| 200 | + |
|
| 201 | + $backgroundExists = true; |
|
| 202 | + try { |
|
| 203 | + $this->appData->getFolder('images')->getFile('background'); |
|
| 204 | + } catch (\Exception $e) { |
|
| 205 | + $backgroundExists = false; |
|
| 206 | + } |
|
| 207 | + |
|
| 208 | + $cacheBusterCounter = $this->config->getAppValue('theming', 'cachebuster', '0'); |
|
| 209 | + |
|
| 210 | + if(!$backgroundLogo || !$backgroundExists) { |
|
| 211 | + return $this->urlGenerator->imagePath('core','background.png') . '?v=' . $cacheBusterCounter; |
|
| 212 | + } |
|
| 213 | + |
|
| 214 | + return $this->urlGenerator->linkToRoute('theming.Theming.getLoginBackground') . '?v=' . $cacheBusterCounter; |
|
| 215 | + } |
|
| 216 | + |
|
| 217 | + /** |
|
| 218 | + * @return string |
|
| 219 | + */ |
|
| 220 | + public function getiTunesAppId() { |
|
| 221 | + return $this->config->getAppValue('theming', 'iTunesAppId', $this->iTunesAppId); |
|
| 222 | + } |
|
| 223 | + |
|
| 224 | + /** |
|
| 225 | + * @return string |
|
| 226 | + */ |
|
| 227 | + public function getiOSClientUrl() { |
|
| 228 | + return $this->config->getAppValue('theming', 'iOSClientUrl', $this->iOSClientUrl); |
|
| 229 | + } |
|
| 230 | + |
|
| 231 | + /** |
|
| 232 | + * @return string |
|
| 233 | + */ |
|
| 234 | + public function getAndroidClientUrl() { |
|
| 235 | + return $this->config->getAppValue('theming', 'AndroidClientUrl', $this->AndroidClientUrl); |
|
| 236 | + } |
|
| 237 | + |
|
| 238 | + |
|
| 239 | + /** |
|
| 240 | + * @return array scss variables to overwrite |
|
| 241 | + */ |
|
| 242 | + public function getScssVariables() { |
|
| 243 | + $cache = $this->cacheFactory->createDistributed('theming'); |
|
| 244 | + if ($value = $cache->get('getScssVariables')) { |
|
| 245 | + return $value; |
|
| 246 | + } |
|
| 247 | + |
|
| 248 | + $variables = [ |
|
| 249 | + 'theming-cachebuster' => "'" . $this->config->getAppValue('theming', 'cachebuster', '0') . "'", |
|
| 250 | + 'theming-logo-mime' => "'" . $this->config->getAppValue('theming', 'logoMime', '') . "'", |
|
| 251 | + 'theming-background-mime' => "'" . $this->config->getAppValue('theming', 'backgroundMime', '') . "'" |
|
| 252 | + ]; |
|
| 253 | + |
|
| 254 | + $variables['image-logo'] = "'".$this->urlGenerator->getAbsoluteURL($this->getLogo())."'"; |
|
| 255 | + $variables['image-login-background'] = "'".$this->urlGenerator->getAbsoluteURL($this->getBackground())."'"; |
|
| 256 | + $variables['image-login-plain'] = 'false'; |
|
| 257 | + |
|
| 258 | + if ($this->config->getAppValue('theming', 'color', null) !== null) { |
|
| 259 | + $variables['color-primary'] = $this->getColorPrimary(); |
|
| 260 | + $variables['color-primary-text'] = $this->getTextColorPrimary(); |
|
| 261 | + $variables['color-primary-element'] = $this->util->elementColor($this->getColorPrimary()); |
|
| 262 | + } |
|
| 263 | + |
|
| 264 | + if ($this->config->getAppValue('theming', 'backgroundMime', null) === 'backgroundColor') { |
|
| 265 | + $variables['image-login-plain'] = 'true'; |
|
| 266 | + } |
|
| 267 | + $cache->set('getScssVariables', $variables); |
|
| 268 | + return $variables; |
|
| 269 | + } |
|
| 270 | + |
|
| 271 | + /** |
|
| 272 | + * Check if the image should be replaced by the theming app |
|
| 273 | + * and return the new image location then |
|
| 274 | + * |
|
| 275 | + * @param string $app name of the app |
|
| 276 | + * @param string $image filename of the image |
|
| 277 | + * @return bool|string false if image should not replaced, otherwise the location of the image |
|
| 278 | + */ |
|
| 279 | + public function replaceImagePath($app, $image) { |
|
| 280 | + if($app==='') { |
|
| 281 | + $app = 'core'; |
|
| 282 | + } |
|
| 283 | + $cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0'); |
|
| 284 | + |
|
| 285 | + if ($image === 'favicon.ico' && $this->shouldReplaceIcons()) { |
|
| 286 | + return $this->urlGenerator->linkToRoute('theming.Icon.getFavicon', ['app' => $app]) . '?v=' . $cacheBusterValue; |
|
| 287 | + } |
|
| 288 | + if ($image === 'favicon-touch.png' && $this->shouldReplaceIcons()) { |
|
| 289 | + return $this->urlGenerator->linkToRoute('theming.Icon.getTouchIcon', ['app' => $app]) . '?v=' . $cacheBusterValue; |
|
| 290 | + } |
|
| 291 | + if ($image === 'manifest.json') { |
|
| 292 | + try { |
|
| 293 | + $appPath = $this->appManager->getAppPath($app); |
|
| 294 | + if (file_exists($appPath . '/img/manifest.json')) { |
|
| 295 | + return false; |
|
| 296 | + } |
|
| 297 | + } catch (AppPathNotFoundException $e) {} |
|
| 298 | + return $this->urlGenerator->linkToRoute('theming.Theming.getManifest') . '?v=' . $cacheBusterValue; |
|
| 299 | + } |
|
| 300 | + return false; |
|
| 301 | + } |
|
| 302 | + |
|
| 303 | + /** |
|
| 304 | + * Check if Imagemagick is enabled and if SVG is supported |
|
| 305 | + * otherwise we can't render custom icons |
|
| 306 | + * |
|
| 307 | + * @return bool |
|
| 308 | + */ |
|
| 309 | + public function shouldReplaceIcons() { |
|
| 310 | + $cache = $this->cacheFactory->createDistributed('theming'); |
|
| 311 | + if($value = $cache->get('shouldReplaceIcons')) { |
|
| 312 | + return (bool)$value; |
|
| 313 | + } |
|
| 314 | + $value = false; |
|
| 315 | + if(extension_loaded('imagick')) { |
|
| 316 | + $checkImagick = new \Imagick(); |
|
| 317 | + if (count($checkImagick->queryFormats('SVG')) >= 1) { |
|
| 318 | + $value = true; |
|
| 319 | + } |
|
| 320 | + $checkImagick->clear(); |
|
| 321 | + } |
|
| 322 | + $cache->set('shouldReplaceIcons', $value); |
|
| 323 | + return $value; |
|
| 324 | + } |
|
| 325 | + |
|
| 326 | + /** |
|
| 327 | + * Increases the cache buster key |
|
| 328 | + */ |
|
| 329 | + private function increaseCacheBuster() { |
|
| 330 | + $cacheBusterKey = $this->config->getAppValue('theming', 'cachebuster', '0'); |
|
| 331 | + $this->config->setAppValue('theming', 'cachebuster', (int)$cacheBusterKey+1); |
|
| 332 | + $this->cacheFactory->createDistributed('theming')->clear('getScssVariables'); |
|
| 333 | + } |
|
| 334 | + |
|
| 335 | + /** |
|
| 336 | + * Update setting in the database |
|
| 337 | + * |
|
| 338 | + * @param string $setting |
|
| 339 | + * @param string $value |
|
| 340 | + */ |
|
| 341 | + public function set($setting, $value) { |
|
| 342 | + $this->config->setAppValue('theming', $setting, $value); |
|
| 343 | + $this->increaseCacheBuster(); |
|
| 344 | + } |
|
| 345 | + |
|
| 346 | + /** |
|
| 347 | + * Revert settings to the default value |
|
| 348 | + * |
|
| 349 | + * @param string $setting setting which should be reverted |
|
| 350 | + * @return string default value |
|
| 351 | + */ |
|
| 352 | + public function undo($setting) { |
|
| 353 | + $this->config->deleteAppValue('theming', $setting); |
|
| 354 | + $this->increaseCacheBuster(); |
|
| 355 | + |
|
| 356 | + switch ($setting) { |
|
| 357 | + case 'name': |
|
| 358 | + $returnValue = $this->getEntity(); |
|
| 359 | + break; |
|
| 360 | + case 'url': |
|
| 361 | + $returnValue = $this->getBaseUrl(); |
|
| 362 | + break; |
|
| 363 | + case 'slogan': |
|
| 364 | + $returnValue = $this->getSlogan(); |
|
| 365 | + break; |
|
| 366 | + case 'color': |
|
| 367 | + $returnValue = $this->getColorPrimary(); |
|
| 368 | + break; |
|
| 369 | + default: |
|
| 370 | + $returnValue = ''; |
|
| 371 | + break; |
|
| 372 | + } |
|
| 373 | + |
|
| 374 | + return $returnValue; |
|
| 375 | + } |
|
| 376 | + |
|
| 377 | + /** |
|
| 378 | + * Color of text in the header and primary buttons |
|
| 379 | + * |
|
| 380 | + * @return string |
|
| 381 | + */ |
|
| 382 | + public function getTextColorPrimary() { |
|
| 383 | + return $this->util->invertTextColor($this->getColorPrimary()) ? '#000000' : '#ffffff'; |
|
| 384 | + } |
|
| 385 | 385 | } |
@@ -144,9 +144,9 @@ discard block |
||
| 144 | 144 | |
| 145 | 145 | public function getShortFooter() { |
| 146 | 146 | $slogan = $this->getSlogan(); |
| 147 | - $footer = '<a href="'. $this->getBaseUrl() . '" target="_blank"' . |
|
| 148 | - ' rel="noreferrer noopener">' .$this->getEntity() . '</a>'. |
|
| 149 | - ($slogan !== '' ? ' – ' . $slogan : ''); |
|
| 147 | + $footer = '<a href="'.$this->getBaseUrl().'" target="_blank"'. |
|
| 148 | + ' rel="noreferrer noopener">'.$this->getEntity().'</a>'. |
|
| 149 | + ($slogan !== '' ? ' – '.$slogan : ''); |
|
| 150 | 150 | |
| 151 | 151 | return $footer; |
| 152 | 152 | } |
@@ -178,16 +178,16 @@ discard block |
||
| 178 | 178 | |
| 179 | 179 | $cacheBusterCounter = $this->config->getAppValue('theming', 'cachebuster', '0'); |
| 180 | 180 | |
| 181 | - if(!$logo || !$logoExists) { |
|
| 182 | - if($useSvg) { |
|
| 181 | + if (!$logo || !$logoExists) { |
|
| 182 | + if ($useSvg) { |
|
| 183 | 183 | $logo = $this->urlGenerator->imagePath('core', 'logo.svg'); |
| 184 | 184 | } else { |
| 185 | 185 | $logo = $this->urlGenerator->imagePath('core', 'logo.png'); |
| 186 | 186 | } |
| 187 | - return $logo . '?v=' . $cacheBusterCounter; |
|
| 187 | + return $logo.'?v='.$cacheBusterCounter; |
|
| 188 | 188 | } |
| 189 | 189 | |
| 190 | - return $this->urlGenerator->linkToRoute('theming.Theming.getLogo') . '?v=' . $cacheBusterCounter; |
|
| 190 | + return $this->urlGenerator->linkToRoute('theming.Theming.getLogo').'?v='.$cacheBusterCounter; |
|
| 191 | 191 | } |
| 192 | 192 | |
| 193 | 193 | /** |
@@ -196,7 +196,7 @@ discard block |
||
| 196 | 196 | * @return string |
| 197 | 197 | */ |
| 198 | 198 | public function getBackground() { |
| 199 | - $backgroundLogo = $this->config->getAppValue('theming', 'backgroundMime',false); |
|
| 199 | + $backgroundLogo = $this->config->getAppValue('theming', 'backgroundMime', false); |
|
| 200 | 200 | |
| 201 | 201 | $backgroundExists = true; |
| 202 | 202 | try { |
@@ -207,11 +207,11 @@ discard block |
||
| 207 | 207 | |
| 208 | 208 | $cacheBusterCounter = $this->config->getAppValue('theming', 'cachebuster', '0'); |
| 209 | 209 | |
| 210 | - if(!$backgroundLogo || !$backgroundExists) { |
|
| 211 | - return $this->urlGenerator->imagePath('core','background.png') . '?v=' . $cacheBusterCounter; |
|
| 210 | + if (!$backgroundLogo || !$backgroundExists) { |
|
| 211 | + return $this->urlGenerator->imagePath('core', 'background.png').'?v='.$cacheBusterCounter; |
|
| 212 | 212 | } |
| 213 | 213 | |
| 214 | - return $this->urlGenerator->linkToRoute('theming.Theming.getLoginBackground') . '?v=' . $cacheBusterCounter; |
|
| 214 | + return $this->urlGenerator->linkToRoute('theming.Theming.getLoginBackground').'?v='.$cacheBusterCounter; |
|
| 215 | 215 | } |
| 216 | 216 | |
| 217 | 217 | /** |
@@ -246,9 +246,9 @@ discard block |
||
| 246 | 246 | } |
| 247 | 247 | |
| 248 | 248 | $variables = [ |
| 249 | - 'theming-cachebuster' => "'" . $this->config->getAppValue('theming', 'cachebuster', '0') . "'", |
|
| 250 | - 'theming-logo-mime' => "'" . $this->config->getAppValue('theming', 'logoMime', '') . "'", |
|
| 251 | - 'theming-background-mime' => "'" . $this->config->getAppValue('theming', 'backgroundMime', '') . "'" |
|
| 249 | + 'theming-cachebuster' => "'".$this->config->getAppValue('theming', 'cachebuster', '0')."'", |
|
| 250 | + 'theming-logo-mime' => "'".$this->config->getAppValue('theming', 'logoMime', '')."'", |
|
| 251 | + 'theming-background-mime' => "'".$this->config->getAppValue('theming', 'backgroundMime', '')."'" |
|
| 252 | 252 | ]; |
| 253 | 253 | |
| 254 | 254 | $variables['image-logo'] = "'".$this->urlGenerator->getAbsoluteURL($this->getLogo())."'"; |
@@ -277,25 +277,25 @@ discard block |
||
| 277 | 277 | * @return bool|string false if image should not replaced, otherwise the location of the image |
| 278 | 278 | */ |
| 279 | 279 | public function replaceImagePath($app, $image) { |
| 280 | - if($app==='') { |
|
| 280 | + if ($app === '') { |
|
| 281 | 281 | $app = 'core'; |
| 282 | 282 | } |
| 283 | 283 | $cacheBusterValue = $this->config->getAppValue('theming', 'cachebuster', '0'); |
| 284 | 284 | |
| 285 | 285 | if ($image === 'favicon.ico' && $this->shouldReplaceIcons()) { |
| 286 | - return $this->urlGenerator->linkToRoute('theming.Icon.getFavicon', ['app' => $app]) . '?v=' . $cacheBusterValue; |
|
| 286 | + return $this->urlGenerator->linkToRoute('theming.Icon.getFavicon', ['app' => $app]).'?v='.$cacheBusterValue; |
|
| 287 | 287 | } |
| 288 | 288 | if ($image === 'favicon-touch.png' && $this->shouldReplaceIcons()) { |
| 289 | - return $this->urlGenerator->linkToRoute('theming.Icon.getTouchIcon', ['app' => $app]) . '?v=' . $cacheBusterValue; |
|
| 289 | + return $this->urlGenerator->linkToRoute('theming.Icon.getTouchIcon', ['app' => $app]).'?v='.$cacheBusterValue; |
|
| 290 | 290 | } |
| 291 | 291 | if ($image === 'manifest.json') { |
| 292 | 292 | try { |
| 293 | 293 | $appPath = $this->appManager->getAppPath($app); |
| 294 | - if (file_exists($appPath . '/img/manifest.json')) { |
|
| 294 | + if (file_exists($appPath.'/img/manifest.json')) { |
|
| 295 | 295 | return false; |
| 296 | 296 | } |
| 297 | 297 | } catch (AppPathNotFoundException $e) {} |
| 298 | - return $this->urlGenerator->linkToRoute('theming.Theming.getManifest') . '?v=' . $cacheBusterValue; |
|
| 298 | + return $this->urlGenerator->linkToRoute('theming.Theming.getManifest').'?v='.$cacheBusterValue; |
|
| 299 | 299 | } |
| 300 | 300 | return false; |
| 301 | 301 | } |
@@ -308,11 +308,11 @@ discard block |
||
| 308 | 308 | */ |
| 309 | 309 | public function shouldReplaceIcons() { |
| 310 | 310 | $cache = $this->cacheFactory->createDistributed('theming'); |
| 311 | - if($value = $cache->get('shouldReplaceIcons')) { |
|
| 312 | - return (bool)$value; |
|
| 311 | + if ($value = $cache->get('shouldReplaceIcons')) { |
|
| 312 | + return (bool) $value; |
|
| 313 | 313 | } |
| 314 | 314 | $value = false; |
| 315 | - if(extension_loaded('imagick')) { |
|
| 315 | + if (extension_loaded('imagick')) { |
|
| 316 | 316 | $checkImagick = new \Imagick(); |
| 317 | 317 | if (count($checkImagick->queryFormats('SVG')) >= 1) { |
| 318 | 318 | $value = true; |
@@ -328,7 +328,7 @@ discard block |
||
| 328 | 328 | */ |
| 329 | 329 | private function increaseCacheBuster() { |
| 330 | 330 | $cacheBusterKey = $this->config->getAppValue('theming', 'cachebuster', '0'); |
| 331 | - $this->config->setAppValue('theming', 'cachebuster', (int)$cacheBusterKey+1); |
|
| 331 | + $this->config->setAppValue('theming', 'cachebuster', (int) $cacheBusterKey + 1); |
|
| 332 | 332 | $this->cacheFactory->createDistributed('theming')->clear('getScssVariables'); |
| 333 | 333 | } |
| 334 | 334 | |
@@ -37,170 +37,170 @@ |
||
| 37 | 37 | use OCA\User_LDAP\User\Manager; |
| 38 | 38 | |
| 39 | 39 | abstract class Proxy { |
| 40 | - static private $accesses = array(); |
|
| 41 | - private $ldap = null; |
|
| 42 | - |
|
| 43 | - /** @var \OCP\ICache|null */ |
|
| 44 | - private $cache; |
|
| 45 | - |
|
| 46 | - /** |
|
| 47 | - * @param ILDAPWrapper $ldap |
|
| 48 | - */ |
|
| 49 | - public function __construct(ILDAPWrapper $ldap) { |
|
| 50 | - $this->ldap = $ldap; |
|
| 51 | - $memcache = \OC::$server->getMemCacheFactory(); |
|
| 52 | - if($memcache->isAvailable()) { |
|
| 53 | - $this->cache = $memcache->createDistributed(); |
|
| 54 | - } |
|
| 55 | - } |
|
| 56 | - |
|
| 57 | - /** |
|
| 58 | - * @param string $configPrefix |
|
| 59 | - */ |
|
| 60 | - private function addAccess($configPrefix) { |
|
| 61 | - static $ocConfig; |
|
| 62 | - static $fs; |
|
| 63 | - static $log; |
|
| 64 | - static $avatarM; |
|
| 65 | - static $userMap; |
|
| 66 | - static $groupMap; |
|
| 67 | - static $db; |
|
| 68 | - static $coreUserManager; |
|
| 69 | - static $coreNotificationManager; |
|
| 70 | - if($fs === null) { |
|
| 71 | - $ocConfig = \OC::$server->getConfig(); |
|
| 72 | - $fs = new FilesystemHelper(); |
|
| 73 | - $log = new LogWrapper(); |
|
| 74 | - $avatarM = \OC::$server->getAvatarManager(); |
|
| 75 | - $db = \OC::$server->getDatabaseConnection(); |
|
| 76 | - $userMap = new UserMapping($db); |
|
| 77 | - $groupMap = new GroupMapping($db); |
|
| 78 | - $coreUserManager = \OC::$server->getUserManager(); |
|
| 79 | - $coreNotificationManager = \OC::$server->getNotificationManager(); |
|
| 80 | - } |
|
| 81 | - $userManager = |
|
| 82 | - new Manager($ocConfig, $fs, $log, $avatarM, new \OCP\Image(), $db, |
|
| 83 | - $coreUserManager, $coreNotificationManager); |
|
| 84 | - $connector = new Connection($this->ldap, $configPrefix); |
|
| 85 | - $access = new Access($connector, $this->ldap, $userManager, new Helper($ocConfig), $ocConfig); |
|
| 86 | - $access->setUserMapper($userMap); |
|
| 87 | - $access->setGroupMapper($groupMap); |
|
| 88 | - self::$accesses[$configPrefix] = $access; |
|
| 89 | - } |
|
| 90 | - |
|
| 91 | - /** |
|
| 92 | - * @param string $configPrefix |
|
| 93 | - * @return mixed |
|
| 94 | - */ |
|
| 95 | - protected function getAccess($configPrefix) { |
|
| 96 | - if(!isset(self::$accesses[$configPrefix])) { |
|
| 97 | - $this->addAccess($configPrefix); |
|
| 98 | - } |
|
| 99 | - return self::$accesses[$configPrefix]; |
|
| 100 | - } |
|
| 101 | - |
|
| 102 | - /** |
|
| 103 | - * @param string $uid |
|
| 104 | - * @return string |
|
| 105 | - */ |
|
| 106 | - protected function getUserCacheKey($uid) { |
|
| 107 | - return 'user-'.$uid.'-lastSeenOn'; |
|
| 108 | - } |
|
| 109 | - |
|
| 110 | - /** |
|
| 111 | - * @param string $gid |
|
| 112 | - * @return string |
|
| 113 | - */ |
|
| 114 | - protected function getGroupCacheKey($gid) { |
|
| 115 | - return 'group-'.$gid.'-lastSeenOn'; |
|
| 116 | - } |
|
| 117 | - |
|
| 118 | - /** |
|
| 119 | - * @param string $id |
|
| 120 | - * @param string $method |
|
| 121 | - * @param array $parameters |
|
| 122 | - * @param bool $passOnWhen |
|
| 123 | - * @return mixed |
|
| 124 | - */ |
|
| 125 | - abstract protected function callOnLastSeenOn($id, $method, $parameters, $passOnWhen); |
|
| 126 | - |
|
| 127 | - /** |
|
| 128 | - * @param string $id |
|
| 129 | - * @param string $method |
|
| 130 | - * @param array $parameters |
|
| 131 | - * @return mixed |
|
| 132 | - */ |
|
| 133 | - abstract protected function walkBackends($id, $method, $parameters); |
|
| 134 | - |
|
| 135 | - /** |
|
| 136 | - * @param string $id |
|
| 137 | - * @return Access |
|
| 138 | - */ |
|
| 139 | - abstract public function getLDAPAccess($id); |
|
| 140 | - |
|
| 141 | - /** |
|
| 142 | - * Takes care of the request to the User backend |
|
| 143 | - * @param string $id |
|
| 144 | - * @param string $method string, the method of the user backend that shall be called |
|
| 145 | - * @param array $parameters an array of parameters to be passed |
|
| 146 | - * @param bool $passOnWhen |
|
| 147 | - * @return mixed, the result of the specified method |
|
| 148 | - */ |
|
| 149 | - protected function handleRequest($id, $method, $parameters, $passOnWhen = false) { |
|
| 150 | - $result = $this->callOnLastSeenOn($id, $method, $parameters, $passOnWhen); |
|
| 151 | - if($result === $passOnWhen) { |
|
| 152 | - $result = $this->walkBackends($id, $method, $parameters); |
|
| 153 | - } |
|
| 154 | - return $result; |
|
| 155 | - } |
|
| 156 | - |
|
| 157 | - /** |
|
| 158 | - * @param string|null $key |
|
| 159 | - * @return string |
|
| 160 | - */ |
|
| 161 | - private function getCacheKey($key) { |
|
| 162 | - $prefix = 'LDAP-Proxy-'; |
|
| 163 | - if($key === null) { |
|
| 164 | - return $prefix; |
|
| 165 | - } |
|
| 166 | - return $prefix.md5($key); |
|
| 167 | - } |
|
| 168 | - |
|
| 169 | - /** |
|
| 170 | - * @param string $key |
|
| 171 | - * @return mixed|null |
|
| 172 | - */ |
|
| 173 | - public function getFromCache($key) { |
|
| 174 | - if($this->cache === null) { |
|
| 175 | - return null; |
|
| 176 | - } |
|
| 177 | - |
|
| 178 | - $key = $this->getCacheKey($key); |
|
| 179 | - $value = $this->cache->get($key); |
|
| 180 | - if ($value === null) { |
|
| 181 | - return null; |
|
| 182 | - } |
|
| 183 | - |
|
| 184 | - return json_decode(base64_decode($value)); |
|
| 185 | - } |
|
| 186 | - |
|
| 187 | - /** |
|
| 188 | - * @param string $key |
|
| 189 | - * @param mixed $value |
|
| 190 | - */ |
|
| 191 | - public function writeToCache($key, $value) { |
|
| 192 | - if($this->cache === null) { |
|
| 193 | - return; |
|
| 194 | - } |
|
| 195 | - $key = $this->getCacheKey($key); |
|
| 196 | - $value = base64_encode(json_encode($value)); |
|
| 197 | - $this->cache->set($key, $value, 2592000); |
|
| 198 | - } |
|
| 199 | - |
|
| 200 | - public function clearCache() { |
|
| 201 | - if($this->cache === null) { |
|
| 202 | - return; |
|
| 203 | - } |
|
| 204 | - $this->cache->clear($this->getCacheKey(null)); |
|
| 205 | - } |
|
| 40 | + static private $accesses = array(); |
|
| 41 | + private $ldap = null; |
|
| 42 | + |
|
| 43 | + /** @var \OCP\ICache|null */ |
|
| 44 | + private $cache; |
|
| 45 | + |
|
| 46 | + /** |
|
| 47 | + * @param ILDAPWrapper $ldap |
|
| 48 | + */ |
|
| 49 | + public function __construct(ILDAPWrapper $ldap) { |
|
| 50 | + $this->ldap = $ldap; |
|
| 51 | + $memcache = \OC::$server->getMemCacheFactory(); |
|
| 52 | + if($memcache->isAvailable()) { |
|
| 53 | + $this->cache = $memcache->createDistributed(); |
|
| 54 | + } |
|
| 55 | + } |
|
| 56 | + |
|
| 57 | + /** |
|
| 58 | + * @param string $configPrefix |
|
| 59 | + */ |
|
| 60 | + private function addAccess($configPrefix) { |
|
| 61 | + static $ocConfig; |
|
| 62 | + static $fs; |
|
| 63 | + static $log; |
|
| 64 | + static $avatarM; |
|
| 65 | + static $userMap; |
|
| 66 | + static $groupMap; |
|
| 67 | + static $db; |
|
| 68 | + static $coreUserManager; |
|
| 69 | + static $coreNotificationManager; |
|
| 70 | + if($fs === null) { |
|
| 71 | + $ocConfig = \OC::$server->getConfig(); |
|
| 72 | + $fs = new FilesystemHelper(); |
|
| 73 | + $log = new LogWrapper(); |
|
| 74 | + $avatarM = \OC::$server->getAvatarManager(); |
|
| 75 | + $db = \OC::$server->getDatabaseConnection(); |
|
| 76 | + $userMap = new UserMapping($db); |
|
| 77 | + $groupMap = new GroupMapping($db); |
|
| 78 | + $coreUserManager = \OC::$server->getUserManager(); |
|
| 79 | + $coreNotificationManager = \OC::$server->getNotificationManager(); |
|
| 80 | + } |
|
| 81 | + $userManager = |
|
| 82 | + new Manager($ocConfig, $fs, $log, $avatarM, new \OCP\Image(), $db, |
|
| 83 | + $coreUserManager, $coreNotificationManager); |
|
| 84 | + $connector = new Connection($this->ldap, $configPrefix); |
|
| 85 | + $access = new Access($connector, $this->ldap, $userManager, new Helper($ocConfig), $ocConfig); |
|
| 86 | + $access->setUserMapper($userMap); |
|
| 87 | + $access->setGroupMapper($groupMap); |
|
| 88 | + self::$accesses[$configPrefix] = $access; |
|
| 89 | + } |
|
| 90 | + |
|
| 91 | + /** |
|
| 92 | + * @param string $configPrefix |
|
| 93 | + * @return mixed |
|
| 94 | + */ |
|
| 95 | + protected function getAccess($configPrefix) { |
|
| 96 | + if(!isset(self::$accesses[$configPrefix])) { |
|
| 97 | + $this->addAccess($configPrefix); |
|
| 98 | + } |
|
| 99 | + return self::$accesses[$configPrefix]; |
|
| 100 | + } |
|
| 101 | + |
|
| 102 | + /** |
|
| 103 | + * @param string $uid |
|
| 104 | + * @return string |
|
| 105 | + */ |
|
| 106 | + protected function getUserCacheKey($uid) { |
|
| 107 | + return 'user-'.$uid.'-lastSeenOn'; |
|
| 108 | + } |
|
| 109 | + |
|
| 110 | + /** |
|
| 111 | + * @param string $gid |
|
| 112 | + * @return string |
|
| 113 | + */ |
|
| 114 | + protected function getGroupCacheKey($gid) { |
|
| 115 | + return 'group-'.$gid.'-lastSeenOn'; |
|
| 116 | + } |
|
| 117 | + |
|
| 118 | + /** |
|
| 119 | + * @param string $id |
|
| 120 | + * @param string $method |
|
| 121 | + * @param array $parameters |
|
| 122 | + * @param bool $passOnWhen |
|
| 123 | + * @return mixed |
|
| 124 | + */ |
|
| 125 | + abstract protected function callOnLastSeenOn($id, $method, $parameters, $passOnWhen); |
|
| 126 | + |
|
| 127 | + /** |
|
| 128 | + * @param string $id |
|
| 129 | + * @param string $method |
|
| 130 | + * @param array $parameters |
|
| 131 | + * @return mixed |
|
| 132 | + */ |
|
| 133 | + abstract protected function walkBackends($id, $method, $parameters); |
|
| 134 | + |
|
| 135 | + /** |
|
| 136 | + * @param string $id |
|
| 137 | + * @return Access |
|
| 138 | + */ |
|
| 139 | + abstract public function getLDAPAccess($id); |
|
| 140 | + |
|
| 141 | + /** |
|
| 142 | + * Takes care of the request to the User backend |
|
| 143 | + * @param string $id |
|
| 144 | + * @param string $method string, the method of the user backend that shall be called |
|
| 145 | + * @param array $parameters an array of parameters to be passed |
|
| 146 | + * @param bool $passOnWhen |
|
| 147 | + * @return mixed, the result of the specified method |
|
| 148 | + */ |
|
| 149 | + protected function handleRequest($id, $method, $parameters, $passOnWhen = false) { |
|
| 150 | + $result = $this->callOnLastSeenOn($id, $method, $parameters, $passOnWhen); |
|
| 151 | + if($result === $passOnWhen) { |
|
| 152 | + $result = $this->walkBackends($id, $method, $parameters); |
|
| 153 | + } |
|
| 154 | + return $result; |
|
| 155 | + } |
|
| 156 | + |
|
| 157 | + /** |
|
| 158 | + * @param string|null $key |
|
| 159 | + * @return string |
|
| 160 | + */ |
|
| 161 | + private function getCacheKey($key) { |
|
| 162 | + $prefix = 'LDAP-Proxy-'; |
|
| 163 | + if($key === null) { |
|
| 164 | + return $prefix; |
|
| 165 | + } |
|
| 166 | + return $prefix.md5($key); |
|
| 167 | + } |
|
| 168 | + |
|
| 169 | + /** |
|
| 170 | + * @param string $key |
|
| 171 | + * @return mixed|null |
|
| 172 | + */ |
|
| 173 | + public function getFromCache($key) { |
|
| 174 | + if($this->cache === null) { |
|
| 175 | + return null; |
|
| 176 | + } |
|
| 177 | + |
|
| 178 | + $key = $this->getCacheKey($key); |
|
| 179 | + $value = $this->cache->get($key); |
|
| 180 | + if ($value === null) { |
|
| 181 | + return null; |
|
| 182 | + } |
|
| 183 | + |
|
| 184 | + return json_decode(base64_decode($value)); |
|
| 185 | + } |
|
| 186 | + |
|
| 187 | + /** |
|
| 188 | + * @param string $key |
|
| 189 | + * @param mixed $value |
|
| 190 | + */ |
|
| 191 | + public function writeToCache($key, $value) { |
|
| 192 | + if($this->cache === null) { |
|
| 193 | + return; |
|
| 194 | + } |
|
| 195 | + $key = $this->getCacheKey($key); |
|
| 196 | + $value = base64_encode(json_encode($value)); |
|
| 197 | + $this->cache->set($key, $value, 2592000); |
|
| 198 | + } |
|
| 199 | + |
|
| 200 | + public function clearCache() { |
|
| 201 | + if($this->cache === null) { |
|
| 202 | + return; |
|
| 203 | + } |
|
| 204 | + $this->cache->clear($this->getCacheKey(null)); |
|
| 205 | + } |
|
| 206 | 206 | } |
@@ -49,7 +49,7 @@ discard block |
||
| 49 | 49 | public function __construct(ILDAPWrapper $ldap) { |
| 50 | 50 | $this->ldap = $ldap; |
| 51 | 51 | $memcache = \OC::$server->getMemCacheFactory(); |
| 52 | - if($memcache->isAvailable()) { |
|
| 52 | + if ($memcache->isAvailable()) { |
|
| 53 | 53 | $this->cache = $memcache->createDistributed(); |
| 54 | 54 | } |
| 55 | 55 | } |
@@ -67,7 +67,7 @@ discard block |
||
| 67 | 67 | static $db; |
| 68 | 68 | static $coreUserManager; |
| 69 | 69 | static $coreNotificationManager; |
| 70 | - if($fs === null) { |
|
| 70 | + if ($fs === null) { |
|
| 71 | 71 | $ocConfig = \OC::$server->getConfig(); |
| 72 | 72 | $fs = new FilesystemHelper(); |
| 73 | 73 | $log = new LogWrapper(); |
@@ -93,7 +93,7 @@ discard block |
||
| 93 | 93 | * @return mixed |
| 94 | 94 | */ |
| 95 | 95 | protected function getAccess($configPrefix) { |
| 96 | - if(!isset(self::$accesses[$configPrefix])) { |
|
| 96 | + if (!isset(self::$accesses[$configPrefix])) { |
|
| 97 | 97 | $this->addAccess($configPrefix); |
| 98 | 98 | } |
| 99 | 99 | return self::$accesses[$configPrefix]; |
@@ -147,8 +147,8 @@ discard block |
||
| 147 | 147 | * @return mixed, the result of the specified method |
| 148 | 148 | */ |
| 149 | 149 | protected function handleRequest($id, $method, $parameters, $passOnWhen = false) { |
| 150 | - $result = $this->callOnLastSeenOn($id, $method, $parameters, $passOnWhen); |
|
| 151 | - if($result === $passOnWhen) { |
|
| 150 | + $result = $this->callOnLastSeenOn($id, $method, $parameters, $passOnWhen); |
|
| 151 | + if ($result === $passOnWhen) { |
|
| 152 | 152 | $result = $this->walkBackends($id, $method, $parameters); |
| 153 | 153 | } |
| 154 | 154 | return $result; |
@@ -160,7 +160,7 @@ discard block |
||
| 160 | 160 | */ |
| 161 | 161 | private function getCacheKey($key) { |
| 162 | 162 | $prefix = 'LDAP-Proxy-'; |
| 163 | - if($key === null) { |
|
| 163 | + if ($key === null) { |
|
| 164 | 164 | return $prefix; |
| 165 | 165 | } |
| 166 | 166 | return $prefix.md5($key); |
@@ -171,7 +171,7 @@ discard block |
||
| 171 | 171 | * @return mixed|null |
| 172 | 172 | */ |
| 173 | 173 | public function getFromCache($key) { |
| 174 | - if($this->cache === null) { |
|
| 174 | + if ($this->cache === null) { |
|
| 175 | 175 | return null; |
| 176 | 176 | } |
| 177 | 177 | |
@@ -189,7 +189,7 @@ discard block |
||
| 189 | 189 | * @param mixed $value |
| 190 | 190 | */ |
| 191 | 191 | public function writeToCache($key, $value) { |
| 192 | - if($this->cache === null) { |
|
| 192 | + if ($this->cache === null) { |
|
| 193 | 193 | return; |
| 194 | 194 | } |
| 195 | 195 | $key = $this->getCacheKey($key); |
@@ -198,7 +198,7 @@ discard block |
||
| 198 | 198 | } |
| 199 | 199 | |
| 200 | 200 | public function clearCache() { |
| 201 | - if($this->cache === null) { |
|
| 201 | + if ($this->cache === null) { |
|
| 202 | 202 | return; |
| 203 | 203 | } |
| 204 | 204 | $this->cache->clear($this->getCacheKey(null)); |
@@ -39,249 +39,249 @@ |
||
| 39 | 39 | |
| 40 | 40 | class Swift implements IObjectStore { |
| 41 | 41 | |
| 42 | - /** |
|
| 43 | - * @var \OpenCloud\OpenStack |
|
| 44 | - */ |
|
| 45 | - private $client; |
|
| 46 | - |
|
| 47 | - /** |
|
| 48 | - * @var array |
|
| 49 | - */ |
|
| 50 | - private $params; |
|
| 51 | - |
|
| 52 | - /** |
|
| 53 | - * @var \OpenCloud\ObjectStore\Service |
|
| 54 | - */ |
|
| 55 | - private $objectStoreService; |
|
| 56 | - |
|
| 57 | - /** |
|
| 58 | - * @var \OpenCloud\ObjectStore\Resource\Container |
|
| 59 | - */ |
|
| 60 | - private $container; |
|
| 61 | - |
|
| 62 | - private $memcache; |
|
| 63 | - |
|
| 64 | - public function __construct($params) { |
|
| 65 | - if (isset($params['bucket'])) { |
|
| 66 | - $params['container'] = $params['bucket']; |
|
| 67 | - } |
|
| 68 | - if (!isset($params['container'])) { |
|
| 69 | - $params['container'] = 'owncloud'; |
|
| 70 | - } |
|
| 71 | - if (!isset($params['autocreate'])) { |
|
| 72 | - // should only be true for tests |
|
| 73 | - $params['autocreate'] = false; |
|
| 74 | - } |
|
| 75 | - |
|
| 76 | - if (isset($params['apiKey'])) { |
|
| 77 | - $this->client = new Rackspace($params['url'], $params); |
|
| 78 | - $cacheKey = $params['username'] . '@' . $params['url'] . '/' . $params['bucket']; |
|
| 79 | - } else { |
|
| 80 | - $this->client = new OpenStack($params['url'], $params); |
|
| 81 | - $cacheKey = $params['username'] . '@' . $params['url'] . '/' . $params['bucket']; |
|
| 82 | - } |
|
| 83 | - |
|
| 84 | - $cacheFactory = \OC::$server->getMemCacheFactory(); |
|
| 85 | - $this->memcache = $cacheFactory->createDistributed('swift::' . $cacheKey); |
|
| 86 | - |
|
| 87 | - $this->params = $params; |
|
| 88 | - } |
|
| 89 | - |
|
| 90 | - protected function init() { |
|
| 91 | - if ($this->container) { |
|
| 92 | - return; |
|
| 93 | - } |
|
| 94 | - |
|
| 95 | - $this->importToken(); |
|
| 96 | - |
|
| 97 | - /** @var Token $token */ |
|
| 98 | - $token = $this->client->getTokenObject(); |
|
| 99 | - |
|
| 100 | - if (!$token || $token->hasExpired()) { |
|
| 101 | - try { |
|
| 102 | - $this->client->authenticate(); |
|
| 103 | - $this->exportToken(); |
|
| 104 | - } catch (ClientErrorResponseException $e) { |
|
| 105 | - $statusCode = $e->getResponse()->getStatusCode(); |
|
| 106 | - if ($statusCode == 412) { |
|
| 107 | - throw new StorageAuthException('Precondition failed, verify the keystone url', $e); |
|
| 108 | - } else if ($statusCode === 401) { |
|
| 109 | - throw new StorageAuthException('Authentication failed, verify the username, password and possibly tenant', $e); |
|
| 110 | - } else { |
|
| 111 | - throw new StorageAuthException('Unknown error', $e); |
|
| 112 | - } |
|
| 113 | - } |
|
| 114 | - } |
|
| 115 | - |
|
| 116 | - |
|
| 117 | - /** @var Catalog $catalog */ |
|
| 118 | - $catalog = $this->client->getCatalog(); |
|
| 119 | - |
|
| 120 | - if (isset($this->params['serviceName'])) { |
|
| 121 | - $serviceName = $this->params['serviceName']; |
|
| 122 | - } else { |
|
| 123 | - $serviceName = Service::DEFAULT_NAME; |
|
| 124 | - } |
|
| 125 | - |
|
| 126 | - if (isset($this->params['urlType'])) { |
|
| 127 | - $urlType = $this->params['urlType']; |
|
| 128 | - if ($urlType !== 'internalURL' && $urlType !== 'publicURL') { |
|
| 129 | - throw new StorageNotAvailableException('Invalid url type'); |
|
| 130 | - } |
|
| 131 | - } else { |
|
| 132 | - $urlType = Service::DEFAULT_URL_TYPE; |
|
| 133 | - } |
|
| 134 | - |
|
| 135 | - $catalogItem = $this->getCatalogForService($catalog, $serviceName); |
|
| 136 | - if (!$catalogItem) { |
|
| 137 | - $available = implode(', ', $this->getAvailableServiceNames($catalog)); |
|
| 138 | - throw new StorageNotAvailableException( |
|
| 139 | - "Service $serviceName not found in service catalog, available services: $available" |
|
| 140 | - ); |
|
| 141 | - } else if (isset($this->params['region'])) { |
|
| 142 | - $this->validateRegion($catalogItem, $this->params['region']); |
|
| 143 | - } |
|
| 144 | - |
|
| 145 | - $this->objectStoreService = $this->client->objectStoreService($serviceName, $this->params['region'], $urlType); |
|
| 146 | - |
|
| 147 | - try { |
|
| 148 | - $this->container = $this->objectStoreService->getContainer($this->params['container']); |
|
| 149 | - } catch (ClientErrorResponseException $ex) { |
|
| 150 | - // if the container does not exist and autocreate is true try to create the container on the fly |
|
| 151 | - if (isset($this->params['autocreate']) && $this->params['autocreate'] === true) { |
|
| 152 | - $this->container = $this->objectStoreService->createContainer($this->params['container']); |
|
| 153 | - } else { |
|
| 154 | - throw $ex; |
|
| 155 | - } |
|
| 156 | - } |
|
| 157 | - } |
|
| 158 | - |
|
| 159 | - private function exportToken() { |
|
| 160 | - $export = $this->client->exportCredentials(); |
|
| 161 | - $export['catalog'] = array_map(function (CatalogItem $item) { |
|
| 162 | - return [ |
|
| 163 | - 'name' => $item->getName(), |
|
| 164 | - 'endpoints' => $item->getEndpoints(), |
|
| 165 | - 'type' => $item->getType() |
|
| 166 | - ]; |
|
| 167 | - }, $export['catalog']->getItems()); |
|
| 168 | - $this->memcache->set('token', json_encode($export)); |
|
| 169 | - } |
|
| 170 | - |
|
| 171 | - private function importToken() { |
|
| 172 | - $cachedTokenString = $this->memcache->get('token'); |
|
| 173 | - if ($cachedTokenString) { |
|
| 174 | - $cachedToken = json_decode($cachedTokenString, true); |
|
| 175 | - $cachedToken['catalog'] = array_map(function (array $item) { |
|
| 176 | - $itemClass = new \stdClass(); |
|
| 177 | - $itemClass->name = $item['name']; |
|
| 178 | - $itemClass->endpoints = array_map(function (array $endpoint) { |
|
| 179 | - return (object) $endpoint; |
|
| 180 | - }, $item['endpoints']); |
|
| 181 | - $itemClass->type = $item['type']; |
|
| 182 | - |
|
| 183 | - return $itemClass; |
|
| 184 | - }, $cachedToken['catalog']); |
|
| 185 | - try { |
|
| 186 | - $this->client->importCredentials($cachedToken); |
|
| 187 | - } catch (\Exception $e) { |
|
| 188 | - $this->client->setTokenObject(new Token()); |
|
| 189 | - } |
|
| 190 | - } |
|
| 191 | - } |
|
| 192 | - |
|
| 193 | - /** |
|
| 194 | - * @param Catalog $catalog |
|
| 195 | - * @param $name |
|
| 196 | - * @return null|CatalogItem |
|
| 197 | - */ |
|
| 198 | - private function getCatalogForService(Catalog $catalog, $name) { |
|
| 199 | - foreach ($catalog->getItems() as $item) { |
|
| 200 | - /** @var CatalogItem $item */ |
|
| 201 | - if ($item->hasType(Service::DEFAULT_TYPE) && $item->hasName($name)) { |
|
| 202 | - return $item; |
|
| 203 | - } |
|
| 204 | - } |
|
| 205 | - |
|
| 206 | - return null; |
|
| 207 | - } |
|
| 208 | - |
|
| 209 | - private function validateRegion(CatalogItem $item, $region) { |
|
| 210 | - $endPoints = $item->getEndpoints(); |
|
| 211 | - foreach ($endPoints as $endPoint) { |
|
| 212 | - if ($endPoint->region === $region) { |
|
| 213 | - return; |
|
| 214 | - } |
|
| 215 | - } |
|
| 216 | - |
|
| 217 | - $availableRegions = implode(', ', array_map(function ($endpoint) { |
|
| 218 | - return $endpoint->region; |
|
| 219 | - }, $endPoints)); |
|
| 220 | - |
|
| 221 | - throw new StorageNotAvailableException("Invalid region '$region', available regions: $availableRegions"); |
|
| 222 | - } |
|
| 223 | - |
|
| 224 | - private function getAvailableServiceNames(Catalog $catalog) { |
|
| 225 | - return array_map(function (CatalogItem $item) { |
|
| 226 | - return $item->getName(); |
|
| 227 | - }, array_filter($catalog->getItems(), function (CatalogItem $item) { |
|
| 228 | - return $item->hasType(Service::DEFAULT_TYPE); |
|
| 229 | - })); |
|
| 230 | - } |
|
| 231 | - |
|
| 232 | - /** |
|
| 233 | - * @return string the container name where objects are stored |
|
| 234 | - */ |
|
| 235 | - public function getStorageId() { |
|
| 236 | - return $this->params['container']; |
|
| 237 | - } |
|
| 238 | - |
|
| 239 | - /** |
|
| 240 | - * @param string $urn the unified resource name used to identify the object |
|
| 241 | - * @param resource $stream stream with the data to write |
|
| 242 | - * @throws Exception from openstack lib when something goes wrong |
|
| 243 | - */ |
|
| 244 | - public function writeObject($urn, $stream) { |
|
| 245 | - $this->init(); |
|
| 246 | - $this->container->uploadObject($urn, $stream); |
|
| 247 | - } |
|
| 248 | - |
|
| 249 | - /** |
|
| 250 | - * @param string $urn the unified resource name used to identify the object |
|
| 251 | - * @return resource stream with the read data |
|
| 252 | - * @throws Exception from openstack lib when something goes wrong |
|
| 253 | - */ |
|
| 254 | - public function readObject($urn) { |
|
| 255 | - $this->init(); |
|
| 256 | - $object = $this->container->getObject($urn); |
|
| 257 | - |
|
| 258 | - // we need to keep a reference to objectContent or |
|
| 259 | - // the stream will be closed before we can do anything with it |
|
| 260 | - /** @var $objectContent \Guzzle\Http\EntityBody * */ |
|
| 261 | - $objectContent = $object->getContent(); |
|
| 262 | - $objectContent->rewind(); |
|
| 263 | - |
|
| 264 | - $stream = $objectContent->getStream(); |
|
| 265 | - // save the object content in the context of the stream to prevent it being gc'd until the stream is closed |
|
| 266 | - stream_context_set_option($stream, 'swift', 'content', $objectContent); |
|
| 267 | - |
|
| 268 | - RetryWrapper::wrap($stream); |
|
| 269 | - } |
|
| 270 | - |
|
| 271 | - /** |
|
| 272 | - * @param string $urn Unified Resource Name |
|
| 273 | - * @return void |
|
| 274 | - * @throws Exception from openstack lib when something goes wrong |
|
| 275 | - */ |
|
| 276 | - public function deleteObject($urn) { |
|
| 277 | - $this->init(); |
|
| 278 | - // see https://github.com/rackspace/php-opencloud/issues/243#issuecomment-30032242 |
|
| 279 | - $this->container->dataObject()->setName($urn)->delete(); |
|
| 280 | - } |
|
| 281 | - |
|
| 282 | - public function deleteContainer($recursive = false) { |
|
| 283 | - $this->init(); |
|
| 284 | - $this->container->delete($recursive); |
|
| 285 | - } |
|
| 42 | + /** |
|
| 43 | + * @var \OpenCloud\OpenStack |
|
| 44 | + */ |
|
| 45 | + private $client; |
|
| 46 | + |
|
| 47 | + /** |
|
| 48 | + * @var array |
|
| 49 | + */ |
|
| 50 | + private $params; |
|
| 51 | + |
|
| 52 | + /** |
|
| 53 | + * @var \OpenCloud\ObjectStore\Service |
|
| 54 | + */ |
|
| 55 | + private $objectStoreService; |
|
| 56 | + |
|
| 57 | + /** |
|
| 58 | + * @var \OpenCloud\ObjectStore\Resource\Container |
|
| 59 | + */ |
|
| 60 | + private $container; |
|
| 61 | + |
|
| 62 | + private $memcache; |
|
| 63 | + |
|
| 64 | + public function __construct($params) { |
|
| 65 | + if (isset($params['bucket'])) { |
|
| 66 | + $params['container'] = $params['bucket']; |
|
| 67 | + } |
|
| 68 | + if (!isset($params['container'])) { |
|
| 69 | + $params['container'] = 'owncloud'; |
|
| 70 | + } |
|
| 71 | + if (!isset($params['autocreate'])) { |
|
| 72 | + // should only be true for tests |
|
| 73 | + $params['autocreate'] = false; |
|
| 74 | + } |
|
| 75 | + |
|
| 76 | + if (isset($params['apiKey'])) { |
|
| 77 | + $this->client = new Rackspace($params['url'], $params); |
|
| 78 | + $cacheKey = $params['username'] . '@' . $params['url'] . '/' . $params['bucket']; |
|
| 79 | + } else { |
|
| 80 | + $this->client = new OpenStack($params['url'], $params); |
|
| 81 | + $cacheKey = $params['username'] . '@' . $params['url'] . '/' . $params['bucket']; |
|
| 82 | + } |
|
| 83 | + |
|
| 84 | + $cacheFactory = \OC::$server->getMemCacheFactory(); |
|
| 85 | + $this->memcache = $cacheFactory->createDistributed('swift::' . $cacheKey); |
|
| 86 | + |
|
| 87 | + $this->params = $params; |
|
| 88 | + } |
|
| 89 | + |
|
| 90 | + protected function init() { |
|
| 91 | + if ($this->container) { |
|
| 92 | + return; |
|
| 93 | + } |
|
| 94 | + |
|
| 95 | + $this->importToken(); |
|
| 96 | + |
|
| 97 | + /** @var Token $token */ |
|
| 98 | + $token = $this->client->getTokenObject(); |
|
| 99 | + |
|
| 100 | + if (!$token || $token->hasExpired()) { |
|
| 101 | + try { |
|
| 102 | + $this->client->authenticate(); |
|
| 103 | + $this->exportToken(); |
|
| 104 | + } catch (ClientErrorResponseException $e) { |
|
| 105 | + $statusCode = $e->getResponse()->getStatusCode(); |
|
| 106 | + if ($statusCode == 412) { |
|
| 107 | + throw new StorageAuthException('Precondition failed, verify the keystone url', $e); |
|
| 108 | + } else if ($statusCode === 401) { |
|
| 109 | + throw new StorageAuthException('Authentication failed, verify the username, password and possibly tenant', $e); |
|
| 110 | + } else { |
|
| 111 | + throw new StorageAuthException('Unknown error', $e); |
|
| 112 | + } |
|
| 113 | + } |
|
| 114 | + } |
|
| 115 | + |
|
| 116 | + |
|
| 117 | + /** @var Catalog $catalog */ |
|
| 118 | + $catalog = $this->client->getCatalog(); |
|
| 119 | + |
|
| 120 | + if (isset($this->params['serviceName'])) { |
|
| 121 | + $serviceName = $this->params['serviceName']; |
|
| 122 | + } else { |
|
| 123 | + $serviceName = Service::DEFAULT_NAME; |
|
| 124 | + } |
|
| 125 | + |
|
| 126 | + if (isset($this->params['urlType'])) { |
|
| 127 | + $urlType = $this->params['urlType']; |
|
| 128 | + if ($urlType !== 'internalURL' && $urlType !== 'publicURL') { |
|
| 129 | + throw new StorageNotAvailableException('Invalid url type'); |
|
| 130 | + } |
|
| 131 | + } else { |
|
| 132 | + $urlType = Service::DEFAULT_URL_TYPE; |
|
| 133 | + } |
|
| 134 | + |
|
| 135 | + $catalogItem = $this->getCatalogForService($catalog, $serviceName); |
|
| 136 | + if (!$catalogItem) { |
|
| 137 | + $available = implode(', ', $this->getAvailableServiceNames($catalog)); |
|
| 138 | + throw new StorageNotAvailableException( |
|
| 139 | + "Service $serviceName not found in service catalog, available services: $available" |
|
| 140 | + ); |
|
| 141 | + } else if (isset($this->params['region'])) { |
|
| 142 | + $this->validateRegion($catalogItem, $this->params['region']); |
|
| 143 | + } |
|
| 144 | + |
|
| 145 | + $this->objectStoreService = $this->client->objectStoreService($serviceName, $this->params['region'], $urlType); |
|
| 146 | + |
|
| 147 | + try { |
|
| 148 | + $this->container = $this->objectStoreService->getContainer($this->params['container']); |
|
| 149 | + } catch (ClientErrorResponseException $ex) { |
|
| 150 | + // if the container does not exist and autocreate is true try to create the container on the fly |
|
| 151 | + if (isset($this->params['autocreate']) && $this->params['autocreate'] === true) { |
|
| 152 | + $this->container = $this->objectStoreService->createContainer($this->params['container']); |
|
| 153 | + } else { |
|
| 154 | + throw $ex; |
|
| 155 | + } |
|
| 156 | + } |
|
| 157 | + } |
|
| 158 | + |
|
| 159 | + private function exportToken() { |
|
| 160 | + $export = $this->client->exportCredentials(); |
|
| 161 | + $export['catalog'] = array_map(function (CatalogItem $item) { |
|
| 162 | + return [ |
|
| 163 | + 'name' => $item->getName(), |
|
| 164 | + 'endpoints' => $item->getEndpoints(), |
|
| 165 | + 'type' => $item->getType() |
|
| 166 | + ]; |
|
| 167 | + }, $export['catalog']->getItems()); |
|
| 168 | + $this->memcache->set('token', json_encode($export)); |
|
| 169 | + } |
|
| 170 | + |
|
| 171 | + private function importToken() { |
|
| 172 | + $cachedTokenString = $this->memcache->get('token'); |
|
| 173 | + if ($cachedTokenString) { |
|
| 174 | + $cachedToken = json_decode($cachedTokenString, true); |
|
| 175 | + $cachedToken['catalog'] = array_map(function (array $item) { |
|
| 176 | + $itemClass = new \stdClass(); |
|
| 177 | + $itemClass->name = $item['name']; |
|
| 178 | + $itemClass->endpoints = array_map(function (array $endpoint) { |
|
| 179 | + return (object) $endpoint; |
|
| 180 | + }, $item['endpoints']); |
|
| 181 | + $itemClass->type = $item['type']; |
|
| 182 | + |
|
| 183 | + return $itemClass; |
|
| 184 | + }, $cachedToken['catalog']); |
|
| 185 | + try { |
|
| 186 | + $this->client->importCredentials($cachedToken); |
|
| 187 | + } catch (\Exception $e) { |
|
| 188 | + $this->client->setTokenObject(new Token()); |
|
| 189 | + } |
|
| 190 | + } |
|
| 191 | + } |
|
| 192 | + |
|
| 193 | + /** |
|
| 194 | + * @param Catalog $catalog |
|
| 195 | + * @param $name |
|
| 196 | + * @return null|CatalogItem |
|
| 197 | + */ |
|
| 198 | + private function getCatalogForService(Catalog $catalog, $name) { |
|
| 199 | + foreach ($catalog->getItems() as $item) { |
|
| 200 | + /** @var CatalogItem $item */ |
|
| 201 | + if ($item->hasType(Service::DEFAULT_TYPE) && $item->hasName($name)) { |
|
| 202 | + return $item; |
|
| 203 | + } |
|
| 204 | + } |
|
| 205 | + |
|
| 206 | + return null; |
|
| 207 | + } |
|
| 208 | + |
|
| 209 | + private function validateRegion(CatalogItem $item, $region) { |
|
| 210 | + $endPoints = $item->getEndpoints(); |
|
| 211 | + foreach ($endPoints as $endPoint) { |
|
| 212 | + if ($endPoint->region === $region) { |
|
| 213 | + return; |
|
| 214 | + } |
|
| 215 | + } |
|
| 216 | + |
|
| 217 | + $availableRegions = implode(', ', array_map(function ($endpoint) { |
|
| 218 | + return $endpoint->region; |
|
| 219 | + }, $endPoints)); |
|
| 220 | + |
|
| 221 | + throw new StorageNotAvailableException("Invalid region '$region', available regions: $availableRegions"); |
|
| 222 | + } |
|
| 223 | + |
|
| 224 | + private function getAvailableServiceNames(Catalog $catalog) { |
|
| 225 | + return array_map(function (CatalogItem $item) { |
|
| 226 | + return $item->getName(); |
|
| 227 | + }, array_filter($catalog->getItems(), function (CatalogItem $item) { |
|
| 228 | + return $item->hasType(Service::DEFAULT_TYPE); |
|
| 229 | + })); |
|
| 230 | + } |
|
| 231 | + |
|
| 232 | + /** |
|
| 233 | + * @return string the container name where objects are stored |
|
| 234 | + */ |
|
| 235 | + public function getStorageId() { |
|
| 236 | + return $this->params['container']; |
|
| 237 | + } |
|
| 238 | + |
|
| 239 | + /** |
|
| 240 | + * @param string $urn the unified resource name used to identify the object |
|
| 241 | + * @param resource $stream stream with the data to write |
|
| 242 | + * @throws Exception from openstack lib when something goes wrong |
|
| 243 | + */ |
|
| 244 | + public function writeObject($urn, $stream) { |
|
| 245 | + $this->init(); |
|
| 246 | + $this->container->uploadObject($urn, $stream); |
|
| 247 | + } |
|
| 248 | + |
|
| 249 | + /** |
|
| 250 | + * @param string $urn the unified resource name used to identify the object |
|
| 251 | + * @return resource stream with the read data |
|
| 252 | + * @throws Exception from openstack lib when something goes wrong |
|
| 253 | + */ |
|
| 254 | + public function readObject($urn) { |
|
| 255 | + $this->init(); |
|
| 256 | + $object = $this->container->getObject($urn); |
|
| 257 | + |
|
| 258 | + // we need to keep a reference to objectContent or |
|
| 259 | + // the stream will be closed before we can do anything with it |
|
| 260 | + /** @var $objectContent \Guzzle\Http\EntityBody * */ |
|
| 261 | + $objectContent = $object->getContent(); |
|
| 262 | + $objectContent->rewind(); |
|
| 263 | + |
|
| 264 | + $stream = $objectContent->getStream(); |
|
| 265 | + // save the object content in the context of the stream to prevent it being gc'd until the stream is closed |
|
| 266 | + stream_context_set_option($stream, 'swift', 'content', $objectContent); |
|
| 267 | + |
|
| 268 | + RetryWrapper::wrap($stream); |
|
| 269 | + } |
|
| 270 | + |
|
| 271 | + /** |
|
| 272 | + * @param string $urn Unified Resource Name |
|
| 273 | + * @return void |
|
| 274 | + * @throws Exception from openstack lib when something goes wrong |
|
| 275 | + */ |
|
| 276 | + public function deleteObject($urn) { |
|
| 277 | + $this->init(); |
|
| 278 | + // see https://github.com/rackspace/php-opencloud/issues/243#issuecomment-30032242 |
|
| 279 | + $this->container->dataObject()->setName($urn)->delete(); |
|
| 280 | + } |
|
| 281 | + |
|
| 282 | + public function deleteContainer($recursive = false) { |
|
| 283 | + $this->init(); |
|
| 284 | + $this->container->delete($recursive); |
|
| 285 | + } |
|
| 286 | 286 | |
| 287 | 287 | } |
@@ -75,14 +75,14 @@ discard block |
||
| 75 | 75 | |
| 76 | 76 | if (isset($params['apiKey'])) { |
| 77 | 77 | $this->client = new Rackspace($params['url'], $params); |
| 78 | - $cacheKey = $params['username'] . '@' . $params['url'] . '/' . $params['bucket']; |
|
| 78 | + $cacheKey = $params['username'].'@'.$params['url'].'/'.$params['bucket']; |
|
| 79 | 79 | } else { |
| 80 | 80 | $this->client = new OpenStack($params['url'], $params); |
| 81 | - $cacheKey = $params['username'] . '@' . $params['url'] . '/' . $params['bucket']; |
|
| 81 | + $cacheKey = $params['username'].'@'.$params['url'].'/'.$params['bucket']; |
|
| 82 | 82 | } |
| 83 | 83 | |
| 84 | 84 | $cacheFactory = \OC::$server->getMemCacheFactory(); |
| 85 | - $this->memcache = $cacheFactory->createDistributed('swift::' . $cacheKey); |
|
| 85 | + $this->memcache = $cacheFactory->createDistributed('swift::'.$cacheKey); |
|
| 86 | 86 | |
| 87 | 87 | $this->params = $params; |
| 88 | 88 | } |
@@ -158,7 +158,7 @@ discard block |
||
| 158 | 158 | |
| 159 | 159 | private function exportToken() { |
| 160 | 160 | $export = $this->client->exportCredentials(); |
| 161 | - $export['catalog'] = array_map(function (CatalogItem $item) { |
|
| 161 | + $export['catalog'] = array_map(function(CatalogItem $item) { |
|
| 162 | 162 | return [ |
| 163 | 163 | 'name' => $item->getName(), |
| 164 | 164 | 'endpoints' => $item->getEndpoints(), |
@@ -172,10 +172,10 @@ discard block |
||
| 172 | 172 | $cachedTokenString = $this->memcache->get('token'); |
| 173 | 173 | if ($cachedTokenString) { |
| 174 | 174 | $cachedToken = json_decode($cachedTokenString, true); |
| 175 | - $cachedToken['catalog'] = array_map(function (array $item) { |
|
| 175 | + $cachedToken['catalog'] = array_map(function(array $item) { |
|
| 176 | 176 | $itemClass = new \stdClass(); |
| 177 | 177 | $itemClass->name = $item['name']; |
| 178 | - $itemClass->endpoints = array_map(function (array $endpoint) { |
|
| 178 | + $itemClass->endpoints = array_map(function(array $endpoint) { |
|
| 179 | 179 | return (object) $endpoint; |
| 180 | 180 | }, $item['endpoints']); |
| 181 | 181 | $itemClass->type = $item['type']; |
@@ -214,7 +214,7 @@ discard block |
||
| 214 | 214 | } |
| 215 | 215 | } |
| 216 | 216 | |
| 217 | - $availableRegions = implode(', ', array_map(function ($endpoint) { |
|
| 217 | + $availableRegions = implode(', ', array_map(function($endpoint) { |
|
| 218 | 218 | return $endpoint->region; |
| 219 | 219 | }, $endPoints)); |
| 220 | 220 | |
@@ -222,9 +222,9 @@ discard block |
||
| 222 | 222 | } |
| 223 | 223 | |
| 224 | 224 | private function getAvailableServiceNames(Catalog $catalog) { |
| 225 | - return array_map(function (CatalogItem $item) { |
|
| 225 | + return array_map(function(CatalogItem $item) { |
|
| 226 | 226 | return $item->getName(); |
| 227 | - }, array_filter($catalog->getItems(), function (CatalogItem $item) { |
|
| 227 | + }, array_filter($catalog->getItems(), function(CatalogItem $item) { |
|
| 228 | 228 | return $item->hasType(Service::DEFAULT_TYPE); |
| 229 | 229 | })); |
| 230 | 230 | } |
@@ -34,85 +34,85 @@ |
||
| 34 | 34 | * @package OC\Security\RateLimiting\Backend |
| 35 | 35 | */ |
| 36 | 36 | class MemoryCache implements IBackend { |
| 37 | - /** @var ICache */ |
|
| 38 | - private $cache; |
|
| 39 | - /** @var ITimeFactory */ |
|
| 40 | - private $timeFactory; |
|
| 37 | + /** @var ICache */ |
|
| 38 | + private $cache; |
|
| 39 | + /** @var ITimeFactory */ |
|
| 40 | + private $timeFactory; |
|
| 41 | 41 | |
| 42 | - /** |
|
| 43 | - * @param ICacheFactory $cacheFactory |
|
| 44 | - * @param ITimeFactory $timeFactory |
|
| 45 | - */ |
|
| 46 | - public function __construct(ICacheFactory $cacheFactory, |
|
| 47 | - ITimeFactory $timeFactory) { |
|
| 48 | - $this->cache = $cacheFactory->createDistributed(__CLASS__); |
|
| 49 | - $this->timeFactory = $timeFactory; |
|
| 50 | - } |
|
| 42 | + /** |
|
| 43 | + * @param ICacheFactory $cacheFactory |
|
| 44 | + * @param ITimeFactory $timeFactory |
|
| 45 | + */ |
|
| 46 | + public function __construct(ICacheFactory $cacheFactory, |
|
| 47 | + ITimeFactory $timeFactory) { |
|
| 48 | + $this->cache = $cacheFactory->createDistributed(__CLASS__); |
|
| 49 | + $this->timeFactory = $timeFactory; |
|
| 50 | + } |
|
| 51 | 51 | |
| 52 | - /** |
|
| 53 | - * @param string $methodIdentifier |
|
| 54 | - * @param string $userIdentifier |
|
| 55 | - * @return string |
|
| 56 | - */ |
|
| 57 | - private function hash($methodIdentifier, |
|
| 58 | - $userIdentifier) { |
|
| 59 | - return hash('sha512', $methodIdentifier . $userIdentifier); |
|
| 60 | - } |
|
| 52 | + /** |
|
| 53 | + * @param string $methodIdentifier |
|
| 54 | + * @param string $userIdentifier |
|
| 55 | + * @return string |
|
| 56 | + */ |
|
| 57 | + private function hash($methodIdentifier, |
|
| 58 | + $userIdentifier) { |
|
| 59 | + return hash('sha512', $methodIdentifier . $userIdentifier); |
|
| 60 | + } |
|
| 61 | 61 | |
| 62 | - /** |
|
| 63 | - * @param string $identifier |
|
| 64 | - * @return array |
|
| 65 | - */ |
|
| 66 | - private function getExistingAttempts($identifier) { |
|
| 67 | - $cachedAttempts = json_decode($this->cache->get($identifier), true); |
|
| 68 | - if(is_array($cachedAttempts)) { |
|
| 69 | - return $cachedAttempts; |
|
| 70 | - } |
|
| 62 | + /** |
|
| 63 | + * @param string $identifier |
|
| 64 | + * @return array |
|
| 65 | + */ |
|
| 66 | + private function getExistingAttempts($identifier) { |
|
| 67 | + $cachedAttempts = json_decode($this->cache->get($identifier), true); |
|
| 68 | + if(is_array($cachedAttempts)) { |
|
| 69 | + return $cachedAttempts; |
|
| 70 | + } |
|
| 71 | 71 | |
| 72 | - return []; |
|
| 73 | - } |
|
| 72 | + return []; |
|
| 73 | + } |
|
| 74 | 74 | |
| 75 | - /** |
|
| 76 | - * {@inheritDoc} |
|
| 77 | - */ |
|
| 78 | - public function getAttempts($methodIdentifier, |
|
| 79 | - $userIdentifier, |
|
| 80 | - $seconds) { |
|
| 81 | - $identifier = $this->hash($methodIdentifier, $userIdentifier); |
|
| 82 | - $existingAttempts = $this->getExistingAttempts($identifier); |
|
| 75 | + /** |
|
| 76 | + * {@inheritDoc} |
|
| 77 | + */ |
|
| 78 | + public function getAttempts($methodIdentifier, |
|
| 79 | + $userIdentifier, |
|
| 80 | + $seconds) { |
|
| 81 | + $identifier = $this->hash($methodIdentifier, $userIdentifier); |
|
| 82 | + $existingAttempts = $this->getExistingAttempts($identifier); |
|
| 83 | 83 | |
| 84 | - $count = 0; |
|
| 85 | - $currentTime = $this->timeFactory->getTime(); |
|
| 86 | - /** @var array $existingAttempts */ |
|
| 87 | - foreach ($existingAttempts as $attempt) { |
|
| 88 | - if(($attempt + $seconds) > $currentTime) { |
|
| 89 | - $count++; |
|
| 90 | - } |
|
| 91 | - } |
|
| 84 | + $count = 0; |
|
| 85 | + $currentTime = $this->timeFactory->getTime(); |
|
| 86 | + /** @var array $existingAttempts */ |
|
| 87 | + foreach ($existingAttempts as $attempt) { |
|
| 88 | + if(($attempt + $seconds) > $currentTime) { |
|
| 89 | + $count++; |
|
| 90 | + } |
|
| 91 | + } |
|
| 92 | 92 | |
| 93 | - return $count; |
|
| 94 | - } |
|
| 93 | + return $count; |
|
| 94 | + } |
|
| 95 | 95 | |
| 96 | - /** |
|
| 97 | - * {@inheritDoc} |
|
| 98 | - */ |
|
| 99 | - public function registerAttempt($methodIdentifier, |
|
| 100 | - $userIdentifier, |
|
| 101 | - $period) { |
|
| 102 | - $identifier = $this->hash($methodIdentifier, $userIdentifier); |
|
| 103 | - $existingAttempts = $this->getExistingAttempts($identifier); |
|
| 104 | - $currentTime = $this->timeFactory->getTime(); |
|
| 96 | + /** |
|
| 97 | + * {@inheritDoc} |
|
| 98 | + */ |
|
| 99 | + public function registerAttempt($methodIdentifier, |
|
| 100 | + $userIdentifier, |
|
| 101 | + $period) { |
|
| 102 | + $identifier = $this->hash($methodIdentifier, $userIdentifier); |
|
| 103 | + $existingAttempts = $this->getExistingAttempts($identifier); |
|
| 104 | + $currentTime = $this->timeFactory->getTime(); |
|
| 105 | 105 | |
| 106 | - // Unset all attempts older than $period |
|
| 107 | - foreach ($existingAttempts as $key => $attempt) { |
|
| 108 | - if(($attempt + $period) < $currentTime) { |
|
| 109 | - unset($existingAttempts[$key]); |
|
| 110 | - } |
|
| 111 | - } |
|
| 112 | - $existingAttempts = array_values($existingAttempts); |
|
| 106 | + // Unset all attempts older than $period |
|
| 107 | + foreach ($existingAttempts as $key => $attempt) { |
|
| 108 | + if(($attempt + $period) < $currentTime) { |
|
| 109 | + unset($existingAttempts[$key]); |
|
| 110 | + } |
|
| 111 | + } |
|
| 112 | + $existingAttempts = array_values($existingAttempts); |
|
| 113 | 113 | |
| 114 | - // Store the new attempt |
|
| 115 | - $existingAttempts[] = (string)$currentTime; |
|
| 116 | - $this->cache->set($identifier, json_encode($existingAttempts)); |
|
| 117 | - } |
|
| 114 | + // Store the new attempt |
|
| 115 | + $existingAttempts[] = (string)$currentTime; |
|
| 116 | + $this->cache->set($identifier, json_encode($existingAttempts)); |
|
| 117 | + } |
|
| 118 | 118 | } |
@@ -33,94 +33,94 @@ |
||
| 33 | 33 | |
| 34 | 34 | class DiscoveryService implements IDiscoveryService { |
| 35 | 35 | |
| 36 | - /** @var ICache */ |
|
| 37 | - private $cache; |
|
| 38 | - |
|
| 39 | - /** @var IClient */ |
|
| 40 | - private $client; |
|
| 41 | - |
|
| 42 | - /** |
|
| 43 | - * @param ICacheFactory $cacheFactory |
|
| 44 | - * @param IClientService $clientService |
|
| 45 | - */ |
|
| 46 | - public function __construct(ICacheFactory $cacheFactory, |
|
| 47 | - IClientService $clientService |
|
| 48 | - ) { |
|
| 49 | - $this->cache = $cacheFactory->createDistributed('ocs-discovery'); |
|
| 50 | - $this->client = $clientService->newClient(); |
|
| 51 | - } |
|
| 52 | - |
|
| 53 | - |
|
| 54 | - /** |
|
| 55 | - * Discover OCS end-points |
|
| 56 | - * |
|
| 57 | - * If no valid discovery data is found the defaults are returned |
|
| 58 | - * |
|
| 59 | - * @param string $remote |
|
| 60 | - * @param string $service the service you want to discover |
|
| 61 | - * @return array |
|
| 62 | - */ |
|
| 63 | - public function discover($remote, $service) { |
|
| 64 | - // Check the cache first |
|
| 65 | - $cacheData = $this->cache->get($remote . '#' . $service); |
|
| 66 | - if($cacheData) { |
|
| 67 | - return json_decode($cacheData, true); |
|
| 68 | - } |
|
| 69 | - |
|
| 70 | - $discoveredServices = []; |
|
| 71 | - |
|
| 72 | - // query the remote server for available services |
|
| 73 | - try { |
|
| 74 | - $response = $this->client->get($remote . '/ocs-provider/', [ |
|
| 75 | - 'timeout' => 10, |
|
| 76 | - 'connect_timeout' => 10, |
|
| 77 | - ]); |
|
| 78 | - if($response->getStatusCode() === Http::STATUS_OK) { |
|
| 79 | - $decodedServices = json_decode($response->getBody(), true); |
|
| 80 | - $discoveredServices = $this->getEndpoints($decodedServices, $service); |
|
| 81 | - } |
|
| 82 | - } catch (\Exception $e) { |
|
| 83 | - // if we couldn't discover the service or any end-points we return a empty array |
|
| 84 | - } |
|
| 85 | - |
|
| 86 | - // Write into cache |
|
| 87 | - $this->cache->set($remote . '#' . $service, json_encode($discoveredServices), 60*60*24); |
|
| 88 | - return $discoveredServices; |
|
| 89 | - } |
|
| 90 | - |
|
| 91 | - /** |
|
| 92 | - * get requested end-points from the requested service |
|
| 93 | - * |
|
| 94 | - * @param $decodedServices |
|
| 95 | - * @param $service |
|
| 96 | - * @return array |
|
| 97 | - */ |
|
| 98 | - protected function getEndpoints($decodedServices, $service) { |
|
| 99 | - |
|
| 100 | - $discoveredServices = []; |
|
| 101 | - |
|
| 102 | - if(is_array($decodedServices) && |
|
| 103 | - isset($decodedServices['services'][$service]['endpoints']) |
|
| 104 | - ) { |
|
| 105 | - foreach ($decodedServices['services'][$service]['endpoints'] as $endpoint => $url) { |
|
| 106 | - if($this->isSafeUrl($url)) { |
|
| 107 | - $discoveredServices[$endpoint] = $url; |
|
| 108 | - } |
|
| 109 | - } |
|
| 110 | - } |
|
| 111 | - |
|
| 112 | - return $discoveredServices; |
|
| 113 | - } |
|
| 114 | - |
|
| 115 | - /** |
|
| 116 | - * Returns whether the specified URL includes only safe characters, if not |
|
| 117 | - * returns false |
|
| 118 | - * |
|
| 119 | - * @param string $url |
|
| 120 | - * @return bool |
|
| 121 | - */ |
|
| 122 | - protected function isSafeUrl($url) { |
|
| 123 | - return (bool)preg_match('/^[\/\.\-A-Za-z0-9]+$/', $url); |
|
| 124 | - } |
|
| 36 | + /** @var ICache */ |
|
| 37 | + private $cache; |
|
| 38 | + |
|
| 39 | + /** @var IClient */ |
|
| 40 | + private $client; |
|
| 41 | + |
|
| 42 | + /** |
|
| 43 | + * @param ICacheFactory $cacheFactory |
|
| 44 | + * @param IClientService $clientService |
|
| 45 | + */ |
|
| 46 | + public function __construct(ICacheFactory $cacheFactory, |
|
| 47 | + IClientService $clientService |
|
| 48 | + ) { |
|
| 49 | + $this->cache = $cacheFactory->createDistributed('ocs-discovery'); |
|
| 50 | + $this->client = $clientService->newClient(); |
|
| 51 | + } |
|
| 52 | + |
|
| 53 | + |
|
| 54 | + /** |
|
| 55 | + * Discover OCS end-points |
|
| 56 | + * |
|
| 57 | + * If no valid discovery data is found the defaults are returned |
|
| 58 | + * |
|
| 59 | + * @param string $remote |
|
| 60 | + * @param string $service the service you want to discover |
|
| 61 | + * @return array |
|
| 62 | + */ |
|
| 63 | + public function discover($remote, $service) { |
|
| 64 | + // Check the cache first |
|
| 65 | + $cacheData = $this->cache->get($remote . '#' . $service); |
|
| 66 | + if($cacheData) { |
|
| 67 | + return json_decode($cacheData, true); |
|
| 68 | + } |
|
| 69 | + |
|
| 70 | + $discoveredServices = []; |
|
| 71 | + |
|
| 72 | + // query the remote server for available services |
|
| 73 | + try { |
|
| 74 | + $response = $this->client->get($remote . '/ocs-provider/', [ |
|
| 75 | + 'timeout' => 10, |
|
| 76 | + 'connect_timeout' => 10, |
|
| 77 | + ]); |
|
| 78 | + if($response->getStatusCode() === Http::STATUS_OK) { |
|
| 79 | + $decodedServices = json_decode($response->getBody(), true); |
|
| 80 | + $discoveredServices = $this->getEndpoints($decodedServices, $service); |
|
| 81 | + } |
|
| 82 | + } catch (\Exception $e) { |
|
| 83 | + // if we couldn't discover the service or any end-points we return a empty array |
|
| 84 | + } |
|
| 85 | + |
|
| 86 | + // Write into cache |
|
| 87 | + $this->cache->set($remote . '#' . $service, json_encode($discoveredServices), 60*60*24); |
|
| 88 | + return $discoveredServices; |
|
| 89 | + } |
|
| 90 | + |
|
| 91 | + /** |
|
| 92 | + * get requested end-points from the requested service |
|
| 93 | + * |
|
| 94 | + * @param $decodedServices |
|
| 95 | + * @param $service |
|
| 96 | + * @return array |
|
| 97 | + */ |
|
| 98 | + protected function getEndpoints($decodedServices, $service) { |
|
| 99 | + |
|
| 100 | + $discoveredServices = []; |
|
| 101 | + |
|
| 102 | + if(is_array($decodedServices) && |
|
| 103 | + isset($decodedServices['services'][$service]['endpoints']) |
|
| 104 | + ) { |
|
| 105 | + foreach ($decodedServices['services'][$service]['endpoints'] as $endpoint => $url) { |
|
| 106 | + if($this->isSafeUrl($url)) { |
|
| 107 | + $discoveredServices[$endpoint] = $url; |
|
| 108 | + } |
|
| 109 | + } |
|
| 110 | + } |
|
| 111 | + |
|
| 112 | + return $discoveredServices; |
|
| 113 | + } |
|
| 114 | + |
|
| 115 | + /** |
|
| 116 | + * Returns whether the specified URL includes only safe characters, if not |
|
| 117 | + * returns false |
|
| 118 | + * |
|
| 119 | + * @param string $url |
|
| 120 | + * @return bool |
|
| 121 | + */ |
|
| 122 | + protected function isSafeUrl($url) { |
|
| 123 | + return (bool)preg_match('/^[\/\.\-A-Za-z0-9]+$/', $url); |
|
| 124 | + } |
|
| 125 | 125 | |
| 126 | 126 | } |
@@ -46,208 +46,208 @@ |
||
| 46 | 46 | * Class to generate URLs |
| 47 | 47 | */ |
| 48 | 48 | class URLGenerator implements IURLGenerator { |
| 49 | - /** @var IConfig */ |
|
| 50 | - private $config; |
|
| 51 | - /** @var ICacheFactory */ |
|
| 52 | - private $cacheFactory; |
|
| 53 | - /** @var IRequest */ |
|
| 54 | - private $request; |
|
| 55 | - |
|
| 56 | - /** |
|
| 57 | - * @param IConfig $config |
|
| 58 | - * @param ICacheFactory $cacheFactory |
|
| 59 | - * @param IRequest $request |
|
| 60 | - */ |
|
| 61 | - public function __construct(IConfig $config, |
|
| 62 | - ICacheFactory $cacheFactory, |
|
| 63 | - IRequest $request) { |
|
| 64 | - $this->config = $config; |
|
| 65 | - $this->cacheFactory = $cacheFactory; |
|
| 66 | - $this->request = $request; |
|
| 67 | - } |
|
| 68 | - |
|
| 69 | - /** |
|
| 70 | - * Creates an url using a defined route |
|
| 71 | - * @param string $route |
|
| 72 | - * @param array $parameters args with param=>value, will be appended to the returned url |
|
| 73 | - * @return string the url |
|
| 74 | - * |
|
| 75 | - * Returns a url to the given route. |
|
| 76 | - */ |
|
| 77 | - public function linkToRoute($route, $parameters = array()) { |
|
| 78 | - // TODO: mock router |
|
| 79 | - $urlLinkTo = \OC::$server->getRouter()->generate($route, $parameters); |
|
| 80 | - return $urlLinkTo; |
|
| 81 | - } |
|
| 82 | - |
|
| 83 | - /** |
|
| 84 | - * Creates an absolute url using a defined route |
|
| 85 | - * @param string $routeName |
|
| 86 | - * @param array $arguments args with param=>value, will be appended to the returned url |
|
| 87 | - * @return string the url |
|
| 88 | - * |
|
| 89 | - * Returns an absolute url to the given route. |
|
| 90 | - */ |
|
| 91 | - public function linkToRouteAbsolute($routeName, $arguments = array()) { |
|
| 92 | - return $this->getAbsoluteURL($this->linkToRoute($routeName, $arguments)); |
|
| 93 | - } |
|
| 94 | - |
|
| 95 | - /** |
|
| 96 | - * Creates an url |
|
| 97 | - * @param string $app app |
|
| 98 | - * @param string $file file |
|
| 99 | - * @param array $args array with param=>value, will be appended to the returned url |
|
| 100 | - * The value of $args will be urlencoded |
|
| 101 | - * @return string the url |
|
| 102 | - * |
|
| 103 | - * Returns a url to the given app and file. |
|
| 104 | - */ |
|
| 105 | - public function linkTo( $app, $file, $args = array() ) { |
|
| 106 | - $frontControllerActive = ($this->config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true'); |
|
| 107 | - |
|
| 108 | - if( $app != '' ) { |
|
| 109 | - $app_path = \OC_App::getAppPath($app); |
|
| 110 | - // Check if the app is in the app folder |
|
| 111 | - if ($app_path && file_exists($app_path . '/' . $file)) { |
|
| 112 | - if (substr($file, -3) == 'php') { |
|
| 113 | - |
|
| 114 | - $urlLinkTo = \OC::$WEBROOT . '/index.php/apps/' . $app; |
|
| 115 | - if ($frontControllerActive) { |
|
| 116 | - $urlLinkTo = \OC::$WEBROOT . '/apps/' . $app; |
|
| 117 | - } |
|
| 118 | - $urlLinkTo .= ($file != 'index.php') ? '/' . $file : ''; |
|
| 119 | - } else { |
|
| 120 | - $urlLinkTo = \OC_App::getAppWebPath($app) . '/' . $file; |
|
| 121 | - } |
|
| 122 | - } else { |
|
| 123 | - $urlLinkTo = \OC::$WEBROOT . '/' . $app . '/' . $file; |
|
| 124 | - } |
|
| 125 | - } else { |
|
| 126 | - if (file_exists(\OC::$SERVERROOT . '/core/' . $file)) { |
|
| 127 | - $urlLinkTo = \OC::$WEBROOT . '/core/' . $file; |
|
| 128 | - } else { |
|
| 129 | - if ($frontControllerActive && $file === 'index.php') { |
|
| 130 | - $urlLinkTo = \OC::$WEBROOT . '/'; |
|
| 131 | - } else { |
|
| 132 | - $urlLinkTo = \OC::$WEBROOT . '/' . $file; |
|
| 133 | - } |
|
| 134 | - } |
|
| 135 | - } |
|
| 136 | - |
|
| 137 | - if ($args && $query = http_build_query($args, '', '&')) { |
|
| 138 | - $urlLinkTo .= '?' . $query; |
|
| 139 | - } |
|
| 140 | - |
|
| 141 | - return $urlLinkTo; |
|
| 142 | - } |
|
| 143 | - |
|
| 144 | - /** |
|
| 145 | - * Creates path to an image |
|
| 146 | - * @param string $app app |
|
| 147 | - * @param string $image image name |
|
| 148 | - * @throws \RuntimeException If the image does not exist |
|
| 149 | - * @return string the url |
|
| 150 | - * |
|
| 151 | - * Returns the path to the image. |
|
| 152 | - */ |
|
| 153 | - public function imagePath($app, $image) { |
|
| 154 | - $cache = $this->cacheFactory->createDistributed('imagePath-'.md5($this->getBaseUrl()).'-'); |
|
| 155 | - $cacheKey = $app.'-'.$image; |
|
| 156 | - if($key = $cache->get($cacheKey)) { |
|
| 157 | - return $key; |
|
| 158 | - } |
|
| 159 | - |
|
| 160 | - // Read the selected theme from the config file |
|
| 161 | - $theme = \OC_Util::getTheme(); |
|
| 162 | - |
|
| 163 | - //if a theme has a png but not an svg always use the png |
|
| 164 | - $basename = substr(basename($image),0,-4); |
|
| 165 | - |
|
| 166 | - $appPath = \OC_App::getAppPath($app); |
|
| 167 | - |
|
| 168 | - // Check if the app is in the app folder |
|
| 169 | - $path = ''; |
|
| 170 | - $themingEnabled = $this->config->getSystemValue('installed', false) && \OCP\App::isEnabled('theming') && \OC_App::isAppLoaded('theming'); |
|
| 171 | - $themingImagePath = false; |
|
| 172 | - if($themingEnabled) { |
|
| 173 | - $themingImagePath = \OC::$server->getThemingDefaults()->replaceImagePath($app, $image); |
|
| 174 | - } |
|
| 175 | - |
|
| 176 | - if (file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$app/img/$image")) { |
|
| 177 | - $path = \OC::$WEBROOT . "/themes/$theme/apps/$app/img/$image"; |
|
| 178 | - } elseif (!file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$app/img/$basename.svg") |
|
| 179 | - && file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$app/img/$basename.png")) { |
|
| 180 | - $path = \OC::$WEBROOT . "/themes/$theme/apps/$app/img/$basename.png"; |
|
| 181 | - } elseif (!empty($app) and file_exists(\OC::$SERVERROOT . "/themes/$theme/$app/img/$image")) { |
|
| 182 | - $path = \OC::$WEBROOT . "/themes/$theme/$app/img/$image"; |
|
| 183 | - } elseif (!empty($app) and (!file_exists(\OC::$SERVERROOT . "/themes/$theme/$app/img/$basename.svg") |
|
| 184 | - && file_exists(\OC::$SERVERROOT . "/themes/$theme/$app/img/$basename.png"))) { |
|
| 185 | - $path = \OC::$WEBROOT . "/themes/$theme/$app/img/$basename.png"; |
|
| 186 | - } elseif (file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$image")) { |
|
| 187 | - $path = \OC::$WEBROOT . "/themes/$theme/core/img/$image"; |
|
| 188 | - } elseif (!file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$basename.svg") |
|
| 189 | - && file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$basename.png")) { |
|
| 190 | - $path = \OC::$WEBROOT . "/themes/$theme/core/img/$basename.png"; |
|
| 191 | - } elseif($themingEnabled && $themingImagePath) { |
|
| 192 | - $path = $themingImagePath; |
|
| 193 | - } elseif ($appPath && file_exists($appPath . "/img/$image")) { |
|
| 194 | - $path = \OC_App::getAppWebPath($app) . "/img/$image"; |
|
| 195 | - } elseif ($appPath && !file_exists($appPath . "/img/$basename.svg") |
|
| 196 | - && file_exists($appPath . "/img/$basename.png")) { |
|
| 197 | - $path = \OC_App::getAppWebPath($app) . "/img/$basename.png"; |
|
| 198 | - } elseif (!empty($app) and file_exists(\OC::$SERVERROOT . "/$app/img/$image")) { |
|
| 199 | - $path = \OC::$WEBROOT . "/$app/img/$image"; |
|
| 200 | - } elseif (!empty($app) and (!file_exists(\OC::$SERVERROOT . "/$app/img/$basename.svg") |
|
| 201 | - && file_exists(\OC::$SERVERROOT . "/$app/img/$basename.png"))) { |
|
| 202 | - $path = \OC::$WEBROOT . "/$app/img/$basename.png"; |
|
| 203 | - } elseif (file_exists(\OC::$SERVERROOT . "/core/img/$image")) { |
|
| 204 | - $path = \OC::$WEBROOT . "/core/img/$image"; |
|
| 205 | - } elseif (!file_exists(\OC::$SERVERROOT . "/core/img/$basename.svg") |
|
| 206 | - && file_exists(\OC::$SERVERROOT . "/core/img/$basename.png")) { |
|
| 207 | - $path = \OC::$WEBROOT . "/themes/$theme/core/img/$basename.png"; |
|
| 208 | - } |
|
| 209 | - |
|
| 210 | - if($path !== '') { |
|
| 211 | - $cache->set($cacheKey, $path); |
|
| 212 | - return $path; |
|
| 213 | - } else { |
|
| 214 | - throw new RuntimeException('image not found: image:' . $image . ' webroot:' . \OC::$WEBROOT . ' serverroot:' . \OC::$SERVERROOT); |
|
| 215 | - } |
|
| 216 | - } |
|
| 217 | - |
|
| 218 | - |
|
| 219 | - /** |
|
| 220 | - * Makes an URL absolute |
|
| 221 | - * @param string $url the url in the ownCloud host |
|
| 222 | - * @return string the absolute version of the url |
|
| 223 | - */ |
|
| 224 | - public function getAbsoluteURL($url) { |
|
| 225 | - $separator = $url[0] === '/' ? '' : '/'; |
|
| 226 | - |
|
| 227 | - if (\OC::$CLI && !defined('PHPUNIT_RUN')) { |
|
| 228 | - return rtrim($this->config->getSystemValue('overwrite.cli.url'), '/') . '/' . ltrim($url, '/'); |
|
| 229 | - } |
|
| 230 | - // The ownCloud web root can already be prepended. |
|
| 231 | - if(substr($url, 0, strlen(\OC::$WEBROOT)) === \OC::$WEBROOT) { |
|
| 232 | - $url = substr($url, strlen(\OC::$WEBROOT)); |
|
| 233 | - } |
|
| 234 | - |
|
| 235 | - return $this->getBaseUrl() . $separator . $url; |
|
| 236 | - } |
|
| 237 | - |
|
| 238 | - /** |
|
| 239 | - * @param string $key |
|
| 240 | - * @return string url to the online documentation |
|
| 241 | - */ |
|
| 242 | - public function linkToDocs($key) { |
|
| 243 | - $theme = \OC::$server->getThemingDefaults(); |
|
| 244 | - return $theme->buildDocLinkToKey($key); |
|
| 245 | - } |
|
| 246 | - |
|
| 247 | - /** |
|
| 248 | - * @return string base url of the current request |
|
| 249 | - */ |
|
| 250 | - public function getBaseUrl() { |
|
| 251 | - return $this->request->getServerProtocol() . '://' . $this->request->getServerHost() . \OC::$WEBROOT; |
|
| 252 | - } |
|
| 49 | + /** @var IConfig */ |
|
| 50 | + private $config; |
|
| 51 | + /** @var ICacheFactory */ |
|
| 52 | + private $cacheFactory; |
|
| 53 | + /** @var IRequest */ |
|
| 54 | + private $request; |
|
| 55 | + |
|
| 56 | + /** |
|
| 57 | + * @param IConfig $config |
|
| 58 | + * @param ICacheFactory $cacheFactory |
|
| 59 | + * @param IRequest $request |
|
| 60 | + */ |
|
| 61 | + public function __construct(IConfig $config, |
|
| 62 | + ICacheFactory $cacheFactory, |
|
| 63 | + IRequest $request) { |
|
| 64 | + $this->config = $config; |
|
| 65 | + $this->cacheFactory = $cacheFactory; |
|
| 66 | + $this->request = $request; |
|
| 67 | + } |
|
| 68 | + |
|
| 69 | + /** |
|
| 70 | + * Creates an url using a defined route |
|
| 71 | + * @param string $route |
|
| 72 | + * @param array $parameters args with param=>value, will be appended to the returned url |
|
| 73 | + * @return string the url |
|
| 74 | + * |
|
| 75 | + * Returns a url to the given route. |
|
| 76 | + */ |
|
| 77 | + public function linkToRoute($route, $parameters = array()) { |
|
| 78 | + // TODO: mock router |
|
| 79 | + $urlLinkTo = \OC::$server->getRouter()->generate($route, $parameters); |
|
| 80 | + return $urlLinkTo; |
|
| 81 | + } |
|
| 82 | + |
|
| 83 | + /** |
|
| 84 | + * Creates an absolute url using a defined route |
|
| 85 | + * @param string $routeName |
|
| 86 | + * @param array $arguments args with param=>value, will be appended to the returned url |
|
| 87 | + * @return string the url |
|
| 88 | + * |
|
| 89 | + * Returns an absolute url to the given route. |
|
| 90 | + */ |
|
| 91 | + public function linkToRouteAbsolute($routeName, $arguments = array()) { |
|
| 92 | + return $this->getAbsoluteURL($this->linkToRoute($routeName, $arguments)); |
|
| 93 | + } |
|
| 94 | + |
|
| 95 | + /** |
|
| 96 | + * Creates an url |
|
| 97 | + * @param string $app app |
|
| 98 | + * @param string $file file |
|
| 99 | + * @param array $args array with param=>value, will be appended to the returned url |
|
| 100 | + * The value of $args will be urlencoded |
|
| 101 | + * @return string the url |
|
| 102 | + * |
|
| 103 | + * Returns a url to the given app and file. |
|
| 104 | + */ |
|
| 105 | + public function linkTo( $app, $file, $args = array() ) { |
|
| 106 | + $frontControllerActive = ($this->config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true'); |
|
| 107 | + |
|
| 108 | + if( $app != '' ) { |
|
| 109 | + $app_path = \OC_App::getAppPath($app); |
|
| 110 | + // Check if the app is in the app folder |
|
| 111 | + if ($app_path && file_exists($app_path . '/' . $file)) { |
|
| 112 | + if (substr($file, -3) == 'php') { |
|
| 113 | + |
|
| 114 | + $urlLinkTo = \OC::$WEBROOT . '/index.php/apps/' . $app; |
|
| 115 | + if ($frontControllerActive) { |
|
| 116 | + $urlLinkTo = \OC::$WEBROOT . '/apps/' . $app; |
|
| 117 | + } |
|
| 118 | + $urlLinkTo .= ($file != 'index.php') ? '/' . $file : ''; |
|
| 119 | + } else { |
|
| 120 | + $urlLinkTo = \OC_App::getAppWebPath($app) . '/' . $file; |
|
| 121 | + } |
|
| 122 | + } else { |
|
| 123 | + $urlLinkTo = \OC::$WEBROOT . '/' . $app . '/' . $file; |
|
| 124 | + } |
|
| 125 | + } else { |
|
| 126 | + if (file_exists(\OC::$SERVERROOT . '/core/' . $file)) { |
|
| 127 | + $urlLinkTo = \OC::$WEBROOT . '/core/' . $file; |
|
| 128 | + } else { |
|
| 129 | + if ($frontControllerActive && $file === 'index.php') { |
|
| 130 | + $urlLinkTo = \OC::$WEBROOT . '/'; |
|
| 131 | + } else { |
|
| 132 | + $urlLinkTo = \OC::$WEBROOT . '/' . $file; |
|
| 133 | + } |
|
| 134 | + } |
|
| 135 | + } |
|
| 136 | + |
|
| 137 | + if ($args && $query = http_build_query($args, '', '&')) { |
|
| 138 | + $urlLinkTo .= '?' . $query; |
|
| 139 | + } |
|
| 140 | + |
|
| 141 | + return $urlLinkTo; |
|
| 142 | + } |
|
| 143 | + |
|
| 144 | + /** |
|
| 145 | + * Creates path to an image |
|
| 146 | + * @param string $app app |
|
| 147 | + * @param string $image image name |
|
| 148 | + * @throws \RuntimeException If the image does not exist |
|
| 149 | + * @return string the url |
|
| 150 | + * |
|
| 151 | + * Returns the path to the image. |
|
| 152 | + */ |
|
| 153 | + public function imagePath($app, $image) { |
|
| 154 | + $cache = $this->cacheFactory->createDistributed('imagePath-'.md5($this->getBaseUrl()).'-'); |
|
| 155 | + $cacheKey = $app.'-'.$image; |
|
| 156 | + if($key = $cache->get($cacheKey)) { |
|
| 157 | + return $key; |
|
| 158 | + } |
|
| 159 | + |
|
| 160 | + // Read the selected theme from the config file |
|
| 161 | + $theme = \OC_Util::getTheme(); |
|
| 162 | + |
|
| 163 | + //if a theme has a png but not an svg always use the png |
|
| 164 | + $basename = substr(basename($image),0,-4); |
|
| 165 | + |
|
| 166 | + $appPath = \OC_App::getAppPath($app); |
|
| 167 | + |
|
| 168 | + // Check if the app is in the app folder |
|
| 169 | + $path = ''; |
|
| 170 | + $themingEnabled = $this->config->getSystemValue('installed', false) && \OCP\App::isEnabled('theming') && \OC_App::isAppLoaded('theming'); |
|
| 171 | + $themingImagePath = false; |
|
| 172 | + if($themingEnabled) { |
|
| 173 | + $themingImagePath = \OC::$server->getThemingDefaults()->replaceImagePath($app, $image); |
|
| 174 | + } |
|
| 175 | + |
|
| 176 | + if (file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$app/img/$image")) { |
|
| 177 | + $path = \OC::$WEBROOT . "/themes/$theme/apps/$app/img/$image"; |
|
| 178 | + } elseif (!file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$app/img/$basename.svg") |
|
| 179 | + && file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$app/img/$basename.png")) { |
|
| 180 | + $path = \OC::$WEBROOT . "/themes/$theme/apps/$app/img/$basename.png"; |
|
| 181 | + } elseif (!empty($app) and file_exists(\OC::$SERVERROOT . "/themes/$theme/$app/img/$image")) { |
|
| 182 | + $path = \OC::$WEBROOT . "/themes/$theme/$app/img/$image"; |
|
| 183 | + } elseif (!empty($app) and (!file_exists(\OC::$SERVERROOT . "/themes/$theme/$app/img/$basename.svg") |
|
| 184 | + && file_exists(\OC::$SERVERROOT . "/themes/$theme/$app/img/$basename.png"))) { |
|
| 185 | + $path = \OC::$WEBROOT . "/themes/$theme/$app/img/$basename.png"; |
|
| 186 | + } elseif (file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$image")) { |
|
| 187 | + $path = \OC::$WEBROOT . "/themes/$theme/core/img/$image"; |
|
| 188 | + } elseif (!file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$basename.svg") |
|
| 189 | + && file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$basename.png")) { |
|
| 190 | + $path = \OC::$WEBROOT . "/themes/$theme/core/img/$basename.png"; |
|
| 191 | + } elseif($themingEnabled && $themingImagePath) { |
|
| 192 | + $path = $themingImagePath; |
|
| 193 | + } elseif ($appPath && file_exists($appPath . "/img/$image")) { |
|
| 194 | + $path = \OC_App::getAppWebPath($app) . "/img/$image"; |
|
| 195 | + } elseif ($appPath && !file_exists($appPath . "/img/$basename.svg") |
|
| 196 | + && file_exists($appPath . "/img/$basename.png")) { |
|
| 197 | + $path = \OC_App::getAppWebPath($app) . "/img/$basename.png"; |
|
| 198 | + } elseif (!empty($app) and file_exists(\OC::$SERVERROOT . "/$app/img/$image")) { |
|
| 199 | + $path = \OC::$WEBROOT . "/$app/img/$image"; |
|
| 200 | + } elseif (!empty($app) and (!file_exists(\OC::$SERVERROOT . "/$app/img/$basename.svg") |
|
| 201 | + && file_exists(\OC::$SERVERROOT . "/$app/img/$basename.png"))) { |
|
| 202 | + $path = \OC::$WEBROOT . "/$app/img/$basename.png"; |
|
| 203 | + } elseif (file_exists(\OC::$SERVERROOT . "/core/img/$image")) { |
|
| 204 | + $path = \OC::$WEBROOT . "/core/img/$image"; |
|
| 205 | + } elseif (!file_exists(\OC::$SERVERROOT . "/core/img/$basename.svg") |
|
| 206 | + && file_exists(\OC::$SERVERROOT . "/core/img/$basename.png")) { |
|
| 207 | + $path = \OC::$WEBROOT . "/themes/$theme/core/img/$basename.png"; |
|
| 208 | + } |
|
| 209 | + |
|
| 210 | + if($path !== '') { |
|
| 211 | + $cache->set($cacheKey, $path); |
|
| 212 | + return $path; |
|
| 213 | + } else { |
|
| 214 | + throw new RuntimeException('image not found: image:' . $image . ' webroot:' . \OC::$WEBROOT . ' serverroot:' . \OC::$SERVERROOT); |
|
| 215 | + } |
|
| 216 | + } |
|
| 217 | + |
|
| 218 | + |
|
| 219 | + /** |
|
| 220 | + * Makes an URL absolute |
|
| 221 | + * @param string $url the url in the ownCloud host |
|
| 222 | + * @return string the absolute version of the url |
|
| 223 | + */ |
|
| 224 | + public function getAbsoluteURL($url) { |
|
| 225 | + $separator = $url[0] === '/' ? '' : '/'; |
|
| 226 | + |
|
| 227 | + if (\OC::$CLI && !defined('PHPUNIT_RUN')) { |
|
| 228 | + return rtrim($this->config->getSystemValue('overwrite.cli.url'), '/') . '/' . ltrim($url, '/'); |
|
| 229 | + } |
|
| 230 | + // The ownCloud web root can already be prepended. |
|
| 231 | + if(substr($url, 0, strlen(\OC::$WEBROOT)) === \OC::$WEBROOT) { |
|
| 232 | + $url = substr($url, strlen(\OC::$WEBROOT)); |
|
| 233 | + } |
|
| 234 | + |
|
| 235 | + return $this->getBaseUrl() . $separator . $url; |
|
| 236 | + } |
|
| 237 | + |
|
| 238 | + /** |
|
| 239 | + * @param string $key |
|
| 240 | + * @return string url to the online documentation |
|
| 241 | + */ |
|
| 242 | + public function linkToDocs($key) { |
|
| 243 | + $theme = \OC::$server->getThemingDefaults(); |
|
| 244 | + return $theme->buildDocLinkToKey($key); |
|
| 245 | + } |
|
| 246 | + |
|
| 247 | + /** |
|
| 248 | + * @return string base url of the current request |
|
| 249 | + */ |
|
| 250 | + public function getBaseUrl() { |
|
| 251 | + return $this->request->getServerProtocol() . '://' . $this->request->getServerHost() . \OC::$WEBROOT; |
|
| 252 | + } |
|
| 253 | 253 | } |
@@ -102,40 +102,40 @@ discard block |
||
| 102 | 102 | * |
| 103 | 103 | * Returns a url to the given app and file. |
| 104 | 104 | */ |
| 105 | - public function linkTo( $app, $file, $args = array() ) { |
|
| 105 | + public function linkTo($app, $file, $args = array()) { |
|
| 106 | 106 | $frontControllerActive = ($this->config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true'); |
| 107 | 107 | |
| 108 | - if( $app != '' ) { |
|
| 108 | + if ($app != '') { |
|
| 109 | 109 | $app_path = \OC_App::getAppPath($app); |
| 110 | 110 | // Check if the app is in the app folder |
| 111 | - if ($app_path && file_exists($app_path . '/' . $file)) { |
|
| 111 | + if ($app_path && file_exists($app_path.'/'.$file)) { |
|
| 112 | 112 | if (substr($file, -3) == 'php') { |
| 113 | 113 | |
| 114 | - $urlLinkTo = \OC::$WEBROOT . '/index.php/apps/' . $app; |
|
| 114 | + $urlLinkTo = \OC::$WEBROOT.'/index.php/apps/'.$app; |
|
| 115 | 115 | if ($frontControllerActive) { |
| 116 | - $urlLinkTo = \OC::$WEBROOT . '/apps/' . $app; |
|
| 116 | + $urlLinkTo = \OC::$WEBROOT.'/apps/'.$app; |
|
| 117 | 117 | } |
| 118 | - $urlLinkTo .= ($file != 'index.php') ? '/' . $file : ''; |
|
| 118 | + $urlLinkTo .= ($file != 'index.php') ? '/'.$file : ''; |
|
| 119 | 119 | } else { |
| 120 | - $urlLinkTo = \OC_App::getAppWebPath($app) . '/' . $file; |
|
| 120 | + $urlLinkTo = \OC_App::getAppWebPath($app).'/'.$file; |
|
| 121 | 121 | } |
| 122 | 122 | } else { |
| 123 | - $urlLinkTo = \OC::$WEBROOT . '/' . $app . '/' . $file; |
|
| 123 | + $urlLinkTo = \OC::$WEBROOT.'/'.$app.'/'.$file; |
|
| 124 | 124 | } |
| 125 | 125 | } else { |
| 126 | - if (file_exists(\OC::$SERVERROOT . '/core/' . $file)) { |
|
| 127 | - $urlLinkTo = \OC::$WEBROOT . '/core/' . $file; |
|
| 126 | + if (file_exists(\OC::$SERVERROOT.'/core/'.$file)) { |
|
| 127 | + $urlLinkTo = \OC::$WEBROOT.'/core/'.$file; |
|
| 128 | 128 | } else { |
| 129 | 129 | if ($frontControllerActive && $file === 'index.php') { |
| 130 | - $urlLinkTo = \OC::$WEBROOT . '/'; |
|
| 130 | + $urlLinkTo = \OC::$WEBROOT.'/'; |
|
| 131 | 131 | } else { |
| 132 | - $urlLinkTo = \OC::$WEBROOT . '/' . $file; |
|
| 132 | + $urlLinkTo = \OC::$WEBROOT.'/'.$file; |
|
| 133 | 133 | } |
| 134 | 134 | } |
| 135 | 135 | } |
| 136 | 136 | |
| 137 | 137 | if ($args && $query = http_build_query($args, '', '&')) { |
| 138 | - $urlLinkTo .= '?' . $query; |
|
| 138 | + $urlLinkTo .= '?'.$query; |
|
| 139 | 139 | } |
| 140 | 140 | |
| 141 | 141 | return $urlLinkTo; |
@@ -153,7 +153,7 @@ discard block |
||
| 153 | 153 | public function imagePath($app, $image) { |
| 154 | 154 | $cache = $this->cacheFactory->createDistributed('imagePath-'.md5($this->getBaseUrl()).'-'); |
| 155 | 155 | $cacheKey = $app.'-'.$image; |
| 156 | - if($key = $cache->get($cacheKey)) { |
|
| 156 | + if ($key = $cache->get($cacheKey)) { |
|
| 157 | 157 | return $key; |
| 158 | 158 | } |
| 159 | 159 | |
@@ -161,7 +161,7 @@ discard block |
||
| 161 | 161 | $theme = \OC_Util::getTheme(); |
| 162 | 162 | |
| 163 | 163 | //if a theme has a png but not an svg always use the png |
| 164 | - $basename = substr(basename($image),0,-4); |
|
| 164 | + $basename = substr(basename($image), 0, -4); |
|
| 165 | 165 | |
| 166 | 166 | $appPath = \OC_App::getAppPath($app); |
| 167 | 167 | |
@@ -169,49 +169,49 @@ discard block |
||
| 169 | 169 | $path = ''; |
| 170 | 170 | $themingEnabled = $this->config->getSystemValue('installed', false) && \OCP\App::isEnabled('theming') && \OC_App::isAppLoaded('theming'); |
| 171 | 171 | $themingImagePath = false; |
| 172 | - if($themingEnabled) { |
|
| 172 | + if ($themingEnabled) { |
|
| 173 | 173 | $themingImagePath = \OC::$server->getThemingDefaults()->replaceImagePath($app, $image); |
| 174 | 174 | } |
| 175 | 175 | |
| 176 | - if (file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$app/img/$image")) { |
|
| 177 | - $path = \OC::$WEBROOT . "/themes/$theme/apps/$app/img/$image"; |
|
| 178 | - } elseif (!file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$app/img/$basename.svg") |
|
| 179 | - && file_exists(\OC::$SERVERROOT . "/themes/$theme/apps/$app/img/$basename.png")) { |
|
| 180 | - $path = \OC::$WEBROOT . "/themes/$theme/apps/$app/img/$basename.png"; |
|
| 181 | - } elseif (!empty($app) and file_exists(\OC::$SERVERROOT . "/themes/$theme/$app/img/$image")) { |
|
| 182 | - $path = \OC::$WEBROOT . "/themes/$theme/$app/img/$image"; |
|
| 183 | - } elseif (!empty($app) and (!file_exists(\OC::$SERVERROOT . "/themes/$theme/$app/img/$basename.svg") |
|
| 184 | - && file_exists(\OC::$SERVERROOT . "/themes/$theme/$app/img/$basename.png"))) { |
|
| 185 | - $path = \OC::$WEBROOT . "/themes/$theme/$app/img/$basename.png"; |
|
| 186 | - } elseif (file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$image")) { |
|
| 187 | - $path = \OC::$WEBROOT . "/themes/$theme/core/img/$image"; |
|
| 188 | - } elseif (!file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$basename.svg") |
|
| 189 | - && file_exists(\OC::$SERVERROOT . "/themes/$theme/core/img/$basename.png")) { |
|
| 190 | - $path = \OC::$WEBROOT . "/themes/$theme/core/img/$basename.png"; |
|
| 191 | - } elseif($themingEnabled && $themingImagePath) { |
|
| 176 | + if (file_exists(\OC::$SERVERROOT."/themes/$theme/apps/$app/img/$image")) { |
|
| 177 | + $path = \OC::$WEBROOT."/themes/$theme/apps/$app/img/$image"; |
|
| 178 | + } elseif (!file_exists(\OC::$SERVERROOT."/themes/$theme/apps/$app/img/$basename.svg") |
|
| 179 | + && file_exists(\OC::$SERVERROOT."/themes/$theme/apps/$app/img/$basename.png")) { |
|
| 180 | + $path = \OC::$WEBROOT."/themes/$theme/apps/$app/img/$basename.png"; |
|
| 181 | + } elseif (!empty($app) and file_exists(\OC::$SERVERROOT."/themes/$theme/$app/img/$image")) { |
|
| 182 | + $path = \OC::$WEBROOT."/themes/$theme/$app/img/$image"; |
|
| 183 | + } elseif (!empty($app) and (!file_exists(\OC::$SERVERROOT."/themes/$theme/$app/img/$basename.svg") |
|
| 184 | + && file_exists(\OC::$SERVERROOT."/themes/$theme/$app/img/$basename.png"))) { |
|
| 185 | + $path = \OC::$WEBROOT."/themes/$theme/$app/img/$basename.png"; |
|
| 186 | + } elseif (file_exists(\OC::$SERVERROOT."/themes/$theme/core/img/$image")) { |
|
| 187 | + $path = \OC::$WEBROOT."/themes/$theme/core/img/$image"; |
|
| 188 | + } elseif (!file_exists(\OC::$SERVERROOT."/themes/$theme/core/img/$basename.svg") |
|
| 189 | + && file_exists(\OC::$SERVERROOT."/themes/$theme/core/img/$basename.png")) { |
|
| 190 | + $path = \OC::$WEBROOT."/themes/$theme/core/img/$basename.png"; |
|
| 191 | + } elseif ($themingEnabled && $themingImagePath) { |
|
| 192 | 192 | $path = $themingImagePath; |
| 193 | - } elseif ($appPath && file_exists($appPath . "/img/$image")) { |
|
| 194 | - $path = \OC_App::getAppWebPath($app) . "/img/$image"; |
|
| 195 | - } elseif ($appPath && !file_exists($appPath . "/img/$basename.svg") |
|
| 196 | - && file_exists($appPath . "/img/$basename.png")) { |
|
| 197 | - $path = \OC_App::getAppWebPath($app) . "/img/$basename.png"; |
|
| 198 | - } elseif (!empty($app) and file_exists(\OC::$SERVERROOT . "/$app/img/$image")) { |
|
| 199 | - $path = \OC::$WEBROOT . "/$app/img/$image"; |
|
| 200 | - } elseif (!empty($app) and (!file_exists(\OC::$SERVERROOT . "/$app/img/$basename.svg") |
|
| 201 | - && file_exists(\OC::$SERVERROOT . "/$app/img/$basename.png"))) { |
|
| 202 | - $path = \OC::$WEBROOT . "/$app/img/$basename.png"; |
|
| 203 | - } elseif (file_exists(\OC::$SERVERROOT . "/core/img/$image")) { |
|
| 204 | - $path = \OC::$WEBROOT . "/core/img/$image"; |
|
| 205 | - } elseif (!file_exists(\OC::$SERVERROOT . "/core/img/$basename.svg") |
|
| 206 | - && file_exists(\OC::$SERVERROOT . "/core/img/$basename.png")) { |
|
| 207 | - $path = \OC::$WEBROOT . "/themes/$theme/core/img/$basename.png"; |
|
| 193 | + } elseif ($appPath && file_exists($appPath."/img/$image")) { |
|
| 194 | + $path = \OC_App::getAppWebPath($app)."/img/$image"; |
|
| 195 | + } elseif ($appPath && !file_exists($appPath."/img/$basename.svg") |
|
| 196 | + && file_exists($appPath."/img/$basename.png")) { |
|
| 197 | + $path = \OC_App::getAppWebPath($app)."/img/$basename.png"; |
|
| 198 | + } elseif (!empty($app) and file_exists(\OC::$SERVERROOT."/$app/img/$image")) { |
|
| 199 | + $path = \OC::$WEBROOT."/$app/img/$image"; |
|
| 200 | + } elseif (!empty($app) and (!file_exists(\OC::$SERVERROOT."/$app/img/$basename.svg") |
|
| 201 | + && file_exists(\OC::$SERVERROOT."/$app/img/$basename.png"))) { |
|
| 202 | + $path = \OC::$WEBROOT."/$app/img/$basename.png"; |
|
| 203 | + } elseif (file_exists(\OC::$SERVERROOT."/core/img/$image")) { |
|
| 204 | + $path = \OC::$WEBROOT."/core/img/$image"; |
|
| 205 | + } elseif (!file_exists(\OC::$SERVERROOT."/core/img/$basename.svg") |
|
| 206 | + && file_exists(\OC::$SERVERROOT."/core/img/$basename.png")) { |
|
| 207 | + $path = \OC::$WEBROOT."/themes/$theme/core/img/$basename.png"; |
|
| 208 | 208 | } |
| 209 | 209 | |
| 210 | - if($path !== '') { |
|
| 210 | + if ($path !== '') { |
|
| 211 | 211 | $cache->set($cacheKey, $path); |
| 212 | 212 | return $path; |
| 213 | 213 | } else { |
| 214 | - throw new RuntimeException('image not found: image:' . $image . ' webroot:' . \OC::$WEBROOT . ' serverroot:' . \OC::$SERVERROOT); |
|
| 214 | + throw new RuntimeException('image not found: image:'.$image.' webroot:'.\OC::$WEBROOT.' serverroot:'.\OC::$SERVERROOT); |
|
| 215 | 215 | } |
| 216 | 216 | } |
| 217 | 217 | |
@@ -225,14 +225,14 @@ discard block |
||
| 225 | 225 | $separator = $url[0] === '/' ? '' : '/'; |
| 226 | 226 | |
| 227 | 227 | if (\OC::$CLI && !defined('PHPUNIT_RUN')) { |
| 228 | - return rtrim($this->config->getSystemValue('overwrite.cli.url'), '/') . '/' . ltrim($url, '/'); |
|
| 228 | + return rtrim($this->config->getSystemValue('overwrite.cli.url'), '/').'/'.ltrim($url, '/'); |
|
| 229 | 229 | } |
| 230 | 230 | // The ownCloud web root can already be prepended. |
| 231 | - if(substr($url, 0, strlen(\OC::$WEBROOT)) === \OC::$WEBROOT) { |
|
| 231 | + if (substr($url, 0, strlen(\OC::$WEBROOT)) === \OC::$WEBROOT) { |
|
| 232 | 232 | $url = substr($url, strlen(\OC::$WEBROOT)); |
| 233 | 233 | } |
| 234 | 234 | |
| 235 | - return $this->getBaseUrl() . $separator . $url; |
|
| 235 | + return $this->getBaseUrl().$separator.$url; |
|
| 236 | 236 | } |
| 237 | 237 | |
| 238 | 238 | /** |
@@ -248,6 +248,6 @@ discard block |
||
| 248 | 248 | * @return string base url of the current request |
| 249 | 249 | */ |
| 250 | 250 | public function getBaseUrl() { |
| 251 | - return $this->request->getServerProtocol() . '://' . $this->request->getServerHost() . \OC::$WEBROOT; |
|
| 251 | + return $this->request->getServerProtocol().'://'.$this->request->getServerHost().\OC::$WEBROOT; |
|
| 252 | 252 | } |
| 253 | 253 | } |
@@ -51,555 +51,555 @@ |
||
| 51 | 51 | * @package OC\IntegrityCheck |
| 52 | 52 | */ |
| 53 | 53 | class Checker { |
| 54 | - const CACHE_KEY = 'oc.integritycheck.checker'; |
|
| 55 | - /** @var EnvironmentHelper */ |
|
| 56 | - private $environmentHelper; |
|
| 57 | - /** @var AppLocator */ |
|
| 58 | - private $appLocator; |
|
| 59 | - /** @var FileAccessHelper */ |
|
| 60 | - private $fileAccessHelper; |
|
| 61 | - /** @var IConfig */ |
|
| 62 | - private $config; |
|
| 63 | - /** @var ICache */ |
|
| 64 | - private $cache; |
|
| 65 | - /** @var IAppManager */ |
|
| 66 | - private $appManager; |
|
| 67 | - /** @var ITempManager */ |
|
| 68 | - private $tempManager; |
|
| 69 | - |
|
| 70 | - /** |
|
| 71 | - * @param EnvironmentHelper $environmentHelper |
|
| 72 | - * @param FileAccessHelper $fileAccessHelper |
|
| 73 | - * @param AppLocator $appLocator |
|
| 74 | - * @param IConfig $config |
|
| 75 | - * @param ICacheFactory $cacheFactory |
|
| 76 | - * @param IAppManager $appManager |
|
| 77 | - * @param ITempManager $tempManager |
|
| 78 | - */ |
|
| 79 | - public function __construct(EnvironmentHelper $environmentHelper, |
|
| 80 | - FileAccessHelper $fileAccessHelper, |
|
| 81 | - AppLocator $appLocator, |
|
| 82 | - IConfig $config = null, |
|
| 83 | - ICacheFactory $cacheFactory, |
|
| 84 | - IAppManager $appManager = null, |
|
| 85 | - ITempManager $tempManager) { |
|
| 86 | - $this->environmentHelper = $environmentHelper; |
|
| 87 | - $this->fileAccessHelper = $fileAccessHelper; |
|
| 88 | - $this->appLocator = $appLocator; |
|
| 89 | - $this->config = $config; |
|
| 90 | - $this->cache = $cacheFactory->createDistributed(self::CACHE_KEY); |
|
| 91 | - $this->appManager = $appManager; |
|
| 92 | - $this->tempManager = $tempManager; |
|
| 93 | - } |
|
| 94 | - |
|
| 95 | - /** |
|
| 96 | - * Whether code signing is enforced or not. |
|
| 97 | - * |
|
| 98 | - * @return bool |
|
| 99 | - */ |
|
| 100 | - public function isCodeCheckEnforced() { |
|
| 101 | - $notSignedChannels = [ '', 'git']; |
|
| 102 | - if (in_array($this->environmentHelper->getChannel(), $notSignedChannels, true)) { |
|
| 103 | - return false; |
|
| 104 | - } |
|
| 105 | - |
|
| 106 | - /** |
|
| 107 | - * This config option is undocumented and supposed to be so, it's only |
|
| 108 | - * applicable for very specific scenarios and we should not advertise it |
|
| 109 | - * too prominent. So please do not add it to config.sample.php. |
|
| 110 | - */ |
|
| 111 | - if ($this->config !== null) { |
|
| 112 | - $isIntegrityCheckDisabled = $this->config->getSystemValue('integrity.check.disabled', false); |
|
| 113 | - } else { |
|
| 114 | - $isIntegrityCheckDisabled = false; |
|
| 115 | - } |
|
| 116 | - if ($isIntegrityCheckDisabled === true) { |
|
| 117 | - return false; |
|
| 118 | - } |
|
| 119 | - |
|
| 120 | - return true; |
|
| 121 | - } |
|
| 122 | - |
|
| 123 | - /** |
|
| 124 | - * Enumerates all files belonging to the folder. Sensible defaults are excluded. |
|
| 125 | - * |
|
| 126 | - * @param string $folderToIterate |
|
| 127 | - * @param string $root |
|
| 128 | - * @return \RecursiveIteratorIterator |
|
| 129 | - * @throws \Exception |
|
| 130 | - */ |
|
| 131 | - private function getFolderIterator($folderToIterate, $root = '') { |
|
| 132 | - $dirItr = new \RecursiveDirectoryIterator( |
|
| 133 | - $folderToIterate, |
|
| 134 | - \RecursiveDirectoryIterator::SKIP_DOTS |
|
| 135 | - ); |
|
| 136 | - if($root === '') { |
|
| 137 | - $root = \OC::$SERVERROOT; |
|
| 138 | - } |
|
| 139 | - $root = rtrim($root, '/'); |
|
| 140 | - |
|
| 141 | - $excludeGenericFilesIterator = new ExcludeFileByNameFilterIterator($dirItr); |
|
| 142 | - $excludeFoldersIterator = new ExcludeFoldersByPathFilterIterator($excludeGenericFilesIterator, $root); |
|
| 143 | - |
|
| 144 | - return new \RecursiveIteratorIterator( |
|
| 145 | - $excludeFoldersIterator, |
|
| 146 | - \RecursiveIteratorIterator::SELF_FIRST |
|
| 147 | - ); |
|
| 148 | - } |
|
| 149 | - |
|
| 150 | - /** |
|
| 151 | - * Returns an array of ['filename' => 'SHA512-hash-of-file'] for all files found |
|
| 152 | - * in the iterator. |
|
| 153 | - * |
|
| 154 | - * @param \RecursiveIteratorIterator $iterator |
|
| 155 | - * @param string $path |
|
| 156 | - * @return array Array of hashes. |
|
| 157 | - */ |
|
| 158 | - private function generateHashes(\RecursiveIteratorIterator $iterator, |
|
| 159 | - $path) { |
|
| 160 | - $hashes = []; |
|
| 161 | - $copiedWebserverSettingFiles = false; |
|
| 162 | - $tmpFolder = ''; |
|
| 163 | - |
|
| 164 | - $baseDirectoryLength = strlen($path); |
|
| 165 | - foreach($iterator as $filename => $data) { |
|
| 166 | - /** @var \DirectoryIterator $data */ |
|
| 167 | - if($data->isDir()) { |
|
| 168 | - continue; |
|
| 169 | - } |
|
| 170 | - |
|
| 171 | - $relativeFileName = substr($filename, $baseDirectoryLength); |
|
| 172 | - $relativeFileName = ltrim($relativeFileName, '/'); |
|
| 173 | - |
|
| 174 | - // Exclude signature.json files in the appinfo and root folder |
|
| 175 | - if($relativeFileName === 'appinfo/signature.json') { |
|
| 176 | - continue; |
|
| 177 | - } |
|
| 178 | - // Exclude signature.json files in the appinfo and core folder |
|
| 179 | - if($relativeFileName === 'core/signature.json') { |
|
| 180 | - continue; |
|
| 181 | - } |
|
| 182 | - |
|
| 183 | - // The .user.ini and the .htaccess file of ownCloud can contain some |
|
| 184 | - // custom modifications such as for example the maximum upload size |
|
| 185 | - // to ensure that this will not lead to false positives this will |
|
| 186 | - // copy the file to a temporary folder and reset it to the default |
|
| 187 | - // values. |
|
| 188 | - if($filename === $this->environmentHelper->getServerRoot() . '/.htaccess' |
|
| 189 | - || $filename === $this->environmentHelper->getServerRoot() . '/.user.ini') { |
|
| 190 | - |
|
| 191 | - if(!$copiedWebserverSettingFiles) { |
|
| 192 | - $tmpFolder = rtrim($this->tempManager->getTemporaryFolder(), '/'); |
|
| 193 | - copy($this->environmentHelper->getServerRoot() . '/.htaccess', $tmpFolder . '/.htaccess'); |
|
| 194 | - copy($this->environmentHelper->getServerRoot() . '/.user.ini', $tmpFolder . '/.user.ini'); |
|
| 195 | - \OC_Files::setUploadLimit( |
|
| 196 | - \OCP\Util::computerFileSize('511MB'), |
|
| 197 | - [ |
|
| 198 | - '.htaccess' => $tmpFolder . '/.htaccess', |
|
| 199 | - '.user.ini' => $tmpFolder . '/.user.ini', |
|
| 200 | - ] |
|
| 201 | - ); |
|
| 202 | - } |
|
| 203 | - } |
|
| 204 | - |
|
| 205 | - // The .user.ini file can contain custom modifications to the file size |
|
| 206 | - // as well. |
|
| 207 | - if($filename === $this->environmentHelper->getServerRoot() . '/.user.ini') { |
|
| 208 | - $fileContent = file_get_contents($tmpFolder . '/.user.ini'); |
|
| 209 | - $hashes[$relativeFileName] = hash('sha512', $fileContent); |
|
| 210 | - continue; |
|
| 211 | - } |
|
| 212 | - |
|
| 213 | - // The .htaccess file in the root folder of ownCloud can contain |
|
| 214 | - // custom content after the installation due to the fact that dynamic |
|
| 215 | - // content is written into it at installation time as well. This |
|
| 216 | - // includes for example the 404 and 403 instructions. |
|
| 217 | - // Thus we ignore everything below the first occurrence of |
|
| 218 | - // "#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####" and have the |
|
| 219 | - // hash generated based on this. |
|
| 220 | - if($filename === $this->environmentHelper->getServerRoot() . '/.htaccess') { |
|
| 221 | - $fileContent = file_get_contents($tmpFolder . '/.htaccess'); |
|
| 222 | - $explodedArray = explode('#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####', $fileContent); |
|
| 223 | - if(count($explodedArray) === 2) { |
|
| 224 | - $hashes[$relativeFileName] = hash('sha512', $explodedArray[0]); |
|
| 225 | - continue; |
|
| 226 | - } |
|
| 227 | - } |
|
| 228 | - |
|
| 229 | - $hashes[$relativeFileName] = hash_file('sha512', $filename); |
|
| 230 | - } |
|
| 231 | - |
|
| 232 | - return $hashes; |
|
| 233 | - } |
|
| 234 | - |
|
| 235 | - /** |
|
| 236 | - * Creates the signature data |
|
| 237 | - * |
|
| 238 | - * @param array $hashes |
|
| 239 | - * @param X509 $certificate |
|
| 240 | - * @param RSA $privateKey |
|
| 241 | - * @return string |
|
| 242 | - */ |
|
| 243 | - private function createSignatureData(array $hashes, |
|
| 244 | - X509 $certificate, |
|
| 245 | - RSA $privateKey) { |
|
| 246 | - ksort($hashes); |
|
| 247 | - |
|
| 248 | - $privateKey->setSignatureMode(RSA::SIGNATURE_PSS); |
|
| 249 | - $privateKey->setMGFHash('sha512'); |
|
| 250 | - // See https://tools.ietf.org/html/rfc3447#page-38 |
|
| 251 | - $privateKey->setSaltLength(0); |
|
| 252 | - $signature = $privateKey->sign(json_encode($hashes)); |
|
| 253 | - |
|
| 254 | - return [ |
|
| 255 | - 'hashes' => $hashes, |
|
| 256 | - 'signature' => base64_encode($signature), |
|
| 257 | - 'certificate' => $certificate->saveX509($certificate->currentCert), |
|
| 258 | - ]; |
|
| 259 | - } |
|
| 260 | - |
|
| 261 | - /** |
|
| 262 | - * Write the signature of the app in the specified folder |
|
| 263 | - * |
|
| 264 | - * @param string $path |
|
| 265 | - * @param X509 $certificate |
|
| 266 | - * @param RSA $privateKey |
|
| 267 | - * @throws \Exception |
|
| 268 | - */ |
|
| 269 | - public function writeAppSignature($path, |
|
| 270 | - X509 $certificate, |
|
| 271 | - RSA $privateKey) { |
|
| 272 | - $appInfoDir = $path . '/appinfo'; |
|
| 273 | - try { |
|
| 274 | - $this->fileAccessHelper->assertDirectoryExists($appInfoDir); |
|
| 275 | - |
|
| 276 | - $iterator = $this->getFolderIterator($path); |
|
| 277 | - $hashes = $this->generateHashes($iterator, $path); |
|
| 278 | - $signature = $this->createSignatureData($hashes, $certificate, $privateKey); |
|
| 279 | - $this->fileAccessHelper->file_put_contents( |
|
| 280 | - $appInfoDir . '/signature.json', |
|
| 281 | - json_encode($signature, JSON_PRETTY_PRINT) |
|
| 282 | - ); |
|
| 283 | - } catch (\Exception $e){ |
|
| 284 | - if (!$this->fileAccessHelper->is_writable($appInfoDir)) { |
|
| 285 | - throw new \Exception($appInfoDir . ' is not writable'); |
|
| 286 | - } |
|
| 287 | - throw $e; |
|
| 288 | - } |
|
| 289 | - } |
|
| 290 | - |
|
| 291 | - /** |
|
| 292 | - * Write the signature of core |
|
| 293 | - * |
|
| 294 | - * @param X509 $certificate |
|
| 295 | - * @param RSA $rsa |
|
| 296 | - * @param string $path |
|
| 297 | - * @throws \Exception |
|
| 298 | - */ |
|
| 299 | - public function writeCoreSignature(X509 $certificate, |
|
| 300 | - RSA $rsa, |
|
| 301 | - $path) { |
|
| 302 | - $coreDir = $path . '/core'; |
|
| 303 | - try { |
|
| 304 | - |
|
| 305 | - $this->fileAccessHelper->assertDirectoryExists($coreDir); |
|
| 306 | - $iterator = $this->getFolderIterator($path, $path); |
|
| 307 | - $hashes = $this->generateHashes($iterator, $path); |
|
| 308 | - $signatureData = $this->createSignatureData($hashes, $certificate, $rsa); |
|
| 309 | - $this->fileAccessHelper->file_put_contents( |
|
| 310 | - $coreDir . '/signature.json', |
|
| 311 | - json_encode($signatureData, JSON_PRETTY_PRINT) |
|
| 312 | - ); |
|
| 313 | - } catch (\Exception $e){ |
|
| 314 | - if (!$this->fileAccessHelper->is_writable($coreDir)) { |
|
| 315 | - throw new \Exception($coreDir . ' is not writable'); |
|
| 316 | - } |
|
| 317 | - throw $e; |
|
| 318 | - } |
|
| 319 | - } |
|
| 320 | - |
|
| 321 | - /** |
|
| 322 | - * Verifies the signature for the specified path. |
|
| 323 | - * |
|
| 324 | - * @param string $signaturePath |
|
| 325 | - * @param string $basePath |
|
| 326 | - * @param string $certificateCN |
|
| 327 | - * @return array |
|
| 328 | - * @throws InvalidSignatureException |
|
| 329 | - * @throws \Exception |
|
| 330 | - */ |
|
| 331 | - private function verify($signaturePath, $basePath, $certificateCN) { |
|
| 332 | - if(!$this->isCodeCheckEnforced()) { |
|
| 333 | - return []; |
|
| 334 | - } |
|
| 335 | - |
|
| 336 | - $signatureData = json_decode($this->fileAccessHelper->file_get_contents($signaturePath), true); |
|
| 337 | - if(!is_array($signatureData)) { |
|
| 338 | - throw new InvalidSignatureException('Signature data not found.'); |
|
| 339 | - } |
|
| 340 | - |
|
| 341 | - $expectedHashes = $signatureData['hashes']; |
|
| 342 | - ksort($expectedHashes); |
|
| 343 | - $signature = base64_decode($signatureData['signature']); |
|
| 344 | - $certificate = $signatureData['certificate']; |
|
| 345 | - |
|
| 346 | - // Check if certificate is signed by Nextcloud Root Authority |
|
| 347 | - $x509 = new \phpseclib\File\X509(); |
|
| 348 | - $rootCertificatePublicKey = $this->fileAccessHelper->file_get_contents($this->environmentHelper->getServerRoot().'/resources/codesigning/root.crt'); |
|
| 349 | - $x509->loadCA($rootCertificatePublicKey); |
|
| 350 | - $x509->loadX509($certificate); |
|
| 351 | - if(!$x509->validateSignature()) { |
|
| 352 | - throw new InvalidSignatureException('Certificate is not valid.'); |
|
| 353 | - } |
|
| 354 | - // Verify if certificate has proper CN. "core" CN is always trusted. |
|
| 355 | - if($x509->getDN(X509::DN_OPENSSL)['CN'] !== $certificateCN && $x509->getDN(X509::DN_OPENSSL)['CN'] !== 'core') { |
|
| 356 | - throw new InvalidSignatureException( |
|
| 357 | - sprintf('Certificate is not valid for required scope. (Requested: %s, current: CN=%s)', $certificateCN, $x509->getDN(true)['CN']) |
|
| 358 | - ); |
|
| 359 | - } |
|
| 360 | - |
|
| 361 | - // Check if the signature of the files is valid |
|
| 362 | - $rsa = new \phpseclib\Crypt\RSA(); |
|
| 363 | - $rsa->loadKey($x509->currentCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']); |
|
| 364 | - $rsa->setSignatureMode(RSA::SIGNATURE_PSS); |
|
| 365 | - $rsa->setMGFHash('sha512'); |
|
| 366 | - // See https://tools.ietf.org/html/rfc3447#page-38 |
|
| 367 | - $rsa->setSaltLength(0); |
|
| 368 | - if(!$rsa->verify(json_encode($expectedHashes), $signature)) { |
|
| 369 | - throw new InvalidSignatureException('Signature could not get verified.'); |
|
| 370 | - } |
|
| 371 | - |
|
| 372 | - // Fixes for the updater as shipped with ownCloud 9.0.x: The updater is |
|
| 373 | - // replaced after the code integrity check is performed. |
|
| 374 | - // |
|
| 375 | - // Due to this reason we exclude the whole updater/ folder from the code |
|
| 376 | - // integrity check. |
|
| 377 | - if($basePath === $this->environmentHelper->getServerRoot()) { |
|
| 378 | - foreach($expectedHashes as $fileName => $hash) { |
|
| 379 | - if(strpos($fileName, 'updater/') === 0) { |
|
| 380 | - unset($expectedHashes[$fileName]); |
|
| 381 | - } |
|
| 382 | - } |
|
| 383 | - } |
|
| 384 | - |
|
| 385 | - // Compare the list of files which are not identical |
|
| 386 | - $currentInstanceHashes = $this->generateHashes($this->getFolderIterator($basePath), $basePath); |
|
| 387 | - $differencesA = array_diff($expectedHashes, $currentInstanceHashes); |
|
| 388 | - $differencesB = array_diff($currentInstanceHashes, $expectedHashes); |
|
| 389 | - $differences = array_unique(array_merge($differencesA, $differencesB)); |
|
| 390 | - $differenceArray = []; |
|
| 391 | - foreach($differences as $filename => $hash) { |
|
| 392 | - // Check if file should not exist in the new signature table |
|
| 393 | - if(!array_key_exists($filename, $expectedHashes)) { |
|
| 394 | - $differenceArray['EXTRA_FILE'][$filename]['expected'] = ''; |
|
| 395 | - $differenceArray['EXTRA_FILE'][$filename]['current'] = $hash; |
|
| 396 | - continue; |
|
| 397 | - } |
|
| 398 | - |
|
| 399 | - // Check if file is missing |
|
| 400 | - if(!array_key_exists($filename, $currentInstanceHashes)) { |
|
| 401 | - $differenceArray['FILE_MISSING'][$filename]['expected'] = $expectedHashes[$filename]; |
|
| 402 | - $differenceArray['FILE_MISSING'][$filename]['current'] = ''; |
|
| 403 | - continue; |
|
| 404 | - } |
|
| 405 | - |
|
| 406 | - // Check if hash does mismatch |
|
| 407 | - if($expectedHashes[$filename] !== $currentInstanceHashes[$filename]) { |
|
| 408 | - $differenceArray['INVALID_HASH'][$filename]['expected'] = $expectedHashes[$filename]; |
|
| 409 | - $differenceArray['INVALID_HASH'][$filename]['current'] = $currentInstanceHashes[$filename]; |
|
| 410 | - continue; |
|
| 411 | - } |
|
| 412 | - |
|
| 413 | - // Should never happen. |
|
| 414 | - throw new \Exception('Invalid behaviour in file hash comparison experienced. Please report this error to the developers.'); |
|
| 415 | - } |
|
| 416 | - |
|
| 417 | - return $differenceArray; |
|
| 418 | - } |
|
| 419 | - |
|
| 420 | - /** |
|
| 421 | - * Whether the code integrity check has passed successful or not |
|
| 422 | - * |
|
| 423 | - * @return bool |
|
| 424 | - */ |
|
| 425 | - public function hasPassedCheck() { |
|
| 426 | - $results = $this->getResults(); |
|
| 427 | - if(empty($results)) { |
|
| 428 | - return true; |
|
| 429 | - } |
|
| 430 | - |
|
| 431 | - return false; |
|
| 432 | - } |
|
| 433 | - |
|
| 434 | - /** |
|
| 435 | - * @return array |
|
| 436 | - */ |
|
| 437 | - public function getResults() { |
|
| 438 | - $cachedResults = $this->cache->get(self::CACHE_KEY); |
|
| 439 | - if(!is_null($cachedResults)) { |
|
| 440 | - return json_decode($cachedResults, true); |
|
| 441 | - } |
|
| 442 | - |
|
| 443 | - if ($this->config !== null) { |
|
| 444 | - return json_decode($this->config->getAppValue('core', self::CACHE_KEY, '{}'), true); |
|
| 445 | - } |
|
| 446 | - return []; |
|
| 447 | - } |
|
| 448 | - |
|
| 449 | - /** |
|
| 450 | - * Stores the results in the app config as well as cache |
|
| 451 | - * |
|
| 452 | - * @param string $scope |
|
| 453 | - * @param array $result |
|
| 454 | - */ |
|
| 455 | - private function storeResults($scope, array $result) { |
|
| 456 | - $resultArray = $this->getResults(); |
|
| 457 | - unset($resultArray[$scope]); |
|
| 458 | - if(!empty($result)) { |
|
| 459 | - $resultArray[$scope] = $result; |
|
| 460 | - } |
|
| 461 | - if ($this->config !== null) { |
|
| 462 | - $this->config->setAppValue('core', self::CACHE_KEY, json_encode($resultArray)); |
|
| 463 | - } |
|
| 464 | - $this->cache->set(self::CACHE_KEY, json_encode($resultArray)); |
|
| 465 | - } |
|
| 466 | - |
|
| 467 | - /** |
|
| 468 | - * |
|
| 469 | - * Clean previous results for a proper rescanning. Otherwise |
|
| 470 | - */ |
|
| 471 | - private function cleanResults() { |
|
| 472 | - $this->config->deleteAppValue('core', self::CACHE_KEY); |
|
| 473 | - $this->cache->remove(self::CACHE_KEY); |
|
| 474 | - } |
|
| 475 | - |
|
| 476 | - /** |
|
| 477 | - * Verify the signature of $appId. Returns an array with the following content: |
|
| 478 | - * [ |
|
| 479 | - * 'FILE_MISSING' => |
|
| 480 | - * [ |
|
| 481 | - * 'filename' => [ |
|
| 482 | - * 'expected' => 'expectedSHA512', |
|
| 483 | - * 'current' => 'currentSHA512', |
|
| 484 | - * ], |
|
| 485 | - * ], |
|
| 486 | - * 'EXTRA_FILE' => |
|
| 487 | - * [ |
|
| 488 | - * 'filename' => [ |
|
| 489 | - * 'expected' => 'expectedSHA512', |
|
| 490 | - * 'current' => 'currentSHA512', |
|
| 491 | - * ], |
|
| 492 | - * ], |
|
| 493 | - * 'INVALID_HASH' => |
|
| 494 | - * [ |
|
| 495 | - * 'filename' => [ |
|
| 496 | - * 'expected' => 'expectedSHA512', |
|
| 497 | - * 'current' => 'currentSHA512', |
|
| 498 | - * ], |
|
| 499 | - * ], |
|
| 500 | - * ] |
|
| 501 | - * |
|
| 502 | - * Array may be empty in case no problems have been found. |
|
| 503 | - * |
|
| 504 | - * @param string $appId |
|
| 505 | - * @param string $path Optional path. If none is given it will be guessed. |
|
| 506 | - * @return array |
|
| 507 | - */ |
|
| 508 | - public function verifyAppSignature($appId, $path = '') { |
|
| 509 | - try { |
|
| 510 | - if($path === '') { |
|
| 511 | - $path = $this->appLocator->getAppPath($appId); |
|
| 512 | - } |
|
| 513 | - $result = $this->verify( |
|
| 514 | - $path . '/appinfo/signature.json', |
|
| 515 | - $path, |
|
| 516 | - $appId |
|
| 517 | - ); |
|
| 518 | - } catch (\Exception $e) { |
|
| 519 | - $result = [ |
|
| 520 | - 'EXCEPTION' => [ |
|
| 521 | - 'class' => get_class($e), |
|
| 522 | - 'message' => $e->getMessage(), |
|
| 523 | - ], |
|
| 524 | - ]; |
|
| 525 | - } |
|
| 526 | - $this->storeResults($appId, $result); |
|
| 527 | - |
|
| 528 | - return $result; |
|
| 529 | - } |
|
| 530 | - |
|
| 531 | - /** |
|
| 532 | - * Verify the signature of core. Returns an array with the following content: |
|
| 533 | - * [ |
|
| 534 | - * 'FILE_MISSING' => |
|
| 535 | - * [ |
|
| 536 | - * 'filename' => [ |
|
| 537 | - * 'expected' => 'expectedSHA512', |
|
| 538 | - * 'current' => 'currentSHA512', |
|
| 539 | - * ], |
|
| 540 | - * ], |
|
| 541 | - * 'EXTRA_FILE' => |
|
| 542 | - * [ |
|
| 543 | - * 'filename' => [ |
|
| 544 | - * 'expected' => 'expectedSHA512', |
|
| 545 | - * 'current' => 'currentSHA512', |
|
| 546 | - * ], |
|
| 547 | - * ], |
|
| 548 | - * 'INVALID_HASH' => |
|
| 549 | - * [ |
|
| 550 | - * 'filename' => [ |
|
| 551 | - * 'expected' => 'expectedSHA512', |
|
| 552 | - * 'current' => 'currentSHA512', |
|
| 553 | - * ], |
|
| 554 | - * ], |
|
| 555 | - * ] |
|
| 556 | - * |
|
| 557 | - * Array may be empty in case no problems have been found. |
|
| 558 | - * |
|
| 559 | - * @return array |
|
| 560 | - */ |
|
| 561 | - public function verifyCoreSignature() { |
|
| 562 | - try { |
|
| 563 | - $result = $this->verify( |
|
| 564 | - $this->environmentHelper->getServerRoot() . '/core/signature.json', |
|
| 565 | - $this->environmentHelper->getServerRoot(), |
|
| 566 | - 'core' |
|
| 567 | - ); |
|
| 568 | - } catch (\Exception $e) { |
|
| 569 | - $result = [ |
|
| 570 | - 'EXCEPTION' => [ |
|
| 571 | - 'class' => get_class($e), |
|
| 572 | - 'message' => $e->getMessage(), |
|
| 573 | - ], |
|
| 574 | - ]; |
|
| 575 | - } |
|
| 576 | - $this->storeResults('core', $result); |
|
| 577 | - |
|
| 578 | - return $result; |
|
| 579 | - } |
|
| 580 | - |
|
| 581 | - /** |
|
| 582 | - * Verify the core code of the instance as well as all applicable applications |
|
| 583 | - * and store the results. |
|
| 584 | - */ |
|
| 585 | - public function runInstanceVerification() { |
|
| 586 | - $this->cleanResults(); |
|
| 587 | - $this->verifyCoreSignature(); |
|
| 588 | - $appIds = $this->appLocator->getAllApps(); |
|
| 589 | - foreach($appIds as $appId) { |
|
| 590 | - // If an application is shipped a valid signature is required |
|
| 591 | - $isShipped = $this->appManager->isShipped($appId); |
|
| 592 | - $appNeedsToBeChecked = false; |
|
| 593 | - if ($isShipped) { |
|
| 594 | - $appNeedsToBeChecked = true; |
|
| 595 | - } elseif ($this->fileAccessHelper->file_exists($this->appLocator->getAppPath($appId) . '/appinfo/signature.json')) { |
|
| 596 | - // Otherwise only if the application explicitly ships a signature.json file |
|
| 597 | - $appNeedsToBeChecked = true; |
|
| 598 | - } |
|
| 599 | - |
|
| 600 | - if($appNeedsToBeChecked) { |
|
| 601 | - $this->verifyAppSignature($appId); |
|
| 602 | - } |
|
| 603 | - } |
|
| 604 | - } |
|
| 54 | + const CACHE_KEY = 'oc.integritycheck.checker'; |
|
| 55 | + /** @var EnvironmentHelper */ |
|
| 56 | + private $environmentHelper; |
|
| 57 | + /** @var AppLocator */ |
|
| 58 | + private $appLocator; |
|
| 59 | + /** @var FileAccessHelper */ |
|
| 60 | + private $fileAccessHelper; |
|
| 61 | + /** @var IConfig */ |
|
| 62 | + private $config; |
|
| 63 | + /** @var ICache */ |
|
| 64 | + private $cache; |
|
| 65 | + /** @var IAppManager */ |
|
| 66 | + private $appManager; |
|
| 67 | + /** @var ITempManager */ |
|
| 68 | + private $tempManager; |
|
| 69 | + |
|
| 70 | + /** |
|
| 71 | + * @param EnvironmentHelper $environmentHelper |
|
| 72 | + * @param FileAccessHelper $fileAccessHelper |
|
| 73 | + * @param AppLocator $appLocator |
|
| 74 | + * @param IConfig $config |
|
| 75 | + * @param ICacheFactory $cacheFactory |
|
| 76 | + * @param IAppManager $appManager |
|
| 77 | + * @param ITempManager $tempManager |
|
| 78 | + */ |
|
| 79 | + public function __construct(EnvironmentHelper $environmentHelper, |
|
| 80 | + FileAccessHelper $fileAccessHelper, |
|
| 81 | + AppLocator $appLocator, |
|
| 82 | + IConfig $config = null, |
|
| 83 | + ICacheFactory $cacheFactory, |
|
| 84 | + IAppManager $appManager = null, |
|
| 85 | + ITempManager $tempManager) { |
|
| 86 | + $this->environmentHelper = $environmentHelper; |
|
| 87 | + $this->fileAccessHelper = $fileAccessHelper; |
|
| 88 | + $this->appLocator = $appLocator; |
|
| 89 | + $this->config = $config; |
|
| 90 | + $this->cache = $cacheFactory->createDistributed(self::CACHE_KEY); |
|
| 91 | + $this->appManager = $appManager; |
|
| 92 | + $this->tempManager = $tempManager; |
|
| 93 | + } |
|
| 94 | + |
|
| 95 | + /** |
|
| 96 | + * Whether code signing is enforced or not. |
|
| 97 | + * |
|
| 98 | + * @return bool |
|
| 99 | + */ |
|
| 100 | + public function isCodeCheckEnforced() { |
|
| 101 | + $notSignedChannels = [ '', 'git']; |
|
| 102 | + if (in_array($this->environmentHelper->getChannel(), $notSignedChannels, true)) { |
|
| 103 | + return false; |
|
| 104 | + } |
|
| 105 | + |
|
| 106 | + /** |
|
| 107 | + * This config option is undocumented and supposed to be so, it's only |
|
| 108 | + * applicable for very specific scenarios and we should not advertise it |
|
| 109 | + * too prominent. So please do not add it to config.sample.php. |
|
| 110 | + */ |
|
| 111 | + if ($this->config !== null) { |
|
| 112 | + $isIntegrityCheckDisabled = $this->config->getSystemValue('integrity.check.disabled', false); |
|
| 113 | + } else { |
|
| 114 | + $isIntegrityCheckDisabled = false; |
|
| 115 | + } |
|
| 116 | + if ($isIntegrityCheckDisabled === true) { |
|
| 117 | + return false; |
|
| 118 | + } |
|
| 119 | + |
|
| 120 | + return true; |
|
| 121 | + } |
|
| 122 | + |
|
| 123 | + /** |
|
| 124 | + * Enumerates all files belonging to the folder. Sensible defaults are excluded. |
|
| 125 | + * |
|
| 126 | + * @param string $folderToIterate |
|
| 127 | + * @param string $root |
|
| 128 | + * @return \RecursiveIteratorIterator |
|
| 129 | + * @throws \Exception |
|
| 130 | + */ |
|
| 131 | + private function getFolderIterator($folderToIterate, $root = '') { |
|
| 132 | + $dirItr = new \RecursiveDirectoryIterator( |
|
| 133 | + $folderToIterate, |
|
| 134 | + \RecursiveDirectoryIterator::SKIP_DOTS |
|
| 135 | + ); |
|
| 136 | + if($root === '') { |
|
| 137 | + $root = \OC::$SERVERROOT; |
|
| 138 | + } |
|
| 139 | + $root = rtrim($root, '/'); |
|
| 140 | + |
|
| 141 | + $excludeGenericFilesIterator = new ExcludeFileByNameFilterIterator($dirItr); |
|
| 142 | + $excludeFoldersIterator = new ExcludeFoldersByPathFilterIterator($excludeGenericFilesIterator, $root); |
|
| 143 | + |
|
| 144 | + return new \RecursiveIteratorIterator( |
|
| 145 | + $excludeFoldersIterator, |
|
| 146 | + \RecursiveIteratorIterator::SELF_FIRST |
|
| 147 | + ); |
|
| 148 | + } |
|
| 149 | + |
|
| 150 | + /** |
|
| 151 | + * Returns an array of ['filename' => 'SHA512-hash-of-file'] for all files found |
|
| 152 | + * in the iterator. |
|
| 153 | + * |
|
| 154 | + * @param \RecursiveIteratorIterator $iterator |
|
| 155 | + * @param string $path |
|
| 156 | + * @return array Array of hashes. |
|
| 157 | + */ |
|
| 158 | + private function generateHashes(\RecursiveIteratorIterator $iterator, |
|
| 159 | + $path) { |
|
| 160 | + $hashes = []; |
|
| 161 | + $copiedWebserverSettingFiles = false; |
|
| 162 | + $tmpFolder = ''; |
|
| 163 | + |
|
| 164 | + $baseDirectoryLength = strlen($path); |
|
| 165 | + foreach($iterator as $filename => $data) { |
|
| 166 | + /** @var \DirectoryIterator $data */ |
|
| 167 | + if($data->isDir()) { |
|
| 168 | + continue; |
|
| 169 | + } |
|
| 170 | + |
|
| 171 | + $relativeFileName = substr($filename, $baseDirectoryLength); |
|
| 172 | + $relativeFileName = ltrim($relativeFileName, '/'); |
|
| 173 | + |
|
| 174 | + // Exclude signature.json files in the appinfo and root folder |
|
| 175 | + if($relativeFileName === 'appinfo/signature.json') { |
|
| 176 | + continue; |
|
| 177 | + } |
|
| 178 | + // Exclude signature.json files in the appinfo and core folder |
|
| 179 | + if($relativeFileName === 'core/signature.json') { |
|
| 180 | + continue; |
|
| 181 | + } |
|
| 182 | + |
|
| 183 | + // The .user.ini and the .htaccess file of ownCloud can contain some |
|
| 184 | + // custom modifications such as for example the maximum upload size |
|
| 185 | + // to ensure that this will not lead to false positives this will |
|
| 186 | + // copy the file to a temporary folder and reset it to the default |
|
| 187 | + // values. |
|
| 188 | + if($filename === $this->environmentHelper->getServerRoot() . '/.htaccess' |
|
| 189 | + || $filename === $this->environmentHelper->getServerRoot() . '/.user.ini') { |
|
| 190 | + |
|
| 191 | + if(!$copiedWebserverSettingFiles) { |
|
| 192 | + $tmpFolder = rtrim($this->tempManager->getTemporaryFolder(), '/'); |
|
| 193 | + copy($this->environmentHelper->getServerRoot() . '/.htaccess', $tmpFolder . '/.htaccess'); |
|
| 194 | + copy($this->environmentHelper->getServerRoot() . '/.user.ini', $tmpFolder . '/.user.ini'); |
|
| 195 | + \OC_Files::setUploadLimit( |
|
| 196 | + \OCP\Util::computerFileSize('511MB'), |
|
| 197 | + [ |
|
| 198 | + '.htaccess' => $tmpFolder . '/.htaccess', |
|
| 199 | + '.user.ini' => $tmpFolder . '/.user.ini', |
|
| 200 | + ] |
|
| 201 | + ); |
|
| 202 | + } |
|
| 203 | + } |
|
| 204 | + |
|
| 205 | + // The .user.ini file can contain custom modifications to the file size |
|
| 206 | + // as well. |
|
| 207 | + if($filename === $this->environmentHelper->getServerRoot() . '/.user.ini') { |
|
| 208 | + $fileContent = file_get_contents($tmpFolder . '/.user.ini'); |
|
| 209 | + $hashes[$relativeFileName] = hash('sha512', $fileContent); |
|
| 210 | + continue; |
|
| 211 | + } |
|
| 212 | + |
|
| 213 | + // The .htaccess file in the root folder of ownCloud can contain |
|
| 214 | + // custom content after the installation due to the fact that dynamic |
|
| 215 | + // content is written into it at installation time as well. This |
|
| 216 | + // includes for example the 404 and 403 instructions. |
|
| 217 | + // Thus we ignore everything below the first occurrence of |
|
| 218 | + // "#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####" and have the |
|
| 219 | + // hash generated based on this. |
|
| 220 | + if($filename === $this->environmentHelper->getServerRoot() . '/.htaccess') { |
|
| 221 | + $fileContent = file_get_contents($tmpFolder . '/.htaccess'); |
|
| 222 | + $explodedArray = explode('#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####', $fileContent); |
|
| 223 | + if(count($explodedArray) === 2) { |
|
| 224 | + $hashes[$relativeFileName] = hash('sha512', $explodedArray[0]); |
|
| 225 | + continue; |
|
| 226 | + } |
|
| 227 | + } |
|
| 228 | + |
|
| 229 | + $hashes[$relativeFileName] = hash_file('sha512', $filename); |
|
| 230 | + } |
|
| 231 | + |
|
| 232 | + return $hashes; |
|
| 233 | + } |
|
| 234 | + |
|
| 235 | + /** |
|
| 236 | + * Creates the signature data |
|
| 237 | + * |
|
| 238 | + * @param array $hashes |
|
| 239 | + * @param X509 $certificate |
|
| 240 | + * @param RSA $privateKey |
|
| 241 | + * @return string |
|
| 242 | + */ |
|
| 243 | + private function createSignatureData(array $hashes, |
|
| 244 | + X509 $certificate, |
|
| 245 | + RSA $privateKey) { |
|
| 246 | + ksort($hashes); |
|
| 247 | + |
|
| 248 | + $privateKey->setSignatureMode(RSA::SIGNATURE_PSS); |
|
| 249 | + $privateKey->setMGFHash('sha512'); |
|
| 250 | + // See https://tools.ietf.org/html/rfc3447#page-38 |
|
| 251 | + $privateKey->setSaltLength(0); |
|
| 252 | + $signature = $privateKey->sign(json_encode($hashes)); |
|
| 253 | + |
|
| 254 | + return [ |
|
| 255 | + 'hashes' => $hashes, |
|
| 256 | + 'signature' => base64_encode($signature), |
|
| 257 | + 'certificate' => $certificate->saveX509($certificate->currentCert), |
|
| 258 | + ]; |
|
| 259 | + } |
|
| 260 | + |
|
| 261 | + /** |
|
| 262 | + * Write the signature of the app in the specified folder |
|
| 263 | + * |
|
| 264 | + * @param string $path |
|
| 265 | + * @param X509 $certificate |
|
| 266 | + * @param RSA $privateKey |
|
| 267 | + * @throws \Exception |
|
| 268 | + */ |
|
| 269 | + public function writeAppSignature($path, |
|
| 270 | + X509 $certificate, |
|
| 271 | + RSA $privateKey) { |
|
| 272 | + $appInfoDir = $path . '/appinfo'; |
|
| 273 | + try { |
|
| 274 | + $this->fileAccessHelper->assertDirectoryExists($appInfoDir); |
|
| 275 | + |
|
| 276 | + $iterator = $this->getFolderIterator($path); |
|
| 277 | + $hashes = $this->generateHashes($iterator, $path); |
|
| 278 | + $signature = $this->createSignatureData($hashes, $certificate, $privateKey); |
|
| 279 | + $this->fileAccessHelper->file_put_contents( |
|
| 280 | + $appInfoDir . '/signature.json', |
|
| 281 | + json_encode($signature, JSON_PRETTY_PRINT) |
|
| 282 | + ); |
|
| 283 | + } catch (\Exception $e){ |
|
| 284 | + if (!$this->fileAccessHelper->is_writable($appInfoDir)) { |
|
| 285 | + throw new \Exception($appInfoDir . ' is not writable'); |
|
| 286 | + } |
|
| 287 | + throw $e; |
|
| 288 | + } |
|
| 289 | + } |
|
| 290 | + |
|
| 291 | + /** |
|
| 292 | + * Write the signature of core |
|
| 293 | + * |
|
| 294 | + * @param X509 $certificate |
|
| 295 | + * @param RSA $rsa |
|
| 296 | + * @param string $path |
|
| 297 | + * @throws \Exception |
|
| 298 | + */ |
|
| 299 | + public function writeCoreSignature(X509 $certificate, |
|
| 300 | + RSA $rsa, |
|
| 301 | + $path) { |
|
| 302 | + $coreDir = $path . '/core'; |
|
| 303 | + try { |
|
| 304 | + |
|
| 305 | + $this->fileAccessHelper->assertDirectoryExists($coreDir); |
|
| 306 | + $iterator = $this->getFolderIterator($path, $path); |
|
| 307 | + $hashes = $this->generateHashes($iterator, $path); |
|
| 308 | + $signatureData = $this->createSignatureData($hashes, $certificate, $rsa); |
|
| 309 | + $this->fileAccessHelper->file_put_contents( |
|
| 310 | + $coreDir . '/signature.json', |
|
| 311 | + json_encode($signatureData, JSON_PRETTY_PRINT) |
|
| 312 | + ); |
|
| 313 | + } catch (\Exception $e){ |
|
| 314 | + if (!$this->fileAccessHelper->is_writable($coreDir)) { |
|
| 315 | + throw new \Exception($coreDir . ' is not writable'); |
|
| 316 | + } |
|
| 317 | + throw $e; |
|
| 318 | + } |
|
| 319 | + } |
|
| 320 | + |
|
| 321 | + /** |
|
| 322 | + * Verifies the signature for the specified path. |
|
| 323 | + * |
|
| 324 | + * @param string $signaturePath |
|
| 325 | + * @param string $basePath |
|
| 326 | + * @param string $certificateCN |
|
| 327 | + * @return array |
|
| 328 | + * @throws InvalidSignatureException |
|
| 329 | + * @throws \Exception |
|
| 330 | + */ |
|
| 331 | + private function verify($signaturePath, $basePath, $certificateCN) { |
|
| 332 | + if(!$this->isCodeCheckEnforced()) { |
|
| 333 | + return []; |
|
| 334 | + } |
|
| 335 | + |
|
| 336 | + $signatureData = json_decode($this->fileAccessHelper->file_get_contents($signaturePath), true); |
|
| 337 | + if(!is_array($signatureData)) { |
|
| 338 | + throw new InvalidSignatureException('Signature data not found.'); |
|
| 339 | + } |
|
| 340 | + |
|
| 341 | + $expectedHashes = $signatureData['hashes']; |
|
| 342 | + ksort($expectedHashes); |
|
| 343 | + $signature = base64_decode($signatureData['signature']); |
|
| 344 | + $certificate = $signatureData['certificate']; |
|
| 345 | + |
|
| 346 | + // Check if certificate is signed by Nextcloud Root Authority |
|
| 347 | + $x509 = new \phpseclib\File\X509(); |
|
| 348 | + $rootCertificatePublicKey = $this->fileAccessHelper->file_get_contents($this->environmentHelper->getServerRoot().'/resources/codesigning/root.crt'); |
|
| 349 | + $x509->loadCA($rootCertificatePublicKey); |
|
| 350 | + $x509->loadX509($certificate); |
|
| 351 | + if(!$x509->validateSignature()) { |
|
| 352 | + throw new InvalidSignatureException('Certificate is not valid.'); |
|
| 353 | + } |
|
| 354 | + // Verify if certificate has proper CN. "core" CN is always trusted. |
|
| 355 | + if($x509->getDN(X509::DN_OPENSSL)['CN'] !== $certificateCN && $x509->getDN(X509::DN_OPENSSL)['CN'] !== 'core') { |
|
| 356 | + throw new InvalidSignatureException( |
|
| 357 | + sprintf('Certificate is not valid for required scope. (Requested: %s, current: CN=%s)', $certificateCN, $x509->getDN(true)['CN']) |
|
| 358 | + ); |
|
| 359 | + } |
|
| 360 | + |
|
| 361 | + // Check if the signature of the files is valid |
|
| 362 | + $rsa = new \phpseclib\Crypt\RSA(); |
|
| 363 | + $rsa->loadKey($x509->currentCert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']); |
|
| 364 | + $rsa->setSignatureMode(RSA::SIGNATURE_PSS); |
|
| 365 | + $rsa->setMGFHash('sha512'); |
|
| 366 | + // See https://tools.ietf.org/html/rfc3447#page-38 |
|
| 367 | + $rsa->setSaltLength(0); |
|
| 368 | + if(!$rsa->verify(json_encode($expectedHashes), $signature)) { |
|
| 369 | + throw new InvalidSignatureException('Signature could not get verified.'); |
|
| 370 | + } |
|
| 371 | + |
|
| 372 | + // Fixes for the updater as shipped with ownCloud 9.0.x: The updater is |
|
| 373 | + // replaced after the code integrity check is performed. |
|
| 374 | + // |
|
| 375 | + // Due to this reason we exclude the whole updater/ folder from the code |
|
| 376 | + // integrity check. |
|
| 377 | + if($basePath === $this->environmentHelper->getServerRoot()) { |
|
| 378 | + foreach($expectedHashes as $fileName => $hash) { |
|
| 379 | + if(strpos($fileName, 'updater/') === 0) { |
|
| 380 | + unset($expectedHashes[$fileName]); |
|
| 381 | + } |
|
| 382 | + } |
|
| 383 | + } |
|
| 384 | + |
|
| 385 | + // Compare the list of files which are not identical |
|
| 386 | + $currentInstanceHashes = $this->generateHashes($this->getFolderIterator($basePath), $basePath); |
|
| 387 | + $differencesA = array_diff($expectedHashes, $currentInstanceHashes); |
|
| 388 | + $differencesB = array_diff($currentInstanceHashes, $expectedHashes); |
|
| 389 | + $differences = array_unique(array_merge($differencesA, $differencesB)); |
|
| 390 | + $differenceArray = []; |
|
| 391 | + foreach($differences as $filename => $hash) { |
|
| 392 | + // Check if file should not exist in the new signature table |
|
| 393 | + if(!array_key_exists($filename, $expectedHashes)) { |
|
| 394 | + $differenceArray['EXTRA_FILE'][$filename]['expected'] = ''; |
|
| 395 | + $differenceArray['EXTRA_FILE'][$filename]['current'] = $hash; |
|
| 396 | + continue; |
|
| 397 | + } |
|
| 398 | + |
|
| 399 | + // Check if file is missing |
|
| 400 | + if(!array_key_exists($filename, $currentInstanceHashes)) { |
|
| 401 | + $differenceArray['FILE_MISSING'][$filename]['expected'] = $expectedHashes[$filename]; |
|
| 402 | + $differenceArray['FILE_MISSING'][$filename]['current'] = ''; |
|
| 403 | + continue; |
|
| 404 | + } |
|
| 405 | + |
|
| 406 | + // Check if hash does mismatch |
|
| 407 | + if($expectedHashes[$filename] !== $currentInstanceHashes[$filename]) { |
|
| 408 | + $differenceArray['INVALID_HASH'][$filename]['expected'] = $expectedHashes[$filename]; |
|
| 409 | + $differenceArray['INVALID_HASH'][$filename]['current'] = $currentInstanceHashes[$filename]; |
|
| 410 | + continue; |
|
| 411 | + } |
|
| 412 | + |
|
| 413 | + // Should never happen. |
|
| 414 | + throw new \Exception('Invalid behaviour in file hash comparison experienced. Please report this error to the developers.'); |
|
| 415 | + } |
|
| 416 | + |
|
| 417 | + return $differenceArray; |
|
| 418 | + } |
|
| 419 | + |
|
| 420 | + /** |
|
| 421 | + * Whether the code integrity check has passed successful or not |
|
| 422 | + * |
|
| 423 | + * @return bool |
|
| 424 | + */ |
|
| 425 | + public function hasPassedCheck() { |
|
| 426 | + $results = $this->getResults(); |
|
| 427 | + if(empty($results)) { |
|
| 428 | + return true; |
|
| 429 | + } |
|
| 430 | + |
|
| 431 | + return false; |
|
| 432 | + } |
|
| 433 | + |
|
| 434 | + /** |
|
| 435 | + * @return array |
|
| 436 | + */ |
|
| 437 | + public function getResults() { |
|
| 438 | + $cachedResults = $this->cache->get(self::CACHE_KEY); |
|
| 439 | + if(!is_null($cachedResults)) { |
|
| 440 | + return json_decode($cachedResults, true); |
|
| 441 | + } |
|
| 442 | + |
|
| 443 | + if ($this->config !== null) { |
|
| 444 | + return json_decode($this->config->getAppValue('core', self::CACHE_KEY, '{}'), true); |
|
| 445 | + } |
|
| 446 | + return []; |
|
| 447 | + } |
|
| 448 | + |
|
| 449 | + /** |
|
| 450 | + * Stores the results in the app config as well as cache |
|
| 451 | + * |
|
| 452 | + * @param string $scope |
|
| 453 | + * @param array $result |
|
| 454 | + */ |
|
| 455 | + private function storeResults($scope, array $result) { |
|
| 456 | + $resultArray = $this->getResults(); |
|
| 457 | + unset($resultArray[$scope]); |
|
| 458 | + if(!empty($result)) { |
|
| 459 | + $resultArray[$scope] = $result; |
|
| 460 | + } |
|
| 461 | + if ($this->config !== null) { |
|
| 462 | + $this->config->setAppValue('core', self::CACHE_KEY, json_encode($resultArray)); |
|
| 463 | + } |
|
| 464 | + $this->cache->set(self::CACHE_KEY, json_encode($resultArray)); |
|
| 465 | + } |
|
| 466 | + |
|
| 467 | + /** |
|
| 468 | + * |
|
| 469 | + * Clean previous results for a proper rescanning. Otherwise |
|
| 470 | + */ |
|
| 471 | + private function cleanResults() { |
|
| 472 | + $this->config->deleteAppValue('core', self::CACHE_KEY); |
|
| 473 | + $this->cache->remove(self::CACHE_KEY); |
|
| 474 | + } |
|
| 475 | + |
|
| 476 | + /** |
|
| 477 | + * Verify the signature of $appId. Returns an array with the following content: |
|
| 478 | + * [ |
|
| 479 | + * 'FILE_MISSING' => |
|
| 480 | + * [ |
|
| 481 | + * 'filename' => [ |
|
| 482 | + * 'expected' => 'expectedSHA512', |
|
| 483 | + * 'current' => 'currentSHA512', |
|
| 484 | + * ], |
|
| 485 | + * ], |
|
| 486 | + * 'EXTRA_FILE' => |
|
| 487 | + * [ |
|
| 488 | + * 'filename' => [ |
|
| 489 | + * 'expected' => 'expectedSHA512', |
|
| 490 | + * 'current' => 'currentSHA512', |
|
| 491 | + * ], |
|
| 492 | + * ], |
|
| 493 | + * 'INVALID_HASH' => |
|
| 494 | + * [ |
|
| 495 | + * 'filename' => [ |
|
| 496 | + * 'expected' => 'expectedSHA512', |
|
| 497 | + * 'current' => 'currentSHA512', |
|
| 498 | + * ], |
|
| 499 | + * ], |
|
| 500 | + * ] |
|
| 501 | + * |
|
| 502 | + * Array may be empty in case no problems have been found. |
|
| 503 | + * |
|
| 504 | + * @param string $appId |
|
| 505 | + * @param string $path Optional path. If none is given it will be guessed. |
|
| 506 | + * @return array |
|
| 507 | + */ |
|
| 508 | + public function verifyAppSignature($appId, $path = '') { |
|
| 509 | + try { |
|
| 510 | + if($path === '') { |
|
| 511 | + $path = $this->appLocator->getAppPath($appId); |
|
| 512 | + } |
|
| 513 | + $result = $this->verify( |
|
| 514 | + $path . '/appinfo/signature.json', |
|
| 515 | + $path, |
|
| 516 | + $appId |
|
| 517 | + ); |
|
| 518 | + } catch (\Exception $e) { |
|
| 519 | + $result = [ |
|
| 520 | + 'EXCEPTION' => [ |
|
| 521 | + 'class' => get_class($e), |
|
| 522 | + 'message' => $e->getMessage(), |
|
| 523 | + ], |
|
| 524 | + ]; |
|
| 525 | + } |
|
| 526 | + $this->storeResults($appId, $result); |
|
| 527 | + |
|
| 528 | + return $result; |
|
| 529 | + } |
|
| 530 | + |
|
| 531 | + /** |
|
| 532 | + * Verify the signature of core. Returns an array with the following content: |
|
| 533 | + * [ |
|
| 534 | + * 'FILE_MISSING' => |
|
| 535 | + * [ |
|
| 536 | + * 'filename' => [ |
|
| 537 | + * 'expected' => 'expectedSHA512', |
|
| 538 | + * 'current' => 'currentSHA512', |
|
| 539 | + * ], |
|
| 540 | + * ], |
|
| 541 | + * 'EXTRA_FILE' => |
|
| 542 | + * [ |
|
| 543 | + * 'filename' => [ |
|
| 544 | + * 'expected' => 'expectedSHA512', |
|
| 545 | + * 'current' => 'currentSHA512', |
|
| 546 | + * ], |
|
| 547 | + * ], |
|
| 548 | + * 'INVALID_HASH' => |
|
| 549 | + * [ |
|
| 550 | + * 'filename' => [ |
|
| 551 | + * 'expected' => 'expectedSHA512', |
|
| 552 | + * 'current' => 'currentSHA512', |
|
| 553 | + * ], |
|
| 554 | + * ], |
|
| 555 | + * ] |
|
| 556 | + * |
|
| 557 | + * Array may be empty in case no problems have been found. |
|
| 558 | + * |
|
| 559 | + * @return array |
|
| 560 | + */ |
|
| 561 | + public function verifyCoreSignature() { |
|
| 562 | + try { |
|
| 563 | + $result = $this->verify( |
|
| 564 | + $this->environmentHelper->getServerRoot() . '/core/signature.json', |
|
| 565 | + $this->environmentHelper->getServerRoot(), |
|
| 566 | + 'core' |
|
| 567 | + ); |
|
| 568 | + } catch (\Exception $e) { |
|
| 569 | + $result = [ |
|
| 570 | + 'EXCEPTION' => [ |
|
| 571 | + 'class' => get_class($e), |
|
| 572 | + 'message' => $e->getMessage(), |
|
| 573 | + ], |
|
| 574 | + ]; |
|
| 575 | + } |
|
| 576 | + $this->storeResults('core', $result); |
|
| 577 | + |
|
| 578 | + return $result; |
|
| 579 | + } |
|
| 580 | + |
|
| 581 | + /** |
|
| 582 | + * Verify the core code of the instance as well as all applicable applications |
|
| 583 | + * and store the results. |
|
| 584 | + */ |
|
| 585 | + public function runInstanceVerification() { |
|
| 586 | + $this->cleanResults(); |
|
| 587 | + $this->verifyCoreSignature(); |
|
| 588 | + $appIds = $this->appLocator->getAllApps(); |
|
| 589 | + foreach($appIds as $appId) { |
|
| 590 | + // If an application is shipped a valid signature is required |
|
| 591 | + $isShipped = $this->appManager->isShipped($appId); |
|
| 592 | + $appNeedsToBeChecked = false; |
|
| 593 | + if ($isShipped) { |
|
| 594 | + $appNeedsToBeChecked = true; |
|
| 595 | + } elseif ($this->fileAccessHelper->file_exists($this->appLocator->getAppPath($appId) . '/appinfo/signature.json')) { |
|
| 596 | + // Otherwise only if the application explicitly ships a signature.json file |
|
| 597 | + $appNeedsToBeChecked = true; |
|
| 598 | + } |
|
| 599 | + |
|
| 600 | + if($appNeedsToBeChecked) { |
|
| 601 | + $this->verifyAppSignature($appId); |
|
| 602 | + } |
|
| 603 | + } |
|
| 604 | + } |
|
| 605 | 605 | } |