@@ -52,552 +52,552 @@ |
||
52 | 52 | * This class provides the functionality needed to install, update and remove apps |
53 | 53 | */ |
54 | 54 | class Installer { |
55 | - /** @var AppFetcher */ |
|
56 | - private $appFetcher; |
|
57 | - /** @var IClientService */ |
|
58 | - private $clientService; |
|
59 | - /** @var ITempManager */ |
|
60 | - private $tempManager; |
|
61 | - /** @var ILogger */ |
|
62 | - private $logger; |
|
63 | - /** @var IConfig */ |
|
64 | - private $config; |
|
65 | - /** @var array - for caching the result of app fetcher */ |
|
66 | - private $apps = null; |
|
67 | - /** @var bool|null - for caching the result of the ready status */ |
|
68 | - private $isInstanceReadyForUpdates = null; |
|
69 | - |
|
70 | - /** |
|
71 | - * @param AppFetcher $appFetcher |
|
72 | - * @param IClientService $clientService |
|
73 | - * @param ITempManager $tempManager |
|
74 | - * @param ILogger $logger |
|
75 | - * @param IConfig $config |
|
76 | - */ |
|
77 | - public function __construct(AppFetcher $appFetcher, |
|
78 | - IClientService $clientService, |
|
79 | - ITempManager $tempManager, |
|
80 | - ILogger $logger, |
|
81 | - IConfig $config) { |
|
82 | - $this->appFetcher = $appFetcher; |
|
83 | - $this->clientService = $clientService; |
|
84 | - $this->tempManager = $tempManager; |
|
85 | - $this->logger = $logger; |
|
86 | - $this->config = $config; |
|
87 | - } |
|
88 | - |
|
89 | - /** |
|
90 | - * Installs an app that is located in one of the app folders already |
|
91 | - * |
|
92 | - * @param string $appId App to install |
|
93 | - * @throws \Exception |
|
94 | - * @return string app ID |
|
95 | - */ |
|
96 | - public function installApp($appId) { |
|
97 | - $app = \OC_App::findAppInDirectories($appId); |
|
98 | - if($app === false) { |
|
99 | - throw new \Exception('App not found in any app directory'); |
|
100 | - } |
|
101 | - |
|
102 | - $basedir = $app['path'].'/'.$appId; |
|
103 | - $info = OC_App::getAppInfo($basedir.'/appinfo/info.xml', true); |
|
104 | - |
|
105 | - $l = \OC::$server->getL10N('core'); |
|
106 | - |
|
107 | - if(!is_array($info)) { |
|
108 | - throw new \Exception( |
|
109 | - $l->t('App "%s" cannot be installed because appinfo file cannot be read.', |
|
110 | - [$appId] |
|
111 | - ) |
|
112 | - ); |
|
113 | - } |
|
114 | - |
|
115 | - $version = implode('.', \OCP\Util::getVersion()); |
|
116 | - if (!\OC_App::isAppCompatible($version, $info)) { |
|
117 | - throw new \Exception( |
|
118 | - // TODO $l |
|
119 | - $l->t('App "%s" cannot be installed because it is not compatible with this version of the server.', |
|
120 | - [$info['name']] |
|
121 | - ) |
|
122 | - ); |
|
123 | - } |
|
124 | - |
|
125 | - // check for required dependencies |
|
126 | - \OC_App::checkAppDependencies($this->config, $l, $info); |
|
127 | - \OC_App::registerAutoloading($appId, $basedir); |
|
128 | - |
|
129 | - //install the database |
|
130 | - if(is_file($basedir.'/appinfo/database.xml')) { |
|
131 | - if (\OC::$server->getConfig()->getAppValue($info['id'], 'installed_version') === null) { |
|
132 | - OC_DB::createDbFromStructure($basedir.'/appinfo/database.xml'); |
|
133 | - } else { |
|
134 | - OC_DB::updateDbFromStructure($basedir.'/appinfo/database.xml'); |
|
135 | - } |
|
136 | - } else { |
|
137 | - $ms = new \OC\DB\MigrationService($info['id'], \OC::$server->getDatabaseConnection()); |
|
138 | - $ms->migrate(); |
|
139 | - } |
|
140 | - |
|
141 | - \OC_App::setupBackgroundJobs($info['background-jobs']); |
|
142 | - |
|
143 | - //run appinfo/install.php |
|
144 | - self::includeAppScript($basedir . '/appinfo/install.php'); |
|
145 | - |
|
146 | - $appData = OC_App::getAppInfo($appId); |
|
147 | - OC_App::executeRepairSteps($appId, $appData['repair-steps']['install']); |
|
148 | - |
|
149 | - //set the installed version |
|
150 | - \OC::$server->getConfig()->setAppValue($info['id'], 'installed_version', OC_App::getAppVersion($info['id'], false)); |
|
151 | - \OC::$server->getConfig()->setAppValue($info['id'], 'enabled', 'no'); |
|
152 | - |
|
153 | - //set remote/public handlers |
|
154 | - foreach($info['remote'] as $name=>$path) { |
|
155 | - \OC::$server->getConfig()->setAppValue('core', 'remote_'.$name, $info['id'].'/'.$path); |
|
156 | - } |
|
157 | - foreach($info['public'] as $name=>$path) { |
|
158 | - \OC::$server->getConfig()->setAppValue('core', 'public_'.$name, $info['id'].'/'.$path); |
|
159 | - } |
|
160 | - |
|
161 | - OC_App::setAppTypes($info['id']); |
|
162 | - |
|
163 | - return $info['id']; |
|
164 | - } |
|
165 | - |
|
166 | - /** |
|
167 | - * Updates the specified app from the appstore |
|
168 | - * |
|
169 | - * @param string $appId |
|
170 | - * @return bool |
|
171 | - */ |
|
172 | - public function updateAppstoreApp($appId) { |
|
173 | - if($this->isUpdateAvailable($appId)) { |
|
174 | - try { |
|
175 | - $this->downloadApp($appId); |
|
176 | - } catch (\Exception $e) { |
|
177 | - $this->logger->logException($e, [ |
|
178 | - 'level' => ILogger::ERROR, |
|
179 | - 'app' => 'core', |
|
180 | - ]); |
|
181 | - return false; |
|
182 | - } |
|
183 | - return OC_App::updateApp($appId); |
|
184 | - } |
|
185 | - |
|
186 | - return false; |
|
187 | - } |
|
188 | - |
|
189 | - /** |
|
190 | - * Downloads an app and puts it into the app directory |
|
191 | - * |
|
192 | - * @param string $appId |
|
193 | - * |
|
194 | - * @throws \Exception If the installation was not successful |
|
195 | - */ |
|
196 | - public function downloadApp($appId) { |
|
197 | - $appId = strtolower($appId); |
|
198 | - |
|
199 | - $apps = $this->appFetcher->get(); |
|
200 | - foreach($apps as $app) { |
|
201 | - if($app['id'] === $appId) { |
|
202 | - // Load the certificate |
|
203 | - $certificate = new X509(); |
|
204 | - $certificate->loadCA(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crt')); |
|
205 | - $loadedCertificate = $certificate->loadX509($app['certificate']); |
|
206 | - |
|
207 | - // Verify if the certificate has been revoked |
|
208 | - $crl = new X509(); |
|
209 | - $crl->loadCA(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crt')); |
|
210 | - $crl->loadCRL(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crl')); |
|
211 | - if($crl->validateSignature() !== true) { |
|
212 | - throw new \Exception('Could not validate CRL signature'); |
|
213 | - } |
|
214 | - $csn = $loadedCertificate['tbsCertificate']['serialNumber']->toString(); |
|
215 | - $revoked = $crl->getRevoked($csn); |
|
216 | - if ($revoked !== false) { |
|
217 | - throw new \Exception( |
|
218 | - sprintf( |
|
219 | - 'Certificate "%s" has been revoked', |
|
220 | - $csn |
|
221 | - ) |
|
222 | - ); |
|
223 | - } |
|
224 | - |
|
225 | - // Verify if the certificate has been issued by the Nextcloud Code Authority CA |
|
226 | - if($certificate->validateSignature() !== true) { |
|
227 | - throw new \Exception( |
|
228 | - sprintf( |
|
229 | - 'App with id %s has a certificate not issued by a trusted Code Signing Authority', |
|
230 | - $appId |
|
231 | - ) |
|
232 | - ); |
|
233 | - } |
|
234 | - |
|
235 | - // Verify if the certificate is issued for the requested app id |
|
236 | - $certInfo = openssl_x509_parse($app['certificate']); |
|
237 | - if(!isset($certInfo['subject']['CN'])) { |
|
238 | - throw new \Exception( |
|
239 | - sprintf( |
|
240 | - 'App with id %s has a cert with no CN', |
|
241 | - $appId |
|
242 | - ) |
|
243 | - ); |
|
244 | - } |
|
245 | - if($certInfo['subject']['CN'] !== $appId) { |
|
246 | - throw new \Exception( |
|
247 | - sprintf( |
|
248 | - 'App with id %s has a cert issued to %s', |
|
249 | - $appId, |
|
250 | - $certInfo['subject']['CN'] |
|
251 | - ) |
|
252 | - ); |
|
253 | - } |
|
254 | - |
|
255 | - // Download the release |
|
256 | - $tempFile = $this->tempManager->getTemporaryFile('.tar.gz'); |
|
257 | - $client = $this->clientService->newClient(); |
|
258 | - $client->get($app['releases'][0]['download'], ['save_to' => $tempFile]); |
|
259 | - |
|
260 | - // Check if the signature actually matches the downloaded content |
|
261 | - $certificate = openssl_get_publickey($app['certificate']); |
|
262 | - $verified = (bool)openssl_verify(file_get_contents($tempFile), base64_decode($app['releases'][0]['signature']), $certificate, OPENSSL_ALGO_SHA512); |
|
263 | - openssl_free_key($certificate); |
|
264 | - |
|
265 | - if($verified === true) { |
|
266 | - // Seems to match, let's proceed |
|
267 | - $extractDir = $this->tempManager->getTemporaryFolder(); |
|
268 | - $archive = new TAR($tempFile); |
|
269 | - |
|
270 | - if($archive) { |
|
271 | - if (!$archive->extract($extractDir)) { |
|
272 | - throw new \Exception( |
|
273 | - sprintf( |
|
274 | - 'Could not extract app %s', |
|
275 | - $appId |
|
276 | - ) |
|
277 | - ); |
|
278 | - } |
|
279 | - $allFiles = scandir($extractDir); |
|
280 | - $folders = array_diff($allFiles, ['.', '..']); |
|
281 | - $folders = array_values($folders); |
|
282 | - |
|
283 | - if(count($folders) > 1) { |
|
284 | - throw new \Exception( |
|
285 | - sprintf( |
|
286 | - 'Extracted app %s has more than 1 folder', |
|
287 | - $appId |
|
288 | - ) |
|
289 | - ); |
|
290 | - } |
|
291 | - |
|
292 | - // Check if appinfo/info.xml has the same app ID as well |
|
293 | - $loadEntities = libxml_disable_entity_loader(false); |
|
294 | - $xml = simplexml_load_file($extractDir . '/' . $folders[0] . '/appinfo/info.xml'); |
|
295 | - libxml_disable_entity_loader($loadEntities); |
|
296 | - if((string)$xml->id !== $appId) { |
|
297 | - throw new \Exception( |
|
298 | - sprintf( |
|
299 | - 'App for id %s has a wrong app ID in info.xml: %s', |
|
300 | - $appId, |
|
301 | - (string)$xml->id |
|
302 | - ) |
|
303 | - ); |
|
304 | - } |
|
305 | - |
|
306 | - // Check if the version is lower than before |
|
307 | - $currentVersion = OC_App::getAppVersion($appId); |
|
308 | - $newVersion = (string)$xml->version; |
|
309 | - if(version_compare($currentVersion, $newVersion) === 1) { |
|
310 | - throw new \Exception( |
|
311 | - sprintf( |
|
312 | - 'App for id %s has version %s and tried to update to lower version %s', |
|
313 | - $appId, |
|
314 | - $currentVersion, |
|
315 | - $newVersion |
|
316 | - ) |
|
317 | - ); |
|
318 | - } |
|
319 | - |
|
320 | - $baseDir = OC_App::getInstallPath() . '/' . $appId; |
|
321 | - // Remove old app with the ID if existent |
|
322 | - OC_Helper::rmdirr($baseDir); |
|
323 | - // Move to app folder |
|
324 | - if(@mkdir($baseDir)) { |
|
325 | - $extractDir .= '/' . $folders[0]; |
|
326 | - OC_Helper::copyr($extractDir, $baseDir); |
|
327 | - } |
|
328 | - OC_Helper::copyr($extractDir, $baseDir); |
|
329 | - OC_Helper::rmdirr($extractDir); |
|
330 | - return; |
|
331 | - } else { |
|
332 | - throw new \Exception( |
|
333 | - sprintf( |
|
334 | - 'Could not extract app with ID %s to %s', |
|
335 | - $appId, |
|
336 | - $extractDir |
|
337 | - ) |
|
338 | - ); |
|
339 | - } |
|
340 | - } else { |
|
341 | - // Signature does not match |
|
342 | - throw new \Exception( |
|
343 | - sprintf( |
|
344 | - 'App with id %s has invalid signature', |
|
345 | - $appId |
|
346 | - ) |
|
347 | - ); |
|
348 | - } |
|
349 | - } |
|
350 | - } |
|
351 | - |
|
352 | - throw new \Exception( |
|
353 | - sprintf( |
|
354 | - 'Could not download app %s', |
|
355 | - $appId |
|
356 | - ) |
|
357 | - ); |
|
358 | - } |
|
359 | - |
|
360 | - /** |
|
361 | - * Check if an update for the app is available |
|
362 | - * |
|
363 | - * @param string $appId |
|
364 | - * @return string|false false or the version number of the update |
|
365 | - */ |
|
366 | - public function isUpdateAvailable($appId) { |
|
367 | - if ($this->isInstanceReadyForUpdates === null) { |
|
368 | - $installPath = OC_App::getInstallPath(); |
|
369 | - if ($installPath === false || $installPath === null) { |
|
370 | - $this->isInstanceReadyForUpdates = false; |
|
371 | - } else { |
|
372 | - $this->isInstanceReadyForUpdates = true; |
|
373 | - } |
|
374 | - } |
|
375 | - |
|
376 | - if ($this->isInstanceReadyForUpdates === false) { |
|
377 | - return false; |
|
378 | - } |
|
379 | - |
|
380 | - if ($this->isInstalledFromGit($appId) === true) { |
|
381 | - return false; |
|
382 | - } |
|
383 | - |
|
384 | - if ($this->apps === null) { |
|
385 | - $this->apps = $this->appFetcher->get(); |
|
386 | - } |
|
387 | - |
|
388 | - foreach($this->apps as $app) { |
|
389 | - if($app['id'] === $appId) { |
|
390 | - $currentVersion = OC_App::getAppVersion($appId); |
|
391 | - $newestVersion = $app['releases'][0]['version']; |
|
392 | - if (version_compare($newestVersion, $currentVersion, '>')) { |
|
393 | - return $newestVersion; |
|
394 | - } else { |
|
395 | - return false; |
|
396 | - } |
|
397 | - } |
|
398 | - } |
|
399 | - |
|
400 | - return false; |
|
401 | - } |
|
402 | - |
|
403 | - /** |
|
404 | - * Check if app has been installed from git |
|
405 | - * @param string $name name of the application to remove |
|
406 | - * @return boolean |
|
407 | - * |
|
408 | - * The function will check if the path contains a .git folder |
|
409 | - */ |
|
410 | - private function isInstalledFromGit($appId) { |
|
411 | - $app = \OC_App::findAppInDirectories($appId); |
|
412 | - if($app === false) { |
|
413 | - return false; |
|
414 | - } |
|
415 | - $basedir = $app['path'].'/'.$appId; |
|
416 | - return file_exists($basedir.'/.git/'); |
|
417 | - } |
|
418 | - |
|
419 | - /** |
|
420 | - * Check if app is already downloaded |
|
421 | - * @param string $name name of the application to remove |
|
422 | - * @return boolean |
|
423 | - * |
|
424 | - * The function will check if the app is already downloaded in the apps repository |
|
425 | - */ |
|
426 | - public function isDownloaded($name) { |
|
427 | - foreach(\OC::$APPSROOTS as $dir) { |
|
428 | - $dirToTest = $dir['path']; |
|
429 | - $dirToTest .= '/'; |
|
430 | - $dirToTest .= $name; |
|
431 | - $dirToTest .= '/'; |
|
432 | - |
|
433 | - if (is_dir($dirToTest)) { |
|
434 | - return true; |
|
435 | - } |
|
436 | - } |
|
437 | - |
|
438 | - return false; |
|
439 | - } |
|
440 | - |
|
441 | - /** |
|
442 | - * Removes an app |
|
443 | - * @param string $appId ID of the application to remove |
|
444 | - * @return boolean |
|
445 | - * |
|
446 | - * |
|
447 | - * This function works as follows |
|
448 | - * -# call uninstall repair steps |
|
449 | - * -# removing the files |
|
450 | - * |
|
451 | - * The function will not delete preferences, tables and the configuration, |
|
452 | - * this has to be done by the function oc_app_uninstall(). |
|
453 | - */ |
|
454 | - public function removeApp($appId) { |
|
455 | - if($this->isDownloaded( $appId )) { |
|
456 | - if (\OC::$server->getAppManager()->isShipped($appId)) { |
|
457 | - return false; |
|
458 | - } |
|
459 | - $appDir = OC_App::getInstallPath() . '/' . $appId; |
|
460 | - OC_Helper::rmdirr($appDir); |
|
461 | - return true; |
|
462 | - }else{ |
|
463 | - \OCP\Util::writeLog('core', 'can\'t remove app '.$appId.'. It is not installed.', ILogger::ERROR); |
|
464 | - |
|
465 | - return false; |
|
466 | - } |
|
467 | - |
|
468 | - } |
|
469 | - |
|
470 | - /** |
|
471 | - * Installs the app within the bundle and marks the bundle as installed |
|
472 | - * |
|
473 | - * @param Bundle $bundle |
|
474 | - * @throws \Exception If app could not get installed |
|
475 | - */ |
|
476 | - public function installAppBundle(Bundle $bundle) { |
|
477 | - $appIds = $bundle->getAppIdentifiers(); |
|
478 | - foreach($appIds as $appId) { |
|
479 | - if(!$this->isDownloaded($appId)) { |
|
480 | - $this->downloadApp($appId); |
|
481 | - } |
|
482 | - $this->installApp($appId); |
|
483 | - $app = new OC_App(); |
|
484 | - $app->enable($appId); |
|
485 | - } |
|
486 | - $bundles = json_decode($this->config->getAppValue('core', 'installed.bundles', json_encode([])), true); |
|
487 | - $bundles[] = $bundle->getIdentifier(); |
|
488 | - $this->config->setAppValue('core', 'installed.bundles', json_encode($bundles)); |
|
489 | - } |
|
490 | - |
|
491 | - /** |
|
492 | - * Installs shipped apps |
|
493 | - * |
|
494 | - * This function installs all apps found in the 'apps' directory that should be enabled by default; |
|
495 | - * @param bool $softErrors When updating we ignore errors and simply log them, better to have a |
|
496 | - * working ownCloud at the end instead of an aborted update. |
|
497 | - * @return array Array of error messages (appid => Exception) |
|
498 | - */ |
|
499 | - public static function installShippedApps($softErrors = false) { |
|
500 | - $appManager = \OC::$server->getAppManager(); |
|
501 | - $config = \OC::$server->getConfig(); |
|
502 | - $errors = []; |
|
503 | - foreach(\OC::$APPSROOTS as $app_dir) { |
|
504 | - if($dir = opendir( $app_dir['path'] )) { |
|
505 | - while( false !== ( $filename = readdir( $dir ))) { |
|
506 | - if( $filename[0] !== '.' and is_dir($app_dir['path']."/$filename") ) { |
|
507 | - if( file_exists( $app_dir['path']."/$filename/appinfo/info.xml" )) { |
|
508 | - if($config->getAppValue($filename, "installed_version", null) === null) { |
|
509 | - $info=OC_App::getAppInfo($filename); |
|
510 | - $enabled = isset($info['default_enable']); |
|
511 | - if (($enabled || in_array($filename, $appManager->getAlwaysEnabledApps())) |
|
512 | - && $config->getAppValue($filename, 'enabled') !== 'no') { |
|
513 | - if ($softErrors) { |
|
514 | - try { |
|
515 | - Installer::installShippedApp($filename); |
|
516 | - } catch (HintException $e) { |
|
517 | - if ($e->getPrevious() instanceof TableExistsException) { |
|
518 | - $errors[$filename] = $e; |
|
519 | - continue; |
|
520 | - } |
|
521 | - throw $e; |
|
522 | - } |
|
523 | - } else { |
|
524 | - Installer::installShippedApp($filename); |
|
525 | - } |
|
526 | - $config->setAppValue($filename, 'enabled', 'yes'); |
|
527 | - } |
|
528 | - } |
|
529 | - } |
|
530 | - } |
|
531 | - } |
|
532 | - closedir( $dir ); |
|
533 | - } |
|
534 | - } |
|
535 | - |
|
536 | - return $errors; |
|
537 | - } |
|
538 | - |
|
539 | - /** |
|
540 | - * install an app already placed in the app folder |
|
541 | - * @param string $app id of the app to install |
|
542 | - * @return integer |
|
543 | - */ |
|
544 | - public static function installShippedApp($app) { |
|
545 | - //install the database |
|
546 | - $appPath = OC_App::getAppPath($app); |
|
547 | - \OC_App::registerAutoloading($app, $appPath); |
|
548 | - |
|
549 | - if(is_file("$appPath/appinfo/database.xml")) { |
|
550 | - try { |
|
551 | - OC_DB::createDbFromStructure("$appPath/appinfo/database.xml"); |
|
552 | - } catch (TableExistsException $e) { |
|
553 | - throw new HintException( |
|
554 | - 'Failed to enable app ' . $app, |
|
555 | - 'Please ask for help via one of our <a href="https://nextcloud.com/support/" target="_blank" rel="noreferrer noopener">support channels</a>.', |
|
556 | - 0, $e |
|
557 | - ); |
|
558 | - } |
|
559 | - } else { |
|
560 | - $ms = new \OC\DB\MigrationService($app, \OC::$server->getDatabaseConnection()); |
|
561 | - $ms->migrate(); |
|
562 | - } |
|
563 | - |
|
564 | - //run appinfo/install.php |
|
565 | - self::includeAppScript("$appPath/appinfo/install.php"); |
|
566 | - |
|
567 | - $info = OC_App::getAppInfo($app); |
|
568 | - if (is_null($info)) { |
|
569 | - return false; |
|
570 | - } |
|
571 | - \OC_App::setupBackgroundJobs($info['background-jobs']); |
|
572 | - |
|
573 | - OC_App::executeRepairSteps($app, $info['repair-steps']['install']); |
|
574 | - |
|
575 | - $config = \OC::$server->getConfig(); |
|
576 | - |
|
577 | - $config->setAppValue($app, 'installed_version', OC_App::getAppVersion($app)); |
|
578 | - if (array_key_exists('ocsid', $info)) { |
|
579 | - $config->setAppValue($app, 'ocsid', $info['ocsid']); |
|
580 | - } |
|
581 | - |
|
582 | - //set remote/public handlers |
|
583 | - foreach($info['remote'] as $name=>$path) { |
|
584 | - $config->setAppValue('core', 'remote_'.$name, $app.'/'.$path); |
|
585 | - } |
|
586 | - foreach($info['public'] as $name=>$path) { |
|
587 | - $config->setAppValue('core', 'public_'.$name, $app.'/'.$path); |
|
588 | - } |
|
589 | - |
|
590 | - OC_App::setAppTypes($info['id']); |
|
591 | - |
|
592 | - return $info['id']; |
|
593 | - } |
|
594 | - |
|
595 | - /** |
|
596 | - * @param string $script |
|
597 | - */ |
|
598 | - private static function includeAppScript($script) { |
|
599 | - if ( file_exists($script) ){ |
|
600 | - include $script; |
|
601 | - } |
|
602 | - } |
|
55 | + /** @var AppFetcher */ |
|
56 | + private $appFetcher; |
|
57 | + /** @var IClientService */ |
|
58 | + private $clientService; |
|
59 | + /** @var ITempManager */ |
|
60 | + private $tempManager; |
|
61 | + /** @var ILogger */ |
|
62 | + private $logger; |
|
63 | + /** @var IConfig */ |
|
64 | + private $config; |
|
65 | + /** @var array - for caching the result of app fetcher */ |
|
66 | + private $apps = null; |
|
67 | + /** @var bool|null - for caching the result of the ready status */ |
|
68 | + private $isInstanceReadyForUpdates = null; |
|
69 | + |
|
70 | + /** |
|
71 | + * @param AppFetcher $appFetcher |
|
72 | + * @param IClientService $clientService |
|
73 | + * @param ITempManager $tempManager |
|
74 | + * @param ILogger $logger |
|
75 | + * @param IConfig $config |
|
76 | + */ |
|
77 | + public function __construct(AppFetcher $appFetcher, |
|
78 | + IClientService $clientService, |
|
79 | + ITempManager $tempManager, |
|
80 | + ILogger $logger, |
|
81 | + IConfig $config) { |
|
82 | + $this->appFetcher = $appFetcher; |
|
83 | + $this->clientService = $clientService; |
|
84 | + $this->tempManager = $tempManager; |
|
85 | + $this->logger = $logger; |
|
86 | + $this->config = $config; |
|
87 | + } |
|
88 | + |
|
89 | + /** |
|
90 | + * Installs an app that is located in one of the app folders already |
|
91 | + * |
|
92 | + * @param string $appId App to install |
|
93 | + * @throws \Exception |
|
94 | + * @return string app ID |
|
95 | + */ |
|
96 | + public function installApp($appId) { |
|
97 | + $app = \OC_App::findAppInDirectories($appId); |
|
98 | + if($app === false) { |
|
99 | + throw new \Exception('App not found in any app directory'); |
|
100 | + } |
|
101 | + |
|
102 | + $basedir = $app['path'].'/'.$appId; |
|
103 | + $info = OC_App::getAppInfo($basedir.'/appinfo/info.xml', true); |
|
104 | + |
|
105 | + $l = \OC::$server->getL10N('core'); |
|
106 | + |
|
107 | + if(!is_array($info)) { |
|
108 | + throw new \Exception( |
|
109 | + $l->t('App "%s" cannot be installed because appinfo file cannot be read.', |
|
110 | + [$appId] |
|
111 | + ) |
|
112 | + ); |
|
113 | + } |
|
114 | + |
|
115 | + $version = implode('.', \OCP\Util::getVersion()); |
|
116 | + if (!\OC_App::isAppCompatible($version, $info)) { |
|
117 | + throw new \Exception( |
|
118 | + // TODO $l |
|
119 | + $l->t('App "%s" cannot be installed because it is not compatible with this version of the server.', |
|
120 | + [$info['name']] |
|
121 | + ) |
|
122 | + ); |
|
123 | + } |
|
124 | + |
|
125 | + // check for required dependencies |
|
126 | + \OC_App::checkAppDependencies($this->config, $l, $info); |
|
127 | + \OC_App::registerAutoloading($appId, $basedir); |
|
128 | + |
|
129 | + //install the database |
|
130 | + if(is_file($basedir.'/appinfo/database.xml')) { |
|
131 | + if (\OC::$server->getConfig()->getAppValue($info['id'], 'installed_version') === null) { |
|
132 | + OC_DB::createDbFromStructure($basedir.'/appinfo/database.xml'); |
|
133 | + } else { |
|
134 | + OC_DB::updateDbFromStructure($basedir.'/appinfo/database.xml'); |
|
135 | + } |
|
136 | + } else { |
|
137 | + $ms = new \OC\DB\MigrationService($info['id'], \OC::$server->getDatabaseConnection()); |
|
138 | + $ms->migrate(); |
|
139 | + } |
|
140 | + |
|
141 | + \OC_App::setupBackgroundJobs($info['background-jobs']); |
|
142 | + |
|
143 | + //run appinfo/install.php |
|
144 | + self::includeAppScript($basedir . '/appinfo/install.php'); |
|
145 | + |
|
146 | + $appData = OC_App::getAppInfo($appId); |
|
147 | + OC_App::executeRepairSteps($appId, $appData['repair-steps']['install']); |
|
148 | + |
|
149 | + //set the installed version |
|
150 | + \OC::$server->getConfig()->setAppValue($info['id'], 'installed_version', OC_App::getAppVersion($info['id'], false)); |
|
151 | + \OC::$server->getConfig()->setAppValue($info['id'], 'enabled', 'no'); |
|
152 | + |
|
153 | + //set remote/public handlers |
|
154 | + foreach($info['remote'] as $name=>$path) { |
|
155 | + \OC::$server->getConfig()->setAppValue('core', 'remote_'.$name, $info['id'].'/'.$path); |
|
156 | + } |
|
157 | + foreach($info['public'] as $name=>$path) { |
|
158 | + \OC::$server->getConfig()->setAppValue('core', 'public_'.$name, $info['id'].'/'.$path); |
|
159 | + } |
|
160 | + |
|
161 | + OC_App::setAppTypes($info['id']); |
|
162 | + |
|
163 | + return $info['id']; |
|
164 | + } |
|
165 | + |
|
166 | + /** |
|
167 | + * Updates the specified app from the appstore |
|
168 | + * |
|
169 | + * @param string $appId |
|
170 | + * @return bool |
|
171 | + */ |
|
172 | + public function updateAppstoreApp($appId) { |
|
173 | + if($this->isUpdateAvailable($appId)) { |
|
174 | + try { |
|
175 | + $this->downloadApp($appId); |
|
176 | + } catch (\Exception $e) { |
|
177 | + $this->logger->logException($e, [ |
|
178 | + 'level' => ILogger::ERROR, |
|
179 | + 'app' => 'core', |
|
180 | + ]); |
|
181 | + return false; |
|
182 | + } |
|
183 | + return OC_App::updateApp($appId); |
|
184 | + } |
|
185 | + |
|
186 | + return false; |
|
187 | + } |
|
188 | + |
|
189 | + /** |
|
190 | + * Downloads an app and puts it into the app directory |
|
191 | + * |
|
192 | + * @param string $appId |
|
193 | + * |
|
194 | + * @throws \Exception If the installation was not successful |
|
195 | + */ |
|
196 | + public function downloadApp($appId) { |
|
197 | + $appId = strtolower($appId); |
|
198 | + |
|
199 | + $apps = $this->appFetcher->get(); |
|
200 | + foreach($apps as $app) { |
|
201 | + if($app['id'] === $appId) { |
|
202 | + // Load the certificate |
|
203 | + $certificate = new X509(); |
|
204 | + $certificate->loadCA(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crt')); |
|
205 | + $loadedCertificate = $certificate->loadX509($app['certificate']); |
|
206 | + |
|
207 | + // Verify if the certificate has been revoked |
|
208 | + $crl = new X509(); |
|
209 | + $crl->loadCA(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crt')); |
|
210 | + $crl->loadCRL(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crl')); |
|
211 | + if($crl->validateSignature() !== true) { |
|
212 | + throw new \Exception('Could not validate CRL signature'); |
|
213 | + } |
|
214 | + $csn = $loadedCertificate['tbsCertificate']['serialNumber']->toString(); |
|
215 | + $revoked = $crl->getRevoked($csn); |
|
216 | + if ($revoked !== false) { |
|
217 | + throw new \Exception( |
|
218 | + sprintf( |
|
219 | + 'Certificate "%s" has been revoked', |
|
220 | + $csn |
|
221 | + ) |
|
222 | + ); |
|
223 | + } |
|
224 | + |
|
225 | + // Verify if the certificate has been issued by the Nextcloud Code Authority CA |
|
226 | + if($certificate->validateSignature() !== true) { |
|
227 | + throw new \Exception( |
|
228 | + sprintf( |
|
229 | + 'App with id %s has a certificate not issued by a trusted Code Signing Authority', |
|
230 | + $appId |
|
231 | + ) |
|
232 | + ); |
|
233 | + } |
|
234 | + |
|
235 | + // Verify if the certificate is issued for the requested app id |
|
236 | + $certInfo = openssl_x509_parse($app['certificate']); |
|
237 | + if(!isset($certInfo['subject']['CN'])) { |
|
238 | + throw new \Exception( |
|
239 | + sprintf( |
|
240 | + 'App with id %s has a cert with no CN', |
|
241 | + $appId |
|
242 | + ) |
|
243 | + ); |
|
244 | + } |
|
245 | + if($certInfo['subject']['CN'] !== $appId) { |
|
246 | + throw new \Exception( |
|
247 | + sprintf( |
|
248 | + 'App with id %s has a cert issued to %s', |
|
249 | + $appId, |
|
250 | + $certInfo['subject']['CN'] |
|
251 | + ) |
|
252 | + ); |
|
253 | + } |
|
254 | + |
|
255 | + // Download the release |
|
256 | + $tempFile = $this->tempManager->getTemporaryFile('.tar.gz'); |
|
257 | + $client = $this->clientService->newClient(); |
|
258 | + $client->get($app['releases'][0]['download'], ['save_to' => $tempFile]); |
|
259 | + |
|
260 | + // Check if the signature actually matches the downloaded content |
|
261 | + $certificate = openssl_get_publickey($app['certificate']); |
|
262 | + $verified = (bool)openssl_verify(file_get_contents($tempFile), base64_decode($app['releases'][0]['signature']), $certificate, OPENSSL_ALGO_SHA512); |
|
263 | + openssl_free_key($certificate); |
|
264 | + |
|
265 | + if($verified === true) { |
|
266 | + // Seems to match, let's proceed |
|
267 | + $extractDir = $this->tempManager->getTemporaryFolder(); |
|
268 | + $archive = new TAR($tempFile); |
|
269 | + |
|
270 | + if($archive) { |
|
271 | + if (!$archive->extract($extractDir)) { |
|
272 | + throw new \Exception( |
|
273 | + sprintf( |
|
274 | + 'Could not extract app %s', |
|
275 | + $appId |
|
276 | + ) |
|
277 | + ); |
|
278 | + } |
|
279 | + $allFiles = scandir($extractDir); |
|
280 | + $folders = array_diff($allFiles, ['.', '..']); |
|
281 | + $folders = array_values($folders); |
|
282 | + |
|
283 | + if(count($folders) > 1) { |
|
284 | + throw new \Exception( |
|
285 | + sprintf( |
|
286 | + 'Extracted app %s has more than 1 folder', |
|
287 | + $appId |
|
288 | + ) |
|
289 | + ); |
|
290 | + } |
|
291 | + |
|
292 | + // Check if appinfo/info.xml has the same app ID as well |
|
293 | + $loadEntities = libxml_disable_entity_loader(false); |
|
294 | + $xml = simplexml_load_file($extractDir . '/' . $folders[0] . '/appinfo/info.xml'); |
|
295 | + libxml_disable_entity_loader($loadEntities); |
|
296 | + if((string)$xml->id !== $appId) { |
|
297 | + throw new \Exception( |
|
298 | + sprintf( |
|
299 | + 'App for id %s has a wrong app ID in info.xml: %s', |
|
300 | + $appId, |
|
301 | + (string)$xml->id |
|
302 | + ) |
|
303 | + ); |
|
304 | + } |
|
305 | + |
|
306 | + // Check if the version is lower than before |
|
307 | + $currentVersion = OC_App::getAppVersion($appId); |
|
308 | + $newVersion = (string)$xml->version; |
|
309 | + if(version_compare($currentVersion, $newVersion) === 1) { |
|
310 | + throw new \Exception( |
|
311 | + sprintf( |
|
312 | + 'App for id %s has version %s and tried to update to lower version %s', |
|
313 | + $appId, |
|
314 | + $currentVersion, |
|
315 | + $newVersion |
|
316 | + ) |
|
317 | + ); |
|
318 | + } |
|
319 | + |
|
320 | + $baseDir = OC_App::getInstallPath() . '/' . $appId; |
|
321 | + // Remove old app with the ID if existent |
|
322 | + OC_Helper::rmdirr($baseDir); |
|
323 | + // Move to app folder |
|
324 | + if(@mkdir($baseDir)) { |
|
325 | + $extractDir .= '/' . $folders[0]; |
|
326 | + OC_Helper::copyr($extractDir, $baseDir); |
|
327 | + } |
|
328 | + OC_Helper::copyr($extractDir, $baseDir); |
|
329 | + OC_Helper::rmdirr($extractDir); |
|
330 | + return; |
|
331 | + } else { |
|
332 | + throw new \Exception( |
|
333 | + sprintf( |
|
334 | + 'Could not extract app with ID %s to %s', |
|
335 | + $appId, |
|
336 | + $extractDir |
|
337 | + ) |
|
338 | + ); |
|
339 | + } |
|
340 | + } else { |
|
341 | + // Signature does not match |
|
342 | + throw new \Exception( |
|
343 | + sprintf( |
|
344 | + 'App with id %s has invalid signature', |
|
345 | + $appId |
|
346 | + ) |
|
347 | + ); |
|
348 | + } |
|
349 | + } |
|
350 | + } |
|
351 | + |
|
352 | + throw new \Exception( |
|
353 | + sprintf( |
|
354 | + 'Could not download app %s', |
|
355 | + $appId |
|
356 | + ) |
|
357 | + ); |
|
358 | + } |
|
359 | + |
|
360 | + /** |
|
361 | + * Check if an update for the app is available |
|
362 | + * |
|
363 | + * @param string $appId |
|
364 | + * @return string|false false or the version number of the update |
|
365 | + */ |
|
366 | + public function isUpdateAvailable($appId) { |
|
367 | + if ($this->isInstanceReadyForUpdates === null) { |
|
368 | + $installPath = OC_App::getInstallPath(); |
|
369 | + if ($installPath === false || $installPath === null) { |
|
370 | + $this->isInstanceReadyForUpdates = false; |
|
371 | + } else { |
|
372 | + $this->isInstanceReadyForUpdates = true; |
|
373 | + } |
|
374 | + } |
|
375 | + |
|
376 | + if ($this->isInstanceReadyForUpdates === false) { |
|
377 | + return false; |
|
378 | + } |
|
379 | + |
|
380 | + if ($this->isInstalledFromGit($appId) === true) { |
|
381 | + return false; |
|
382 | + } |
|
383 | + |
|
384 | + if ($this->apps === null) { |
|
385 | + $this->apps = $this->appFetcher->get(); |
|
386 | + } |
|
387 | + |
|
388 | + foreach($this->apps as $app) { |
|
389 | + if($app['id'] === $appId) { |
|
390 | + $currentVersion = OC_App::getAppVersion($appId); |
|
391 | + $newestVersion = $app['releases'][0]['version']; |
|
392 | + if (version_compare($newestVersion, $currentVersion, '>')) { |
|
393 | + return $newestVersion; |
|
394 | + } else { |
|
395 | + return false; |
|
396 | + } |
|
397 | + } |
|
398 | + } |
|
399 | + |
|
400 | + return false; |
|
401 | + } |
|
402 | + |
|
403 | + /** |
|
404 | + * Check if app has been installed from git |
|
405 | + * @param string $name name of the application to remove |
|
406 | + * @return boolean |
|
407 | + * |
|
408 | + * The function will check if the path contains a .git folder |
|
409 | + */ |
|
410 | + private function isInstalledFromGit($appId) { |
|
411 | + $app = \OC_App::findAppInDirectories($appId); |
|
412 | + if($app === false) { |
|
413 | + return false; |
|
414 | + } |
|
415 | + $basedir = $app['path'].'/'.$appId; |
|
416 | + return file_exists($basedir.'/.git/'); |
|
417 | + } |
|
418 | + |
|
419 | + /** |
|
420 | + * Check if app is already downloaded |
|
421 | + * @param string $name name of the application to remove |
|
422 | + * @return boolean |
|
423 | + * |
|
424 | + * The function will check if the app is already downloaded in the apps repository |
|
425 | + */ |
|
426 | + public function isDownloaded($name) { |
|
427 | + foreach(\OC::$APPSROOTS as $dir) { |
|
428 | + $dirToTest = $dir['path']; |
|
429 | + $dirToTest .= '/'; |
|
430 | + $dirToTest .= $name; |
|
431 | + $dirToTest .= '/'; |
|
432 | + |
|
433 | + if (is_dir($dirToTest)) { |
|
434 | + return true; |
|
435 | + } |
|
436 | + } |
|
437 | + |
|
438 | + return false; |
|
439 | + } |
|
440 | + |
|
441 | + /** |
|
442 | + * Removes an app |
|
443 | + * @param string $appId ID of the application to remove |
|
444 | + * @return boolean |
|
445 | + * |
|
446 | + * |
|
447 | + * This function works as follows |
|
448 | + * -# call uninstall repair steps |
|
449 | + * -# removing the files |
|
450 | + * |
|
451 | + * The function will not delete preferences, tables and the configuration, |
|
452 | + * this has to be done by the function oc_app_uninstall(). |
|
453 | + */ |
|
454 | + public function removeApp($appId) { |
|
455 | + if($this->isDownloaded( $appId )) { |
|
456 | + if (\OC::$server->getAppManager()->isShipped($appId)) { |
|
457 | + return false; |
|
458 | + } |
|
459 | + $appDir = OC_App::getInstallPath() . '/' . $appId; |
|
460 | + OC_Helper::rmdirr($appDir); |
|
461 | + return true; |
|
462 | + }else{ |
|
463 | + \OCP\Util::writeLog('core', 'can\'t remove app '.$appId.'. It is not installed.', ILogger::ERROR); |
|
464 | + |
|
465 | + return false; |
|
466 | + } |
|
467 | + |
|
468 | + } |
|
469 | + |
|
470 | + /** |
|
471 | + * Installs the app within the bundle and marks the bundle as installed |
|
472 | + * |
|
473 | + * @param Bundle $bundle |
|
474 | + * @throws \Exception If app could not get installed |
|
475 | + */ |
|
476 | + public function installAppBundle(Bundle $bundle) { |
|
477 | + $appIds = $bundle->getAppIdentifiers(); |
|
478 | + foreach($appIds as $appId) { |
|
479 | + if(!$this->isDownloaded($appId)) { |
|
480 | + $this->downloadApp($appId); |
|
481 | + } |
|
482 | + $this->installApp($appId); |
|
483 | + $app = new OC_App(); |
|
484 | + $app->enable($appId); |
|
485 | + } |
|
486 | + $bundles = json_decode($this->config->getAppValue('core', 'installed.bundles', json_encode([])), true); |
|
487 | + $bundles[] = $bundle->getIdentifier(); |
|
488 | + $this->config->setAppValue('core', 'installed.bundles', json_encode($bundles)); |
|
489 | + } |
|
490 | + |
|
491 | + /** |
|
492 | + * Installs shipped apps |
|
493 | + * |
|
494 | + * This function installs all apps found in the 'apps' directory that should be enabled by default; |
|
495 | + * @param bool $softErrors When updating we ignore errors and simply log them, better to have a |
|
496 | + * working ownCloud at the end instead of an aborted update. |
|
497 | + * @return array Array of error messages (appid => Exception) |
|
498 | + */ |
|
499 | + public static function installShippedApps($softErrors = false) { |
|
500 | + $appManager = \OC::$server->getAppManager(); |
|
501 | + $config = \OC::$server->getConfig(); |
|
502 | + $errors = []; |
|
503 | + foreach(\OC::$APPSROOTS as $app_dir) { |
|
504 | + if($dir = opendir( $app_dir['path'] )) { |
|
505 | + while( false !== ( $filename = readdir( $dir ))) { |
|
506 | + if( $filename[0] !== '.' and is_dir($app_dir['path']."/$filename") ) { |
|
507 | + if( file_exists( $app_dir['path']."/$filename/appinfo/info.xml" )) { |
|
508 | + if($config->getAppValue($filename, "installed_version", null) === null) { |
|
509 | + $info=OC_App::getAppInfo($filename); |
|
510 | + $enabled = isset($info['default_enable']); |
|
511 | + if (($enabled || in_array($filename, $appManager->getAlwaysEnabledApps())) |
|
512 | + && $config->getAppValue($filename, 'enabled') !== 'no') { |
|
513 | + if ($softErrors) { |
|
514 | + try { |
|
515 | + Installer::installShippedApp($filename); |
|
516 | + } catch (HintException $e) { |
|
517 | + if ($e->getPrevious() instanceof TableExistsException) { |
|
518 | + $errors[$filename] = $e; |
|
519 | + continue; |
|
520 | + } |
|
521 | + throw $e; |
|
522 | + } |
|
523 | + } else { |
|
524 | + Installer::installShippedApp($filename); |
|
525 | + } |
|
526 | + $config->setAppValue($filename, 'enabled', 'yes'); |
|
527 | + } |
|
528 | + } |
|
529 | + } |
|
530 | + } |
|
531 | + } |
|
532 | + closedir( $dir ); |
|
533 | + } |
|
534 | + } |
|
535 | + |
|
536 | + return $errors; |
|
537 | + } |
|
538 | + |
|
539 | + /** |
|
540 | + * install an app already placed in the app folder |
|
541 | + * @param string $app id of the app to install |
|
542 | + * @return integer |
|
543 | + */ |
|
544 | + public static function installShippedApp($app) { |
|
545 | + //install the database |
|
546 | + $appPath = OC_App::getAppPath($app); |
|
547 | + \OC_App::registerAutoloading($app, $appPath); |
|
548 | + |
|
549 | + if(is_file("$appPath/appinfo/database.xml")) { |
|
550 | + try { |
|
551 | + OC_DB::createDbFromStructure("$appPath/appinfo/database.xml"); |
|
552 | + } catch (TableExistsException $e) { |
|
553 | + throw new HintException( |
|
554 | + 'Failed to enable app ' . $app, |
|
555 | + 'Please ask for help via one of our <a href="https://nextcloud.com/support/" target="_blank" rel="noreferrer noopener">support channels</a>.', |
|
556 | + 0, $e |
|
557 | + ); |
|
558 | + } |
|
559 | + } else { |
|
560 | + $ms = new \OC\DB\MigrationService($app, \OC::$server->getDatabaseConnection()); |
|
561 | + $ms->migrate(); |
|
562 | + } |
|
563 | + |
|
564 | + //run appinfo/install.php |
|
565 | + self::includeAppScript("$appPath/appinfo/install.php"); |
|
566 | + |
|
567 | + $info = OC_App::getAppInfo($app); |
|
568 | + if (is_null($info)) { |
|
569 | + return false; |
|
570 | + } |
|
571 | + \OC_App::setupBackgroundJobs($info['background-jobs']); |
|
572 | + |
|
573 | + OC_App::executeRepairSteps($app, $info['repair-steps']['install']); |
|
574 | + |
|
575 | + $config = \OC::$server->getConfig(); |
|
576 | + |
|
577 | + $config->setAppValue($app, 'installed_version', OC_App::getAppVersion($app)); |
|
578 | + if (array_key_exists('ocsid', $info)) { |
|
579 | + $config->setAppValue($app, 'ocsid', $info['ocsid']); |
|
580 | + } |
|
581 | + |
|
582 | + //set remote/public handlers |
|
583 | + foreach($info['remote'] as $name=>$path) { |
|
584 | + $config->setAppValue('core', 'remote_'.$name, $app.'/'.$path); |
|
585 | + } |
|
586 | + foreach($info['public'] as $name=>$path) { |
|
587 | + $config->setAppValue('core', 'public_'.$name, $app.'/'.$path); |
|
588 | + } |
|
589 | + |
|
590 | + OC_App::setAppTypes($info['id']); |
|
591 | + |
|
592 | + return $info['id']; |
|
593 | + } |
|
594 | + |
|
595 | + /** |
|
596 | + * @param string $script |
|
597 | + */ |
|
598 | + private static function includeAppScript($script) { |
|
599 | + if ( file_exists($script) ){ |
|
600 | + include $script; |
|
601 | + } |
|
602 | + } |
|
603 | 603 | } |
@@ -95,7 +95,7 @@ discard block |
||
95 | 95 | */ |
96 | 96 | public function installApp($appId) { |
97 | 97 | $app = \OC_App::findAppInDirectories($appId); |
98 | - if($app === false) { |
|
98 | + if ($app === false) { |
|
99 | 99 | throw new \Exception('App not found in any app directory'); |
100 | 100 | } |
101 | 101 | |
@@ -104,7 +104,7 @@ discard block |
||
104 | 104 | |
105 | 105 | $l = \OC::$server->getL10N('core'); |
106 | 106 | |
107 | - if(!is_array($info)) { |
|
107 | + if (!is_array($info)) { |
|
108 | 108 | throw new \Exception( |
109 | 109 | $l->t('App "%s" cannot be installed because appinfo file cannot be read.', |
110 | 110 | [$appId] |
@@ -127,7 +127,7 @@ discard block |
||
127 | 127 | \OC_App::registerAutoloading($appId, $basedir); |
128 | 128 | |
129 | 129 | //install the database |
130 | - if(is_file($basedir.'/appinfo/database.xml')) { |
|
130 | + if (is_file($basedir.'/appinfo/database.xml')) { |
|
131 | 131 | if (\OC::$server->getConfig()->getAppValue($info['id'], 'installed_version') === null) { |
132 | 132 | OC_DB::createDbFromStructure($basedir.'/appinfo/database.xml'); |
133 | 133 | } else { |
@@ -141,7 +141,7 @@ discard block |
||
141 | 141 | \OC_App::setupBackgroundJobs($info['background-jobs']); |
142 | 142 | |
143 | 143 | //run appinfo/install.php |
144 | - self::includeAppScript($basedir . '/appinfo/install.php'); |
|
144 | + self::includeAppScript($basedir.'/appinfo/install.php'); |
|
145 | 145 | |
146 | 146 | $appData = OC_App::getAppInfo($appId); |
147 | 147 | OC_App::executeRepairSteps($appId, $appData['repair-steps']['install']); |
@@ -151,10 +151,10 @@ discard block |
||
151 | 151 | \OC::$server->getConfig()->setAppValue($info['id'], 'enabled', 'no'); |
152 | 152 | |
153 | 153 | //set remote/public handlers |
154 | - foreach($info['remote'] as $name=>$path) { |
|
154 | + foreach ($info['remote'] as $name=>$path) { |
|
155 | 155 | \OC::$server->getConfig()->setAppValue('core', 'remote_'.$name, $info['id'].'/'.$path); |
156 | 156 | } |
157 | - foreach($info['public'] as $name=>$path) { |
|
157 | + foreach ($info['public'] as $name=>$path) { |
|
158 | 158 | \OC::$server->getConfig()->setAppValue('core', 'public_'.$name, $info['id'].'/'.$path); |
159 | 159 | } |
160 | 160 | |
@@ -170,7 +170,7 @@ discard block |
||
170 | 170 | * @return bool |
171 | 171 | */ |
172 | 172 | public function updateAppstoreApp($appId) { |
173 | - if($this->isUpdateAvailable($appId)) { |
|
173 | + if ($this->isUpdateAvailable($appId)) { |
|
174 | 174 | try { |
175 | 175 | $this->downloadApp($appId); |
176 | 176 | } catch (\Exception $e) { |
@@ -197,18 +197,18 @@ discard block |
||
197 | 197 | $appId = strtolower($appId); |
198 | 198 | |
199 | 199 | $apps = $this->appFetcher->get(); |
200 | - foreach($apps as $app) { |
|
201 | - if($app['id'] === $appId) { |
|
200 | + foreach ($apps as $app) { |
|
201 | + if ($app['id'] === $appId) { |
|
202 | 202 | // Load the certificate |
203 | 203 | $certificate = new X509(); |
204 | - $certificate->loadCA(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crt')); |
|
204 | + $certificate->loadCA(file_get_contents(__DIR__.'/../../resources/codesigning/root.crt')); |
|
205 | 205 | $loadedCertificate = $certificate->loadX509($app['certificate']); |
206 | 206 | |
207 | 207 | // Verify if the certificate has been revoked |
208 | 208 | $crl = new X509(); |
209 | - $crl->loadCA(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crt')); |
|
210 | - $crl->loadCRL(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crl')); |
|
211 | - if($crl->validateSignature() !== true) { |
|
209 | + $crl->loadCA(file_get_contents(__DIR__.'/../../resources/codesigning/root.crt')); |
|
210 | + $crl->loadCRL(file_get_contents(__DIR__.'/../../resources/codesigning/root.crl')); |
|
211 | + if ($crl->validateSignature() !== true) { |
|
212 | 212 | throw new \Exception('Could not validate CRL signature'); |
213 | 213 | } |
214 | 214 | $csn = $loadedCertificate['tbsCertificate']['serialNumber']->toString(); |
@@ -223,7 +223,7 @@ discard block |
||
223 | 223 | } |
224 | 224 | |
225 | 225 | // Verify if the certificate has been issued by the Nextcloud Code Authority CA |
226 | - if($certificate->validateSignature() !== true) { |
|
226 | + if ($certificate->validateSignature() !== true) { |
|
227 | 227 | throw new \Exception( |
228 | 228 | sprintf( |
229 | 229 | 'App with id %s has a certificate not issued by a trusted Code Signing Authority', |
@@ -234,7 +234,7 @@ discard block |
||
234 | 234 | |
235 | 235 | // Verify if the certificate is issued for the requested app id |
236 | 236 | $certInfo = openssl_x509_parse($app['certificate']); |
237 | - if(!isset($certInfo['subject']['CN'])) { |
|
237 | + if (!isset($certInfo['subject']['CN'])) { |
|
238 | 238 | throw new \Exception( |
239 | 239 | sprintf( |
240 | 240 | 'App with id %s has a cert with no CN', |
@@ -242,7 +242,7 @@ discard block |
||
242 | 242 | ) |
243 | 243 | ); |
244 | 244 | } |
245 | - if($certInfo['subject']['CN'] !== $appId) { |
|
245 | + if ($certInfo['subject']['CN'] !== $appId) { |
|
246 | 246 | throw new \Exception( |
247 | 247 | sprintf( |
248 | 248 | 'App with id %s has a cert issued to %s', |
@@ -259,15 +259,15 @@ discard block |
||
259 | 259 | |
260 | 260 | // Check if the signature actually matches the downloaded content |
261 | 261 | $certificate = openssl_get_publickey($app['certificate']); |
262 | - $verified = (bool)openssl_verify(file_get_contents($tempFile), base64_decode($app['releases'][0]['signature']), $certificate, OPENSSL_ALGO_SHA512); |
|
262 | + $verified = (bool) openssl_verify(file_get_contents($tempFile), base64_decode($app['releases'][0]['signature']), $certificate, OPENSSL_ALGO_SHA512); |
|
263 | 263 | openssl_free_key($certificate); |
264 | 264 | |
265 | - if($verified === true) { |
|
265 | + if ($verified === true) { |
|
266 | 266 | // Seems to match, let's proceed |
267 | 267 | $extractDir = $this->tempManager->getTemporaryFolder(); |
268 | 268 | $archive = new TAR($tempFile); |
269 | 269 | |
270 | - if($archive) { |
|
270 | + if ($archive) { |
|
271 | 271 | if (!$archive->extract($extractDir)) { |
272 | 272 | throw new \Exception( |
273 | 273 | sprintf( |
@@ -280,7 +280,7 @@ discard block |
||
280 | 280 | $folders = array_diff($allFiles, ['.', '..']); |
281 | 281 | $folders = array_values($folders); |
282 | 282 | |
283 | - if(count($folders) > 1) { |
|
283 | + if (count($folders) > 1) { |
|
284 | 284 | throw new \Exception( |
285 | 285 | sprintf( |
286 | 286 | 'Extracted app %s has more than 1 folder', |
@@ -291,22 +291,22 @@ discard block |
||
291 | 291 | |
292 | 292 | // Check if appinfo/info.xml has the same app ID as well |
293 | 293 | $loadEntities = libxml_disable_entity_loader(false); |
294 | - $xml = simplexml_load_file($extractDir . '/' . $folders[0] . '/appinfo/info.xml'); |
|
294 | + $xml = simplexml_load_file($extractDir.'/'.$folders[0].'/appinfo/info.xml'); |
|
295 | 295 | libxml_disable_entity_loader($loadEntities); |
296 | - if((string)$xml->id !== $appId) { |
|
296 | + if ((string) $xml->id !== $appId) { |
|
297 | 297 | throw new \Exception( |
298 | 298 | sprintf( |
299 | 299 | 'App for id %s has a wrong app ID in info.xml: %s', |
300 | 300 | $appId, |
301 | - (string)$xml->id |
|
301 | + (string) $xml->id |
|
302 | 302 | ) |
303 | 303 | ); |
304 | 304 | } |
305 | 305 | |
306 | 306 | // Check if the version is lower than before |
307 | 307 | $currentVersion = OC_App::getAppVersion($appId); |
308 | - $newVersion = (string)$xml->version; |
|
309 | - if(version_compare($currentVersion, $newVersion) === 1) { |
|
308 | + $newVersion = (string) $xml->version; |
|
309 | + if (version_compare($currentVersion, $newVersion) === 1) { |
|
310 | 310 | throw new \Exception( |
311 | 311 | sprintf( |
312 | 312 | 'App for id %s has version %s and tried to update to lower version %s', |
@@ -317,12 +317,12 @@ discard block |
||
317 | 317 | ); |
318 | 318 | } |
319 | 319 | |
320 | - $baseDir = OC_App::getInstallPath() . '/' . $appId; |
|
320 | + $baseDir = OC_App::getInstallPath().'/'.$appId; |
|
321 | 321 | // Remove old app with the ID if existent |
322 | 322 | OC_Helper::rmdirr($baseDir); |
323 | 323 | // Move to app folder |
324 | - if(@mkdir($baseDir)) { |
|
325 | - $extractDir .= '/' . $folders[0]; |
|
324 | + if (@mkdir($baseDir)) { |
|
325 | + $extractDir .= '/'.$folders[0]; |
|
326 | 326 | OC_Helper::copyr($extractDir, $baseDir); |
327 | 327 | } |
328 | 328 | OC_Helper::copyr($extractDir, $baseDir); |
@@ -385,8 +385,8 @@ discard block |
||
385 | 385 | $this->apps = $this->appFetcher->get(); |
386 | 386 | } |
387 | 387 | |
388 | - foreach($this->apps as $app) { |
|
389 | - if($app['id'] === $appId) { |
|
388 | + foreach ($this->apps as $app) { |
|
389 | + if ($app['id'] === $appId) { |
|
390 | 390 | $currentVersion = OC_App::getAppVersion($appId); |
391 | 391 | $newestVersion = $app['releases'][0]['version']; |
392 | 392 | if (version_compare($newestVersion, $currentVersion, '>')) { |
@@ -409,7 +409,7 @@ discard block |
||
409 | 409 | */ |
410 | 410 | private function isInstalledFromGit($appId) { |
411 | 411 | $app = \OC_App::findAppInDirectories($appId); |
412 | - if($app === false) { |
|
412 | + if ($app === false) { |
|
413 | 413 | return false; |
414 | 414 | } |
415 | 415 | $basedir = $app['path'].'/'.$appId; |
@@ -424,7 +424,7 @@ discard block |
||
424 | 424 | * The function will check if the app is already downloaded in the apps repository |
425 | 425 | */ |
426 | 426 | public function isDownloaded($name) { |
427 | - foreach(\OC::$APPSROOTS as $dir) { |
|
427 | + foreach (\OC::$APPSROOTS as $dir) { |
|
428 | 428 | $dirToTest = $dir['path']; |
429 | 429 | $dirToTest .= '/'; |
430 | 430 | $dirToTest .= $name; |
@@ -452,14 +452,14 @@ discard block |
||
452 | 452 | * this has to be done by the function oc_app_uninstall(). |
453 | 453 | */ |
454 | 454 | public function removeApp($appId) { |
455 | - if($this->isDownloaded( $appId )) { |
|
455 | + if ($this->isDownloaded($appId)) { |
|
456 | 456 | if (\OC::$server->getAppManager()->isShipped($appId)) { |
457 | 457 | return false; |
458 | 458 | } |
459 | - $appDir = OC_App::getInstallPath() . '/' . $appId; |
|
459 | + $appDir = OC_App::getInstallPath().'/'.$appId; |
|
460 | 460 | OC_Helper::rmdirr($appDir); |
461 | 461 | return true; |
462 | - }else{ |
|
462 | + } else { |
|
463 | 463 | \OCP\Util::writeLog('core', 'can\'t remove app '.$appId.'. It is not installed.', ILogger::ERROR); |
464 | 464 | |
465 | 465 | return false; |
@@ -475,8 +475,8 @@ discard block |
||
475 | 475 | */ |
476 | 476 | public function installAppBundle(Bundle $bundle) { |
477 | 477 | $appIds = $bundle->getAppIdentifiers(); |
478 | - foreach($appIds as $appId) { |
|
479 | - if(!$this->isDownloaded($appId)) { |
|
478 | + foreach ($appIds as $appId) { |
|
479 | + if (!$this->isDownloaded($appId)) { |
|
480 | 480 | $this->downloadApp($appId); |
481 | 481 | } |
482 | 482 | $this->installApp($appId); |
@@ -500,13 +500,13 @@ discard block |
||
500 | 500 | $appManager = \OC::$server->getAppManager(); |
501 | 501 | $config = \OC::$server->getConfig(); |
502 | 502 | $errors = []; |
503 | - foreach(\OC::$APPSROOTS as $app_dir) { |
|
504 | - if($dir = opendir( $app_dir['path'] )) { |
|
505 | - while( false !== ( $filename = readdir( $dir ))) { |
|
506 | - if( $filename[0] !== '.' and is_dir($app_dir['path']."/$filename") ) { |
|
507 | - if( file_exists( $app_dir['path']."/$filename/appinfo/info.xml" )) { |
|
508 | - if($config->getAppValue($filename, "installed_version", null) === null) { |
|
509 | - $info=OC_App::getAppInfo($filename); |
|
503 | + foreach (\OC::$APPSROOTS as $app_dir) { |
|
504 | + if ($dir = opendir($app_dir['path'])) { |
|
505 | + while (false !== ($filename = readdir($dir))) { |
|
506 | + if ($filename[0] !== '.' and is_dir($app_dir['path']."/$filename")) { |
|
507 | + if (file_exists($app_dir['path']."/$filename/appinfo/info.xml")) { |
|
508 | + if ($config->getAppValue($filename, "installed_version", null) === null) { |
|
509 | + $info = OC_App::getAppInfo($filename); |
|
510 | 510 | $enabled = isset($info['default_enable']); |
511 | 511 | if (($enabled || in_array($filename, $appManager->getAlwaysEnabledApps())) |
512 | 512 | && $config->getAppValue($filename, 'enabled') !== 'no') { |
@@ -529,7 +529,7 @@ discard block |
||
529 | 529 | } |
530 | 530 | } |
531 | 531 | } |
532 | - closedir( $dir ); |
|
532 | + closedir($dir); |
|
533 | 533 | } |
534 | 534 | } |
535 | 535 | |
@@ -546,12 +546,12 @@ discard block |
||
546 | 546 | $appPath = OC_App::getAppPath($app); |
547 | 547 | \OC_App::registerAutoloading($app, $appPath); |
548 | 548 | |
549 | - if(is_file("$appPath/appinfo/database.xml")) { |
|
549 | + if (is_file("$appPath/appinfo/database.xml")) { |
|
550 | 550 | try { |
551 | 551 | OC_DB::createDbFromStructure("$appPath/appinfo/database.xml"); |
552 | 552 | } catch (TableExistsException $e) { |
553 | 553 | throw new HintException( |
554 | - 'Failed to enable app ' . $app, |
|
554 | + 'Failed to enable app '.$app, |
|
555 | 555 | 'Please ask for help via one of our <a href="https://nextcloud.com/support/" target="_blank" rel="noreferrer noopener">support channels</a>.', |
556 | 556 | 0, $e |
557 | 557 | ); |
@@ -580,10 +580,10 @@ discard block |
||
580 | 580 | } |
581 | 581 | |
582 | 582 | //set remote/public handlers |
583 | - foreach($info['remote'] as $name=>$path) { |
|
583 | + foreach ($info['remote'] as $name=>$path) { |
|
584 | 584 | $config->setAppValue('core', 'remote_'.$name, $app.'/'.$path); |
585 | 585 | } |
586 | - foreach($info['public'] as $name=>$path) { |
|
586 | + foreach ($info['public'] as $name=>$path) { |
|
587 | 587 | $config->setAppValue('core', 'public_'.$name, $app.'/'.$path); |
588 | 588 | } |
589 | 589 | |
@@ -596,7 +596,7 @@ discard block |
||
596 | 596 | * @param string $script |
597 | 597 | */ |
598 | 598 | private static function includeAppScript($script) { |
599 | - if ( file_exists($script) ){ |
|
599 | + if (file_exists($script)) { |
|
600 | 600 | include $script; |
601 | 601 | } |
602 | 602 | } |
@@ -459,7 +459,7 @@ |
||
459 | 459 | $appDir = OC_App::getInstallPath() . '/' . $appId; |
460 | 460 | OC_Helper::rmdirr($appDir); |
461 | 461 | return true; |
462 | - }else{ |
|
462 | + } else{ |
|
463 | 463 | \OCP\Util::writeLog('core', 'can\'t remove app '.$appId.'. It is not installed.', ILogger::ERROR); |
464 | 464 | |
465 | 465 | return false; |
@@ -57,505 +57,505 @@ |
||
57 | 57 | * @since 4.0.0 |
58 | 58 | */ |
59 | 59 | class Util { |
60 | - /** |
|
61 | - * @deprecated 14.0.0 use \OCP\ILogger::DEBUG |
|
62 | - */ |
|
63 | - const DEBUG=0; |
|
64 | - /** |
|
65 | - * @deprecated 14.0.0 use \OCP\ILogger::INFO |
|
66 | - */ |
|
67 | - const INFO=1; |
|
68 | - /** |
|
69 | - * @deprecated 14.0.0 use \OCP\ILogger::WARN |
|
70 | - */ |
|
71 | - const WARN=2; |
|
72 | - /** |
|
73 | - * @deprecated 14.0.0 use \OCP\ILogger::ERROR |
|
74 | - */ |
|
75 | - const ERROR=3; |
|
76 | - /** |
|
77 | - * @deprecated 14.0.0 use \OCP\ILogger::FATAL |
|
78 | - */ |
|
79 | - const FATAL=4; |
|
80 | - |
|
81 | - /** \OCP\Share\IManager */ |
|
82 | - private static $shareManager; |
|
83 | - |
|
84 | - /** |
|
85 | - * get the current installed version of ownCloud |
|
86 | - * @return array |
|
87 | - * @since 4.0.0 |
|
88 | - */ |
|
89 | - public static function getVersion() { |
|
90 | - return \OC_Util::getVersion(); |
|
91 | - } |
|
60 | + /** |
|
61 | + * @deprecated 14.0.0 use \OCP\ILogger::DEBUG |
|
62 | + */ |
|
63 | + const DEBUG=0; |
|
64 | + /** |
|
65 | + * @deprecated 14.0.0 use \OCP\ILogger::INFO |
|
66 | + */ |
|
67 | + const INFO=1; |
|
68 | + /** |
|
69 | + * @deprecated 14.0.0 use \OCP\ILogger::WARN |
|
70 | + */ |
|
71 | + const WARN=2; |
|
72 | + /** |
|
73 | + * @deprecated 14.0.0 use \OCP\ILogger::ERROR |
|
74 | + */ |
|
75 | + const ERROR=3; |
|
76 | + /** |
|
77 | + * @deprecated 14.0.0 use \OCP\ILogger::FATAL |
|
78 | + */ |
|
79 | + const FATAL=4; |
|
80 | + |
|
81 | + /** \OCP\Share\IManager */ |
|
82 | + private static $shareManager; |
|
83 | + |
|
84 | + /** |
|
85 | + * get the current installed version of ownCloud |
|
86 | + * @return array |
|
87 | + * @since 4.0.0 |
|
88 | + */ |
|
89 | + public static function getVersion() { |
|
90 | + return \OC_Util::getVersion(); |
|
91 | + } |
|
92 | 92 | |
93 | - /** |
|
94 | - * Set current update channel |
|
95 | - * @param string $channel |
|
96 | - * @since 8.1.0 |
|
97 | - */ |
|
98 | - public static function setChannel($channel) { |
|
99 | - \OC::$server->getConfig()->setSystemValue('updater.release.channel', $channel); |
|
100 | - } |
|
93 | + /** |
|
94 | + * Set current update channel |
|
95 | + * @param string $channel |
|
96 | + * @since 8.1.0 |
|
97 | + */ |
|
98 | + public static function setChannel($channel) { |
|
99 | + \OC::$server->getConfig()->setSystemValue('updater.release.channel', $channel); |
|
100 | + } |
|
101 | 101 | |
102 | - /** |
|
103 | - * Get current update channel |
|
104 | - * @return string |
|
105 | - * @since 8.1.0 |
|
106 | - */ |
|
107 | - public static function getChannel() { |
|
108 | - return \OC_Util::getChannel(); |
|
109 | - } |
|
110 | - |
|
111 | - /** |
|
112 | - * write a message in the log |
|
113 | - * @param string $app |
|
114 | - * @param string $message |
|
115 | - * @param int $level |
|
116 | - * @since 4.0.0 |
|
117 | - * @deprecated 13.0.0 use log of \OCP\ILogger |
|
118 | - */ |
|
119 | - public static function writeLog( $app, $message, $level ) { |
|
120 | - $context = ['app' => $app]; |
|
121 | - \OC::$server->getLogger()->log($level, $message, $context); |
|
122 | - } |
|
123 | - |
|
124 | - /** |
|
125 | - * write exception into the log |
|
126 | - * @param string $app app name |
|
127 | - * @param \Exception $ex exception to log |
|
128 | - * @param int $level log level, defaults to \OCP\Util::FATAL |
|
129 | - * @since ....0.0 - parameter $level was added in 7.0.0 |
|
130 | - * @deprecated 8.2.0 use logException of \OCP\ILogger |
|
131 | - */ |
|
132 | - public static function logException( $app, \Exception $ex, $level = ILogger::FATAL) { |
|
133 | - \OC::$server->getLogger()->logException($ex, ['app' => $app]); |
|
134 | - } |
|
135 | - |
|
136 | - /** |
|
137 | - * check if sharing is disabled for the current user |
|
138 | - * |
|
139 | - * @return boolean |
|
140 | - * @since 7.0.0 |
|
141 | - * @deprecated 9.1.0 Use \OC::$server->getShareManager()->sharingDisabledForUser |
|
142 | - */ |
|
143 | - public static function isSharingDisabledForUser() { |
|
144 | - if (self::$shareManager === null) { |
|
145 | - self::$shareManager = \OC::$server->getShareManager(); |
|
146 | - } |
|
147 | - |
|
148 | - $user = \OC::$server->getUserSession()->getUser(); |
|
149 | - if ($user !== null) { |
|
150 | - $user = $user->getUID(); |
|
151 | - } |
|
152 | - |
|
153 | - return self::$shareManager->sharingDisabledForUser($user); |
|
154 | - } |
|
155 | - |
|
156 | - /** |
|
157 | - * get l10n object |
|
158 | - * @param string $application |
|
159 | - * @param string|null $language |
|
160 | - * @return \OCP\IL10N |
|
161 | - * @since 6.0.0 - parameter $language was added in 8.0.0 |
|
162 | - */ |
|
163 | - public static function getL10N($application, $language = null) { |
|
164 | - return \OC::$server->getL10N($application, $language); |
|
165 | - } |
|
166 | - |
|
167 | - /** |
|
168 | - * add a css file |
|
169 | - * @param string $application |
|
170 | - * @param string $file |
|
171 | - * @since 4.0.0 |
|
172 | - */ |
|
173 | - public static function addStyle( $application, $file = null ) { |
|
174 | - \OC_Util::addStyle( $application, $file ); |
|
175 | - } |
|
176 | - |
|
177 | - /** |
|
178 | - * add a javascript file |
|
179 | - * @param string $application |
|
180 | - * @param string $file |
|
181 | - * @since 4.0.0 |
|
182 | - */ |
|
183 | - public static function addScript( $application, $file = null ) { |
|
184 | - \OC_Util::addScript( $application, $file ); |
|
185 | - } |
|
186 | - |
|
187 | - /** |
|
188 | - * Add a translation JS file |
|
189 | - * @param string $application application id |
|
190 | - * @param string $languageCode language code, defaults to the current locale |
|
191 | - * @since 8.0.0 |
|
192 | - */ |
|
193 | - public static function addTranslations($application, $languageCode = null) { |
|
194 | - \OC_Util::addTranslations($application, $languageCode); |
|
195 | - } |
|
196 | - |
|
197 | - /** |
|
198 | - * Add a custom element to the header |
|
199 | - * If $text is null then the element will be written as empty element. |
|
200 | - * So use "" to get a closing tag. |
|
201 | - * @param string $tag tag name of the element |
|
202 | - * @param array $attributes array of attributes for the element |
|
203 | - * @param string $text the text content for the element |
|
204 | - * @since 4.0.0 |
|
205 | - */ |
|
206 | - public static function addHeader($tag, $attributes, $text=null) { |
|
207 | - \OC_Util::addHeader($tag, $attributes, $text); |
|
208 | - } |
|
209 | - |
|
210 | - /** |
|
211 | - * Creates an absolute url to the given app and file. |
|
212 | - * @param string $app app |
|
213 | - * @param string $file file |
|
214 | - * @param array $args array with param=>value, will be appended to the returned url |
|
215 | - * The value of $args will be urlencoded |
|
216 | - * @return string the url |
|
217 | - * @since 4.0.0 - parameter $args was added in 4.5.0 |
|
218 | - */ |
|
219 | - public static function linkToAbsolute( $app, $file, $args = array() ) { |
|
220 | - $urlGenerator = \OC::$server->getURLGenerator(); |
|
221 | - return $urlGenerator->getAbsoluteURL( |
|
222 | - $urlGenerator->linkTo($app, $file, $args) |
|
223 | - ); |
|
224 | - } |
|
225 | - |
|
226 | - /** |
|
227 | - * Creates an absolute url for remote use. |
|
228 | - * @param string $service id |
|
229 | - * @return string the url |
|
230 | - * @since 4.0.0 |
|
231 | - */ |
|
232 | - public static function linkToRemote( $service ) { |
|
233 | - $urlGenerator = \OC::$server->getURLGenerator(); |
|
234 | - $remoteBase = $urlGenerator->linkTo('', 'remote.php') . '/' . $service; |
|
235 | - return $urlGenerator->getAbsoluteURL( |
|
236 | - $remoteBase . (($service[strlen($service) - 1] != '/') ? '/' : '') |
|
237 | - ); |
|
238 | - } |
|
239 | - |
|
240 | - /** |
|
241 | - * Creates an absolute url for public use |
|
242 | - * @param string $service id |
|
243 | - * @return string the url |
|
244 | - * @since 4.5.0 |
|
245 | - */ |
|
246 | - public static function linkToPublic($service) { |
|
247 | - return \OC_Helper::linkToPublic($service); |
|
248 | - } |
|
249 | - |
|
250 | - /** |
|
251 | - * Returns the server host name without an eventual port number |
|
252 | - * @return string the server hostname |
|
253 | - * @since 5.0.0 |
|
254 | - */ |
|
255 | - public static function getServerHostName() { |
|
256 | - $host_name = \OC::$server->getRequest()->getServerHost(); |
|
257 | - // strip away port number (if existing) |
|
258 | - $colon_pos = strpos($host_name, ':'); |
|
259 | - if ($colon_pos != FALSE) { |
|
260 | - $host_name = substr($host_name, 0, $colon_pos); |
|
261 | - } |
|
262 | - return $host_name; |
|
263 | - } |
|
264 | - |
|
265 | - /** |
|
266 | - * Returns the default email address |
|
267 | - * @param string $user_part the user part of the address |
|
268 | - * @return string the default email address |
|
269 | - * |
|
270 | - * Assembles a default email address (using the server hostname |
|
271 | - * and the given user part, and returns it |
|
272 | - * Example: when given lostpassword-noreply as $user_part param, |
|
273 | - * and is currently accessed via http(s)://example.com/, |
|
274 | - * it would return '[email protected]' |
|
275 | - * |
|
276 | - * If the configuration value 'mail_from_address' is set in |
|
277 | - * config.php, this value will override the $user_part that |
|
278 | - * is passed to this function |
|
279 | - * @since 5.0.0 |
|
280 | - */ |
|
281 | - public static function getDefaultEmailAddress($user_part) { |
|
282 | - $config = \OC::$server->getConfig(); |
|
283 | - $user_part = $config->getSystemValue('mail_from_address', $user_part); |
|
284 | - $host_name = self::getServerHostName(); |
|
285 | - $host_name = $config->getSystemValue('mail_domain', $host_name); |
|
286 | - $defaultEmailAddress = $user_part.'@'.$host_name; |
|
287 | - |
|
288 | - $mailer = \OC::$server->getMailer(); |
|
289 | - if ($mailer->validateMailAddress($defaultEmailAddress)) { |
|
290 | - return $defaultEmailAddress; |
|
291 | - } |
|
292 | - |
|
293 | - // in case we cannot build a valid email address from the hostname let's fallback to 'localhost.localdomain' |
|
294 | - return $user_part.'@localhost.localdomain'; |
|
295 | - } |
|
296 | - |
|
297 | - /** |
|
298 | - * Make a human file size (2048 to 2 kB) |
|
299 | - * @param int $bytes file size in bytes |
|
300 | - * @return string a human readable file size |
|
301 | - * @since 4.0.0 |
|
302 | - */ |
|
303 | - public static function humanFileSize($bytes) { |
|
304 | - return \OC_Helper::humanFileSize($bytes); |
|
305 | - } |
|
306 | - |
|
307 | - /** |
|
308 | - * Make a computer file size (2 kB to 2048) |
|
309 | - * @param string $str file size in a fancy format |
|
310 | - * @return float a file size in bytes |
|
311 | - * |
|
312 | - * Inspired by: http://www.php.net/manual/en/function.filesize.php#92418 |
|
313 | - * @since 4.0.0 |
|
314 | - */ |
|
315 | - public static function computerFileSize($str) { |
|
316 | - return \OC_Helper::computerFileSize($str); |
|
317 | - } |
|
318 | - |
|
319 | - /** |
|
320 | - * connects a function to a hook |
|
321 | - * |
|
322 | - * @param string $signalClass class name of emitter |
|
323 | - * @param string $signalName name of signal |
|
324 | - * @param string|object $slotClass class name of slot |
|
325 | - * @param string $slotName name of slot |
|
326 | - * @return bool |
|
327 | - * |
|
328 | - * This function makes it very easy to connect to use hooks. |
|
329 | - * |
|
330 | - * TODO: write example |
|
331 | - * @since 4.0.0 |
|
332 | - */ |
|
333 | - static public function connectHook($signalClass, $signalName, $slotClass, $slotName) { |
|
334 | - return \OC_Hook::connect($signalClass, $signalName, $slotClass, $slotName); |
|
335 | - } |
|
336 | - |
|
337 | - /** |
|
338 | - * Emits a signal. To get data from the slot use references! |
|
339 | - * @param string $signalclass class name of emitter |
|
340 | - * @param string $signalname name of signal |
|
341 | - * @param array $params default: array() array with additional data |
|
342 | - * @return bool true if slots exists or false if not |
|
343 | - * |
|
344 | - * TODO: write example |
|
345 | - * @since 4.0.0 |
|
346 | - */ |
|
347 | - static public function emitHook($signalclass, $signalname, $params = array()) { |
|
348 | - return \OC_Hook::emit($signalclass, $signalname, $params); |
|
349 | - } |
|
350 | - |
|
351 | - /** |
|
352 | - * Cached encrypted CSRF token. Some static unit-tests of ownCloud compare |
|
353 | - * multiple OC_Template elements which invoke `callRegister`. If the value |
|
354 | - * would not be cached these unit-tests would fail. |
|
355 | - * @var string |
|
356 | - */ |
|
357 | - private static $token = ''; |
|
358 | - |
|
359 | - /** |
|
360 | - * Register an get/post call. This is important to prevent CSRF attacks |
|
361 | - * @since 4.5.0 |
|
362 | - */ |
|
363 | - public static function callRegister() { |
|
364 | - if(self::$token === '') { |
|
365 | - self::$token = \OC::$server->getCsrfTokenManager()->getToken()->getEncryptedValue(); |
|
366 | - } |
|
367 | - return self::$token; |
|
368 | - } |
|
369 | - |
|
370 | - /** |
|
371 | - * Check an ajax get/post call if the request token is valid. exit if not. |
|
372 | - * @since 4.5.0 |
|
373 | - * @deprecated 9.0.0 Use annotations based on the app framework. |
|
374 | - */ |
|
375 | - public static function callCheck() { |
|
376 | - if(!\OC::$server->getRequest()->passesStrictCookieCheck()) { |
|
377 | - header('Location: '.\OC::$WEBROOT); |
|
378 | - exit(); |
|
379 | - } |
|
380 | - |
|
381 | - if (!\OC::$server->getRequest()->passesCSRFCheck()) { |
|
382 | - exit(); |
|
383 | - } |
|
384 | - } |
|
385 | - |
|
386 | - /** |
|
387 | - * Used to sanitize HTML |
|
388 | - * |
|
389 | - * This function is used to sanitize HTML and should be applied on any |
|
390 | - * string or array of strings before displaying it on a web page. |
|
391 | - * |
|
392 | - * @param string|array $value |
|
393 | - * @return string|array an array of sanitized strings or a single sanitized string, depends on the input parameter. |
|
394 | - * @since 4.5.0 |
|
395 | - */ |
|
396 | - public static function sanitizeHTML($value) { |
|
397 | - return \OC_Util::sanitizeHTML($value); |
|
398 | - } |
|
399 | - |
|
400 | - /** |
|
401 | - * Public function to encode url parameters |
|
402 | - * |
|
403 | - * This function is used to encode path to file before output. |
|
404 | - * Encoding is done according to RFC 3986 with one exception: |
|
405 | - * Character '/' is preserved as is. |
|
406 | - * |
|
407 | - * @param string $component part of URI to encode |
|
408 | - * @return string |
|
409 | - * @since 6.0.0 |
|
410 | - */ |
|
411 | - public static function encodePath($component) { |
|
412 | - return \OC_Util::encodePath($component); |
|
413 | - } |
|
414 | - |
|
415 | - /** |
|
416 | - * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is. |
|
417 | - * |
|
418 | - * @param array $input The array to work on |
|
419 | - * @param int $case Either MB_CASE_UPPER or MB_CASE_LOWER (default) |
|
420 | - * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8 |
|
421 | - * @return array |
|
422 | - * @since 4.5.0 |
|
423 | - */ |
|
424 | - public static function mb_array_change_key_case($input, $case = MB_CASE_LOWER, $encoding = 'UTF-8') { |
|
425 | - return \OC_Helper::mb_array_change_key_case($input, $case, $encoding); |
|
426 | - } |
|
427 | - |
|
428 | - /** |
|
429 | - * replaces a copy of string delimited by the start and (optionally) length parameters with the string given in replacement. |
|
430 | - * |
|
431 | - * @param string $string The input string. Opposite to the PHP build-in function does not accept an array. |
|
432 | - * @param string $replacement The replacement string. |
|
433 | - * @param int $start If start is positive, the replacing will begin at the start'th offset into string. If start is negative, the replacing will begin at the start'th character from the end of string. |
|
434 | - * @param int $length Length of the part to be replaced |
|
435 | - * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8 |
|
436 | - * @return string |
|
437 | - * @since 4.5.0 |
|
438 | - * @deprecated 8.2.0 Use substr_replace() instead. |
|
439 | - */ |
|
440 | - public static function mb_substr_replace($string, $replacement, $start, $length = null, $encoding = 'UTF-8') { |
|
441 | - return substr_replace($string, $replacement, $start, $length); |
|
442 | - } |
|
443 | - |
|
444 | - /** |
|
445 | - * Replace all occurrences of the search string with the replacement string |
|
446 | - * |
|
447 | - * @param string $search The value being searched for, otherwise known as the needle. String. |
|
448 | - * @param string $replace The replacement string. |
|
449 | - * @param string $subject The string or array being searched and replaced on, otherwise known as the haystack. |
|
450 | - * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8 |
|
451 | - * @param int $count If passed, this will be set to the number of replacements performed. |
|
452 | - * @return string |
|
453 | - * @since 4.5.0 |
|
454 | - * @deprecated 8.2.0 Use str_replace() instead. |
|
455 | - */ |
|
456 | - public static function mb_str_replace($search, $replace, $subject, $encoding = 'UTF-8', &$count = null) { |
|
457 | - return str_replace($search, $replace, $subject, $count); |
|
458 | - } |
|
459 | - |
|
460 | - /** |
|
461 | - * performs a search in a nested array |
|
462 | - * |
|
463 | - * @param array $haystack the array to be searched |
|
464 | - * @param string $needle the search string |
|
465 | - * @param mixed $index optional, only search this key name |
|
466 | - * @return mixed the key of the matching field, otherwise false |
|
467 | - * @since 4.5.0 |
|
468 | - */ |
|
469 | - public static function recursiveArraySearch($haystack, $needle, $index = null) { |
|
470 | - return \OC_Helper::recursiveArraySearch($haystack, $needle, $index); |
|
471 | - } |
|
472 | - |
|
473 | - /** |
|
474 | - * calculates the maximum upload size respecting system settings, free space and user quota |
|
475 | - * |
|
476 | - * @param string $dir the current folder where the user currently operates |
|
477 | - * @param int $free the number of bytes free on the storage holding $dir, if not set this will be received from the storage directly |
|
478 | - * @return int number of bytes representing |
|
479 | - * @since 5.0.0 |
|
480 | - */ |
|
481 | - public static function maxUploadFilesize($dir, $free = null) { |
|
482 | - return \OC_Helper::maxUploadFilesize($dir, $free); |
|
483 | - } |
|
484 | - |
|
485 | - /** |
|
486 | - * Calculate free space left within user quota |
|
487 | - * @param string $dir the current folder where the user currently operates |
|
488 | - * @return int number of bytes representing |
|
489 | - * @since 7.0.0 |
|
490 | - */ |
|
491 | - public static function freeSpace($dir) { |
|
492 | - return \OC_Helper::freeSpace($dir); |
|
493 | - } |
|
494 | - |
|
495 | - /** |
|
496 | - * Calculate PHP upload limit |
|
497 | - * |
|
498 | - * @return int number of bytes representing |
|
499 | - * @since 7.0.0 |
|
500 | - */ |
|
501 | - public static function uploadLimit() { |
|
502 | - return \OC_Helper::uploadLimit(); |
|
503 | - } |
|
504 | - |
|
505 | - /** |
|
506 | - * Returns whether the given file name is valid |
|
507 | - * @param string $file file name to check |
|
508 | - * @return bool true if the file name is valid, false otherwise |
|
509 | - * @deprecated 8.1.0 use \OC\Files\View::verifyPath() |
|
510 | - * @since 7.0.0 |
|
511 | - * @suppress PhanDeprecatedFunction |
|
512 | - */ |
|
513 | - public static function isValidFileName($file) { |
|
514 | - return \OC_Util::isValidFileName($file); |
|
515 | - } |
|
516 | - |
|
517 | - /** |
|
518 | - * Compare two strings to provide a natural sort |
|
519 | - * @param string $a first string to compare |
|
520 | - * @param string $b second string to compare |
|
521 | - * @return int -1 if $b comes before $a, 1 if $a comes before $b |
|
522 | - * or 0 if the strings are identical |
|
523 | - * @since 7.0.0 |
|
524 | - */ |
|
525 | - public static function naturalSortCompare($a, $b) { |
|
526 | - return \OC\NaturalSort::getInstance()->compare($a, $b); |
|
527 | - } |
|
528 | - |
|
529 | - /** |
|
530 | - * check if a password is required for each public link |
|
531 | - * @return boolean |
|
532 | - * @since 7.0.0 |
|
533 | - */ |
|
534 | - public static function isPublicLinkPasswordRequired() { |
|
535 | - return \OC_Util::isPublicLinkPasswordRequired(); |
|
536 | - } |
|
537 | - |
|
538 | - /** |
|
539 | - * check if share API enforces a default expire date |
|
540 | - * @return boolean |
|
541 | - * @since 8.0.0 |
|
542 | - */ |
|
543 | - public static function isDefaultExpireDateEnforced() { |
|
544 | - return \OC_Util::isDefaultExpireDateEnforced(); |
|
545 | - } |
|
546 | - |
|
547 | - protected static $needUpgradeCache = null; |
|
548 | - |
|
549 | - /** |
|
550 | - * Checks whether the current version needs upgrade. |
|
551 | - * |
|
552 | - * @return bool true if upgrade is needed, false otherwise |
|
553 | - * @since 7.0.0 |
|
554 | - */ |
|
555 | - public static function needUpgrade() { |
|
556 | - if (!isset(self::$needUpgradeCache)) { |
|
557 | - self::$needUpgradeCache=\OC_Util::needUpgrade(\OC::$server->getSystemConfig()); |
|
558 | - } |
|
559 | - return self::$needUpgradeCache; |
|
560 | - } |
|
102 | + /** |
|
103 | + * Get current update channel |
|
104 | + * @return string |
|
105 | + * @since 8.1.0 |
|
106 | + */ |
|
107 | + public static function getChannel() { |
|
108 | + return \OC_Util::getChannel(); |
|
109 | + } |
|
110 | + |
|
111 | + /** |
|
112 | + * write a message in the log |
|
113 | + * @param string $app |
|
114 | + * @param string $message |
|
115 | + * @param int $level |
|
116 | + * @since 4.0.0 |
|
117 | + * @deprecated 13.0.0 use log of \OCP\ILogger |
|
118 | + */ |
|
119 | + public static function writeLog( $app, $message, $level ) { |
|
120 | + $context = ['app' => $app]; |
|
121 | + \OC::$server->getLogger()->log($level, $message, $context); |
|
122 | + } |
|
123 | + |
|
124 | + /** |
|
125 | + * write exception into the log |
|
126 | + * @param string $app app name |
|
127 | + * @param \Exception $ex exception to log |
|
128 | + * @param int $level log level, defaults to \OCP\Util::FATAL |
|
129 | + * @since ....0.0 - parameter $level was added in 7.0.0 |
|
130 | + * @deprecated 8.2.0 use logException of \OCP\ILogger |
|
131 | + */ |
|
132 | + public static function logException( $app, \Exception $ex, $level = ILogger::FATAL) { |
|
133 | + \OC::$server->getLogger()->logException($ex, ['app' => $app]); |
|
134 | + } |
|
135 | + |
|
136 | + /** |
|
137 | + * check if sharing is disabled for the current user |
|
138 | + * |
|
139 | + * @return boolean |
|
140 | + * @since 7.0.0 |
|
141 | + * @deprecated 9.1.0 Use \OC::$server->getShareManager()->sharingDisabledForUser |
|
142 | + */ |
|
143 | + public static function isSharingDisabledForUser() { |
|
144 | + if (self::$shareManager === null) { |
|
145 | + self::$shareManager = \OC::$server->getShareManager(); |
|
146 | + } |
|
147 | + |
|
148 | + $user = \OC::$server->getUserSession()->getUser(); |
|
149 | + if ($user !== null) { |
|
150 | + $user = $user->getUID(); |
|
151 | + } |
|
152 | + |
|
153 | + return self::$shareManager->sharingDisabledForUser($user); |
|
154 | + } |
|
155 | + |
|
156 | + /** |
|
157 | + * get l10n object |
|
158 | + * @param string $application |
|
159 | + * @param string|null $language |
|
160 | + * @return \OCP\IL10N |
|
161 | + * @since 6.0.0 - parameter $language was added in 8.0.0 |
|
162 | + */ |
|
163 | + public static function getL10N($application, $language = null) { |
|
164 | + return \OC::$server->getL10N($application, $language); |
|
165 | + } |
|
166 | + |
|
167 | + /** |
|
168 | + * add a css file |
|
169 | + * @param string $application |
|
170 | + * @param string $file |
|
171 | + * @since 4.0.0 |
|
172 | + */ |
|
173 | + public static function addStyle( $application, $file = null ) { |
|
174 | + \OC_Util::addStyle( $application, $file ); |
|
175 | + } |
|
176 | + |
|
177 | + /** |
|
178 | + * add a javascript file |
|
179 | + * @param string $application |
|
180 | + * @param string $file |
|
181 | + * @since 4.0.0 |
|
182 | + */ |
|
183 | + public static function addScript( $application, $file = null ) { |
|
184 | + \OC_Util::addScript( $application, $file ); |
|
185 | + } |
|
186 | + |
|
187 | + /** |
|
188 | + * Add a translation JS file |
|
189 | + * @param string $application application id |
|
190 | + * @param string $languageCode language code, defaults to the current locale |
|
191 | + * @since 8.0.0 |
|
192 | + */ |
|
193 | + public static function addTranslations($application, $languageCode = null) { |
|
194 | + \OC_Util::addTranslations($application, $languageCode); |
|
195 | + } |
|
196 | + |
|
197 | + /** |
|
198 | + * Add a custom element to the header |
|
199 | + * If $text is null then the element will be written as empty element. |
|
200 | + * So use "" to get a closing tag. |
|
201 | + * @param string $tag tag name of the element |
|
202 | + * @param array $attributes array of attributes for the element |
|
203 | + * @param string $text the text content for the element |
|
204 | + * @since 4.0.0 |
|
205 | + */ |
|
206 | + public static function addHeader($tag, $attributes, $text=null) { |
|
207 | + \OC_Util::addHeader($tag, $attributes, $text); |
|
208 | + } |
|
209 | + |
|
210 | + /** |
|
211 | + * Creates an absolute url to the given app and file. |
|
212 | + * @param string $app app |
|
213 | + * @param string $file file |
|
214 | + * @param array $args array with param=>value, will be appended to the returned url |
|
215 | + * The value of $args will be urlencoded |
|
216 | + * @return string the url |
|
217 | + * @since 4.0.0 - parameter $args was added in 4.5.0 |
|
218 | + */ |
|
219 | + public static function linkToAbsolute( $app, $file, $args = array() ) { |
|
220 | + $urlGenerator = \OC::$server->getURLGenerator(); |
|
221 | + return $urlGenerator->getAbsoluteURL( |
|
222 | + $urlGenerator->linkTo($app, $file, $args) |
|
223 | + ); |
|
224 | + } |
|
225 | + |
|
226 | + /** |
|
227 | + * Creates an absolute url for remote use. |
|
228 | + * @param string $service id |
|
229 | + * @return string the url |
|
230 | + * @since 4.0.0 |
|
231 | + */ |
|
232 | + public static function linkToRemote( $service ) { |
|
233 | + $urlGenerator = \OC::$server->getURLGenerator(); |
|
234 | + $remoteBase = $urlGenerator->linkTo('', 'remote.php') . '/' . $service; |
|
235 | + return $urlGenerator->getAbsoluteURL( |
|
236 | + $remoteBase . (($service[strlen($service) - 1] != '/') ? '/' : '') |
|
237 | + ); |
|
238 | + } |
|
239 | + |
|
240 | + /** |
|
241 | + * Creates an absolute url for public use |
|
242 | + * @param string $service id |
|
243 | + * @return string the url |
|
244 | + * @since 4.5.0 |
|
245 | + */ |
|
246 | + public static function linkToPublic($service) { |
|
247 | + return \OC_Helper::linkToPublic($service); |
|
248 | + } |
|
249 | + |
|
250 | + /** |
|
251 | + * Returns the server host name without an eventual port number |
|
252 | + * @return string the server hostname |
|
253 | + * @since 5.0.0 |
|
254 | + */ |
|
255 | + public static function getServerHostName() { |
|
256 | + $host_name = \OC::$server->getRequest()->getServerHost(); |
|
257 | + // strip away port number (if existing) |
|
258 | + $colon_pos = strpos($host_name, ':'); |
|
259 | + if ($colon_pos != FALSE) { |
|
260 | + $host_name = substr($host_name, 0, $colon_pos); |
|
261 | + } |
|
262 | + return $host_name; |
|
263 | + } |
|
264 | + |
|
265 | + /** |
|
266 | + * Returns the default email address |
|
267 | + * @param string $user_part the user part of the address |
|
268 | + * @return string the default email address |
|
269 | + * |
|
270 | + * Assembles a default email address (using the server hostname |
|
271 | + * and the given user part, and returns it |
|
272 | + * Example: when given lostpassword-noreply as $user_part param, |
|
273 | + * and is currently accessed via http(s)://example.com/, |
|
274 | + * it would return '[email protected]' |
|
275 | + * |
|
276 | + * If the configuration value 'mail_from_address' is set in |
|
277 | + * config.php, this value will override the $user_part that |
|
278 | + * is passed to this function |
|
279 | + * @since 5.0.0 |
|
280 | + */ |
|
281 | + public static function getDefaultEmailAddress($user_part) { |
|
282 | + $config = \OC::$server->getConfig(); |
|
283 | + $user_part = $config->getSystemValue('mail_from_address', $user_part); |
|
284 | + $host_name = self::getServerHostName(); |
|
285 | + $host_name = $config->getSystemValue('mail_domain', $host_name); |
|
286 | + $defaultEmailAddress = $user_part.'@'.$host_name; |
|
287 | + |
|
288 | + $mailer = \OC::$server->getMailer(); |
|
289 | + if ($mailer->validateMailAddress($defaultEmailAddress)) { |
|
290 | + return $defaultEmailAddress; |
|
291 | + } |
|
292 | + |
|
293 | + // in case we cannot build a valid email address from the hostname let's fallback to 'localhost.localdomain' |
|
294 | + return $user_part.'@localhost.localdomain'; |
|
295 | + } |
|
296 | + |
|
297 | + /** |
|
298 | + * Make a human file size (2048 to 2 kB) |
|
299 | + * @param int $bytes file size in bytes |
|
300 | + * @return string a human readable file size |
|
301 | + * @since 4.0.0 |
|
302 | + */ |
|
303 | + public static function humanFileSize($bytes) { |
|
304 | + return \OC_Helper::humanFileSize($bytes); |
|
305 | + } |
|
306 | + |
|
307 | + /** |
|
308 | + * Make a computer file size (2 kB to 2048) |
|
309 | + * @param string $str file size in a fancy format |
|
310 | + * @return float a file size in bytes |
|
311 | + * |
|
312 | + * Inspired by: http://www.php.net/manual/en/function.filesize.php#92418 |
|
313 | + * @since 4.0.0 |
|
314 | + */ |
|
315 | + public static function computerFileSize($str) { |
|
316 | + return \OC_Helper::computerFileSize($str); |
|
317 | + } |
|
318 | + |
|
319 | + /** |
|
320 | + * connects a function to a hook |
|
321 | + * |
|
322 | + * @param string $signalClass class name of emitter |
|
323 | + * @param string $signalName name of signal |
|
324 | + * @param string|object $slotClass class name of slot |
|
325 | + * @param string $slotName name of slot |
|
326 | + * @return bool |
|
327 | + * |
|
328 | + * This function makes it very easy to connect to use hooks. |
|
329 | + * |
|
330 | + * TODO: write example |
|
331 | + * @since 4.0.0 |
|
332 | + */ |
|
333 | + static public function connectHook($signalClass, $signalName, $slotClass, $slotName) { |
|
334 | + return \OC_Hook::connect($signalClass, $signalName, $slotClass, $slotName); |
|
335 | + } |
|
336 | + |
|
337 | + /** |
|
338 | + * Emits a signal. To get data from the slot use references! |
|
339 | + * @param string $signalclass class name of emitter |
|
340 | + * @param string $signalname name of signal |
|
341 | + * @param array $params default: array() array with additional data |
|
342 | + * @return bool true if slots exists or false if not |
|
343 | + * |
|
344 | + * TODO: write example |
|
345 | + * @since 4.0.0 |
|
346 | + */ |
|
347 | + static public function emitHook($signalclass, $signalname, $params = array()) { |
|
348 | + return \OC_Hook::emit($signalclass, $signalname, $params); |
|
349 | + } |
|
350 | + |
|
351 | + /** |
|
352 | + * Cached encrypted CSRF token. Some static unit-tests of ownCloud compare |
|
353 | + * multiple OC_Template elements which invoke `callRegister`. If the value |
|
354 | + * would not be cached these unit-tests would fail. |
|
355 | + * @var string |
|
356 | + */ |
|
357 | + private static $token = ''; |
|
358 | + |
|
359 | + /** |
|
360 | + * Register an get/post call. This is important to prevent CSRF attacks |
|
361 | + * @since 4.5.0 |
|
362 | + */ |
|
363 | + public static function callRegister() { |
|
364 | + if(self::$token === '') { |
|
365 | + self::$token = \OC::$server->getCsrfTokenManager()->getToken()->getEncryptedValue(); |
|
366 | + } |
|
367 | + return self::$token; |
|
368 | + } |
|
369 | + |
|
370 | + /** |
|
371 | + * Check an ajax get/post call if the request token is valid. exit if not. |
|
372 | + * @since 4.5.0 |
|
373 | + * @deprecated 9.0.0 Use annotations based on the app framework. |
|
374 | + */ |
|
375 | + public static function callCheck() { |
|
376 | + if(!\OC::$server->getRequest()->passesStrictCookieCheck()) { |
|
377 | + header('Location: '.\OC::$WEBROOT); |
|
378 | + exit(); |
|
379 | + } |
|
380 | + |
|
381 | + if (!\OC::$server->getRequest()->passesCSRFCheck()) { |
|
382 | + exit(); |
|
383 | + } |
|
384 | + } |
|
385 | + |
|
386 | + /** |
|
387 | + * Used to sanitize HTML |
|
388 | + * |
|
389 | + * This function is used to sanitize HTML and should be applied on any |
|
390 | + * string or array of strings before displaying it on a web page. |
|
391 | + * |
|
392 | + * @param string|array $value |
|
393 | + * @return string|array an array of sanitized strings or a single sanitized string, depends on the input parameter. |
|
394 | + * @since 4.5.0 |
|
395 | + */ |
|
396 | + public static function sanitizeHTML($value) { |
|
397 | + return \OC_Util::sanitizeHTML($value); |
|
398 | + } |
|
399 | + |
|
400 | + /** |
|
401 | + * Public function to encode url parameters |
|
402 | + * |
|
403 | + * This function is used to encode path to file before output. |
|
404 | + * Encoding is done according to RFC 3986 with one exception: |
|
405 | + * Character '/' is preserved as is. |
|
406 | + * |
|
407 | + * @param string $component part of URI to encode |
|
408 | + * @return string |
|
409 | + * @since 6.0.0 |
|
410 | + */ |
|
411 | + public static function encodePath($component) { |
|
412 | + return \OC_Util::encodePath($component); |
|
413 | + } |
|
414 | + |
|
415 | + /** |
|
416 | + * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is. |
|
417 | + * |
|
418 | + * @param array $input The array to work on |
|
419 | + * @param int $case Either MB_CASE_UPPER or MB_CASE_LOWER (default) |
|
420 | + * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8 |
|
421 | + * @return array |
|
422 | + * @since 4.5.0 |
|
423 | + */ |
|
424 | + public static function mb_array_change_key_case($input, $case = MB_CASE_LOWER, $encoding = 'UTF-8') { |
|
425 | + return \OC_Helper::mb_array_change_key_case($input, $case, $encoding); |
|
426 | + } |
|
427 | + |
|
428 | + /** |
|
429 | + * replaces a copy of string delimited by the start and (optionally) length parameters with the string given in replacement. |
|
430 | + * |
|
431 | + * @param string $string The input string. Opposite to the PHP build-in function does not accept an array. |
|
432 | + * @param string $replacement The replacement string. |
|
433 | + * @param int $start If start is positive, the replacing will begin at the start'th offset into string. If start is negative, the replacing will begin at the start'th character from the end of string. |
|
434 | + * @param int $length Length of the part to be replaced |
|
435 | + * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8 |
|
436 | + * @return string |
|
437 | + * @since 4.5.0 |
|
438 | + * @deprecated 8.2.0 Use substr_replace() instead. |
|
439 | + */ |
|
440 | + public static function mb_substr_replace($string, $replacement, $start, $length = null, $encoding = 'UTF-8') { |
|
441 | + return substr_replace($string, $replacement, $start, $length); |
|
442 | + } |
|
443 | + |
|
444 | + /** |
|
445 | + * Replace all occurrences of the search string with the replacement string |
|
446 | + * |
|
447 | + * @param string $search The value being searched for, otherwise known as the needle. String. |
|
448 | + * @param string $replace The replacement string. |
|
449 | + * @param string $subject The string or array being searched and replaced on, otherwise known as the haystack. |
|
450 | + * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8 |
|
451 | + * @param int $count If passed, this will be set to the number of replacements performed. |
|
452 | + * @return string |
|
453 | + * @since 4.5.0 |
|
454 | + * @deprecated 8.2.0 Use str_replace() instead. |
|
455 | + */ |
|
456 | + public static function mb_str_replace($search, $replace, $subject, $encoding = 'UTF-8', &$count = null) { |
|
457 | + return str_replace($search, $replace, $subject, $count); |
|
458 | + } |
|
459 | + |
|
460 | + /** |
|
461 | + * performs a search in a nested array |
|
462 | + * |
|
463 | + * @param array $haystack the array to be searched |
|
464 | + * @param string $needle the search string |
|
465 | + * @param mixed $index optional, only search this key name |
|
466 | + * @return mixed the key of the matching field, otherwise false |
|
467 | + * @since 4.5.0 |
|
468 | + */ |
|
469 | + public static function recursiveArraySearch($haystack, $needle, $index = null) { |
|
470 | + return \OC_Helper::recursiveArraySearch($haystack, $needle, $index); |
|
471 | + } |
|
472 | + |
|
473 | + /** |
|
474 | + * calculates the maximum upload size respecting system settings, free space and user quota |
|
475 | + * |
|
476 | + * @param string $dir the current folder where the user currently operates |
|
477 | + * @param int $free the number of bytes free on the storage holding $dir, if not set this will be received from the storage directly |
|
478 | + * @return int number of bytes representing |
|
479 | + * @since 5.0.0 |
|
480 | + */ |
|
481 | + public static function maxUploadFilesize($dir, $free = null) { |
|
482 | + return \OC_Helper::maxUploadFilesize($dir, $free); |
|
483 | + } |
|
484 | + |
|
485 | + /** |
|
486 | + * Calculate free space left within user quota |
|
487 | + * @param string $dir the current folder where the user currently operates |
|
488 | + * @return int number of bytes representing |
|
489 | + * @since 7.0.0 |
|
490 | + */ |
|
491 | + public static function freeSpace($dir) { |
|
492 | + return \OC_Helper::freeSpace($dir); |
|
493 | + } |
|
494 | + |
|
495 | + /** |
|
496 | + * Calculate PHP upload limit |
|
497 | + * |
|
498 | + * @return int number of bytes representing |
|
499 | + * @since 7.0.0 |
|
500 | + */ |
|
501 | + public static function uploadLimit() { |
|
502 | + return \OC_Helper::uploadLimit(); |
|
503 | + } |
|
504 | + |
|
505 | + /** |
|
506 | + * Returns whether the given file name is valid |
|
507 | + * @param string $file file name to check |
|
508 | + * @return bool true if the file name is valid, false otherwise |
|
509 | + * @deprecated 8.1.0 use \OC\Files\View::verifyPath() |
|
510 | + * @since 7.0.0 |
|
511 | + * @suppress PhanDeprecatedFunction |
|
512 | + */ |
|
513 | + public static function isValidFileName($file) { |
|
514 | + return \OC_Util::isValidFileName($file); |
|
515 | + } |
|
516 | + |
|
517 | + /** |
|
518 | + * Compare two strings to provide a natural sort |
|
519 | + * @param string $a first string to compare |
|
520 | + * @param string $b second string to compare |
|
521 | + * @return int -1 if $b comes before $a, 1 if $a comes before $b |
|
522 | + * or 0 if the strings are identical |
|
523 | + * @since 7.0.0 |
|
524 | + */ |
|
525 | + public static function naturalSortCompare($a, $b) { |
|
526 | + return \OC\NaturalSort::getInstance()->compare($a, $b); |
|
527 | + } |
|
528 | + |
|
529 | + /** |
|
530 | + * check if a password is required for each public link |
|
531 | + * @return boolean |
|
532 | + * @since 7.0.0 |
|
533 | + */ |
|
534 | + public static function isPublicLinkPasswordRequired() { |
|
535 | + return \OC_Util::isPublicLinkPasswordRequired(); |
|
536 | + } |
|
537 | + |
|
538 | + /** |
|
539 | + * check if share API enforces a default expire date |
|
540 | + * @return boolean |
|
541 | + * @since 8.0.0 |
|
542 | + */ |
|
543 | + public static function isDefaultExpireDateEnforced() { |
|
544 | + return \OC_Util::isDefaultExpireDateEnforced(); |
|
545 | + } |
|
546 | + |
|
547 | + protected static $needUpgradeCache = null; |
|
548 | + |
|
549 | + /** |
|
550 | + * Checks whether the current version needs upgrade. |
|
551 | + * |
|
552 | + * @return bool true if upgrade is needed, false otherwise |
|
553 | + * @since 7.0.0 |
|
554 | + */ |
|
555 | + public static function needUpgrade() { |
|
556 | + if (!isset(self::$needUpgradeCache)) { |
|
557 | + self::$needUpgradeCache=\OC_Util::needUpgrade(\OC::$server->getSystemConfig()); |
|
558 | + } |
|
559 | + return self::$needUpgradeCache; |
|
560 | + } |
|
561 | 561 | } |
@@ -60,23 +60,23 @@ discard block |
||
60 | 60 | /** |
61 | 61 | * @deprecated 14.0.0 use \OCP\ILogger::DEBUG |
62 | 62 | */ |
63 | - const DEBUG=0; |
|
63 | + const DEBUG = 0; |
|
64 | 64 | /** |
65 | 65 | * @deprecated 14.0.0 use \OCP\ILogger::INFO |
66 | 66 | */ |
67 | - const INFO=1; |
|
67 | + const INFO = 1; |
|
68 | 68 | /** |
69 | 69 | * @deprecated 14.0.0 use \OCP\ILogger::WARN |
70 | 70 | */ |
71 | - const WARN=2; |
|
71 | + const WARN = 2; |
|
72 | 72 | /** |
73 | 73 | * @deprecated 14.0.0 use \OCP\ILogger::ERROR |
74 | 74 | */ |
75 | - const ERROR=3; |
|
75 | + const ERROR = 3; |
|
76 | 76 | /** |
77 | 77 | * @deprecated 14.0.0 use \OCP\ILogger::FATAL |
78 | 78 | */ |
79 | - const FATAL=4; |
|
79 | + const FATAL = 4; |
|
80 | 80 | |
81 | 81 | /** \OCP\Share\IManager */ |
82 | 82 | private static $shareManager; |
@@ -116,7 +116,7 @@ discard block |
||
116 | 116 | * @since 4.0.0 |
117 | 117 | * @deprecated 13.0.0 use log of \OCP\ILogger |
118 | 118 | */ |
119 | - public static function writeLog( $app, $message, $level ) { |
|
119 | + public static function writeLog($app, $message, $level) { |
|
120 | 120 | $context = ['app' => $app]; |
121 | 121 | \OC::$server->getLogger()->log($level, $message, $context); |
122 | 122 | } |
@@ -129,7 +129,7 @@ discard block |
||
129 | 129 | * @since ....0.0 - parameter $level was added in 7.0.0 |
130 | 130 | * @deprecated 8.2.0 use logException of \OCP\ILogger |
131 | 131 | */ |
132 | - public static function logException( $app, \Exception $ex, $level = ILogger::FATAL) { |
|
132 | + public static function logException($app, \Exception $ex, $level = ILogger::FATAL) { |
|
133 | 133 | \OC::$server->getLogger()->logException($ex, ['app' => $app]); |
134 | 134 | } |
135 | 135 | |
@@ -170,8 +170,8 @@ discard block |
||
170 | 170 | * @param string $file |
171 | 171 | * @since 4.0.0 |
172 | 172 | */ |
173 | - public static function addStyle( $application, $file = null ) { |
|
174 | - \OC_Util::addStyle( $application, $file ); |
|
173 | + public static function addStyle($application, $file = null) { |
|
174 | + \OC_Util::addStyle($application, $file); |
|
175 | 175 | } |
176 | 176 | |
177 | 177 | /** |
@@ -180,8 +180,8 @@ discard block |
||
180 | 180 | * @param string $file |
181 | 181 | * @since 4.0.0 |
182 | 182 | */ |
183 | - public static function addScript( $application, $file = null ) { |
|
184 | - \OC_Util::addScript( $application, $file ); |
|
183 | + public static function addScript($application, $file = null) { |
|
184 | + \OC_Util::addScript($application, $file); |
|
185 | 185 | } |
186 | 186 | |
187 | 187 | /** |
@@ -203,7 +203,7 @@ discard block |
||
203 | 203 | * @param string $text the text content for the element |
204 | 204 | * @since 4.0.0 |
205 | 205 | */ |
206 | - public static function addHeader($tag, $attributes, $text=null) { |
|
206 | + public static function addHeader($tag, $attributes, $text = null) { |
|
207 | 207 | \OC_Util::addHeader($tag, $attributes, $text); |
208 | 208 | } |
209 | 209 | |
@@ -216,7 +216,7 @@ discard block |
||
216 | 216 | * @return string the url |
217 | 217 | * @since 4.0.0 - parameter $args was added in 4.5.0 |
218 | 218 | */ |
219 | - public static function linkToAbsolute( $app, $file, $args = array() ) { |
|
219 | + public static function linkToAbsolute($app, $file, $args = array()) { |
|
220 | 220 | $urlGenerator = \OC::$server->getURLGenerator(); |
221 | 221 | return $urlGenerator->getAbsoluteURL( |
222 | 222 | $urlGenerator->linkTo($app, $file, $args) |
@@ -229,11 +229,11 @@ discard block |
||
229 | 229 | * @return string the url |
230 | 230 | * @since 4.0.0 |
231 | 231 | */ |
232 | - public static function linkToRemote( $service ) { |
|
232 | + public static function linkToRemote($service) { |
|
233 | 233 | $urlGenerator = \OC::$server->getURLGenerator(); |
234 | - $remoteBase = $urlGenerator->linkTo('', 'remote.php') . '/' . $service; |
|
234 | + $remoteBase = $urlGenerator->linkTo('', 'remote.php').'/'.$service; |
|
235 | 235 | return $urlGenerator->getAbsoluteURL( |
236 | - $remoteBase . (($service[strlen($service) - 1] != '/') ? '/' : '') |
|
236 | + $remoteBase.(($service[strlen($service) - 1] != '/') ? '/' : '') |
|
237 | 237 | ); |
238 | 238 | } |
239 | 239 | |
@@ -361,7 +361,7 @@ discard block |
||
361 | 361 | * @since 4.5.0 |
362 | 362 | */ |
363 | 363 | public static function callRegister() { |
364 | - if(self::$token === '') { |
|
364 | + if (self::$token === '') { |
|
365 | 365 | self::$token = \OC::$server->getCsrfTokenManager()->getToken()->getEncryptedValue(); |
366 | 366 | } |
367 | 367 | return self::$token; |
@@ -373,7 +373,7 @@ discard block |
||
373 | 373 | * @deprecated 9.0.0 Use annotations based on the app framework. |
374 | 374 | */ |
375 | 375 | public static function callCheck() { |
376 | - if(!\OC::$server->getRequest()->passesStrictCookieCheck()) { |
|
376 | + if (!\OC::$server->getRequest()->passesStrictCookieCheck()) { |
|
377 | 377 | header('Location: '.\OC::$WEBROOT); |
378 | 378 | exit(); |
379 | 379 | } |
@@ -554,7 +554,7 @@ discard block |
||
554 | 554 | */ |
555 | 555 | public static function needUpgrade() { |
556 | 556 | if (!isset(self::$needUpgradeCache)) { |
557 | - self::$needUpgradeCache=\OC_Util::needUpgrade(\OC::$server->getSystemConfig()); |
|
557 | + self::$needUpgradeCache = \OC_Util::needUpgrade(\OC::$server->getSystemConfig()); |
|
558 | 558 | } |
559 | 559 | return self::$needUpgradeCache; |
560 | 560 | } |
@@ -36,134 +36,134 @@ |
||
36 | 36 | * https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md#3-psrlogloggerinterface |
37 | 37 | */ |
38 | 38 | interface ILogger { |
39 | - /** |
|
40 | - * @since 14.0.0 |
|
41 | - */ |
|
42 | - const DEBUG=0; |
|
43 | - /** |
|
44 | - * @since 14.0.0 |
|
45 | - */ |
|
46 | - const INFO=1; |
|
47 | - /** |
|
48 | - * @since 14.0.0 |
|
49 | - */ |
|
50 | - const WARN=2; |
|
51 | - /** |
|
52 | - * @since 14.0.0 |
|
53 | - */ |
|
54 | - const ERROR=3; |
|
55 | - /** |
|
56 | - * @since 14.0.0 |
|
57 | - */ |
|
58 | - const FATAL=4; |
|
39 | + /** |
|
40 | + * @since 14.0.0 |
|
41 | + */ |
|
42 | + const DEBUG=0; |
|
43 | + /** |
|
44 | + * @since 14.0.0 |
|
45 | + */ |
|
46 | + const INFO=1; |
|
47 | + /** |
|
48 | + * @since 14.0.0 |
|
49 | + */ |
|
50 | + const WARN=2; |
|
51 | + /** |
|
52 | + * @since 14.0.0 |
|
53 | + */ |
|
54 | + const ERROR=3; |
|
55 | + /** |
|
56 | + * @since 14.0.0 |
|
57 | + */ |
|
58 | + const FATAL=4; |
|
59 | 59 | |
60 | - /** |
|
61 | - * System is unusable. |
|
62 | - * |
|
63 | - * @param string $message |
|
64 | - * @param array $context |
|
65 | - * @return null |
|
66 | - * @since 7.0.0 |
|
67 | - */ |
|
68 | - public function emergency(string $message, array $context = []); |
|
60 | + /** |
|
61 | + * System is unusable. |
|
62 | + * |
|
63 | + * @param string $message |
|
64 | + * @param array $context |
|
65 | + * @return null |
|
66 | + * @since 7.0.0 |
|
67 | + */ |
|
68 | + public function emergency(string $message, array $context = []); |
|
69 | 69 | |
70 | - /** |
|
71 | - * Action must be taken immediately. |
|
72 | - * |
|
73 | - * @param string $message |
|
74 | - * @param array $context |
|
75 | - * @return null |
|
76 | - * @since 7.0.0 |
|
77 | - */ |
|
78 | - public function alert(string $message, array $context = []); |
|
70 | + /** |
|
71 | + * Action must be taken immediately. |
|
72 | + * |
|
73 | + * @param string $message |
|
74 | + * @param array $context |
|
75 | + * @return null |
|
76 | + * @since 7.0.0 |
|
77 | + */ |
|
78 | + public function alert(string $message, array $context = []); |
|
79 | 79 | |
80 | - /** |
|
81 | - * Critical conditions. |
|
82 | - * |
|
83 | - * @param string $message |
|
84 | - * @param array $context |
|
85 | - * @return null |
|
86 | - * @since 7.0.0 |
|
87 | - */ |
|
88 | - public function critical(string $message, array $context = []); |
|
80 | + /** |
|
81 | + * Critical conditions. |
|
82 | + * |
|
83 | + * @param string $message |
|
84 | + * @param array $context |
|
85 | + * @return null |
|
86 | + * @since 7.0.0 |
|
87 | + */ |
|
88 | + public function critical(string $message, array $context = []); |
|
89 | 89 | |
90 | - /** |
|
91 | - * Runtime errors that do not require immediate action but should typically |
|
92 | - * be logged and monitored. |
|
93 | - * |
|
94 | - * @param string $message |
|
95 | - * @param array $context |
|
96 | - * @return null |
|
97 | - * @since 7.0.0 |
|
98 | - */ |
|
99 | - public function error(string $message, array $context = []); |
|
90 | + /** |
|
91 | + * Runtime errors that do not require immediate action but should typically |
|
92 | + * be logged and monitored. |
|
93 | + * |
|
94 | + * @param string $message |
|
95 | + * @param array $context |
|
96 | + * @return null |
|
97 | + * @since 7.0.0 |
|
98 | + */ |
|
99 | + public function error(string $message, array $context = []); |
|
100 | 100 | |
101 | - /** |
|
102 | - * Exceptional occurrences that are not errors. |
|
103 | - * |
|
104 | - * @param string $message |
|
105 | - * @param array $context |
|
106 | - * @return null |
|
107 | - * @since 7.0.0 |
|
108 | - */ |
|
109 | - public function warning(string $message, array $context = []); |
|
101 | + /** |
|
102 | + * Exceptional occurrences that are not errors. |
|
103 | + * |
|
104 | + * @param string $message |
|
105 | + * @param array $context |
|
106 | + * @return null |
|
107 | + * @since 7.0.0 |
|
108 | + */ |
|
109 | + public function warning(string $message, array $context = []); |
|
110 | 110 | |
111 | - /** |
|
112 | - * Normal but significant events. |
|
113 | - * |
|
114 | - * @param string $message |
|
115 | - * @param array $context |
|
116 | - * @return null |
|
117 | - * @since 7.0.0 |
|
118 | - */ |
|
119 | - public function notice(string $message, array $context = []); |
|
111 | + /** |
|
112 | + * Normal but significant events. |
|
113 | + * |
|
114 | + * @param string $message |
|
115 | + * @param array $context |
|
116 | + * @return null |
|
117 | + * @since 7.0.0 |
|
118 | + */ |
|
119 | + public function notice(string $message, array $context = []); |
|
120 | 120 | |
121 | - /** |
|
122 | - * Interesting events. |
|
123 | - * |
|
124 | - * @param string $message |
|
125 | - * @param array $context |
|
126 | - * @return null |
|
127 | - * @since 7.0.0 |
|
128 | - */ |
|
129 | - public function info(string $message, array $context = []); |
|
121 | + /** |
|
122 | + * Interesting events. |
|
123 | + * |
|
124 | + * @param string $message |
|
125 | + * @param array $context |
|
126 | + * @return null |
|
127 | + * @since 7.0.0 |
|
128 | + */ |
|
129 | + public function info(string $message, array $context = []); |
|
130 | 130 | |
131 | - /** |
|
132 | - * Detailed debug information. |
|
133 | - * |
|
134 | - * @param string $message |
|
135 | - * @param array $context |
|
136 | - * @return null |
|
137 | - * @since 7.0.0 |
|
138 | - */ |
|
139 | - public function debug(string $message, array $context = []); |
|
131 | + /** |
|
132 | + * Detailed debug information. |
|
133 | + * |
|
134 | + * @param string $message |
|
135 | + * @param array $context |
|
136 | + * @return null |
|
137 | + * @since 7.0.0 |
|
138 | + */ |
|
139 | + public function debug(string $message, array $context = []); |
|
140 | 140 | |
141 | - /** |
|
142 | - * Logs with an arbitrary level. |
|
143 | - * |
|
144 | - * @param int $level |
|
145 | - * @param string $message |
|
146 | - * @param array $context |
|
147 | - * @return mixed |
|
148 | - * @since 7.0.0 |
|
149 | - */ |
|
150 | - public function log(int $level, string $message, array $context = []); |
|
141 | + /** |
|
142 | + * Logs with an arbitrary level. |
|
143 | + * |
|
144 | + * @param int $level |
|
145 | + * @param string $message |
|
146 | + * @param array $context |
|
147 | + * @return mixed |
|
148 | + * @since 7.0.0 |
|
149 | + */ |
|
150 | + public function log(int $level, string $message, array $context = []); |
|
151 | 151 | |
152 | - /** |
|
153 | - * Logs an exception very detailed |
|
154 | - * An additional message can we written to the log by adding it to the |
|
155 | - * context. |
|
156 | - * |
|
157 | - * <code> |
|
158 | - * $logger->logException($ex, [ |
|
159 | - * 'message' => 'Exception during background job execution' |
|
160 | - * ]); |
|
161 | - * </code> |
|
162 | - * |
|
163 | - * @param \Exception|\Throwable $exception |
|
164 | - * @param array $context |
|
165 | - * @return void |
|
166 | - * @since 8.2.0 |
|
167 | - */ |
|
168 | - public function logException(\Throwable $exception, array $context = []); |
|
152 | + /** |
|
153 | + * Logs an exception very detailed |
|
154 | + * An additional message can we written to the log by adding it to the |
|
155 | + * context. |
|
156 | + * |
|
157 | + * <code> |
|
158 | + * $logger->logException($ex, [ |
|
159 | + * 'message' => 'Exception during background job execution' |
|
160 | + * ]); |
|
161 | + * </code> |
|
162 | + * |
|
163 | + * @param \Exception|\Throwable $exception |
|
164 | + * @param array $context |
|
165 | + * @return void |
|
166 | + * @since 8.2.0 |
|
167 | + */ |
|
168 | + public function logException(\Throwable $exception, array $context = []); |
|
169 | 169 | } |
@@ -39,23 +39,23 @@ |
||
39 | 39 | /** |
40 | 40 | * @since 14.0.0 |
41 | 41 | */ |
42 | - const DEBUG=0; |
|
42 | + const DEBUG = 0; |
|
43 | 43 | /** |
44 | 44 | * @since 14.0.0 |
45 | 45 | */ |
46 | - const INFO=1; |
|
46 | + const INFO = 1; |
|
47 | 47 | /** |
48 | 48 | * @since 14.0.0 |
49 | 49 | */ |
50 | - const WARN=2; |
|
50 | + const WARN = 2; |
|
51 | 51 | /** |
52 | 52 | * @since 14.0.0 |
53 | 53 | */ |
54 | - const ERROR=3; |
|
54 | + const ERROR = 3; |
|
55 | 55 | /** |
56 | 56 | * @since 14.0.0 |
57 | 57 | */ |
58 | - const FATAL=4; |
|
58 | + const FATAL = 4; |
|
59 | 59 | |
60 | 60 | /** |
61 | 61 | * System is unusable. |
@@ -32,30 +32,30 @@ |
||
32 | 32 | |
33 | 33 | $lastConfirm = (int) \OC::$server->getSession()->get('last-password-confirm'); |
34 | 34 | if ($lastConfirm < (time() - 30 * 60 + 15)) { // allow 15 seconds delay |
35 | - $l = \OC::$server->getL10N('core'); |
|
36 | - OC_JSON::error(array( 'data' => array( 'message' => $l->t('Password confirmation is required')))); |
|
37 | - exit(); |
|
35 | + $l = \OC::$server->getL10N('core'); |
|
36 | + OC_JSON::error(array( 'data' => array( 'message' => $l->t('Password confirmation is required')))); |
|
37 | + exit(); |
|
38 | 38 | } |
39 | 39 | |
40 | 40 | $groups = isset($_POST['groups']) ? (array)$_POST['groups'] : []; |
41 | 41 | $appIds = isset($_POST['appIds']) ? (array)$_POST['appIds'] : []; |
42 | 42 | |
43 | 43 | try { |
44 | - $updateRequired = false; |
|
45 | - foreach($appIds as $appId) { |
|
46 | - $app = new OC_App(); |
|
47 | - $appId = OC_App::cleanAppId($appId); |
|
48 | - $app->enable($appId, $groups); |
|
49 | - if(\OC_App::shouldUpgrade($appId)) { |
|
50 | - $updateRequired = true; |
|
51 | - } |
|
52 | - } |
|
44 | + $updateRequired = false; |
|
45 | + foreach($appIds as $appId) { |
|
46 | + $app = new OC_App(); |
|
47 | + $appId = OC_App::cleanAppId($appId); |
|
48 | + $app->enable($appId, $groups); |
|
49 | + if(\OC_App::shouldUpgrade($appId)) { |
|
50 | + $updateRequired = true; |
|
51 | + } |
|
52 | + } |
|
53 | 53 | |
54 | - OC_JSON::success(['data' => ['update_required' => $updateRequired]]); |
|
54 | + OC_JSON::success(['data' => ['update_required' => $updateRequired]]); |
|
55 | 55 | } catch (Exception $e) { |
56 | - \OC::$server->getLogger()->logException($e, [ |
|
57 | - 'level' => ILogger::DEBUG, |
|
58 | - 'app' => 'core', |
|
59 | - ]); |
|
60 | - OC_JSON::error(array("data" => array("message" => $e->getMessage()) )); |
|
56 | + \OC::$server->getLogger()->logException($e, [ |
|
57 | + 'level' => ILogger::DEBUG, |
|
58 | + 'app' => 'core', |
|
59 | + ]); |
|
60 | + OC_JSON::error(array("data" => array("message" => $e->getMessage()) )); |
|
61 | 61 | } |
@@ -33,20 +33,20 @@ discard block |
||
33 | 33 | $lastConfirm = (int) \OC::$server->getSession()->get('last-password-confirm'); |
34 | 34 | if ($lastConfirm < (time() - 30 * 60 + 15)) { // allow 15 seconds delay |
35 | 35 | $l = \OC::$server->getL10N('core'); |
36 | - OC_JSON::error(array( 'data' => array( 'message' => $l->t('Password confirmation is required')))); |
|
36 | + OC_JSON::error(array('data' => array('message' => $l->t('Password confirmation is required')))); |
|
37 | 37 | exit(); |
38 | 38 | } |
39 | 39 | |
40 | -$groups = isset($_POST['groups']) ? (array)$_POST['groups'] : []; |
|
41 | -$appIds = isset($_POST['appIds']) ? (array)$_POST['appIds'] : []; |
|
40 | +$groups = isset($_POST['groups']) ? (array) $_POST['groups'] : []; |
|
41 | +$appIds = isset($_POST['appIds']) ? (array) $_POST['appIds'] : []; |
|
42 | 42 | |
43 | 43 | try { |
44 | 44 | $updateRequired = false; |
45 | - foreach($appIds as $appId) { |
|
45 | + foreach ($appIds as $appId) { |
|
46 | 46 | $app = new OC_App(); |
47 | 47 | $appId = OC_App::cleanAppId($appId); |
48 | 48 | $app->enable($appId, $groups); |
49 | - if(\OC_App::shouldUpgrade($appId)) { |
|
49 | + if (\OC_App::shouldUpgrade($appId)) { |
|
50 | 50 | $updateRequired = true; |
51 | 51 | } |
52 | 52 | } |
@@ -57,5 +57,5 @@ discard block |
||
57 | 57 | 'level' => ILogger::DEBUG, |
58 | 58 | 'app' => 'core', |
59 | 59 | ]); |
60 | - OC_JSON::error(array("data" => array("message" => $e->getMessage()) )); |
|
60 | + OC_JSON::error(array("data" => array("message" => $e->getMessage()))); |
|
61 | 61 | } |
@@ -71,948 +71,948 @@ |
||
71 | 71 | * @package OC\Settings\Controller |
72 | 72 | */ |
73 | 73 | class UsersController extends Controller { |
74 | - /** @var IL10N */ |
|
75 | - private $l10n; |
|
76 | - /** @var IUserSession */ |
|
77 | - private $userSession; |
|
78 | - /** @var bool */ |
|
79 | - private $isAdmin; |
|
80 | - /** @var IUserManager */ |
|
81 | - private $userManager; |
|
82 | - /** @var IGroupManager */ |
|
83 | - private $groupManager; |
|
84 | - /** @var IConfig */ |
|
85 | - private $config; |
|
86 | - /** @var ILogger */ |
|
87 | - private $log; |
|
88 | - /** @var IMailer */ |
|
89 | - private $mailer; |
|
90 | - /** @var bool contains the state of the encryption app */ |
|
91 | - private $isEncryptionAppEnabled; |
|
92 | - /** @var bool contains the state of the admin recovery setting */ |
|
93 | - private $isRestoreEnabled = false; |
|
94 | - /** @var IAppManager */ |
|
95 | - private $appManager; |
|
96 | - /** @var IAvatarManager */ |
|
97 | - private $avatarManager; |
|
98 | - /** @var AccountManager */ |
|
99 | - private $accountManager; |
|
100 | - /** @var ISecureRandom */ |
|
101 | - private $secureRandom; |
|
102 | - /** @var NewUserMailHelper */ |
|
103 | - private $newUserMailHelper; |
|
104 | - /** @var Manager */ |
|
105 | - private $keyManager; |
|
106 | - /** @var IJobList */ |
|
107 | - private $jobList; |
|
108 | - |
|
109 | - /** @var IUserMountCache */ |
|
110 | - private $userMountCache; |
|
111 | - |
|
112 | - /** @var IManager */ |
|
113 | - private $encryptionManager; |
|
114 | - |
|
115 | - public function __construct(string $appName, |
|
116 | - IRequest $request, |
|
117 | - IUserManager $userManager, |
|
118 | - IGroupManager $groupManager, |
|
119 | - IUserSession $userSession, |
|
120 | - IConfig $config, |
|
121 | - bool $isAdmin, |
|
122 | - IL10N $l10n, |
|
123 | - ILogger $log, |
|
124 | - IMailer $mailer, |
|
125 | - IURLGenerator $urlGenerator, |
|
126 | - IAppManager $appManager, |
|
127 | - IAvatarManager $avatarManager, |
|
128 | - AccountManager $accountManager, |
|
129 | - ISecureRandom $secureRandom, |
|
130 | - NewUserMailHelper $newUserMailHelper, |
|
131 | - Manager $keyManager, |
|
132 | - IJobList $jobList, |
|
133 | - IUserMountCache $userMountCache, |
|
134 | - IManager $encryptionManager) { |
|
135 | - parent::__construct($appName, $request); |
|
136 | - $this->userManager = $userManager; |
|
137 | - $this->groupManager = $groupManager; |
|
138 | - $this->userSession = $userSession; |
|
139 | - $this->config = $config; |
|
140 | - $this->isAdmin = $isAdmin; |
|
141 | - $this->l10n = $l10n; |
|
142 | - $this->log = $log; |
|
143 | - $this->mailer = $mailer; |
|
144 | - $this->appManager = $appManager; |
|
145 | - $this->avatarManager = $avatarManager; |
|
146 | - $this->accountManager = $accountManager; |
|
147 | - $this->secureRandom = $secureRandom; |
|
148 | - $this->newUserMailHelper = $newUserMailHelper; |
|
149 | - $this->keyManager = $keyManager; |
|
150 | - $this->jobList = $jobList; |
|
151 | - $this->userMountCache = $userMountCache; |
|
152 | - $this->encryptionManager = $encryptionManager; |
|
153 | - |
|
154 | - // check for encryption state - TODO see formatUserForIndex |
|
155 | - $this->isEncryptionAppEnabled = $appManager->isEnabledForUser('encryption'); |
|
156 | - if ($this->isEncryptionAppEnabled) { |
|
157 | - // putting this directly in empty is possible in PHP 5.5+ |
|
158 | - $result = $config->getAppValue('encryption', 'recoveryAdminEnabled', '0'); |
|
159 | - $this->isRestoreEnabled = !empty($result); |
|
160 | - } |
|
161 | - } |
|
162 | - |
|
163 | - /** |
|
164 | - * @param IUser $user |
|
165 | - * @param array|null $userGroups |
|
166 | - * @return array |
|
167 | - */ |
|
168 | - private function formatUserForIndex(IUser $user, array $userGroups = null): array { |
|
169 | - |
|
170 | - // TODO: eliminate this encryption specific code below and somehow |
|
171 | - // hook in additional user info from other apps |
|
172 | - |
|
173 | - // recovery isn't possible if admin or user has it disabled and encryption |
|
174 | - // is enabled - so we eliminate the else paths in the conditional tree |
|
175 | - // below |
|
176 | - $restorePossible = false; |
|
177 | - |
|
178 | - if ($this->isEncryptionAppEnabled) { |
|
179 | - if ($this->isRestoreEnabled) { |
|
180 | - // check for the users recovery setting |
|
181 | - $recoveryMode = $this->config->getUserValue($user->getUID(), 'encryption', 'recoveryEnabled', '0'); |
|
182 | - // method call inside empty is possible with PHP 5.5+ |
|
183 | - $recoveryModeEnabled = !empty($recoveryMode); |
|
184 | - if ($recoveryModeEnabled) { |
|
185 | - // user also has recovery mode enabled |
|
186 | - $restorePossible = true; |
|
187 | - } |
|
188 | - } else { |
|
189 | - $modules = $this->encryptionManager->getEncryptionModules(); |
|
190 | - $restorePossible = true; |
|
191 | - foreach ($modules as $id => $module) { |
|
192 | - /* @var IEncryptionModule $instance */ |
|
193 | - $instance = call_user_func($module['callback']); |
|
194 | - if ($instance->needDetailedAccessList()) { |
|
195 | - $restorePossible = false; |
|
196 | - break; |
|
197 | - } |
|
198 | - } |
|
199 | - } |
|
200 | - } else { |
|
201 | - // recovery is possible if encryption is disabled (plain files are |
|
202 | - // available) |
|
203 | - $restorePossible = true; |
|
204 | - } |
|
205 | - |
|
206 | - $subAdminGroups = $this->groupManager->getSubAdmin()->getSubAdminsGroupsName($user); |
|
207 | - |
|
208 | - $displayName = $user->getEMailAddress(); |
|
209 | - if (is_null($displayName)) { |
|
210 | - $displayName = ''; |
|
211 | - } |
|
212 | - |
|
213 | - $avatarAvailable = false; |
|
214 | - try { |
|
215 | - $avatarAvailable = $this->avatarManager->getAvatar($user->getUID())->exists(); |
|
216 | - } catch (\Exception $e) { |
|
217 | - //No avatar yet |
|
218 | - } |
|
219 | - |
|
220 | - return [ |
|
221 | - 'name' => $user->getUID(), |
|
222 | - 'displayname' => $user->getDisplayName(), |
|
223 | - 'groups' => empty($userGroups) ? $this->groupManager->getUserGroupNames($user) : $userGroups, |
|
224 | - 'subadmin' => $subAdminGroups, |
|
225 | - 'quota' => $user->getQuota(), |
|
226 | - 'quota_bytes' => Util::computerFileSize($user->getQuota()), |
|
227 | - 'storageLocation' => $user->getHome(), |
|
228 | - 'lastLogin' => $user->getLastLogin() * 1000, |
|
229 | - 'backend' => $user->getBackendClassName(), |
|
230 | - 'email' => $displayName, |
|
231 | - 'isRestoreDisabled' => !$restorePossible, |
|
232 | - 'isAvatarAvailable' => $avatarAvailable, |
|
233 | - 'isEnabled' => $user->isEnabled(), |
|
234 | - ]; |
|
235 | - } |
|
236 | - |
|
237 | - /** |
|
238 | - * @param array $userIDs Array with schema [$uid => $displayName] |
|
239 | - * @return IUser[] |
|
240 | - */ |
|
241 | - private function getUsersForUID(array $userIDs): array { |
|
242 | - $users = []; |
|
243 | - foreach ($userIDs as $uid => $displayName) { |
|
244 | - $users[$uid] = $this->userManager->get($uid); |
|
245 | - } |
|
246 | - return $users; |
|
247 | - } |
|
248 | - |
|
249 | - /** |
|
250 | - * @NoAdminRequired |
|
251 | - * |
|
252 | - * @param int $offset |
|
253 | - * @param int $limit |
|
254 | - * @param string $gid GID to filter for |
|
255 | - * @param string $pattern Pattern to search for in the username |
|
256 | - * @param string $backend Backend to filter for (class-name) |
|
257 | - * @return DataResponse |
|
258 | - * |
|
259 | - * TODO: Tidy up and write unit tests - code is mainly static method calls |
|
260 | - */ |
|
261 | - public function index(int $offset = 0, int $limit = 10, string $gid = '', string $pattern = '', string $backend = ''): DataResponse { |
|
262 | - // Remove backends |
|
263 | - if (!empty($backend)) { |
|
264 | - $activeBackends = $this->userManager->getBackends(); |
|
265 | - $this->userManager->clearBackends(); |
|
266 | - foreach ($activeBackends as $singleActiveBackend) { |
|
267 | - if ($backend === get_class($singleActiveBackend)) { |
|
268 | - $this->userManager->registerBackend($singleActiveBackend); |
|
269 | - break; |
|
270 | - } |
|
271 | - } |
|
272 | - } |
|
273 | - |
|
274 | - $userObjects = []; |
|
275 | - $users = []; |
|
276 | - if ($this->isAdmin) { |
|
277 | - if ($gid !== '' && $gid !== '_disabledUsers' && $gid !== '_everyone') { |
|
278 | - $batch = $this->getUsersForUID($this->groupManager->displayNamesInGroup($gid, $pattern, $limit, $offset)); |
|
279 | - } else { |
|
280 | - $batch = $this->userManager->search($pattern, $limit, $offset); |
|
281 | - } |
|
282 | - |
|
283 | - foreach ($batch as $user) { |
|
284 | - if (($gid !== '_disabledUsers' && $user->isEnabled()) || |
|
285 | - ($gid === '_disabledUsers' && !$user->isEnabled()) |
|
286 | - ) { |
|
287 | - $userObjects[] = $user; |
|
288 | - $users[] = $this->formatUserForIndex($user); |
|
289 | - } |
|
290 | - } |
|
291 | - |
|
292 | - } else { |
|
293 | - $subAdminOfGroups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($this->userSession->getUser()); |
|
294 | - // New class returns IGroup[] so convert back |
|
295 | - $gids = []; |
|
296 | - foreach ($subAdminOfGroups as $group) { |
|
297 | - $gids[] = $group->getGID(); |
|
298 | - } |
|
299 | - $subAdminOfGroups = $gids; |
|
300 | - |
|
301 | - // Set the $gid parameter to an empty value if the subadmin has no rights to access a specific group |
|
302 | - if ($gid !== '' && $gid !== '_disabledUsers' && !in_array($gid, $subAdminOfGroups)) { |
|
303 | - $gid = ''; |
|
304 | - } |
|
305 | - |
|
306 | - // Batch all groups the user is subadmin of when a group is specified |
|
307 | - $batch = []; |
|
308 | - if ($gid !== '' && $gid !== '_disabledUsers' && $gid !== '_everyone') { |
|
309 | - $batch = $this->groupManager->displayNamesInGroup($gid, $pattern, $limit, $offset); |
|
310 | - } else { |
|
311 | - foreach ($subAdminOfGroups as $group) { |
|
312 | - $groupUsers = $this->groupManager->displayNamesInGroup($group, $pattern, $limit, $offset); |
|
313 | - |
|
314 | - foreach ($groupUsers as $uid => $displayName) { |
|
315 | - $batch[$uid] = $displayName; |
|
316 | - } |
|
317 | - } |
|
318 | - } |
|
319 | - $batch = $this->getUsersForUID($batch); |
|
320 | - |
|
321 | - foreach ($batch as $user) { |
|
322 | - // Only add the groups, this user is a subadmin of |
|
323 | - $userGroups = array_values(array_intersect( |
|
324 | - $this->groupManager->getUserGroupIds($user), |
|
325 | - $subAdminOfGroups |
|
326 | - )); |
|
327 | - if (($gid !== '_disabledUsers' && $user->isEnabled()) || |
|
328 | - ($gid === '_disabledUsers' && !$user->isEnabled()) |
|
329 | - ) { |
|
330 | - $userObjects[] = $user; |
|
331 | - $users[] = $this->formatUserForIndex($user, $userGroups); |
|
332 | - } |
|
333 | - } |
|
334 | - } |
|
335 | - |
|
336 | - $usedSpace = $this->userMountCache->getUsedSpaceForUsers($userObjects); |
|
337 | - |
|
338 | - foreach ($users as &$userData) { |
|
339 | - $userData['size'] = isset($usedSpace[$userData['name']]) ? $usedSpace[$userData['name']] : 0; |
|
340 | - } |
|
341 | - |
|
342 | - return new DataResponse($users); |
|
343 | - } |
|
344 | - |
|
345 | - /** |
|
346 | - * @NoAdminRequired |
|
347 | - * @PasswordConfirmationRequired |
|
348 | - * |
|
349 | - * @param string $username |
|
350 | - * @param string $password |
|
351 | - * @param array $groups |
|
352 | - * @param string $email |
|
353 | - * @return DataResponse |
|
354 | - */ |
|
355 | - public function create(string $username, string $password, array $groups = [], $email = ''): DataResponse { |
|
356 | - if ($email !== '' && !$this->mailer->validateMailAddress($email)) { |
|
357 | - return new DataResponse( |
|
358 | - [ |
|
359 | - 'message' => $this->l10n->t('Invalid mail address') |
|
360 | - ], |
|
361 | - Http::STATUS_UNPROCESSABLE_ENTITY |
|
362 | - ); |
|
363 | - } |
|
364 | - |
|
365 | - $currentUser = $this->userSession->getUser(); |
|
366 | - |
|
367 | - if (!$this->isAdmin) { |
|
368 | - if (!empty($groups)) { |
|
369 | - foreach ($groups as $key => $group) { |
|
370 | - $groupObject = $this->groupManager->get($group); |
|
371 | - if ($groupObject === null) { |
|
372 | - unset($groups[$key]); |
|
373 | - continue; |
|
374 | - } |
|
375 | - |
|
376 | - if (!$this->groupManager->getSubAdmin()->isSubAdminOfGroup($currentUser, $groupObject)) { |
|
377 | - unset($groups[$key]); |
|
378 | - } |
|
379 | - } |
|
380 | - } |
|
381 | - |
|
382 | - if (empty($groups)) { |
|
383 | - return new DataResponse( |
|
384 | - [ |
|
385 | - 'message' => $this->l10n->t('No valid group selected'), |
|
386 | - ], |
|
387 | - Http::STATUS_FORBIDDEN |
|
388 | - ); |
|
389 | - } |
|
390 | - } |
|
391 | - |
|
392 | - if ($this->userManager->userExists($username)) { |
|
393 | - return new DataResponse( |
|
394 | - [ |
|
395 | - 'message' => $this->l10n->t('A user with that name already exists.') |
|
396 | - ], |
|
397 | - Http::STATUS_CONFLICT |
|
398 | - ); |
|
399 | - } |
|
400 | - |
|
401 | - $generatePasswordResetToken = false; |
|
402 | - if ($password === '') { |
|
403 | - if ($email === '') { |
|
404 | - return new DataResponse( |
|
405 | - [ |
|
406 | - 'message' => $this->l10n->t('To send a password link to the user an email address is required.') |
|
407 | - ], |
|
408 | - Http::STATUS_UNPROCESSABLE_ENTITY |
|
409 | - ); |
|
410 | - } |
|
411 | - |
|
412 | - $password = $this->secureRandom->generate(30); |
|
413 | - // Make sure we pass the password_policy |
|
414 | - $password .= $this->secureRandom->generate(2, '$!.,;:-~+*[]{}()'); |
|
415 | - $generatePasswordResetToken = true; |
|
416 | - } |
|
417 | - |
|
418 | - try { |
|
419 | - $user = $this->userManager->createUser($username, $password); |
|
420 | - } catch (\Exception $exception) { |
|
421 | - $message = $exception->getMessage(); |
|
422 | - if ($exception instanceof HintException && $exception->getHint()) { |
|
423 | - $message = $exception->getHint(); |
|
424 | - } |
|
425 | - if (!$message) { |
|
426 | - $message = $this->l10n->t('Unable to create user.'); |
|
427 | - } |
|
428 | - return new DataResponse( |
|
429 | - [ |
|
430 | - 'message' => (string)$message, |
|
431 | - ], |
|
432 | - Http::STATUS_FORBIDDEN |
|
433 | - ); |
|
434 | - } |
|
435 | - |
|
436 | - if ($user instanceof IUser) { |
|
437 | - if ($groups !== null) { |
|
438 | - foreach ($groups as $groupName) { |
|
439 | - $group = $this->groupManager->get($groupName); |
|
440 | - |
|
441 | - if (empty($group)) { |
|
442 | - $group = $this->groupManager->createGroup($groupName); |
|
443 | - } |
|
444 | - $group->addUser($user); |
|
445 | - } |
|
446 | - } |
|
447 | - /** |
|
448 | - * Send new user mail only if a mail is set |
|
449 | - */ |
|
450 | - if ($email !== '') { |
|
451 | - $user->setEMailAddress($email); |
|
452 | - try { |
|
453 | - $emailTemplate = $this->newUserMailHelper->generateTemplate($user, $generatePasswordResetToken); |
|
454 | - $this->newUserMailHelper->sendMail($user, $emailTemplate); |
|
455 | - } catch (\Exception $e) { |
|
456 | - $this->log->logException($e, [ |
|
457 | - 'message' => "Can't send new user mail to $email", |
|
458 | - 'level' => ILogger::ERROR, |
|
459 | - 'app' => 'settings', |
|
460 | - ]); |
|
461 | - } |
|
462 | - } |
|
463 | - // fetch users groups |
|
464 | - $userGroups = $this->groupManager->getUserGroupNames($user); |
|
465 | - |
|
466 | - return new DataResponse( |
|
467 | - $this->formatUserForIndex($user, $userGroups), |
|
468 | - Http::STATUS_CREATED |
|
469 | - ); |
|
470 | - } |
|
471 | - |
|
472 | - return new DataResponse( |
|
473 | - [ |
|
474 | - 'message' => $this->l10n->t('Unable to create user.') |
|
475 | - ], |
|
476 | - Http::STATUS_FORBIDDEN |
|
477 | - ); |
|
478 | - |
|
479 | - } |
|
480 | - |
|
481 | - /** |
|
482 | - * @NoAdminRequired |
|
483 | - * @PasswordConfirmationRequired |
|
484 | - * |
|
485 | - * @param string $id |
|
486 | - * @return DataResponse |
|
487 | - */ |
|
488 | - public function destroy(string $id): DataResponse { |
|
489 | - $userId = $this->userSession->getUser()->getUID(); |
|
490 | - $user = $this->userManager->get($id); |
|
491 | - |
|
492 | - if ($userId === $id) { |
|
493 | - return new DataResponse( |
|
494 | - [ |
|
495 | - 'status' => 'error', |
|
496 | - 'data' => [ |
|
497 | - 'message' => $this->l10n->t('Unable to delete user.') |
|
498 | - ] |
|
499 | - ], |
|
500 | - Http::STATUS_FORBIDDEN |
|
501 | - ); |
|
502 | - } |
|
503 | - |
|
504 | - if (!$this->isAdmin && !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)) { |
|
505 | - return new DataResponse( |
|
506 | - [ |
|
507 | - 'status' => 'error', |
|
508 | - 'data' => [ |
|
509 | - 'message' => $this->l10n->t('Authentication error') |
|
510 | - ] |
|
511 | - ], |
|
512 | - Http::STATUS_FORBIDDEN |
|
513 | - ); |
|
514 | - } |
|
515 | - |
|
516 | - if ($user && $user->delete()) { |
|
517 | - return new DataResponse( |
|
518 | - [ |
|
519 | - 'status' => 'success', |
|
520 | - 'data' => [ |
|
521 | - 'username' => $id |
|
522 | - ] |
|
523 | - ], |
|
524 | - Http::STATUS_NO_CONTENT |
|
525 | - ); |
|
526 | - } |
|
527 | - |
|
528 | - return new DataResponse( |
|
529 | - [ |
|
530 | - 'status' => 'error', |
|
531 | - 'data' => [ |
|
532 | - 'message' => $this->l10n->t('Unable to delete user.') |
|
533 | - ] |
|
534 | - ], |
|
535 | - Http::STATUS_FORBIDDEN |
|
536 | - ); |
|
537 | - } |
|
538 | - |
|
539 | - /** |
|
540 | - * @NoAdminRequired |
|
541 | - * |
|
542 | - * @param string $id |
|
543 | - * @param int $enabled |
|
544 | - * @return DataResponse |
|
545 | - */ |
|
546 | - public function setEnabled(string $id, int $enabled): DataResponse { |
|
547 | - $enabled = (bool)$enabled; |
|
548 | - if ($enabled) { |
|
549 | - $errorMsgGeneral = $this->l10n->t('Error while enabling user.'); |
|
550 | - } else { |
|
551 | - $errorMsgGeneral = $this->l10n->t('Error while disabling user.'); |
|
552 | - } |
|
553 | - |
|
554 | - $userId = $this->userSession->getUser()->getUID(); |
|
555 | - $user = $this->userManager->get($id); |
|
556 | - |
|
557 | - if ($userId === $id) { |
|
558 | - return new DataResponse( |
|
559 | - [ |
|
560 | - 'status' => 'error', |
|
561 | - 'data' => [ |
|
562 | - 'message' => $errorMsgGeneral |
|
563 | - ] |
|
564 | - ], Http::STATUS_FORBIDDEN |
|
565 | - ); |
|
566 | - } |
|
567 | - |
|
568 | - if ($user) { |
|
569 | - if (!$this->isAdmin && !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)) { |
|
570 | - return new DataResponse( |
|
571 | - [ |
|
572 | - 'status' => 'error', |
|
573 | - 'data' => [ |
|
574 | - 'message' => $this->l10n->t('Authentication error') |
|
575 | - ] |
|
576 | - ], |
|
577 | - Http::STATUS_FORBIDDEN |
|
578 | - ); |
|
579 | - } |
|
580 | - |
|
581 | - $user->setEnabled($enabled); |
|
582 | - return new DataResponse( |
|
583 | - [ |
|
584 | - 'status' => 'success', |
|
585 | - 'data' => [ |
|
586 | - 'username' => $id, |
|
587 | - 'enabled' => $enabled |
|
588 | - ] |
|
589 | - ] |
|
590 | - ); |
|
591 | - } else { |
|
592 | - return new DataResponse( |
|
593 | - [ |
|
594 | - 'status' => 'error', |
|
595 | - 'data' => [ |
|
596 | - 'message' => $errorMsgGeneral |
|
597 | - ] |
|
598 | - ], |
|
599 | - Http::STATUS_FORBIDDEN |
|
600 | - ); |
|
601 | - } |
|
602 | - |
|
603 | - } |
|
604 | - |
|
605 | - /** |
|
606 | - * Set the mail address of a user |
|
607 | - * |
|
608 | - * @NoAdminRequired |
|
609 | - * @NoSubadminRequired |
|
610 | - * @PasswordConfirmationRequired |
|
611 | - * |
|
612 | - * @param string $account |
|
613 | - * @param bool $onlyVerificationCode only return verification code without updating the data |
|
614 | - * @return DataResponse |
|
615 | - */ |
|
616 | - public function getVerificationCode(string $account, bool $onlyVerificationCode): DataResponse { |
|
617 | - |
|
618 | - $user = $this->userSession->getUser(); |
|
619 | - |
|
620 | - if ($user === null) { |
|
621 | - return new DataResponse([], Http::STATUS_BAD_REQUEST); |
|
622 | - } |
|
623 | - |
|
624 | - $accountData = $this->accountManager->getUser($user); |
|
625 | - $cloudId = $user->getCloudId(); |
|
626 | - $message = 'Use my Federated Cloud ID to share with me: ' . $cloudId; |
|
627 | - $signature = $this->signMessage($user, $message); |
|
628 | - |
|
629 | - $code = $message . ' ' . $signature; |
|
630 | - $codeMd5 = $message . ' ' . md5($signature); |
|
631 | - |
|
632 | - switch ($account) { |
|
633 | - case 'verify-twitter': |
|
634 | - $accountData[AccountManager::PROPERTY_TWITTER]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS; |
|
635 | - $msg = $this->l10n->t('In order to verify your Twitter account, post the following tweet on Twitter (please make sure to post it without any line breaks):'); |
|
636 | - $code = $codeMd5; |
|
637 | - $type = AccountManager::PROPERTY_TWITTER; |
|
638 | - $data = $accountData[AccountManager::PROPERTY_TWITTER]['value']; |
|
639 | - $accountData[AccountManager::PROPERTY_TWITTER]['signature'] = $signature; |
|
640 | - break; |
|
641 | - case 'verify-website': |
|
642 | - $accountData[AccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS; |
|
643 | - $msg = $this->l10n->t('In order to verify your Website, store the following content in your web-root at \'.well-known/CloudIdVerificationCode.txt\' (please make sure that the complete text is in one line):'); |
|
644 | - $type = AccountManager::PROPERTY_WEBSITE; |
|
645 | - $data = $accountData[AccountManager::PROPERTY_WEBSITE]['value']; |
|
646 | - $accountData[AccountManager::PROPERTY_WEBSITE]['signature'] = $signature; |
|
647 | - break; |
|
648 | - default: |
|
649 | - return new DataResponse([], Http::STATUS_BAD_REQUEST); |
|
650 | - } |
|
651 | - |
|
652 | - if ($onlyVerificationCode === false) { |
|
653 | - $this->accountManager->updateUser($user, $accountData); |
|
654 | - |
|
655 | - $this->jobList->add(VerifyUserData::class, |
|
656 | - [ |
|
657 | - 'verificationCode' => $code, |
|
658 | - 'data' => $data, |
|
659 | - 'type' => $type, |
|
660 | - 'uid' => $user->getUID(), |
|
661 | - 'try' => 0, |
|
662 | - 'lastRun' => $this->getCurrentTime() |
|
663 | - ] |
|
664 | - ); |
|
665 | - } |
|
666 | - |
|
667 | - return new DataResponse(['msg' => $msg, 'code' => $code]); |
|
668 | - } |
|
669 | - |
|
670 | - /** |
|
671 | - * get current timestamp |
|
672 | - * |
|
673 | - * @return int |
|
674 | - */ |
|
675 | - protected function getCurrentTime(): int { |
|
676 | - return time(); |
|
677 | - } |
|
678 | - |
|
679 | - /** |
|
680 | - * sign message with users private key |
|
681 | - * |
|
682 | - * @param IUser $user |
|
683 | - * @param string $message |
|
684 | - * |
|
685 | - * @return string base64 encoded signature |
|
686 | - */ |
|
687 | - protected function signMessage(IUser $user, string $message): string { |
|
688 | - $privateKey = $this->keyManager->getKey($user)->getPrivate(); |
|
689 | - openssl_sign(json_encode($message), $signature, $privateKey, OPENSSL_ALGO_SHA512); |
|
690 | - return base64_encode($signature); |
|
691 | - } |
|
692 | - |
|
693 | - /** |
|
694 | - * @NoAdminRequired |
|
695 | - * @NoSubadminRequired |
|
696 | - * @PasswordConfirmationRequired |
|
697 | - * |
|
698 | - * @param string $avatarScope |
|
699 | - * @param string $displayname |
|
700 | - * @param string $displaynameScope |
|
701 | - * @param string $phone |
|
702 | - * @param string $phoneScope |
|
703 | - * @param string $email |
|
704 | - * @param string $emailScope |
|
705 | - * @param string $website |
|
706 | - * @param string $websiteScope |
|
707 | - * @param string $address |
|
708 | - * @param string $addressScope |
|
709 | - * @param string $twitter |
|
710 | - * @param string $twitterScope |
|
711 | - * @return DataResponse |
|
712 | - */ |
|
713 | - public function setUserSettings($avatarScope, |
|
714 | - $displayname, |
|
715 | - $displaynameScope, |
|
716 | - $phone, |
|
717 | - $phoneScope, |
|
718 | - $email, |
|
719 | - $emailScope, |
|
720 | - $website, |
|
721 | - $websiteScope, |
|
722 | - $address, |
|
723 | - $addressScope, |
|
724 | - $twitter, |
|
725 | - $twitterScope |
|
726 | - ) { |
|
727 | - |
|
728 | - if (!empty($email) && !$this->mailer->validateMailAddress($email)) { |
|
729 | - return new DataResponse( |
|
730 | - [ |
|
731 | - 'status' => 'error', |
|
732 | - 'data' => [ |
|
733 | - 'message' => $this->l10n->t('Invalid mail address') |
|
734 | - ] |
|
735 | - ], |
|
736 | - Http::STATUS_UNPROCESSABLE_ENTITY |
|
737 | - ); |
|
738 | - } |
|
739 | - |
|
740 | - $user = $this->userSession->getUser(); |
|
741 | - |
|
742 | - $data = $this->accountManager->getUser($user); |
|
743 | - |
|
744 | - $data[AccountManager::PROPERTY_AVATAR] = ['scope' => $avatarScope]; |
|
745 | - if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) { |
|
746 | - $data[AccountManager::PROPERTY_DISPLAYNAME] = ['value' => $displayname, 'scope' => $displaynameScope]; |
|
747 | - $data[AccountManager::PROPERTY_EMAIL] = ['value' => $email, 'scope' => $emailScope]; |
|
748 | - } |
|
749 | - |
|
750 | - if ($this->appManager->isEnabledForUser('federatedfilesharing')) { |
|
751 | - $federatedFileSharing = new \OCA\FederatedFileSharing\AppInfo\Application(); |
|
752 | - $shareProvider = $federatedFileSharing->getFederatedShareProvider(); |
|
753 | - if ($shareProvider->isLookupServerUploadEnabled()) { |
|
754 | - $data[AccountManager::PROPERTY_WEBSITE] = ['value' => $website, 'scope' => $websiteScope]; |
|
755 | - $data[AccountManager::PROPERTY_ADDRESS] = ['value' => $address, 'scope' => $addressScope]; |
|
756 | - $data[AccountManager::PROPERTY_PHONE] = ['value' => $phone, 'scope' => $phoneScope]; |
|
757 | - $data[AccountManager::PROPERTY_TWITTER] = ['value' => $twitter, 'scope' => $twitterScope]; |
|
758 | - } |
|
759 | - } |
|
760 | - |
|
761 | - try { |
|
762 | - $this->saveUserSettings($user, $data); |
|
763 | - return new DataResponse( |
|
764 | - [ |
|
765 | - 'status' => 'success', |
|
766 | - 'data' => [ |
|
767 | - 'userId' => $user->getUID(), |
|
768 | - 'avatarScope' => $data[AccountManager::PROPERTY_AVATAR]['scope'], |
|
769 | - 'displayname' => $data[AccountManager::PROPERTY_DISPLAYNAME]['value'], |
|
770 | - 'displaynameScope' => $data[AccountManager::PROPERTY_DISPLAYNAME]['scope'], |
|
771 | - 'email' => $data[AccountManager::PROPERTY_EMAIL]['value'], |
|
772 | - 'emailScope' => $data[AccountManager::PROPERTY_EMAIL]['scope'], |
|
773 | - 'website' => $data[AccountManager::PROPERTY_WEBSITE]['value'], |
|
774 | - 'websiteScope' => $data[AccountManager::PROPERTY_WEBSITE]['scope'], |
|
775 | - 'address' => $data[AccountManager::PROPERTY_ADDRESS]['value'], |
|
776 | - 'addressScope' => $data[AccountManager::PROPERTY_ADDRESS]['scope'], |
|
777 | - 'message' => $this->l10n->t('Settings saved') |
|
778 | - ] |
|
779 | - ], |
|
780 | - Http::STATUS_OK |
|
781 | - ); |
|
782 | - } catch (ForbiddenException $e) { |
|
783 | - return new DataResponse([ |
|
784 | - 'status' => 'error', |
|
785 | - 'data' => [ |
|
786 | - 'message' => $e->getMessage() |
|
787 | - ], |
|
788 | - ]); |
|
789 | - } |
|
790 | - |
|
791 | - } |
|
792 | - |
|
793 | - |
|
794 | - /** |
|
795 | - * update account manager with new user data |
|
796 | - * |
|
797 | - * @param IUser $user |
|
798 | - * @param array $data |
|
799 | - * @throws ForbiddenException |
|
800 | - */ |
|
801 | - protected function saveUserSettings(IUser $user, array $data) { |
|
802 | - |
|
803 | - // keep the user back-end up-to-date with the latest display name and email |
|
804 | - // address |
|
805 | - $oldDisplayName = $user->getDisplayName(); |
|
806 | - $oldDisplayName = is_null($oldDisplayName) ? '' : $oldDisplayName; |
|
807 | - if (isset($data[AccountManager::PROPERTY_DISPLAYNAME]['value']) |
|
808 | - && $oldDisplayName !== $data[AccountManager::PROPERTY_DISPLAYNAME]['value'] |
|
809 | - ) { |
|
810 | - $result = $user->setDisplayName($data[AccountManager::PROPERTY_DISPLAYNAME]['value']); |
|
811 | - if ($result === false) { |
|
812 | - throw new ForbiddenException($this->l10n->t('Unable to change full name')); |
|
813 | - } |
|
814 | - } |
|
815 | - |
|
816 | - $oldEmailAddress = $user->getEMailAddress(); |
|
817 | - $oldEmailAddress = is_null($oldEmailAddress) ? '' : $oldEmailAddress; |
|
818 | - if (isset($data[AccountManager::PROPERTY_EMAIL]['value']) |
|
819 | - && $oldEmailAddress !== $data[AccountManager::PROPERTY_EMAIL]['value'] |
|
820 | - ) { |
|
821 | - // this is the only permission a backend provides and is also used |
|
822 | - // for the permission of setting a email address |
|
823 | - if (!$user->canChangeDisplayName()) { |
|
824 | - throw new ForbiddenException($this->l10n->t('Unable to change email address')); |
|
825 | - } |
|
826 | - $user->setEMailAddress($data[AccountManager::PROPERTY_EMAIL]['value']); |
|
827 | - } |
|
828 | - |
|
829 | - $this->accountManager->updateUser($user, $data); |
|
830 | - } |
|
831 | - |
|
832 | - /** |
|
833 | - * Count all unique users visible for the current admin/subadmin. |
|
834 | - * |
|
835 | - * @NoAdminRequired |
|
836 | - * |
|
837 | - * @return DataResponse |
|
838 | - */ |
|
839 | - public function stats(): DataResponse { |
|
840 | - $userCount = 0; |
|
841 | - if ($this->isAdmin) { |
|
842 | - $countByBackend = $this->userManager->countUsers(); |
|
843 | - |
|
844 | - if (!empty($countByBackend)) { |
|
845 | - foreach ($countByBackend as $count) { |
|
846 | - $userCount += $count; |
|
847 | - } |
|
848 | - } |
|
849 | - } else { |
|
850 | - $groups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($this->userSession->getUser()); |
|
851 | - |
|
852 | - $uniqueUsers = []; |
|
853 | - foreach ($groups as $group) { |
|
854 | - foreach ($group->getUsers() as $uid => $displayName) { |
|
855 | - $uniqueUsers[$uid] = true; |
|
856 | - } |
|
857 | - } |
|
858 | - |
|
859 | - $userCount = count($uniqueUsers); |
|
860 | - } |
|
861 | - |
|
862 | - return new DataResponse( |
|
863 | - [ |
|
864 | - 'totalUsers' => $userCount |
|
865 | - ] |
|
866 | - ); |
|
867 | - } |
|
868 | - |
|
869 | - |
|
870 | - /** |
|
871 | - * Set the displayName of a user |
|
872 | - * |
|
873 | - * @NoAdminRequired |
|
874 | - * @NoSubadminRequired |
|
875 | - * @PasswordConfirmationRequired |
|
876 | - * @todo merge into saveUserSettings |
|
877 | - * |
|
878 | - * @param string $username |
|
879 | - * @param string $displayName |
|
880 | - * @return DataResponse |
|
881 | - */ |
|
882 | - public function setDisplayName(string $username, string $displayName) { |
|
883 | - $currentUser = $this->userSession->getUser(); |
|
884 | - $user = $this->userManager->get($username); |
|
885 | - |
|
886 | - if ($user === null || |
|
887 | - !$user->canChangeDisplayName() || |
|
888 | - ( |
|
889 | - !$this->groupManager->isAdmin($currentUser->getUID()) && |
|
890 | - !$this->groupManager->getSubAdmin()->isUserAccessible($currentUser, $user) && |
|
891 | - $currentUser->getUID() !== $username |
|
892 | - |
|
893 | - ) |
|
894 | - ) { |
|
895 | - return new DataResponse([ |
|
896 | - 'status' => 'error', |
|
897 | - 'data' => [ |
|
898 | - 'message' => $this->l10n->t('Authentication error'), |
|
899 | - ], |
|
900 | - ]); |
|
901 | - } |
|
902 | - |
|
903 | - $userData = $this->accountManager->getUser($user); |
|
904 | - $userData[AccountManager::PROPERTY_DISPLAYNAME]['value'] = $displayName; |
|
905 | - |
|
906 | - |
|
907 | - try { |
|
908 | - $this->saveUserSettings($user, $userData); |
|
909 | - return new DataResponse([ |
|
910 | - 'status' => 'success', |
|
911 | - 'data' => [ |
|
912 | - 'message' => $this->l10n->t('Your full name has been changed.'), |
|
913 | - 'username' => $username, |
|
914 | - 'displayName' => $displayName, |
|
915 | - ], |
|
916 | - ]); |
|
917 | - } catch (ForbiddenException $e) { |
|
918 | - return new DataResponse([ |
|
919 | - 'status' => 'error', |
|
920 | - 'data' => [ |
|
921 | - 'message' => $e->getMessage(), |
|
922 | - 'displayName' => $user->getDisplayName(), |
|
923 | - ], |
|
924 | - ]); |
|
925 | - } |
|
926 | - } |
|
927 | - |
|
928 | - /** |
|
929 | - * Set the mail address of a user |
|
930 | - * |
|
931 | - * @NoAdminRequired |
|
932 | - * @NoSubadminRequired |
|
933 | - * @PasswordConfirmationRequired |
|
934 | - * |
|
935 | - * @param string $id |
|
936 | - * @param string $mailAddress |
|
937 | - * @return DataResponse |
|
938 | - */ |
|
939 | - public function setEMailAddress(string $id, string $mailAddress) { |
|
940 | - $user = $this->userManager->get($id); |
|
941 | - if (!$this->isAdmin |
|
942 | - && !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user) |
|
943 | - ) { |
|
944 | - return new DataResponse( |
|
945 | - [ |
|
946 | - 'status' => 'error', |
|
947 | - 'data' => [ |
|
948 | - 'message' => $this->l10n->t('Forbidden') |
|
949 | - ] |
|
950 | - ], |
|
951 | - Http::STATUS_FORBIDDEN |
|
952 | - ); |
|
953 | - } |
|
954 | - |
|
955 | - if ($mailAddress !== '' && !$this->mailer->validateMailAddress($mailAddress)) { |
|
956 | - return new DataResponse( |
|
957 | - [ |
|
958 | - 'status' => 'error', |
|
959 | - 'data' => [ |
|
960 | - 'message' => $this->l10n->t('Invalid mail address') |
|
961 | - ] |
|
962 | - ], |
|
963 | - Http::STATUS_UNPROCESSABLE_ENTITY |
|
964 | - ); |
|
965 | - } |
|
966 | - |
|
967 | - if (!$user) { |
|
968 | - return new DataResponse( |
|
969 | - [ |
|
970 | - 'status' => 'error', |
|
971 | - 'data' => [ |
|
972 | - 'message' => $this->l10n->t('Invalid user') |
|
973 | - ] |
|
974 | - ], |
|
975 | - Http::STATUS_UNPROCESSABLE_ENTITY |
|
976 | - ); |
|
977 | - } |
|
978 | - // this is the only permission a backend provides and is also used |
|
979 | - // for the permission of setting a email address |
|
980 | - if (!$user->canChangeDisplayName()) { |
|
981 | - return new DataResponse( |
|
982 | - [ |
|
983 | - 'status' => 'error', |
|
984 | - 'data' => [ |
|
985 | - 'message' => $this->l10n->t('Unable to change mail address') |
|
986 | - ] |
|
987 | - ], |
|
988 | - Http::STATUS_FORBIDDEN |
|
989 | - ); |
|
990 | - } |
|
991 | - |
|
992 | - $userData = $this->accountManager->getUser($user); |
|
993 | - $userData[AccountManager::PROPERTY_EMAIL]['value'] = $mailAddress; |
|
994 | - |
|
995 | - try { |
|
996 | - $this->saveUserSettings($user, $userData); |
|
997 | - return new DataResponse( |
|
998 | - [ |
|
999 | - 'status' => 'success', |
|
1000 | - 'data' => [ |
|
1001 | - 'username' => $id, |
|
1002 | - 'mailAddress' => $mailAddress, |
|
1003 | - 'message' => $this->l10n->t('Email saved') |
|
1004 | - ] |
|
1005 | - ], |
|
1006 | - Http::STATUS_OK |
|
1007 | - ); |
|
1008 | - } catch (ForbiddenException $e) { |
|
1009 | - return new DataResponse([ |
|
1010 | - 'status' => 'error', |
|
1011 | - 'data' => [ |
|
1012 | - 'message' => $e->getMessage() |
|
1013 | - ], |
|
1014 | - ]); |
|
1015 | - } |
|
1016 | - } |
|
74 | + /** @var IL10N */ |
|
75 | + private $l10n; |
|
76 | + /** @var IUserSession */ |
|
77 | + private $userSession; |
|
78 | + /** @var bool */ |
|
79 | + private $isAdmin; |
|
80 | + /** @var IUserManager */ |
|
81 | + private $userManager; |
|
82 | + /** @var IGroupManager */ |
|
83 | + private $groupManager; |
|
84 | + /** @var IConfig */ |
|
85 | + private $config; |
|
86 | + /** @var ILogger */ |
|
87 | + private $log; |
|
88 | + /** @var IMailer */ |
|
89 | + private $mailer; |
|
90 | + /** @var bool contains the state of the encryption app */ |
|
91 | + private $isEncryptionAppEnabled; |
|
92 | + /** @var bool contains the state of the admin recovery setting */ |
|
93 | + private $isRestoreEnabled = false; |
|
94 | + /** @var IAppManager */ |
|
95 | + private $appManager; |
|
96 | + /** @var IAvatarManager */ |
|
97 | + private $avatarManager; |
|
98 | + /** @var AccountManager */ |
|
99 | + private $accountManager; |
|
100 | + /** @var ISecureRandom */ |
|
101 | + private $secureRandom; |
|
102 | + /** @var NewUserMailHelper */ |
|
103 | + private $newUserMailHelper; |
|
104 | + /** @var Manager */ |
|
105 | + private $keyManager; |
|
106 | + /** @var IJobList */ |
|
107 | + private $jobList; |
|
108 | + |
|
109 | + /** @var IUserMountCache */ |
|
110 | + private $userMountCache; |
|
111 | + |
|
112 | + /** @var IManager */ |
|
113 | + private $encryptionManager; |
|
114 | + |
|
115 | + public function __construct(string $appName, |
|
116 | + IRequest $request, |
|
117 | + IUserManager $userManager, |
|
118 | + IGroupManager $groupManager, |
|
119 | + IUserSession $userSession, |
|
120 | + IConfig $config, |
|
121 | + bool $isAdmin, |
|
122 | + IL10N $l10n, |
|
123 | + ILogger $log, |
|
124 | + IMailer $mailer, |
|
125 | + IURLGenerator $urlGenerator, |
|
126 | + IAppManager $appManager, |
|
127 | + IAvatarManager $avatarManager, |
|
128 | + AccountManager $accountManager, |
|
129 | + ISecureRandom $secureRandom, |
|
130 | + NewUserMailHelper $newUserMailHelper, |
|
131 | + Manager $keyManager, |
|
132 | + IJobList $jobList, |
|
133 | + IUserMountCache $userMountCache, |
|
134 | + IManager $encryptionManager) { |
|
135 | + parent::__construct($appName, $request); |
|
136 | + $this->userManager = $userManager; |
|
137 | + $this->groupManager = $groupManager; |
|
138 | + $this->userSession = $userSession; |
|
139 | + $this->config = $config; |
|
140 | + $this->isAdmin = $isAdmin; |
|
141 | + $this->l10n = $l10n; |
|
142 | + $this->log = $log; |
|
143 | + $this->mailer = $mailer; |
|
144 | + $this->appManager = $appManager; |
|
145 | + $this->avatarManager = $avatarManager; |
|
146 | + $this->accountManager = $accountManager; |
|
147 | + $this->secureRandom = $secureRandom; |
|
148 | + $this->newUserMailHelper = $newUserMailHelper; |
|
149 | + $this->keyManager = $keyManager; |
|
150 | + $this->jobList = $jobList; |
|
151 | + $this->userMountCache = $userMountCache; |
|
152 | + $this->encryptionManager = $encryptionManager; |
|
153 | + |
|
154 | + // check for encryption state - TODO see formatUserForIndex |
|
155 | + $this->isEncryptionAppEnabled = $appManager->isEnabledForUser('encryption'); |
|
156 | + if ($this->isEncryptionAppEnabled) { |
|
157 | + // putting this directly in empty is possible in PHP 5.5+ |
|
158 | + $result = $config->getAppValue('encryption', 'recoveryAdminEnabled', '0'); |
|
159 | + $this->isRestoreEnabled = !empty($result); |
|
160 | + } |
|
161 | + } |
|
162 | + |
|
163 | + /** |
|
164 | + * @param IUser $user |
|
165 | + * @param array|null $userGroups |
|
166 | + * @return array |
|
167 | + */ |
|
168 | + private function formatUserForIndex(IUser $user, array $userGroups = null): array { |
|
169 | + |
|
170 | + // TODO: eliminate this encryption specific code below and somehow |
|
171 | + // hook in additional user info from other apps |
|
172 | + |
|
173 | + // recovery isn't possible if admin or user has it disabled and encryption |
|
174 | + // is enabled - so we eliminate the else paths in the conditional tree |
|
175 | + // below |
|
176 | + $restorePossible = false; |
|
177 | + |
|
178 | + if ($this->isEncryptionAppEnabled) { |
|
179 | + if ($this->isRestoreEnabled) { |
|
180 | + // check for the users recovery setting |
|
181 | + $recoveryMode = $this->config->getUserValue($user->getUID(), 'encryption', 'recoveryEnabled', '0'); |
|
182 | + // method call inside empty is possible with PHP 5.5+ |
|
183 | + $recoveryModeEnabled = !empty($recoveryMode); |
|
184 | + if ($recoveryModeEnabled) { |
|
185 | + // user also has recovery mode enabled |
|
186 | + $restorePossible = true; |
|
187 | + } |
|
188 | + } else { |
|
189 | + $modules = $this->encryptionManager->getEncryptionModules(); |
|
190 | + $restorePossible = true; |
|
191 | + foreach ($modules as $id => $module) { |
|
192 | + /* @var IEncryptionModule $instance */ |
|
193 | + $instance = call_user_func($module['callback']); |
|
194 | + if ($instance->needDetailedAccessList()) { |
|
195 | + $restorePossible = false; |
|
196 | + break; |
|
197 | + } |
|
198 | + } |
|
199 | + } |
|
200 | + } else { |
|
201 | + // recovery is possible if encryption is disabled (plain files are |
|
202 | + // available) |
|
203 | + $restorePossible = true; |
|
204 | + } |
|
205 | + |
|
206 | + $subAdminGroups = $this->groupManager->getSubAdmin()->getSubAdminsGroupsName($user); |
|
207 | + |
|
208 | + $displayName = $user->getEMailAddress(); |
|
209 | + if (is_null($displayName)) { |
|
210 | + $displayName = ''; |
|
211 | + } |
|
212 | + |
|
213 | + $avatarAvailable = false; |
|
214 | + try { |
|
215 | + $avatarAvailable = $this->avatarManager->getAvatar($user->getUID())->exists(); |
|
216 | + } catch (\Exception $e) { |
|
217 | + //No avatar yet |
|
218 | + } |
|
219 | + |
|
220 | + return [ |
|
221 | + 'name' => $user->getUID(), |
|
222 | + 'displayname' => $user->getDisplayName(), |
|
223 | + 'groups' => empty($userGroups) ? $this->groupManager->getUserGroupNames($user) : $userGroups, |
|
224 | + 'subadmin' => $subAdminGroups, |
|
225 | + 'quota' => $user->getQuota(), |
|
226 | + 'quota_bytes' => Util::computerFileSize($user->getQuota()), |
|
227 | + 'storageLocation' => $user->getHome(), |
|
228 | + 'lastLogin' => $user->getLastLogin() * 1000, |
|
229 | + 'backend' => $user->getBackendClassName(), |
|
230 | + 'email' => $displayName, |
|
231 | + 'isRestoreDisabled' => !$restorePossible, |
|
232 | + 'isAvatarAvailable' => $avatarAvailable, |
|
233 | + 'isEnabled' => $user->isEnabled(), |
|
234 | + ]; |
|
235 | + } |
|
236 | + |
|
237 | + /** |
|
238 | + * @param array $userIDs Array with schema [$uid => $displayName] |
|
239 | + * @return IUser[] |
|
240 | + */ |
|
241 | + private function getUsersForUID(array $userIDs): array { |
|
242 | + $users = []; |
|
243 | + foreach ($userIDs as $uid => $displayName) { |
|
244 | + $users[$uid] = $this->userManager->get($uid); |
|
245 | + } |
|
246 | + return $users; |
|
247 | + } |
|
248 | + |
|
249 | + /** |
|
250 | + * @NoAdminRequired |
|
251 | + * |
|
252 | + * @param int $offset |
|
253 | + * @param int $limit |
|
254 | + * @param string $gid GID to filter for |
|
255 | + * @param string $pattern Pattern to search for in the username |
|
256 | + * @param string $backend Backend to filter for (class-name) |
|
257 | + * @return DataResponse |
|
258 | + * |
|
259 | + * TODO: Tidy up and write unit tests - code is mainly static method calls |
|
260 | + */ |
|
261 | + public function index(int $offset = 0, int $limit = 10, string $gid = '', string $pattern = '', string $backend = ''): DataResponse { |
|
262 | + // Remove backends |
|
263 | + if (!empty($backend)) { |
|
264 | + $activeBackends = $this->userManager->getBackends(); |
|
265 | + $this->userManager->clearBackends(); |
|
266 | + foreach ($activeBackends as $singleActiveBackend) { |
|
267 | + if ($backend === get_class($singleActiveBackend)) { |
|
268 | + $this->userManager->registerBackend($singleActiveBackend); |
|
269 | + break; |
|
270 | + } |
|
271 | + } |
|
272 | + } |
|
273 | + |
|
274 | + $userObjects = []; |
|
275 | + $users = []; |
|
276 | + if ($this->isAdmin) { |
|
277 | + if ($gid !== '' && $gid !== '_disabledUsers' && $gid !== '_everyone') { |
|
278 | + $batch = $this->getUsersForUID($this->groupManager->displayNamesInGroup($gid, $pattern, $limit, $offset)); |
|
279 | + } else { |
|
280 | + $batch = $this->userManager->search($pattern, $limit, $offset); |
|
281 | + } |
|
282 | + |
|
283 | + foreach ($batch as $user) { |
|
284 | + if (($gid !== '_disabledUsers' && $user->isEnabled()) || |
|
285 | + ($gid === '_disabledUsers' && !$user->isEnabled()) |
|
286 | + ) { |
|
287 | + $userObjects[] = $user; |
|
288 | + $users[] = $this->formatUserForIndex($user); |
|
289 | + } |
|
290 | + } |
|
291 | + |
|
292 | + } else { |
|
293 | + $subAdminOfGroups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($this->userSession->getUser()); |
|
294 | + // New class returns IGroup[] so convert back |
|
295 | + $gids = []; |
|
296 | + foreach ($subAdminOfGroups as $group) { |
|
297 | + $gids[] = $group->getGID(); |
|
298 | + } |
|
299 | + $subAdminOfGroups = $gids; |
|
300 | + |
|
301 | + // Set the $gid parameter to an empty value if the subadmin has no rights to access a specific group |
|
302 | + if ($gid !== '' && $gid !== '_disabledUsers' && !in_array($gid, $subAdminOfGroups)) { |
|
303 | + $gid = ''; |
|
304 | + } |
|
305 | + |
|
306 | + // Batch all groups the user is subadmin of when a group is specified |
|
307 | + $batch = []; |
|
308 | + if ($gid !== '' && $gid !== '_disabledUsers' && $gid !== '_everyone') { |
|
309 | + $batch = $this->groupManager->displayNamesInGroup($gid, $pattern, $limit, $offset); |
|
310 | + } else { |
|
311 | + foreach ($subAdminOfGroups as $group) { |
|
312 | + $groupUsers = $this->groupManager->displayNamesInGroup($group, $pattern, $limit, $offset); |
|
313 | + |
|
314 | + foreach ($groupUsers as $uid => $displayName) { |
|
315 | + $batch[$uid] = $displayName; |
|
316 | + } |
|
317 | + } |
|
318 | + } |
|
319 | + $batch = $this->getUsersForUID($batch); |
|
320 | + |
|
321 | + foreach ($batch as $user) { |
|
322 | + // Only add the groups, this user is a subadmin of |
|
323 | + $userGroups = array_values(array_intersect( |
|
324 | + $this->groupManager->getUserGroupIds($user), |
|
325 | + $subAdminOfGroups |
|
326 | + )); |
|
327 | + if (($gid !== '_disabledUsers' && $user->isEnabled()) || |
|
328 | + ($gid === '_disabledUsers' && !$user->isEnabled()) |
|
329 | + ) { |
|
330 | + $userObjects[] = $user; |
|
331 | + $users[] = $this->formatUserForIndex($user, $userGroups); |
|
332 | + } |
|
333 | + } |
|
334 | + } |
|
335 | + |
|
336 | + $usedSpace = $this->userMountCache->getUsedSpaceForUsers($userObjects); |
|
337 | + |
|
338 | + foreach ($users as &$userData) { |
|
339 | + $userData['size'] = isset($usedSpace[$userData['name']]) ? $usedSpace[$userData['name']] : 0; |
|
340 | + } |
|
341 | + |
|
342 | + return new DataResponse($users); |
|
343 | + } |
|
344 | + |
|
345 | + /** |
|
346 | + * @NoAdminRequired |
|
347 | + * @PasswordConfirmationRequired |
|
348 | + * |
|
349 | + * @param string $username |
|
350 | + * @param string $password |
|
351 | + * @param array $groups |
|
352 | + * @param string $email |
|
353 | + * @return DataResponse |
|
354 | + */ |
|
355 | + public function create(string $username, string $password, array $groups = [], $email = ''): DataResponse { |
|
356 | + if ($email !== '' && !$this->mailer->validateMailAddress($email)) { |
|
357 | + return new DataResponse( |
|
358 | + [ |
|
359 | + 'message' => $this->l10n->t('Invalid mail address') |
|
360 | + ], |
|
361 | + Http::STATUS_UNPROCESSABLE_ENTITY |
|
362 | + ); |
|
363 | + } |
|
364 | + |
|
365 | + $currentUser = $this->userSession->getUser(); |
|
366 | + |
|
367 | + if (!$this->isAdmin) { |
|
368 | + if (!empty($groups)) { |
|
369 | + foreach ($groups as $key => $group) { |
|
370 | + $groupObject = $this->groupManager->get($group); |
|
371 | + if ($groupObject === null) { |
|
372 | + unset($groups[$key]); |
|
373 | + continue; |
|
374 | + } |
|
375 | + |
|
376 | + if (!$this->groupManager->getSubAdmin()->isSubAdminOfGroup($currentUser, $groupObject)) { |
|
377 | + unset($groups[$key]); |
|
378 | + } |
|
379 | + } |
|
380 | + } |
|
381 | + |
|
382 | + if (empty($groups)) { |
|
383 | + return new DataResponse( |
|
384 | + [ |
|
385 | + 'message' => $this->l10n->t('No valid group selected'), |
|
386 | + ], |
|
387 | + Http::STATUS_FORBIDDEN |
|
388 | + ); |
|
389 | + } |
|
390 | + } |
|
391 | + |
|
392 | + if ($this->userManager->userExists($username)) { |
|
393 | + return new DataResponse( |
|
394 | + [ |
|
395 | + 'message' => $this->l10n->t('A user with that name already exists.') |
|
396 | + ], |
|
397 | + Http::STATUS_CONFLICT |
|
398 | + ); |
|
399 | + } |
|
400 | + |
|
401 | + $generatePasswordResetToken = false; |
|
402 | + if ($password === '') { |
|
403 | + if ($email === '') { |
|
404 | + return new DataResponse( |
|
405 | + [ |
|
406 | + 'message' => $this->l10n->t('To send a password link to the user an email address is required.') |
|
407 | + ], |
|
408 | + Http::STATUS_UNPROCESSABLE_ENTITY |
|
409 | + ); |
|
410 | + } |
|
411 | + |
|
412 | + $password = $this->secureRandom->generate(30); |
|
413 | + // Make sure we pass the password_policy |
|
414 | + $password .= $this->secureRandom->generate(2, '$!.,;:-~+*[]{}()'); |
|
415 | + $generatePasswordResetToken = true; |
|
416 | + } |
|
417 | + |
|
418 | + try { |
|
419 | + $user = $this->userManager->createUser($username, $password); |
|
420 | + } catch (\Exception $exception) { |
|
421 | + $message = $exception->getMessage(); |
|
422 | + if ($exception instanceof HintException && $exception->getHint()) { |
|
423 | + $message = $exception->getHint(); |
|
424 | + } |
|
425 | + if (!$message) { |
|
426 | + $message = $this->l10n->t('Unable to create user.'); |
|
427 | + } |
|
428 | + return new DataResponse( |
|
429 | + [ |
|
430 | + 'message' => (string)$message, |
|
431 | + ], |
|
432 | + Http::STATUS_FORBIDDEN |
|
433 | + ); |
|
434 | + } |
|
435 | + |
|
436 | + if ($user instanceof IUser) { |
|
437 | + if ($groups !== null) { |
|
438 | + foreach ($groups as $groupName) { |
|
439 | + $group = $this->groupManager->get($groupName); |
|
440 | + |
|
441 | + if (empty($group)) { |
|
442 | + $group = $this->groupManager->createGroup($groupName); |
|
443 | + } |
|
444 | + $group->addUser($user); |
|
445 | + } |
|
446 | + } |
|
447 | + /** |
|
448 | + * Send new user mail only if a mail is set |
|
449 | + */ |
|
450 | + if ($email !== '') { |
|
451 | + $user->setEMailAddress($email); |
|
452 | + try { |
|
453 | + $emailTemplate = $this->newUserMailHelper->generateTemplate($user, $generatePasswordResetToken); |
|
454 | + $this->newUserMailHelper->sendMail($user, $emailTemplate); |
|
455 | + } catch (\Exception $e) { |
|
456 | + $this->log->logException($e, [ |
|
457 | + 'message' => "Can't send new user mail to $email", |
|
458 | + 'level' => ILogger::ERROR, |
|
459 | + 'app' => 'settings', |
|
460 | + ]); |
|
461 | + } |
|
462 | + } |
|
463 | + // fetch users groups |
|
464 | + $userGroups = $this->groupManager->getUserGroupNames($user); |
|
465 | + |
|
466 | + return new DataResponse( |
|
467 | + $this->formatUserForIndex($user, $userGroups), |
|
468 | + Http::STATUS_CREATED |
|
469 | + ); |
|
470 | + } |
|
471 | + |
|
472 | + return new DataResponse( |
|
473 | + [ |
|
474 | + 'message' => $this->l10n->t('Unable to create user.') |
|
475 | + ], |
|
476 | + Http::STATUS_FORBIDDEN |
|
477 | + ); |
|
478 | + |
|
479 | + } |
|
480 | + |
|
481 | + /** |
|
482 | + * @NoAdminRequired |
|
483 | + * @PasswordConfirmationRequired |
|
484 | + * |
|
485 | + * @param string $id |
|
486 | + * @return DataResponse |
|
487 | + */ |
|
488 | + public function destroy(string $id): DataResponse { |
|
489 | + $userId = $this->userSession->getUser()->getUID(); |
|
490 | + $user = $this->userManager->get($id); |
|
491 | + |
|
492 | + if ($userId === $id) { |
|
493 | + return new DataResponse( |
|
494 | + [ |
|
495 | + 'status' => 'error', |
|
496 | + 'data' => [ |
|
497 | + 'message' => $this->l10n->t('Unable to delete user.') |
|
498 | + ] |
|
499 | + ], |
|
500 | + Http::STATUS_FORBIDDEN |
|
501 | + ); |
|
502 | + } |
|
503 | + |
|
504 | + if (!$this->isAdmin && !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)) { |
|
505 | + return new DataResponse( |
|
506 | + [ |
|
507 | + 'status' => 'error', |
|
508 | + 'data' => [ |
|
509 | + 'message' => $this->l10n->t('Authentication error') |
|
510 | + ] |
|
511 | + ], |
|
512 | + Http::STATUS_FORBIDDEN |
|
513 | + ); |
|
514 | + } |
|
515 | + |
|
516 | + if ($user && $user->delete()) { |
|
517 | + return new DataResponse( |
|
518 | + [ |
|
519 | + 'status' => 'success', |
|
520 | + 'data' => [ |
|
521 | + 'username' => $id |
|
522 | + ] |
|
523 | + ], |
|
524 | + Http::STATUS_NO_CONTENT |
|
525 | + ); |
|
526 | + } |
|
527 | + |
|
528 | + return new DataResponse( |
|
529 | + [ |
|
530 | + 'status' => 'error', |
|
531 | + 'data' => [ |
|
532 | + 'message' => $this->l10n->t('Unable to delete user.') |
|
533 | + ] |
|
534 | + ], |
|
535 | + Http::STATUS_FORBIDDEN |
|
536 | + ); |
|
537 | + } |
|
538 | + |
|
539 | + /** |
|
540 | + * @NoAdminRequired |
|
541 | + * |
|
542 | + * @param string $id |
|
543 | + * @param int $enabled |
|
544 | + * @return DataResponse |
|
545 | + */ |
|
546 | + public function setEnabled(string $id, int $enabled): DataResponse { |
|
547 | + $enabled = (bool)$enabled; |
|
548 | + if ($enabled) { |
|
549 | + $errorMsgGeneral = $this->l10n->t('Error while enabling user.'); |
|
550 | + } else { |
|
551 | + $errorMsgGeneral = $this->l10n->t('Error while disabling user.'); |
|
552 | + } |
|
553 | + |
|
554 | + $userId = $this->userSession->getUser()->getUID(); |
|
555 | + $user = $this->userManager->get($id); |
|
556 | + |
|
557 | + if ($userId === $id) { |
|
558 | + return new DataResponse( |
|
559 | + [ |
|
560 | + 'status' => 'error', |
|
561 | + 'data' => [ |
|
562 | + 'message' => $errorMsgGeneral |
|
563 | + ] |
|
564 | + ], Http::STATUS_FORBIDDEN |
|
565 | + ); |
|
566 | + } |
|
567 | + |
|
568 | + if ($user) { |
|
569 | + if (!$this->isAdmin && !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user)) { |
|
570 | + return new DataResponse( |
|
571 | + [ |
|
572 | + 'status' => 'error', |
|
573 | + 'data' => [ |
|
574 | + 'message' => $this->l10n->t('Authentication error') |
|
575 | + ] |
|
576 | + ], |
|
577 | + Http::STATUS_FORBIDDEN |
|
578 | + ); |
|
579 | + } |
|
580 | + |
|
581 | + $user->setEnabled($enabled); |
|
582 | + return new DataResponse( |
|
583 | + [ |
|
584 | + 'status' => 'success', |
|
585 | + 'data' => [ |
|
586 | + 'username' => $id, |
|
587 | + 'enabled' => $enabled |
|
588 | + ] |
|
589 | + ] |
|
590 | + ); |
|
591 | + } else { |
|
592 | + return new DataResponse( |
|
593 | + [ |
|
594 | + 'status' => 'error', |
|
595 | + 'data' => [ |
|
596 | + 'message' => $errorMsgGeneral |
|
597 | + ] |
|
598 | + ], |
|
599 | + Http::STATUS_FORBIDDEN |
|
600 | + ); |
|
601 | + } |
|
602 | + |
|
603 | + } |
|
604 | + |
|
605 | + /** |
|
606 | + * Set the mail address of a user |
|
607 | + * |
|
608 | + * @NoAdminRequired |
|
609 | + * @NoSubadminRequired |
|
610 | + * @PasswordConfirmationRequired |
|
611 | + * |
|
612 | + * @param string $account |
|
613 | + * @param bool $onlyVerificationCode only return verification code without updating the data |
|
614 | + * @return DataResponse |
|
615 | + */ |
|
616 | + public function getVerificationCode(string $account, bool $onlyVerificationCode): DataResponse { |
|
617 | + |
|
618 | + $user = $this->userSession->getUser(); |
|
619 | + |
|
620 | + if ($user === null) { |
|
621 | + return new DataResponse([], Http::STATUS_BAD_REQUEST); |
|
622 | + } |
|
623 | + |
|
624 | + $accountData = $this->accountManager->getUser($user); |
|
625 | + $cloudId = $user->getCloudId(); |
|
626 | + $message = 'Use my Federated Cloud ID to share with me: ' . $cloudId; |
|
627 | + $signature = $this->signMessage($user, $message); |
|
628 | + |
|
629 | + $code = $message . ' ' . $signature; |
|
630 | + $codeMd5 = $message . ' ' . md5($signature); |
|
631 | + |
|
632 | + switch ($account) { |
|
633 | + case 'verify-twitter': |
|
634 | + $accountData[AccountManager::PROPERTY_TWITTER]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS; |
|
635 | + $msg = $this->l10n->t('In order to verify your Twitter account, post the following tweet on Twitter (please make sure to post it without any line breaks):'); |
|
636 | + $code = $codeMd5; |
|
637 | + $type = AccountManager::PROPERTY_TWITTER; |
|
638 | + $data = $accountData[AccountManager::PROPERTY_TWITTER]['value']; |
|
639 | + $accountData[AccountManager::PROPERTY_TWITTER]['signature'] = $signature; |
|
640 | + break; |
|
641 | + case 'verify-website': |
|
642 | + $accountData[AccountManager::PROPERTY_WEBSITE]['verified'] = AccountManager::VERIFICATION_IN_PROGRESS; |
|
643 | + $msg = $this->l10n->t('In order to verify your Website, store the following content in your web-root at \'.well-known/CloudIdVerificationCode.txt\' (please make sure that the complete text is in one line):'); |
|
644 | + $type = AccountManager::PROPERTY_WEBSITE; |
|
645 | + $data = $accountData[AccountManager::PROPERTY_WEBSITE]['value']; |
|
646 | + $accountData[AccountManager::PROPERTY_WEBSITE]['signature'] = $signature; |
|
647 | + break; |
|
648 | + default: |
|
649 | + return new DataResponse([], Http::STATUS_BAD_REQUEST); |
|
650 | + } |
|
651 | + |
|
652 | + if ($onlyVerificationCode === false) { |
|
653 | + $this->accountManager->updateUser($user, $accountData); |
|
654 | + |
|
655 | + $this->jobList->add(VerifyUserData::class, |
|
656 | + [ |
|
657 | + 'verificationCode' => $code, |
|
658 | + 'data' => $data, |
|
659 | + 'type' => $type, |
|
660 | + 'uid' => $user->getUID(), |
|
661 | + 'try' => 0, |
|
662 | + 'lastRun' => $this->getCurrentTime() |
|
663 | + ] |
|
664 | + ); |
|
665 | + } |
|
666 | + |
|
667 | + return new DataResponse(['msg' => $msg, 'code' => $code]); |
|
668 | + } |
|
669 | + |
|
670 | + /** |
|
671 | + * get current timestamp |
|
672 | + * |
|
673 | + * @return int |
|
674 | + */ |
|
675 | + protected function getCurrentTime(): int { |
|
676 | + return time(); |
|
677 | + } |
|
678 | + |
|
679 | + /** |
|
680 | + * sign message with users private key |
|
681 | + * |
|
682 | + * @param IUser $user |
|
683 | + * @param string $message |
|
684 | + * |
|
685 | + * @return string base64 encoded signature |
|
686 | + */ |
|
687 | + protected function signMessage(IUser $user, string $message): string { |
|
688 | + $privateKey = $this->keyManager->getKey($user)->getPrivate(); |
|
689 | + openssl_sign(json_encode($message), $signature, $privateKey, OPENSSL_ALGO_SHA512); |
|
690 | + return base64_encode($signature); |
|
691 | + } |
|
692 | + |
|
693 | + /** |
|
694 | + * @NoAdminRequired |
|
695 | + * @NoSubadminRequired |
|
696 | + * @PasswordConfirmationRequired |
|
697 | + * |
|
698 | + * @param string $avatarScope |
|
699 | + * @param string $displayname |
|
700 | + * @param string $displaynameScope |
|
701 | + * @param string $phone |
|
702 | + * @param string $phoneScope |
|
703 | + * @param string $email |
|
704 | + * @param string $emailScope |
|
705 | + * @param string $website |
|
706 | + * @param string $websiteScope |
|
707 | + * @param string $address |
|
708 | + * @param string $addressScope |
|
709 | + * @param string $twitter |
|
710 | + * @param string $twitterScope |
|
711 | + * @return DataResponse |
|
712 | + */ |
|
713 | + public function setUserSettings($avatarScope, |
|
714 | + $displayname, |
|
715 | + $displaynameScope, |
|
716 | + $phone, |
|
717 | + $phoneScope, |
|
718 | + $email, |
|
719 | + $emailScope, |
|
720 | + $website, |
|
721 | + $websiteScope, |
|
722 | + $address, |
|
723 | + $addressScope, |
|
724 | + $twitter, |
|
725 | + $twitterScope |
|
726 | + ) { |
|
727 | + |
|
728 | + if (!empty($email) && !$this->mailer->validateMailAddress($email)) { |
|
729 | + return new DataResponse( |
|
730 | + [ |
|
731 | + 'status' => 'error', |
|
732 | + 'data' => [ |
|
733 | + 'message' => $this->l10n->t('Invalid mail address') |
|
734 | + ] |
|
735 | + ], |
|
736 | + Http::STATUS_UNPROCESSABLE_ENTITY |
|
737 | + ); |
|
738 | + } |
|
739 | + |
|
740 | + $user = $this->userSession->getUser(); |
|
741 | + |
|
742 | + $data = $this->accountManager->getUser($user); |
|
743 | + |
|
744 | + $data[AccountManager::PROPERTY_AVATAR] = ['scope' => $avatarScope]; |
|
745 | + if ($this->config->getSystemValue('allow_user_to_change_display_name', true) !== false) { |
|
746 | + $data[AccountManager::PROPERTY_DISPLAYNAME] = ['value' => $displayname, 'scope' => $displaynameScope]; |
|
747 | + $data[AccountManager::PROPERTY_EMAIL] = ['value' => $email, 'scope' => $emailScope]; |
|
748 | + } |
|
749 | + |
|
750 | + if ($this->appManager->isEnabledForUser('federatedfilesharing')) { |
|
751 | + $federatedFileSharing = new \OCA\FederatedFileSharing\AppInfo\Application(); |
|
752 | + $shareProvider = $federatedFileSharing->getFederatedShareProvider(); |
|
753 | + if ($shareProvider->isLookupServerUploadEnabled()) { |
|
754 | + $data[AccountManager::PROPERTY_WEBSITE] = ['value' => $website, 'scope' => $websiteScope]; |
|
755 | + $data[AccountManager::PROPERTY_ADDRESS] = ['value' => $address, 'scope' => $addressScope]; |
|
756 | + $data[AccountManager::PROPERTY_PHONE] = ['value' => $phone, 'scope' => $phoneScope]; |
|
757 | + $data[AccountManager::PROPERTY_TWITTER] = ['value' => $twitter, 'scope' => $twitterScope]; |
|
758 | + } |
|
759 | + } |
|
760 | + |
|
761 | + try { |
|
762 | + $this->saveUserSettings($user, $data); |
|
763 | + return new DataResponse( |
|
764 | + [ |
|
765 | + 'status' => 'success', |
|
766 | + 'data' => [ |
|
767 | + 'userId' => $user->getUID(), |
|
768 | + 'avatarScope' => $data[AccountManager::PROPERTY_AVATAR]['scope'], |
|
769 | + 'displayname' => $data[AccountManager::PROPERTY_DISPLAYNAME]['value'], |
|
770 | + 'displaynameScope' => $data[AccountManager::PROPERTY_DISPLAYNAME]['scope'], |
|
771 | + 'email' => $data[AccountManager::PROPERTY_EMAIL]['value'], |
|
772 | + 'emailScope' => $data[AccountManager::PROPERTY_EMAIL]['scope'], |
|
773 | + 'website' => $data[AccountManager::PROPERTY_WEBSITE]['value'], |
|
774 | + 'websiteScope' => $data[AccountManager::PROPERTY_WEBSITE]['scope'], |
|
775 | + 'address' => $data[AccountManager::PROPERTY_ADDRESS]['value'], |
|
776 | + 'addressScope' => $data[AccountManager::PROPERTY_ADDRESS]['scope'], |
|
777 | + 'message' => $this->l10n->t('Settings saved') |
|
778 | + ] |
|
779 | + ], |
|
780 | + Http::STATUS_OK |
|
781 | + ); |
|
782 | + } catch (ForbiddenException $e) { |
|
783 | + return new DataResponse([ |
|
784 | + 'status' => 'error', |
|
785 | + 'data' => [ |
|
786 | + 'message' => $e->getMessage() |
|
787 | + ], |
|
788 | + ]); |
|
789 | + } |
|
790 | + |
|
791 | + } |
|
792 | + |
|
793 | + |
|
794 | + /** |
|
795 | + * update account manager with new user data |
|
796 | + * |
|
797 | + * @param IUser $user |
|
798 | + * @param array $data |
|
799 | + * @throws ForbiddenException |
|
800 | + */ |
|
801 | + protected function saveUserSettings(IUser $user, array $data) { |
|
802 | + |
|
803 | + // keep the user back-end up-to-date with the latest display name and email |
|
804 | + // address |
|
805 | + $oldDisplayName = $user->getDisplayName(); |
|
806 | + $oldDisplayName = is_null($oldDisplayName) ? '' : $oldDisplayName; |
|
807 | + if (isset($data[AccountManager::PROPERTY_DISPLAYNAME]['value']) |
|
808 | + && $oldDisplayName !== $data[AccountManager::PROPERTY_DISPLAYNAME]['value'] |
|
809 | + ) { |
|
810 | + $result = $user->setDisplayName($data[AccountManager::PROPERTY_DISPLAYNAME]['value']); |
|
811 | + if ($result === false) { |
|
812 | + throw new ForbiddenException($this->l10n->t('Unable to change full name')); |
|
813 | + } |
|
814 | + } |
|
815 | + |
|
816 | + $oldEmailAddress = $user->getEMailAddress(); |
|
817 | + $oldEmailAddress = is_null($oldEmailAddress) ? '' : $oldEmailAddress; |
|
818 | + if (isset($data[AccountManager::PROPERTY_EMAIL]['value']) |
|
819 | + && $oldEmailAddress !== $data[AccountManager::PROPERTY_EMAIL]['value'] |
|
820 | + ) { |
|
821 | + // this is the only permission a backend provides and is also used |
|
822 | + // for the permission of setting a email address |
|
823 | + if (!$user->canChangeDisplayName()) { |
|
824 | + throw new ForbiddenException($this->l10n->t('Unable to change email address')); |
|
825 | + } |
|
826 | + $user->setEMailAddress($data[AccountManager::PROPERTY_EMAIL]['value']); |
|
827 | + } |
|
828 | + |
|
829 | + $this->accountManager->updateUser($user, $data); |
|
830 | + } |
|
831 | + |
|
832 | + /** |
|
833 | + * Count all unique users visible for the current admin/subadmin. |
|
834 | + * |
|
835 | + * @NoAdminRequired |
|
836 | + * |
|
837 | + * @return DataResponse |
|
838 | + */ |
|
839 | + public function stats(): DataResponse { |
|
840 | + $userCount = 0; |
|
841 | + if ($this->isAdmin) { |
|
842 | + $countByBackend = $this->userManager->countUsers(); |
|
843 | + |
|
844 | + if (!empty($countByBackend)) { |
|
845 | + foreach ($countByBackend as $count) { |
|
846 | + $userCount += $count; |
|
847 | + } |
|
848 | + } |
|
849 | + } else { |
|
850 | + $groups = $this->groupManager->getSubAdmin()->getSubAdminsGroups($this->userSession->getUser()); |
|
851 | + |
|
852 | + $uniqueUsers = []; |
|
853 | + foreach ($groups as $group) { |
|
854 | + foreach ($group->getUsers() as $uid => $displayName) { |
|
855 | + $uniqueUsers[$uid] = true; |
|
856 | + } |
|
857 | + } |
|
858 | + |
|
859 | + $userCount = count($uniqueUsers); |
|
860 | + } |
|
861 | + |
|
862 | + return new DataResponse( |
|
863 | + [ |
|
864 | + 'totalUsers' => $userCount |
|
865 | + ] |
|
866 | + ); |
|
867 | + } |
|
868 | + |
|
869 | + |
|
870 | + /** |
|
871 | + * Set the displayName of a user |
|
872 | + * |
|
873 | + * @NoAdminRequired |
|
874 | + * @NoSubadminRequired |
|
875 | + * @PasswordConfirmationRequired |
|
876 | + * @todo merge into saveUserSettings |
|
877 | + * |
|
878 | + * @param string $username |
|
879 | + * @param string $displayName |
|
880 | + * @return DataResponse |
|
881 | + */ |
|
882 | + public function setDisplayName(string $username, string $displayName) { |
|
883 | + $currentUser = $this->userSession->getUser(); |
|
884 | + $user = $this->userManager->get($username); |
|
885 | + |
|
886 | + if ($user === null || |
|
887 | + !$user->canChangeDisplayName() || |
|
888 | + ( |
|
889 | + !$this->groupManager->isAdmin($currentUser->getUID()) && |
|
890 | + !$this->groupManager->getSubAdmin()->isUserAccessible($currentUser, $user) && |
|
891 | + $currentUser->getUID() !== $username |
|
892 | + |
|
893 | + ) |
|
894 | + ) { |
|
895 | + return new DataResponse([ |
|
896 | + 'status' => 'error', |
|
897 | + 'data' => [ |
|
898 | + 'message' => $this->l10n->t('Authentication error'), |
|
899 | + ], |
|
900 | + ]); |
|
901 | + } |
|
902 | + |
|
903 | + $userData = $this->accountManager->getUser($user); |
|
904 | + $userData[AccountManager::PROPERTY_DISPLAYNAME]['value'] = $displayName; |
|
905 | + |
|
906 | + |
|
907 | + try { |
|
908 | + $this->saveUserSettings($user, $userData); |
|
909 | + return new DataResponse([ |
|
910 | + 'status' => 'success', |
|
911 | + 'data' => [ |
|
912 | + 'message' => $this->l10n->t('Your full name has been changed.'), |
|
913 | + 'username' => $username, |
|
914 | + 'displayName' => $displayName, |
|
915 | + ], |
|
916 | + ]); |
|
917 | + } catch (ForbiddenException $e) { |
|
918 | + return new DataResponse([ |
|
919 | + 'status' => 'error', |
|
920 | + 'data' => [ |
|
921 | + 'message' => $e->getMessage(), |
|
922 | + 'displayName' => $user->getDisplayName(), |
|
923 | + ], |
|
924 | + ]); |
|
925 | + } |
|
926 | + } |
|
927 | + |
|
928 | + /** |
|
929 | + * Set the mail address of a user |
|
930 | + * |
|
931 | + * @NoAdminRequired |
|
932 | + * @NoSubadminRequired |
|
933 | + * @PasswordConfirmationRequired |
|
934 | + * |
|
935 | + * @param string $id |
|
936 | + * @param string $mailAddress |
|
937 | + * @return DataResponse |
|
938 | + */ |
|
939 | + public function setEMailAddress(string $id, string $mailAddress) { |
|
940 | + $user = $this->userManager->get($id); |
|
941 | + if (!$this->isAdmin |
|
942 | + && !$this->groupManager->getSubAdmin()->isUserAccessible($this->userSession->getUser(), $user) |
|
943 | + ) { |
|
944 | + return new DataResponse( |
|
945 | + [ |
|
946 | + 'status' => 'error', |
|
947 | + 'data' => [ |
|
948 | + 'message' => $this->l10n->t('Forbidden') |
|
949 | + ] |
|
950 | + ], |
|
951 | + Http::STATUS_FORBIDDEN |
|
952 | + ); |
|
953 | + } |
|
954 | + |
|
955 | + if ($mailAddress !== '' && !$this->mailer->validateMailAddress($mailAddress)) { |
|
956 | + return new DataResponse( |
|
957 | + [ |
|
958 | + 'status' => 'error', |
|
959 | + 'data' => [ |
|
960 | + 'message' => $this->l10n->t('Invalid mail address') |
|
961 | + ] |
|
962 | + ], |
|
963 | + Http::STATUS_UNPROCESSABLE_ENTITY |
|
964 | + ); |
|
965 | + } |
|
966 | + |
|
967 | + if (!$user) { |
|
968 | + return new DataResponse( |
|
969 | + [ |
|
970 | + 'status' => 'error', |
|
971 | + 'data' => [ |
|
972 | + 'message' => $this->l10n->t('Invalid user') |
|
973 | + ] |
|
974 | + ], |
|
975 | + Http::STATUS_UNPROCESSABLE_ENTITY |
|
976 | + ); |
|
977 | + } |
|
978 | + // this is the only permission a backend provides and is also used |
|
979 | + // for the permission of setting a email address |
|
980 | + if (!$user->canChangeDisplayName()) { |
|
981 | + return new DataResponse( |
|
982 | + [ |
|
983 | + 'status' => 'error', |
|
984 | + 'data' => [ |
|
985 | + 'message' => $this->l10n->t('Unable to change mail address') |
|
986 | + ] |
|
987 | + ], |
|
988 | + Http::STATUS_FORBIDDEN |
|
989 | + ); |
|
990 | + } |
|
991 | + |
|
992 | + $userData = $this->accountManager->getUser($user); |
|
993 | + $userData[AccountManager::PROPERTY_EMAIL]['value'] = $mailAddress; |
|
994 | + |
|
995 | + try { |
|
996 | + $this->saveUserSettings($user, $userData); |
|
997 | + return new DataResponse( |
|
998 | + [ |
|
999 | + 'status' => 'success', |
|
1000 | + 'data' => [ |
|
1001 | + 'username' => $id, |
|
1002 | + 'mailAddress' => $mailAddress, |
|
1003 | + 'message' => $this->l10n->t('Email saved') |
|
1004 | + ] |
|
1005 | + ], |
|
1006 | + Http::STATUS_OK |
|
1007 | + ); |
|
1008 | + } catch (ForbiddenException $e) { |
|
1009 | + return new DataResponse([ |
|
1010 | + 'status' => 'error', |
|
1011 | + 'data' => [ |
|
1012 | + 'message' => $e->getMessage() |
|
1013 | + ], |
|
1014 | + ]); |
|
1015 | + } |
|
1016 | + } |
|
1017 | 1017 | |
1018 | 1018 | } |
@@ -48,159 +48,159 @@ |
||
48 | 48 | */ |
49 | 49 | |
50 | 50 | class File implements IWriter, IFileBased { |
51 | - /** @var string */ |
|
52 | - protected $logFile; |
|
53 | - /** @var IConfig */ |
|
54 | - private $config; |
|
51 | + /** @var string */ |
|
52 | + protected $logFile; |
|
53 | + /** @var IConfig */ |
|
54 | + private $config; |
|
55 | 55 | |
56 | - public function __construct(string $path, string $fallbackPath = '', IConfig $config) { |
|
57 | - $this->logFile = $path; |
|
58 | - if (!file_exists($this->logFile)) { |
|
59 | - if( |
|
60 | - ( |
|
61 | - !is_writable(dirname($this->logFile)) |
|
62 | - || !touch($this->logFile) |
|
63 | - ) |
|
64 | - && $fallbackPath !== '' |
|
65 | - ) { |
|
66 | - $this->logFile = $fallbackPath; |
|
67 | - } |
|
68 | - } |
|
69 | - $this->config = $config; |
|
70 | - } |
|
56 | + public function __construct(string $path, string $fallbackPath = '', IConfig $config) { |
|
57 | + $this->logFile = $path; |
|
58 | + if (!file_exists($this->logFile)) { |
|
59 | + if( |
|
60 | + ( |
|
61 | + !is_writable(dirname($this->logFile)) |
|
62 | + || !touch($this->logFile) |
|
63 | + ) |
|
64 | + && $fallbackPath !== '' |
|
65 | + ) { |
|
66 | + $this->logFile = $fallbackPath; |
|
67 | + } |
|
68 | + } |
|
69 | + $this->config = $config; |
|
70 | + } |
|
71 | 71 | |
72 | - /** |
|
73 | - * write a message in the log |
|
74 | - * @param string $app |
|
75 | - * @param string|array $message |
|
76 | - * @param int $level |
|
77 | - */ |
|
78 | - public function write(string $app, $message, int $level) { |
|
79 | - // default to ISO8601 |
|
80 | - $format = $this->config->getSystemValue('logdateformat', \DateTime::ATOM); |
|
81 | - $logTimeZone = $this->config->getSystemValue('logtimezone', 'UTC'); |
|
82 | - try { |
|
83 | - $timezone = new \DateTimeZone($logTimeZone); |
|
84 | - } catch (\Exception $e) { |
|
85 | - $timezone = new \DateTimeZone('UTC'); |
|
86 | - } |
|
87 | - $time = \DateTime::createFromFormat("U.u", number_format(microtime(true), 4, ".", "")); |
|
88 | - if ($time === false) { |
|
89 | - $time = new \DateTime(null, $timezone); |
|
90 | - } else { |
|
91 | - // apply timezone if $time is created from UNIX timestamp |
|
92 | - $time->setTimezone($timezone); |
|
93 | - } |
|
94 | - $request = \OC::$server->getRequest(); |
|
95 | - $reqId = $request->getId(); |
|
96 | - $remoteAddr = $request->getRemoteAddress(); |
|
97 | - // remove username/passwords from URLs before writing the to the log file |
|
98 | - $time = $time->format($format); |
|
99 | - $url = ($request->getRequestUri() !== '') ? $request->getRequestUri() : '--'; |
|
100 | - $method = is_string($request->getMethod()) ? $request->getMethod() : '--'; |
|
101 | - if($this->config->getSystemValue('installed', false)) { |
|
102 | - $user = \OC_User::getUser() ? \OC_User::getUser() : '--'; |
|
103 | - } else { |
|
104 | - $user = '--'; |
|
105 | - } |
|
106 | - $userAgent = $request->getHeader('User-Agent'); |
|
107 | - if ($userAgent === '') { |
|
108 | - $userAgent = '--'; |
|
109 | - } |
|
110 | - $version = $this->config->getSystemValue('version', ''); |
|
111 | - $entry = compact( |
|
112 | - 'reqId', |
|
113 | - 'level', |
|
114 | - 'time', |
|
115 | - 'remoteAddr', |
|
116 | - 'user', |
|
117 | - 'app', |
|
118 | - 'method', |
|
119 | - 'url', |
|
120 | - 'message', |
|
121 | - 'userAgent', |
|
122 | - 'version' |
|
123 | - ); |
|
124 | - // PHP's json_encode only accept proper UTF-8 strings, loop over all |
|
125 | - // elements to ensure that they are properly UTF-8 compliant or convert |
|
126 | - // them manually. |
|
127 | - foreach($entry as $key => $value) { |
|
128 | - if(is_string($value)) { |
|
129 | - $testEncode = json_encode($value); |
|
130 | - if($testEncode === false) { |
|
131 | - $entry[$key] = utf8_encode($value); |
|
132 | - } |
|
133 | - } |
|
134 | - } |
|
135 | - $entry = json_encode($entry, JSON_PARTIAL_OUTPUT_ON_ERROR); |
|
136 | - $handle = @fopen($this->logFile, 'a'); |
|
137 | - if ((fileperms($this->logFile) & 0777) != 0640) { |
|
138 | - @chmod($this->logFile, 0640); |
|
139 | - } |
|
140 | - if ($handle) { |
|
141 | - fwrite($handle, $entry."\n"); |
|
142 | - fclose($handle); |
|
143 | - } else { |
|
144 | - // Fall back to error_log |
|
145 | - error_log($entry); |
|
146 | - } |
|
147 | - if (php_sapi_name() === 'cli-server') { |
|
148 | - error_log($message, 4); |
|
149 | - } |
|
150 | - } |
|
72 | + /** |
|
73 | + * write a message in the log |
|
74 | + * @param string $app |
|
75 | + * @param string|array $message |
|
76 | + * @param int $level |
|
77 | + */ |
|
78 | + public function write(string $app, $message, int $level) { |
|
79 | + // default to ISO8601 |
|
80 | + $format = $this->config->getSystemValue('logdateformat', \DateTime::ATOM); |
|
81 | + $logTimeZone = $this->config->getSystemValue('logtimezone', 'UTC'); |
|
82 | + try { |
|
83 | + $timezone = new \DateTimeZone($logTimeZone); |
|
84 | + } catch (\Exception $e) { |
|
85 | + $timezone = new \DateTimeZone('UTC'); |
|
86 | + } |
|
87 | + $time = \DateTime::createFromFormat("U.u", number_format(microtime(true), 4, ".", "")); |
|
88 | + if ($time === false) { |
|
89 | + $time = new \DateTime(null, $timezone); |
|
90 | + } else { |
|
91 | + // apply timezone if $time is created from UNIX timestamp |
|
92 | + $time->setTimezone($timezone); |
|
93 | + } |
|
94 | + $request = \OC::$server->getRequest(); |
|
95 | + $reqId = $request->getId(); |
|
96 | + $remoteAddr = $request->getRemoteAddress(); |
|
97 | + // remove username/passwords from URLs before writing the to the log file |
|
98 | + $time = $time->format($format); |
|
99 | + $url = ($request->getRequestUri() !== '') ? $request->getRequestUri() : '--'; |
|
100 | + $method = is_string($request->getMethod()) ? $request->getMethod() : '--'; |
|
101 | + if($this->config->getSystemValue('installed', false)) { |
|
102 | + $user = \OC_User::getUser() ? \OC_User::getUser() : '--'; |
|
103 | + } else { |
|
104 | + $user = '--'; |
|
105 | + } |
|
106 | + $userAgent = $request->getHeader('User-Agent'); |
|
107 | + if ($userAgent === '') { |
|
108 | + $userAgent = '--'; |
|
109 | + } |
|
110 | + $version = $this->config->getSystemValue('version', ''); |
|
111 | + $entry = compact( |
|
112 | + 'reqId', |
|
113 | + 'level', |
|
114 | + 'time', |
|
115 | + 'remoteAddr', |
|
116 | + 'user', |
|
117 | + 'app', |
|
118 | + 'method', |
|
119 | + 'url', |
|
120 | + 'message', |
|
121 | + 'userAgent', |
|
122 | + 'version' |
|
123 | + ); |
|
124 | + // PHP's json_encode only accept proper UTF-8 strings, loop over all |
|
125 | + // elements to ensure that they are properly UTF-8 compliant or convert |
|
126 | + // them manually. |
|
127 | + foreach($entry as $key => $value) { |
|
128 | + if(is_string($value)) { |
|
129 | + $testEncode = json_encode($value); |
|
130 | + if($testEncode === false) { |
|
131 | + $entry[$key] = utf8_encode($value); |
|
132 | + } |
|
133 | + } |
|
134 | + } |
|
135 | + $entry = json_encode($entry, JSON_PARTIAL_OUTPUT_ON_ERROR); |
|
136 | + $handle = @fopen($this->logFile, 'a'); |
|
137 | + if ((fileperms($this->logFile) & 0777) != 0640) { |
|
138 | + @chmod($this->logFile, 0640); |
|
139 | + } |
|
140 | + if ($handle) { |
|
141 | + fwrite($handle, $entry."\n"); |
|
142 | + fclose($handle); |
|
143 | + } else { |
|
144 | + // Fall back to error_log |
|
145 | + error_log($entry); |
|
146 | + } |
|
147 | + if (php_sapi_name() === 'cli-server') { |
|
148 | + error_log($message, 4); |
|
149 | + } |
|
150 | + } |
|
151 | 151 | |
152 | - /** |
|
153 | - * get entries from the log in reverse chronological order |
|
154 | - * @param int $limit |
|
155 | - * @param int $offset |
|
156 | - * @return array |
|
157 | - */ |
|
158 | - public function getEntries(int $limit=50, int $offset=0):array { |
|
159 | - $minLevel = $this->config->getSystemValue("loglevel", ILogger::WARN); |
|
160 | - $entries = array(); |
|
161 | - $handle = @fopen($this->logFile, 'rb'); |
|
162 | - if ($handle) { |
|
163 | - fseek($handle, 0, SEEK_END); |
|
164 | - $pos = ftell($handle); |
|
165 | - $line = ''; |
|
166 | - $entriesCount = 0; |
|
167 | - $lines = 0; |
|
168 | - // Loop through each character of the file looking for new lines |
|
169 | - while ($pos >= 0 && ($limit === null ||$entriesCount < $limit)) { |
|
170 | - fseek($handle, $pos); |
|
171 | - $ch = fgetc($handle); |
|
172 | - if ($ch == "\n" || $pos == 0) { |
|
173 | - if ($line != '') { |
|
174 | - // Add the first character if at the start of the file, |
|
175 | - // because it doesn't hit the else in the loop |
|
176 | - if ($pos == 0) { |
|
177 | - $line = $ch.$line; |
|
178 | - } |
|
179 | - $entry = json_decode($line); |
|
180 | - // Add the line as an entry if it is passed the offset and is equal or above the log level |
|
181 | - if ($entry->level >= $minLevel) { |
|
182 | - $lines++; |
|
183 | - if ($lines > $offset) { |
|
184 | - $entries[] = $entry; |
|
185 | - $entriesCount++; |
|
186 | - } |
|
187 | - } |
|
188 | - $line = ''; |
|
189 | - } |
|
190 | - } else { |
|
191 | - $line = $ch.$line; |
|
192 | - } |
|
193 | - $pos--; |
|
194 | - } |
|
195 | - fclose($handle); |
|
196 | - } |
|
197 | - return $entries; |
|
198 | - } |
|
152 | + /** |
|
153 | + * get entries from the log in reverse chronological order |
|
154 | + * @param int $limit |
|
155 | + * @param int $offset |
|
156 | + * @return array |
|
157 | + */ |
|
158 | + public function getEntries(int $limit=50, int $offset=0):array { |
|
159 | + $minLevel = $this->config->getSystemValue("loglevel", ILogger::WARN); |
|
160 | + $entries = array(); |
|
161 | + $handle = @fopen($this->logFile, 'rb'); |
|
162 | + if ($handle) { |
|
163 | + fseek($handle, 0, SEEK_END); |
|
164 | + $pos = ftell($handle); |
|
165 | + $line = ''; |
|
166 | + $entriesCount = 0; |
|
167 | + $lines = 0; |
|
168 | + // Loop through each character of the file looking for new lines |
|
169 | + while ($pos >= 0 && ($limit === null ||$entriesCount < $limit)) { |
|
170 | + fseek($handle, $pos); |
|
171 | + $ch = fgetc($handle); |
|
172 | + if ($ch == "\n" || $pos == 0) { |
|
173 | + if ($line != '') { |
|
174 | + // Add the first character if at the start of the file, |
|
175 | + // because it doesn't hit the else in the loop |
|
176 | + if ($pos == 0) { |
|
177 | + $line = $ch.$line; |
|
178 | + } |
|
179 | + $entry = json_decode($line); |
|
180 | + // Add the line as an entry if it is passed the offset and is equal or above the log level |
|
181 | + if ($entry->level >= $minLevel) { |
|
182 | + $lines++; |
|
183 | + if ($lines > $offset) { |
|
184 | + $entries[] = $entry; |
|
185 | + $entriesCount++; |
|
186 | + } |
|
187 | + } |
|
188 | + $line = ''; |
|
189 | + } |
|
190 | + } else { |
|
191 | + $line = $ch.$line; |
|
192 | + } |
|
193 | + $pos--; |
|
194 | + } |
|
195 | + fclose($handle); |
|
196 | + } |
|
197 | + return $entries; |
|
198 | + } |
|
199 | 199 | |
200 | - /** |
|
201 | - * @return string |
|
202 | - */ |
|
203 | - public function getLogFilePath():string { |
|
204 | - return $this->logFile; |
|
205 | - } |
|
200 | + /** |
|
201 | + * @return string |
|
202 | + */ |
|
203 | + public function getLogFilePath():string { |
|
204 | + return $this->logFile; |
|
205 | + } |
|
206 | 206 | } |
@@ -56,7 +56,7 @@ discard block |
||
56 | 56 | public function __construct(string $path, string $fallbackPath = '', IConfig $config) { |
57 | 57 | $this->logFile = $path; |
58 | 58 | if (!file_exists($this->logFile)) { |
59 | - if( |
|
59 | + if ( |
|
60 | 60 | ( |
61 | 61 | !is_writable(dirname($this->logFile)) |
62 | 62 | || !touch($this->logFile) |
@@ -98,7 +98,7 @@ discard block |
||
98 | 98 | $time = $time->format($format); |
99 | 99 | $url = ($request->getRequestUri() !== '') ? $request->getRequestUri() : '--'; |
100 | 100 | $method = is_string($request->getMethod()) ? $request->getMethod() : '--'; |
101 | - if($this->config->getSystemValue('installed', false)) { |
|
101 | + if ($this->config->getSystemValue('installed', false)) { |
|
102 | 102 | $user = \OC_User::getUser() ? \OC_User::getUser() : '--'; |
103 | 103 | } else { |
104 | 104 | $user = '--'; |
@@ -124,10 +124,10 @@ discard block |
||
124 | 124 | // PHP's json_encode only accept proper UTF-8 strings, loop over all |
125 | 125 | // elements to ensure that they are properly UTF-8 compliant or convert |
126 | 126 | // them manually. |
127 | - foreach($entry as $key => $value) { |
|
128 | - if(is_string($value)) { |
|
127 | + foreach ($entry as $key => $value) { |
|
128 | + if (is_string($value)) { |
|
129 | 129 | $testEncode = json_encode($value); |
130 | - if($testEncode === false) { |
|
130 | + if ($testEncode === false) { |
|
131 | 131 | $entry[$key] = utf8_encode($value); |
132 | 132 | } |
133 | 133 | } |
@@ -155,7 +155,7 @@ discard block |
||
155 | 155 | * @param int $offset |
156 | 156 | * @return array |
157 | 157 | */ |
158 | - public function getEntries(int $limit=50, int $offset=0):array { |
|
158 | + public function getEntries(int $limit = 50, int $offset = 0):array { |
|
159 | 159 | $minLevel = $this->config->getSystemValue("loglevel", ILogger::WARN); |
160 | 160 | $entries = array(); |
161 | 161 | $handle = @fopen($this->logFile, 'rb'); |
@@ -166,7 +166,7 @@ discard block |
||
166 | 166 | $entriesCount = 0; |
167 | 167 | $lines = 0; |
168 | 168 | // Loop through each character of the file looking for new lines |
169 | - while ($pos >= 0 && ($limit === null ||$entriesCount < $limit)) { |
|
169 | + while ($pos >= 0 && ($limit === null || $entriesCount < $limit)) { |
|
170 | 170 | fseek($handle, $pos); |
171 | 171 | $ch = fgetc($handle); |
172 | 172 | if ($ch == "\n" || $pos == 0) { |
@@ -31,13 +31,13 @@ |
||
31 | 31 | * @since 14.0.0 |
32 | 32 | */ |
33 | 33 | interface IFileBased { |
34 | - /** |
|
35 | - * @since 14.0.0 |
|
36 | - */ |
|
37 | - public function getLogFilePath():string; |
|
34 | + /** |
|
35 | + * @since 14.0.0 |
|
36 | + */ |
|
37 | + public function getLogFilePath():string; |
|
38 | 38 | |
39 | - /** |
|
40 | - * @since 14.0.0 |
|
41 | - */ |
|
42 | - public function getEntries(int $limit=50, int $offset=0): array; |
|
39 | + /** |
|
40 | + * @since 14.0.0 |
|
41 | + */ |
|
42 | + public function getEntries(int $limit=50, int $offset=0): array; |
|
43 | 43 | } |
@@ -39,5 +39,5 @@ |
||
39 | 39 | /** |
40 | 40 | * @since 14.0.0 |
41 | 41 | */ |
42 | - public function getEntries(int $limit=50, int $offset=0): array; |
|
42 | + public function getEntries(int $limit = 50, int $offset = 0): array; |
|
43 | 43 | } |
@@ -55,203 +55,203 @@ |
||
55 | 55 | */ |
56 | 56 | class GetSharedSecret extends Job { |
57 | 57 | |
58 | - /** @var IClient */ |
|
59 | - private $httpClient; |
|
58 | + /** @var IClient */ |
|
59 | + private $httpClient; |
|
60 | 60 | |
61 | - /** @var IJobList */ |
|
62 | - private $jobList; |
|
61 | + /** @var IJobList */ |
|
62 | + private $jobList; |
|
63 | 63 | |
64 | - /** @var IURLGenerator */ |
|
65 | - private $urlGenerator; |
|
64 | + /** @var IURLGenerator */ |
|
65 | + private $urlGenerator; |
|
66 | 66 | |
67 | - /** @var TrustedServers */ |
|
68 | - private $trustedServers; |
|
67 | + /** @var TrustedServers */ |
|
68 | + private $trustedServers; |
|
69 | 69 | |
70 | - /** @var IDiscoveryService */ |
|
71 | - private $ocsDiscoveryService; |
|
70 | + /** @var IDiscoveryService */ |
|
71 | + private $ocsDiscoveryService; |
|
72 | 72 | |
73 | - /** @var ILogger */ |
|
74 | - private $logger; |
|
73 | + /** @var ILogger */ |
|
74 | + private $logger; |
|
75 | 75 | |
76 | - /** @var ITimeFactory */ |
|
77 | - private $timeFactory; |
|
76 | + /** @var ITimeFactory */ |
|
77 | + private $timeFactory; |
|
78 | 78 | |
79 | - /** @var bool */ |
|
80 | - protected $retainJob = false; |
|
79 | + /** @var bool */ |
|
80 | + protected $retainJob = false; |
|
81 | 81 | |
82 | - private $defaultEndPoint = '/ocs/v2.php/apps/federation/api/v1/shared-secret'; |
|
82 | + private $defaultEndPoint = '/ocs/v2.php/apps/federation/api/v1/shared-secret'; |
|
83 | 83 | |
84 | - /** @var int 30 day = 2592000sec */ |
|
85 | - private $maxLifespan = 2592000; |
|
84 | + /** @var int 30 day = 2592000sec */ |
|
85 | + private $maxLifespan = 2592000; |
|
86 | 86 | |
87 | - /** |
|
88 | - * RequestSharedSecret constructor. |
|
89 | - * |
|
90 | - * @param IClientService $httpClientService |
|
91 | - * @param IURLGenerator $urlGenerator |
|
92 | - * @param IJobList $jobList |
|
93 | - * @param TrustedServers $trustedServers |
|
94 | - * @param ILogger $logger |
|
95 | - * @param IDiscoveryService $ocsDiscoveryService |
|
96 | - * @param ITimeFactory $timeFactory |
|
97 | - */ |
|
98 | - public function __construct( |
|
99 | - IClientService $httpClientService, |
|
100 | - IURLGenerator $urlGenerator, |
|
101 | - IJobList $jobList, |
|
102 | - TrustedServers $trustedServers, |
|
103 | - ILogger $logger, |
|
104 | - IDiscoveryService $ocsDiscoveryService, |
|
105 | - ITimeFactory $timeFactory |
|
106 | - ) { |
|
107 | - $this->logger = $logger; |
|
108 | - $this->httpClient = $httpClientService->newClient(); |
|
109 | - $this->jobList = $jobList; |
|
110 | - $this->urlGenerator = $urlGenerator; |
|
111 | - $this->ocsDiscoveryService = $ocsDiscoveryService; |
|
112 | - $this->trustedServers = $trustedServers; |
|
113 | - $this->timeFactory = $timeFactory; |
|
114 | - } |
|
87 | + /** |
|
88 | + * RequestSharedSecret constructor. |
|
89 | + * |
|
90 | + * @param IClientService $httpClientService |
|
91 | + * @param IURLGenerator $urlGenerator |
|
92 | + * @param IJobList $jobList |
|
93 | + * @param TrustedServers $trustedServers |
|
94 | + * @param ILogger $logger |
|
95 | + * @param IDiscoveryService $ocsDiscoveryService |
|
96 | + * @param ITimeFactory $timeFactory |
|
97 | + */ |
|
98 | + public function __construct( |
|
99 | + IClientService $httpClientService, |
|
100 | + IURLGenerator $urlGenerator, |
|
101 | + IJobList $jobList, |
|
102 | + TrustedServers $trustedServers, |
|
103 | + ILogger $logger, |
|
104 | + IDiscoveryService $ocsDiscoveryService, |
|
105 | + ITimeFactory $timeFactory |
|
106 | + ) { |
|
107 | + $this->logger = $logger; |
|
108 | + $this->httpClient = $httpClientService->newClient(); |
|
109 | + $this->jobList = $jobList; |
|
110 | + $this->urlGenerator = $urlGenerator; |
|
111 | + $this->ocsDiscoveryService = $ocsDiscoveryService; |
|
112 | + $this->trustedServers = $trustedServers; |
|
113 | + $this->timeFactory = $timeFactory; |
|
114 | + } |
|
115 | 115 | |
116 | - /** |
|
117 | - * run the job, then remove it from the joblist |
|
118 | - * |
|
119 | - * @param JobList $jobList |
|
120 | - * @param ILogger|null $logger |
|
121 | - */ |
|
122 | - public function execute($jobList, ILogger $logger = null) { |
|
123 | - $target = $this->argument['url']; |
|
124 | - // only execute if target is still in the list of trusted domains |
|
125 | - if ($this->trustedServers->isTrustedServer($target)) { |
|
126 | - $this->parentExecute($jobList, $logger); |
|
127 | - } |
|
116 | + /** |
|
117 | + * run the job, then remove it from the joblist |
|
118 | + * |
|
119 | + * @param JobList $jobList |
|
120 | + * @param ILogger|null $logger |
|
121 | + */ |
|
122 | + public function execute($jobList, ILogger $logger = null) { |
|
123 | + $target = $this->argument['url']; |
|
124 | + // only execute if target is still in the list of trusted domains |
|
125 | + if ($this->trustedServers->isTrustedServer($target)) { |
|
126 | + $this->parentExecute($jobList, $logger); |
|
127 | + } |
|
128 | 128 | |
129 | - $jobList->remove($this, $this->argument); |
|
129 | + $jobList->remove($this, $this->argument); |
|
130 | 130 | |
131 | - if ($this->retainJob) { |
|
132 | - $this->reAddJob($this->argument); |
|
133 | - } |
|
134 | - } |
|
131 | + if ($this->retainJob) { |
|
132 | + $this->reAddJob($this->argument); |
|
133 | + } |
|
134 | + } |
|
135 | 135 | |
136 | - /** |
|
137 | - * call execute() method of parent |
|
138 | - * |
|
139 | - * @param JobList $jobList |
|
140 | - * @param ILogger $logger |
|
141 | - */ |
|
142 | - protected function parentExecute($jobList, $logger = null) { |
|
143 | - parent::execute($jobList, $logger); |
|
144 | - } |
|
136 | + /** |
|
137 | + * call execute() method of parent |
|
138 | + * |
|
139 | + * @param JobList $jobList |
|
140 | + * @param ILogger $logger |
|
141 | + */ |
|
142 | + protected function parentExecute($jobList, $logger = null) { |
|
143 | + parent::execute($jobList, $logger); |
|
144 | + } |
|
145 | 145 | |
146 | - protected function run($argument) { |
|
147 | - $target = $argument['url']; |
|
148 | - $created = isset($argument['created']) ? (int)$argument['created'] : $this->timeFactory->getTime(); |
|
149 | - $currentTime = $this->timeFactory->getTime(); |
|
150 | - $source = $this->urlGenerator->getAbsoluteURL('/'); |
|
151 | - $source = rtrim($source, '/'); |
|
152 | - $token = $argument['token']; |
|
146 | + protected function run($argument) { |
|
147 | + $target = $argument['url']; |
|
148 | + $created = isset($argument['created']) ? (int)$argument['created'] : $this->timeFactory->getTime(); |
|
149 | + $currentTime = $this->timeFactory->getTime(); |
|
150 | + $source = $this->urlGenerator->getAbsoluteURL('/'); |
|
151 | + $source = rtrim($source, '/'); |
|
152 | + $token = $argument['token']; |
|
153 | 153 | |
154 | - // kill job after 30 days of trying |
|
155 | - $deadline = $currentTime - $this->maxLifespan; |
|
156 | - if ($created < $deadline) { |
|
157 | - $this->retainJob = false; |
|
158 | - $this->trustedServers->setServerStatus($target,TrustedServers::STATUS_FAILURE); |
|
159 | - return; |
|
160 | - } |
|
154 | + // kill job after 30 days of trying |
|
155 | + $deadline = $currentTime - $this->maxLifespan; |
|
156 | + if ($created < $deadline) { |
|
157 | + $this->retainJob = false; |
|
158 | + $this->trustedServers->setServerStatus($target,TrustedServers::STATUS_FAILURE); |
|
159 | + return; |
|
160 | + } |
|
161 | 161 | |
162 | - $endPoints = $this->ocsDiscoveryService->discover($target, 'FEDERATED_SHARING'); |
|
163 | - $endPoint = isset($endPoints['shared-secret']) ? $endPoints['shared-secret'] : $this->defaultEndPoint; |
|
162 | + $endPoints = $this->ocsDiscoveryService->discover($target, 'FEDERATED_SHARING'); |
|
163 | + $endPoint = isset($endPoints['shared-secret']) ? $endPoints['shared-secret'] : $this->defaultEndPoint; |
|
164 | 164 | |
165 | - // make sure that we have a well formatted url |
|
166 | - $url = rtrim($target, '/') . '/' . trim($endPoint, '/'); |
|
165 | + // make sure that we have a well formatted url |
|
166 | + $url = rtrim($target, '/') . '/' . trim($endPoint, '/'); |
|
167 | 167 | |
168 | - $result = null; |
|
169 | - try { |
|
170 | - $result = $this->httpClient->get( |
|
171 | - $url, |
|
172 | - [ |
|
173 | - 'query' => |
|
174 | - [ |
|
175 | - 'url' => $source, |
|
176 | - 'token' => $token, |
|
177 | - 'format' => 'json', |
|
178 | - ], |
|
179 | - 'timeout' => 3, |
|
180 | - 'connect_timeout' => 3, |
|
181 | - ] |
|
182 | - ); |
|
168 | + $result = null; |
|
169 | + try { |
|
170 | + $result = $this->httpClient->get( |
|
171 | + $url, |
|
172 | + [ |
|
173 | + 'query' => |
|
174 | + [ |
|
175 | + 'url' => $source, |
|
176 | + 'token' => $token, |
|
177 | + 'format' => 'json', |
|
178 | + ], |
|
179 | + 'timeout' => 3, |
|
180 | + 'connect_timeout' => 3, |
|
181 | + ] |
|
182 | + ); |
|
183 | 183 | |
184 | - $status = $result->getStatusCode(); |
|
184 | + $status = $result->getStatusCode(); |
|
185 | 185 | |
186 | - } catch (ClientException $e) { |
|
187 | - $status = $e->getCode(); |
|
188 | - if ($status === Http::STATUS_FORBIDDEN) { |
|
189 | - $this->logger->info($target . ' refused to exchange a shared secret with you.', ['app' => 'federation']); |
|
190 | - } else { |
|
191 | - $this->logger->info($target . ' responded with a ' . $status . ' containing: ' . $e->getMessage(), ['app' => 'federation']); |
|
192 | - } |
|
193 | - } catch (RequestException $e) { |
|
194 | - $status = -1; // There is no status code if we could not connect |
|
195 | - $this->logger->logException($e, [ |
|
196 | - 'message' => 'Could not connect to ' . $target, |
|
197 | - 'level' => ILogger::INFO, |
|
198 | - 'app' => 'federation', |
|
199 | - ]); |
|
200 | - } catch (RingException $e) { |
|
201 | - $status = -1; // There is no status code if we could not connect |
|
202 | - $this->logger->logException($e, [ |
|
203 | - 'message' => 'Could not connect to ' . $target, |
|
204 | - 'level' => ILogger::INFO, |
|
205 | - 'app' => 'federation', |
|
206 | - ]); |
|
207 | - } catch (\Exception $e) { |
|
208 | - $status = Http::STATUS_INTERNAL_SERVER_ERROR; |
|
209 | - $this->logger->logException($e, ['app' => 'federation']); |
|
210 | - } |
|
186 | + } catch (ClientException $e) { |
|
187 | + $status = $e->getCode(); |
|
188 | + if ($status === Http::STATUS_FORBIDDEN) { |
|
189 | + $this->logger->info($target . ' refused to exchange a shared secret with you.', ['app' => 'federation']); |
|
190 | + } else { |
|
191 | + $this->logger->info($target . ' responded with a ' . $status . ' containing: ' . $e->getMessage(), ['app' => 'federation']); |
|
192 | + } |
|
193 | + } catch (RequestException $e) { |
|
194 | + $status = -1; // There is no status code if we could not connect |
|
195 | + $this->logger->logException($e, [ |
|
196 | + 'message' => 'Could not connect to ' . $target, |
|
197 | + 'level' => ILogger::INFO, |
|
198 | + 'app' => 'federation', |
|
199 | + ]); |
|
200 | + } catch (RingException $e) { |
|
201 | + $status = -1; // There is no status code if we could not connect |
|
202 | + $this->logger->logException($e, [ |
|
203 | + 'message' => 'Could not connect to ' . $target, |
|
204 | + 'level' => ILogger::INFO, |
|
205 | + 'app' => 'federation', |
|
206 | + ]); |
|
207 | + } catch (\Exception $e) { |
|
208 | + $status = Http::STATUS_INTERNAL_SERVER_ERROR; |
|
209 | + $this->logger->logException($e, ['app' => 'federation']); |
|
210 | + } |
|
211 | 211 | |
212 | - // if we received a unexpected response we try again later |
|
213 | - if ( |
|
214 | - $status !== Http::STATUS_OK |
|
215 | - && $status !== Http::STATUS_FORBIDDEN |
|
216 | - ) { |
|
217 | - $this->retainJob = true; |
|
218 | - } |
|
212 | + // if we received a unexpected response we try again later |
|
213 | + if ( |
|
214 | + $status !== Http::STATUS_OK |
|
215 | + && $status !== Http::STATUS_FORBIDDEN |
|
216 | + ) { |
|
217 | + $this->retainJob = true; |
|
218 | + } |
|
219 | 219 | |
220 | - if ($status === Http::STATUS_OK && $result instanceof IResponse) { |
|
221 | - $body = $result->getBody(); |
|
222 | - $result = json_decode($body, true); |
|
223 | - if (isset($result['ocs']['data']['sharedSecret'])) { |
|
224 | - $this->trustedServers->addSharedSecret( |
|
225 | - $target, |
|
226 | - $result['ocs']['data']['sharedSecret'] |
|
227 | - ); |
|
228 | - } else { |
|
229 | - $this->logger->error( |
|
230 | - 'remote server "' . $target . '"" does not return a valid shared secret. Received data: ' . $body, |
|
231 | - ['app' => 'federation'] |
|
232 | - ); |
|
233 | - $this->trustedServers->setServerStatus($target, TrustedServers::STATUS_FAILURE); |
|
234 | - } |
|
235 | - } |
|
220 | + if ($status === Http::STATUS_OK && $result instanceof IResponse) { |
|
221 | + $body = $result->getBody(); |
|
222 | + $result = json_decode($body, true); |
|
223 | + if (isset($result['ocs']['data']['sharedSecret'])) { |
|
224 | + $this->trustedServers->addSharedSecret( |
|
225 | + $target, |
|
226 | + $result['ocs']['data']['sharedSecret'] |
|
227 | + ); |
|
228 | + } else { |
|
229 | + $this->logger->error( |
|
230 | + 'remote server "' . $target . '"" does not return a valid shared secret. Received data: ' . $body, |
|
231 | + ['app' => 'federation'] |
|
232 | + ); |
|
233 | + $this->trustedServers->setServerStatus($target, TrustedServers::STATUS_FAILURE); |
|
234 | + } |
|
235 | + } |
|
236 | 236 | |
237 | - } |
|
237 | + } |
|
238 | 238 | |
239 | - /** |
|
240 | - * re-add background job |
|
241 | - * |
|
242 | - * @param array $argument |
|
243 | - */ |
|
244 | - protected function reAddJob(array $argument) { |
|
245 | - $url = $argument['url']; |
|
246 | - $created = isset($argument['created']) ? (int)$argument['created'] : $this->timeFactory->getTime(); |
|
247 | - $token = $argument['token']; |
|
248 | - $this->jobList->add( |
|
249 | - GetSharedSecret::class, |
|
250 | - [ |
|
251 | - 'url' => $url, |
|
252 | - 'token' => $token, |
|
253 | - 'created' => $created |
|
254 | - ] |
|
255 | - ); |
|
256 | - } |
|
239 | + /** |
|
240 | + * re-add background job |
|
241 | + * |
|
242 | + * @param array $argument |
|
243 | + */ |
|
244 | + protected function reAddJob(array $argument) { |
|
245 | + $url = $argument['url']; |
|
246 | + $created = isset($argument['created']) ? (int)$argument['created'] : $this->timeFactory->getTime(); |
|
247 | + $token = $argument['token']; |
|
248 | + $this->jobList->add( |
|
249 | + GetSharedSecret::class, |
|
250 | + [ |
|
251 | + 'url' => $url, |
|
252 | + 'token' => $token, |
|
253 | + 'created' => $created |
|
254 | + ] |
|
255 | + ); |
|
256 | + } |
|
257 | 257 | } |
@@ -145,7 +145,7 @@ discard block |
||
145 | 145 | |
146 | 146 | protected function run($argument) { |
147 | 147 | $target = $argument['url']; |
148 | - $created = isset($argument['created']) ? (int)$argument['created'] : $this->timeFactory->getTime(); |
|
148 | + $created = isset($argument['created']) ? (int) $argument['created'] : $this->timeFactory->getTime(); |
|
149 | 149 | $currentTime = $this->timeFactory->getTime(); |
150 | 150 | $source = $this->urlGenerator->getAbsoluteURL('/'); |
151 | 151 | $source = rtrim($source, '/'); |
@@ -155,7 +155,7 @@ discard block |
||
155 | 155 | $deadline = $currentTime - $this->maxLifespan; |
156 | 156 | if ($created < $deadline) { |
157 | 157 | $this->retainJob = false; |
158 | - $this->trustedServers->setServerStatus($target,TrustedServers::STATUS_FAILURE); |
|
158 | + $this->trustedServers->setServerStatus($target, TrustedServers::STATUS_FAILURE); |
|
159 | 159 | return; |
160 | 160 | } |
161 | 161 | |
@@ -163,7 +163,7 @@ discard block |
||
163 | 163 | $endPoint = isset($endPoints['shared-secret']) ? $endPoints['shared-secret'] : $this->defaultEndPoint; |
164 | 164 | |
165 | 165 | // make sure that we have a well formatted url |
166 | - $url = rtrim($target, '/') . '/' . trim($endPoint, '/'); |
|
166 | + $url = rtrim($target, '/').'/'.trim($endPoint, '/'); |
|
167 | 167 | |
168 | 168 | $result = null; |
169 | 169 | try { |
@@ -186,21 +186,21 @@ discard block |
||
186 | 186 | } catch (ClientException $e) { |
187 | 187 | $status = $e->getCode(); |
188 | 188 | if ($status === Http::STATUS_FORBIDDEN) { |
189 | - $this->logger->info($target . ' refused to exchange a shared secret with you.', ['app' => 'federation']); |
|
189 | + $this->logger->info($target.' refused to exchange a shared secret with you.', ['app' => 'federation']); |
|
190 | 190 | } else { |
191 | - $this->logger->info($target . ' responded with a ' . $status . ' containing: ' . $e->getMessage(), ['app' => 'federation']); |
|
191 | + $this->logger->info($target.' responded with a '.$status.' containing: '.$e->getMessage(), ['app' => 'federation']); |
|
192 | 192 | } |
193 | 193 | } catch (RequestException $e) { |
194 | 194 | $status = -1; // There is no status code if we could not connect |
195 | 195 | $this->logger->logException($e, [ |
196 | - 'message' => 'Could not connect to ' . $target, |
|
196 | + 'message' => 'Could not connect to '.$target, |
|
197 | 197 | 'level' => ILogger::INFO, |
198 | 198 | 'app' => 'federation', |
199 | 199 | ]); |
200 | 200 | } catch (RingException $e) { |
201 | 201 | $status = -1; // There is no status code if we could not connect |
202 | 202 | $this->logger->logException($e, [ |
203 | - 'message' => 'Could not connect to ' . $target, |
|
203 | + 'message' => 'Could not connect to '.$target, |
|
204 | 204 | 'level' => ILogger::INFO, |
205 | 205 | 'app' => 'federation', |
206 | 206 | ]); |
@@ -227,7 +227,7 @@ discard block |
||
227 | 227 | ); |
228 | 228 | } else { |
229 | 229 | $this->logger->error( |
230 | - 'remote server "' . $target . '"" does not return a valid shared secret. Received data: ' . $body, |
|
230 | + 'remote server "'.$target.'"" does not return a valid shared secret. Received data: '.$body, |
|
231 | 231 | ['app' => 'federation'] |
232 | 232 | ); |
233 | 233 | $this->trustedServers->setServerStatus($target, TrustedServers::STATUS_FAILURE); |
@@ -243,7 +243,7 @@ discard block |
||
243 | 243 | */ |
244 | 244 | protected function reAddJob(array $argument) { |
245 | 245 | $url = $argument['url']; |
246 | - $created = isset($argument['created']) ? (int)$argument['created'] : $this->timeFactory->getTime(); |
|
246 | + $created = isset($argument['created']) ? (int) $argument['created'] : $this->timeFactory->getTime(); |
|
247 | 247 | $token = $argument['token']; |
248 | 248 | $this->jobList->add( |
249 | 249 | GetSharedSecret::class, |
@@ -33,17 +33,17 @@ |
||
33 | 33 | * location and manage that with your own tools. |
34 | 34 | */ |
35 | 35 | class Rotate extends \OC\BackgroundJob\Job { |
36 | - use RotationTrait; |
|
36 | + use RotationTrait; |
|
37 | 37 | |
38 | - public function run($dummy) { |
|
39 | - $systemConfig = \OC::$server->getSystemConfig(); |
|
40 | - $this->filePath = $systemConfig->getValue('logfile', $systemConfig->getValue('datadirectory', \OC::$SERVERROOT . '/data') . '/nextcloud.log'); |
|
38 | + public function run($dummy) { |
|
39 | + $systemConfig = \OC::$server->getSystemConfig(); |
|
40 | + $this->filePath = $systemConfig->getValue('logfile', $systemConfig->getValue('datadirectory', \OC::$SERVERROOT . '/data') . '/nextcloud.log'); |
|
41 | 41 | |
42 | - $this->maxSize = \OC::$server->getConfig()->getSystemValue('log_rotate_size', 100 * 1024 * 1024); |
|
43 | - if($this->shouldRotateBySize()) { |
|
44 | - $rotatedFile = $this->rotate(); |
|
45 | - $msg = 'Log file "'.$this->filePath.'" was over '.$this->maxSize.' bytes, moved to "'.$rotatedFile.'"'; |
|
46 | - \OC::$server->getLogger()->warning($msg, ['app' => Rotate::class]); |
|
47 | - } |
|
48 | - } |
|
42 | + $this->maxSize = \OC::$server->getConfig()->getSystemValue('log_rotate_size', 100 * 1024 * 1024); |
|
43 | + if($this->shouldRotateBySize()) { |
|
44 | + $rotatedFile = $this->rotate(); |
|
45 | + $msg = 'Log file "'.$this->filePath.'" was over '.$this->maxSize.' bytes, moved to "'.$rotatedFile.'"'; |
|
46 | + \OC::$server->getLogger()->warning($msg, ['app' => Rotate::class]); |
|
47 | + } |
|
48 | + } |
|
49 | 49 | } |
@@ -37,10 +37,10 @@ |
||
37 | 37 | |
38 | 38 | public function run($dummy) { |
39 | 39 | $systemConfig = \OC::$server->getSystemConfig(); |
40 | - $this->filePath = $systemConfig->getValue('logfile', $systemConfig->getValue('datadirectory', \OC::$SERVERROOT . '/data') . '/nextcloud.log'); |
|
40 | + $this->filePath = $systemConfig->getValue('logfile', $systemConfig->getValue('datadirectory', \OC::$SERVERROOT.'/data').'/nextcloud.log'); |
|
41 | 41 | |
42 | 42 | $this->maxSize = \OC::$server->getConfig()->getSystemValue('log_rotate_size', 100 * 1024 * 1024); |
43 | - if($this->shouldRotateBySize()) { |
|
43 | + if ($this->shouldRotateBySize()) { |
|
44 | 44 | $rotatedFile = $this->rotate(); |
45 | 45 | $msg = 'Log file "'.$this->filePath.'" was over '.$this->maxSize.' bytes, moved to "'.$rotatedFile.'"'; |
46 | 46 | \OC::$server->getLogger()->warning($msg, ['app' => Rotate::class]); |